From 13155a37d3db4869a5c4d8c44f144a82efcd8e9f Mon Sep 17 00:00:00 2001 From: storywithoutend Date: Wed, 9 Aug 2023 15:19:41 +0800 Subject: [PATCH 01/10] add contenthash to profile tab --- e2e/specs/stateless/profileEditor.spec.ts | 2 ++ playwright/pageObjects/profilePage.ts | 4 ++++ src/components/pages/profile/ProfileButton.tsx | 17 ++++++++++++++--- src/components/pages/profile/ProfileDetails.tsx | 7 +++++++ .../pages/profile/[name]/tabs/ProfileTab.tsx | 1 + 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/e2e/specs/stateless/profileEditor.spec.ts b/e2e/specs/stateless/profileEditor.spec.ts index 71d2af633..d5ae1d87c 100644 --- a/e2e/specs/stateless/profileEditor.spec.ts +++ b/e2e/specs/stateless/profileEditor.spec.ts @@ -35,6 +35,7 @@ const DEFAULT_RECORDS = { value: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC', }, ], + contentHash: 'ipfs://bafybeico3uuyj3vphxpvbowchdwjlrlrh62awxscrnii7w7flu5z6fk77y', } test.describe('unwrapped', () => { @@ -172,6 +173,7 @@ test.describe('unwrapped', () => { 'ETC_LEGACY0x3C4...293BC', ) await expect(profilePage.record('text', 'email')).toHaveText('fakeemail@fake.com') + await expect(profilePage.contentHash()).toContainText('ipfs://bafybeic...') await profilePage.editProfileButton.click() await profilePage.profileEditor.getByTestId('warning-overlay-next-button').click() diff --git a/playwright/pageObjects/profilePage.ts b/playwright/pageObjects/profilePage.ts index 7cb240fc5..5dccd6d2e 100644 --- a/playwright/pageObjects/profilePage.ts +++ b/playwright/pageObjects/profilePage.ts @@ -63,6 +63,10 @@ export class ProfilePage { return this.page.getByTestId(`other-profile-button-${key}`) } + contentHash(): Locator { + return this.page.getByTestId('other-profile-button-contenthash') + } + async profileEditorAddInputs(keys: string[]) { await this.page.getByTestId('show-add-profile-records-modal-button').click() for (const key of keys) { diff --git a/src/components/pages/profile/ProfileButton.tsx b/src/components/pages/profile/ProfileButton.tsx index 2f19817fd..1232833bd 100644 --- a/src/components/pages/profile/ProfileButton.tsx +++ b/src/components/pages/profile/ProfileButton.tsx @@ -11,6 +11,7 @@ import { DynamicSocialIcon, socialIconTypes } from '@app/assets/social/DynamicSo import { usePrimary } from '@app/hooks/usePrimary' import { getDestination } from '@app/routes' import { useBreakpoint } from '@app/utils/BreakpointProvider' +import { getContentHashLink, getProtocolTypeAndContentId } from '@app/utils/contenthash' import { getSocialData } from '@app/utils/getSocialData' import { shortenAddress } from '@app/utils/utils' @@ -105,10 +106,11 @@ export const OtherProfileButton = ({ }: { iconKey: string value: string - type?: 'text' | 'address' + type?: 'text' | 'address' | 'contenthash' }) => { const breakpoints = useBreakpoint() - const isLink = value?.startsWith('http://') || value?.startsWith('https://') + const isLink = + value?.startsWith('http://') || value?.startsWith('https://') || type === 'contenthash' const formattedValue = useMemo(() => { if (breakpoints.sm) { @@ -122,11 +124,20 @@ export const OtherProfileButton = ({ const linkProps = useMemo(() => { if (!isLink) return {} + if (type === 'contenthash') { + const { protocolType, contentId } = getProtocolTypeAndContentId(value) + const _link = getContentHashLink('', 0, { protocolType, decoded: contentId }) + if (!_link) return {} + return { + as: 'a', + link: _link, + } as const + } return { as: 'a', link: value, } as const - }, [value, isLink]) + }, [value, type, isLink]) return ( > addresses: Array> + contentHash?: ContentHash expiryDate: Date | undefined pccExpired: boolean owners: ReturnType @@ -310,6 +314,9 @@ export const ProfileDetails = ({ !supportedProfileItems.includes(x.key.toLowerCase()), ) .map((x) => ({ ...x, type: 'text' })), + ...(contentHash + ? [{ key: 'contenthash', type: 'contenthash', value: contentHashToString(contentHash) }] + : []), ] const mappedOwners = ownershipInfoCalc(name, pccExpired, owners, gracePeriodEndDate, expiryDate) diff --git a/src/components/pages/profile/[name]/tabs/ProfileTab.tsx b/src/components/pages/profile/[name]/tabs/ProfileTab.tsx index 5701e9b3c..e998fc107 100644 --- a/src/components/pages/profile/[name]/tabs/ProfileTab.tsx +++ b/src/components/pages/profile/[name]/tabs/ProfileTab.tsx @@ -128,6 +128,7 @@ const ProfileTab = ({ nameDetails, name }: Props) => { textRecords={(profile?.records?.texts || []) .map((item: any) => ({ key: item.key, value: item.value })) .filter((item: any) => item.value !== null)} + contentHash={profile?.records?.contentHash} owners={owners} name={normalisedName} actions={profileActions.profileActions} From b1ce6e58ec29a76bc06e08e17528c91eabbe6bbc Mon Sep 17 00:00:00 2001 From: tate Date: Mon, 14 Aug 2023 10:14:31 +1000 Subject: [PATCH 02/10] fix: extend warning not showing when user is owner but not manager --- src/components/ProfileSnippet.tsx | 15 ++++++++++----- .../tabs/MoreTab/Miscellaneous/ExtendButton.tsx | 3 ++- .../pages/profile/[name]/tabs/ProfileTab.tsx | 1 - src/hooks/abilities/useAbilities.ts | 2 +- .../abilities/shouldShowExtendWarning.test.ts | 17 +++++++++++++++++ src/utils/abilities/shouldShowExtendWarning.ts | 6 ++++++ 6 files changed, 36 insertions(+), 8 deletions(-) create mode 100644 src/utils/abilities/shouldShowExtendWarning.test.ts create mode 100644 src/utils/abilities/shouldShowExtendWarning.ts diff --git a/src/components/ProfileSnippet.tsx b/src/components/ProfileSnippet.tsx index c6474fb8a..3bc233c43 100644 --- a/src/components/ProfileSnippet.tsx +++ b/src/components/ProfileSnippet.tsx @@ -5,8 +5,10 @@ import styled, { css } from 'styled-components' import { Button, NametagSVG, Tag, Typography, mq } from '@ensdomains/thorin' import FastForwardSVG from '@app/assets/FastForward.svg' +import { useAbilities } from '@app/hooks/abilities/useAbilities' import useBeautifiedName from '@app/hooks/useBeautifiedName' import { useRouterWithHistory } from '@app/hooks/useRouterWithHistory' +import { shouldShowExtendWarning } from '@app/utils/abilities/shouldShowExtendWarning' import { useTransactionFlow } from '../transaction-flow/TransactionFlowProvider' import { NameAvatar } from './AvatarWithZorb' @@ -159,14 +161,12 @@ export const ProfileSnippet = ({ getTextRecord, button, network, - canEdit, isPrimary, children, }: { name: string getTextRecord?: (key: string) => { value: string } | undefined button?: 'viewProfile' | 'extend' | 'register' - canEdit?: boolean isPrimary?: boolean network: number children?: React.ReactNode @@ -174,6 +174,8 @@ export const ProfileSnippet = ({ const router = useRouterWithHistory() const { t } = useTranslation('common') + const abilities = useAbilities(name) + const { prepareDataInput } = useTransactionFlow() const showExtendNamesInput = prepareDataInput('ExtendNames') @@ -194,7 +196,10 @@ export const ProfileSnippet = ({ prefix={} data-testid="extend-button" onClick={() => { - showExtendNamesInput(`extend-names-${name}`, { names: [name], isSelf: canEdit }) + showExtendNamesInput(`extend-names-${name}`, { + names: [name], + isSelf: shouldShowExtendWarning(abilities.data), + }) }} > {t('action.extend', { ns: 'common' })} @@ -221,7 +226,7 @@ export const ProfileSnippet = ({ ) // eslint-disable-next-line react-hooks/exhaustive-deps - }, [button, name, canEdit]) + }, [button, name, abilities.data]) return ( @@ -231,7 +236,7 @@ export const ProfileSnippet = ({ label={name} name={name} network={network} - noCache={canEdit} + noCache={abilities.data?.canEdit} /> {ActionButton && {ActionButton}} diff --git a/src/components/pages/profile/[name]/tabs/MoreTab/Miscellaneous/ExtendButton.tsx b/src/components/pages/profile/[name]/tabs/MoreTab/Miscellaneous/ExtendButton.tsx index 922b4988b..e210ed82c 100644 --- a/src/components/pages/profile/[name]/tabs/MoreTab/Miscellaneous/ExtendButton.tsx +++ b/src/components/pages/profile/[name]/tabs/MoreTab/Miscellaneous/ExtendButton.tsx @@ -5,6 +5,7 @@ import { Button, FastForwardSVG, mq } from '@ensdomains/thorin' import { useAbilities } from '@app/hooks/abilities/useAbilities' import { useTransactionFlow } from '@app/transaction-flow/TransactionFlowProvider' +import { shouldShowExtendWarning } from '@app/utils/abilities/shouldShowExtendWarning' const FastForwardIcon = styled.svg( ({ theme }) => css` @@ -43,7 +44,7 @@ export const ExtendButton = ({ name }: { name: string }) => { onClick={() => { showExtendNamesInput(`extend-names-${name}`, { names: [name], - isSelf: abilities.data?.canEdit, + isSelf: shouldShowExtendWarning(abilities.data), }) }} prefix={} diff --git a/src/components/pages/profile/[name]/tabs/ProfileTab.tsx b/src/components/pages/profile/[name]/tabs/ProfileTab.tsx index 5701e9b3c..8b9d5f0c1 100644 --- a/src/components/pages/profile/[name]/tabs/ProfileTab.tsx +++ b/src/components/pages/profile/[name]/tabs/ProfileTab.tsx @@ -92,7 +92,6 @@ const ProfileTab = ({ nameDetails, name }: Props) => { network={chainId} getTextRecord={getTextRecord} button={snippetButton} - canEdit={abilities.data?.canEdit} isPrimary={name === primaryData?.name} > {nameDetails.isNonASCII && ( diff --git a/src/hooks/abilities/useAbilities.ts b/src/hooks/abilities/useAbilities.ts index d3be9d472..f6ece6448 100644 --- a/src/hooks/abilities/useAbilities.ts +++ b/src/hooks/abilities/useAbilities.ts @@ -56,7 +56,7 @@ export type SendAbilities = { canSendError?: string } -type Abilities = ExtendAbilities & +export type Abilities = ExtendAbilities & DeleteAbilities & EditAbilities & ReclaimAbilities & diff --git a/src/utils/abilities/shouldShowExtendWarning.test.ts b/src/utils/abilities/shouldShowExtendWarning.test.ts new file mode 100644 index 000000000..d3c9f6ce1 --- /dev/null +++ b/src/utils/abilities/shouldShowExtendWarning.test.ts @@ -0,0 +1,17 @@ +import { shouldShowExtendWarning } from "./shouldShowExtendWarning" + +it('should return true when can edit', () => { + expect(shouldShowExtendWarning({ canEdit: true } as any)).toBe(true) +}) + +it('should return true when can send', () => { + expect(shouldShowExtendWarning({ canSend: true } as any)).toBe(true) +}) + +it('should return false when no abilities', () => { + expect(shouldShowExtendWarning(undefined)).toBe(false) +}) + +it('should return false when no edit or send abilities', () => { + expect(shouldShowExtendWarning({ canEdit: false, canSend: false } as any)).toBe(false) +}) \ No newline at end of file diff --git a/src/utils/abilities/shouldShowExtendWarning.ts b/src/utils/abilities/shouldShowExtendWarning.ts new file mode 100644 index 000000000..84881d075 --- /dev/null +++ b/src/utils/abilities/shouldShowExtendWarning.ts @@ -0,0 +1,6 @@ +import { Abilities } from '@app/hooks/abilities/useAbilities' + +export const shouldShowExtendWarning = (abilities: Abilities | undefined) => { + if (!abilities) return false + return abilities?.canEdit || abilities.canSend +} From 16838eb6086262950d41d3437835245ecd77b7ba Mon Sep 17 00:00:00 2001 From: tate Date: Thu, 17 Aug 2023 14:40:38 +1000 Subject: [PATCH 03/10] feat: sepolia faucet + dynamic faucet data --- public/locales/en/common.json | 2 +- src/components/@molecules/FaucetBanner.tsx | 40 +++++++++++--- src/hooks/useFaucet.ts | 61 ++++++++++++++-------- src/utils/constants.ts | 2 + 4 files changed, 73 insertions(+), 32 deletions(-) diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 2307e1a2d..544e31d15 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -344,7 +344,7 @@ "empty": "No options found" }, "testnetFaucet": { - "explanation": "Each address on goerli can claim {{ amount }}ETH to test out the ENS manager app, as well as all the new contracts.", + "explanation": "Each address on {{ testnet }} can claim {{ amount }} {{ ticker }} to test out the ENS manager app, as well as any other testnet usage.", "note": "It may take a few minutes to show up in your wallet." } } diff --git a/src/components/@molecules/FaucetBanner.tsx b/src/components/@molecules/FaucetBanner.tsx index e899f3c0f..f275d2798 100644 --- a/src/components/@molecules/FaucetBanner.tsx +++ b/src/components/@molecules/FaucetBanner.tsx @@ -1,5 +1,7 @@ +import { BigNumber } from '@ethersproject/bignumber' +import { formatEther } from '@ethersproject/units' import { useRouter } from 'next/router' -import { useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import styled, { css } from 'styled-components' @@ -15,7 +17,7 @@ import { } from '@ensdomains/thorin' import { useAccountSafely } from '@app/hooks/useAccountSafely' -import { useChainId } from '@app/hooks/useChainId' +import { useChainName } from '@app/hooks/useChainName' import useFaucet from '@app/hooks/useFaucet' import { InnerDialog } from '../@atoms/InnerDialog' @@ -44,8 +46,12 @@ const LargeCheckIcon = styled.svg( `, ) +const getAmountFromHex = (hex: `0x${string}`) => formatEther(BigNumber.from(hex)) +const msToDays = (ms: number) => Math.floor(ms / 1000 / 60 / 60 / 24) +const chainEthTicker = (chainName: string) => `${chainName.slice(0, 2)}ETH` + const FaucetBanner = () => { - const chainId = useChainId() + const chainName = useChainName() const { isReady } = useRouter() const { address } = useAccountSafely() const { @@ -60,7 +66,14 @@ const FaucetBanner = () => { const closeDialog = () => setDialogOpen(false) const openDialog = () => setDialogOpen(true) - if (chainId !== 5 || !isReady) return null + const amount = useMemo(() => getAmountFromHex(data?.amount || '0x0'), [data?.amount]) + + useEffect(() => { + closeDialog() + }, [chainName, address]) + + if ((chainName !== 'goerli' && chainName !== 'sepolia') || !isReady || isLoading || !data) + return null const BannerComponent = ( @@ -69,9 +82,13 @@ const FaucetBanner = () => { icon={} onClick={openDialog} alert="info" - title="You have unclaimed goETH!" + title={`You have unclaimed ${chainName} ETH!`} > - {t('testnetFaucet.explanation', { amount: '0.25' })} + {t('testnetFaucet.explanation', { + amount, + testnet: chainName, + ticker: chainEthTicker(chainName), + })} ) @@ -80,11 +97,18 @@ const FaucetBanner = () => { {dialogStage === 'default' ? ( <> - + diff --git a/src/hooks/useFaucet.ts b/src/hooks/useFaucet.ts index 19083c996..be8146cdc 100644 --- a/src/hooks/useFaucet.ts +++ b/src/hooks/useFaucet.ts @@ -1,9 +1,11 @@ +import { useEffect } from 'react' import { useMutation, useQuery, useQueryClient } from 'wagmi' import { useQueryKeys } from '@app/utils/cacheKeyFactory' +import { FAUCET_WORKER_URL } from '@app/utils/constants' import { useAccountSafely } from './useAccountSafely' -import { useChainId } from './useChainId' +import { useChainName } from './useChainName' type BaseJsonRPC = { jsonrpc: string @@ -29,45 +31,53 @@ type FaucetStatus = 'ok' | 'paused' | 'out of funds' type JsonRpc = BaseJsonRPC & (ErrorJsonRPC | SuccessJsonRPC) -const ENDPOINT = +const createEndpoint = (chainName: string) => process.env.NODE_ENV === 'development' - ? 'http://localhost:8787/' - : 'https://ens-faucet.ens-cf.workers.dev/' + ? `http://localhost:8787/${chainName}` + : `${FAUCET_WORKER_URL}/${chainName}` const useFaucet = () => { const queryClient = useQueryClient() + const { address } = useAccountSafely() - const chainId = useChainId() + const chainName = useChainName() + const queryKey = useQueryKeys().faucet(address) + const { data, error, isLoading } = useQuery( - useQueryKeys().faucet(address), + queryKey, async () => { - const result: JsonRpc<{ eligible: boolean; next: number; status: FaucetStatus }> = - await fetch(ENDPOINT, { - method: 'POST', - body: JSON.stringify({ - jsonrpc: '2.0', - method: 'faucet_getAddress', - params: [address], - id: 1, - }), - headers: { - // eslint-disable-next-line @typescript-eslint/naming-convention - 'Content-Type': 'application/json', - }, - }).then((res) => res.json()) + const result: JsonRpc<{ + eligible: boolean + amount: `0x${string}` + interval: number + next: number + status: FaucetStatus + }> = await fetch(createEndpoint(chainName), { + method: 'POST', + body: JSON.stringify({ + jsonrpc: '2.0', + method: 'faucet_getAddress', + params: [address], + id: 1, + }), + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'Content-Type': 'application/json', + }, + }).then((res) => res.json()) if (result.error) throw new Error(result.error.message) return result.result }, { - enabled: !!address && chainId === 5, + enabled: !!address && (chainName === 'goerli' || chainName === 'sepolia'), }, ) const mutation = useMutation( async () => { - const result: JsonRpc<{ id: string }> = await fetch(ENDPOINT, { + const result: JsonRpc<{ id: string }> = await fetch(createEndpoint(chainName), { method: 'POST', body: JSON.stringify({ jsonrpc: '2.0', @@ -87,11 +97,16 @@ const useFaucet = () => { }, { onSuccess: () => { - queryClient.resetQueries(['getFaucetEligibility', address]) + queryClient.resetQueries(queryKey) }, }, ) + useEffect(() => { + mutation.reset() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [chainName, address]) + return { data, error, diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 24625f813..63375c563 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -87,6 +87,8 @@ export const MOONPAY_WORKER_URL: { [key: number]: string } = { 1337: 'https://moonpay-goerli.ens-cf.workers.dev', } +export const FAUCET_WORKER_URL = 'https://ens-faucet.ens-cf.workers.dev' + export const WC_PROJECT_ID = '9b14144d470af1e03ab9d88aaa127332' // 102% of price as buffer for fluctuations From 5b78decff44c57b79c3fa47c079099e0eb94bae4 Mon Sep 17 00:00:00 2001 From: Leon Talbert Date: Mon, 21 Aug 2023 12:01:33 +0800 Subject: [PATCH 04/10] Exception for parent logic when parent is .eth --- .../[name]/tabs/PermissionsTab/NameChangePermissions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/pages/profile/[name]/tabs/PermissionsTab/NameChangePermissions.tsx b/src/components/pages/profile/[name]/tabs/PermissionsTab/NameChangePermissions.tsx index 4d0291945..a599b4fac 100644 --- a/src/components/pages/profile/[name]/tabs/PermissionsTab/NameChangePermissions.tsx +++ b/src/components/pages/profile/[name]/tabs/PermissionsTab/NameChangePermissions.tsx @@ -89,7 +89,7 @@ export const NameChangePermissions = ({ const { prepareDataInput } = useTransactionFlow() const showRevokePermissionsInput = prepareDataInput('RevokePermissions') - const isParentLocked = parentState === 'locked' + const isParentLocked = parentState === 'locked' || wrapperData?.parent.IS_DOT_ETH const permissions = CHILD_FUSES_WITHOUT_CBF.reduce<{ burned: FuseItem[]; unburned: FuseItem[] }>( (acc, permission) => { From dac1854f9b40414d5aac79b16e78240a014beb7b Mon Sep 17 00:00:00 2001 From: Leon Talbert Date: Mon, 21 Aug 2023 17:27:08 +0800 Subject: [PATCH 05/10] sendname default toggle off --- e2e/specs/stateless/sendFlow.spec.ts | 17 ++++++++++++++--- src/transaction-flow/input/SendName-flow.tsx | 13 +++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/e2e/specs/stateless/sendFlow.spec.ts b/e2e/specs/stateless/sendFlow.spec.ts index cdf2ce604..a6b3d1c60 100644 --- a/e2e/specs/stateless/sendFlow.spec.ts +++ b/e2e/specs/stateless/sendFlow.spec.ts @@ -24,7 +24,7 @@ test.describe('Happy', () => { await morePage.sendButton.click() await page.getByTestId('dogfood').type(accounts.getAddress('user3')) - await page.getByTestId('owner-checkbox').click() + await page.getByTestId('manager-checkbox').click() await sendNameModal.clickNextButton() await transactionModal.autoComplete() @@ -55,6 +55,7 @@ test.describe('Happy', () => { await login.connect() await morePage.sendButton.click() + await page.getByTestId('manager-checkbox').click() // Should not allow the manager to change the owner await expect(page.getByTestId('Make Owner')).toHaveCount(0) @@ -89,7 +90,7 @@ test.describe('Happy', () => { await login.connect() await morePage.sendButton.click() - await page.getByTestId('manager-checkbox').click() + await page.getByTestId('owner-checkbox').click() await page.getByTestId('dogfood').type(accounts.getAddress('user3')) await sendNameModal.clickNextButton() @@ -124,7 +125,7 @@ test.describe('Happy', () => { await morePage.sendButton.click() - await page.getByTestId('owner-checkbox').click() + await page.getByTestId('manager-checkbox').click() await page.getByTestId('dogfood').type(accounts.getAddress('user3')) await sendNameModal.clickNextButton() @@ -157,6 +158,8 @@ test.describe('Happy', () => { await morePage.sendButton.click() + await page.getByTestId('manager-checkbox').click() + await page.getByTestId('owner-checkbox').click() await page.getByTestId('dogfood').type(accounts.getAddress('user3')) await sendNameModal.clickNextButton() await transactionModal.confirm() @@ -165,6 +168,8 @@ test.describe('Happy', () => { // Should work after going back after first transaction await page.getByTestId('dogfood').type(accounts.getAddress('user3')) + await page.getByTestId('manager-checkbox').click() + await page.getByTestId('owner-checkbox').click() await sendNameModal.clickNextButton() await transactionModal.autoComplete() @@ -209,6 +214,7 @@ test.describe('Unwrapped subnames', () => { await login.connect() await morePage.sendButton.click() + await page.getByTestId('manager-checkbox').click() await page.getByTestId('dogfood').type(accounts.getAddress('user3')) await sendNameModal.clickNextButton() @@ -246,6 +252,7 @@ test.describe('Unwrapped subnames', () => { await login.connect() await morePage.sendButton.click() + await page.getByTestId('manager-checkbox').click() await page.getByTestId('dogfood').type(accounts.getAddress('user3')) await sendNameModal.clickNextButton() await transactionModal.autoComplete() @@ -343,6 +350,7 @@ test.describe('Wrapped subnames', () => { await morePage.sendButton.click() + await page.getByTestId('manager-checkbox').click() await expect(page.getByText('Make manager')).toBeVisible() await page.getByTestId('dogfood').type(accounts.getAddress('user3')) await sendNameModal.clickNextButton() @@ -382,6 +390,7 @@ test.describe('Wrapped subnames', () => { await login.connect() await morePage.sendButton.click() + await page.getByTestId('manager-checkbox').click() await expect(page.getByText('Make manager')).toBeVisible() await page.getByTestId('dogfood').type(accounts.getAddress('user3')) await sendNameModal.clickNextButton() @@ -450,6 +459,7 @@ test.describe('Wrapped subname with PCC burned', () => { await login.connect() await morePage.sendButton.click() + await page.getByTestId('owner-checkbox').click() await expect(page.getByText('Make owner')).toBeVisible() await page.getByTestId('dogfood').type(accounts.getAddress('user3')) await sendNameModal.clickNextButton() @@ -483,6 +493,7 @@ test.describe('Wrapped name', () => { await login.connect() await morePage.sendButton.click() + await page.getByTestId('owner-checkbox').click() await expect(page.getByText('Make owner')).toBeVisible() await page.getByTestId('dogfood').type(accounts.getAddress('user3')) await sendNameModal.clickNextButton() diff --git a/src/transaction-flow/input/SendName-flow.tsx b/src/transaction-flow/input/SendName-flow.tsx index 930856b5f..18405e786 100644 --- a/src/transaction-flow/input/SendName-flow.tsx +++ b/src/transaction-flow/input/SendName-flow.tsx @@ -153,8 +153,8 @@ export const SendName = ({ data, dispatch, onDismiss }: Props) => { } = useForm({ mode: 'onChange', defaultValues: { - managerChoice: 'manager', - ownerChoice: 'owner', + managerChoice: '', + ownerChoice: '', dogfoodRaw: '', address: '', }, @@ -162,11 +162,14 @@ export const SendName = ({ data, dispatch, onDismiss }: Props) => { const managerChoiceWatch = watch('managerChoice') const ownerChoiceWatch = watch('ownerChoice') + const addressWatch = watch('address') const abilities = useAbilities(name) const { canSendManager, canSendOwner, sendNameFunctionCallDetails } = abilities.data || {} const loadingAbilities = abilities.isLoading const hasChoice = - (canSendManager && managerChoiceWatch) || (canSendOwner && ownerChoiceWatch) || loadingAbilities + !loadingAbilities && + addressWatch && + ((canSendManager && managerChoiceWatch) || (canSendOwner && ownerChoiceWatch)) const hasErrors = Object.keys(formState.errors || {}).length > 0 @@ -203,7 +206,6 @@ export const SendName = ({ data, dispatch, onDismiss }: Props) => { @@ -220,7 +222,6 @@ export const SendName = ({ data, dispatch, onDismiss }: Props) => { @@ -257,7 +258,7 @@ export const SendName = ({ data, dispatch, onDismiss }: Props) => { } trailing={ - } From ca6c1ffa4a5e70a044c9a073e2d2f92ae9365311 Mon Sep 17 00:00:00 2001 From: Leon Talbert Date: Mon, 21 Aug 2023 17:34:32 +0800 Subject: [PATCH 06/10] Remove resolver type tag --- .../profile/[name]/tabs/MoreTab/Resolver.tsx | 96 +++++++------------ 1 file changed, 37 insertions(+), 59 deletions(-) diff --git a/src/components/pages/profile/[name]/tabs/MoreTab/Resolver.tsx b/src/components/pages/profile/[name]/tabs/MoreTab/Resolver.tsx index e8da401cc..4e673aa17 100644 --- a/src/components/pages/profile/[name]/tabs/MoreTab/Resolver.tsx +++ b/src/components/pages/profile/[name]/tabs/MoreTab/Resolver.tsx @@ -1,13 +1,12 @@ import { useTranslation } from 'react-i18next' import styled, { css } from 'styled-components' -import { Skeleton, Tag, Typography, mq } from '@ensdomains/thorin' +import { Typography, mq } from '@ensdomains/thorin' import { cacheableComponentStyles } from '@app/components/@atoms/CacheableComponent' import { DisabledButtonWithTooltip } from '@app/components/@molecules/DisabledButtonWithTooltip' import RecordItem from '@app/components/RecordItem' import { useHasGlobalError } from '@app/hooks/errors/useHasGlobalError' -import { useResolverType } from '@app/hooks/resolver/useResolverType' import { useTransactionFlow } from '@app/transaction-flow/TransactionFlowProvider' import { TabWrapper } from '../../../TabWrapper' @@ -60,16 +59,6 @@ const InnerHeading = styled.div( `, ) -const TagsContainer = styled.div( - ({ theme }) => css` - display: flex; - flex-direction: row; - align-items: center; - justify-content: flex-start; - gap: ${theme.space['1']}; - `, -) - const Resolver = ({ name, canEditResolver, @@ -96,54 +85,43 @@ const Resolver = ({ }) } - const { data: { type: resolverType, tone, isWildcard } = {}, isLoading: isResolverTypeLoading } = - useResolverType(name) - return ( - - - - - - {t('tabs.more.resolver.label')} - - - {t(`tabs.more.resolver.${resolverType}`)} - {isWildcard && ( - {t('tabs.more.resolver.wildcard')} - )} - - - {canEdit && !hasGlobalError && ( - <> - {canEditResolver ? ( - - ) : ( - - )} - - )} - - - - + + + + + {t('tabs.more.resolver.label')} + + + {canEdit && !hasGlobalError && ( + <> + {canEditResolver ? ( + + ) : ( + + )} + + )} + + + ) } From ce9faa5b7a0e84a0e24b087e04d5f56635e7be4e Mon Sep 17 00:00:00 2001 From: Leon Talbert Date: Mon, 21 Aug 2023 17:52:27 +0800 Subject: [PATCH 07/10] remove resolver type tests --- e2e/specs/stateless/profileEditor.spec.ts | 32 ----------------------- 1 file changed, 32 deletions(-) diff --git a/e2e/specs/stateless/profileEditor.spec.ts b/e2e/specs/stateless/profileEditor.spec.ts index 71d2af633..546109be3 100644 --- a/e2e/specs/stateless/profileEditor.spec.ts +++ b/e2e/specs/stateless/profileEditor.spec.ts @@ -6,9 +6,6 @@ import { emptyAddress } from '@app/utils/constants' const oldResolver = '0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB' const newResolver = '0x0E801D84Fa97b50751Dbf25036d067dCf18858bF' -// This not an actual resovler but a dummy address that has been inserted to the second to last known resolver -// to test the situation where unwrapped do not show a warning when editing profile. -const dummyRersolver = '0xd7a4F6473f32aC2Af804B3686AE8F1932bC35750' const DEFAULT_RECORDS = { texts: [ @@ -324,35 +321,6 @@ test.describe('wrapped', () => { }) }) -test.describe('resolver status', () => { - test('should not show warning when editing unwrapped name with second to last resolver', async ({ - page, - login, - makeName, - makePageObject, - }) => { - const name = await makeName({ - label: 'unwrapped', - type: 'legacy', - resolver: dummyRersolver, - }) - - const morePage = makePageObject('MorePage') - const profilePage = makePageObject('ProfilePage') - - await morePage.goto(name) - await login.connect() - - await expect(morePage.resolver).toHaveText(dummyRersolver) - await expect(page.getByText('Latest')).toBeVisible() - - await profilePage.goto(name) - - await profilePage.editProfileButton.click() - await expect(profilePage.profileEditor.getByText('Edit your profile')).toBeVisible() - }) -}) - test.describe('subgraph errors', () => { test('should disable edit profile button when there is a subgraph error', async ({ page, From bacb891ef27b0ac4bf41963aa6f7247421b23e22 Mon Sep 17 00:00:00 2001 From: storywithoutend Date: Mon, 28 Aug 2023 19:09:01 +0800 Subject: [PATCH 08/10] convert contenthash to string before adding to other records --- src/components/pages/profile/ProfileDetails.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/pages/profile/ProfileDetails.tsx b/src/components/pages/profile/ProfileDetails.tsx index 421e1999d..a248f7d7d 100644 --- a/src/components/pages/profile/ProfileDetails.tsx +++ b/src/components/pages/profile/ProfileDetails.tsx @@ -306,6 +306,7 @@ export const ProfileDetails = ({ }) => { const breakpoint = useBreakpoint() + const _contentHash = contentHashToString(contentHash) const otherRecords = [ ...textRecords .filter( @@ -314,9 +315,7 @@ export const ProfileDetails = ({ !supportedProfileItems.includes(x.key.toLowerCase()), ) .map((x) => ({ ...x, type: 'text' })), - ...(contentHash - ? [{ key: 'contenthash', type: 'contenthash', value: contentHashToString(contentHash) }] - : []), + ...(_contentHash ? [{ key: 'contenthash', type: 'contenthash', value: _contentHash }] : []), ] const mappedOwners = ownershipInfoCalc(name, pccExpired, owners, gracePeriodEndDate, expiryDate) From a709d3e9c94e88ac084a85832c9e2ffc91932899 Mon Sep 17 00:00:00 2001 From: storywithoutend Date: Mon, 28 Aug 2023 19:33:33 +0800 Subject: [PATCH 09/10] added tests for content hash display on ProfileDetails --- .../pages/profile/ProfileDetails.test.tsx | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/components/pages/profile/ProfileDetails.test.tsx b/src/components/pages/profile/ProfileDetails.test.tsx index 8b4602ac5..12ccb0a28 100644 --- a/src/components/pages/profile/ProfileDetails.test.tsx +++ b/src/components/pages/profile/ProfileDetails.test.tsx @@ -4,6 +4,19 @@ import { OwnerArray } from '@app/types' import { ownershipInfoCalc } from './ProfileDetails' +import { ProfileDetails } from './ProfileDetails' +import { render, screen } from '@app/test-utils' + +jest.mock('@app/utils/BreakpointProvider', () => ({ + useBreakpoint: () => ({ + xs: true, + sm: true, + md: true, + lg: true, + xl: false, + }) +})) + describe('onwershipInfoCalc', () => { it('should return no owner if PCC is expired', () => { const result = ownershipInfoCalc('', true, [], new Date(), new Date()) @@ -95,3 +108,16 @@ describe('onwershipInfoCalc', () => { ]) }) }) + +describe('ProfileDetails', () => { + it('should show content hash if there is valid contenthash', () => { + render() + expect(screen.getByTestId('other-profile-button-contenthash')).toBeVisible() + expect(screen.getByText('ipfs://QmXoypiz...')).toBeVisible() + }) + + it('should not show content hash if contenthash is empty', () => { + render() + expect(screen.queryByTestId('other-profile-button-contenthash')).toBeNull() + }) +}) \ No newline at end of file From c04b7014cb4b679f46dc3c521abbada49ca7f498 Mon Sep 17 00:00:00 2001 From: tate Date: Thu, 31 Aug 2023 12:20:19 +1000 Subject: [PATCH 10/10] fix: add chainid identifier to registration-status --- .../profile/[name]/registration/Registration.tsx | 2 +- .../pages/profile/[name]/registration/types.ts | 2 +- .../[name]/registration/useMoonpayRegistration.ts | 4 ++-- src/hooks/useRegistrationReducer.ts | 12 ++++++++++-- src/pages/register.tsx | 3 +++ 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/components/pages/profile/[name]/registration/Registration.tsx b/src/components/pages/profile/[name]/registration/Registration.tsx index d446afae4..1b3bffbc2 100644 --- a/src/components/pages/profile/[name]/registration/Registration.tsx +++ b/src/components/pages/profile/[name]/registration/Registration.tsx @@ -110,7 +110,7 @@ const Registration = ({ nameDetails, isLoading }: Props) => { const chainId = useChainId() const { address } = useAccount() const primary = usePrimary(address!, !address) - const selected = { name: nameDetails.normalisedName, address: address! } + const selected = { name: nameDetails.normalisedName, address: address!, chainId } const { normalisedName, beautifiedName } = nameDetails const defaultResolverAddress = useContractAddress('PublicResolver') const { data: resolverExists, isLoading: resolverExistsLoading } = useResolverExists( diff --git a/src/components/pages/profile/[name]/registration/types.ts b/src/components/pages/profile/[name]/registration/types.ts index cf0eef77f..19c2f905e 100644 --- a/src/components/pages/profile/[name]/registration/types.ts +++ b/src/components/pages/profile/[name]/registration/types.ts @@ -41,7 +41,7 @@ export type BackObj = { back: boolean } export type RegistrationData = Prettify> -export type SelectedItemProperties = { address: string; name: string } +export type SelectedItemProperties = { address: string; name: string; chainId: number } export type RegistrationReducerDataItem = Prettify< Omit & { diff --git a/src/components/pages/profile/[name]/registration/useMoonpayRegistration.ts b/src/components/pages/profile/[name]/registration/useMoonpayRegistration.ts index 8b805c7fc..577cee4fc 100644 --- a/src/components/pages/profile/[name]/registration/useMoonpayRegistration.ts +++ b/src/components/pages/profile/[name]/registration/useMoonpayRegistration.ts @@ -10,12 +10,12 @@ import { useQueryKeys } from '@app/utils/cacheKeyFactory' import { MOONPAY_WORKER_URL } from '@app/utils/constants' import { getLabelFromName } from '@app/utils/utils' -import { MoonpayTransactionStatus } from './types' +import { MoonpayTransactionStatus, SelectedItemProperties } from './types' export const useMoonpayRegistration = ( dispatch: ReturnType['dispatch'], normalisedName: string, - selected: { name: string; address: string }, + selected: SelectedItemProperties, item: ReturnType['item'], ) => { const chainId = useChainId() diff --git a/src/hooks/useRegistrationReducer.ts b/src/hooks/useRegistrationReducer.ts index e1897eec2..f000f67bd 100644 --- a/src/hooks/useRegistrationReducer.ts +++ b/src/hooks/useRegistrationReducer.ts @@ -7,6 +7,8 @@ import { } from '@app/components/pages/profile/[name]/registration/types' import { useLocalStorageReducer } from '@app/hooks/useLocalStorage' +import { useChainId } from './useChainId' + export const randomSecret = () => { // the first 4 bytes of the namehash of enslabs.eth const platformSource = '9923eb94' @@ -31,6 +33,7 @@ const defaultData: RegistrationReducerDataItem = { name: '', isMoonpayFlow: false, externalTransactionId: '', + chainId: 1, } const isBrowser = !!( @@ -57,7 +60,11 @@ const makeDefaultData = (selected: SelectedItemProperties): RegistrationReducerD export const getSelectedIndex = ( state: RegistrationReducerData, selected: SelectedItemProperties, -) => state.items.findIndex((x) => x.address === selected.address && x.name === selected.name) +) => + state.items.findIndex( + (x) => + x.address === selected.address && x.name === selected.name && x.chainId === selected.chainId, + ) /* eslint-disable no-param-reassign */ const reducer = (state: RegistrationReducerData, action: RegistrationReducerAction) => { @@ -138,7 +145,8 @@ const useRegistrationReducer = ({ address: string | undefined name: string }) => { - const selected = { address, name } as SelectedItemProperties + const chainId = useChainId() + const selected = { address: address!, name, chainId } as const const [state, dispatch] = useLocalStorageReducer< RegistrationReducerData, RegistrationReducerAction diff --git a/src/pages/register.tsx b/src/pages/register.tsx index 4ae5665bf..5df1e3af3 100644 --- a/src/pages/register.tsx +++ b/src/pages/register.tsx @@ -2,6 +2,7 @@ import { ReactElement } from 'react' import { useAccount } from 'wagmi' import Registration from '@app/components/pages/profile/[name]/registration/Registration' +import { useChainId } from '@app/hooks/useChainId' import { useInitial } from '@app/hooks/useInitial' import { useNameDetails } from '@app/hooks/useNameDetails' import { getSelectedIndex } from '@app/hooks/useRegistrationReducer' @@ -15,6 +16,7 @@ export default function Page() { const initial = useInitial() const { address } = useAccount() + const chainId = useChainId() const nameDetails = useNameDetails(name, true) const { isLoading: detailsLoading, registrationStatus } = nameDetails @@ -31,6 +33,7 @@ export default function Page() { const index = getSelectedIndex(registrationData, { address: address!, name: nameDetails.normalisedName, + chainId, }) if (index !== -1) { const { stepIndex, queue } = registrationData.items[index]