From 0df51076a926ec767ce6276cf5d9de05f56a3271 Mon Sep 17 00:00:00 2001 From: Stanislav Lysak Date: Fri, 1 Nov 2024 11:32:33 +0200 Subject: [PATCH 1/5] FET-1689: Name Renew Deeplink Params --- src/components/ProfileSnippet.tsx | 24 +++++++++++++++---- .../input/ExtendNames/ExtendNames-flow.tsx | 9 +++++-- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/components/ProfileSnippet.tsx b/src/components/ProfileSnippet.tsx index 148dc7b1c..25f6bb582 100644 --- a/src/components/ProfileSnippet.tsx +++ b/src/components/ProfileSnippet.tsx @@ -1,3 +1,4 @@ +import { useConnectModal } from '@rainbow-me/rainbowkit' import { useSearchParams } from 'next/navigation' import { useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' @@ -173,6 +174,14 @@ export const getUserDefinedUrl = (url?: string) => { return `` } +const parseNumericString = (time: string | null) => { + if (!time) return + + if (typeof +time === 'number' && !Number.isNaN(+time)) { + return +time + } +} + export const ProfileSnippet = ({ name, getTextRecord, @@ -192,6 +201,7 @@ export const ProfileSnippet = ({ const router = useRouterWithHistory() const { t } = useTranslation('common') + const { openConnectModal } = useConnectModal() const { usePreparedDataInput } = useTransactionFlow() const showExtendNamesInput = usePreparedDataInput('ExtendNames') const abilities = useAbilities({ name }) @@ -208,24 +218,30 @@ export const ProfileSnippet = ({ const searchParams = useSearchParams() - const renew = (searchParams.get('renew') ?? null) !== null const available = details.registrationStatus === 'available' const { canSelfExtend, canEdit } = abilities.data ?? {} + const renewSeconds = parseNumericString(searchParams.get('renew')) + useEffect(() => { - if (renew && !isConnected) { + if (renewSeconds && available) { return router.push(`/${name}/register`) } - if (renew && !available) { + if (renewSeconds && !available && !isConnected) { + return openConnectModal?.() + } + + if (renewSeconds && !available && isConnected) { showExtendNamesInput(`extend-names-${name}`, { names: [name], isSelf: canSelfExtend, + seconds: renewSeconds, }) } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isConnected, available, renew, name, canSelfExtend]) + }, [isConnected, available, renewSeconds, name, canSelfExtend, openConnectModal]) const ActionButton = useMemo(() => { if (button === 'extend') diff --git a/src/transaction-flow/input/ExtendNames/ExtendNames-flow.tsx b/src/transaction-flow/input/ExtendNames/ExtendNames-flow.tsx index 50343fdf2..ac308fec1 100644 --- a/src/transaction-flow/input/ExtendNames/ExtendNames-flow.tsx +++ b/src/transaction-flow/input/ExtendNames/ExtendNames-flow.tsx @@ -160,6 +160,7 @@ const NamesList = ({ names }: NamesListProps) => { type Data = { names: string[] + seconds?: number isSelf?: boolean } @@ -169,7 +170,11 @@ export type Props = { const minSeconds = ONE_DAY -const ExtendNames = ({ data: { names, isSelf }, dispatch, onDismiss }: Props) => { +const ExtendNames = ({ + data: { seconds: defaultSeconds = ONE_YEAR, names, isSelf }, + dispatch, + onDismiss, +}: Props) => { const { t } = useTranslation(['transactionFlow', 'common']) const { data: ethPrice } = useEthPrice() @@ -195,7 +200,7 @@ const ExtendNames = ({ data: { names, isSelf }, dispatch, onDismiss }: Props) => const decrementView = () => (viewIdx <= 0 ? onDismiss() : setViewIdx(viewIdx - 1)) const view = flow[viewIdx] - const [seconds, setSeconds] = useState(ONE_YEAR) + const [seconds, setSeconds] = useState(Math.max(defaultSeconds, ONE_YEAR)) const [durationType, setDurationType] = useState<'years' | 'date'>('years') const years = secondsToYears(seconds) From 30d96fa9ccf57a810bbdc1ea8d3922aeb3101806 Mon Sep 17 00:00:00 2001 From: Stanislav Lysak Date: Fri, 1 Nov 2024 11:56:27 +0200 Subject: [PATCH 2/5] test patch --- e2e/specs/stateless/extendNames.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/e2e/specs/stateless/extendNames.spec.ts b/e2e/specs/stateless/extendNames.spec.ts index fb2e7f9d8..725d87949 100644 --- a/e2e/specs/stateless/extendNames.spec.ts +++ b/e2e/specs/stateless/extendNames.spec.ts @@ -624,7 +624,7 @@ test('should be able to extend a single wrapped name using deep link', async ({ const homePage = makePageObject('HomePage') await homePage.goto() await login.connect() - await page.goto(`/${name}?renew`) + await page.goto(`/${name}?renew=123`) const timestamp = await profilePage.getExpiryTimestamp() @@ -670,7 +670,7 @@ test('should not be able to extend a name which is not registered', async ({ const homePage = makePageObject('HomePage') await homePage.goto() await login.connect() - await page.goto(`/${name}?renew`) + await page.goto(`/${name}?renew=123`) await expect(page.getByRole('heading', { name: `Register ${name}` })).toBeVisible() }) @@ -681,6 +681,6 @@ test('renew deep link should redirect to registration when not logged in', async const name = 'this-name-does-not-exist.eth' const homePage = makePageObject('HomePage') await homePage.goto() - await page.goto(`/${name}?renew`) + await page.goto(`/${name}?renew=123`) await expect(page.getByRole('heading', { name: `Register ${name}` })).toBeVisible() }) From f7eaf503ffb36b6cae0353d98ffc2ca59fcca042 Mon Sep 17 00:00:00 2001 From: Stanislav Lysak Date: Mon, 4 Nov 2024 09:45:13 +0200 Subject: [PATCH 3/5] extend modal patch --- src/components/ProfileSnippet.tsx | 42 +----------- .../pages/profile/[name]/Profile.tsx | 67 ++++++++++++++++++- 2 files changed, 67 insertions(+), 42 deletions(-) diff --git a/src/components/ProfileSnippet.tsx b/src/components/ProfileSnippet.tsx index 25f6bb582..503b7d28a 100644 --- a/src/components/ProfileSnippet.tsx +++ b/src/components/ProfileSnippet.tsx @@ -1,9 +1,6 @@ -import { useConnectModal } from '@rainbow-me/rainbowkit' -import { useSearchParams } from 'next/navigation' -import { useEffect, useMemo } from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import styled, { css } from 'styled-components' -import { useAccount } from 'wagmi' import { Button, mq, NametagSVG, Tag, Typography } from '@ensdomains/thorin' @@ -11,7 +8,6 @@ import FastForwardSVG from '@app/assets/FastForward.svg' import VerifiedPersonSVG from '@app/assets/VerifiedPerson.svg' import { useAbilities } from '@app/hooks/abilities/useAbilities' import { useBeautifiedName } from '@app/hooks/useBeautifiedName' -import { useNameDetails } from '@app/hooks/useNameDetails' import { useRouterWithHistory } from '@app/hooks/useRouterWithHistory' import { useTransactionFlow } from '../transaction-flow/TransactionFlowProvider' @@ -174,14 +170,6 @@ export const getUserDefinedUrl = (url?: string) => { return `` } -const parseNumericString = (time: string | null) => { - if (!time) return - - if (typeof +time === 'number' && !Number.isNaN(+time)) { - return +time - } -} - export const ProfileSnippet = ({ name, getTextRecord, @@ -201,12 +189,9 @@ export const ProfileSnippet = ({ const router = useRouterWithHistory() const { t } = useTranslation('common') - const { openConnectModal } = useConnectModal() const { usePreparedDataInput } = useTransactionFlow() const showExtendNamesInput = usePreparedDataInput('ExtendNames') const abilities = useAbilities({ name }) - const details = useNameDetails({ name }) - const { isConnected } = useAccount() const beautifiedName = useBeautifiedName(name) @@ -216,33 +201,8 @@ export const ProfileSnippet = ({ const location = getTextRecord?.('location')?.value const recordName = getTextRecord?.('name')?.value - const searchParams = useSearchParams() - - const available = details.registrationStatus === 'available' - const { canSelfExtend, canEdit } = abilities.data ?? {} - const renewSeconds = parseNumericString(searchParams.get('renew')) - - useEffect(() => { - if (renewSeconds && available) { - return router.push(`/${name}/register`) - } - - if (renewSeconds && !available && !isConnected) { - return openConnectModal?.() - } - - if (renewSeconds && !available && isConnected) { - showExtendNamesInput(`extend-names-${name}`, { - names: [name], - isSelf: canSelfExtend, - seconds: renewSeconds, - }) - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isConnected, available, renewSeconds, name, canSelfExtend, openConnectModal]) - const ActionButton = useMemo(() => { if (button === 'extend') return ( diff --git a/src/components/pages/profile/[name]/Profile.tsx b/src/components/pages/profile/[name]/Profile.tsx index 68847c87f..035c54b32 100644 --- a/src/components/pages/profile/[name]/Profile.tsx +++ b/src/components/pages/profile/[name]/Profile.tsx @@ -1,9 +1,11 @@ import Head from 'next/head' -import { useEffect, useMemo } from 'react' +import { useSearchParams } from 'next/navigation' +import { useEffect, useMemo, useState } from 'react' import { Trans, useTranslation } from 'react-i18next' import styled, { css } from 'styled-components' import { match } from 'ts-pattern' import { useAccount } from 'wagmi' +import { useConnectModal } from '@rainbow-me/rainbowkit' import { Banner, CheckCircleSVG, Typography } from '@ensdomains/thorin' @@ -16,6 +18,7 @@ import { useProtectedRoute } from '@app/hooks/useProtectedRoute' import { useQueryParameterState } from '@app/hooks/useQueryParameterState' import { useRouterWithHistory } from '@app/hooks/useRouterWithHistory' import { Content, ContentWarning } from '@app/layouts/Content' +import { useTransactionFlow } from '@app/transaction-flow/TransactionFlowProvider' import { OG_IMAGE_URL } from '@app/utils/constants' import { shouldRedirect } from '@app/utils/shouldRedirect' import { formatFullExpiry, makeEtherscanLink } from '@app/utils/utils' @@ -104,6 +107,67 @@ export const NameAvailableBanner = ({ ) } +function useRenew(name: string) { + const parseNumericString = (time: string | null) => { + if (!time) return + + if (typeof +time === 'number' && !Number.isNaN(+time)) { + return +time + } + } + + const [opened, setOpened] = useState(false) + const router = useRouterWithHistory() + + const { registrationStatus, isLoading } = useNameDetails({ name }) + const abilities = useAbilities({ name }) + const searchParams = useSearchParams() + const { isConnected, isDisconnected } = useAccount() + const { usePreparedDataInput } = useTransactionFlow() + const { openConnectModal } = useConnectModal() + const showExtendNamesInput = usePreparedDataInput('ExtendNames') + + const { data: { canSelfExtend } = {} } = abilities + const isAvailableName = registrationStatus === 'available' + const renewSeconds = parseNumericString(searchParams.get('renew')) + + useEffect(() => { + if (opened || !renewSeconds || isLoading) return + + if (isAvailableName) { + setOpened(true) + router.push(`/${name}/register`) + return + } + + if (!isAvailableName && isDisconnected) { + setOpened(true) + openConnectModal?.() + return + } + + if (!isAvailableName && isConnected) { + setOpened(true) + showExtendNamesInput(`extend-names-${name}`, { + names: [name], + isSelf: canSelfExtend, + seconds: renewSeconds, + }) + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + isAvailableName, + isLoading, + isConnected, + isDisconnected, + name, + canSelfExtend, + renewSeconds, + opened, + ]) +} + const ProfileContent = ({ isSelf, isLoading: parentIsLoading, name }: Props) => { const router = useRouterWithHistory() const { t } = useTranslation('profile') @@ -181,6 +245,7 @@ const ProfileContent = ({ isSelf, isLoading: parentIsLoading, name }: Props) => const abilities = useAbilities({ name: normalisedName }) + useRenew(normalisedName) // hook for redirecting to the correct profile url // profile.decryptedName fetches labels from NW/subgraph // normalisedName fetches labels from localStorage From b9719617a1a5cf11d4e17f6d41fe1c33ee4e2aa7 Mon Sep 17 00:00:00 2001 From: Stanislav Lysak Date: Mon, 4 Nov 2024 10:03:37 +0200 Subject: [PATCH 4/5] prettier format --- src/components/pages/profile/[name]/Profile.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/pages/profile/[name]/Profile.tsx b/src/components/pages/profile/[name]/Profile.tsx index 035c54b32..e42be984c 100644 --- a/src/components/pages/profile/[name]/Profile.tsx +++ b/src/components/pages/profile/[name]/Profile.tsx @@ -1,3 +1,4 @@ +import { useConnectModal } from '@rainbow-me/rainbowkit' import Head from 'next/head' import { useSearchParams } from 'next/navigation' import { useEffect, useMemo, useState } from 'react' @@ -5,7 +6,6 @@ import { Trans, useTranslation } from 'react-i18next' import styled, { css } from 'styled-components' import { match } from 'ts-pattern' import { useAccount } from 'wagmi' -import { useConnectModal } from '@rainbow-me/rainbowkit' import { Banner, CheckCircleSVG, Typography } from '@ensdomains/thorin' From 42ddd5cf305d29060ae392ba113222349e6b1a1d Mon Sep 17 00:00:00 2001 From: Stanislav Lysak Date: Mon, 4 Nov 2024 12:07:20 +0200 Subject: [PATCH 5/5] increase timeout --- e2e/specs/stateless/extendNames.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/specs/stateless/extendNames.spec.ts b/e2e/specs/stateless/extendNames.spec.ts index 725d87949..459ca0336 100644 --- a/e2e/specs/stateless/extendNames.spec.ts +++ b/e2e/specs/stateless/extendNames.spec.ts @@ -92,7 +92,7 @@ test('should be able to extend multiple names on the address page', async ({ await page.waitForLoadState('networkidle') await expect(page.getByText('Your "Extend names" transaction was successful')).toBeVisible({ - timeout: 10000, + timeout: 15000, }) await subgraph.sync()