/** * External dependencies */ import { Loader } from '@woocommerce/onboarding'; import { __ } from '@wordpress/i18n'; import { useEffect, useState } from '@wordpress/element'; /** * Internal dependencies */ import analyzingYourResponses from '../../assets/images/loader-analyzing-your-responses.svg'; import designingTheBestLook from '../../assets/images/loader-designing-the-best-look.svg'; import comparingTheTopPerformingStores from '../../assets/images/loader-comparing-top-performing-stores.svg'; import assemblingAiOptimizedStore from '../../assets/images/loader-assembling-ai-optimized-store.svg'; import applyingFinishingTouches from '../../assets/images/loader-applying-the-finishing-touches.svg'; import generatingContent from '../../assets/images/loader-generating-content.svg'; import openingTheDoors from '../../assets/images/loader-opening-the-doors.svg'; const loaderSteps = [ { title: __( 'Analyzing your responses', 'woocommerce' ), image: ( { ), progress: 14, }, { title: __( 'Comparing the top performing stores', 'woocommerce' ), image: ( { ), progress: 28, }, { title: __( 'Designing the best look for your business', 'woocommerce' ), image: ( { ), progress: 42, }, { title: __( 'Generating content', 'woocommerce' ), image: ( { ), progress: 56, }, { title: __( 'Assembling your AI-optimized store', 'woocommerce' ), image: ( { ), progress: 70, }, { title: __( 'Applying the finishing touches', 'woocommerce' ), image: ( { ), progress: 84, }, { title: __( 'Opening the doors', 'woocommerce' ), image: ( { ), progress: 100, }, ]; // Make the loader last longer and provide a smoother progress by duplicating the steps. const createAugmentedSteps = ( steps: typeof loaderSteps, numOfDupes: number ) => { // Duplicate each step, so we can animate each one // (e.g. each step will be duplicated 3 times, and each duplicate will // have different progress) const augmentedSteps = steps .map( ( item, index, array ) => { // Get the next item in the array const nextItem = array[ index + 1 ]; // If there is no next item, we're at the end of the array // so just return the current item if ( ! nextItem ) return [ item ]; // If there is a next item, we're not at the end of the array // so return the current item, plus duplicates const duplicates = [ item ]; const progressIncreaseBy = ( nextItem.progress - item.progress ) / numOfDupes; for ( let i = 0; i < numOfDupes; i++ ) { duplicates.push( { ...item, progress: item.progress + ( i + 1 ) * progressIncreaseBy, } ); } return duplicates; } ) .flat(); return augmentedSteps; }; // Loader for the API call without the last frame. export const ApiCallLoader = () => { const [ progress, setProgress ] = useState( 5 ); useEffect( () => { const preload = ( src: string ) => { const img = new Image(); img.src = src; img.onload = () => {}; }; // We preload the these images to avoid flickering. We only need to preload them because the others are small enough to be inlined in base64. preload( assemblingAiOptimizedStore ); preload( openingTheDoors ); }, [] ); const augmentedSteps = createAugmentedSteps( loaderSteps.slice( 0, -1 ), 10 ); return ( { // to get around bad set state timing issue setTimeout( () => { setProgress( augmentedSteps[ index ].progress ); }, 0 ); } } > { augmentedSteps.map( ( step, index ) => ( { step.image } { step.title } ) ) } ); }; export const AssembleHubLoader = () => { // Show the last two steps of the loader so that the last frame is the shortest time possible const augmentedSteps = createAugmentedSteps( loaderSteps.slice( -2 ), 10 ); const [ progress, setProgress ] = useState( augmentedSteps[ 0 ].progress ); return ( { // to get around bad set state timing issue setTimeout( () => { setProgress( augmentedSteps[ index ].progress ); }, 0 ); } } > { augmentedSteps.map( ( step, index ) => ( { step.image } { step.title } ) ) } ); };