2023-08-29 06:00:54 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
2023-10-12 06:35:46 +00:00
|
|
|
import { assign, spawn } from 'xstate';
|
2024-03-04 19:09:36 +00:00
|
|
|
import { getQuery, updateQueryString } from '@woocommerce/navigation';
|
2023-09-22 12:43:42 +00:00
|
|
|
import { dispatch } from '@wordpress/data';
|
|
|
|
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
|
2023-08-29 06:00:54 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
|
|
|
import {
|
2023-09-20 07:27:08 +00:00
|
|
|
ColorPaletteResponse,
|
2023-08-29 06:00:54 +00:00
|
|
|
designWithAiStateMachineContext,
|
|
|
|
designWithAiStateMachineEvents,
|
2023-09-19 08:41:52 +00:00
|
|
|
LookAndToneCompletionResponse,
|
2023-09-21 01:49:00 +00:00
|
|
|
Header,
|
|
|
|
Footer,
|
2023-09-25 10:30:31 +00:00
|
|
|
HomepageTemplate,
|
2023-08-29 06:00:54 +00:00
|
|
|
} from './types';
|
2023-09-15 04:48:12 +00:00
|
|
|
import { aiWizardClosedBeforeCompletionEvent } from './events';
|
2023-09-06 06:21:09 +00:00
|
|
|
import {
|
|
|
|
businessInfoDescriptionCompleteEvent,
|
|
|
|
lookAndFeelCompleteEvent,
|
|
|
|
toneOfVoiceCompleteEvent,
|
|
|
|
} from './pages';
|
2024-04-23 17:38:06 +00:00
|
|
|
import { trackEvent } from '../tracking';
|
2023-08-29 06:00:54 +00:00
|
|
|
|
2023-11-09 01:32:47 +00:00
|
|
|
const assignStartLoadingTime = assign<
|
|
|
|
designWithAiStateMachineContext,
|
|
|
|
designWithAiStateMachineEvents
|
|
|
|
>( {
|
|
|
|
startLoadingTime: () => performance.now(),
|
|
|
|
} );
|
|
|
|
|
2023-08-29 06:00:54 +00:00
|
|
|
const assignBusinessInfoDescription = assign<
|
|
|
|
designWithAiStateMachineContext,
|
|
|
|
designWithAiStateMachineEvents
|
|
|
|
>( {
|
2023-09-07 09:05:47 +00:00
|
|
|
businessInfoDescription: ( _context, event: unknown ) => {
|
2023-08-29 06:00:54 +00:00
|
|
|
return {
|
|
|
|
descriptionText: ( event as businessInfoDescriptionCompleteEvent )
|
|
|
|
.payload,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
} );
|
2023-09-06 06:21:09 +00:00
|
|
|
|
|
|
|
const assignLookAndFeel = assign<
|
|
|
|
designWithAiStateMachineContext,
|
|
|
|
designWithAiStateMachineEvents
|
|
|
|
>( {
|
2023-11-10 06:51:00 +00:00
|
|
|
lookAndFeel: ( context, event: unknown ) => {
|
2023-09-06 06:21:09 +00:00
|
|
|
return {
|
2023-11-10 06:51:00 +00:00
|
|
|
...context.lookAndFeel,
|
2023-09-06 06:21:09 +00:00
|
|
|
choice: ( event as lookAndFeelCompleteEvent ).payload,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
} );
|
|
|
|
|
|
|
|
const assignToneOfVoice = assign<
|
|
|
|
designWithAiStateMachineContext,
|
|
|
|
designWithAiStateMachineEvents
|
|
|
|
>( {
|
2023-11-10 06:51:00 +00:00
|
|
|
toneOfVoice: ( context, event: unknown ) => {
|
2023-09-06 06:21:09 +00:00
|
|
|
return {
|
2023-11-10 06:51:00 +00:00
|
|
|
...context.toneOfVoice,
|
2023-09-06 06:21:09 +00:00
|
|
|
choice: ( event as toneOfVoiceCompleteEvent ).payload,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
} );
|
|
|
|
|
|
|
|
const assignLookAndTone = assign<
|
|
|
|
designWithAiStateMachineContext,
|
|
|
|
designWithAiStateMachineEvents
|
|
|
|
>( {
|
2023-09-07 09:05:47 +00:00
|
|
|
lookAndFeel: ( _context, event: unknown ) => {
|
2023-09-06 06:21:09 +00:00
|
|
|
return {
|
2023-09-07 09:05:47 +00:00
|
|
|
choice: ( event as { data: LookAndToneCompletionResponse } ).data
|
|
|
|
.look,
|
2023-11-10 06:51:00 +00:00
|
|
|
aiRecommended: ( event as { data: LookAndToneCompletionResponse } )
|
|
|
|
.data.look,
|
2023-09-06 06:21:09 +00:00
|
|
|
};
|
|
|
|
},
|
2023-09-07 09:05:47 +00:00
|
|
|
toneOfVoice: ( _context, event: unknown ) => {
|
2023-09-06 06:21:09 +00:00
|
|
|
return {
|
2023-09-07 09:05:47 +00:00
|
|
|
choice: ( event as { data: LookAndToneCompletionResponse } ).data
|
|
|
|
.tone,
|
2023-11-10 06:51:00 +00:00
|
|
|
aiRecommended: ( event as { data: LookAndToneCompletionResponse } )
|
|
|
|
.data.tone,
|
2023-09-06 06:21:09 +00:00
|
|
|
};
|
|
|
|
},
|
|
|
|
} );
|
|
|
|
|
2023-09-19 08:41:52 +00:00
|
|
|
const assignDefaultColorPalette = assign<
|
|
|
|
designWithAiStateMachineContext,
|
|
|
|
designWithAiStateMachineEvents
|
|
|
|
>( {
|
|
|
|
aiSuggestions: ( context, event: unknown ) => {
|
|
|
|
return {
|
|
|
|
...context.aiSuggestions,
|
|
|
|
defaultColorPalette: (
|
|
|
|
event as {
|
|
|
|
data: {
|
2023-09-20 07:27:08 +00:00
|
|
|
response: ColorPaletteResponse;
|
2023-09-19 08:41:52 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
).data.response,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
} );
|
|
|
|
|
2023-09-20 04:26:15 +00:00
|
|
|
const assignFontPairing = assign<
|
|
|
|
designWithAiStateMachineContext,
|
|
|
|
designWithAiStateMachineEvents
|
|
|
|
>( {
|
2023-10-12 06:35:46 +00:00
|
|
|
aiSuggestions: ( context ) => {
|
|
|
|
let fontPairing = context.aiSuggestions.fontPairing;
|
|
|
|
const choice = context.lookAndFeel.choice;
|
2023-10-06 08:53:52 +00:00
|
|
|
|
2023-10-12 06:35:46 +00:00
|
|
|
switch ( true ) {
|
|
|
|
case choice === 'Contemporary':
|
|
|
|
fontPairing = 'Inter + Inter';
|
|
|
|
break;
|
|
|
|
case choice === 'Classic':
|
|
|
|
fontPairing = 'Bodoni Moda + Overpass';
|
|
|
|
break;
|
|
|
|
case choice === 'Bold':
|
2023-12-20 14:41:19 +00:00
|
|
|
fontPairing = 'Rubik + Inter';
|
2023-10-12 06:35:46 +00:00
|
|
|
break;
|
2023-10-06 08:53:52 +00:00
|
|
|
}
|
|
|
|
|
2023-09-20 04:26:15 +00:00
|
|
|
return {
|
|
|
|
...context.aiSuggestions,
|
2023-10-12 06:35:46 +00:00
|
|
|
fontPairing,
|
2023-09-20 04:26:15 +00:00
|
|
|
};
|
|
|
|
},
|
|
|
|
} );
|
|
|
|
|
2023-09-21 01:49:00 +00:00
|
|
|
const assignHeader = assign<
|
|
|
|
designWithAiStateMachineContext,
|
|
|
|
designWithAiStateMachineEvents
|
|
|
|
>( {
|
|
|
|
aiSuggestions: ( context, event: unknown ) => {
|
|
|
|
return {
|
|
|
|
...context.aiSuggestions,
|
|
|
|
header: (
|
|
|
|
event as {
|
|
|
|
data: {
|
|
|
|
response: Header;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
).data.response.slug,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
} );
|
|
|
|
|
|
|
|
const assignFooter = assign<
|
|
|
|
designWithAiStateMachineContext,
|
|
|
|
designWithAiStateMachineEvents
|
|
|
|
>( {
|
|
|
|
aiSuggestions: ( context, event: unknown ) => {
|
|
|
|
return {
|
|
|
|
...context.aiSuggestions,
|
|
|
|
footer: (
|
|
|
|
event as {
|
|
|
|
data: {
|
|
|
|
response: Footer;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
).data.response.slug,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
} );
|
|
|
|
|
2023-09-25 10:30:31 +00:00
|
|
|
const assignHomepageTemplate = assign<
|
|
|
|
designWithAiStateMachineContext,
|
|
|
|
designWithAiStateMachineEvents
|
|
|
|
>( {
|
|
|
|
aiSuggestions: ( context, event: unknown ) => {
|
|
|
|
return {
|
|
|
|
...context.aiSuggestions,
|
|
|
|
homepageTemplate: (
|
|
|
|
event as {
|
|
|
|
data: {
|
|
|
|
response: HomepageTemplate;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
).data.response.homepage_template,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
} );
|
|
|
|
|
2023-09-22 12:43:42 +00:00
|
|
|
const updateWooAiStoreDescriptionOption = ( descriptionText: string ) => {
|
|
|
|
return dispatch( OPTIONS_STORE_NAME ).updateOptions( {
|
|
|
|
woo_ai_describe_store_description: descriptionText,
|
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
|
|
|
const spawnSaveDescriptionToOption = assign<
|
|
|
|
designWithAiStateMachineContext,
|
|
|
|
designWithAiStateMachineEvents,
|
|
|
|
designWithAiStateMachineEvents
|
|
|
|
>( {
|
|
|
|
spawnSaveDescriptionToOptionRef: (
|
|
|
|
context: designWithAiStateMachineContext
|
|
|
|
) =>
|
|
|
|
spawn(
|
|
|
|
() =>
|
|
|
|
updateWooAiStoreDescriptionOption(
|
|
|
|
context.businessInfoDescription.descriptionText
|
|
|
|
),
|
|
|
|
'update-woo-ai-business-description-option'
|
|
|
|
),
|
|
|
|
} );
|
|
|
|
|
2023-09-28 03:15:38 +00:00
|
|
|
const assignAPICallLoaderError = assign<
|
|
|
|
designWithAiStateMachineContext,
|
|
|
|
designWithAiStateMachineEvents
|
|
|
|
>( {
|
|
|
|
apiCallLoader: () => {
|
2024-04-23 17:38:06 +00:00
|
|
|
trackEvent( 'customize_your_store_ai_wizard_error' );
|
2023-11-09 01:32:47 +00:00
|
|
|
|
2023-09-28 03:15:38 +00:00
|
|
|
return {
|
|
|
|
hasErrors: true,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
} );
|
|
|
|
|
2023-09-06 06:21:09 +00:00
|
|
|
const logAIAPIRequestError = () => {
|
|
|
|
// log AI API request error
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
console.log( 'API Request error' );
|
|
|
|
};
|
|
|
|
|
2023-09-12 06:32:50 +00:00
|
|
|
const updateQueryStep = (
|
|
|
|
_context: unknown,
|
|
|
|
_evt: unknown,
|
|
|
|
{ action }: { action: unknown }
|
|
|
|
) => {
|
|
|
|
const { path } = getQuery() as { path: string };
|
|
|
|
const step = ( action as { step: string } ).step;
|
|
|
|
const pathFragments = path.split( '/' ); // [0] '', [1] 'customize-store', [2] cys step slug [3] design-with-ai step slug
|
|
|
|
if (
|
|
|
|
pathFragments[ 1 ] === 'customize-store' &&
|
|
|
|
pathFragments[ 2 ] === 'design-with-ai'
|
|
|
|
) {
|
|
|
|
if ( pathFragments[ 3 ] !== step ) {
|
|
|
|
// this state machine is only concerned with [2], so we ignore changes to [3]
|
|
|
|
// [1] is handled by router at root of wc-admin
|
|
|
|
updateQueryString(
|
|
|
|
{},
|
|
|
|
`/customize-store/design-with-ai/${ step }`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-09-15 04:48:12 +00:00
|
|
|
const recordTracksStepViewed = (
|
|
|
|
_context: unknown,
|
|
|
|
_event: unknown,
|
|
|
|
{ action }: { action: unknown }
|
|
|
|
) => {
|
|
|
|
const { step } = action as { step: string };
|
2024-04-23 17:38:06 +00:00
|
|
|
trackEvent( 'customize_your_store_ai_wizard_step_view', {
|
2023-09-15 04:48:12 +00:00
|
|
|
step,
|
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
|
|
|
const recordTracksStepClosed = (
|
|
|
|
_context: unknown,
|
|
|
|
event: aiWizardClosedBeforeCompletionEvent
|
|
|
|
) => {
|
|
|
|
const { step } = event.payload;
|
2024-04-23 17:38:06 +00:00
|
|
|
trackEvent( `customize_your_store_ai_wizard_step_close`, {
|
2023-09-15 04:48:12 +00:00
|
|
|
step: step.replaceAll( '-', '_' ),
|
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
|
|
|
const recordTracksStepCompleted = (
|
|
|
|
_context: unknown,
|
|
|
|
_event: unknown,
|
|
|
|
{ action }: { action: unknown }
|
|
|
|
) => {
|
|
|
|
const { step } = action as { step: string };
|
2024-04-23 17:38:06 +00:00
|
|
|
trackEvent( 'customize_your_store_ai_wizard_step_complete', {
|
2023-09-15 04:48:12 +00:00
|
|
|
step,
|
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
2024-03-04 19:09:36 +00:00
|
|
|
const redirectToAssemblerHub = async () => {
|
2024-02-05 11:36:33 +00:00
|
|
|
// This is a workaround to update the "activeThemeHasMods" in the parent's machine
|
|
|
|
// state context. We should find a better way to do this using xstate actions,
|
|
|
|
// since state machines should rely only on their context.
|
|
|
|
// Will be fixed on: https://github.com/woocommerce/woocommerce/issues/44349
|
|
|
|
// This is needed because the iframe loads the entire Customize Store app.
|
|
|
|
// This means that the iframe instance will have different state machines
|
|
|
|
// than the parent window.
|
|
|
|
// Check https://github.com/woocommerce/woocommerce/pull/44206 for more details.
|
|
|
|
window.parent.__wcCustomizeStore.activeThemeHasMods = true;
|
2023-09-26 12:34:47 +00:00
|
|
|
};
|
|
|
|
|
2023-08-29 06:00:54 +00:00
|
|
|
export const actions = {
|
2023-11-09 01:32:47 +00:00
|
|
|
assignStartLoadingTime,
|
2023-08-29 06:00:54 +00:00
|
|
|
assignBusinessInfoDescription,
|
2023-09-06 06:21:09 +00:00
|
|
|
assignLookAndFeel,
|
|
|
|
assignToneOfVoice,
|
|
|
|
assignLookAndTone,
|
2023-09-19 08:41:52 +00:00
|
|
|
assignDefaultColorPalette,
|
2023-09-20 04:26:15 +00:00
|
|
|
assignFontPairing,
|
2023-09-21 01:49:00 +00:00
|
|
|
assignHeader,
|
|
|
|
assignFooter,
|
2023-09-25 10:30:31 +00:00
|
|
|
assignHomepageTemplate,
|
2023-09-28 03:15:38 +00:00
|
|
|
assignAPICallLoaderError,
|
2023-09-06 06:21:09 +00:00
|
|
|
logAIAPIRequestError,
|
2023-09-12 06:32:50 +00:00
|
|
|
updateQueryStep,
|
2023-09-15 04:48:12 +00:00
|
|
|
recordTracksStepViewed,
|
|
|
|
recordTracksStepClosed,
|
|
|
|
recordTracksStepCompleted,
|
2023-09-22 12:43:42 +00:00
|
|
|
spawnSaveDescriptionToOption,
|
2023-09-26 12:34:47 +00:00
|
|
|
redirectToAssemblerHub,
|
2023-08-29 06:00:54 +00:00
|
|
|
};
|