From 78533e8a2c241194d1536fa54665ce96ffa923f3 Mon Sep 17 00:00:00 2001 From: Renato Augusto Gama dos Santos Date: Wed, 24 Apr 2024 13:19:11 -0300 Subject: [PATCH] AI Title Optimization: Add modal content (#37003) * AI Title: Add modal content and options * changelog * AI Title: Fix whitespace * AI Title: Avoid calling map directly --- .../changelog/add-title-optimization-content | 4 + .../components/modal/index.tsx | 4 +- .../components/modal/style.scss | 1 - .../components/title-optimization/index.tsx | 93 ++++++++++++++++++- .../components/title-optimization/style.scss | 23 +++++ .../title-optimization-options.scss | 24 +++++ .../title-optimization-options.tsx | 51 ++++++++++ 7 files changed, 194 insertions(+), 6 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/add-title-optimization-content create mode 100644 projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/title-optimization-options.scss create mode 100644 projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/title-optimization-options.tsx diff --git a/projects/plugins/jetpack/changelog/add-title-optimization-content b/projects/plugins/jetpack/changelog/add-title-optimization-content new file mode 100644 index 0000000000000..5f703ffb4ae85 --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-title-optimization-content @@ -0,0 +1,4 @@ +Significance: minor +Type: other + +Add title optimization modal content diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/modal/index.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/modal/index.tsx index 042b639ad936c..cbf26b000347e 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/modal/index.tsx +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/modal/index.tsx @@ -36,16 +36,18 @@ export default function AiAssistantModal( { hideHeader = true, requestingState = 'init', title = __( 'AI Assistant', 'jetpack' ), + maxWidth = 720, }: { children: React.ReactNode; handleClose: () => void; hideHeader?: boolean; requestingState?: RequestingStateProp; title?: string; + maxWidth?: number; } ) { return ( -
+

{ children } diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/modal/style.scss b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/modal/style.scss index 680cc2a798468..7490df155e44a 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/modal/style.scss +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/modal/style.scss @@ -7,7 +7,6 @@ display: flex; flex-direction: column; width: 100vw; - max-width: 720px; } &__header { diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/index.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/index.tsx index cd2ed6e07b525..05943aaddf262 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/index.tsx +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/index.tsx @@ -1,10 +1,17 @@ /** * External dependencies */ -import { Button } from '@wordpress/components'; +import { useAiSuggestions } from '@automattic/jetpack-ai-client'; +import { Button, Spinner } from '@wordpress/components'; import { useState, useCallback } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; +/** + * Internal dependencies + */ +import usePostContent from '../../hooks/use-post-content'; import AiAssistantModal from '../modal'; +import TitleOptimizationOptions from './title-optimization-options'; +import './style.scss'; export default function TitleOptimization( { busy, @@ -14,26 +21,104 @@ export default function TitleOptimization( { disabled: boolean; } ) { const modalTitle = __( 'Optimize post title', 'jetpack' ); + + const postContent = usePostContent(); + const [ selected, setSelected ] = useState( 'title-0' ); const [ isTitleOptimizationModalVisible, setIsTitleOptimizationModalVisible ] = useState( false ); + const [ generating, setGenerating ] = useState( false ); + const [ options, setOptions ] = useState( [] ); const toggleTitleOptimizationModal = useCallback( () => { setIsTitleOptimizationModalVisible( ! isTitleOptimizationModalVisible ); }, [ isTitleOptimizationModalVisible ] ); + const handleDone = useCallback( ( content: string ) => { + setGenerating( false ); + try { + const parsedContent = JSON.parse( content ); + setOptions( parsedContent ); + } catch ( e ) { + // Do nothing + } + }, [] ); + + const { request } = useAiSuggestions( { + onDone: handleDone, + onError: () => { + setGenerating( false ); + }, + } ); + + const handleRequest = useCallback( () => { + // Message to request a backend prompt for this feature + const messages = [ + { + role: 'jetpack-ai' as const, + context: { + type: 'title-optimization', + content: postContent, + }, + }, + ]; + + request( messages, { feature: 'jetpack-ai-title-optimization' } ); + }, [ postContent, request ] ); + + const handleTitleOptimization = useCallback( () => { + setGenerating( true ); + toggleTitleOptimizationModal(); + handleRequest(); + }, [ handleRequest, toggleTitleOptimizationModal ] ); + return (

{ __( 'Use AI to optimize key details of your post.', 'jetpack' ) }

{ isTitleOptimizationModalVisible && ( - -

{ __( 'This is the modal content.', 'jetpack' ) }

+ + { generating ? ( +
+ + { __( 'Reading your post and generating suggestions…', 'jetpack' ) } +
+ ) : ( + <> + + { __( 'Choose an optimized title below:', 'jetpack' ) } + + setSelected( e.target.value ) } + selected={ selected } + options={ options?.map?.( ( option, index ) => ( { + value: `title-${ index }`, + label: option.title, + description: option.explanation, + } ) ) } + /> +
+ + +
+ + ) }
) }
diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/style.scss b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/style.scss index e69de29bb2d1d..74231e44d9649 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/style.scss +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/style.scss @@ -0,0 +1,23 @@ +.jetpack-ai-title-optimization { + &__loading { + display: flex; + flex-direction: column; + gap: 16px; + width: 100%; + height: 200px; + align-items: center; + justify-content: center; + } + + &__intro { + margin-top: 16px; + } + + &__cta { + display: flex; + gap: 8px; + justify-content: flex-end; + align-items: center; + margin-top: 16px; + } +} diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/title-optimization-options.scss b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/title-optimization-options.scss new file mode 100644 index 0000000000000..de4a29ad4e466 --- /dev/null +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/title-optimization-options.scss @@ -0,0 +1,24 @@ +.jetpack-ai-title-optimization__options { + margin-top: 16px; +} + +.jetpack-ai-title-optimization__option { + display: flex; + justify-content: center; + align-items: start; + gap: 16px; + margin-bottom: 16px; + + & > input { + margin-top: 8px; + } + + &-content { + display: flex; + flex-direction: column; + } + + &-label { + font-weight: 600; + } +} diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/title-optimization-options.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/title-optimization-options.tsx new file mode 100644 index 0000000000000..2e8b7ef5223ce --- /dev/null +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/title-optimization/title-optimization-options.tsx @@ -0,0 +1,51 @@ +/** + * Internal dependencies + */ +import './title-optimization-options.scss'; + +type TitleOptimizationOptions = { + value: string; + label: string; + description: string; +}[]; + +const id = 'title-optimization-option'; + +export default function TitleOptimizationOptions( { + options, + selected, + onChangeValue, +}: { + options: TitleOptimizationOptions; + selected: string; + onChangeValue: ( event: React.ChangeEvent< HTMLInputElement > ) => void; +} ) { + return ( +
+ { options.map( ( option, index ) => ( +
+ +
+ + + { option.description } + +
+
+ ) ) } +
+ ); +}