Skip to content

Commit

Permalink
CYS - Ensure the offline modal is displayed whenever AI is unavailable (
Browse files Browse the repository at this point in the history
#42949)

* Show the ai offline modal independently from the onboarding tour

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

* Remove log

* Fix tests

* Add customizing parameter to avoid showing the aioffline modal when customizing

---------

Co-authored-by: github-actions <github-actions@github.com>
  • Loading branch information
albarin and github-actions committed Dec 21, 2023
1 parent d1a8f91 commit b3db937
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
useViewportMatch,
} from '@wordpress/compose';
import { __ } from '@wordpress/i18n';
import { useState, useContext } from '@wordpress/element';
import { useState, useContext, useEffect } from '@wordpress/element';
import {
// @ts-ignore No types for this exist yet.
__unstableMotion as motion,
Expand Down Expand Up @@ -47,17 +47,54 @@ import { OnboardingTour, useOnboardingTour } from './onboarding-tour';
import { HighlightedBlockContextProvider } from './context/highlighted-block-context';
import { Transitional } from '../transitional';
import { CustomizeStoreContext } from './';
import { recordEvent } from '@woocommerce/tracks';
import { AiOfflineModal } from '~/customize-store/assembler-hub/onboarding-tour/ai-offline-modal';
import { useQuery } from '@woocommerce/navigation';

const { useGlobalStyle } = unlock( blockEditorPrivateApis );

const ANIMATION_DURATION = 0.5;

export const Layout = () => {
const [ logoBlockIds, setLogoBlockIds ] = useState< Array< string > >( [] );

const { sendEvent, currentState, context } = useContext(
CustomizeStoreContext
);

const aiOnline = context.aiOnline;
const { customizing } = useQuery();

const [ showAiOfflineModal, setShowAiOfflineModal ] = useState(
! aiOnline && customizing !== 'true'
);

useEffect( () => {
setShowAiOfflineModal( ! aiOnline && customizing !== 'true' );
}, [ aiOnline, customizing ] );

// This ensures the edited entity id and type are initialized properly.
useInitEditedEntityFromURL();
const { shouldTourBeShown, isResizeHandleVisible, ...onboardingTourProps } =
useOnboardingTour();
const {
shouldTourBeShown,
isResizeHandleVisible,
setShowWelcomeTour,
onClose,
...onboardingTourProps
} = useOnboardingTour();

const takeTour = () => {
// Click on "Take a tour" button
recordEvent( 'customize_your_store_assembler_hub_tour_start' );
setShowWelcomeTour( false );
setShowAiOfflineModal( false );
};

const skipTour = () => {
recordEvent( 'customize_your_store_assembler_hub_tour_skip' );
onClose();
setShowAiOfflineModal( false );
};

const isMobileViewport = useViewportMatch( 'medium', '<' );
const disableMotion = useReducedMotion();
Expand All @@ -71,9 +108,6 @@ export const Layout = () => {
const { record: template } = useEditedEntityRecord();
const { id: templateId, type: templateType } = template;

const { sendEvent, currentState, context } = useContext(
CustomizeStoreContext
);
const [ isSurveyOpen, setSurveyOpen ] = useState( false );
const editor = <Editor isLoading={ isEditorLoading } />;

Expand Down Expand Up @@ -206,8 +240,23 @@ export const Layout = () => {
) }
</div>
</div>
{ ! isEditorLoading && shouldTourBeShown && (
<OnboardingTour { ...onboardingTourProps } />
{ ! isEditorLoading &&
shouldTourBeShown &&
! showAiOfflineModal && (
<OnboardingTour
skipTour={ skipTour }
takeTour={ takeTour }
onClose={ onClose }
{ ...onboardingTourProps }
/>
) }

{ ! isEditorLoading && showAiOfflineModal && (
<AiOfflineModal
shouldTourBeShown={ shouldTourBeShown }
skipTour={ skipTour }
takeTour={ takeTour }
/>
) }
</EntityProvider>
</EntityProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* External dependencies
*/
import { Button, Modal } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

type AiOfflineModalProps = {
skipTour: () => void;
takeTour: () => void;
shouldTourBeShown: boolean;
};

export const AiOfflineModal = ( {
skipTour,
takeTour,
shouldTourBeShown,
}: AiOfflineModalProps ) => {
return (
<Modal
className="woocommerce-customize-store__onboarding-welcome-modal"
title={ __( 'Welcome to your store!', 'woocommerce' ) }
onRequestClose={ skipTour }
shouldCloseOnClickOutside={ false }
>
<span className="woocommerce-customize-store__title">
{ __(
'Our AI tool had a few issues generating your content.',
'woocommerce'
) }
</span>
<p>
{ __(
"But don't let that stop you! Start customizing the look and feel of your store by adding your logo and selecting your colors and layout. ",
'woocommerce'
) }
{ shouldTourBeShown &&
__(
"Take a quick tour to discover what's possible.",
'woocommerce'
) }
</p>
{ shouldTourBeShown && (
<div className="woocommerce-customize-store__design-change-warning-modal-footer">
<Button onClick={ skipTour } variant="link">
{ __( 'Skip', 'woocommerce' ) }
</Button>
<Button onClick={ takeTour } variant="primary">
{ __( 'Take a tour', 'woocommerce' ) }
</Button>
</div>
) }
</Modal>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,80 +2,34 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { useContext, useState } from '@wordpress/element';
import { useState } from '@wordpress/element';
import { TourKit, TourKitTypes } from '@woocommerce/components';
import { recordEvent } from '@woocommerce/tracks';
import { Button, Modal } from '@wordpress/components';

/**
* Internal dependencies
*/
import { CustomizeStoreContext } from '..';
export * from './use-onboarding-tour';

type OnboardingTourProps = {
onClose: () => void;
skipTour: () => void;
takeTour: () => void;
showWelcomeTour: boolean;
setShowWelcomeTour: ( show: boolean ) => void;
setIsResizeHandleVisible: ( isVisible: boolean ) => void;
};

export const OnboardingTour = ( {
onClose,
setShowWelcomeTour,
skipTour,
takeTour,
showWelcomeTour,
setIsResizeHandleVisible,
}: OnboardingTourProps ) => {
const [ placement, setPlacement ] =
useState< TourKitTypes.WooConfig[ 'placement' ] >( 'left' );

const { context } = useContext( CustomizeStoreContext );
const aiOnline = context.aiOnline;

if ( showWelcomeTour ) {
const takeTour = () => {
// Click on "Take a tour" button
recordEvent( 'customize_your_store_assembler_hub_tour_start' );
setShowWelcomeTour( false );
};

const skipTour = () => {
recordEvent( 'customize_your_store_assembler_hub_tour_skip' );
onClose();
};

if ( ! aiOnline ) {
return (
<Modal
className="woocommerce-customize-store__onboarding-welcome-modal"
title={ __( 'Welcome to your store!', 'woocommerce' ) }
onRequestClose={ skipTour }
shouldCloseOnClickOutside={ false }
>
<span className="woocommerce-customize-store__title">
{ __(
'Our AI tool had a few issues generating your content.',
'woocommerce'
) }
</span>
<p>
{ __(
"But don't let that stop you! Start customizing the look and feel of your store by adding your logo and selecting your colors and layout. Take a quick tour to discover what's possible.",
'woocommerce'
) }
</p>
<div className="woocommerce-customize-store__design-change-warning-modal-footer">
<Button onClick={ skipTour } variant="link">
{ __( 'Skip', 'woocommerce' ) }
</Button>
<Button onClick={ takeTour } variant="primary">
{ __( 'Take a tour', 'woocommerce' ) }
</Button>
</div>
</Modal>
);
}

return (
<TourKit
config={ {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ jest.mock( '../../', () => ( {
describe( 'OnboardingTour', () => {
let props: {
onClose: jest.Mock;
skipTour: jest.Mock;
takeTour: jest.Mock;
setShowWelcomeTour: jest.Mock;
showWelcomeTour: boolean;
setIsResizeHandleVisible: ( isVisible: boolean ) => void;
Expand All @@ -30,6 +32,8 @@ describe( 'OnboardingTour', () => {
beforeEach( () => {
props = {
onClose: jest.fn(),
skipTour: jest.fn(),
takeTour: jest.fn(),
setShowWelcomeTour: jest.fn(),
showWelcomeTour: true,
setIsResizeHandleVisible: jest.fn(),
Expand Down Expand Up @@ -61,9 +65,7 @@ describe( 'OnboardingTour', () => {
} )
.click();

expect( recordEvent ).toHaveBeenCalledWith(
'customize_your_store_assembler_hub_tour_start'
);
expect( props.takeTour ).toHaveBeenCalled();
} );

it( 'should record an event when clicking on "Skip" button', () => {
Expand All @@ -75,12 +77,10 @@ describe( 'OnboardingTour', () => {
} )
.click();

expect( recordEvent ).toHaveBeenCalledWith(
'customize_your_store_assembler_hub_tour_skip'
);
expect( props.skipTour ).toHaveBeenCalled();
} );

it( 'should record an event when clicking on "Skip" button', () => {
it( 'should record an event when clicking on the "Close Tour" button', () => {
render( <OnboardingTour { ...props } showWelcomeTour={ false } /> );

screen
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,11 @@ export const ExistingAiThemeBanner = ( {
recordEvent( 'customize_your_store_intro_customize_click' );
navigateOrParent(
window,
getNewPath( {}, '/customize-store/assembler-hub', {} )
getNewPath(
{ customizing: true },
'/customize-store/assembler-hub',
{}
)
);
} }
bannerButtonText={ __( 'Customize', 'woocommerce' ) }
Expand Down
4 changes: 4 additions & 0 deletions plugins/woocommerce/changelog/42949-show-ai-offline-modal
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: enhancement

Ensure the offline modal is displayed whenever AI is unavailable and not just on the initial store setup.

0 comments on commit b3db937

Please sign in to comment.