From fabcdf261aef3486f50198821abf31459f249a68 Mon Sep 17 00:00:00 2001 From: Renan Date: Thu, 19 Sep 2024 11:30:08 +0200 Subject: [PATCH 1/5] Use same tracking values as in start --- .../hooks/use-record-signup-complete.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/client/landing/stepper/hooks/use-record-signup-complete.ts b/client/landing/stepper/hooks/use-record-signup-complete.ts index 9fbf972986507..4302ecf111a17 100644 --- a/client/landing/stepper/hooks/use-record-signup-complete.ts +++ b/client/landing/stepper/hooks/use-record-signup-complete.ts @@ -3,11 +3,16 @@ import { useSelect } from '@wordpress/data'; import { useCallback } from 'react'; import { USER_STORE, ONBOARD_STORE } from 'calypso/landing/stepper/stores'; import { SIGNUP_DOMAIN_ORIGIN, recordSignupComplete } from 'calypso/lib/analytics/signup'; +import { useStore } from 'calypso/state'; +import isUserRegistrationDaysWithinRange from 'calypso/state/selectors/is-user-registration-days-within-range'; +import { getSignupDependencyStore } from 'calypso/state/signup/dependency-store/selectors'; import { useSite } from './use-site'; import type { UserSelect, OnboardSelect } from '@automattic/data-stores'; export const useRecordSignupComplete = ( flow: string | null ) => { const site = useSite(); + const store = useStore(); + const state = store.getState(); const siteId = site?.ID || null; const theme = site?.options?.theme_slug || ''; const { domainCartItem, planCartItem, siteCount, selectedDomain } = useSelect( ( select ) => { @@ -18,16 +23,16 @@ export const useRecordSignupComplete = ( flow: string | null ) => { selectedDomain: ( select( ONBOARD_STORE ) as OnboardSelect ).getSelectedDomain(), }; }, [] ); + const isNewishUser = isUserRegistrationDaysWithinRange( state, null, 0, 7 ); + const dependencies = getSignupDependencyStore( state ); return useCallback( () => { - // FIXME: once moving to the Stepper version of User step, - // wire the value of `isNewUser()` from the user store. - const isNewUser = ! siteCount; + const isNewUser = !! ( dependencies && dependencies.username ); - // FIXME: - // currently it's impossible to derive this data since it requires - // the length of registration, so I use isNewUser here as an approximation - const isNew7DUserSite = isNewUser; + const isNew7DUserSite = !! ( + isNewUser || + ( isNewishUser && dependencies && dependencies?.siteSlug && siteCount && siteCount <= 1 ) + ); // Domain product slugs can be a domain purchases like dotcom_domain or dotblog_domain or a mapping like domain_mapping // When purchasing free subdomains the product_slugs is empty (since there is no actual produce being purchased) From 54f2da0b2fe176cc9f5a617a2d5140519caf8fe4 Mon Sep 17 00:00:00 2001 From: escapemanuele Date: Thu, 19 Sep 2024 16:26:45 +0200 Subject: [PATCH 2/5] Pass destinationState to signup completion tracking --- .../processing-step/index.tsx | 2 +- .../hooks/use-record-signup-complete.ts | 98 ++++++++++--------- 2 files changed, 52 insertions(+), 48 deletions(-) diff --git a/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/index.tsx b/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/index.tsx index bb110507806dc..d4d9efbf05b40 100644 --- a/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/index.tsx +++ b/client/landing/stepper/declarative-flow/internals/steps-repository/processing-step/index.tsx @@ -104,7 +104,7 @@ const ProcessingStep: React.FC< ProcessingStepProps > = function ( props ) { if ( availableFlows[ flow ] ) { availableFlows[ flow ]().then( ( flowExport ) => { if ( flowExport.default.isSignupFlow ) { - recordSignupComplete(); + recordSignupComplete( { ...destinationState } ); } } ); } diff --git a/client/landing/stepper/hooks/use-record-signup-complete.ts b/client/landing/stepper/hooks/use-record-signup-complete.ts index 4302ecf111a17..55875137b8295 100644 --- a/client/landing/stepper/hooks/use-record-signup-complete.ts +++ b/client/landing/stepper/hooks/use-record-signup-complete.ts @@ -3,16 +3,13 @@ import { useSelect } from '@wordpress/data'; import { useCallback } from 'react'; import { USER_STORE, ONBOARD_STORE } from 'calypso/landing/stepper/stores'; import { SIGNUP_DOMAIN_ORIGIN, recordSignupComplete } from 'calypso/lib/analytics/signup'; -import { useStore } from 'calypso/state'; +import { useSelector } from 'calypso/state'; import isUserRegistrationDaysWithinRange from 'calypso/state/selectors/is-user-registration-days-within-range'; -import { getSignupDependencyStore } from 'calypso/state/signup/dependency-store/selectors'; import { useSite } from './use-site'; import type { UserSelect, OnboardSelect } from '@automattic/data-stores'; export const useRecordSignupComplete = ( flow: string | null ) => { const site = useSite(); - const store = useStore(); - const state = store.getState(); const siteId = site?.ID || null; const theme = site?.options?.theme_slug || ''; const { domainCartItem, planCartItem, siteCount, selectedDomain } = useSelect( ( select ) => { @@ -23,53 +20,60 @@ export const useRecordSignupComplete = ( flow: string | null ) => { selectedDomain: ( select( ONBOARD_STORE ) as OnboardSelect ).getSelectedDomain(), }; }, [] ); - const isNewishUser = isUserRegistrationDaysWithinRange( state, null, 0, 7 ); - const dependencies = getSignupDependencyStore( state ); - return useCallback( () => { - const isNewUser = !! ( dependencies && dependencies.username ); + const isNewishUser = useSelector( ( state ) => + isUserRegistrationDaysWithinRange( state, null, 0, 7 ) + ); - const isNew7DUserSite = !! ( - isNewUser || - ( isNewishUser && dependencies && dependencies?.siteSlug && siteCount && siteCount <= 1 ) - ); + return useCallback( + ( signupCompletionState: Record< string, unknown > ) => { + const siteSlug = site?.slug ?? signupCompletionState?.siteSlug; - // Domain product slugs can be a domain purchases like dotcom_domain or dotblog_domain or a mapping like domain_mapping - // When purchasing free subdomains the product_slugs is empty (since there is no actual produce being purchased) - // so we avoid capturing the product slug in these instances. - const domainProductSlug = domainCartItem?.product_slug ?? undefined; + const isNewUser = ! siteCount; - // Domain cart items can sometimes be included when free. So the selected domain is explicitly checked to see if it's free. - // For mappings and transfers this attribute should be empty but it needs to be checked. - const hasCartItems = !! ( domainProductSlug || planCartItem ); // see the function `dependenciesContainCartItem() + const isNew7DUserSite = !! ( + isNewUser || + ( isNewishUser && siteSlug && siteCount && siteCount <= 1 ) + ); - // When there is no plan put in the cart, `planCartItem` is `null` instead of `undefined` like domainCartItem. - // It worths a investigation of whether the both should behave the same. - const planProductSlug = planCartItem?.product_slug ?? undefined; - // To have a paid domain item it has to either be a paid domain or a different domain product like mapping or transfer. - const hasPaidDomainItem = - ( selectedDomain && ! selectedDomain.is_free ) || !! domainProductSlug; + // Domain product slugs can be a domain purchases like dotcom_domain or dotblog_domain or a mapping like domain_mapping + // When purchasing free subdomains the product_slugs is empty (since there is no actual produce being purchased) + // so we avoid capturing the product slug in these instances. + const domainProductSlug = domainCartItem?.product_slug ?? undefined; - recordSignupComplete( - { - flow, - siteId, - isNewUser, - hasCartItems, - isNew7DUserSite, - theme, - intent: flow, - startingPoint: flow, - isBlankCanvas: theme?.includes( 'blank-canvas' ), - planProductSlug, - domainProductSlug, - isMapping: - hasPaidDomainItem && domainCartItem ? isDomainMapping( domainCartItem ) : undefined, - isTransfer: - hasPaidDomainItem && domainCartItem ? isDomainTransfer( domainCartItem ) : undefined, - signupDomainOrigin: SIGNUP_DOMAIN_ORIGIN.NOT_SET, - }, - true - ); - }, [ domainCartItem, flow, planCartItem, selectedDomain, siteCount, siteId, theme ] ); + // Domain cart items can sometimes be included when free. So the selected domain is explicitly checked to see if it's free. + // For mappings and transfers this attribute should be empty but it needs to be checked. + const hasCartItems = !! ( domainProductSlug || planCartItem ); // see the function `dependenciesContainCartItem() + + // When there is no plan put in the cart, `planCartItem` is `null` instead of `undefined` like domainCartItem. + // It worths a investigation of whether the both should behave the same. + const planProductSlug = planCartItem?.product_slug ?? undefined; + // To have a paid domain item it has to either be a paid domain or a different domain product like mapping or transfer. + const hasPaidDomainItem = + ( selectedDomain && ! selectedDomain.is_free ) || !! domainProductSlug; + + recordSignupComplete( + { + flow, + siteId: siteId ?? signupCompletionState?.siteId, + isNewUser, + hasCartItems, + isNew7DUserSite, + theme, + intent: flow, + startingPoint: flow, + isBlankCanvas: theme?.includes( 'blank-canvas' ), + planProductSlug, + domainProductSlug, + isMapping: + hasPaidDomainItem && domainCartItem ? isDomainMapping( domainCartItem ) : undefined, + isTransfer: + hasPaidDomainItem && domainCartItem ? isDomainTransfer( domainCartItem ) : undefined, + signupDomainOrigin: SIGNUP_DOMAIN_ORIGIN.NOT_SET, + }, + true + ); + }, + [ domainCartItem, flow, planCartItem, selectedDomain, siteCount, siteId, theme ] + ); }; From cfddcb9b127900441a5d48680ffbdeabc7e22198 Mon Sep 17 00:00:00 2001 From: escapemanuele Date: Thu, 19 Sep 2024 16:31:03 +0200 Subject: [PATCH 3/5] Add newUser call --- .../hooks/use-record-signup-complete.ts | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/client/landing/stepper/hooks/use-record-signup-complete.ts b/client/landing/stepper/hooks/use-record-signup-complete.ts index 55875137b8295..04ba55b9ec003 100644 --- a/client/landing/stepper/hooks/use-record-signup-complete.ts +++ b/client/landing/stepper/hooks/use-record-signup-complete.ts @@ -12,14 +12,18 @@ export const useRecordSignupComplete = ( flow: string | null ) => { const site = useSite(); const siteId = site?.ID || null; const theme = site?.options?.theme_slug || ''; - const { domainCartItem, planCartItem, siteCount, selectedDomain } = useSelect( ( select ) => { - return { - siteCount: ( select( USER_STORE ) as UserSelect ).getCurrentUser()?.site_count, - domainCartItem: ( select( ONBOARD_STORE ) as OnboardSelect ).getDomainCartItem(), - planCartItem: ( select( ONBOARD_STORE ) as OnboardSelect ).getPlanCartItem(), - selectedDomain: ( select( ONBOARD_STORE ) as OnboardSelect ).getSelectedDomain(), - }; - }, [] ); + const { domainCartItem, planCartItem, siteCount, selectedDomain, currentUser } = useSelect( + ( select ) => { + return { + siteCount: ( select( USER_STORE ) as UserSelect ).getCurrentUser()?.site_count, + domainCartItem: ( select( ONBOARD_STORE ) as OnboardSelect ).getDomainCartItem(), + planCartItem: ( select( ONBOARD_STORE ) as OnboardSelect ).getPlanCartItem(), + selectedDomain: ( select( ONBOARD_STORE ) as OnboardSelect ).getSelectedDomain(), + currentUser: ( select( USER_STORE ) as UserSelect ).getCurrentUser(), + }; + }, + [] + ); const isNewishUser = useSelector( ( state ) => isUserRegistrationDaysWithinRange( state, null, 0, 7 ) @@ -29,7 +33,7 @@ export const useRecordSignupComplete = ( flow: string | null ) => { ( signupCompletionState: Record< string, unknown > ) => { const siteSlug = site?.slug ?? signupCompletionState?.siteSlug; - const isNewUser = ! siteCount; + const isNewUser = !! currentUser?.username; const isNew7DUserSite = !! ( isNewUser || From 0a3765387635aed96b3cbca74d2dab21302adfce Mon Sep 17 00:00:00 2001 From: escapemanuele Date: Thu, 19 Sep 2024 21:01:51 +0200 Subject: [PATCH 4/5] Register in the store when it is a new account --- client/blocks/signup-form/passwordless.jsx | 8 ++++---- client/blocks/signup-form/signup-form-social-first.tsx | 3 +++ .../internals/steps-repository/__user/index.tsx | 4 ++++ .../__user/use-create-user-mutation.ts | 6 ++++++ .../stepper/hooks/use-record-signup-complete.ts | 6 ++---- packages/data-stores/src/user/actions.ts | 10 +++++++++- packages/data-stores/src/user/reducer.ts | 10 +++++++++- packages/data-stores/src/user/selectors.ts | 1 + 8 files changed, 38 insertions(+), 10 deletions(-) diff --git a/client/blocks/signup-form/passwordless.jsx b/client/blocks/signup-form/passwordless.jsx index f2ecd4c90734d..b6478424e0b40 100644 --- a/client/blocks/signup-form/passwordless.jsx +++ b/client/blocks/signup-form/passwordless.jsx @@ -169,10 +169,6 @@ class PasswordlessSignupForm extends Component { const { flowName, queryArgs = {} } = this.props; const { redirect_to, oauth2_client_id, oauth2_redirect } = queryArgs; - if ( this.props.onCreateAccountSuccess ) { - return this.props.onCreateAccountSuccess( userData ); - } - recordRegistration( { userData, flow: flowName, @@ -187,6 +183,10 @@ class PasswordlessSignupForm extends Component { ? { oauth2_client_id, oauth2_redirect } : { redirect: redirect_to } ), } ); + + if ( this.props.onCreateAccountSuccess ) { + return this.props.onCreateAccountSuccess( userData ); + } }; submitStep = ( data ) => { diff --git a/client/blocks/signup-form/signup-form-social-first.tsx b/client/blocks/signup-form/signup-form-social-first.tsx index 52cc70f29a19c..8be6e5af4929f 100644 --- a/client/blocks/signup-form/signup-form-social-first.tsx +++ b/client/blocks/signup-form/signup-form-social-first.tsx @@ -35,6 +35,7 @@ interface SignupFormSocialFirst { extra: { first_name: string; last_name: string; username_hint: string }; } | null ) => void; + onCreateAccountSuccess?: ( data: AccountCreateReturn ) => void; queryArgs: QueryArgs; userEmail: string; notice: JSX.Element | false; @@ -69,6 +70,7 @@ const SignupFormSocialFirst = ( { logInUrl, socialServiceResponse, handleSocialResponse, + onCreateAccountSuccess, queryArgs, userEmail, notice, @@ -175,6 +177,7 @@ const SignupFormSocialFirst = ( { ); } } } + onCreateAccountSuccess={ onCreateAccountSuccess } { ...gravatarProps } />