Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add registration tracking events for .eth and TLD (.box) funnel #840

Open
wants to merge 35 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
9e7f8e5
Add registration tracking events for .eth funnel
nhohb Aug 30, 2024
ec53030
add estimatedTotal to pricing callback
storywithoutend Aug 30, 2024
faa5383
Refactor useRegistrationEventTracker and add tracking for "Payment se…
nhohb Aug 30, 2024
e7e4e33
Revert strict mode for development, add condition for commit wallet o…
nhohb Sep 4, 2024
7043e84
Add track registration event for TLD (.box)
nhohb Sep 4, 2024
fdcfeab
Add data for baseMockData on Pricing test
nhohb Sep 4, 2024
33be2fe
Add unit test for useRegistrationEventTracker
nhohb Sep 5, 2024
5a7ceca
Change registration tracking event name to snake case
nhohb Sep 6, 2024
95adbe7
Add e2e test for registration tracking event on registerName
nhohb Sep 6, 2024
e751725
Add e2e test for registration tracking events
nhohb Sep 10, 2024
eb52980
add some sample code
storywithoutend Sep 10, 2024
7ad1666
Update event tracker hook based on PR feedback
nhohb Sep 11, 2024
79d9b4b
Wrap event tracker expect code with test.step
nhohb Sep 12, 2024
d245e6b
Add e2e test for box registration and update search input wait time u…
nhohb Sep 13, 2024
eb690f3
Merge branch 'main' of github.com:ensdomains/ens-app-v3 into feat/FET…
nhohb Sep 13, 2024
8510aca
Remove all page.pause on registerName test
nhohb Sep 13, 2024
a25cbdf
reorder stateless tests
storywithoutend Sep 20, 2024
7615aa6
Move listen console event to fixtures, update analytics props for ref…
nhohb Sep 20, 2024
78eb780
Merge branch 'feat/FET-1177-add-registration-tracking-events' of gith…
nhohb Sep 20, 2024
8f0f4ac
Update e2e test for registerName based on David's improvement console…
nhohb Sep 26, 2024
680f8bc
Merge branch 'main' of github.com:ensdomains/ens-app-v3 into feat/FET…
nhohb Sep 26, 2024
ab690fe
Update e2e for box.spec based on new consoleListener
nhohb Sep 26, 2024
1d92628
Update regex for consoleListener
nhohb Sep 26, 2024
b79df4f
Merge branch 'main' into feat/FET-1177-add-registration-tracking-events
storywithoutend Sep 30, 2024
ab526e0
fix build issues
storywithoutend Sep 30, 2024
740fafc
make some code fixes
storywithoutend Sep 30, 2024
4884d4d
fix merge mistaks in registerName
storywithoutend Sep 30, 2024
34d39ee
fix registerName bug
storywithoutend Sep 30, 2024
ad01f0b
change an expect in registerName to get more info
storywithoutend Sep 30, 2024
7b2e76b
fix regex
storywithoutend Sep 30, 2024
80525bc
remove non serial test from serial scope in registerName spec
storywithoutend Sep 30, 2024
68edbb1
update test
storywithoutend Oct 1, 2024
62733f4
Merge branch 'main' into feat/FET-1177-add-registration-tracking-events
sugh01 Oct 1, 2024
7efbc80
Merge branch 'main' into feat/FET-1177-add-registration-tracking-events
sugh01 Oct 5, 2024
167aa22
fix merge conflict
sugh01 Oct 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions e2e/specs/stateless/registerName.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import {
* get stuck looking for the complete button
*/

const chain = 'localhost'
const trackEventPrefix = 'Event triggered on local development'

test.describe.serial('normal registration', () => {
const name = `registration-normal-${Date.now()}.eth`

Expand All @@ -36,6 +39,16 @@ test.describe.serial('normal registration', () => {
account: createAccounts().getAddress('user') as `0x${string}`,
})

const events: string[] = []

page.on('console', (msg) => {
const message = msg.text()

if (message.includes(trackEventPrefix)) {
events.push(message.replace(trackEventPrefix, '').trim())
}
})

const homePage = makePageObject('HomePage')
const registrationPage = makePageObject('RegistrationPage')
const transactionModal = makePageObject('TransactionModal')
Expand All @@ -44,11 +57,18 @@ test.describe.serial('normal registration', () => {

await homePage.goto()
await login.connect()
await expect(events).toContain(JSON.stringify({ type: 'home_page', chain }))

// should redirect to registration page
await homePage.searchInput.fill(name)
await homePage.searchInput.press('Enter')
await expect(events).toContain(
JSON.stringify({ type: 'start_searching', chain, props: { keyword: name } }),
)
await expect(page.getByRole('heading', { name: `Register ${name}` })).toBeVisible()
nhohb marked this conversation as resolved.
Show resolved Hide resolved
await expect(events).toContain(
JSON.stringify({ type: 'search_selected', chain, props: { keyword: name } }),
)

// should have payment choice ethereum checked and show primary name setting as checked
await expect(page.getByTestId('payment-choice-ethereum')).toBeChecked()
Expand Down
10 changes: 9 additions & 1 deletion src/components/@molecules/SearchInput/SearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ import { UsePriceQueryKey } from '@app/hooks/ensjs/public/usePrice'
import { UseWrapperDataQueryKey } from '@app/hooks/ensjs/public/useWrapperData'
import { useLocalStorage } from '@app/hooks/useLocalStorage'
import { createQueryKey } from '@app/hooks/useQueryOptions'
import { useRegistrationEventTracker } from '@app/hooks/useRegistrationEventTracker'
import { useRouterWithHistory } from '@app/hooks/useRouterWithHistory'
import { useValidate, validate } from '@app/hooks/useValidate'
import { useElementSize } from '@app/hooks/useWindowSize'
import { CreateQueryKey, GenericQueryKey } from '@app/types'
import { CreateQueryKey, GenericQueryKey, PlausibleProps, PlausibleType } from '@app/types'
import { useBreakpoint } from '@app/utils/BreakpointProvider'
import { getRegistrationStatus } from '@app/utils/registrationStatus'
import { thread, yearsToSeconds } from '@app/utils/utils'
Expand Down Expand Up @@ -311,6 +312,7 @@ type CreateSearchHandlerProps = {
setHistory: Dispatch<SetStateAction<HistoryItem[]>>
setInputVal: Dispatch<SetStateAction<string>>
queryClient: QueryClient
trackRegistrationEvent: (type: PlausibleType, customProps?: PlausibleProps | undefined) => void
}

const createSearchHandler =
Expand All @@ -323,6 +325,7 @@ const createSearchHandler =
setHistory,
setInputVal,
queryClient,
trackRegistrationEvent,
}: CreateSearchHandlerProps): SearchHandler =>
(index: number) => {
if (index === -1) return
Expand All @@ -338,6 +341,8 @@ const createSearchHandler =
{ lastAccessed: Date.now(), nameType, text, isValid: selectedItem.isValid },
])

trackRegistrationEvent('search_selected', { keyword: text })

const path = getRouteForSearchItem({ address, chainId, queryClient, selectedItem })
nhohb marked this conversation as resolved.
Show resolved Hide resolved
setInputVal('')
searchInputRef.current?.blur()
Expand Down Expand Up @@ -654,6 +659,7 @@ export const SearchInput = ({ size = 'extraLarge' }: { size?: 'medium' | 'extraL
const handleFocusOut = useCallback(() => toggle(false), [toggle])

const dropdownItems = useBuildDropdownItems(inputVal, history)
const { trackRegistrationEvent } = useRegistrationEventTracker()

// eslint-disable-next-line react-hooks/exhaustive-deps
const handleSearch = useCallback(
Expand All @@ -666,6 +672,7 @@ export const SearchInput = ({ size = 'extraLarge' }: { size?: 'medium' | 'extraL
searchInputRef,
setHistory,
setInputVal,
trackRegistrationEvent,
}),
[address, chainId, dropdownItems, queryClient, router, searchInputRef, setHistory, setInputVal],
)
Expand All @@ -689,6 +696,7 @@ export const SearchInput = ({ size = 'extraLarge' }: { size?: 'medium' | 'extraL
setInputVal(val)
setUsingPlaceholder(true)
debouncer(() => setUsingPlaceholder(false))
debouncer(() => trackRegistrationEvent('start_searching', { keyword: val }))
}

const SearchInputElement = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { useAddRecentTransaction } from '@app/hooks/transactions/useAddRecentTra
import { useRecentTransactions } from '@app/hooks/transactions/useRecentTransactions'
import { useIsSafeApp } from '@app/hooks/useIsSafeApp'
import { useQueryOptions } from '@app/hooks/useQueryOptions'
import { useRegistrationEventTracker } from '@app/hooks/useRegistrationEventTracker'
import {
ManagedDialogProps,
TransactionFlowAction,
Expand Down Expand Up @@ -295,7 +296,7 @@ export const TransactionStageModal = ({
}: ManagedDialogProps) => {
const { t } = useTranslation()
const chainName = useChainName()

const { trackRegistrationEvent } = useRegistrationEventTracker()
const { data: isSafeApp, isLoading: safeAppStatusLoading } = useIsSafeApp()
const { data: connectorClient } = useConnectorClient<ConfigWithEns>()
const client = useClient()
Expand Down Expand Up @@ -476,7 +477,15 @@ export const TransactionStageModal = ({
!!requestError ||
isTransactionRequestCachedData
}
onClick={() => sendTransaction(request!)}
onClick={() => {
sendTransaction(request!)

if (['commitName', 'registerName'].includes(actionName)) {
trackRegistrationEvent(
actionName === 'commitName' ? 'commit_wallet_opened' : 'finish_wallet_opened',
)
}
}}
data-testid="transaction-modal-confirm-button"
>
{t('transaction.dialog.confirm.openWallet')}
Expand All @@ -496,6 +505,8 @@ export const TransactionStageModal = ({
transactionLoading,
request,
isTransactionRequestCachedData,
trackRegistrationEvent,
actionName,
])

const stepStatus = useMemo(() => {
Expand Down
9 changes: 8 additions & 1 deletion src/components/pages/dotbox/[name]/DotBoxRegistration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import DotBoxLogoSVG from '@app/assets/dotbox/DotBoxLogo.svg'
import OutlinkSVG from '@app/assets/Outlink.svg'
import { Card } from '@app/components/Card'
import { useDotBoxAvailabilityOffchain } from '@app/hooks/dotbox/useDotBoxAvailabilityOffchain'
import { useRegistrationEventTracker } from '@app/hooks/useRegistrationEventTracker'
import { useRouterWithHistory } from '@app/hooks/useRouterWithHistory'
import { Content } from '@app/layouts/Content'
import { shouldRedirect } from '@app/utils/shouldRedirect'
Expand Down Expand Up @@ -104,6 +105,8 @@ export const DotBoxRegistration = () => {
const dotBoxResult = useDotBoxAvailabilityOffchain({ name })
const nameStatus = dotBoxResult?.data?.data.status

const { trackRegistrationEvent } = useRegistrationEventTracker()

shouldRedirect(router, 'DotBoxRegistration.tsx', `/profile/${name}`, dotBoxResult)

const { t } = useTranslation('dnssec')
Expand Down Expand Up @@ -159,7 +162,11 @@ export const DotBoxRegistration = () => {
.with({ isLoading: true }, () => <Spinner size="medium" color="accent" />)
.with({ isLoading: false, nameStatus: 'AVAILABLE' }, () => (
<a href={dotBoxResult?.data?.data.href} target="_blank" rel="noreferrer">
<Button width="45" size="small">
<Button
width="45"
size="small"
onClick={() => trackRegistrationEvent('registration_started')}
>
<OutlinkInner>
Register on my.box
<OutlinkSVG />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ProfileRecord } from '@app/constants/profileRecordOptions'
import { useContractAddress } from '@app/hooks/chain/useContractAddress'
import { usePrimaryName } from '@app/hooks/ensjs/public/usePrimaryName'
import { useNameDetails } from '@app/hooks/useNameDetails'
import { useRegistrationEventTracker } from '@app/hooks/useRegistrationEventTracker'
import useRegistrationReducer from '@app/hooks/useRegistrationReducer'
import { useResolverExists } from '@app/hooks/useResolverExists'
import { useRouterWithHistory } from '@app/hooks/useRouterWithHistory'
Expand Down Expand Up @@ -122,6 +123,7 @@ const Registration = ({ nameDetails, isLoading }: Props) => {

const labelTooLong = isLabelTooLong(normalisedName)
const { dispatch, item } = useRegistrationReducer(selected)
const { trackRegistrationEvent, trackPaymentSelectedEvent } = useRegistrationEventTracker()
const step = item.queue[item.stepIndex]

const keySuffix = `${nameDetails.normalisedName}-${address}`
Expand All @@ -142,7 +144,16 @@ const Registration = ({ nameDetails, isLoading }: Props) => {
seconds,
reverseRecord,
paymentMethodChoice,
estimatedTotal,
ethPrice,
}: RegistrationStepData['pricing']) => {
trackPaymentSelectedEvent({
duration: seconds,
paymentMethod: paymentMethodChoice,
estimatedTotal,
ethPrice,
})

if (paymentMethodChoice === PaymentMethod.moonpay) {
initiateMoonpayRegistrationMutation.mutate(secondsToYears(seconds))
return
Expand Down Expand Up @@ -199,6 +210,11 @@ const Registration = ({ nameDetails, isLoading }: Props) => {
dispatch({ name: back ? 'decreaseStep' : 'increaseStep', selected })
}

const infoCallback = ({ back }: BackObj) => {
genericCallback({ back })
trackRegistrationEvent('commit_started')
}

const transactionsCallback = useCallback(
({ back, resetSecret }: BackObj & { resetSecret?: boolean }) => {
if (resetSecret) {
Expand Down Expand Up @@ -301,7 +317,7 @@ const Registration = ({ nameDetails, isLoading }: Props) => {
<Info
name={normalisedName}
registrationData={item}
callback={genericCallback}
callback={infoCallback}
onProfileClick={infoProfileCallback}
/>
))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ describe('ActionButton', () => {
seconds: yearsToSeconds(1),
balance: { value: 100n } as any,
totalRequiredBalance: 1n,
estimatedTotal: 1n,
ethPrice: 1n,
}

it('should have disabled "Next" button if no choice has been made', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { ConnectButton } from '@app/components/ConnectButton'
import { useAccountSafely } from '@app/hooks/account/useAccountSafely'
import { useContractAddress } from '@app/hooks/chain/useContractAddress'
import { useEstimateFullRegistration } from '@app/hooks/gasEstimation/useEstimateRegistration'
import { useEthPrice } from '@app/hooks/useEthPrice'
import { useBreakpoint } from '@app/utils/BreakpointProvider'
import { ONE_DAY, ONE_YEAR } from '@app/utils/time'

Expand Down Expand Up @@ -380,6 +381,8 @@ export type ActionButtonProps = {
seconds: number
balance: GetBalanceData | undefined
totalRequiredBalance?: bigint
estimatedTotal?: bigint
ethPrice?: bigint
}

export const ActionButton = (props: ActionButtonProps) => {
Expand All @@ -404,20 +407,35 @@ export const ActionButton = (props: ActionButtonProps) => {
reverseRecord,
seconds,
paymentMethodChoice,
estimatedTotal,
ethPrice,
callback,
}) => (
<Button
loading={initiateMoonpayRegistrationMutation.isPending}
data-testid="next-button"
onClick={() => callback({ reverseRecord, seconds, paymentMethodChoice })}
onClick={() =>
callback({
reverseRecord,
seconds,
paymentMethodChoice,
estimatedTotal,
ethPrice,
})
}
disabled={!paymentMethodChoice || initiateMoonpayRegistrationMutation.isPending}
>
{t('action.next', { ns: 'common' })}
</Button>
),
)
.with(
P.when((_props) => typeof _props.balance?.value !== 'bigint' || !_props.totalRequiredBalance),
P.when(
(_props) =>
typeof _props.balance?.value !== 'bigint' ||
!_props.totalRequiredBalance ||
!_props.ethPrice,
),
() => (
<Button data-testid="next-button" disabled>
{t('loading', { ns: 'common' })}
Expand All @@ -438,15 +456,25 @@ export const ActionButton = (props: ActionButtonProps) => {
</Button>
),
)
.otherwise(({ reverseRecord, seconds, paymentMethodChoice, callback }) => (
<Button
data-testid="next-button"
onClick={() => callback({ reverseRecord, seconds, paymentMethodChoice })}
disabled={!paymentMethodChoice}
>
{t('action.next', { ns: 'common' })}
</Button>
))
.otherwise(
({ reverseRecord, seconds, paymentMethodChoice, estimatedTotal, ethPrice, callback }) => (
<Button
data-testid="next-button"
onClick={() =>
callback({
reverseRecord,
seconds,
paymentMethodChoice,
estimatedTotal,
ethPrice,
})
}
disabled={!paymentMethodChoice}
>
{t('action.next', { ns: 'common' })}
</Button>
),
)
}

export type PricingProps = {
Expand Down Expand Up @@ -484,6 +512,7 @@ const Pricing = ({
const { address } = useAccountSafely()
const { data: balance } = useBalance({ address })
const resolverAddress = useContractAddress({ contract: 'ensPublicResolver' })
const { data: ethPrice } = useEthPrice()

const [seconds, setSeconds] = useState(() => registrationData.seconds ?? ONE_YEAR)

Expand Down Expand Up @@ -535,6 +564,8 @@ const Pricing = ({
const totalRequiredBalance = durationRequiredBalance
? durationRequiredBalance + (premiumFee || 0n) + (estimatedGasFee || 0n)
: 0n
const estimatedTotal =
(totalDurationBasedFee || 0n) + (premiumFee || 0n) + (estimatedGasFee || 0n)

const previousYearlyFee = usePreviousDistinct(yearlyFee) || 0n

Expand Down Expand Up @@ -594,6 +625,8 @@ const Pricing = ({
seconds,
balance,
totalRequiredBalance,
estimatedTotal,
ethPrice,
}}
/>
</MobileFullWidth>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Button, CountdownCircle, Dialog, Heading, mq, Spinner } from '@ensdomai
import MobileFullWidth from '@app/components/@atoms/MobileFullWidth'
import { Card } from '@app/components/Card'
import { useExistingCommitment } from '@app/hooks/registration/useExistingCommitment'
import { useRegistrationEventTracker } from '@app/hooks/useRegistrationEventTracker'
import useRegistrationParams from '@app/hooks/useRegistrationParams'
import { CenteredTypography } from '@app/transaction-flow/input/ProfileEditor/components/CenteredTypography'
import { createTransactionItem } from '@app/transaction-flow/transaction'
Expand Down Expand Up @@ -93,6 +94,7 @@ type Props = {

const Transactions = ({ registrationData, name, callback, onStart }: Props) => {
const { t } = useTranslation('register')
const { trackRegistrationEvent } = useRegistrationEventTracker()

const { address } = useAccount()
const keySuffix = `${name}-${address}`
Expand Down Expand Up @@ -140,6 +142,8 @@ const Transactions = ({ registrationData, name, callback, onStart }: Props) => {
autoClose: true,
resumeLink: `/register/${name}`,
})

trackRegistrationEvent('finish_started')
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's 2 register_started events. Can you double check if both are needed? This is why I suggested that we clear the events variable and check if the event has been called multiple times in registerName.spec.ts

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've analyzed the flow and determined the following:

  • If a user completes a commit transaction, the registerTx variable will be undefined.
    The makeRegisterNameFlow function will be called.

  • If the user refreshes the page, the registerTx variable will have a value, and the showRegisterTransaction function will be called.


const showCommitTransaction = () => {
Expand All @@ -148,6 +152,8 @@ const Transactions = ({ registrationData, name, callback, onStart }: Props) => {

const showRegisterTransaction = () => {
resumeTransactionFlow(registerKey)

trackRegistrationEvent('finish_started')
}

const resetTransactions = () => {
Expand Down
Loading
Loading