diff --git a/projects/plugins/jetpack/changelog/change-jetpack-ai-featured-image-show-current b/projects/plugins/jetpack/changelog/change-jetpack-ai-featured-image-show-current new file mode 100644 index 0000000000000..84db90c3cf89e --- /dev/null +++ b/projects/plugins/jetpack/changelog/change-jetpack-ai-featured-image-show-current @@ -0,0 +1,4 @@ +Significance: minor +Type: other + +Jetpack AI: featured image generator modal noww shows current featured image if present diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/components/carrousel.scss b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/components/carrousel.scss index fe4e880a78200..e6532f4ed5085 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/components/carrousel.scss +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/components/carrousel.scss @@ -1,3 +1,5 @@ +$scale-factor: 0.55; + .ai-assistant-image { &__blank { display: flex; @@ -47,6 +49,7 @@ display: flex; align-items: center; overflow: hidden; + height: calc( 768px * $scale-factor ); .ai-carrousel { &__prev { @@ -97,7 +100,7 @@ &-footer-left { display: flex; - width: 25%; + flex-grow: 1; } &-counter { @@ -106,7 +109,6 @@ align-items: center; font-size: 13px; padding: 6px 0; - width: 50%; .ai-carrousel { &__prev, @@ -129,14 +131,17 @@ } &-image { - width: 78%; + max-height: calc( 768px * $scale-factor ); + max-width: calc( 1024px * $scale-factor ); + width: auto; height: auto; + margin: auto 0; } &-image-container { display: flex; width: 100%; - height: auto; + height: 100%; position: absolute; left: 8px; transform: translateX( 100% ); diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/components/carrousel.tsx b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/components/carrousel.tsx index f9dec07e3cb2b..d352fc985ecab 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/components/carrousel.tsx +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/components/carrousel.tsx @@ -3,6 +3,7 @@ */ import { AiFeedbackThumbs } from '@automattic/jetpack-ai-client'; import { Spinner } from '@wordpress/components'; +import { useEffect, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { Icon, chevronLeft, chevronRight } from '@wordpress/icons'; import clsx from 'clsx'; @@ -62,6 +63,7 @@ export default function Carrousel( { handleNextImage: () => void; actions?: React.JSX.Element; } ) { + const [ imageFeedbackDisabled, setImageFeedbackDisabled ] = useState( false ); const prevButton = ( @@ -385,7 +387,7 @@ export default function FeaturedImage( { ) } 0 ? handleRegenerate : handleGenerate } + onGenerate={ + pointer?.current > 0 || postFeaturedMediaId ? handleRegenerate : handleGenerate + } generating={ currentPointer?.generating } notEnoughRequests={ notEnoughRequests } requireUpgrade={ requireUpgrade } diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/hooks/use-ai-image.ts b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/hooks/use-ai-image.ts index bd2fcdd96695a..0edcb0176663b 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/hooks/use-ai-image.ts +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/hooks/use-ai-image.ts @@ -7,8 +7,8 @@ import { ImageStyle, askQuestionSync, } from '@automattic/jetpack-ai-client'; -import { useDispatch } from '@wordpress/data'; -import { useCallback, useRef, useState } from '@wordpress/element'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { useCallback, useEffect, useRef, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { cleanForSlug } from '@wordpress/url'; /** @@ -19,7 +19,7 @@ import useSaveToMediaLibrary from '../../../hooks/use-save-to-media-library'; /** * Types */ -import { FEATURED_IMAGE_FEATURE_NAME, GENERAL_IMAGE_FEATURE_NAME } from '../types'; +import { CoreSelectors, FEATURED_IMAGE_FEATURE_NAME, GENERAL_IMAGE_FEATURE_NAME } from '../types'; import type { CarrouselImageData, CarrouselImages } from '../components/carrousel'; import type { RoleType } from '@automattic/jetpack-ai-client'; import type { FeatureControl } from 'extensions/store/wordpress-com/types.js'; @@ -42,11 +42,13 @@ export default function useAiImage( { type, cost, autoStart = true, + previousMediaId, }: { feature: AiImageFeature; type: AiImageType; cost: number; autoStart?: boolean; + previousMediaId?: number; } ) { const { generateImageWithParameters } = useImageGenerator(); const { increaseRequestsCount, featuresControl } = useAiFeature(); @@ -54,7 +56,10 @@ export default function useAiImage( { const { createNotice } = useDispatch( 'core/notices' ); /* Images Control */ + // pointer keeps track of request/generation iteration const pointer = useRef( 0 ); + // and current keeps track of what is the image exposed at the moment + // TODO: should current be any relevant here? It's just modal/carrousel logic after all const [ current, setCurrent ] = useState( 0 ); const [ images, setImages ] = useState< CarrouselImages >( [ { generating: autoStart } ] ); @@ -75,6 +80,25 @@ export default function useAiImage( { } ); }, [] ); + // the selec/useEffect combo... + const loadedMedia = useSelect( + ( select: ( store ) => CoreSelectors ) => select( 'core' )?.getMedia?.( previousMediaId ), + [ previousMediaId ] + ); + useEffect( () => { + if ( loadedMedia ) { + updateImages( + { + image: loadedMedia.source_url, + libraryId: loadedMedia.id, + libraryUrl: loadedMedia.source_url, + generating: false, + }, + pointer.current + ); + } + }, [ loadedMedia, updateImages ] ); + /* * Function to show a snackbar notice on the editor. */ @@ -123,6 +147,9 @@ export default function useAiImage( { style?: string; } ) => { return new Promise< ImageResponse >( ( resolve, reject ) => { + if ( previousMediaId && pointer.current === 0 ) { + pointer.current++; + } updateImages( { generating: true, error: null }, pointer.current ); // Ensure the site has enough requests to generate the image. @@ -208,12 +235,13 @@ export default function useAiImage( { saveToMediaLibrary, showSnackbarNotice, getImageNameSuggestion, + previousMediaId, ] ); const handlePreviousImage = useCallback( () => { setCurrent( Math.max( current - 1, 0 ) ); - }, [ current, setCurrent ] ); + }, [ current ] ); const handleNextImage = useCallback( () => { setCurrent( Math.min( current + 1, images.length - 1 ) ); diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/types.ts b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/types.ts index f0c0c5ae3d125..77f688f7dd94a 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/types.ts +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/ai-image/types.ts @@ -4,3 +4,16 @@ export const IMAGE_GENERATION_MODEL_STABLE_DIFFUSION = 'stable-diffusion' as con export const IMAGE_GENERATION_MODEL_DALL_E_3 = 'dall-e-3' as const; export const PLACEMENT_MEDIA_SOURCE_DROPDOWN = 'media-source-dropdown' as const; export const PLACEMENT_BLOCK_PLACEHOLDER_BUTTON = 'block-placeholder-button' as const; + +export interface EditorSelectors { + // actually getEditedPostAttribute can bring different values, but for our current use, number is fine (media ID) + getEditedPostAttribute: ( attribute: string ) => number; + isEditorPanelOpened: ( panel: string ) => boolean; +} + +export interface CoreSelectors { + getMedia: ( mediaId: number ) => { + id: number; + source_url: string; + } | null; +}