CYS: Improve opt in flow (#50529)

* CYS: Improve opt in flow

* Add changefile(s) from automation for the following project(s): woocommerce

* fix import

* fix imports and remove not used functions

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Luigi Teschio 2024-08-09 17:44:10 +02:00 committed by GitHub
parent 1160414b14
commit 1fbfa83fb4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 257 additions and 136 deletions

View File

@ -45,12 +45,12 @@ import { addFilter } from '@wordpress/hooks';
import { CustomizeStoreComponent } from '../types'; import { CustomizeStoreComponent } from '../types';
import { Layout } from './layout'; import { Layout } from './layout';
import './style.scss'; import './style.scss';
import { PreloadFonts } from './preload-fonts';
import { GoBackWarningModal } from './go-back-warning-modal'; import { GoBackWarningModal } from './go-back-warning-modal';
import { onBackButtonClicked } from '../utils'; import { onBackButtonClicked } from '../utils';
import { getNewPath } from '@woocommerce/navigation'; import { getNewPath } from '@woocommerce/navigation';
import useBodyClass from '../hooks/use-body-class'; import useBodyClass from '../hooks/use-body-class';
import { OptInSubscribe } from './opt-in/opt-in';
import { OptInContextProvider } from './opt-in/context';
import './tracking'; import './tracking';
const { RouterProvider } = unlock( routerPrivateApis ); const { RouterProvider } = unlock( routerPrivateApis );
@ -181,12 +181,14 @@ export const AssemblerHub: CustomizeStoreComponent = ( props ) => {
) } ) }
<CustomizeStoreContext.Provider value={ props }> <CustomizeStoreContext.Provider value={ props }>
<ShortcutProvider style={ { height: '100%' } }> <ShortcutProvider style={ { height: '100%' } }>
<GlobalStylesProvider> <OptInContextProvider>
<RouterProvider> <GlobalStylesProvider>
<Layout /> <RouterProvider>
</RouterProvider> <Layout />
<PreloadFonts /> </RouterProvider>
</GlobalStylesProvider> <OptInSubscribe />
</GlobalStylesProvider>
</OptInContextProvider>
</ShortcutProvider> </ShortcutProvider>
</CustomizeStoreContext.Provider> </CustomizeStoreContext.Provider>
</> </>

View File

@ -0,0 +1,53 @@
/**
* External dependencies
*/
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
import { useSelect } from '@wordpress/data';
import React, { createContext, useState } from '@wordpress/element';
import type { ReactNode } from 'react';
export const enum OPTIN_FLOW_STATUS {
'IDLE' = 'IDLE',
'LOADING' = 'LOADING',
'DONE' = 'DONE',
}
export const OptInContext = createContext< {
optInFlowStatus: OPTIN_FLOW_STATUS;
setOptInFlowStatus: ( status: OPTIN_FLOW_STATUS ) => void;
} >( {
optInFlowStatus: OPTIN_FLOW_STATUS.IDLE,
setOptInFlowStatus: () => {},
} );
export const OptInContextProvider = ( {
children,
}: {
children: ReactNode;
} ) => {
const isAllowTrackingEnabled = useSelect(
( select ) =>
select( OPTIONS_STORE_NAME ).getOption(
'woocommerce_allow_tracking'
) === 'yes',
[]
);
const [ optInFlowStatus, setOptInFlowStatus ] =
useState< OPTIN_FLOW_STATUS >(
isAllowTrackingEnabled
? OPTIN_FLOW_STATUS.DONE
: OPTIN_FLOW_STATUS.IDLE
);
return (
<OptInContext.Provider
value={ {
optInFlowStatus,
setOptInFlowStatus,
} }
>
{ children }
</OptInContext.Provider>
);
};

View File

@ -0,0 +1,161 @@
/**
* External dependencies
*/
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
import apiFetch from '@wordpress/api-fetch';
import { resolveSelect, select, subscribe, useDispatch } from '@wordpress/data';
import { useContext, useEffect } from '@wordpress/element';
// @ts-expect-error No types for this exist yet.
// eslint-disable-next-line @woocommerce/dependency-group
import { store as coreStore } from '@wordpress/core-data';
// @ts-expect-error No types for this exist yet.
// eslint-disable-next-line @woocommerce/dependency-group
import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
// @ts-expect-error No types for this exist yet.
// eslint-disable-next-line @woocommerce/dependency-group
import { unlock } from '@wordpress/edit-site/build-module/lock-unlock';
/**
* Internal dependencies
*/
import { FontFamily, FontFace } from '../../types/font';
import { usePatterns } from '../hooks/use-patterns';
import { installFontFamilies } from '../utils/fonts';
import { FONT_FAMILIES_TO_INSTALL } from '../sidebar/global-styles/font-pairing-variations/constants';
import { OptInContext, OPTIN_FLOW_STATUS } from './context';
const { useGlobalSetting } = unlock( blockEditorPrivateApis );
export const OptInSubscribe = () => {
const { setOptInFlowStatus } = useContext( OptInContext );
const [ enabledFontFamilies, setFontFamilies ]: [
{
custom: Array< FontFamily >;
theme: Array< FontFamily >;
},
( font: {
custom: Array< FontFamily >;
theme: Array< FontFamily >;
} ) => void
] = useGlobalSetting( 'typography.fontFamilies' );
const {
// @ts-expect-error No types for this exist yet.
__experimentalSaveSpecifiedEntityEdits: saveSpecifiedEntityEdits,
} = useDispatch( coreStore );
const installPatterns = async () => {
await apiFetch< {
success: boolean;
} >( {
path: `/wc/private/patterns`,
method: 'POST',
} );
};
const installFonts = async () => {
await installFontFamilies();
const globalStylesId =
// @ts-expect-error No types for this exist yet.
select( coreStore ).__experimentalGetCurrentGlobalStylesId();
const installedFontFamilies = ( await resolveSelect(
coreStore
// @ts-expect-error No types for this exist yet.
).getEntityRecords( 'postType', 'wp_font_family', {
_embed: true,
per_page: -1,
} ) ) as Array< {
id: number;
font_family_settings: FontFamily;
_embedded: {
font_faces: Array< {
font_face_settings: FontFace;
} >;
};
} >;
const parsedInstalledFontFamilies = ( installedFontFamilies || [] ).map(
( fontFamilyPost ) => {
return {
id: fontFamilyPost.id,
...fontFamilyPost.font_family_settings,
fontFace:
fontFamilyPost?._embedded?.font_faces.map(
( face ) => face.font_face_settings
) || [],
};
}
);
const { custom } = enabledFontFamilies;
const enabledFontSlugs = [
...( custom ? custom.map( ( font ) => font.slug ) : [] ),
];
const fontFamiliesToEnable = parsedInstalledFontFamilies.reduce(
( acc, font ) => {
if (
enabledFontSlugs.includes( font.slug ) ||
FONT_FAMILIES_TO_INSTALL[ font.slug ] === undefined
) {
return acc;
}
return [
...acc,
{
...font,
},
];
},
[] as Array< FontFamily >
);
setFontFamilies( {
...enabledFontFamilies,
custom: [
...( enabledFontFamilies.custom ?? [] ),
...( fontFamiliesToEnable ?? [] ),
],
} );
saveSpecifiedEntityEdits( 'root', 'globalStyles', globalStylesId, [
'settings.typography.fontFamilies',
] );
};
const { invalidateCache } = usePatterns();
useEffect( () => {
const unsubscribe = subscribe( async () => {
const isOptedIn =
select( OPTIONS_STORE_NAME ).getOption(
'woocommerce_allow_tracking'
) === 'yes';
if ( isOptedIn ) {
setOptInFlowStatus( OPTIN_FLOW_STATUS.LOADING );
await installPatterns();
invalidateCache();
await installFonts();
setOptInFlowStatus( OPTIN_FLOW_STATUS.DONE );
unsubscribe();
}
// @ts-expect-error The type is not updated.
}, OPTIONS_STORE_NAME );
return () => {
unsubscribe();
};
// We don't want to run this effect on every render, only once because it is a subscription.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [] );
return null;
};

View File

@ -2,9 +2,6 @@
/** /**
* External dependencies * External dependencies
*/ */
// @ts-expect-error No types for this exist yet.
import { store as coreStore } from '@wordpress/core-data';
import { useSelect, useDispatch } from '@wordpress/data';
import { import {
privateApis as blockEditorPrivateApis, privateApis as blockEditorPrivateApis,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@ -16,26 +13,18 @@ import { unlock } from '@wordpress/edit-site/build-module/lock-unlock';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { import { FONT_PAIRINGS } from './sidebar/global-styles/font-pairing-variations/constants';
FONT_PAIRINGS,
FONT_FAMILIES_TO_INSTALL,
} from './sidebar/global-styles/font-pairing-variations/constants';
import { FontFamiliesLoader } from './sidebar/global-styles/font-pairing-variations/font-families-loader'; import { FontFamiliesLoader } from './sidebar/global-styles/font-pairing-variations/font-families-loader';
import { useContext, useEffect, useMemo } from '@wordpress/element'; import { useContext, useMemo } from '@wordpress/element';
import { FontFace, FontFamily } from '../types/font'; import { FontFamily } from '../types/font';
import { FontFamiliesLoaderDotCom } from './sidebar/global-styles/font-pairing-variations/font-families-loader-dot-com'; import { FontFamiliesLoaderDotCom } from './sidebar/global-styles/font-pairing-variations/font-families-loader-dot-com';
import { CustomizeStoreContext } from '.'; import { CustomizeStoreContext } from '.';
import { isAIFlow, isNoAIFlow } from '../guards'; import { isAIFlow, isNoAIFlow } from '../guards';
const { useGlobalSetting } = unlock( blockEditorPrivateApis ); const { useGlobalSetting } = unlock( blockEditorPrivateApis );
let areFontsPreloaded = false;
export const PreloadFonts = () => { export const PreloadFonts = () => {
// @ts-expect-error No types for this exist yet. const [ enabledFontFamilies ]: [
const { __experimentalSaveSpecifiedEntityEdits: saveSpecifiedEntityEdits } =
useDispatch( coreStore );
const [ enabledFontFamilies, setFontFamilies ]: [
{ {
custom: Array< FontFamily >; custom: Array< FontFamily >;
theme: Array< FontFamily >; theme: Array< FontFamily >;
@ -55,99 +44,6 @@ export const PreloadFonts = () => {
const { context } = useContext( CustomizeStoreContext ); const { context } = useContext( CustomizeStoreContext );
const { globalStylesId, installedFontFamilies } = useSelect( ( select ) => {
// @ts-expect-error No types for this exist yet.
const { __experimentalGetCurrentGlobalStylesId, getEntityRecords } =
select( coreStore );
return {
globalStylesId: __experimentalGetCurrentGlobalStylesId(),
installedFontFamilies: getEntityRecords(
'postType',
'wp_font_family',
{ _embed: true, per_page: -1 }
) as Array< {
id: number;
font_family_settings: FontFamily;
_embedded: {
font_faces: Array< {
font_face_settings: FontFace;
} >;
};
} >,
};
} );
const parsedInstalledFontFamilies = useMemo( () => {
return (
( installedFontFamilies || [] ).map( ( fontFamilyPost ) => {
return {
id: fontFamilyPost.id,
...fontFamilyPost.font_family_settings,
fontFace:
fontFamilyPost?._embedded?.font_faces.map(
( face ) => face.font_face_settings
) || [],
};
} ) || []
);
}, [ installedFontFamilies ] );
useEffect( () => {
if (
areFontsPreloaded ||
installedFontFamilies === null ||
enabledFontFamilies === null
) {
return;
}
const { custom } = enabledFontFamilies;
const enabledFontSlugs = [
...( custom ? custom.map( ( font ) => font.slug ) : [] ),
];
const fontFamiliesToEnable = parsedInstalledFontFamilies.reduce(
( acc, font ) => {
if (
enabledFontSlugs.includes( font.slug ) ||
FONT_FAMILIES_TO_INSTALL[ font.slug ] === undefined
) {
return acc;
}
return [
...acc,
{
...font,
},
];
},
[] as Array< FontFamily >
);
setFontFamilies( {
...enabledFontFamilies,
custom: [
...( enabledFontFamilies.custom ?? [] ),
...( fontFamiliesToEnable ?? [] ),
],
} );
saveSpecifiedEntityEdits( 'root', 'globalStyles', globalStylesId, [
'settings.typography.fontFamilies',
] );
areFontsPreloaded = true;
}, [
enabledFontFamilies,
globalStylesId,
installedFontFamilies,
parsedInstalledFontFamilies,
saveSpecifiedEntityEdits,
setFontFamilies,
] );
const allFontChoices = FONT_PAIRINGS.map( const allFontChoices = FONT_PAIRINGS.map(
( fontPair ) => fontPair?.settings?.typography?.fontFamilies?.theme ( fontPair ) => fontPair?.settings?.typography?.fontFamilies?.theme
); );

View File

@ -30,6 +30,10 @@ import { CustomizeStoreContext } from '~/customize-store/assembler-hub';
import { FlowType } from '~/customize-store/types'; import { FlowType } from '~/customize-store/types';
import { FontFamily } from './font-families-loader-dot-com'; import { FontFamily } from './font-families-loader-dot-com';
import { isAIFlow } from '~/customize-store/guards'; import { isAIFlow } from '~/customize-store/guards';
import {
OptInContext,
OPTIN_FLOW_STATUS,
} from '~/customize-store/assembler-hub/opt-in/context';
export const FontPairing = () => { export const FontPairing = () => {
const { aiSuggestions, isLoading } = useSelect( ( select ) => { const { aiSuggestions, isLoading } = useSelect( ( select ) => {
@ -72,6 +76,8 @@ export const FontPairing = () => {
) === 'yes' ) === 'yes'
); );
const { optInFlowStatus } = useContext( OptInContext );
const fontPairings = useMemo( () => { const fontPairings = useMemo( () => {
if ( isAIFlow( context.flowType ) ) { if ( isAIFlow( context.flowType ) ) {
return aiOnline && aiSuggestions?.lookAndFeel return aiOnline && aiSuggestions?.lookAndFeel
@ -106,7 +112,15 @@ export const FontPairing = () => {
} }
); );
if ( ! trackingAllowed || ! isFontLibraryAvailable ) { // We only show the default fonts when:
// - user did not allow tracking
// - site doesn't have the Font Library available
// - opt-in flow is still processing
if (
! trackingAllowed ||
! isFontLibraryAvailable ||
optInFlowStatus !== OPTIN_FLOW_STATUS.DONE
) {
return defaultFonts; return defaultFonts;
} }
@ -134,14 +148,15 @@ export const FontPairing = () => {
}, [ }, [
aiOnline, aiOnline,
aiSuggestions?.lookAndFeel, aiSuggestions?.lookAndFeel,
baseFontFamilies, baseFontFamilies.theme,
context.flowType, context.flowType,
custom, custom,
isFontLibraryAvailable, isFontLibraryAvailable,
optInFlowStatus,
trackingAllowed, trackingAllowed,
] ); ] );
if ( isLoading ) { if ( isLoading || optInFlowStatus === OPTIN_FLOW_STATUS.LOADING ) {
return ( return (
<div className="woocommerce-customize-store_font-pairing-spinner-container"> <div className="woocommerce-customize-store_font-pairing-spinner-container">
<Spinner /> <Spinner />

View File

@ -163,7 +163,7 @@ export const SidebarNavigationScreenHomepagePTK = ( {
const [ optInDataSharing, setIsOptInDataSharing ] = const [ optInDataSharing, setIsOptInDataSharing ] =
useState< boolean >( true ); useState< boolean >( true );
const [ isFetchingPatterns, setIsFetchingPatterns ] = useState( false ); const [ isSettingTracking, setIsSettingTracking ] = useState( false );
const optIn = () => { const optIn = () => {
trackEvent( trackEvent(
@ -324,25 +324,18 @@ export const SidebarNavigationScreenHomepagePTK = ( {
onClick={ async () => { onClick={ async () => {
optIn(); optIn();
await enableTracking(); await enableTracking();
setIsFetchingPatterns( setIsSettingTracking(
true true
); );
await apiFetch< {
success: boolean;
} >( {
path: `/wc/private/patterns`,
method: 'POST',
} );
invalidateCache();
closeModal(); closeModal();
setIsFetchingPatterns( setIsSettingTracking(
false false
); );
} } } }
variant="primary" variant="primary"
disabled={ ! optInDataSharing } disabled={ ! optInDataSharing }
> >
{ isFetchingPatterns ? ( { isSettingTracking ? (
<Spinner /> <Spinner />
) : ( ) : (
__( __(

View File

@ -24,7 +24,6 @@ import { FontPairing } from '../global-styles';
import { CustomizeStoreContext } from '../..'; import { CustomizeStoreContext } from '../..';
import { FlowType } from '~/customize-store/types'; import { FlowType } from '~/customize-store/types';
import { trackEvent } from '~/customize-store/tracking'; import { trackEvent } from '~/customize-store/tracking';
import { installFontFamilies } from '../../utils/fonts';
import { enableTracking } from '~/customize-store/design-without-ai/services'; import { enableTracking } from '~/customize-store/design-without-ai/services';
export const SidebarNavigationScreenTypography = ( { export const SidebarNavigationScreenTypography = ( {
@ -91,7 +90,7 @@ export const SidebarNavigationScreenTypography = ( {
const openModal = () => setIsModalOpen( true ); const openModal = () => setIsModalOpen( true );
const closeModal = () => setIsModalOpen( false ); const closeModal = () => setIsModalOpen( false );
const [ isFetchingFonts, setIsFetchingFonts ] = useState( false ); const [ isSettingTracking, setIsSettingTracking ] = useState( false );
const [ OptInDataSharing, setIsOptInDataSharing ] = const [ OptInDataSharing, setIsOptInDataSharing ] =
useState< boolean >( true ); useState< boolean >( true );
@ -175,17 +174,15 @@ export const SidebarNavigationScreenTypography = ( {
<Button <Button
onClick={ async () => { onClick={ async () => {
optIn(); optIn();
setIsFetchingFonts( true );
await enableTracking(); await enableTracking();
await installFontFamilies();
closeModal(); closeModal();
setIsFetchingFonts( false ); setIsSettingTracking( false );
} } } }
variant="primary" variant="primary"
disabled={ ! OptInDataSharing } disabled={ ! OptInDataSharing }
> >
{ isFetchingFonts ? ( { isSettingTracking ? (
<Spinner /> <Spinner />
) : ( ) : (
__( 'Opt in', 'woocommerce' ) __( 'Opt in', 'woocommerce' )

View File

@ -0,0 +1,4 @@
Significance: minor
Type: fix
CYS: Improve opt in flow