Skip to content

Commit

Permalink
My Jetpack: add A/B experiment to welcome flow (#38664)
Browse files Browse the repository at this point in the history
* My Jetpack: add A/B test to Welcome flow

* changelog

* Fix project version

* Sideload Tracks script after connection

* Default experiment and control to redirect to user connection

* My Jetpack: only reset notice after experiment loads

* My Jetpack: don't change step if loading experiment

* Prevent 'control' user to see evaluation step and code adjustments

* Reshuffle conditions

* Add try/catch to explats connection

---------

Co-authored-by: robertsreberski <[email protected]>
  • Loading branch information
IanRamosC and robertsreberski authored Aug 5, 2024
1 parent a2d6810 commit 150447c
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { Col, Button, Text, TermsOfService } from '@automattic/jetpack-components';
import { initializeExPlat, loadExperimentAssignment } from '@automattic/jetpack-explat';
import { __ } from '@wordpress/i18n';
import { useCallback, useContext } from 'react';
import { NoticeContext } from '../../context/notices/noticeContext';
import { NOTICE_SITE_CONNECTED } from '../../context/notices/noticeTemplates';
import useAnalytics from '../../hooks/use-analytics';
import sideloadTracks from '../../utils/side-load-tracks';
import styles from './style.module.scss';
import { WelcomeFlowExperiment } from '.';
import type { Dispatch, SetStateAction } from 'react';

type ConnectionStepProps = {
onActivateSite: ( e?: Event ) => Promise< void >;
onUpdateWelcomeFlowExperiment: Dispatch< SetStateAction< WelcomeFlowExperiment > >;
isActivating: boolean;
};

Expand All @@ -16,23 +21,52 @@ type ConnectionStepProps = {
*
* @param {object} props - ConnectioStepProps
* @param {Function} props.onActivateSite - Alias for handleRegisterSite
* @param {Function} props.onUpdateWelcomeFlowExperiment - Updating the welcomeFlowExperiment state
* @param {boolean} props.isActivating - Alias for siteIsRegistering
* @returns {object} The ConnectionStep component.
*/
const ConnectionStep = ( { onActivateSite, isActivating }: ConnectionStepProps ) => {
const ConnectionStep = ( {
onActivateSite,
onUpdateWelcomeFlowExperiment,
isActivating,
}: ConnectionStepProps ) => {
const { recordEvent } = useAnalytics();
const { setNotice, resetNotice } = useContext( NoticeContext );

const activationButtonLabel = __( 'Activate Jetpack in one click', 'jetpack-my-jetpack' );

const onConnectSiteClick = useCallback( () => {
const onConnectSiteClick = useCallback( async () => {
recordEvent( 'jetpack_myjetpack_welcome_banner_connect_site_click' );
onActivateSite().then( () => {
recordEvent( 'jetpack_myjetpack_welcome_banner_connect_site_success' );
onUpdateWelcomeFlowExperiment( state => ( { ...state, isLoading: true } ) );
await onActivateSite();

recordEvent( 'jetpack_myjetpack_welcome_banner_connect_site_success' );

try {
await sideloadTracks();

initializeExPlat();

const { variationName } = await loadExperimentAssignment(
'jetpack_my_jetpack_post_connection_flow_202408'
);

if ( variationName !== 'treatment' ) {
// For control or default, we redirect to the connection page as described in the experiment.
window.location.href = 'admin.php?page=my-jetpack#/connection';
}

onUpdateWelcomeFlowExperiment( state => ( {
...state,
variation: variationName as WelcomeFlowExperiment[ 'variation' ], // casting to 'control' or 'treatment'
} ) );
} finally {
resetNotice();
setNotice( NOTICE_SITE_CONNECTED, resetNotice );
} );
}, [ onActivateSite, recordEvent, resetNotice, setNotice ] );

onUpdateWelcomeFlowExperiment( state => ( { ...state, isLoading: false } ) );
}
}, [ onActivateSite, onUpdateWelcomeFlowExperiment, recordEvent, resetNotice, setNotice ] );

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import EvaluationStep, { EvaluationAreas } from './EvaluationStep';
import styles from './style.module.scss';
import type { FC, PropsWithChildren } from 'react';

export type WelcomeFlowExperiment = {
isLoading: boolean;
variation: 'control' | 'treatment';
};

const WelcomeFlow: FC< PropsWithChildren > = ( { children } ) => {
const { recordEvent } = useAnalytics();
const { dismissWelcomeBanner } = useWelcomeBanner();
Expand All @@ -30,20 +35,24 @@ const WelcomeFlow: FC< PropsWithChildren > = ( { children } ) => {
} );
const [ isProcessingEvaluation, setIsProcessingEvaluation ] = useState( false );
const [ prevStep, setPrevStep ] = useState( '' );
const [ welcomeFlowExperiment, setWelcomeFlowExperiment ] = useState< WelcomeFlowExperiment >( {
isLoading: false,
variation: 'control',
} );

const currentStep = useMemo( () => {
if ( ! siteIsRegistered ) {
if ( ! siteIsRegistered || welcomeFlowExperiment.isLoading ) {
return 'connection';
} else if ( ! isProcessingEvaluation ) {
if ( ! isJetpackUserNew() ) {
if ( ! isJetpackUserNew() || welcomeFlowExperiment.variation !== 'treatment' ) {
// If the user is not new, we don't show the evaluation step
return null;
}
return 'evaluation';
}

return 'evaluation-processing';
}, [ isProcessingEvaluation, siteIsRegistered ] );
}, [ isProcessingEvaluation, siteIsRegistered, welcomeFlowExperiment ] );

useEffect( () => {
if ( prevStep !== currentStep ) {
Expand Down Expand Up @@ -113,7 +122,8 @@ const WelcomeFlow: FC< PropsWithChildren > = ( { children } ) => {
{ 'connection' === currentStep && (
<ConnectionStep
onActivateSite={ handleRegisterSite }
isActivating={ siteIsRegistering }
onUpdateWelcomeFlowExperiment={ setWelcomeFlowExperiment }
isActivating={ siteIsRegistering || welcomeFlowExperiment.isLoading }
/>
) }
{ 'evaluation' === currentStep && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Dispatch, SetStateAction } from 'react';

type ValueStoreType = {
isWelcomeBannerVisible: boolean;
isLoadingWelcomeFlowExperiment?: boolean;
recommendedModules: JetpackModule[] | null;
recommendedModulesVisible: boolean;
};
Expand Down
58 changes: 58 additions & 0 deletions projects/packages/my-jetpack/_inc/utils/side-load-tracks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
declare global {
interface Window {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
_tkq: Array< Array< any > >;
}
}

/**
* Function to get the current year and week number.
*
* @returns {string} The current year and week number.
*/
function getCurrentYearAndWeek() {
const date = new Date();
const year = date.getFullYear();

const firstDayOfYear = new Date( year, 0, 1 );
const daysSinceFirstDay = Math.floor(
( date.getTime() - firstDayOfYear.getTime() ) / ( 24 * 60 * 60 * 1000 )
);

// Calculate the current week number (assuming week starts on Sunday)
const weekNumber = Math.ceil( ( daysSinceFirstDay + firstDayOfYear.getDay() + 1 ) / 7 );
const formattedWeekNumber = weekNumber.toString().padStart( 2, '0' );

return `${ year }${ formattedWeekNumber }`;
}

/**
* Function to dynamically load a script into the document.
* It creates a new script element, sets its source to the provided URL,
* and appends it to the document's head.
*
* @param {string} src - The URL of the script to load.
* @returns {Promise<void>} A promise that resolves once the script has loaded.
*/
function loadScript( src: string ): Promise< void > {
return new Promise( resolve => {
const script = document.createElement( 'script' );
script.src = src;
script.onload = () => resolve();
document.head.appendChild( script );
} );
}

/**
* Function to sideload Tracks script.
*
* It initializes the _tkq array on the window object if it doesn't exist,
* and then loads the tracking script from the specified URL. Once the script has loaded,
* the provided callback function is called.
*
* @returns {Promise<void>} A promise that resolves once the Tracks has been side loaded.
*/
export default function sideloadTracks(): Promise< void > {
window._tkq = window._tkq || [];
return loadScript( `//stats.wp.com/w.js?${ getCurrentYearAndWeek() }` );
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: changed
Comment: Added a simple A/B experimento to the Welcome Flow on My Jetpack.


0 comments on commit 150447c

Please sign in to comment.