@automattic/jetpack-ai-client 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/build/ai-client/src/index.d.ts +1 -0
- package/build/ai-client/src/index.js +1 -0
- package/build/ai-client/src/logo-generator/components/feature-fetch-failure-screen.d.ts +1 -1
- package/build/ai-client/src/logo-generator/components/feature-fetch-failure-screen.js +2 -1
- package/build/ai-client/src/logo-generator/components/generator-modal.js +23 -10
- package/build/ai-client/src/logo-generator/components/prompt.d.ts +9 -0
- package/build/ai-client/src/logo-generator/components/prompt.js +32 -27
- package/build/ai-client/src/logo-generator/hooks/use-logo-generator.js +1 -1
- package/build/ai-client/src/logo-generator/index.d.ts +1 -0
- package/build/ai-client/src/logo-generator/index.js +1 -0
- package/build/ai-client/src/logo-generator/store/actions.js +3 -0
- package/build/ai-client/src/logo-generator/store/reducer.d.ts +32 -3
- package/build/ai-client/src/logo-generator/store/reducer.js +10 -0
- package/build/ai-client/src/logo-generator/store/types.d.ts +1 -0
- package/build/ai-client/src/logo-generator/types.d.ts +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/logo-generator/components/feature-fetch-failure-screen.tsx +14 -5
- package/src/logo-generator/components/generator-modal.tsx +27 -10
- package/src/logo-generator/components/prompt.scss +1 -1
- package/src/logo-generator/components/prompt.tsx +87 -57
- package/src/logo-generator/hooks/use-logo-generator.ts +1 -1
- package/src/logo-generator/index.ts +1 -0
- package/src/logo-generator/store/actions.ts +4 -0
- package/src/logo-generator/store/reducer.ts +10 -0
- package/src/logo-generator/store/types.ts +1 -0
- package/src/logo-generator/types.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.24.0] - 2024-10-29
|
|
9
|
+
### Added
|
|
10
|
+
- AI Client: export image generator hook constants [#39917]
|
|
11
|
+
|
|
12
|
+
## [0.23.0] - 2024-10-28
|
|
13
|
+
### Changed
|
|
14
|
+
- AI Client: Decouple prompt input as component and export it for reusability. [#39864]
|
|
15
|
+
- AI Client: Make reload handler prop optional. [#39848]
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- AI Client: Fix initial state being mapped even when fetch fails. [#39846]
|
|
19
|
+
|
|
8
20
|
## [0.22.0] - 2024-10-21
|
|
9
21
|
### Changed
|
|
10
22
|
- AI Client: Add types for AI assistant feature payload data branch featuresControl. [#39826]
|
|
@@ -439,6 +451,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
439
451
|
- Updated package dependencies. [#31659]
|
|
440
452
|
- Updated package dependencies. [#31785]
|
|
441
453
|
|
|
454
|
+
[0.24.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.23.0...v0.24.0
|
|
455
|
+
[0.23.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.22.0...v0.23.0
|
|
442
456
|
[0.22.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.21.0...v0.22.0
|
|
443
457
|
[0.21.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.20.1...v0.21.0
|
|
444
458
|
[0.20.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.20.0...v0.20.1
|
|
@@ -9,6 +9,7 @@ export { default as useAudioTranscription } from './hooks/use-audio-transcriptio
|
|
|
9
9
|
export { default as useTranscriptionPostProcessing } from './hooks/use-transcription-post-processing/index.js';
|
|
10
10
|
export { default as useAudioValidation } from './hooks/use-audio-validation/index.js';
|
|
11
11
|
export { default as useImageGenerator } from './hooks/use-image-generator/index.js';
|
|
12
|
+
export * from './hooks/use-image-generator/constants.js';
|
|
12
13
|
export * from './icons/index.js';
|
|
13
14
|
export * from './components/index.js';
|
|
14
15
|
export * from './data-flow/index.js';
|
|
@@ -15,6 +15,7 @@ export { default as useAudioTranscription } from './hooks/use-audio-transcriptio
|
|
|
15
15
|
export { default as useTranscriptionPostProcessing } from './hooks/use-transcription-post-processing/index.js';
|
|
16
16
|
export { default as useAudioValidation } from './hooks/use-audio-validation/index.js';
|
|
17
17
|
export { default as useImageGenerator } from './hooks/use-image-generator/index.js';
|
|
18
|
+
export * from './hooks/use-image-generator/constants.js';
|
|
18
19
|
/*
|
|
19
20
|
* Components: Icons
|
|
20
21
|
*/
|
|
@@ -6,5 +6,6 @@ import { Button } from '@wordpress/components';
|
|
|
6
6
|
import { __ } from '@wordpress/i18n';
|
|
7
7
|
export const FeatureFetchFailureScreen = ({ onCancel, onRetry }) => {
|
|
8
8
|
const errorMessage = __('We are sorry. There was an error loading your Jetpack AI plan data. Please, try again.', 'jetpack-ai-client');
|
|
9
|
-
|
|
9
|
+
const errorMessageWithoutRetry = __('We are sorry. There was an error loading your Jetpack AI plan data. Please, reload the page and try again.', 'jetpack-ai-client');
|
|
10
|
+
return (_jsxs("div", { className: "jetpack-ai-logo-generator-modal__notice-message-wrapper", children: [_jsx("div", { className: "jetpack-ai-logo-generator-modal__notice-message", children: _jsx("span", { className: "jetpack-ai-logo-generator-modal__loading-message", children: onRetry ? errorMessage : errorMessageWithoutRetry }) }), _jsxs("div", { className: "jetpack-ai-logo-generator-modal__notice-actions", children: [_jsx(Button, { variant: "tertiary", onClick: onCancel, children: __('Cancel', 'jetpack-ai-client') }), onRetry && (_jsx(Button, { variant: "primary", onClick: onRetry, children: __('Try again', 'jetpack-ai-client') }))] })] }));
|
|
10
11
|
};
|
|
@@ -29,7 +29,7 @@ import { UpgradeScreen } from './upgrade-screen.js';
|
|
|
29
29
|
import { VisitSiteBanner } from './visit-site-banner.js';
|
|
30
30
|
import './generator-modal.scss';
|
|
31
31
|
const debug = debugFactory('jetpack-ai-calypso:generator-modal');
|
|
32
|
-
export const GeneratorModal = ({ isOpen, onClose, onApplyLogo, onReload, siteDetails, context, placement, }) => {
|
|
32
|
+
export const GeneratorModal = ({ isOpen, onClose, onApplyLogo, onReload = null, siteDetails, context, placement, }) => {
|
|
33
33
|
const { tracks } = useAnalytics();
|
|
34
34
|
const { recordEvent: recordTracksEvent } = tracks;
|
|
35
35
|
const { setSiteDetails, fetchAiAssistantFeature, loadLogoHistory, setIsLoadingHistory } = useDispatch(STORE_NAME);
|
|
@@ -41,7 +41,7 @@ export const GeneratorModal = ({ isOpen, onClose, onApplyLogo, onReload, siteDet
|
|
|
41
41
|
const [needsFeature, setNeedsFeature] = useState(false);
|
|
42
42
|
const [needsMoreRequests, setNeedsMoreRequests] = useState(false);
|
|
43
43
|
const { selectedLogo, getAiAssistantFeature, generateFirstPrompt, generateLogo, setContext, tierPlansEnabled, site, requireUpgrade, } = useLogoGenerator();
|
|
44
|
-
const { featureFetchError, firstLogoPromptFetchError, clearErrors } = useRequestErrors();
|
|
44
|
+
const { featureFetchError, setFeatureFetchError, firstLogoPromptFetchError, clearErrors } = useRequestErrors();
|
|
45
45
|
const siteId = siteDetails?.ID;
|
|
46
46
|
const [logoAccepted, setLogoAccepted] = useState(false);
|
|
47
47
|
const { nextTierCheckoutURL: upgradeURL } = useCheckout();
|
|
@@ -70,6 +70,13 @@ export const GeneratorModal = ({ isOpen, onClose, onApplyLogo, onReload, siteDet
|
|
|
70
70
|
*/
|
|
71
71
|
const initializeModal = useCallback(async () => {
|
|
72
72
|
try {
|
|
73
|
+
if (!siteId) {
|
|
74
|
+
throw new Error('Site ID is missing');
|
|
75
|
+
}
|
|
76
|
+
if (!feature?.featuresControl?.['logo-generator']?.enabled) {
|
|
77
|
+
setFeatureFetchError('Failed to fetch feature data');
|
|
78
|
+
throw new Error('Failed to fetch feature data');
|
|
79
|
+
}
|
|
73
80
|
const hasHistory = !isLogoHistoryEmpty(String(siteId));
|
|
74
81
|
const logoCost = feature?.costs?.['jetpack-ai-logo-generator']?.logo ?? DEFAULT_LOGO_COST;
|
|
75
82
|
const promptCreationCost = 1;
|
|
@@ -86,9 +93,9 @@ export const GeneratorModal = ({ isOpen, onClose, onApplyLogo, onReload, siteDet
|
|
|
86
93
|
? currentLimit - currentUsage < logoCost + promptCreationCost
|
|
87
94
|
: currentLimit < currentUsage);
|
|
88
95
|
// If the site requires an upgrade, show the upgrade screen immediately.
|
|
89
|
-
setNeedsFeature(
|
|
96
|
+
setNeedsFeature(currentValue === 0);
|
|
90
97
|
setNeedsMoreRequests(siteNeedsMoreRequests);
|
|
91
|
-
if (
|
|
98
|
+
if (currentValue === 0 || siteNeedsMoreRequests) {
|
|
92
99
|
setLoadingState(null);
|
|
93
100
|
return;
|
|
94
101
|
}
|
|
@@ -135,6 +142,7 @@ export const GeneratorModal = ({ isOpen, onClose, onApplyLogo, onReload, siteDet
|
|
|
135
142
|
isLogoHistoryEmpty,
|
|
136
143
|
siteId,
|
|
137
144
|
requireUpgrade,
|
|
145
|
+
setFeatureFetchError,
|
|
138
146
|
]);
|
|
139
147
|
const handleModalOpen = useCallback(async () => {
|
|
140
148
|
setContext(context);
|
|
@@ -153,6 +161,14 @@ export const GeneratorModal = ({ isOpen, onClose, onApplyLogo, onReload, siteDet
|
|
|
153
161
|
setIsLoadingHistory(false);
|
|
154
162
|
recordTracksEvent(EVENT_MODAL_CLOSE, { context, placement });
|
|
155
163
|
};
|
|
164
|
+
const handleReload = useCallback(() => {
|
|
165
|
+
if (!onReload) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
closeModal();
|
|
169
|
+
requestedFeatureData.current = false;
|
|
170
|
+
onReload();
|
|
171
|
+
}, [onReload, closeModal]);
|
|
156
172
|
const handleApplyLogo = (mediaId) => {
|
|
157
173
|
setLogoAccepted(true);
|
|
158
174
|
onApplyLogo?.(mediaId);
|
|
@@ -177,7 +193,7 @@ export const GeneratorModal = ({ isOpen, onClose, onApplyLogo, onReload, siteDet
|
|
|
177
193
|
// Handles modal opening logic
|
|
178
194
|
useEffect(() => {
|
|
179
195
|
// While the modal is not open, the siteId is not set, or the feature data is not available, do nothing.
|
|
180
|
-
if (!isOpen
|
|
196
|
+
if (!isOpen) {
|
|
181
197
|
return;
|
|
182
198
|
}
|
|
183
199
|
// Prevent multiple calls of the handleModalOpen function
|
|
@@ -185,16 +201,13 @@ export const GeneratorModal = ({ isOpen, onClose, onApplyLogo, onReload, siteDet
|
|
|
185
201
|
needsToHandleModalOpen.current = false;
|
|
186
202
|
handleModalOpen();
|
|
187
203
|
}
|
|
188
|
-
}, [isOpen,
|
|
204
|
+
}, [isOpen, handleModalOpen]);
|
|
189
205
|
let body;
|
|
190
206
|
if (loadingState) {
|
|
191
207
|
body = _jsx(FirstLoadScreen, { state: loadingState });
|
|
192
208
|
}
|
|
193
209
|
else if (featureFetchError || firstLogoPromptFetchError) {
|
|
194
|
-
body = (_jsx(FeatureFetchFailureScreen, { onCancel: closeModal, onRetry:
|
|
195
|
-
closeModal();
|
|
196
|
-
onReload?.();
|
|
197
|
-
} }));
|
|
210
|
+
body = (_jsx(FeatureFetchFailureScreen, { onCancel: closeModal, onRetry: onReload ? handleReload : null }));
|
|
198
211
|
}
|
|
199
212
|
else if (needsFeature || needsMoreRequests) {
|
|
200
213
|
body = (_jsx(UpgradeScreen, { onCancel: closeModal, upgradeURL: upgradeURL, reason: needsFeature ? 'feature' : 'requests' }));
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
1
2
|
import './prompt.scss';
|
|
2
3
|
type PromptProps = {
|
|
3
4
|
initialPrompt?: string;
|
|
4
5
|
};
|
|
6
|
+
export declare const AiModalPromptInput: ({ prompt, setPrompt, disabled, generateHandler, placeholder, buttonLabel, }: {
|
|
7
|
+
prompt: string;
|
|
8
|
+
setPrompt: Dispatch<SetStateAction<string>>;
|
|
9
|
+
disabled: boolean;
|
|
10
|
+
generateHandler: () => void;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
buttonLabel?: string;
|
|
13
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
5
14
|
export declare const Prompt: ({ initialPrompt }: PromptProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
15
|
export {};
|
|
@@ -21,6 +21,36 @@ import { FairUsageNotice } from './fair-usage-notice.js';
|
|
|
21
21
|
import { UpgradeNudge } from './upgrade-nudge.js';
|
|
22
22
|
import './prompt.scss';
|
|
23
23
|
const debug = debugFactory('jetpack-ai-calypso:prompt-box');
|
|
24
|
+
export const AiModalPromptInput = ({ prompt = '', setPrompt = () => { }, disabled = false, generateHandler = () => { }, placeholder = '', buttonLabel = '', }) => {
|
|
25
|
+
const inputRef = useRef(null);
|
|
26
|
+
const hasPrompt = prompt?.length >= MINIMUM_PROMPT_LENGTH;
|
|
27
|
+
const onPromptInput = (event) => {
|
|
28
|
+
setPrompt(event.target.textContent || '');
|
|
29
|
+
};
|
|
30
|
+
const onPromptPaste = (event) => {
|
|
31
|
+
event.preventDefault();
|
|
32
|
+
const selection = event.currentTarget.ownerDocument.getSelection();
|
|
33
|
+
if (!selection || !selection.rangeCount) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
// Paste plain text only
|
|
37
|
+
const text = event.clipboardData.getData('text/plain');
|
|
38
|
+
selection.deleteFromDocument();
|
|
39
|
+
const range = selection.getRangeAt(0);
|
|
40
|
+
range.insertNode(document.createTextNode(text));
|
|
41
|
+
selection.collapseToEnd();
|
|
42
|
+
setPrompt(inputRef.current?.textContent || '');
|
|
43
|
+
};
|
|
44
|
+
const onKeyDown = (event) => {
|
|
45
|
+
if (event.key === 'Enter') {
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
generateHandler();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
return (_jsxs("div", { className: "jetpack-ai-logo-generator__prompt-query", children: [_jsx("div", { role: "textbox", tabIndex: 0, ref: inputRef, contentEditable: !disabled,
|
|
51
|
+
// The content editable div is expected to be updated by the enhance prompt, so warnings are suppressed
|
|
52
|
+
suppressContentEditableWarning: true, className: "prompt-query__input", onInput: onPromptInput, onPaste: onPromptPaste, onKeyDown: onKeyDown, "data-placeholder": placeholder }), _jsx(Button, { variant: "primary", className: "jetpack-ai-logo-generator__prompt-submit", onClick: generateHandler, disabled: disabled || !hasPrompt, children: buttonLabel || __('Generate', 'jetpack-ai-client') })] }));
|
|
53
|
+
};
|
|
24
54
|
export const Prompt = ({ initialPrompt = '' }) => {
|
|
25
55
|
const { tracks } = useAnalytics();
|
|
26
56
|
const { recordEvent: recordTracksEvent } = tracks;
|
|
@@ -70,7 +100,7 @@ export const Prompt = ({ initialPrompt = '' }) => {
|
|
|
70
100
|
}
|
|
71
101
|
}, [prompt]);
|
|
72
102
|
useEffect(() => {
|
|
73
|
-
if (imageStyles.length > 0) {
|
|
103
|
+
if (imageStyles && imageStyles.length > 0) {
|
|
74
104
|
// Sort styles to have "None" and "Auto" first
|
|
75
105
|
setStyles([
|
|
76
106
|
imageStyles.find(({ value }) => value === IMAGE_STYLE_NONE),
|
|
@@ -103,23 +133,6 @@ export const Prompt = ({ initialPrompt = '' }) => {
|
|
|
103
133
|
generateLogo({ prompt, style });
|
|
104
134
|
}
|
|
105
135
|
}, [context, generateLogo, prompt, style]);
|
|
106
|
-
const onPromptInput = (event) => {
|
|
107
|
-
setPrompt(event.target.textContent || '');
|
|
108
|
-
};
|
|
109
|
-
const onPromptPaste = (event) => {
|
|
110
|
-
event.preventDefault();
|
|
111
|
-
const selection = event.currentTarget.ownerDocument.getSelection();
|
|
112
|
-
if (!selection || !selection.rangeCount) {
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
// Paste plain text only
|
|
116
|
-
const text = event.clipboardData.getData('text/plain');
|
|
117
|
-
selection.deleteFromDocument();
|
|
118
|
-
const range = selection.getRangeAt(0);
|
|
119
|
-
range.insertNode(document.createTextNode(text));
|
|
120
|
-
selection.collapseToEnd();
|
|
121
|
-
setPrompt(inputRef.current?.textContent || '');
|
|
122
|
-
};
|
|
123
136
|
const onUpgradeClick = () => {
|
|
124
137
|
recordTracksEvent(EVENT_UPGRADE, { context, placement: EVENT_PLACEMENT_INPUT_FOOTER });
|
|
125
138
|
};
|
|
@@ -128,15 +141,7 @@ export const Prompt = ({ initialPrompt = '' }) => {
|
|
|
128
141
|
setStyle(imageStyle);
|
|
129
142
|
recordTracksEvent(EVENT_SWITCH_STYLE, { context, style: imageStyle });
|
|
130
143
|
}, [context, setStyle, recordTracksEvent]);
|
|
131
|
-
|
|
132
|
-
if (event.key === 'Enter') {
|
|
133
|
-
event.preventDefault();
|
|
134
|
-
onGenerate();
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
return (_jsxs("div", { className: "jetpack-ai-logo-generator__prompt", children: [_jsxs("div", { className: "jetpack-ai-logo-generator__prompt-header", children: [_jsx("div", { className: "jetpack-ai-logo-generator__prompt-label", children: __('Describe your site:', 'jetpack-ai-client') }), _jsx("div", { className: "jetpack-ai-logo-generator__prompt-actions", children: _jsxs(Button, { variant: "link", disabled: isBusy || requireUpgrade || !hasPrompt, onClick: onEnhance, children: [_jsx(AiIcon, {}), enhanceButtonLabel] }) }), showStyleSelector && (_jsx(SelectControl, { __nextHasNoMarginBottom: true, value: style, options: styles, onChange: updateStyle, disabled: isBusy || requireUpgrade }))] }), _jsxs("div", { className: "jetpack-ai-logo-generator__prompt-query", children: [_jsx("div", { role: "textbox", tabIndex: 0, ref: inputRef, contentEditable: !isBusy && !requireUpgrade,
|
|
138
|
-
// The content editable div is expected to be updated by the enhance prompt, so warnings are suppressed
|
|
139
|
-
suppressContentEditableWarning: true, className: "prompt-query__input", onInput: onPromptInput, onPaste: onPromptPaste, onKeyDown: onKeyDown, "data-placeholder": __('Describe your site or simply ask for a logo specifying some details about it', 'jetpack-ai-client') }), _jsx(Button, { variant: "primary", className: "jetpack-ai-logo-generator__prompt-submit", onClick: onGenerate, disabled: isBusy || requireUpgrade || !hasPrompt, children: __('Generate', 'jetpack-ai-client') })] }), _jsxs("div", { className: "jetpack-ai-logo-generator__prompt-footer", children: [!isUnlimited && !requireUpgrade && (_jsxs("div", { className: "jetpack-ai-logo-generator__prompt-requests", children: [_jsx("div", { children: sprintf(
|
|
144
|
+
return (_jsxs("div", { className: "jetpack-ai-logo-generator__prompt", children: [_jsxs("div", { className: "jetpack-ai-logo-generator__prompt-header", children: [_jsx("div", { className: "jetpack-ai-logo-generator__prompt-label", children: __('Describe your site:', 'jetpack-ai-client') }), _jsx("div", { className: "jetpack-ai-logo-generator__prompt-actions", children: _jsxs(Button, { variant: "link", disabled: isBusy || requireUpgrade || !hasPrompt, onClick: onEnhance, children: [_jsx(AiIcon, {}), enhanceButtonLabel] }) }), showStyleSelector && (_jsx(SelectControl, { __nextHasNoMarginBottom: true, value: style, options: styles, onChange: updateStyle, disabled: isBusy || requireUpgrade }))] }), _jsx(AiModalPromptInput, { prompt: prompt, setPrompt: setPrompt, generateHandler: onGenerate, disabled: isBusy || requireUpgrade, placeholder: __('Describe your site or simply ask for a logo specifying some details about it', 'jetpack-ai-client') }), _jsxs("div", { className: "jetpack-ai-logo-generator__prompt-footer", children: [!isUnlimited && !requireUpgrade && (_jsxs("div", { className: "jetpack-ai-logo-generator__prompt-requests", children: [_jsx("div", { children: sprintf(
|
|
140
145
|
// translators: %u is the number of requests
|
|
141
146
|
__('%u requests remaining.', 'jetpack-ai-client'), requestsRemaining) }), hasNextTier && (_jsxs(_Fragment, { children: ["\u00A0", _jsx(Button, { variant: "link", href: checkoutUrl, target: "_blank", onClick: onUpgradeClick, children: __('Upgrade', 'jetpack-ai-client') })] })), "\u00A0", _jsx(Tooltip, { text: __('Logo generation costs 10 requests; prompt enhancement costs 1 request each', 'jetpack-ai-client'), placement: "bottom", children: _jsx(Icon, { className: "prompt-footer__icon", icon: info }) })] })), requireUpgrade && tierPlansEnabled && _jsx(UpgradeNudge, {}), requireUpgrade && !tierPlansEnabled && _jsx(FairUsageNotice, {}), enhancePromptFetchError && (_jsx("div", { className: "jetpack-ai-logo-generator__prompt-error", children: __('Error enhancing prompt. Please try again.', 'jetpack-ai-client') })), logoFetchError && (_jsx("div", { className: "jetpack-ai-logo-generator__prompt-error", children: __('Error generating logo. Please try again.', 'jetpack-ai-client') }))] })] }));
|
|
142
147
|
};
|
|
@@ -45,7 +45,7 @@ const useLogoGenerator = () => {
|
|
|
45
45
|
const aiAssistantFeatureData = getAiAssistantFeature(siteId);
|
|
46
46
|
const logoGenerationCost = aiAssistantFeatureData?.costs?.['jetpack-ai-logo-generator']?.logo;
|
|
47
47
|
const logoGeneratorControl = aiAssistantFeatureData?.featuresControl?.['logo-generator'];
|
|
48
|
-
const imageStyles = logoGeneratorControl?.styles;
|
|
48
|
+
const imageStyles = logoGeneratorControl?.styles || [];
|
|
49
49
|
const generateFirstPrompt = useCallback(async function () {
|
|
50
50
|
setFirstLogoPromptFetchError(null);
|
|
51
51
|
increaseAiAssistantRequestsCount();
|
|
@@ -56,6 +56,9 @@ const actions = {
|
|
|
56
56
|
path: '/wpcom/v2/jetpack-ai/ai-assistant-feature',
|
|
57
57
|
query: 'force=wpcom',
|
|
58
58
|
});
|
|
59
|
+
if (response.data) {
|
|
60
|
+
throw new Error('Failed to fetch');
|
|
61
|
+
}
|
|
59
62
|
// Store the feature in the store.
|
|
60
63
|
dispatch(actions.storeAiAssistantFeature(mapAiFeatureResponseToAiFeatureProps(response)));
|
|
61
64
|
}
|
|
@@ -214,6 +214,38 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
214
214
|
history: import("./types.js").Logo[];
|
|
215
215
|
selectedLogoIndex: number;
|
|
216
216
|
} | {
|
|
217
|
+
features: {
|
|
218
|
+
aiAssistantFeature: {
|
|
219
|
+
_meta: {
|
|
220
|
+
isRequesting: boolean;
|
|
221
|
+
asyncRequestCountdown: number;
|
|
222
|
+
asyncRequestTimerId: number;
|
|
223
|
+
isRequestingImage: boolean;
|
|
224
|
+
};
|
|
225
|
+
hasFeature: boolean;
|
|
226
|
+
isOverLimit: boolean;
|
|
227
|
+
requestsCount: number;
|
|
228
|
+
requestsLimit: number;
|
|
229
|
+
requireUpgrade: boolean;
|
|
230
|
+
errorMessage?: string;
|
|
231
|
+
errorCode?: string;
|
|
232
|
+
upgradeType: import("./types.js").UpgradeTypeProp;
|
|
233
|
+
currentTier?: import("./types.js").TierProp;
|
|
234
|
+
usagePeriod?: {
|
|
235
|
+
currentStart: string;
|
|
236
|
+
nextStart: string;
|
|
237
|
+
requestsCount: number;
|
|
238
|
+
};
|
|
239
|
+
nextTier?: import("./types.js").TierProp;
|
|
240
|
+
tierPlansEnabled?: boolean;
|
|
241
|
+
costs?: {
|
|
242
|
+
'jetpack-ai-logo-generator': {
|
|
243
|
+
logo: number;
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
featuresControl?: import("./types.js").FeaturesControl;
|
|
247
|
+
};
|
|
248
|
+
};
|
|
217
249
|
_meta: {
|
|
218
250
|
featureFetchError: RequestError;
|
|
219
251
|
isSavingLogoToLibrary?: boolean;
|
|
@@ -229,9 +261,6 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
229
261
|
isLoadingHistory?: boolean;
|
|
230
262
|
};
|
|
231
263
|
siteDetails?: SiteDetails | Record<string, never>;
|
|
232
|
-
features: {
|
|
233
|
-
aiAssistantFeature?: AiFeatureStateProps;
|
|
234
|
-
};
|
|
235
264
|
history: import("./types.js").Logo[];
|
|
236
265
|
selectedLogoIndex: number;
|
|
237
266
|
} | {
|
|
@@ -236,6 +236,16 @@ export default function reducer(state = INITIAL_STATE, action) {
|
|
|
236
236
|
case ACTION_SET_FEATURE_FETCH_ERROR:
|
|
237
237
|
return {
|
|
238
238
|
...state,
|
|
239
|
+
features: {
|
|
240
|
+
...state.features,
|
|
241
|
+
aiAssistantFeature: {
|
|
242
|
+
...state?.features?.aiAssistantFeature,
|
|
243
|
+
_meta: {
|
|
244
|
+
...state?.features?.aiAssistantFeature?._meta,
|
|
245
|
+
isRequesting: false,
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
},
|
|
239
249
|
_meta: {
|
|
240
250
|
...(state._meta ?? {}),
|
|
241
251
|
featureFetchError: action.error,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "@automattic/jetpack-ai-client",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.24.0",
|
|
5
5
|
"description": "A JS client for consuming Jetpack AI services",
|
|
6
6
|
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/ai-client/#readme",
|
|
7
7
|
"bugs": {
|
package/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ export { default as useAudioTranscription } from './hooks/use-audio-transcriptio
|
|
|
16
16
|
export { default as useTranscriptionPostProcessing } from './hooks/use-transcription-post-processing/index.js';
|
|
17
17
|
export { default as useAudioValidation } from './hooks/use-audio-validation/index.js';
|
|
18
18
|
export { default as useImageGenerator } from './hooks/use-image-generator/index.js';
|
|
19
|
+
export * from './hooks/use-image-generator/constants.js';
|
|
19
20
|
|
|
20
21
|
/*
|
|
21
22
|
* Components: Icons
|
|
@@ -10,25 +10,34 @@ import type React from 'react';
|
|
|
10
10
|
|
|
11
11
|
export const FeatureFetchFailureScreen: React.FC< {
|
|
12
12
|
onCancel: () => void;
|
|
13
|
-
onRetry
|
|
13
|
+
onRetry?: () => void;
|
|
14
14
|
} > = ( { onCancel, onRetry } ) => {
|
|
15
15
|
const errorMessage = __(
|
|
16
16
|
'We are sorry. There was an error loading your Jetpack AI plan data. Please, try again.',
|
|
17
17
|
'jetpack-ai-client'
|
|
18
18
|
);
|
|
19
19
|
|
|
20
|
+
const errorMessageWithoutRetry = __(
|
|
21
|
+
'We are sorry. There was an error loading your Jetpack AI plan data. Please, reload the page and try again.',
|
|
22
|
+
'jetpack-ai-client'
|
|
23
|
+
);
|
|
24
|
+
|
|
20
25
|
return (
|
|
21
26
|
<div className="jetpack-ai-logo-generator-modal__notice-message-wrapper">
|
|
22
27
|
<div className="jetpack-ai-logo-generator-modal__notice-message">
|
|
23
|
-
<span className="jetpack-ai-logo-generator-modal__loading-message">
|
|
28
|
+
<span className="jetpack-ai-logo-generator-modal__loading-message">
|
|
29
|
+
{ onRetry ? errorMessage : errorMessageWithoutRetry }
|
|
30
|
+
</span>
|
|
24
31
|
</div>
|
|
25
32
|
<div className="jetpack-ai-logo-generator-modal__notice-actions">
|
|
26
33
|
<Button variant="tertiary" onClick={ onCancel }>
|
|
27
34
|
{ __( 'Cancel', 'jetpack-ai-client' ) }
|
|
28
35
|
</Button>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
{ onRetry && (
|
|
37
|
+
<Button variant="primary" onClick={ onRetry }>
|
|
38
|
+
{ __( 'Try again', 'jetpack-ai-client' ) }
|
|
39
|
+
</Button>
|
|
40
|
+
) }
|
|
32
41
|
</div>
|
|
33
42
|
</div>
|
|
34
43
|
);
|
|
@@ -45,7 +45,7 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
45
45
|
isOpen,
|
|
46
46
|
onClose,
|
|
47
47
|
onApplyLogo,
|
|
48
|
-
onReload,
|
|
48
|
+
onReload = null,
|
|
49
49
|
siteDetails,
|
|
50
50
|
context,
|
|
51
51
|
placement,
|
|
@@ -73,7 +73,8 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
73
73
|
site,
|
|
74
74
|
requireUpgrade,
|
|
75
75
|
} = useLogoGenerator();
|
|
76
|
-
const { featureFetchError, firstLogoPromptFetchError, clearErrors } =
|
|
76
|
+
const { featureFetchError, setFeatureFetchError, firstLogoPromptFetchError, clearErrors } =
|
|
77
|
+
useRequestErrors();
|
|
77
78
|
const siteId = siteDetails?.ID;
|
|
78
79
|
const [ logoAccepted, setLogoAccepted ] = useState( false );
|
|
79
80
|
const { nextTierCheckoutURL: upgradeURL } = useCheckout();
|
|
@@ -105,6 +106,15 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
105
106
|
*/
|
|
106
107
|
const initializeModal = useCallback( async () => {
|
|
107
108
|
try {
|
|
109
|
+
if ( ! siteId ) {
|
|
110
|
+
throw new Error( 'Site ID is missing' );
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if ( ! feature?.featuresControl?.[ 'logo-generator' ]?.enabled ) {
|
|
114
|
+
setFeatureFetchError( 'Failed to fetch feature data' );
|
|
115
|
+
throw new Error( 'Failed to fetch feature data' );
|
|
116
|
+
}
|
|
117
|
+
|
|
108
118
|
const hasHistory = ! isLogoHistoryEmpty( String( siteId ) );
|
|
109
119
|
|
|
110
120
|
const logoCost = feature?.costs?.[ 'jetpack-ai-logo-generator' ]?.logo ?? DEFAULT_LOGO_COST;
|
|
@@ -125,10 +135,10 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
125
135
|
: currentLimit < currentUsage );
|
|
126
136
|
|
|
127
137
|
// If the site requires an upgrade, show the upgrade screen immediately.
|
|
128
|
-
setNeedsFeature(
|
|
138
|
+
setNeedsFeature( currentValue === 0 );
|
|
129
139
|
setNeedsMoreRequests( siteNeedsMoreRequests );
|
|
130
140
|
|
|
131
|
-
if (
|
|
141
|
+
if ( currentValue === 0 || siteNeedsMoreRequests ) {
|
|
132
142
|
setLoadingState( null );
|
|
133
143
|
return;
|
|
134
144
|
}
|
|
@@ -179,6 +189,7 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
179
189
|
isLogoHistoryEmpty,
|
|
180
190
|
siteId,
|
|
181
191
|
requireUpgrade,
|
|
192
|
+
setFeatureFetchError,
|
|
182
193
|
] );
|
|
183
194
|
|
|
184
195
|
const handleModalOpen = useCallback( async () => {
|
|
@@ -201,6 +212,15 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
201
212
|
recordTracksEvent( EVENT_MODAL_CLOSE, { context, placement } );
|
|
202
213
|
};
|
|
203
214
|
|
|
215
|
+
const handleReload = useCallback( () => {
|
|
216
|
+
if ( ! onReload ) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
closeModal();
|
|
220
|
+
requestedFeatureData.current = false;
|
|
221
|
+
onReload();
|
|
222
|
+
}, [ onReload, closeModal ] );
|
|
223
|
+
|
|
204
224
|
const handleApplyLogo = ( mediaId: number ) => {
|
|
205
225
|
setLogoAccepted( true );
|
|
206
226
|
onApplyLogo?.( mediaId );
|
|
@@ -229,7 +249,7 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
229
249
|
// Handles modal opening logic
|
|
230
250
|
useEffect( () => {
|
|
231
251
|
// While the modal is not open, the siteId is not set, or the feature data is not available, do nothing.
|
|
232
|
-
if ( ! isOpen
|
|
252
|
+
if ( ! isOpen ) {
|
|
233
253
|
return;
|
|
234
254
|
}
|
|
235
255
|
|
|
@@ -238,7 +258,7 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
238
258
|
needsToHandleModalOpen.current = false;
|
|
239
259
|
handleModalOpen();
|
|
240
260
|
}
|
|
241
|
-
}, [ isOpen,
|
|
261
|
+
}, [ isOpen, handleModalOpen ] );
|
|
242
262
|
|
|
243
263
|
let body: React.ReactNode;
|
|
244
264
|
|
|
@@ -248,10 +268,7 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
248
268
|
body = (
|
|
249
269
|
<FeatureFetchFailureScreen
|
|
250
270
|
onCancel={ closeModal }
|
|
251
|
-
onRetry={
|
|
252
|
-
closeModal();
|
|
253
|
-
onReload?.();
|
|
254
|
-
} }
|
|
271
|
+
onRetry={ onReload ? handleReload : null }
|
|
255
272
|
/>
|
|
256
273
|
);
|
|
257
274
|
} else if ( needsFeature || needsMoreRequests ) {
|
|
@@ -7,6 +7,7 @@ import { __, sprintf } from '@wordpress/i18n';
|
|
|
7
7
|
import { Icon, info } from '@wordpress/icons';
|
|
8
8
|
import debugFactory from 'debug';
|
|
9
9
|
import { useCallback, useEffect, useState, useRef } from 'react';
|
|
10
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
10
11
|
/**
|
|
11
12
|
* Internal dependencies
|
|
12
13
|
*/
|
|
@@ -37,6 +38,81 @@ type PromptProps = {
|
|
|
37
38
|
initialPrompt?: string;
|
|
38
39
|
};
|
|
39
40
|
|
|
41
|
+
export const AiModalPromptInput = ( {
|
|
42
|
+
prompt = '',
|
|
43
|
+
setPrompt = () => {},
|
|
44
|
+
disabled = false,
|
|
45
|
+
generateHandler = () => {},
|
|
46
|
+
placeholder = '',
|
|
47
|
+
buttonLabel = '',
|
|
48
|
+
}: {
|
|
49
|
+
prompt: string;
|
|
50
|
+
setPrompt: Dispatch< SetStateAction< string > >;
|
|
51
|
+
disabled: boolean;
|
|
52
|
+
generateHandler: () => void;
|
|
53
|
+
placeholder?: string;
|
|
54
|
+
buttonLabel?: string;
|
|
55
|
+
} ) => {
|
|
56
|
+
const inputRef = useRef< HTMLDivElement | null >( null );
|
|
57
|
+
const hasPrompt = prompt?.length >= MINIMUM_PROMPT_LENGTH;
|
|
58
|
+
|
|
59
|
+
const onPromptInput = ( event: React.ChangeEvent< HTMLInputElement > ) => {
|
|
60
|
+
setPrompt( event.target.textContent || '' );
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const onPromptPaste = ( event: React.ClipboardEvent< HTMLInputElement > ) => {
|
|
64
|
+
event.preventDefault();
|
|
65
|
+
|
|
66
|
+
const selection = event.currentTarget.ownerDocument.getSelection();
|
|
67
|
+
if ( ! selection || ! selection.rangeCount ) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Paste plain text only
|
|
72
|
+
const text = event.clipboardData.getData( 'text/plain' );
|
|
73
|
+
|
|
74
|
+
selection.deleteFromDocument();
|
|
75
|
+
const range = selection.getRangeAt( 0 );
|
|
76
|
+
range.insertNode( document.createTextNode( text ) );
|
|
77
|
+
selection.collapseToEnd();
|
|
78
|
+
|
|
79
|
+
setPrompt( inputRef.current?.textContent || '' );
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const onKeyDown = ( event: React.KeyboardEvent ) => {
|
|
83
|
+
if ( event.key === 'Enter' ) {
|
|
84
|
+
event.preventDefault();
|
|
85
|
+
generateHandler();
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div className="jetpack-ai-logo-generator__prompt-query">
|
|
91
|
+
<div
|
|
92
|
+
role="textbox"
|
|
93
|
+
tabIndex={ 0 }
|
|
94
|
+
ref={ inputRef }
|
|
95
|
+
contentEditable={ ! disabled }
|
|
96
|
+
// The content editable div is expected to be updated by the enhance prompt, so warnings are suppressed
|
|
97
|
+
suppressContentEditableWarning
|
|
98
|
+
className="prompt-query__input"
|
|
99
|
+
onInput={ onPromptInput }
|
|
100
|
+
onPaste={ onPromptPaste }
|
|
101
|
+
onKeyDown={ onKeyDown }
|
|
102
|
+
data-placeholder={ placeholder }
|
|
103
|
+
></div>
|
|
104
|
+
<Button
|
|
105
|
+
variant="primary"
|
|
106
|
+
className="jetpack-ai-logo-generator__prompt-submit"
|
|
107
|
+
onClick={ generateHandler }
|
|
108
|
+
disabled={ disabled || ! hasPrompt }
|
|
109
|
+
>
|
|
110
|
+
{ buttonLabel || __( 'Generate', 'jetpack-ai-client' ) }
|
|
111
|
+
</Button>
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
115
|
+
|
|
40
116
|
export const Prompt = ( { initialPrompt = '' }: PromptProps ) => {
|
|
41
117
|
const { tracks } = useAnalytics();
|
|
42
118
|
const { recordEvent: recordTracksEvent } = tracks;
|
|
@@ -107,7 +183,7 @@ export const Prompt = ( { initialPrompt = '' }: PromptProps ) => {
|
|
|
107
183
|
}, [ prompt ] );
|
|
108
184
|
|
|
109
185
|
useEffect( () => {
|
|
110
|
-
if ( imageStyles.length > 0 ) {
|
|
186
|
+
if ( imageStyles && imageStyles.length > 0 ) {
|
|
111
187
|
// Sort styles to have "None" and "Auto" first
|
|
112
188
|
setStyles(
|
|
113
189
|
[
|
|
@@ -143,29 +219,6 @@ export const Prompt = ( { initialPrompt = '' }: PromptProps ) => {
|
|
|
143
219
|
}
|
|
144
220
|
}, [ context, generateLogo, prompt, style ] );
|
|
145
221
|
|
|
146
|
-
const onPromptInput = ( event: React.ChangeEvent< HTMLInputElement > ) => {
|
|
147
|
-
setPrompt( event.target.textContent || '' );
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const onPromptPaste = ( event: React.ClipboardEvent< HTMLInputElement > ) => {
|
|
151
|
-
event.preventDefault();
|
|
152
|
-
|
|
153
|
-
const selection = event.currentTarget.ownerDocument.getSelection();
|
|
154
|
-
if ( ! selection || ! selection.rangeCount ) {
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Paste plain text only
|
|
159
|
-
const text = event.clipboardData.getData( 'text/plain' );
|
|
160
|
-
|
|
161
|
-
selection.deleteFromDocument();
|
|
162
|
-
const range = selection.getRangeAt( 0 );
|
|
163
|
-
range.insertNode( document.createTextNode( text ) );
|
|
164
|
-
selection.collapseToEnd();
|
|
165
|
-
|
|
166
|
-
setPrompt( inputRef.current?.textContent || '' );
|
|
167
|
-
};
|
|
168
|
-
|
|
169
222
|
const onUpgradeClick = () => {
|
|
170
223
|
recordTracksEvent( EVENT_UPGRADE, { context, placement: EVENT_PLACEMENT_INPUT_FOOTER } );
|
|
171
224
|
};
|
|
@@ -179,13 +232,6 @@ export const Prompt = ( { initialPrompt = '' }: PromptProps ) => {
|
|
|
179
232
|
[ context, setStyle, recordTracksEvent ]
|
|
180
233
|
);
|
|
181
234
|
|
|
182
|
-
const onKeyDown = ( event: React.KeyboardEvent ) => {
|
|
183
|
-
if ( event.key === 'Enter' ) {
|
|
184
|
-
event.preventDefault();
|
|
185
|
-
onGenerate();
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
|
|
189
235
|
return (
|
|
190
236
|
<div className="jetpack-ai-logo-generator__prompt">
|
|
191
237
|
<div className="jetpack-ai-logo-generator__prompt-header">
|
|
@@ -212,32 +258,16 @@ export const Prompt = ( { initialPrompt = '' }: PromptProps ) => {
|
|
|
212
258
|
/>
|
|
213
259
|
) }
|
|
214
260
|
</div>
|
|
215
|
-
<
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
onPaste={ onPromptPaste }
|
|
226
|
-
onKeyDown={ onKeyDown }
|
|
227
|
-
data-placeholder={ __(
|
|
228
|
-
'Describe your site or simply ask for a logo specifying some details about it',
|
|
229
|
-
'jetpack-ai-client'
|
|
230
|
-
) }
|
|
231
|
-
></div>
|
|
232
|
-
<Button
|
|
233
|
-
variant="primary"
|
|
234
|
-
className="jetpack-ai-logo-generator__prompt-submit"
|
|
235
|
-
onClick={ onGenerate }
|
|
236
|
-
disabled={ isBusy || requireUpgrade || ! hasPrompt }
|
|
237
|
-
>
|
|
238
|
-
{ __( 'Generate', 'jetpack-ai-client' ) }
|
|
239
|
-
</Button>
|
|
240
|
-
</div>
|
|
261
|
+
<AiModalPromptInput
|
|
262
|
+
prompt={ prompt }
|
|
263
|
+
setPrompt={ setPrompt }
|
|
264
|
+
generateHandler={ onGenerate }
|
|
265
|
+
disabled={ isBusy || requireUpgrade }
|
|
266
|
+
placeholder={ __(
|
|
267
|
+
'Describe your site or simply ask for a logo specifying some details about it',
|
|
268
|
+
'jetpack-ai-client'
|
|
269
|
+
) }
|
|
270
|
+
/>
|
|
241
271
|
<div className="jetpack-ai-logo-generator__prompt-footer">
|
|
242
272
|
{ ! isUnlimited && ! requireUpgrade && (
|
|
243
273
|
<div className="jetpack-ai-logo-generator__prompt-requests">
|
|
@@ -92,7 +92,7 @@ const useLogoGenerator = () => {
|
|
|
92
92
|
const logoGeneratorControl = aiAssistantFeatureData?.featuresControl?.[
|
|
93
93
|
'logo-generator'
|
|
94
94
|
] as LogoGeneratorFeatureControl;
|
|
95
|
-
const imageStyles: Array< ImageStyleObject > = logoGeneratorControl?.styles;
|
|
95
|
+
const imageStyles: Array< ImageStyleObject > = logoGeneratorControl?.styles || [];
|
|
96
96
|
|
|
97
97
|
const generateFirstPrompt = useCallback(
|
|
98
98
|
async function (): Promise< string > {
|
|
@@ -93,6 +93,10 @@ const actions = {
|
|
|
93
93
|
query: 'force=wpcom',
|
|
94
94
|
} );
|
|
95
95
|
|
|
96
|
+
if ( response.data ) {
|
|
97
|
+
throw new Error( 'Failed to fetch' );
|
|
98
|
+
}
|
|
99
|
+
|
|
96
100
|
// Store the feature in the store.
|
|
97
101
|
dispatch(
|
|
98
102
|
actions.storeAiAssistantFeature( mapAiFeatureResponseToAiFeatureProps( response ) )
|
|
@@ -325,6 +325,16 @@ export default function reducer(
|
|
|
325
325
|
case ACTION_SET_FEATURE_FETCH_ERROR:
|
|
326
326
|
return {
|
|
327
327
|
...state,
|
|
328
|
+
features: {
|
|
329
|
+
...state.features,
|
|
330
|
+
aiAssistantFeature: {
|
|
331
|
+
...state?.features?.aiAssistantFeature,
|
|
332
|
+
_meta: {
|
|
333
|
+
...state?.features?.aiAssistantFeature?._meta,
|
|
334
|
+
isRequesting: false,
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
},
|
|
328
338
|
_meta: {
|
|
329
339
|
...( state._meta ?? {} ),
|
|
330
340
|
featureFetchError: action.error,
|
|
@@ -220,6 +220,7 @@ export type AiAssistantFeatureEndpointResponseProps = {
|
|
|
220
220
|
};
|
|
221
221
|
};
|
|
222
222
|
'features-control'?: FeaturesControl;
|
|
223
|
+
data?: string; // when WP responds with a 200 status code but it's the error wrap
|
|
223
224
|
};
|
|
224
225
|
|
|
225
226
|
export type SaveLogo = ( logo: Logo ) => Promise< { mediaId: number; mediaURL: string } >;
|