Skip to content

Commit

Permalink
Merge branch 'main' into fet980-ownership-tab
Browse files Browse the repository at this point in the history
  • Loading branch information
storywithoutend committed Sep 20, 2023
2 parents 1ad034c + 76ca291 commit 0fbb5ac
Show file tree
Hide file tree
Showing 22 changed files with 217 additions and 141 deletions.
34 changes: 2 additions & 32 deletions e2e/specs/stateless/profileEditor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand All @@ -35,6 +32,7 @@ const DEFAULT_RECORDS = {
value: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC',
},
],
contentHash: 'ipfs://bafybeico3uuyj3vphxpvbowchdwjlrlrh62awxscrnii7w7flu5z6fk77y',
}

test.describe('unwrapped', () => {
Expand Down Expand Up @@ -172,6 +170,7 @@ test.describe('unwrapped', () => {
'ETC_LEGACY0x3C4...293BC',
)
await expect(profilePage.record('text', 'email')).toHaveText('[email protected]')
await expect(profilePage.contentHash()).toContainText('ipfs://bafybeic...')

await profilePage.editProfileButton.click()
await profilePage.profileEditor.getByTestId('warning-overlay-next-button').click()
Expand Down Expand Up @@ -324,35 +323,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,
Expand Down
4 changes: 4 additions & 0 deletions playwright/pageObjects/profilePage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,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."
},
"roles": {
Expand Down
40 changes: 32 additions & 8 deletions src/components/@molecules/FaucetBanner.tsx
Original file line number Diff line number Diff line change
@@ -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'

Expand All @@ -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'
Expand Down Expand Up @@ -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 {
Expand All @@ -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 = (
<BannerWrapper>
Expand All @@ -69,9 +82,13 @@ const FaucetBanner = () => {
icon={<EthSVG />}
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),
})}
</StyledBanner>
</BannerWrapper>
)
Expand All @@ -80,11 +97,18 @@ const FaucetBanner = () => {
<Dialog open={dialogOpen} onClose={closeDialog} onDismiss={closeDialog} variant="blank">
{dialogStage === 'default' ? (
<>
<Dialog.Heading title="Faucet Claim" subtitle="Claim once every 90 days" />
<Dialog.Heading
title="Faucet Claim"
subtitle={`Claim once every ${msToDays(data.interval)} days`}
/>
<InnerDialog>
<DisplayItems
displayItems={[
{ label: 'Value', value: '0.25 goETH', useRawLabel: true },
{
label: 'Value',
value: `${amount} ${chainEthTicker(chainName)}`,
useRawLabel: true,
},
{ label: 'Address', value: address || '', type: 'address', useRawLabel: true },
]}
/>
Expand Down
15 changes: 10 additions & 5 deletions src/components/ProfileSnippet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -159,21 +161,21 @@ 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
}) => {
const router = useRouterWithHistory()
const { t } = useTranslation('common')

const abilities = useAbilities(name)

const { prepareDataInput } = useTransactionFlow()
const showExtendNamesInput = prepareDataInput('ExtendNames')

Expand All @@ -194,7 +196,10 @@ export const ProfileSnippet = ({
prefix={<FastForwardSVG />}
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' })}
Expand All @@ -221,7 +226,7 @@ export const ProfileSnippet = ({
</Button>
)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [button, name, canEdit])
}, [button, name, abilities.data])

return (
<Container $banner={banner} data-testid="profile-snippet">
Expand All @@ -231,7 +236,7 @@ export const ProfileSnippet = ({
label={name}
name={name}
network={network}
noCache={canEdit}
noCache={abilities.data?.canEdit}
/>
<ButtonStack>
{ActionButton && <DetailButtonWrapper>{ActionButton}</DetailButtonWrapper>}
Expand Down
17 changes: 14 additions & 3 deletions src/components/pages/profile/ProfileButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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) {
Expand All @@ -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 (
<RecordItem
Expand Down
26 changes: 26 additions & 0 deletions src/components/pages/profile/ProfileDetails.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -95,3 +108,16 @@ describe('onwershipInfoCalc', () => {
])
})
})

describe('ProfileDetails', () => {
it('should show content hash if there is valid contenthash', () => {
render(<ProfileDetails name="test.eth" expiryDate={undefined} textRecords={[]} addresses={[]} pccExpired={false} owners={[]} actions={[]} contentHash="ipfs://QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco" />)
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(<ProfileDetails name="test.eth" expiryDate={undefined} textRecords={[]} addresses={[]} pccExpired={false} owners={[]} actions={[]} contentHash={{}} />)
expect(screen.queryByTestId('other-profile-button-contenthash')).toBeNull()
})
})
6 changes: 6 additions & 0 deletions src/components/pages/profile/ProfileDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import supportedProfileItems from '@app/constants/supportedGeneralRecordKeys.jso
import supportedTexts from '@app/constants/supportedSocialRecordKeys.json'
import useOwners from '@app/hooks/useOwners'
import { useProfileActions } from '@app/hooks/useProfileActions'
import { ContentHash } from '@app/types'
import { useBreakpoint } from '@app/utils/BreakpointProvider'
import { contentHashToString } from '@app/utils/contenthash'
import { checkETH2LDFromName, formatExpiry } from '@app/utils/utils'

import {
Expand Down Expand Up @@ -282,6 +284,7 @@ export const ownershipInfoCalc = (
export const ProfileDetails = ({
textRecords = [],
addresses = [],
contentHash,
expiryDate,
pccExpired,
owners,
Expand All @@ -292,6 +295,7 @@ export const ProfileDetails = ({
}: {
textRecords: Array<Record<'key' | 'value', string>>
addresses: Array<Record<'key' | 'value', string>>
contentHash?: ContentHash
expiryDate: Date | undefined
pccExpired: boolean
owners: ReturnType<typeof useOwners>
Expand All @@ -302,6 +306,7 @@ export const ProfileDetails = ({
}) => {
const breakpoint = useBreakpoint()

const _contentHash = contentHashToString(contentHash)
const otherRecords = [
...textRecords
.filter(
Expand All @@ -310,6 +315,7 @@ export const ProfileDetails = ({
!supportedProfileItems.includes(x.key.toLowerCase()),
)
.map((x) => ({ ...x, type: 'text' })),
...(_contentHash ? [{ key: 'contenthash', type: 'contenthash', value: _contentHash }] : []),
]

const mappedOwners = ownershipInfoCalc(name, pccExpired, owners, gracePeriodEndDate, expiryDate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion src/components/pages/profile/[name]/registration/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export type BackObj = { back: boolean }

export type RegistrationData = Prettify<UnionToIntersection<RegistrationStepData[RegistrationStep]>>

export type SelectedItemProperties = { address: string; name: string }
export type SelectedItemProperties = { address: string; name: string; chainId: number }

export type RegistrationReducerDataItem = Prettify<
Omit<RegistrationData, 'paymentMethodChoice'> & {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof useRegistrationReducer>['dispatch'],
normalisedName: string,
selected: { name: string; address: string },
selected: SelectedItemProperties,
item: ReturnType<typeof useRegistrationReducer>['item'],
) => {
const chainId = useChainId()
Expand Down
Loading

0 comments on commit 0fbb5ac

Please sign in to comment.