Skip to content

Commit

Permalink
My Jetpack: add post-activation experiment
Browse files Browse the repository at this point in the history
  • Loading branch information
IanRamosC committed Oct 25, 2024
1 parent c47c77d commit 3502981
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -1,45 +1,43 @@
import { Col, Button, Text, TermsOfService } from '@automattic/jetpack-components';
import { Col, Button, Text, TermsOfService, getRedirectUrl } 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 useProductsByOwnership from '../../data/products/use-products-by-ownership';
import { getMyJetpackWindowInitialState } from '../../data/utils/get-my-jetpack-window-state';
import useAnalytics from '../../hooks/use-analytics';
import sideloadTracks from '../../utils/side-load-tracks';
import styles from './style.module.scss';
import type { WelcomeFlowExperiment } from '.';
import type { Dispatch, SetStateAction } from 'react';

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

/**
* Component that renders the Welcome banner on My Jetpack.
*
* @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
* @param {object} props - ConnectioStepProps
* @param {Function} props.onActivateSite - Alias for handleRegisterSite
* @param {boolean} props.isActivating - Alias for siteIsRegistering
* @return {object} The ConnectionStep component.
*/
const ConnectionStep = ( {
onActivateSite,
onUpdateWelcomeFlowExperiment,
isActivating,
}: ConnectionStepProps ) => {
const ConnectionStep = ( { onActivateSite, isActivating }: ConnectionStepProps ) => {
const { recordEvent } = useAnalytics();
const { setNotice, resetNotice } = useContext( NoticeContext );

const { purchaseToken, siteUrl, adminUrl } = getMyJetpackWindowInitialState();
const redirectUri = `?redirect_uri=${ encodeURIComponent( window.location.href ) }`;
const connectAfterCheckoutUrl = `&connect_after_checkout=true&from_site_slug=${ siteUrl }&admin_url=${ adminUrl }`;
const query = `${ redirectUri }${ purchaseToken }${ connectAfterCheckoutUrl }`;
const jetpackPlansPath = getRedirectUrl( 'jetpack-plans', { query } );

const activationButtonLabel = __( 'Activate Jetpack in one click', 'jetpack-my-jetpack' );
const { refetch: refetchOwnershipData } = useProductsByOwnership();

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

recordEvent( 'jetpack_myjetpack_welcome_banner_connect_site_success' );
Expand All @@ -50,23 +48,20 @@ const ConnectionStep = ( {
initializeExPlat();

const { variationName } = await loadExperimentAssignment(
'jetpack_my_jetpack_welcome_flow_display_default_recommendations_upfront_202410'
'jetpack_my_jetpack_recommendations_pricing_page_202411'
);

onUpdateWelcomeFlowExperiment( state => ( {
...state,
variation: ( variationName ?? 'control' ) as WelcomeFlowExperiment[ 'variation' ], // casting to 'control' or 'treatment'
} ) );
if ( variationName === 'treatment' ) {
window.location.href = jetpackPlansPath;
}
} finally {
resetNotice();
setNotice( NOTICE_SITE_CONNECTED, resetNotice );
refetchOwnershipData();

onUpdateWelcomeFlowExperiment( state => ( { ...state, isLoading: false } ) );
}
}, [
jetpackPlansPath,
onActivateSite,
onUpdateWelcomeFlowExperiment,
recordEvent,
refetchOwnershipData,
resetNotice,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ interface Props extends PropsWithChildren {
setWelcomeFlowExperiment: React.Dispatch< React.SetStateAction< WelcomeFlowExperiment > >;
}

const WelcomeFlow: FC< Props > = ( {
welcomeFlowExperiment,
setWelcomeFlowExperiment,
children,
} ) => {
const WelcomeFlow: FC< Props > = ( { children } ) => {
const { recordEvent } = useAnalytics();
const { dismissWelcomeBanner } = useWelcomeBanner();
const { recommendedModules, submitEvaluation, saveEvaluationResult } =
Expand Down Expand Up @@ -134,8 +130,7 @@ const WelcomeFlow: FC< Props > = ( {
{ 'connection' === currentStep && (
<ConnectionStep
onActivateSite={ handleRegisterSite }
onUpdateWelcomeFlowExperiment={ setWelcomeFlowExperiment }
isActivating={ siteIsRegistering || welcomeFlowExperiment.isLoading }
isActivating={ siteIsRegistering }
/>
) }
{ 'evaluation' === currentStep && (
Expand Down
1 change: 1 addition & 0 deletions projects/packages/my-jetpack/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ interface Window {
showFullJetpackStatsCard: boolean;
videoPressStats: boolean;
};
purchaseToken: string;
lifecycleStats: {
historicallyActiveModules: JetpackModule[];
brokenModules: {
Expand Down
54 changes: 54 additions & 0 deletions projects/packages/my-jetpack/src/class-initializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,28 @@ public static function can_use_analytics() {

return $tracking->should_enable_tracking( new Terms_Of_Service(), $status );
}

/**
* Gets a purchase token that is used for Jetpack logged out visitor checkout.
* The purchase token should be appended to all CTA url's that lead to checkout.
*
* @return string|boolean
*/
protected static function get_purchase_token() {
if ( ! self::current_user_can_purchase() ) {
return false;
}

$purchase_token = \Jetpack_Options::get_option( 'purchase_token', false );

if ( $purchase_token ) {
return $purchase_token;
}
// If the purchase token is not saved in the options table yet, then add it.
\Jetpack_Options::update_option( 'purchase_token', self::generate_purchase_token(), true );
return \Jetpack_Options::get_option( 'purchase_token', false );
}

/**
* Enqueue admin page assets.
*
Expand Down Expand Up @@ -265,6 +287,7 @@ public static function enqueue_scripts() {
'loadAddLicenseScreen' => self::is_licensing_ui_enabled(),
'adminUrl' => esc_url( admin_url() ),
'IDCContainerID' => static::get_idc_container_id(),
'purchaseToken' => self::get_purchase_token(),
'userIsAdmin' => current_user_can( 'manage_options' ),
'userIsNewToJetpack' => self::is_jetpack_user_new(),
'lifecycleStats' => array(
Expand Down Expand Up @@ -967,4 +990,35 @@ public static function alert_if_missing_connection( array $red_bubble_slugs ) {

return $red_bubble_slugs;
}

/**
* Generates a purchase token that is used for Jetpack logged out visitor checkout.
*
* @return string
*/
protected static function generate_purchase_token() {
return wp_generate_password( 12, false );
}

/**
* Determine if the current user is allowed to make Jetpack purchases without
* a WordPress.com account
*
* @return boolean True if the user can make purchases, false if not
*/
public static function current_user_can_purchase() {
// The site must be site-connected to Jetpack (no users connected).
$connection_manager = new Connection_Manager();

if ( ! $connection_manager->is_site_connection() ) {
return false;
}

// Make sure only administrators can make purchases.
if ( ! current_user_can( 'manage_options' ) ) {
return false;
}

return true;
}
}

0 comments on commit 3502981

Please sign in to comment.