@automattic/jetpack-ai-client 0.16.4 → 0.18.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 +15 -0
- package/build/components/ai-control/extension-ai-control.d.ts +2 -1
- package/build/components/ai-control/extension-ai-control.js +5 -2
- package/build/components/message/index.d.ts +6 -0
- package/build/components/message/index.js +13 -0
- package/build/jwt/index.js +1 -1
- package/build/logo-generator/components/fair-usage-notice.d.ts +11 -0
- package/build/logo-generator/components/fair-usage-notice.js +19 -0
- package/build/logo-generator/components/generator-modal.js +13 -6
- package/build/logo-generator/components/history-carousel.js +6 -5
- package/build/logo-generator/components/logo-presenter.js +8 -3
- package/build/logo-generator/components/prompt.js +5 -4
- package/build/logo-generator/hooks/use-checkout.js +3 -2
- package/build/logo-generator/hooks/use-fair-usage-notice-message.d.ts +3 -0
- package/build/logo-generator/hooks/use-fair-usage-notice-message.js +43 -0
- package/build/logo-generator/hooks/use-logo-generator.d.ts +3 -0
- package/build/logo-generator/hooks/use-logo-generator.js +7 -2
- package/build/logo-generator/store/actions.d.ts +4 -0
- package/build/logo-generator/store/actions.js +8 -1
- package/build/logo-generator/store/constants.d.ts +1 -0
- package/build/logo-generator/store/constants.js +1 -0
- package/build/logo-generator/store/reducer.d.ts +37 -0
- package/build/logo-generator/store/reducer.js +10 -1
- package/build/logo-generator/store/selectors.d.ts +14 -0
- package/build/logo-generator/store/selectors.js +21 -0
- package/build/logo-generator/store/types.d.ts +13 -0
- package/package.json +13 -13
- package/src/components/ai-control/extension-ai-control.tsx +10 -1
- package/src/components/message/index.tsx +20 -0
- package/src/jwt/index.ts +1 -2
- package/src/logo-generator/components/fair-usage-notice.tsx +38 -0
- package/src/logo-generator/components/generator-modal.tsx +24 -7
- package/src/logo-generator/components/history-carousel.tsx +8 -1
- package/src/logo-generator/components/logo-presenter.tsx +16 -5
- package/src/logo-generator/components/prompt.tsx +8 -4
- package/src/logo-generator/hooks/use-checkout.ts +7 -2
- package/src/logo-generator/hooks/use-fair-usage-notice-message.tsx +68 -0
- package/src/logo-generator/hooks/use-logo-generator.ts +8 -0
- package/src/logo-generator/store/actions.ts +9 -0
- package/src/logo-generator/store/constants.ts +1 -0
- package/src/logo-generator/store/reducer.ts +12 -0
- package/src/logo-generator/store/selectors.ts +24 -0
- package/src/logo-generator/store/types.ts +13 -0
|
@@ -24,6 +24,7 @@ import type { SiteDetails } from '../types.js';
|
|
|
24
24
|
* @param {Array< { url: string; description: string; mediaId?: number } >} action.history - The logo history
|
|
25
25
|
* @param {RequestError} action.error - The error to set
|
|
26
26
|
* @param {string} action.context - The context where the tool is being used
|
|
27
|
+
* @param {boolean} action.isLoadingHistory - Whether the history is being loaded
|
|
27
28
|
* @return {LogoGeneratorStateProp} The new state
|
|
28
29
|
*/
|
|
29
30
|
export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
@@ -51,6 +52,7 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
51
52
|
}>;
|
|
52
53
|
error?: RequestError;
|
|
53
54
|
context?: string;
|
|
55
|
+
isLoadingHistory?: boolean;
|
|
54
56
|
}): {
|
|
55
57
|
features: {
|
|
56
58
|
aiAssistantFeature: {
|
|
@@ -77,6 +79,7 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
77
79
|
logo: number;
|
|
78
80
|
};
|
|
79
81
|
};
|
|
82
|
+
featuresControl?: import("./types.js").FeaturesControl;
|
|
80
83
|
_meta?: {
|
|
81
84
|
isRequesting: boolean;
|
|
82
85
|
asyncRequestCountdown: number;
|
|
@@ -97,6 +100,7 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
97
100
|
saveToLibraryError?: RequestError;
|
|
98
101
|
logoUpdateError?: RequestError;
|
|
99
102
|
context: string;
|
|
103
|
+
isLoadingHistory: boolean;
|
|
100
104
|
};
|
|
101
105
|
siteDetails?: SiteDetails | Record<string, never>;
|
|
102
106
|
history: import("./types.js").Logo[];
|
|
@@ -119,6 +123,7 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
119
123
|
saveToLibraryError?: RequestError;
|
|
120
124
|
logoUpdateError?: RequestError;
|
|
121
125
|
context: string;
|
|
126
|
+
isLoadingHistory: boolean;
|
|
122
127
|
};
|
|
123
128
|
siteDetails?: SiteDetails | Record<string, never>;
|
|
124
129
|
features: {
|
|
@@ -137,6 +142,7 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
137
142
|
saveToLibraryError?: RequestError;
|
|
138
143
|
logoUpdateError?: RequestError;
|
|
139
144
|
context?: string;
|
|
145
|
+
isLoadingHistory?: boolean;
|
|
140
146
|
};
|
|
141
147
|
siteDetails?: SiteDetails | Record<string, never>;
|
|
142
148
|
features: {
|
|
@@ -157,6 +163,7 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
157
163
|
saveToLibraryError?: RequestError;
|
|
158
164
|
logoUpdateError?: RequestError;
|
|
159
165
|
context?: string;
|
|
166
|
+
isLoadingHistory?: boolean;
|
|
160
167
|
};
|
|
161
168
|
siteDetails?: SiteDetails | Record<string, never>;
|
|
162
169
|
features: {
|
|
@@ -177,6 +184,7 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
177
184
|
saveToLibraryError?: RequestError;
|
|
178
185
|
logoUpdateError?: RequestError;
|
|
179
186
|
context?: string;
|
|
187
|
+
isLoadingHistory?: boolean;
|
|
180
188
|
};
|
|
181
189
|
siteDetails?: SiteDetails | Record<string, never>;
|
|
182
190
|
features: {
|
|
@@ -197,6 +205,7 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
197
205
|
saveToLibraryError?: RequestError;
|
|
198
206
|
logoUpdateError?: RequestError;
|
|
199
207
|
context?: string;
|
|
208
|
+
isLoadingHistory?: boolean;
|
|
200
209
|
};
|
|
201
210
|
siteDetails?: SiteDetails | Record<string, never>;
|
|
202
211
|
features: {
|
|
@@ -217,6 +226,7 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
217
226
|
saveToLibraryError?: RequestError;
|
|
218
227
|
logoUpdateError?: RequestError;
|
|
219
228
|
context?: string;
|
|
229
|
+
isLoadingHistory?: boolean;
|
|
220
230
|
};
|
|
221
231
|
siteDetails?: SiteDetails | Record<string, never>;
|
|
222
232
|
features: {
|
|
@@ -237,6 +247,7 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
237
247
|
saveToLibraryError?: RequestError;
|
|
238
248
|
logoUpdateError?: RequestError;
|
|
239
249
|
context?: string;
|
|
250
|
+
isLoadingHistory?: boolean;
|
|
240
251
|
};
|
|
241
252
|
siteDetails?: SiteDetails | Record<string, never>;
|
|
242
253
|
features: {
|
|
@@ -257,6 +268,7 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
257
268
|
saveToLibraryError?: RequestError;
|
|
258
269
|
logoUpdateError?: RequestError;
|
|
259
270
|
context?: string;
|
|
271
|
+
isLoadingHistory?: boolean;
|
|
260
272
|
};
|
|
261
273
|
siteDetails?: SiteDetails | Record<string, never>;
|
|
262
274
|
features: {
|
|
@@ -277,6 +289,7 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
277
289
|
saveToLibraryError?: RequestError;
|
|
278
290
|
logoUpdateError?: RequestError;
|
|
279
291
|
context?: string;
|
|
292
|
+
isLoadingHistory?: boolean;
|
|
280
293
|
};
|
|
281
294
|
siteDetails?: SiteDetails | Record<string, never>;
|
|
282
295
|
features: {
|
|
@@ -297,6 +310,7 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
297
310
|
logoFetchError?: RequestError;
|
|
298
311
|
logoUpdateError?: RequestError;
|
|
299
312
|
context?: string;
|
|
313
|
+
isLoadingHistory?: boolean;
|
|
300
314
|
};
|
|
301
315
|
siteDetails?: SiteDetails | Record<string, never>;
|
|
302
316
|
features: {
|
|
@@ -317,6 +331,7 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
317
331
|
logoFetchError?: RequestError;
|
|
318
332
|
saveToLibraryError?: RequestError;
|
|
319
333
|
context?: string;
|
|
334
|
+
isLoadingHistory?: boolean;
|
|
320
335
|
};
|
|
321
336
|
siteDetails?: SiteDetails | Record<string, never>;
|
|
322
337
|
features: {
|
|
@@ -337,6 +352,28 @@ export default function reducer(state: LogoGeneratorStateProp, action: {
|
|
|
337
352
|
logoFetchError?: RequestError;
|
|
338
353
|
saveToLibraryError?: RequestError;
|
|
339
354
|
logoUpdateError?: RequestError;
|
|
355
|
+
isLoadingHistory?: boolean;
|
|
356
|
+
};
|
|
357
|
+
siteDetails?: SiteDetails | Record<string, never>;
|
|
358
|
+
features: {
|
|
359
|
+
aiAssistantFeature?: AiFeatureStateProps;
|
|
360
|
+
};
|
|
361
|
+
history: import("./types.js").Logo[];
|
|
362
|
+
selectedLogoIndex: number;
|
|
363
|
+
} | {
|
|
364
|
+
_meta: {
|
|
365
|
+
isLoadingHistory: boolean;
|
|
366
|
+
isSavingLogoToLibrary?: boolean;
|
|
367
|
+
isApplyingLogo?: boolean;
|
|
368
|
+
isRequestingImage?: boolean;
|
|
369
|
+
isEnhancingPrompt?: boolean;
|
|
370
|
+
featureFetchError?: RequestError;
|
|
371
|
+
firstLogoPromptFetchError?: RequestError;
|
|
372
|
+
enhancePromptFetchError?: RequestError;
|
|
373
|
+
logoFetchError?: RequestError;
|
|
374
|
+
saveToLibraryError?: RequestError;
|
|
375
|
+
logoUpdateError?: RequestError;
|
|
376
|
+
context?: string;
|
|
340
377
|
};
|
|
341
378
|
siteDetails?: SiteDetails | Record<string, never>;
|
|
342
379
|
features: {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Types & Constants
|
|
3
3
|
*/
|
|
4
4
|
import { DEFAULT_LOGO_COST } from '../constants.js';
|
|
5
|
-
import { ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT, ACTION_REQUEST_AI_ASSISTANT_FEATURE, ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE, ACTION_STORE_AI_ASSISTANT_FEATURE, ASYNC_REQUEST_COUNTDOWN_INIT_VALUE, FREE_PLAN_REQUESTS_LIMIT, UNLIMITED_PLAN_REQUESTS_LIMIT, ACTION_SET_TIER_PLANS_ENABLED, ACTION_SET_SITE_DETAILS, ACTION_SET_SELECTED_LOGO_INDEX, ACTION_ADD_LOGO_TO_HISTORY, ACTION_SAVE_SELECTED_LOGO, ACTION_SET_IS_SAVING_LOGO_TO_LIBRARY, ACTION_SET_IS_REQUESTING_IMAGE, ACTION_SET_IS_APPLYING_LOGO, ACTION_SET_IS_ENHANCING_PROMPT, ACTION_SET_SITE_HISTORY, ACTION_SET_FEATURE_FETCH_ERROR, ACTION_SET_FIRST_LOGO_PROMPT_FETCH_ERROR, ACTION_SET_ENHANCE_PROMPT_FETCH_ERROR, ACTION_SET_LOGO_FETCH_ERROR, ACTION_SET_SAVE_TO_LIBRARY_ERROR, ACTION_SET_LOGO_UPDATE_ERROR, ACTION_SET_CONTEXT, } from './constants.js';
|
|
5
|
+
import { ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT, ACTION_REQUEST_AI_ASSISTANT_FEATURE, ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE, ACTION_STORE_AI_ASSISTANT_FEATURE, ASYNC_REQUEST_COUNTDOWN_INIT_VALUE, FREE_PLAN_REQUESTS_LIMIT, UNLIMITED_PLAN_REQUESTS_LIMIT, ACTION_SET_TIER_PLANS_ENABLED, ACTION_SET_SITE_DETAILS, ACTION_SET_SELECTED_LOGO_INDEX, ACTION_ADD_LOGO_TO_HISTORY, ACTION_SAVE_SELECTED_LOGO, ACTION_SET_IS_SAVING_LOGO_TO_LIBRARY, ACTION_SET_IS_REQUESTING_IMAGE, ACTION_SET_IS_APPLYING_LOGO, ACTION_SET_IS_ENHANCING_PROMPT, ACTION_SET_SITE_HISTORY, ACTION_SET_FEATURE_FETCH_ERROR, ACTION_SET_FIRST_LOGO_PROMPT_FETCH_ERROR, ACTION_SET_ENHANCE_PROMPT_FETCH_ERROR, ACTION_SET_LOGO_FETCH_ERROR, ACTION_SET_SAVE_TO_LIBRARY_ERROR, ACTION_SET_LOGO_UPDATE_ERROR, ACTION_SET_CONTEXT, ACTION_SET_IS_LOADING_HISTORY, } from './constants.js';
|
|
6
6
|
import INITIAL_STATE from './initial-state.js';
|
|
7
7
|
/**
|
|
8
8
|
* Reducer for the Logo Generator store.
|
|
@@ -28,6 +28,7 @@ import INITIAL_STATE from './initial-state.js';
|
|
|
28
28
|
* @param {Array< { url: string; description: string; mediaId?: number } >} action.history - The logo history
|
|
29
29
|
* @param {RequestError} action.error - The error to set
|
|
30
30
|
* @param {string} action.context - The context where the tool is being used
|
|
31
|
+
* @param {boolean} action.isLoadingHistory - Whether the history is being loaded
|
|
31
32
|
* @return {LogoGeneratorStateProp} The new state
|
|
32
33
|
*/
|
|
33
34
|
export default function reducer(state = INITIAL_STATE, action) {
|
|
@@ -288,6 +289,14 @@ export default function reducer(state = INITIAL_STATE, action) {
|
|
|
288
289
|
context: action.context,
|
|
289
290
|
},
|
|
290
291
|
};
|
|
292
|
+
case ACTION_SET_IS_LOADING_HISTORY:
|
|
293
|
+
return {
|
|
294
|
+
...state,
|
|
295
|
+
_meta: {
|
|
296
|
+
...(state._meta ?? {}),
|
|
297
|
+
isLoadingHistory: action.isLoadingHistory,
|
|
298
|
+
},
|
|
299
|
+
};
|
|
291
300
|
}
|
|
292
301
|
return state;
|
|
293
302
|
}
|
|
@@ -115,5 +115,19 @@ declare const selectors: {
|
|
|
115
115
|
* @return {string} The context value.
|
|
116
116
|
*/
|
|
117
117
|
getContext(state: LogoGeneratorStateProp): string;
|
|
118
|
+
/**
|
|
119
|
+
* Get tier plans enabled status.
|
|
120
|
+
*
|
|
121
|
+
* @param {LogoGeneratorStateProp} state - The app state tree.
|
|
122
|
+
* @return {boolean} The tier plans enabled status.
|
|
123
|
+
*/
|
|
124
|
+
getTierPlansEnabled(state: LogoGeneratorStateProp): boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Get tier plans enabled status.
|
|
127
|
+
*
|
|
128
|
+
* @param {LogoGeneratorStateProp} state - The app state tree.
|
|
129
|
+
* @return {boolean} The loading logo history status.
|
|
130
|
+
*/
|
|
131
|
+
getIsLoadingHistory(state: LogoGeneratorStateProp): boolean;
|
|
118
132
|
};
|
|
119
133
|
export default selectors;
|
|
@@ -104,6 +104,9 @@ const selectors = {
|
|
|
104
104
|
*/
|
|
105
105
|
getRequireUpgrade(state) {
|
|
106
106
|
const feature = state.features.aiAssistantFeature;
|
|
107
|
+
if (!feature?.tierPlansEnabled) {
|
|
108
|
+
return feature?.requireUpgrade;
|
|
109
|
+
}
|
|
107
110
|
const logoCost = feature?.costs?.['jetpack-ai-logo-generator']?.logo ?? DEFAULT_LOGO_COST;
|
|
108
111
|
const currentLimit = feature?.currentTier?.value || 0;
|
|
109
112
|
const currentUsage = feature?.usagePeriod?.requestsCount || 0;
|
|
@@ -169,5 +172,23 @@ const selectors = {
|
|
|
169
172
|
getContext(state) {
|
|
170
173
|
return state._meta?.context ?? '';
|
|
171
174
|
},
|
|
175
|
+
/**
|
|
176
|
+
* Get tier plans enabled status.
|
|
177
|
+
*
|
|
178
|
+
* @param {LogoGeneratorStateProp} state - The app state tree.
|
|
179
|
+
* @return {boolean} The tier plans enabled status.
|
|
180
|
+
*/
|
|
181
|
+
getTierPlansEnabled(state) {
|
|
182
|
+
return state.features.aiAssistantFeature?.tierPlansEnabled ?? false;
|
|
183
|
+
},
|
|
184
|
+
/**
|
|
185
|
+
* Get tier plans enabled status.
|
|
186
|
+
*
|
|
187
|
+
* @param {LogoGeneratorStateProp} state - The app state tree.
|
|
188
|
+
* @return {boolean} The loading logo history status.
|
|
189
|
+
*/
|
|
190
|
+
getIsLoadingHistory(state) {
|
|
191
|
+
return state._meta?.isLoadingHistory ?? false;
|
|
192
|
+
},
|
|
172
193
|
};
|
|
173
194
|
export default selectors;
|
|
@@ -53,6 +53,14 @@ export type TierProp = {
|
|
|
53
53
|
export type TierLimitProp = TierUnlimitedProps['limit'] | TierFreeProps['limit'] | Tier100Props['limit'] | Tier200Props['limit'] | Tier500Props['limit'] | Tier750Props['limit'] | Tier1000Props['limit'];
|
|
54
54
|
export type TierSlugProp = TierUnlimitedProps['slug'] | TierFreeProps['slug'] | Tier100Props['slug'] | Tier200Props['slug'] | Tier500Props['slug'] | Tier750Props['slug'] | Tier1000Props['slug'];
|
|
55
55
|
export type TierValueProp = TierUnlimitedProps['value'] | TierFreeProps['value'] | Tier100Props['value'] | Tier200Props['value'] | Tier500Props['value'] | Tier750Props['value'] | Tier1000Props['value'];
|
|
56
|
+
export type FeatureControl = {
|
|
57
|
+
enabled: boolean;
|
|
58
|
+
'min-jetpack-version': string;
|
|
59
|
+
[key: string]: FeatureControl | boolean | string;
|
|
60
|
+
};
|
|
61
|
+
export type FeaturesControl = {
|
|
62
|
+
[key: string]: FeatureControl;
|
|
63
|
+
};
|
|
56
64
|
export type AiFeatureProps = {
|
|
57
65
|
hasFeature: boolean;
|
|
58
66
|
isOverLimit: boolean;
|
|
@@ -75,6 +83,7 @@ export type AiFeatureProps = {
|
|
|
75
83
|
logo: number;
|
|
76
84
|
};
|
|
77
85
|
};
|
|
86
|
+
featuresControl?: FeaturesControl;
|
|
78
87
|
};
|
|
79
88
|
export type AiFeatureStateProps = AiFeatureProps & {
|
|
80
89
|
_meta?: {
|
|
@@ -103,6 +112,7 @@ export type LogoGeneratorStateProp = {
|
|
|
103
112
|
saveToLibraryError?: RequestError;
|
|
104
113
|
logoUpdateError?: RequestError;
|
|
105
114
|
context: string;
|
|
115
|
+
isLoadingHistory: boolean;
|
|
106
116
|
};
|
|
107
117
|
siteDetails?: SiteDetails | Record<string, never>;
|
|
108
118
|
features: {
|
|
@@ -131,6 +141,8 @@ export type Selectors = {
|
|
|
131
141
|
getSaveToLibraryError(): RequestError;
|
|
132
142
|
getLogoUpdateError(): RequestError;
|
|
133
143
|
getContext(): string;
|
|
144
|
+
getTierPlansEnabled(): boolean;
|
|
145
|
+
getIsLoadingHistory(): boolean;
|
|
134
146
|
};
|
|
135
147
|
export type AiAssistantFeatureEndpointResponseProps = {
|
|
136
148
|
'is-enabled': boolean;
|
|
@@ -157,6 +169,7 @@ export type AiAssistantFeatureEndpointResponseProps = {
|
|
|
157
169
|
logo: number;
|
|
158
170
|
};
|
|
159
171
|
};
|
|
172
|
+
'features-control'?: FeaturesControl;
|
|
160
173
|
};
|
|
161
174
|
export type SaveLogo = (logo: Logo) => Promise<{
|
|
162
175
|
mediaId: number;
|
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.18.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": {
|
|
@@ -43,21 +43,21 @@
|
|
|
43
43
|
"main": "./build/index.js",
|
|
44
44
|
"types": "./build/index.d.ts",
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@automattic/jetpack-base-styles": "^0.6.
|
|
47
|
-
"@automattic/jetpack-connection": "^0.35.
|
|
48
|
-
"@automattic/jetpack-shared-extension-utils": "^0.15.
|
|
46
|
+
"@automattic/jetpack-base-styles": "^0.6.31",
|
|
47
|
+
"@automattic/jetpack-connection": "^0.35.6",
|
|
48
|
+
"@automattic/jetpack-shared-extension-utils": "^0.15.8",
|
|
49
49
|
"@microsoft/fetch-event-source": "2.0.1",
|
|
50
50
|
"@types/react": "18.3.3",
|
|
51
51
|
"@types/wordpress__block-editor": "11.5.15",
|
|
52
|
-
"@wordpress/api-fetch": "7.
|
|
53
|
-
"@wordpress/blob": "4.
|
|
54
|
-
"@wordpress/block-editor": "14.
|
|
55
|
-
"@wordpress/components": "28.
|
|
56
|
-
"@wordpress/compose": "7.
|
|
57
|
-
"@wordpress/data": "10.
|
|
58
|
-
"@wordpress/element": "6.
|
|
59
|
-
"@wordpress/i18n": "5.
|
|
60
|
-
"@wordpress/icons": "10.
|
|
52
|
+
"@wordpress/api-fetch": "7.6.0",
|
|
53
|
+
"@wordpress/blob": "4.6.0",
|
|
54
|
+
"@wordpress/block-editor": "14.1.0",
|
|
55
|
+
"@wordpress/components": "28.6.0",
|
|
56
|
+
"@wordpress/compose": "7.6.0",
|
|
57
|
+
"@wordpress/data": "10.6.0",
|
|
58
|
+
"@wordpress/element": "6.6.0",
|
|
59
|
+
"@wordpress/i18n": "5.6.0",
|
|
60
|
+
"@wordpress/icons": "10.6.0",
|
|
61
61
|
"clsx": "2.1.1",
|
|
62
62
|
"debug": "4.3.4",
|
|
63
63
|
"markdown-it": "14.0.0",
|
|
@@ -10,7 +10,12 @@ import React, { forwardRef } from 'react';
|
|
|
10
10
|
/**
|
|
11
11
|
* Internal dependencies
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
GuidelineMessage,
|
|
15
|
+
ErrorMessage,
|
|
16
|
+
UpgradeMessage,
|
|
17
|
+
FairUsageLimitMessage,
|
|
18
|
+
} from '../message/index.js';
|
|
14
19
|
import AIControl from './ai-control.js';
|
|
15
20
|
import './style.scss';
|
|
16
21
|
/**
|
|
@@ -31,6 +36,7 @@ type ExtensionAIControlProps = {
|
|
|
31
36
|
error?: RequestingErrorProps;
|
|
32
37
|
requestsRemaining?: number;
|
|
33
38
|
showUpgradeMessage?: boolean;
|
|
39
|
+
showFairUsageMessage?: boolean;
|
|
34
40
|
upgradeUrl?: string;
|
|
35
41
|
wrapperRef?: React.MutableRefObject< HTMLDivElement | null >;
|
|
36
42
|
onChange?: ( newValue: string ) => void;
|
|
@@ -62,6 +68,7 @@ export function ExtensionAIControl(
|
|
|
62
68
|
error,
|
|
63
69
|
requestsRemaining,
|
|
64
70
|
showUpgradeMessage = false,
|
|
71
|
+
showFairUsageMessage = false,
|
|
65
72
|
upgradeUrl,
|
|
66
73
|
wrapperRef,
|
|
67
74
|
onChange,
|
|
@@ -215,6 +222,8 @@ export function ExtensionAIControl(
|
|
|
215
222
|
upgradeUrl={ upgradeUrl }
|
|
216
223
|
/>
|
|
217
224
|
);
|
|
225
|
+
} else if ( showFairUsageMessage ) {
|
|
226
|
+
message = <FairUsageLimitMessage />;
|
|
218
227
|
} else if ( showUpgradeMessage ) {
|
|
219
228
|
message = (
|
|
220
229
|
<UpgradeMessage
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { ExternalLink, Button } from '@wordpress/components';
|
|
5
|
+
import { createInterpolateElement } from '@wordpress/element';
|
|
5
6
|
import { __, sprintf } from '@wordpress/i18n';
|
|
6
7
|
import { Icon, check, arrowRight } from '@wordpress/icons';
|
|
7
8
|
import clsx from 'clsx';
|
|
@@ -114,6 +115,25 @@ export function GuidelineMessage(): React.ReactElement {
|
|
|
114
115
|
);
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
/**
|
|
119
|
+
* React component to render a fair usage limit message.
|
|
120
|
+
*
|
|
121
|
+
* @return {React.ReactElement } - Message component.
|
|
122
|
+
*/
|
|
123
|
+
export function FairUsageLimitMessage(): React.ReactElement {
|
|
124
|
+
const message = __(
|
|
125
|
+
"You've reached this month's request limit, per our <link>fair usage policy</link>",
|
|
126
|
+
'jetpack-ai-client'
|
|
127
|
+
);
|
|
128
|
+
const element = createInterpolateElement( message, {
|
|
129
|
+
link: (
|
|
130
|
+
<ExternalLink href="https://jetpack.com/redirect/?source=ai-assistant-fair-usage-policy" />
|
|
131
|
+
),
|
|
132
|
+
} );
|
|
133
|
+
|
|
134
|
+
return <Message severity={ MESSAGE_SEVERITY_WARNING }>{ element }</Message>;
|
|
135
|
+
}
|
|
136
|
+
|
|
117
137
|
/**
|
|
118
138
|
* React component to render an upgrade message for free tier users
|
|
119
139
|
*
|
package/src/jwt/index.ts
CHANGED
|
@@ -49,8 +49,6 @@ export default async function requestJwt( {
|
|
|
49
49
|
siteId = siteId || window.JP_CONNECTION_INITIAL_STATE.siteSuffix;
|
|
50
50
|
expirationTime = expirationTime || JWT_TOKEN_EXPIRATION_TIME;
|
|
51
51
|
|
|
52
|
-
const isSimple = isSimpleSite();
|
|
53
|
-
|
|
54
52
|
// Trying to pick the token from localStorage
|
|
55
53
|
const token = localStorage.getItem( JWT_TOKEN_ID );
|
|
56
54
|
let tokenData: TokenDataProps | null = null;
|
|
@@ -70,6 +68,7 @@ export default async function requestJwt( {
|
|
|
70
68
|
|
|
71
69
|
let data: TokenDataEndpointResponseProps;
|
|
72
70
|
|
|
71
|
+
const isSimple = isSimpleSite();
|
|
73
72
|
if ( ! isSimple ) {
|
|
74
73
|
data = await apiFetch( {
|
|
75
74
|
/*
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Notice } from '@wordpress/components';
|
|
2
|
+
import useFairUsageNoticeMessage from '../hooks/use-fair-usage-notice-message.js';
|
|
3
|
+
/**
|
|
4
|
+
* Types
|
|
5
|
+
*/
|
|
6
|
+
import type { ReactElement } from 'react';
|
|
7
|
+
|
|
8
|
+
type FairUsageNoticeProps = {
|
|
9
|
+
variant?: 'error' | 'muted';
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The fair usage notice component.
|
|
14
|
+
* @param {FairUsageNoticeProps} props - Fair usage notice component props.
|
|
15
|
+
* @param {FairUsageNoticeProps.variant} props.variant - The variant of the notice to render.
|
|
16
|
+
* @return {ReactElement} the Notice component with the fair usage message.
|
|
17
|
+
*/
|
|
18
|
+
export const FairUsageNotice = ( { variant = 'error' }: FairUsageNoticeProps ) => {
|
|
19
|
+
const useFairUsageNoticeMessageElement = useFairUsageNoticeMessage();
|
|
20
|
+
|
|
21
|
+
if ( variant === 'muted' ) {
|
|
22
|
+
return (
|
|
23
|
+
<span className="jetpack-ai-fair-usage-notice-muted-variant">
|
|
24
|
+
{ useFairUsageNoticeMessageElement }
|
|
25
|
+
</span>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if ( variant === 'error' ) {
|
|
30
|
+
return (
|
|
31
|
+
<Notice status="error" isDismissible={ false } className="jetpack-ai-fair-usage-notice">
|
|
32
|
+
{ useFairUsageNoticeMessageElement }
|
|
33
|
+
</Notice>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return null;
|
|
38
|
+
};
|
|
@@ -24,6 +24,7 @@ import useLogoGenerator from '../hooks/use-logo-generator.js';
|
|
|
24
24
|
import useRequestErrors from '../hooks/use-request-errors.js';
|
|
25
25
|
import { isLogoHistoryEmpty, clearDeletedMedia } from '../lib/logo-storage.js';
|
|
26
26
|
import { STORE_NAME } from '../store/index.js';
|
|
27
|
+
// import { FairUsageNotice } from './fair-usage-notice.js';
|
|
27
28
|
import { FeatureFetchFailureScreen } from './feature-fetch-failure-screen.js';
|
|
28
29
|
import { FirstLoadScreen } from './first-load-screen.js';
|
|
29
30
|
import { HistoryCarousel } from './history-carousel.js';
|
|
@@ -51,7 +52,8 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
51
52
|
} ) => {
|
|
52
53
|
const { tracks } = useAnalytics();
|
|
53
54
|
const { recordEvent: recordTracksEvent } = tracks;
|
|
54
|
-
const { setSiteDetails, fetchAiAssistantFeature, loadLogoHistory } =
|
|
55
|
+
const { setSiteDetails, fetchAiAssistantFeature, loadLogoHistory, setIsLoadingHistory } =
|
|
56
|
+
useDispatch( STORE_NAME );
|
|
55
57
|
const { getIsRequestingAiAssistantFeature } = select( STORE_NAME );
|
|
56
58
|
const [ loadingState, setLoadingState ] = useState<
|
|
57
59
|
'loadingFeature' | 'analyzing' | 'generating' | null
|
|
@@ -61,8 +63,14 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
61
63
|
const requestedFeatureData = useRef< boolean >( false );
|
|
62
64
|
const [ needsFeature, setNeedsFeature ] = useState( false );
|
|
63
65
|
const [ needsMoreRequests, setNeedsMoreRequests ] = useState( false );
|
|
64
|
-
const {
|
|
65
|
-
|
|
66
|
+
const {
|
|
67
|
+
selectedLogo,
|
|
68
|
+
getAiAssistantFeature,
|
|
69
|
+
generateFirstPrompt,
|
|
70
|
+
generateLogo,
|
|
71
|
+
setContext,
|
|
72
|
+
tierPlansEnabled,
|
|
73
|
+
} = useLogoGenerator();
|
|
66
74
|
const { featureFetchError, firstLogoPromptFetchError, clearErrors } = useRequestErrors();
|
|
67
75
|
const siteId = siteDetails?.ID;
|
|
68
76
|
const [ logoAccepted, setLogoAccepted ] = useState( false );
|
|
@@ -96,11 +104,12 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
96
104
|
const initializeModal = useCallback( async () => {
|
|
97
105
|
try {
|
|
98
106
|
const hasHistory = ! isLogoHistoryEmpty( String( siteId ) );
|
|
107
|
+
|
|
99
108
|
const logoCost = feature?.costs?.[ 'jetpack-ai-logo-generator' ]?.logo ?? DEFAULT_LOGO_COST;
|
|
100
109
|
const promptCreationCost = 1;
|
|
101
110
|
const currentLimit = feature?.currentTier?.value || 0;
|
|
102
111
|
const currentUsage = feature?.usagePeriod?.requestsCount || 0;
|
|
103
|
-
const isUnlimited = currentLimit === 1;
|
|
112
|
+
const isUnlimited = ! tierPlansEnabled ? currentLimit > 0 : currentLimit === 1;
|
|
104
113
|
const hasNoNextTier = ! feature?.nextTier; // If there is no next tier, the user cannot upgrade.
|
|
105
114
|
|
|
106
115
|
// The user needs an upgrade immediately if they have no logos and not enough requests remaining for one prompt and one logo generation.
|
|
@@ -108,16 +117,20 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
108
117
|
! isUnlimited &&
|
|
109
118
|
! hasNoNextTier &&
|
|
110
119
|
! hasHistory &&
|
|
111
|
-
|
|
120
|
+
( tierPlansEnabled
|
|
121
|
+
? currentLimit - currentUsage < logoCost + promptCreationCost
|
|
122
|
+
: currentLimit < currentUsage );
|
|
112
123
|
|
|
113
124
|
// If the site requires an upgrade, show the upgrade screen immediately.
|
|
114
|
-
setNeedsFeature(
|
|
125
|
+
setNeedsFeature( currentLimit === 0 );
|
|
115
126
|
setNeedsMoreRequests( siteNeedsMoreRequests );
|
|
116
|
-
|
|
127
|
+
|
|
128
|
+
if ( currentLimit === 0 || siteNeedsMoreRequests ) {
|
|
117
129
|
setLoadingState( null );
|
|
118
130
|
return;
|
|
119
131
|
}
|
|
120
132
|
|
|
133
|
+
setIsLoadingHistory( true );
|
|
121
134
|
// Load the logo history and clear any deleted media.
|
|
122
135
|
await clearDeletedMedia( String( siteId ) );
|
|
123
136
|
loadLogoHistory( siteId );
|
|
@@ -125,6 +138,7 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
125
138
|
// If there is any logo, we do not need to generate a first logo again.
|
|
126
139
|
if ( ! isLogoHistoryEmpty( String( siteId ) ) ) {
|
|
127
140
|
setLoadingState( null );
|
|
141
|
+
setIsLoadingHistory( false );
|
|
128
142
|
return;
|
|
129
143
|
}
|
|
130
144
|
|
|
@@ -133,6 +147,7 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
133
147
|
} catch ( error ) {
|
|
134
148
|
debug( 'Error fetching feature', error );
|
|
135
149
|
setLoadingState( null );
|
|
150
|
+
setIsLoadingHistory( false );
|
|
136
151
|
}
|
|
137
152
|
}, [
|
|
138
153
|
feature,
|
|
@@ -159,6 +174,7 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
159
174
|
setNeedsMoreRequests( false );
|
|
160
175
|
clearErrors();
|
|
161
176
|
setLogoAccepted( false );
|
|
177
|
+
setIsLoadingHistory( false );
|
|
162
178
|
recordTracksEvent( EVENT_MODAL_CLOSE, { context, placement } );
|
|
163
179
|
};
|
|
164
180
|
|
|
@@ -227,6 +243,7 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
|
227
243
|
body = (
|
|
228
244
|
<>
|
|
229
245
|
{ ! logoAccepted && <Prompt initialPrompt={ initialPrompt } /> }
|
|
246
|
+
|
|
230
247
|
<LogoPresenter
|
|
231
248
|
logo={ selectedLogo }
|
|
232
249
|
onApplyLogo={ handleApplyLogo }
|
|
@@ -7,6 +7,7 @@ import clsx from 'clsx';
|
|
|
7
7
|
/**
|
|
8
8
|
* Internal dependencies
|
|
9
9
|
*/
|
|
10
|
+
import loader from '../assets/images/loader.gif';
|
|
10
11
|
import { EVENT_NAVIGATE } from '../constants.js';
|
|
11
12
|
import useLogoGenerator from '../hooks/use-logo-generator.js';
|
|
12
13
|
import './history-carousel.scss';
|
|
@@ -18,7 +19,8 @@ import type React from 'react';
|
|
|
18
19
|
export const HistoryCarousel: React.FC = () => {
|
|
19
20
|
const { tracks } = useAnalytics();
|
|
20
21
|
const { recordEvent: recordTracksEvent } = tracks;
|
|
21
|
-
const { logos, selectedLogo, setSelectedLogoIndex, context } =
|
|
22
|
+
const { logos, selectedLogo, setSelectedLogoIndex, context, isLoadingHistory } =
|
|
23
|
+
useLogoGenerator();
|
|
22
24
|
|
|
23
25
|
const handleClick = ( index: number ) => {
|
|
24
26
|
recordTracksEvent( EVENT_NAVIGATE, {
|
|
@@ -41,6 +43,11 @@ export const HistoryCarousel: React.FC = () => {
|
|
|
41
43
|
|
|
42
44
|
return (
|
|
43
45
|
<div className="jetpack-ai-logo-generator__carousel">
|
|
46
|
+
{ ! logos.length && isLoadingHistory && (
|
|
47
|
+
<Button disabled className={ clsx( 'jetpack-ai-logo-generator__carousel-logo' ) }>
|
|
48
|
+
<img height="48" width="48" src={ loader } alt={ 'loading' } />
|
|
49
|
+
</Button>
|
|
50
|
+
) }
|
|
44
51
|
{ logos.map( ( logo, index ) => (
|
|
45
52
|
<Button
|
|
46
53
|
key={ logo.url }
|
|
@@ -129,6 +129,17 @@ const LogoLoading: React.FC = () => {
|
|
|
129
129
|
);
|
|
130
130
|
};
|
|
131
131
|
|
|
132
|
+
const LogoFetching: React.FC = () => {
|
|
133
|
+
return (
|
|
134
|
+
<>
|
|
135
|
+
<ImageLoader className="jetpack-ai-logo-generator-modal-presenter__logo" />
|
|
136
|
+
<span className="jetpack-ai-logo-generator-modal-presenter__loading-text">
|
|
137
|
+
{ __( 'Fetching previous logos…', 'jetpack-ai-client' ) }
|
|
138
|
+
</span>
|
|
139
|
+
</>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
132
143
|
const LogoReady: React.FC< {
|
|
133
144
|
siteId: string;
|
|
134
145
|
logo: Logo;
|
|
@@ -177,16 +188,16 @@ export const LogoPresenter: React.FC< LogoPresenterProps > = ( {
|
|
|
177
188
|
logoAccepted = false,
|
|
178
189
|
siteId,
|
|
179
190
|
} ) => {
|
|
191
|
+
// eslint-disable-next-line @wordpress/no-unused-vars-before-return -- @todo Start extending jetpack-js-tools/eslintrc/react in eslintrc, then we can remove this disable comment.
|
|
180
192
|
const { isRequestingImage } = useLogoGenerator();
|
|
181
193
|
const { saveToLibraryError, logoUpdateError } = useRequestErrors();
|
|
182
194
|
|
|
183
|
-
if ( ! logo ) {
|
|
184
|
-
return null;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
195
|
let logoContent: React.ReactNode;
|
|
188
196
|
|
|
189
|
-
if (
|
|
197
|
+
if ( ! logo ) {
|
|
198
|
+
debug( 'No logo provided, history still loading or logo being generated' );
|
|
199
|
+
logoContent = <LogoFetching />;
|
|
200
|
+
} else if ( loading || isRequestingImage ) {
|
|
190
201
|
logoContent = <LogoLoading />;
|
|
191
202
|
} else if ( logoAccepted ) {
|
|
192
203
|
logoContent = <LogoUpdated logo={ logo } />;
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
import { useCheckout } from '../hooks/use-checkout.js';
|
|
21
21
|
import useLogoGenerator from '../hooks/use-logo-generator.js';
|
|
22
22
|
import useRequestErrors from '../hooks/use-request-errors.js';
|
|
23
|
+
import { FairUsageNotice } from './fair-usage-notice.js';
|
|
23
24
|
import { UpgradeNudge } from './upgrade-nudge.js';
|
|
24
25
|
import './prompt.scss';
|
|
25
26
|
|
|
@@ -44,6 +45,7 @@ export const Prompt: React.FC< { initialPrompt?: string } > = ( { initialPrompt
|
|
|
44
45
|
getAiAssistantFeature,
|
|
45
46
|
requireUpgrade,
|
|
46
47
|
context,
|
|
48
|
+
tierPlansEnabled,
|
|
47
49
|
} = useLogoGenerator();
|
|
48
50
|
|
|
49
51
|
const enhancingLabel = __( 'Enhancing…', 'jetpack-ai-client' );
|
|
@@ -100,13 +102,14 @@ export const Prompt: React.FC< { initialPrompt?: string } > = ( { initialPrompt
|
|
|
100
102
|
const onPromptPaste = ( event: React.ClipboardEvent< HTMLInputElement > ) => {
|
|
101
103
|
event.preventDefault();
|
|
102
104
|
|
|
103
|
-
// Paste plain text only
|
|
104
|
-
const text = event.clipboardData.getData( 'text/plain' );
|
|
105
|
-
|
|
106
105
|
const selection = window.getSelection();
|
|
107
106
|
if ( ! selection || ! selection.rangeCount ) {
|
|
108
107
|
return;
|
|
109
108
|
}
|
|
109
|
+
|
|
110
|
+
// Paste plain text only
|
|
111
|
+
const text = event.clipboardData.getData( 'text/plain' );
|
|
112
|
+
|
|
110
113
|
selection.deleteFromDocument();
|
|
111
114
|
const range = selection.getRangeAt( 0 );
|
|
112
115
|
range.insertNode( document.createTextNode( text ) );
|
|
@@ -194,7 +197,8 @@ export const Prompt: React.FC< { initialPrompt?: string } > = ( { initialPrompt
|
|
|
194
197
|
</Tooltip>
|
|
195
198
|
</div>
|
|
196
199
|
) }
|
|
197
|
-
{
|
|
200
|
+
{ requireUpgrade && tierPlansEnabled && <UpgradeNudge /> }
|
|
201
|
+
{ requireUpgrade && ! tierPlansEnabled && <FairUsageNotice /> }
|
|
198
202
|
{ enhancePromptFetchError && (
|
|
199
203
|
<div className="jetpack-ai-logo-generator__prompt-error">
|
|
200
204
|
{ __( 'Error enhancing prompt. Please try again.', 'jetpack-ai-client' ) }
|