From ad8757c8cd4da9c77ce6eebb8e4a0ba94de7014d Mon Sep 17 00:00:00 2001 From: Douglas Date: Thu, 6 Feb 2025 16:58:16 -0300 Subject: [PATCH 1/3] add actual requests to seo assistant --- .../seo-assistant/assistant-wizard.tsx | 4 +- .../use-meta-description-step.tsx | 85 +++++++++------ .../seo-assistant/use-title-step.tsx | 103 +++++++++++------- 3 files changed, 118 insertions(+), 74 deletions(-) diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/assistant-wizard.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/assistant-wizard.tsx index 49e6369ede00e..52ba9934a9830 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/assistant-wizard.tsx +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/assistant-wizard.tsx @@ -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 diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-meta-description-step.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-meta-description-step.tsx index 4a37081440edb..772040319f91c 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-meta-description-step.tsx +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-meta-description-step.tsx @@ -1,14 +1,46 @@ -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( ',' ), + }, + }, + ], + { + postId, + feature: 'seo-meta-description', + } + ); + + return response; + }, [ keywords, postContent, postId ] ); const handleMetaDescriptionSelect = useCallback( ( option: OptionMessage ) => { @@ -18,6 +50,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' ) } ); @@ -42,19 +89,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 @@ -74,27 +109,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', diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-title-step.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-title-step.tsx index 51d6b8171d3c6..289f73c86948c 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-title-step.tsx +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-title-step.tsx @@ -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 ) => { @@ -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( @@ -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 ) { @@ -81,6 +116,7 @@ export const useTitleStep = (): Step => { } editLastMessage( editedMessage ); + if ( newTitles.length ) { // this sets the title options for internal state setTitleOptions( newTitles ); @@ -88,30 +124,15 @@ export const useTitleStep = (): Step => { 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 } } ); From 5dda177a0dc5cb442f4d6b642ccfc6708cb86427 Mon Sep 17 00:00:00 2001 From: Douglas Date: Thu, 6 Feb 2025 16:59:05 -0300 Subject: [PATCH 2/3] changelog --- .../plugins/jetpack/changelog/update-seo-assistant-requests | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 projects/plugins/jetpack/changelog/update-seo-assistant-requests diff --git a/projects/plugins/jetpack/changelog/update-seo-assistant-requests b/projects/plugins/jetpack/changelog/update-seo-assistant-requests new file mode 100644 index 0000000000000..7510ba8f0e2ae --- /dev/null +++ b/projects/plugins/jetpack/changelog/update-seo-assistant-requests @@ -0,0 +1,4 @@ +Significance: patch +Type: other + +SEO Assistant: Add actual requests From 94269d4468865e3d950ec4f64fdbd44d49fcee35 Mon Sep 17 00:00:00 2001 From: Douglas Date: Fri, 7 Feb 2025 11:46:05 -0300 Subject: [PATCH 3/3] change meta description count to 1 --- .../components/seo-assistant/use-meta-description-step.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-meta-description-step.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-meta-description-step.tsx index 772040319f91c..ae19e91052a98 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-meta-description-step.tsx +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/seo-assistant/use-meta-description-step.tsx @@ -30,6 +30,7 @@ export const useMetaDescriptionStep = ( { keywords }: { keywords: string } ): St type: 'seo-meta-description', content: postContent, keywords: keywords.split( ',' ), + count: 1, }, }, ],