← All changes
|
src/QuickEdit/components/modals/AiImagePickerModal.jsx
+31
-16
3.1.2
→
3.2.1
View file →
| @@ -1,10 +1,12 @@ | ||
| 1 | 1 | import { generateImage } from '@shared/api/DataApi'; |
| 2 | -import { importImage, importImageServer } from '@shared/api/wp'; | |
| 2 | +import { downloadImage } from '@shared/api/wp'; | |
| 3 | +import { useStampedPreview } from '@shared/hooks/useStampedPreview'; | |
| 3 | 4 | import { track } from '@shared/lib/track'; |
| 4 | 5 | import { useImageGenerationStore } from '@shared/state/generate-images'; |
| 5 | 6 | import { |
| 6 | 7 | Button, |
| 8 | + CheckboxControl, | |
| 7 | 9 | Modal, |
| 8 | 10 | Notice, |
| 9 | 11 | Spinner, |
| 10 | 12 | TextareaControl, |
| @@ -47,13 +49,15 @@ | ||
| 47 | 49 | subtractOneCredit, |
| 48 | 50 | aiImageOptions, |
| 49 | 51 | setAiImageOption, |
| 50 | 52 | } = useImageGenerationStore(); |
| 53 | + const [disclose, setDisclose] = useState(false); | |
| 51 | 54 | const [generating, setGenerating] = useState(false); |
| 52 | 55 | const [applying, setApplying] = useState(false); |
| 53 | 56 | const [error, setError] = useState(''); |
| 54 | 57 | const [preview, setPreview] = useState(null); // { src, id } |
| 55 | 58 | const abortRef = useRef(null); |
| 59 | + const previewSrc = useStampedPreview(preview?.src, disclose); | |
| 56 | 60 | |
| 57 | 61 | const noCredits = imageCredits.remaining === 0; |
| 58 | 62 | const usedCredits = imageCredits.total - imageCredits.remaining; |
| 59 | 63 | |
| @@ -70,9 +74,13 @@ | ||
| 70 | 74 | images, |
| 71 | 75 | id: gid, |
| 72 | 76 | } = await generateImage(aiImageOptions, abortRef.current.signal); |
| 73 | 77 | updateImageCredits(newCredits); |
| 74 | - setPreview({ src: images[0].url, id: gid }); | |
| 78 | + setPreview({ | |
| 79 | + src: images[0].url, | |
| 80 | + id: gid, | |
| 81 | + alt: images[0].alt ?? aiImageOptions.prompt, | |
| 82 | + }); | |
| 75 | 83 | track('ai_image_generated', { size: aiImageOptions.size }); |
| 76 | 84 | } catch (err) { |
| 77 | 85 | if (err?.code === 20) return; // aborted |
| 78 | 86 | if (!err?.imageCredits) { |
| @@ -95,21 +103,20 @@ | ||
| 95 | 103 | if (!preview?.src || applying) return; |
| 96 | 104 | setError(''); |
| 97 | 105 | setApplying(true); |
| 98 | 106 | try { |
| 99 | - let attachment; | |
| 100 | - try { | |
| 101 | - attachment = await importImage(preview.src, { | |
| 102 | - alt: aiImageOptions.prompt, | |
| 107 | + const attachment = await downloadImage( | |
| 108 | + preview.id, | |
| 109 | + preview.src, | |
| 110 | + 'ai-generated', | |
| 111 | + null, | |
| 112 | + { | |
| 113 | + alt: preview.alt, | |
| 103 | 114 | filename: 'ai-image.jpg', |
| 104 | 115 | caption: '', |
| 105 | - }); | |
| 106 | - } catch (_e) { | |
| 107 | - attachment = await importImageServer(preview.src, { | |
| 108 | - alt: aiImageOptions.prompt, | |
| 109 | - caption: '', | |
| 110 | - }); | |
| 111 | - } | |
| 116 | + disclose, | |
| 117 | + }, | |
| 118 | + ); | |
| 112 | 119 | const mediaId = attachment?.id; |
| 113 | 120 | if (!mediaId) throw new Error('No media id returned'); |
| 114 | 121 | |
| 115 | 122 | // Product image: write the featured image via WC's save |
| @@ -152,9 +159,9 @@ | ||
| 152 | 159 | fieldKey: field, |
| 153 | 160 | value: { |
| 154 | 161 | url: attachment.url || attachment.source_url, |
| 155 | 162 | id: mediaId, |
| 156 | - alt: aiImageOptions.prompt || '', | |
| 163 | + alt: attachment.alt_text ?? '', | |
| 157 | 164 | }, |
| 158 | 165 | }, |
| 159 | 166 | ], |
| 160 | 167 | }); |
| @@ -212,15 +219,15 @@ | ||
| 212 | 219 | </Notice> |
| 213 | 220 | ) : null} |
| 214 | 221 | {preview?.src ? ( |
| 215 | 222 | <div className="extendify-quick-edit-ai-preview"> |
| 216 | - <img src={preview.src} alt={aiImageOptions.prompt} /> | |
| 223 | + <img src={previewSrc} alt={preview.alt} /> | |
| 217 | 224 | </div> |
| 218 | 225 | ) : ( |
| 219 | 226 | <form onSubmit={onGenerate} className="extendify-quick-edit-ai-form"> |
| 220 | 227 | <TextareaControl |
| 221 | 228 | autoFocus |
| 222 | - label={__('Image prompt', 'extendify-local')} | |
| 229 | + label={__('Image description', 'extendify-local')} | |
| 223 | 230 | placeholder={__( |
| 224 | 231 | 'Describe the image you want to create', |
| 225 | 232 | 'extendify-local', |
| 226 | 233 | )} |
| @@ -258,8 +265,16 @@ | ||
| 258 | 265 | __('Portrait', 'extendify-local') |
| 259 | 266 | } |
| 260 | 267 | /> |
| 261 | 268 | </ToggleGroupControl> |
| 269 | + <CheckboxControl | |
| 270 | + __nextHasNoMarginBottom | |
| 271 | + // translators: Checkbox that adds a visible "AI Generated" mark onto the image. | |
| 272 | + label={__('Label image as AI-generated', 'extendify-local')} | |
| 273 | + checked={disclose} | |
| 274 | + onChange={setDisclose} | |
| 275 | + disabled={generating} | |
| 276 | + /> | |
| 262 | 277 | {generating ? ( |
| 263 | 278 | // biome-ignore lint/a11y/useSemanticElements: deliberate live region; <output> changes display + semantics |
| 264 | 279 | <div className="extendify-quick-edit-ai-generating" role="status"> |
| 265 | 280 | <Spinner /> |