Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Title Optimization: improve error handling #39340

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: other

Title Optimization: properly handle errors and show the correct UI for each.
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
/**
* External dependencies
*/
import { useAiSuggestions } from '@automattic/jetpack-ai-client';
import {
useAiSuggestions,
RequestingErrorProps,
ERROR_QUOTA_EXCEEDED,
ERROR_NETWORK,
ERROR_SERVICE_UNAVAILABLE,
ERROR_UNCLEAR_PROMPT,
} from '@automattic/jetpack-ai-client';
import { useAnalytics } from '@automattic/jetpack-shared-extension-utils';
import { Button, Spinner, ExternalLink } from '@wordpress/components';
import { Button, Spinner, ExternalLink, Notice } from '@wordpress/components';
import { useDispatch } from '@wordpress/data';
import { useState, useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import QuotaExceededMessage from '../../../../blocks/ai-assistant/components/quota-exceeded-message';
import { getFeatureAvailability } from '../../../../blocks/ai-assistant/lib/utils/get-feature-availability';
import useAutoSaveAndRedirect from '../../../../shared/use-autosave-and-redirect';
import usePostContent from '../../hooks/use-post-content';
Expand All @@ -25,6 +33,43 @@ const isKeywordsFeatureAvailable = getFeatureAvailability(
'ai-title-optimization-keywords-support'
);

/**
* A generic error message that we can reuse.
*/
const genericErrorMessage = __(
'The generation of your suggested titles failed. Please try again.',
'jetpack'
);

const ERROR_JSON_PARSE = 'json-parse-error';
type TitleOptimizationJSONError = {
code: typeof ERROR_JSON_PARSE;
message: string;
};

type TitleOptimizationError = RequestingErrorProps | TitleOptimizationJSONError;

const TitleOptimizationErrorMessage = ( { error }: { error: TitleOptimizationError } ) => {
if ( error.code === ERROR_QUOTA_EXCEEDED ) {
return (
<div className="jetpack-ai-title-optimization__error">
<QuotaExceededMessage useLightNudge={ true } />
</div>
);
}

// Use the provided message, if available, otherwise use the generic error message
const errorMessage = error.message ? error.message : genericErrorMessage;

return (
<div className="jetpack-ai-title-optimization__error">
<Notice status="error" isDismissible={ false }>
{ errorMessage }
</Notice>
</div>
);
};

export default function TitleOptimization( {
placement,
busy,
Expand Down Expand Up @@ -58,7 +103,7 @@ export default function TitleOptimization( {
const [ isTitleOptimizationModalVisible, setIsTitleOptimizationModalVisible ] = useState( false );
const [ generating, setGenerating ] = useState( false );
const [ options, setOptions ] = useState( [] );
const [ error, setError ] = useState( false );
const [ error, setError ] = useState< TitleOptimizationError | null >( null );
const [ optimizationKeywords, setOptimizationKeywords ] = useState( '' );
const { editPost } = useDispatch( 'core/editor' );
const { autosave } = useAutoSaveAndRedirect();
Expand All @@ -80,16 +125,20 @@ export default function TitleOptimization( {
setOptions( parsedContent );
setSelected( parsedContent?.[ 0 ]?.title );
} catch ( e ) {
// Do nothing
const jsonError: TitleOptimizationJSONError = {
code: ERROR_JSON_PARSE,
message: genericErrorMessage,
};
setError( jsonError );
}
},
[ increaseAiAssistantRequestsCount ]
);

const { request, stopSuggestion } = useAiSuggestions( {
onDone: handleDone,
onError: () => {
setError( true );
onError: ( e: RequestingErrorProps ) => {
setError( e );
setGenerating( false );
},
} );
Expand Down Expand Up @@ -127,7 +176,7 @@ export default function TitleOptimization( {
}, [ handleRequest, toggleTitleOptimizationModal ] );

const handleTryAgain = useCallback( () => {
setError( false );
setError( null );
handleRequest( true ); // retry the generation
}, [ handleRequest ] );

Expand Down Expand Up @@ -160,6 +209,14 @@ export default function TitleOptimization( {
stopSuggestion();
}, [ stopSuggestion, toggleTitleOptimizationModal ] );

// When can we retry?
const showTryAgainButton =
error &&
[ ERROR_JSON_PARSE, ERROR_NETWORK, ERROR_SERVICE_UNAVAILABLE, ERROR_UNCLEAR_PROMPT ].includes(
error.code
);
const showReplaceTitleButton = ! error;

return (
<div>
<p>{ sidebarDescription }</p>
Expand Down Expand Up @@ -190,12 +247,7 @@ export default function TitleOptimization( {
) : (
<>
{ error ? (
<div className="jetpack-ai-title-optimization__error">
{ __(
'The generation of your suggested titles failed. Please try again.',
'jetpack'
) }
</div>
<TitleOptimizationErrorMessage error={ error } />
) : (
<>
{ isKeywordsFeatureAvailable && (
Expand Down Expand Up @@ -226,11 +278,12 @@ export default function TitleOptimization( {
<Button variant="secondary" onClick={ toggleTitleOptimizationModal }>
{ __( 'Cancel', 'jetpack' ) }
</Button>
{ error ? (
{ showTryAgainButton && (
<Button variant="primary" onClick={ handleTryAgain }>
{ __( 'Try again', 'jetpack' ) }
</Button>
) : (
) }
{ showReplaceTitleButton && (
<Button variant="primary" onClick={ handleAccept }>
{ __( 'Replace title', 'jetpack' ) }
</Button>
Expand Down
Loading