PluginProbe
Extendify / 3.2.2
Extendify v3.2.2
3.2.2 3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 All 128 releases
extendify / src / Draft / pages / GenerateImage.jsx

GenerateImage.jsx in Extendify 3.2.2, at src/Draft/pages/GenerateImage.jsx

141 lines 4.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { GenerateForm } from '@draft/components/image-generation/GenerateForm';
2 import { ImagePreview } from '@draft/components/image-generation/ImagePreview';
3 import { useRouter } from '@draft/hooks/useRouter';
4 import { pageState } from '@draft/state/factory';
5 import { backArrow } from '@draft/svg/BackArrow';
6 import { generateImage } from '@shared/api/DataApi';
7 import { useImageCreditsSync } from '@shared/hooks/useImageCreditsSync';
8 import { useImageGenerationStore } from '@shared/state/generate-images';
9 import {
10 BaseControl,
11 __experimentalHeading as Heading,
12 Panel,
13 PanelBody,
14 } from '@wordpress/components';
15 import { useEffect, useRef, useState } from '@wordpress/element';
16 import { __ } from '@wordpress/i18n';
17
18 const usePageState = pageState('AI Image', (set) => ({
19 imageDetails: { src: '', id: undefined },
20 setImageDetails: (newState) => {
21 set((state) => ({ ...state, imageDetails: newState }));
22 },
23 }));
24
25 export const GenerateImage = () => {
26 const {
27 imageCredits: curCredits,
28 updateImageCredits,
29 subtractOneCredit,
30 aiImageOptions,
31 } = useImageGenerationStore();
32 const [isGenerating, setIsGenerating] = useState(false);
33 const [disclose, setDisclose] = useState(false);
34 const [errorMessage, setErrorMessage] = useState('');
35 const abortController = useRef(null);
36 const noCredits = curCredits.remaining === 0;
37 const { imageDetails, setImageDetails } = usePageState();
38 const { goBack } = useRouter();
39
40 useImageCreditsSync();
41
42 const clearImageResponse = () => setImageDetails({ src: '', id: undefined });
43 const handleSubmit = async (event) => {
44 event.preventDefault();
45 setErrorMessage('');
46 if (noCredits) {
47 abortController.current?.abort();
48 return;
49 }
50
51 try {
52 setIsGenerating(true);
53 subtractOneCredit();
54 abortController.current = new AbortController();
55 const { imageCredits, images, id } = await generateImage(
56 { ...aiImageOptions, source: 'draft' },
57 abortController.current.signal,
58 );
59 updateImageCredits(imageCredits);
60 setImageDetails({
61 src: images[0].url,
62 id,
63 alt: images[0].alt ?? aiImageOptions.prompt,
64 });
65 } catch (error) {
66 // If the request was aborted (canceled), don't show an error
67 if (error?.code === 20) return;
68 // If we didn't get back any credit info, it was a server error
69 if (!error?.imageCredits) {
70 // Pause to prevent flickering
71 await new Promise((resolve) => setTimeout(resolve, 1000));
72 setErrorMessage(error.message);
73 // Add back the credit we subtracted
74 updateImageCredits({ remaining: curCredits.remaining });
75 return;
76 }
77 // Probably out of credits here
78 updateImageCredits(error.imageCredits);
79 setErrorMessage(error.message);
80 } finally {
81 setIsGenerating(false);
82 }
83 };
84
85 useEffect(() => {
86 if (imageDetails.src || isGenerating) return;
87 // refocus when image is removed
88 document.getElementById('draft-ai-image-textarea')?.focus();
89 }, [imageDetails.src, isGenerating]);
90
91 return (
92 <Panel>
93 <div className="flex h-12 items-center gap-1 pl-1">
94 <button
95 className="h-9 w-9 border-0 bg-transparent"
96 onClick={goBack}
97 type="button"
98 aria-label={__('Go Back', 'extendify-local')}
99 >
100 {backArrow}
101 </button>
102 <Heading className="mb-0">
103 {__('AI Image Generator', 'extendify-local')}
104 </Heading>
105 </div>
106 <PanelBody className="-mt-4 border-t-0">
107 <BaseControl label={__('Image Description', 'extendify-local')}>
108 <ImagePreview
109 prompt={aiImageOptions.prompt}
110 alt={imageDetails?.alt}
111 size={aiImageOptions.size}
112 isGenerating={isGenerating}
113 id={imageDetails?.id}
114 src={imageDetails?.src}
115 disclose={disclose}
116 clearImageResponse={clearImageResponse}
117 />
118 {imageDetails.src ? null : (
119 <form onSubmit={handleSubmit} className="flex flex-col gap-5">
120 <GenerateForm
121 isGenerating={isGenerating}
122 errorMessage={errorMessage}
123 disclose={disclose}
124 setDisclose={setDisclose}
125 />
126 </form>
127 )}
128 </BaseControl>
129 </PanelBody>
130 </Panel>
131 );
132 };
133
134 export const routes = [
135 {
136 slug: 'ai-image',
137 title: __('AI Image', 'extendify-local'),
138 component: GenerateImage,
139 },
140 ];
141