@@ -39,8 +37,6 @@ export default function CriticalCssMeta() {
cssState={ cssState }
isCloud={ false }
showFatalError={ showFatalError }
- hasRetried={ hasRetried }
- retry={ retry }
highlightRegenerateButton={ !! regenerateReason }
extraText={ __(
'Remember to regenerate each time you make changes that affect your HTML or CSS structure.',
diff --git a/projects/plugins/boost/app/assets/src/js/features/critical-css/lib/critical-css-errors.ts b/projects/plugins/boost/app/assets/src/js/features/critical-css/lib/critical-css-errors.ts
index 52260adb4b0b3..7967660eead18 100644
--- a/projects/plugins/boost/app/assets/src/js/features/critical-css/lib/critical-css-errors.ts
+++ b/projects/plugins/boost/app/assets/src/js/features/critical-css/lib/critical-css-errors.ts
@@ -34,6 +34,11 @@ export function isFatalError( cssState: CriticalCssState ): boolean {
return false;
}
+ // If there are no providers, the state is being re-initialized. So dismiss any show-stopper errors.
+ if ( cssState.providers.length === 0 ) {
+ return false;
+ }
+
const hasActiveProvider = cssState.providers.some(
provider => provider.status === 'success' || provider.status === 'pending'
);
diff --git a/projects/plugins/boost/app/assets/src/js/features/critical-css/lib/stores/critical-css-state.ts b/projects/plugins/boost/app/assets/src/js/features/critical-css/lib/stores/critical-css-state.ts
index ec0b282d2250b..b5e7da70af7b7 100644
--- a/projects/plugins/boost/app/assets/src/js/features/critical-css/lib/stores/critical-css-state.ts
+++ b/projects/plugins/boost/app/assets/src/js/features/critical-css/lib/stores/critical-css-state.ts
@@ -57,14 +57,20 @@ export function criticalCssErrorState( message: string ): CriticalCssState {
* All Critical CSS State actions return a success flag and the new state. This hook wraps the
* common logic for handling the result of these actions.
*
- * @param {string} action - The name of the action.
- * @param {z.ZodSchema} schema - The schema for the action request.
- * @param {Function} onSuccess - Optional callback for handling the new state.
+ * @param {string} action - The name of the action.
+ * @param {z.ZodSchema} schema - The schema for the action request.
+ * @param {CriticalCssState} optimisticState - The state to use for optimistic updates.
+ * @param {Function} onSuccess - Optional callback for handling the new state.
*/
function useCriticalCssAction<
ActionSchema extends z.ZodSchema,
ActionRequestData extends z.infer< ActionSchema >,
->( action: string, schema: ActionRequestData, onSuccess?: ( state: CriticalCssState ) => void ) {
+>(
+ action: string,
+ schema: ActionRequestData,
+ optimisticState?: CriticalCssState,
+ onSuccess?: ( state: CriticalCssState ) => void
+) {
const responseSchema = z.object( {
success: z.boolean(),
state: CriticalCssStateSchema,
@@ -90,6 +96,13 @@ function useCriticalCssAction<
action_response: responseSchema,
},
callbacks: {
+ optimisticUpdate: ( _requestData, state: CriticalCssState ) => {
+ if ( optimisticState ) {
+ return optimisticState;
+ }
+
+ return state;
+ },
onResult: ( result, _state ): CriticalCssState => {
if ( result.success ) {
if ( onSuccess ) {
@@ -150,10 +163,23 @@ export function useSetProviderErrorsAction() {
/**
* Hook which creates a callable action for regenerating Critical CSS.
+ *
+ * @param {Function} callback - Optional callback to call when a regeneration starts successfully.
*/
-export function useRegenerateCriticalCssAction() {
+export function useRegenerateCriticalCssAction( callback?: () => void ) {
const [ , resetReason ] = useRegenerationReason();
- return useCriticalCssAction( 'request-regenerate', z.void(), resetReason );
+
+ const onSuccess = () => {
+ if ( callback ) {
+ callback();
+ }
+
+ resetReason();
+ };
+
+ // Optimistically update the state to hide any errors and immediately show the pending state.
+ const optimisticState: CriticalCssState = { status: 'pending', providers: [] };
+ return useCriticalCssAction( 'request-regenerate', z.void(), optimisticState, onSuccess );
}
/**
diff --git a/projects/plugins/boost/app/assets/src/js/features/critical-css/lib/use-retry-regenerate.ts b/projects/plugins/boost/app/assets/src/js/features/critical-css/lib/use-retry-regenerate.ts
index c8ef9b5bf6a97..01e4b193271c1 100644
--- a/projects/plugins/boost/app/assets/src/js/features/critical-css/lib/use-retry-regenerate.ts
+++ b/projects/plugins/boost/app/assets/src/js/features/critical-css/lib/use-retry-regenerate.ts
@@ -1,5 +1,5 @@
-import { useState } from 'react';
import { useRegenerateCriticalCssAction } from './stores/critical-css-state';
+import { useCriticalCssRetriedAfterErrorState } from '../critical-css-context/critical-css-context-provider';
/**
* Helper for "Retry" buttons for Critical CSS which need to track whether they have been clicked
@@ -8,13 +8,12 @@ import { useRegenerateCriticalCssAction } from './stores/critical-css-state';
* Returns a boolean indicating whether retrying has been attempted, and a function to call to retry.
*/
export function useRetryRegenerate(): [ boolean, () => void ] {
- const [ retried, setRetried ] = useState( false );
- const regenerateAction = useRegenerateCriticalCssAction();
+ const [ retriedAfterError, setRetriedAfterError ] = useCriticalCssRetriedAfterErrorState();
+ const regenerateAction = useRegenerateCriticalCssAction( () => setRetriedAfterError( true ) );
function retry() {
- setRetried( true );
regenerateAction.mutate();
}
- return [ retried, retry ];
+ return [ retriedAfterError, retry ];
}
diff --git a/projects/plugins/boost/app/assets/src/js/features/critical-css/show-stopper-error/show-stopper-error.tsx b/projects/plugins/boost/app/assets/src/js/features/critical-css/show-stopper-error/show-stopper-error.tsx
index 5e640257c58f6..e46c527cd8a3d 100644
--- a/projects/plugins/boost/app/assets/src/js/features/critical-css/show-stopper-error/show-stopper-error.tsx
+++ b/projects/plugins/boost/app/assets/src/js/features/critical-css/show-stopper-error/show-stopper-error.tsx
@@ -11,19 +11,16 @@ import getCriticalCssErrorSetInterpolateVars from '$lib/utils/get-critical-css-e
import formatErrorSetUrls from '$lib/utils/format-error-set-urls';
import actionLinkInterpolateVar from '$lib/utils/action-link-interpolate-var';
import { recordBoostEvent } from '$lib/utils/analytics';
+import { useRetryRegenerate } from '../lib/use-retry-regenerate';
type ShowStopperErrorTypes = {
supportLink?: string;
cssState: CriticalCssState;
- retry: () => void;
- showRetry?: boolean;
};
const ShowStopperError: React.FC< ShowStopperErrorTypes > = ( {
supportLink = 'https://wordpress.org/support/plugin/jetpack-boost/',
cssState,
- retry,
- showRetry,
} ) => {
const primaryErrorSet = getPrimaryErrorSet( cssState );
const showLearnSection = primaryErrorSet && cssState.status === 'generated';
@@ -55,12 +52,7 @@ const ShowStopperError: React.FC< ShowStopperErrorTypes > = ( {
>
) : (
-
+
) }
>
@@ -136,7 +128,9 @@ const DocumentationSection = ( {
);
};
-const OtherErrors = ( { cssState, retry, showRetry, supportLink }: ShowStopperErrorTypes ) => {
+const OtherErrors = ( { cssState, supportLink }: ShowStopperErrorTypes ) => {
+ const [ hasRetried, retry ] = useRetryRegenerate();
+
const firstTimeError = __(
'An unexpected error has occurred. As this error may be temporary, please try and refresh the Critical CSS.',
'jetpack-boost'
@@ -184,7 +178,7 @@ const OtherErrors = ( { cssState, retry, showRetry, supportLink }: ShowStopperEr
>
) : (
<>
-
{ showRetry ? firstTimeError : secondTimeError }
+
{ ! hasRetried ? firstTimeError : secondTimeError }
{ sprintf(
/* translators: %s: error message */
@@ -192,7 +186,7 @@ const OtherErrors = ( { cssState, retry, showRetry, supportLink }: ShowStopperEr
cssState.status_error
) }
- { showRetry ? (
+ { ! hasRetried ? (