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

SEO Assistant: Add actual requests #41612

Merged
merged 4 commits into from
Feb 7, 2025
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

SEO Assistant: Add actual requests
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ export default function AssistantWizard( { close } ) {

// Keywords
const keywordsStepData = useKeywordsStep();
const titleStepData = useTitleStep();
const metaStepData = useMetaDescriptionStep();
const titleStepData = useTitleStep( { keywords: keywordsStepData.value } );
const metaStepData = useMetaDescriptionStep( { keywords: keywordsStepData.value } );
const completionStepData = useCompletionStep();
const welcomeStepData = useWelcomeStep();
// Memoize steps array to prevent unnecessary recreations
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,47 @@
import { useDispatch } from '@wordpress/data';
/*
* External dependencies
*/
import { askQuestionSync, usePostContent } from '@automattic/jetpack-ai-client';
import { useDispatch, useSelect } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';
import { useCallback, useState, createInterpolateElement } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
/*
* Internal dependencies
*/
import { useMessages } from './wizard-messages';
import type { Step, OptionMessage } from './types';

export const useMetaDescriptionStep = (): Step => {
export const useMetaDescriptionStep = ( { keywords }: { keywords: string } ): Step => {
const [ selectedMetaDescription, setSelectedMetaDescription ] = useState< string >();
const [ metaDescriptionOptions, setMetaDescriptionOptions ] = useState< OptionMessage[] >( [] );
const { messages, setMessages, addMessage, editLastMessage, setSelectedMessage } = useMessages();
const { editPost } = useDispatch( 'core/editor' );
const { editPost } = useDispatch( editorStore );
const postContent = usePostContent();
const postId = useSelect( select => select( editorStore ).getCurrentPostId(), [] );
const [ generatedCount, setGeneratedCount ] = useState( 0 );

const request = useCallback( async () => {
const response = await askQuestionSync(
[
{
role: 'jetpack-ai' as const,
context: {
type: 'seo-meta-description',
content: postContent,
keywords: keywords.split( ',' ),
count: 1,
},
},
],
{
postId,
feature: 'seo-meta-description',
}
);

return response;
}, [ keywords, postContent, postId ] );

const handleMetaDescriptionSelect = useCallback(
( option: OptionMessage ) => {
Expand All @@ -18,6 +51,21 @@ export const useMetaDescriptionStep = (): Step => {
[ setSelectedMessage ]
);

const getMetaDescriptions = useCallback( async () => {
const response = await request();
// TODO: handle errors
const parsedResponse: { descriptions: string[] } = JSON.parse( response );
const count = parsedResponse.descriptions?.length;
const newDescriptions = parsedResponse.descriptions.map( ( description, index ) => ( {
id: `meta-${ generatedCount + count + index }`,
content: description,
} ) );

setGeneratedCount( current => current + count );

return newDescriptions;
}, [ generatedCount, request ] );

const handleMetaDescriptionSubmit = useCallback( async () => {
await editPost( { meta: { advanced_seo_description: selectedMetaDescription } } );
addMessage( { content: __( 'Meta description updated! ✅', 'jetpack' ) } );
Expand All @@ -42,19 +90,7 @@ export const useMetaDescriptionStep = (): Step => {
// we only generate if options are empty
setMessages( [ initialMessage ] );
if ( newMetaDescriptions.length === 0 ) {
newMetaDescriptions = await new Promise( resolve =>
setTimeout(
() =>
resolve( [
{
id: 'meta-1',
content:
'Explore breathtaking flower and plant photography in our Flora Guide, featuring tips and inspiration for gardening and plant enthusiasts to enhance their outdoor spaces.',
},
] ),
1500
)
);
newMetaDescriptions = await getMetaDescriptions();
}
setMetaDescriptionOptions( newMetaDescriptions );
const editedFirstMessage = fromSkip
Expand All @@ -74,27 +110,15 @@ export const useMetaDescriptionStep = (): Step => {
addMessage( { ...meta, type: 'option', isUser: true } )
);
},
[ metaDescriptionOptions, addMessage, setMessages, editLastMessage ]
[ metaDescriptionOptions, setMessages, editLastMessage, getMetaDescriptions, addMessage ]
);

const handleMetaDescriptionRegenerate = useCallback( async () => {
const newMetaDescription = await new Promise< Array< OptionMessage > >( resolve =>
setTimeout(
() =>
resolve( [
{
id: 'meta-1' + Math.random(),
content:
'Explore breathtaking flower and plant photography in our Flora Guide, featuring tips and inspiration for gardening and plant enthusiasts to enhance their outdoor spaces.',
},
] ),
1500
)
);
const newMetaDescription = await getMetaDescriptions();

setMetaDescriptionOptions( prev => [ ...prev, ...newMetaDescription ] );
newMetaDescription.forEach( meta => addMessage( { ...meta, type: 'option', isUser: true } ) );
}, [ addMessage ] );
}, [ addMessage, getMetaDescriptions ] );

return {
id: 'meta',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,47 @@
import { useDispatch } from '@wordpress/data';
/*
* External dependencies
*/
import { askQuestionSync, usePostContent } from '@automattic/jetpack-ai-client';
import { useDispatch, useSelect } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';
import { useCallback, useState, createInterpolateElement } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
/*
* Internal dependencies
*/
import { useMessages } from './wizard-messages';
import type { Step, OptionMessage } from './types';

export const useTitleStep = (): Step => {
export const useTitleStep = ( { keywords }: { keywords: string } ): Step => {
const [ selectedTitle, setSelectedTitle ] = useState< string >( '' );
const [ titleOptions, setTitleOptions ] = useState< OptionMessage[] >( [] );
const { editPost } = useDispatch( 'core/editor' );
const { messages, setMessages, addMessage, editLastMessage, setSelectedMessage } = useMessages();
const [ prevStepValue, setPrevStepValue ] = useState();
const postContent = usePostContent();
const postId = useSelect( select => select( editorStore ).getCurrentPostId(), [] );
const [ generatedCount, setGeneratedCount ] = useState( 0 );

const request = useCallback( async () => {
const response = await askQuestionSync(
[
{
role: 'jetpack-ai' as const,
context: {
type: 'seo-title',
content: postContent,
keywords: keywords.split( ',' ),
},
},
],
{
postId,
feature: 'seo-title',
}
);

return response;
}, [ keywords, postContent, postId ] );

const handleTitleSelect = useCallback(
( option: OptionMessage ) => {
Expand All @@ -19,13 +51,30 @@ export const useTitleStep = (): Step => {
[ setSelectedMessage ]
);

const getTitles = useCallback( async () => {
const response = await request();
// TODO: handle errors
const parsedResponse: { titles: string[] } = JSON.parse( response );
const count = parsedResponse.titles?.length;
const newTitles = parsedResponse.titles.map( ( title, index ) => ( {
id: `title-${ generatedCount + count + index }`,
content: title,
} ) );

setGeneratedCount( current => current + count );

return newTitles;
}, [ generatedCount, request ] );

const handleTitleGenerate = useCallback(
async ( { fromSkip, stepValue: keywords } ) => {
const prevStepHasChanged = keywords !== prevStepValue;
async ( { fromSkip, stepValue: stepKeywords } ) => {
const prevStepHasChanged = stepKeywords !== prevStepValue;

if ( ! prevStepHasChanged ) {
return;
}
setPrevStepValue( keywords );

setPrevStepValue( stepKeywords );
const initialMessage = fromSkip
? {
content: createInterpolateElement(
Expand All @@ -40,26 +89,12 @@ export const useTitleStep = (): Step => {
};
setMessages( [ initialMessage ] );
let newTitles = [ ...titleOptions ];

// we only generate if options are empty
if ( newTitles.length === 0 || prevStepHasChanged ) {
newTitles = await new Promise( resolve =>
setTimeout(
() =>
resolve( [
{
id: '1',
content: 'A Photo Gallery for Gardening Enthusiasths: Flora Guide',
},
{
id: '2',
content:
'Flora Guide: Beautiful Photos of Flowers and Plants for Gardening Enthusiasts',
},
] ),
1500
)
);
newTitles = await getTitles();
}

let editedMessage;

if ( fromSkip ) {
Expand All @@ -81,37 +116,23 @@ export const useTitleStep = (): Step => {
}

editLastMessage( editedMessage );

if ( newTitles.length ) {
// this sets the title options for internal state
setTitleOptions( newTitles );
// this addes title options as message-buttons
newTitles.forEach( title => addMessage( { ...title, type: 'option', isUser: true } ) );
}
},
[ titleOptions, addMessage, setMessages, prevStepValue, editLastMessage ]
[ prevStepValue, setMessages, titleOptions, editLastMessage, getTitles, addMessage ]
);

const handleTitleRegenerate = useCallback( async () => {
const newTitles = await new Promise< Array< OptionMessage > >( resolve =>
setTimeout(
() =>
resolve( [
{
id: '1' + Math.random(),
content: 'A Photo Gallery for Gardening Enthusiasths: Flora Guide',
},
{
id: '2' + Math.random(),
content:
'Flora Guide: Beautiful Photos of Flowers and Plants for Gardening Enthusiasts',
},
] ),
1500
)
);
const newTitles = await getTitles();

setTitleOptions( [ ...titleOptions, ...newTitles ] );
newTitles.forEach( title => addMessage( { ...title, type: 'option', isUser: true } ) );
}, [ addMessage, titleOptions ] );
}, [ addMessage, getTitles, titleOptions ] );

const handleTitleSubmit = useCallback( async () => {
await editPost( { title: selectedTitle, meta: { jetpack_seo_html_title: selectedTitle } } );
Expand Down
Loading