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

Stepper: Onboarding flow update tracks events #94560

Merged
merged 12 commits into from
Sep 19, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ export const useStepRouteTracking = ( {
// Also record page view for data and analytics
const pathname = window.location.pathname;
const pageTitle = `Setup > ${ flowName } > ${ stepSlug }`;
recordPageView( pathname, pageTitle );
const params = {
flow: flowName,
};
recordPageView( pathname, pageTitle, params );

// We leave out intent and design from the dependency list, due to the ONBOARD_STORE being reset in the exit flow.
// The store reset causes these values to become empty, and may trigger this event again.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,56 +27,65 @@ import {
import { getAvailableProductsList } from 'calypso/state/products-list/selectors';
import getSitesItems from 'calypso/state/selectors/get-sites-items';
import { fetchUsernameSuggestion } from 'calypso/state/signup/optional-dependencies/actions';
import { removeStep } from 'calypso/state/signup/progress/actions';
import { removeStep, dispatchRecordSubmitStep } from 'calypso/state/signup/progress/actions';
import { setDesignType } from 'calypso/state/signup/steps/design-type/actions';
import { getDesignType } from 'calypso/state/signup/steps/design-type/selectors';
import { ProvidedDependencies, StepProps } from '../../types';
import { useIsManagedSiteFlowProps } from './use-is-managed-site-flow';

const RenderDomainsStepConnect = connect(
( state, { flow }: StepProps ) => {
const productsList = getAvailableProductsList( state );
const productsLoaded = ! isEmpty( productsList );
const multiDomainDefaultPlan = planItem( PLAN_PERSONAL );
const userLoggedIn = isUserLoggedIn( state as object );
const currentUserSiteCount = getCurrentUserSiteCount( state as object );
const stepSectionName = window.location.pathname.includes( 'use-your-domain' )
? 'use-your-domain'
: undefined;

return {
designType: getDesignType( state ),
currentUser: getCurrentUser( state as object ),
productsList,
productsLoaded,
isDomainOnly: false,
sites: getSitesItems( state ),
userSiteCount: currentUserSiteCount,
previousStepName: 'user',
isPlanSelectionAvailableLaterInFlow: true,
userLoggedIn,
multiDomainDefaultPlan,
domainsWithPlansOnly: currentUserHasFlag( state as object, DOMAINS_WITH_PLANS_ONLY ),
flowName: flow,
path: window.location.pathname,
positionInFlow: 1,
isReskinned: true,
stepSectionName,
};
},
{
const mapDispatchToProps = ( dispatch: any, props: any ) => {
return {
recordAddDomainButtonClick,
recordAddDomainButtonClickInMapDomain,
recordAddDomainButtonClickInTransferDomain,
recordAddDomainButtonClickInUseYourDomain,
recordUseYourDomainButtonClick,
removeStep,
submitSignupStep: (
step: Record< string, unknown >,
providedDependencies: Record< string, unknown >,
optionalProps: Record< string, unknown >
) => {
dispatch( dispatchRecordSubmitStep( step, providedDependencies, optionalProps ) );
props.submitSignupStep?.( step );
},
submitDomainStepSelection,
setDesignType,
recordTracksEvent,
fetchUsernameSuggestion,
}
)( withCartKey( withShoppingCart( localize( RenderDomainsStep ) ) ) );
};
};

const RenderDomainsStepConnect = connect( ( state, { flow }: StepProps ) => {
const productsList = getAvailableProductsList( state );
const productsLoaded = ! isEmpty( productsList );
const multiDomainDefaultPlan = planItem( PLAN_PERSONAL );
const userLoggedIn = isUserLoggedIn( state as object );
const currentUserSiteCount = getCurrentUserSiteCount( state as object );
const stepSectionName = window.location.pathname.includes( 'use-your-domain' )
? 'use-your-domain'
: undefined;

return {
designType: getDesignType( state ),
currentUser: getCurrentUser( state as object ),
productsList,
productsLoaded,
isDomainOnly: false,
sites: getSitesItems( state ),
userSiteCount: currentUserSiteCount,
previousStepName: 'user',
isPlanSelectionAvailableLaterInFlow: true,
userLoggedIn,
multiDomainDefaultPlan,
domainsWithPlansOnly: currentUserHasFlag( state as object, DOMAINS_WITH_PLANS_ONLY ),
flowName: flow,
path: window.location.pathname,
positionInFlow: 1,
isReskinned: true,
stepSectionName,
};
}, mapDispatchToProps )( withCartKey( withShoppingCart( localize( RenderDomainsStep ) ) ) );

export default function DomainsStep( props: StepProps ) {
const [ stepState, setStepState ] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useSelect, useDispatch as useWPDispatch } from '@wordpress/data';
import { localize } from 'i18n-calypso';
import { useState } from 'react';
import { useDispatch } from 'react-redux';
import { AnyAction } from 'redux';
import { useQuery } from 'calypso/landing/stepper/hooks/use-query';
import { useSite } from 'calypso/landing/stepper/hooks/use-site';
import { useSiteSlug } from 'calypso/landing/stepper/hooks/use-site-slug';
Expand All @@ -15,6 +16,7 @@ import { useSelector } from 'calypso/state';
import { recordTracksEvent } from 'calypso/state/analytics/actions';
import { getCurrentUserName } from 'calypso/state/current-user/selectors';
import { errorNotice } from 'calypso/state/notices/actions';
import { dispatchRecordSubmitStep } from 'calypso/state/signup/progress/actions';
import { ProvidedDependencies, StepProps } from '../../types';

import './style.scss';
Expand Down Expand Up @@ -56,54 +58,68 @@ export default function PlansStepAdaptor( props: StepProps ) {

const [ planInterval, setPlanInterval ] = useState< string | undefined >( undefined );

const onPlanIntervalUpdate = ( path: string ) => {
const intervalType = getIntervalType( path );
setPlanInterval( intervalType );
};

/**
* The plans step has a quirk where it calls `submitSignupStep` then synchronously calls `goToNextStep` after it.
* This doesn't give `setStepState` a chance to update and the data is not passed to `submit`.
*/
let mostRecentState: ProvidedDependencies;

const onPlanIntervalUpdate = ( path: string ) => {
const intervalType = getIntervalType( path );
setPlanInterval( intervalType );
};

const handleSubmitSignupStep = (
step: Record< string, unknown >,
providedDependencies: Record< string, unknown >,
optionalProps: Record< string, unknown >
) => {
if ( step.stepName === 'domains' ) {
if ( step.isPurchasingItem === false ) {
setDomainCartItem( undefined );
setDomainCartItems( undefined );
} else if ( step.siteUrl ) {
setSiteUrl( step.siteUrl );
}
} else {
setStepState( ( mostRecentState = { ...stepState, ...step } ) );
dispatch(
dispatchRecordSubmitStep(
{ ...stepState, ...step },
providedDependencies,
optionalProps
) as unknown as AnyAction
);
props.navigation.submit?.(
( mostRecentState = { ...stepState, ...step, ...mostRecentState } )
);
}
};

return (
<LocalizedPlanStep
selectedSite={ site }
saveSignupStep={ ( state: ProvidedDependencies ) => {
setStepState( ( mostRecentState = { ...stepState, ...state } ) );
} }
submitSignupStep={ ( state: ProvidedDependencies ) => {
/* The plans step removes paid domains when the user picks a free plan
after picking a paid domain */
if ( state.stepName === 'domains' ) {
if ( state.isPurchasingItem === false ) {
setDomainCartItem( undefined );
setDomainCartItems( undefined );
} else if ( state.siteUrl ) {
setSiteUrl( state.siteUrl );
}
} else {
setStepState( ( mostRecentState = { ...stepState, ...state } ) );
props.navigation.submit?.(
( mostRecentState = { ...stepState, ...state, ...mostRecentState } )
);
}
} }
submitSignupStep={ handleSubmitSignupStep }
goToNextStep={ () => props.navigation.submit?.( { ...stepState, ...mostRecentState } ) }
step={ stepState }
customerType={ customerType }
errorNotice={ ( message: string ) => dispatch( errorNotice( message ) ) }
signupDependencies={ signupDependencies }
stepName="plans"
flowName={ props.flow }
recordTracksEvent={ ( event: unknown ) => dispatch( recordTracksEvent( event ) ) }
recordTracksEvent={ ( name: string, props: unknown ) => {
dispatch( recordTracksEvent( name, props ) );
} }
onPlanIntervalUpdate={ onPlanIntervalUpdate }
intervalType={ planInterval }
wrapperProps={ {
hideBack: isMobile,
goBack: props.navigation.goBack,
recordTracksEvent: ( event: unknown ) => dispatch( recordTracksEvent( event ) ),
recordTracksEvent: ( name: string, props: unknown ) =>
dispatch( recordTracksEvent( name, props ) ),
isFullLayout: true,
isExtraWideLayout: false,
} }
Expand Down
16 changes: 16 additions & 0 deletions client/state/signup/progress/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,22 @@ export function saveSignupStep( step ) {
};
}

export function dispatchRecordSubmitStep( step, providedDependencies, optionalProps ) {
assertValidDependencies( step.stepName, providedDependencies );
return ( dispatch, getState ) => {
const lastKnownFlow = getCurrentFlowName( getState() );
const { intent } = getSignupDependencyStore( getState() );

dispatch(
recordSubmitStep( lastKnownFlow, step.stepName, providedDependencies, {
intent,
...optionalProps,
...( step.wasSkipped && { was_skipped: step.wasSkipped } ),
} )
);
};
}

export function submitSignupStep( step, providedDependencies, optionalProps ) {
assertValidDependencies( step.stepName, providedDependencies );
return ( dispatch, getState ) => {
Expand Down
Loading