Skip to content

Commit

Permalink
base implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
storywithoutend committed Sep 14, 2023
1 parent 645b344 commit c4daca5
Show file tree
Hide file tree
Showing 51 changed files with 2,356 additions and 789 deletions.
1,110 changes: 1,110 additions & 0 deletions e2e/specs/stateless/ownership.spec.ts

Large diffs are not rendered by default.

494 changes: 0 additions & 494 deletions e2e/specs/stateless/sendFlow.spec.ts

This file was deleted.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"@ensdomains/ens-validation": "^0.1.0",
"@ensdomains/ensjs": "3.0.0-alpha.66",
"@ensdomains/eth-ens-namehash": "^2.0.15",
"@ensdomains/thorin": "0.6.43",
"@ensdomains/thorin": "0.6.44",
"@ethersproject/abi": "^5.6.4",
"@ethersproject/abstract-provider": "^5.7.0",
"@ethersproject/abstract-signer": "^5.7.0",
Expand Down
33 changes: 33 additions & 0 deletions playwright/pageObjects/editRolesModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* eslint-disable import/no-extraneous-dependencies */
import { Locator, Page } from '@playwright/test'

import type { Role } from '@app/hooks/ownership/useRoles/useRoles'

export class EditRolesModal {
readonly page: Page

readonly searchInput: Locator

readonly setToSelfButton: Locator

readonly saveButton: Locator

constructor(page: Page) {
this.page = page
this.searchInput = this.page.getByTestId('edit-roles-search-input')
this.setToSelfButton = this.page.getByTestId('edit-roles-set-to-self-button')
this.saveButton = this.page.getByTestId('edit-roles-save-button')
}

roleCard(role: Role) {
return this.page.getByTestId(`role-card-${role}`)
}

roleCardChangeButton(role: Role) {
return this.roleCard(role).getByTestId('role-card-change-button')
}

searchResult(address: string) {
return this.page.getByTestId(`search-result-${address}`)
}
}
4 changes: 4 additions & 0 deletions playwright/pageObjects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { Page } from '@playwright/test'
import { Web3ProviderBackend } from 'headless-web3-provider'

import { AddressPage } from './addressPage'
import { EditRolesModal } from './editRolesModal'
import { ExtendNamesModal } from './extendNamesModal'
import { HomePage } from './homePage'
import { MorePage } from './morePage'
import { OwnershipPage } from './ownershipPage'
import { PermissionsPage } from './permissionsPage'
import { ProfilePage } from './profilePage'
import { RegistrationPage } from './registrationPage'
Expand All @@ -17,9 +19,11 @@ type Dependencies = { page: Page; wallet: Web3ProviderBackend }

const pageObjects = {
AddressPage,
EditRolesModal,
ExtendNamesModal,
HomePage,
MorePage,
OwnershipPage,
PermissionsPage,
ProfilePage,
RegistrationPage,
Expand Down
51 changes: 51 additions & 0 deletions playwright/pageObjects/ownershipPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* eslint-disable import/no-extraneous-dependencies */
import { Locator, Page } from '@playwright/test'

import { Role } from '@app/hooks/ownership/useRoles/useRoles'

export class OwnershipPage {
readonly page: Page

readonly editRolesButton: Locator

readonly sendNameButton: Locator

readonly sendDNSButton: Locator

readonly refreshDNSButton: Locator

readonly extendButton: Locator

readonly syncManagerButton: Locator

constructor(page: Page) {
this.page = page
this.editRolesButton = this.page.getByTestId('role-action-edit-roles')
this.sendNameButton = this.page.getByTestId('role-action-send-name')
this.sendDNSButton = this.page.getByTestId('role-action-send-dns')
this.syncManagerButton = this.page.getByTestId('role-action-sync-manager')
this.refreshDNSButton = this.page.getByTestId('role-action-refresh-dns')
this.extendButton = this.page.getByTestId('expiry-action-extend')
}

async goto(name: string) {
await this.page.goto(`/${name}?tab=ownership`)
}

roleRow(address: string) {
return this.page.getByTestId(`role-row-${address}`)
}

roleTag(role: Role) {
return this.page.getByTestId(`role-tag-${role}`)
}

async getExpiryTimestamp() {
const timestamp = await this.page
.getByTestId('expiry-panel-expiry')
.getAttribute('data-timestamp')
console.log('>>>>', timestamp)
const parsed = parseInt(timestamp || '')
return parsed
}
}
28 changes: 20 additions & 8 deletions playwright/pageObjects/sendNameModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,30 @@ import { Locator, Page } from '@playwright/test'
export class SendNameModal {
readonly page: Page

readonly nextButton: Locator
readonly searchInput: Locator

readonly resetProfileSwitch: Locator

readonly sendButton: Locator

readonly confirmButton: Locator

readonly summaryHeader: Locator

constructor(page: Page) {
this.page = page
this.nextButton = this.page.getByRole('button', { name: 'Next' })
this.searchInput = this.page.getByTestId('send-name-search-input')
this.resetProfileSwitch = this.page.getByTestId('send-name-reset-profile-switch')
this.sendButton = this.page.getByTestId('send-name-send-button')
this.confirmButton = this.page.getByTestId('send-name-confirm-button')
this.summaryHeader = this.page.getByRole('button', { name: 'Summary of changes' })
}

searchResult(address: string) {
return this.page.getByTestId(`search-result-${address}`)
}

async clickNextButton() {
try {
return await this.nextButton.click()
} catch {
await this.page.pause()
}
summaryItem(type: 'owner' | 'manager' | 'eth-record' | 'reset-profile') {
return this.page.getByTestId(`send-name-summary-${type}`)
}
}
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@
"approveNameWrapper": "Approve NameWrapper",
"clearRecords": "Clear records",
"updateRecords": "Update records",
"resetProfileWithRecords": "Reset profile with records",
"transferName": "Transfer name",
"transferSubname": "Transfer subname",
"changePermissions": "Change permissions",
Expand Down Expand Up @@ -252,7 +253,8 @@
"duration": "Duration",
"cost": "Cost",
"update": "Update",
"resolver": "Resolver"
"resolver": "Resolver",
"records": "Records"
},
"itemValue": {
"records_one": "{{count}} record",
Expand Down
11 changes: 8 additions & 3 deletions public/locales/en/profile.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@
},
"ownership": {
"name": "Ownership",
"warning": {
"ownerNotManager": "You are the owner but not the manager. This may be unintended if you’ve recently recieved this name from another address.",
"managerNotParentOwner": "The owner of <strong>{{parent}}</strong> can change ownership, roles, and settings. They cannot change the profile.",
"managerNotDNSOwner": "You are the Manager but not DNS Owner of this name. DNS names can be reclaimed by the DNS Owner at any time. You can send this name to the Owner, or update the DNS record to match.",
"dnsOwnerNotManager": "You cannot make changes to this name because you are the DNS Owner, but not the Manager. You can sync the manager to fix this."
},
"sections": {
"roles": {
"title": "Roles",
Expand All @@ -64,9 +70,8 @@
},
"grace-period": {
"title": "Grace period ends",
"tooltip": {
"content": "A 90 day grace window after expiration, when the name can still be extended but not re-registered."
}
"tooltip": "A 90 day grace window after expiration, when the name can still be extended but not re-registered."

},
"registration": {
"title": "Registered"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import styled, { css } from 'styled-components'

import { Banner } from '@ensdomains/thorin'

import useRoles from '@app/hooks/ownership/useRoles/useRoles'
import type { useNameDetails } from '@app/hooks/useNameDetails'
import { useNameType } from '@app/hooks/useNameType'

import { useOwnershipWarning } from './hooks/useOwnershipWarning'
import { ContractSection } from './sections/ContractSection/ContractSection'
import { ExpirySection } from './sections/ExpirySection/ExpirySection'
import { RolesSection } from './sections/RolesSection/RolesSection'
Expand All @@ -22,11 +26,14 @@ type Props = {

export const OwnershipTab = ({ name, details }: Props) => {
const roles = useRoles(name, { grouped: true })

const nameType = useNameType(name)
const warning = useOwnershipWarning({ name, details, nameType })
const isLoading = roles.isLoading || details.isLoading

if (isLoading) return null
return (
<Container>
{warning.data && <Banner alert="warning">{warning.data}</Banner>}
<RolesSection name={name} roles={roles.data!} details={details} />
<ExpirySection name={name} details={details} />
<ContractSection details={details} />
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { useMemo } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { P, match } from 'ts-pattern'

import { useAccountSafely } from '@app/hooks/useAccountSafely'
import type { useNameDetails } from '@app/hooks/useNameDetails'
import type { useNameType } from '@app/hooks/useNameType'
import useParentBasicName from '@app/hooks/useParentBasicName'

const getParentNameFromName = (name: string) => name.split('.').slice(1).join('.')

type Input = {
name: string
details: ReturnType<typeof useNameDetails>
nameType: ReturnType<typeof useNameType>
}

export const useOwnershipWarning = ({ name, nameType, details }: Input) => {
const { t } = useTranslation('profile')
const account = useAccountSafely()
const parent = useParentBasicName(name)
const isLoading = !account.address || nameType.isLoading || details.isLoading || parent.isLoading

const data = useMemo(() => {
if (isLoading) return undefined
return match([
nameType.data,
{
isRegistrant: details.ownerData?.registrant === account.address,
isParentOwner: parent?.ownerData?.owner === account.address,
isManager: details.ownerData?.owner === account.address,
isDNSOwner: details.dnsOwner === account.address,
},
])
.with(
[
'eth-unwrapped-2ld',
{
isRegistrant: true,
isManager: false,
isParentOwner: P._,
isDNSOwner: P._,
},
],
() => t('tabs.ownership.warning.ownerNotManager'),
)
.with(
[
P.union('dns-unwrapped-2ld', 'dns-wrapped-2ld'),
{
isRegistrant: P._,
isManager: true,
isParentOwner: P._,
isDNSOwner: false,
},
],
() => t('tabs.ownership.warning.managerNotDNSOwner'),
)
.with(
[
P.union('dns-unwrapped-2ld', 'dns-wrapped-2ld'),
{
isRegistrant: P._,
isManager: false,
isParentOwner: P._,
isDNSOwner: true,
},
],
() => t('tabs.ownership.warning.dnsOwnerNotManager'),
)
.with(
[
P.union(
'eth-unwrapped-subname',
'eth-wrapped-subname',
'dns-unwrapped-subname',
'dns-wrapped-subname',
),
{
isRegistrant: P._,
isManager: true,
isParentOwner: false,
isDNSOwner: P._,
},
],
() => (
<Trans
t={t}
i18nKey="tabs.ownership.warning.managerNotParentOwner"
values={{ parent: getParentNameFromName(name) }}
/>
),
)
.otherwise(() => undefined)
}, [
isLoading,
name,
account.address,
parent.ownerData,
details.ownerData,
details.dnsOwner,
nameType.data,
t,
])

return {
data,
isLoading,
}
}
Loading

0 comments on commit c4daca5

Please sign in to comment.