Detect cache when launching store (#48586)

* Remove duplicate

* Add is-coming-soon-page endpoint

* Render invalid cache warning

* Use meta tag instead

* Add url for debug purpose

* Use a different copy for the congrats page when the cache is still shown

* Clean up unused codes

* Guard the api with permission

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

* Reuse $is_fse_theme

* Lint fixes

* Update 48586-update-48516-detect-cache-when-launching-store

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

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

* Update plugins/woocommerce/changelog/48586-update-48516-detect-cache-when-launching-store

Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com>

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

* changed to js method

* Add changefile(s) from automation for the following project(s): @woocommerce/data, woocommerce

* empty commit to trigger CI

* Add changefile(s) from automation for the following project(s): @woocommerce/data, woocommerce

* empty commit to trigger CI

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com>
Co-authored-by: rjchow <me@rjchow.com>
This commit is contained in:
Moon 2024-06-19 03:23:29 -07:00 committed by GitHub
parent efc9dc4c62
commit f95d8b240b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 184 additions and 16 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: tweak
Verify if the coming soon cache is displayed when launching the store and alerts the user if it is still present.

View File

@ -1,6 +1,6 @@
export type Settings = { export type Settings = {
[ key: string ]: unknown; [ key: string ]: unknown;
} & { } & { siteUrl?: string; shopUrl?: string } & {
general?: { general?: {
[ key: string ]: string; [ key: string ]: string;
}; };

View File

@ -29,12 +29,14 @@ export type CongratsProps = {
isWooExpress: boolean; isWooExpress: boolean;
completeSurvey: () => void; completeSurvey: () => void;
children?: React.ReactNode; children?: React.ReactNode;
siteIsShowingCachedContent: boolean;
}; };
export const Congrats = ( { export const Congrats = ( {
hasCompleteSurvey, hasCompleteSurvey,
isWooExpress, isWooExpress,
completeSurvey, completeSurvey,
siteIsShowingCachedContent,
children, children,
}: CongratsProps ) => { }: CongratsProps ) => {
const copyLink = __( 'Copy link', 'woocommerce' ); const copyLink = __( 'Copy link', 'woocommerce' );
@ -116,16 +118,42 @@ export const Congrats = ( {
</div> </div>
<div className="woocommerce-launch-store__congrats-content"> <div className="woocommerce-launch-store__congrats-content">
<h1 className="woocommerce-launch-store__congrats-heading"> <h1 className="woocommerce-launch-store__congrats-heading">
{ __( { siteIsShowingCachedContent
'Congratulations! Your store is now live', ? __(
'woocommerce' 'Congratulations! Your store will launch soon',
) } 'woocommerce'
)
: __(
'Congratulations! Your store is now live',
'woocommerce'
) }
</h1> </h1>
<h2 className="woocommerce-launch-store__congrats-subheading"> <h2 className="woocommerce-launch-store__congrats-subheading">
{ __( { siteIsShowingCachedContent
"You've successfully launched your store and are ready to start selling! We can't wait to see your business grow.", ? createInterpolateElement(
'woocommerce' __(
) } 'Itll be ready to view as soon as your <link></link> have updated. Please wait, or contact your web host to find out how to do this manually.',
'woocommerce'
),
{
link: (
<a
href="https://woocommerce.com/document/server-caches/"
target="_blank"
rel="noreferrer"
>
{ __(
'server caches',
'woocommerce'
) }
</a>
),
}
)
: __(
"You've successfully launched your store and are ready to start selling! We can't wait to see your business grow.",
'woocommerce'
) }
</h2> </h2>
<div className="woocommerce-launch-store__congrats-midsection-container"> <div className="woocommerce-launch-store__congrats-midsection-container">
<div className="woocommerce-launch-store__congrats-visit-store"> <div className="woocommerce-launch-store__congrats-visit-store">

View File

@ -43,6 +43,9 @@ export const LaunchYourStoreSuccess = ( props: MainContentComponentProps ) => {
} }
isWooExpress={ isWooExpress() } isWooExpress={ isWooExpress() }
completeSurvey={ completeSurvey } completeSurvey={ completeSurvey }
siteIsShowingCachedContent={
props.context.siteIsShowingCachedContent
}
> >
<h2 className="woocommerce-launch-store__congrats-main-actions-title"> <h2 className="woocommerce-launch-store__congrats-main-actions-title">
{ __( "What's next?", 'woocommerce' ) } { __( "What's next?", 'woocommerce' ) }

View File

@ -99,6 +99,9 @@
letter-spacing: -0.1px; letter-spacing: -0.1px;
margin: 4px 0 0; margin: 4px 0 0;
max-width: 560px; max-width: 560px;
a {
text-decoration: none;
}
} }
.woocommerce-launch-store__congrats-main-actions-title { .woocommerce-launch-store__congrats-main-actions-title {

View File

@ -27,6 +27,7 @@ export type MainContentMachineContext = {
allTasklists: TaskListType[]; allTasklists: TaskListType[];
activePlugins: string[]; activePlugins: string[];
}; };
siteIsShowingCachedContent: boolean;
}; };
export type MainContentComponentProps = LaunchYourStoreComponentProps & { export type MainContentComponentProps = LaunchYourStoreComponentProps & {
@ -34,6 +35,7 @@ export type MainContentComponentProps = LaunchYourStoreComponentProps & {
}; };
export type MainContentMachineEvents = export type MainContentMachineEvents =
| { type: 'SHOW_LAUNCH_STORE_SUCCESS' } | { type: 'SHOW_LAUNCH_STORE_SUCCESS' }
| { type: 'SHOW_LAUNCH_STORE_PENDING_CACHE' }
| { type: 'EXTERNAL_URL_UPDATE' } | { type: 'EXTERNAL_URL_UPDATE' }
| { type: 'SHOW_LOADING' } | { type: 'SHOW_LOADING' }
| congratsEvents; | congratsEvents;
@ -54,6 +56,9 @@ export const mainContentMachine = setup( {
) => { ) => {
updateQueryParams( params ); updateQueryParams( params );
}, },
assignSiteCachedStatus: assign( {
siteIsShowingCachedContent: true,
} ),
}, },
guards: { guards: {
hasContentLocation: ( hasContentLocation: (
@ -78,6 +83,7 @@ export const mainContentMachine = setup( {
allTasklists: [], allTasklists: [],
activePlugins: [], activePlugins: [],
}, },
siteIsShowingCachedContent: false,
}, },
invoke: { invoke: {
id: 'contentQueryParamListener', id: 'contentQueryParamListener',
@ -149,6 +155,10 @@ export const mainContentMachine = setup( {
SHOW_LAUNCH_STORE_SUCCESS: { SHOW_LAUNCH_STORE_SUCCESS: {
target: '#launchStoreSuccess', target: '#launchStoreSuccess',
}, },
SHOW_LAUNCH_STORE_PENDING_CACHE: {
actions: [ 'assignSiteCachedStatus' ],
target: '#launchStoreSuccess',
},
SHOW_LOADING: { SHOW_LOADING: {
target: '#loading', target: '#loading',
}, },

View File

@ -9,12 +9,18 @@ import {
fromPromise, fromPromise,
assign, assign,
spawnChild, spawnChild,
enqueueActions,
} from 'xstate5'; } from 'xstate5';
import React from 'react'; import React from 'react';
import clsx from 'clsx'; import clsx from 'clsx';
import { getQuery, navigateTo } from '@woocommerce/navigation'; import { getQuery, navigateTo } from '@woocommerce/navigation';
import { OPTIONS_STORE_NAME, TaskListType, TaskType } from '@woocommerce/data'; import {
import { dispatch } from '@wordpress/data'; OPTIONS_STORE_NAME,
SETTINGS_STORE_NAME,
TaskListType,
TaskType,
} from '@woocommerce/data';
import { dispatch, resolveSelect } from '@wordpress/data';
import { recordEvent } from '@woocommerce/tracks'; import { recordEvent } from '@woocommerce/tracks';
import apiFetch from '@wordpress/api-fetch'; import apiFetch from '@wordpress/api-fetch';
@ -44,6 +50,7 @@ export type SidebarMachineContext = {
launchStoreError?: { launchStoreError?: {
message: string; message: string;
}; };
siteIsShowingCachedContent?: boolean;
}; };
export type SidebarComponentProps = LaunchYourStoreComponentProps & { export type SidebarComponentProps = LaunchYourStoreComponentProps & {
context: SidebarMachineContext; context: SidebarMachineContext;
@ -81,6 +88,64 @@ const getTestOrderCount = async () => {
return result.count; return result.count;
}; };
export const pageHasComingSoonMetaTag = async ( {
url,
}: {
url: string;
} ): Promise< boolean > => {
try {
const response = await fetch( url, {
method: 'GET',
credentials: 'omit',
cache: 'no-store',
} );
if ( ! response.ok ) {
throw new Error( `Failed to fetch ${ url }` );
}
const html = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString( html, 'text/html' );
const metaTag = doc.querySelector(
'meta[name="woo-coming-soon-page"]'
);
if ( metaTag ) {
return true;
}
return false;
} catch ( error ) {
throw new Error( `Error fetching ${ url }: ${ error }` );
}
};
const getSiteCachedStatus = async () => {
const settings = await resolveSelect( SETTINGS_STORE_NAME ).getSettings(
'wc_admin'
);
// if store URL exists, check both storeUrl and siteUrl otherwise only check siteUrl
// we want to check both because there's a chance that caching is especially disabled for woocommerce pages, e.g WPEngine
const requests = [] as Promise< boolean >[];
if ( settings?.shopUrl ) {
requests.push(
pageHasComingSoonMetaTag( {
url: settings.shopUrl,
} )
);
}
if ( settings?.siteUrl ) {
requests.push(
pageHasComingSoonMetaTag( {
url: settings.siteUrl,
} )
);
}
const results = await Promise.all( requests );
return results.some( ( result ) => result );
};
const deleteTestOrders = async ( { const deleteTestOrders = async ( {
input, input,
}: { }: {
@ -154,6 +219,10 @@ export const sidebarMachine = setup( {
( { context } ) => context.mainContentMachineRef, ( { context } ) => context.mainContentMachineRef,
{ type: 'SHOW_LAUNCH_STORE_SUCCESS' } { type: 'SHOW_LAUNCH_STORE_SUCCESS' }
), ),
showLaunchStorePendingCache: sendTo(
( { context } ) => context.mainContentMachineRef,
{ type: 'SHOW_LAUNCH_STORE_PENDING_CACHE' }
),
showLoadingPage: sendTo( showLoadingPage: sendTo(
( { context } ) => context.mainContentMachineRef, ( { context } ) => context.mainContentMachineRef,
{ type: 'SHOW_LOADING' } { type: 'SHOW_LOADING' }
@ -189,6 +258,11 @@ export const sidebarMachine = setup( {
success success
); );
}, },
recordStoreLaunchCachedContentDetected: () => {
recordEvent(
'launch_your_store_hub_store_launch_cached_content_detected'
);
},
}, },
guards: { guards: {
hasSidebarLocation: ( hasSidebarLocation: (
@ -203,11 +277,15 @@ export const sidebarMachine = setup( {
'woocommerce-payments' 'woocommerce-payments'
); );
}, },
siteIsShowingCachedContent: ( { context } ) => {
return !! context.siteIsShowingCachedContent;
},
}, },
actors: { actors: {
sidebarQueryParamListener, sidebarQueryParamListener,
getTasklist: fromPromise( getLysTasklist ), getTasklist: fromPromise( getLysTasklist ),
getTestOrderCount: fromPromise( getTestOrderCount ), getTestOrderCount: fromPromise( getTestOrderCount ),
getSiteCachedStatus: fromPromise( getSiteCachedStatus ),
updateLaunchStoreOptions: fromPromise( launchStoreAction ), updateLaunchStoreOptions: fromPromise( launchStoreAction ),
deleteTestOrders: fromPromise( deleteTestOrders ), deleteTestOrders: fromPromise( deleteTestOrders ),
fetchCongratsData, fetchCongratsData,
@ -317,13 +395,13 @@ export const sidebarMachine = setup( {
{ {
src: 'updateLaunchStoreOptions', src: 'updateLaunchStoreOptions',
onDone: { onDone: {
target: '#storeLaunchSuccessful',
actions: [ actions: [
{ {
type: 'recordStoreLaunchResults', type: 'recordStoreLaunchResults',
params: { success: true }, params: { success: true },
}, },
], ],
target: 'checkingForCachedContent',
}, },
onError: { onError: {
actions: [ actions: [
@ -360,6 +438,23 @@ export const sidebarMachine = setup( {
}, },
], ],
}, },
checkingForCachedContent: {
invoke: [
{
src: 'getSiteCachedStatus',
onDone: {
target: '#storeLaunchSuccessful',
actions: assign( {
siteIsShowingCachedContent: ( { event } ) =>
event.output,
} ),
},
onError: {
target: '#storeLaunchSuccessful',
},
},
],
},
}, },
}, },
storeLaunchSuccessful: { storeLaunchSuccessful: {
@ -373,7 +468,18 @@ export const sidebarMachine = setup( {
content: 'launch-store-success', content: 'launch-store-success',
}, },
}, },
{ type: 'showLaunchStoreSuccessPage' }, enqueueActions( ( { check, enqueue } ) => {
if ( check( 'siteIsShowingCachedContent' ) ) {
enqueue( {
type: 'showLaunchStorePendingCache',
} );
enqueue( {
type: 'recordStoreLaunchCachedContentDetected',
} );
return;
}
enqueue( { type: 'showLaunchStoreSuccessPage' } );
} ),
], ],
}, },
openExternalUrl: { openExternalUrl: {

View File

@ -0,0 +1,4 @@
Significance: minor
Type: tweak
Verify if the coming soon cache is displayed when launching the store and alerts the user if it is still present.

View File

@ -52,17 +52,27 @@ class ComingSoonRequestHandler {
$coming_soon_template = get_query_template( 'coming-soon' ); $coming_soon_template = get_query_template( 'coming-soon' );
if ( ! wc_current_theme_is_fse_theme() && $this->coming_soon_helper->is_store_coming_soon() ) { $is_fse_theme = wc_current_theme_is_fse_theme();
$is_store_coming_soon = $this->coming_soon_helper->is_store_coming_soon();
if ( ! $is_fse_theme && $is_store_coming_soon ) {
get_header(); get_header();
} }
add_action(
'wp_head',
function () {
echo "<meta name='woo-coming-soon-page' content='yes'>";
}
);
include $coming_soon_template; include $coming_soon_template;
if ( ! wc_current_theme_is_fse_theme() && $this->coming_soon_helper->is_store_coming_soon() ) { if ( ! $is_fse_theme && $is_store_coming_soon ) {
get_footer(); get_footer();
} }
if ( wc_current_theme_is_fse_theme() ) { if ( $is_fse_theme ) {
// Since we've already rendered a template, return null to ensure no other template is rendered. // Since we've already rendered a template, return null to ensure no other template is rendered.
return null; return null;
} else { } else {