@automattic/jetpack-ai-client 0.14.6 → 0.15.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.
Files changed (128) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/build/ask-question/sync.d.ts +2 -8
  3. package/build/ask-question/sync.js +20 -19
  4. package/build/hooks/use-image-generator/index.js +1 -1
  5. package/build/hooks/use-save-to-media-library/index.d.ts +12 -0
  6. package/build/hooks/use-save-to-media-library/index.js +74 -0
  7. package/build/index.d.ts +2 -0
  8. package/build/index.js +5 -0
  9. package/build/libs/index.d.ts +1 -1
  10. package/build/libs/index.js +1 -1
  11. package/build/libs/markdown/index.d.ts +2 -2
  12. package/build/libs/markdown/index.js +2 -2
  13. package/build/libs/markdown/markdown-to-html.d.ts +8 -1
  14. package/build/libs/markdown/markdown-to-html.js +10 -1
  15. package/build/logo-generator/assets/icons/ai.d.ts +6 -0
  16. package/build/logo-generator/assets/icons/ai.js +8 -0
  17. package/build/logo-generator/assets/icons/check.d.ts +6 -0
  18. package/build/logo-generator/assets/icons/check.js +8 -0
  19. package/build/logo-generator/assets/icons/logo.d.ts +6 -0
  20. package/build/logo-generator/assets/icons/logo.js +8 -0
  21. package/build/logo-generator/assets/icons/media.d.ts +6 -0
  22. package/build/logo-generator/assets/icons/media.js +8 -0
  23. package/build/logo-generator/components/feature-fetch-failure-screen.d.ts +8 -0
  24. package/build/logo-generator/components/feature-fetch-failure-screen.js +10 -0
  25. package/build/logo-generator/components/first-load-screen.d.ts +5 -0
  26. package/build/logo-generator/components/first-load-screen.js +16 -0
  27. package/build/logo-generator/components/generator-modal.d.ts +7 -0
  28. package/build/logo-generator/components/generator-modal.js +184 -0
  29. package/build/logo-generator/components/history-carousel.d.ts +6 -0
  30. package/build/logo-generator/components/history-carousel.js +36 -0
  31. package/build/logo-generator/components/image-loader.d.ts +7 -0
  32. package/build/logo-generator/components/image-loader.js +12 -0
  33. package/build/logo-generator/components/logo-presenter.d.ts +4 -0
  34. package/build/logo-generator/components/logo-presenter.js +106 -0
  35. package/build/logo-generator/components/prompt.d.ts +5 -0
  36. package/build/logo-generator/components/prompt.js +96 -0
  37. package/build/logo-generator/components/upgrade-nudge.d.ts +2 -0
  38. package/build/logo-generator/components/upgrade-nudge.js +30 -0
  39. package/build/logo-generator/components/upgrade-screen.d.ts +9 -0
  40. package/build/logo-generator/components/upgrade-screen.js +24 -0
  41. package/build/logo-generator/components/visit-site-banner.d.ts +10 -0
  42. package/build/logo-generator/components/visit-site-banner.js +16 -0
  43. package/build/logo-generator/constants.d.ts +16 -0
  44. package/build/logo-generator/constants.js +19 -0
  45. package/build/logo-generator/hooks/use-checkout.d.ts +4 -0
  46. package/build/logo-generator/hooks/use-checkout.js +26 -0
  47. package/build/logo-generator/hooks/use-logo-generator.d.ts +46 -0
  48. package/build/logo-generator/hooks/use-logo-generator.js +286 -0
  49. package/build/logo-generator/hooks/use-request-errors.d.ts +16 -0
  50. package/build/logo-generator/hooks/use-request-errors.js +46 -0
  51. package/build/logo-generator/index.d.ts +1 -0
  52. package/build/logo-generator/index.js +1 -0
  53. package/build/logo-generator/lib/logo-storage.d.ts +58 -0
  54. package/build/logo-generator/lib/logo-storage.js +123 -0
  55. package/build/logo-generator/lib/media-exists.d.ts +12 -0
  56. package/build/logo-generator/lib/media-exists.js +33 -0
  57. package/build/logo-generator/lib/set-site-logo.d.ts +13 -0
  58. package/build/logo-generator/lib/set-site-logo.js +26 -0
  59. package/build/logo-generator/lib/wpcom-limited-request.d.ts +7 -0
  60. package/build/logo-generator/lib/wpcom-limited-request.js +33 -0
  61. package/build/logo-generator/store/actions.d.ts +105 -0
  62. package/build/logo-generator/store/actions.js +193 -0
  63. package/build/logo-generator/store/constants.d.ts +44 -0
  64. package/build/logo-generator/store/constants.js +44 -0
  65. package/build/logo-generator/store/index.d.ts +1 -0
  66. package/build/logo-generator/store/index.js +19 -0
  67. package/build/logo-generator/store/initial-state.d.ts +3 -0
  68. package/build/logo-generator/store/initial-state.js +40 -0
  69. package/build/logo-generator/store/reducer.d.ts +347 -0
  70. package/build/logo-generator/store/reducer.js +293 -0
  71. package/build/logo-generator/store/selectors.d.ts +119 -0
  72. package/build/logo-generator/store/selectors.js +173 -0
  73. package/build/logo-generator/store/types.d.ts +164 -0
  74. package/build/logo-generator/store/types.js +1 -0
  75. package/build/logo-generator/types.d.ts +82 -0
  76. package/build/logo-generator/types.js +1 -0
  77. package/build/types.d.ts +6 -0
  78. package/package.json +5 -3
  79. package/src/ask-question/sync.ts +22 -27
  80. package/src/hooks/use-image-generator/index.ts +1 -1
  81. package/src/hooks/use-save-to-media-library/index.ts +95 -0
  82. package/src/index.ts +6 -0
  83. package/src/libs/index.ts +1 -0
  84. package/src/libs/markdown/index.ts +2 -2
  85. package/src/libs/markdown/markdown-to-html.ts +20 -3
  86. package/src/logo-generator/assets/icons/ai.tsx +21 -0
  87. package/src/logo-generator/assets/icons/check.tsx +23 -0
  88. package/src/logo-generator/assets/icons/icons.scss +5 -0
  89. package/src/logo-generator/assets/icons/logo.tsx +23 -0
  90. package/src/logo-generator/assets/icons/media.tsx +24 -0
  91. package/src/logo-generator/assets/images/jetpack-logo.svg +4 -0
  92. package/src/logo-generator/assets/images/loader.gif +0 -0
  93. package/src/logo-generator/assets/index.d.ts +3 -0
  94. package/src/logo-generator/components/feature-fetch-failure-screen.tsx +35 -0
  95. package/src/logo-generator/components/first-load-screen.scss +12 -0
  96. package/src/logo-generator/components/first-load-screen.tsx +32 -0
  97. package/src/logo-generator/components/generator-modal.scss +92 -0
  98. package/src/logo-generator/components/generator-modal.tsx +291 -0
  99. package/src/logo-generator/components/history-carousel.scss +36 -0
  100. package/src/logo-generator/components/history-carousel.tsx +57 -0
  101. package/src/logo-generator/components/image-loader.tsx +22 -0
  102. package/src/logo-generator/components/logo-presenter.scss +116 -0
  103. package/src/logo-generator/components/logo-presenter.tsx +234 -0
  104. package/src/logo-generator/components/prompt.scss +102 -0
  105. package/src/logo-generator/components/prompt.tsx +211 -0
  106. package/src/logo-generator/components/upgrade-nudge.scss +43 -0
  107. package/src/logo-generator/components/upgrade-nudge.tsx +58 -0
  108. package/src/logo-generator/components/upgrade-screen.tsx +67 -0
  109. package/src/logo-generator/components/visit-site-banner.scss +29 -0
  110. package/src/logo-generator/components/visit-site-banner.tsx +50 -0
  111. package/src/logo-generator/constants.ts +22 -0
  112. package/src/logo-generator/hooks/use-checkout.ts +37 -0
  113. package/src/logo-generator/hooks/use-logo-generator.ts +389 -0
  114. package/src/logo-generator/hooks/use-request-errors.ts +70 -0
  115. package/src/logo-generator/index.ts +1 -0
  116. package/src/logo-generator/lib/logo-storage.ts +166 -0
  117. package/src/logo-generator/lib/media-exists.ts +42 -0
  118. package/src/logo-generator/lib/set-site-logo.ts +32 -0
  119. package/src/logo-generator/lib/wpcom-limited-request.ts +41 -0
  120. package/src/logo-generator/store/actions.ts +251 -0
  121. package/src/logo-generator/store/constants.ts +49 -0
  122. package/src/logo-generator/store/index.ts +25 -0
  123. package/src/logo-generator/store/initial-state.ts +43 -0
  124. package/src/logo-generator/store/reducer.ts +387 -0
  125. package/src/logo-generator/store/selectors.ts +201 -0
  126. package/src/logo-generator/store/types.ts +207 -0
  127. package/src/logo-generator/types.ts +97 -0
  128. package/src/types.ts +8 -0
@@ -0,0 +1,387 @@
1
+ /**
2
+ * Types & Constants
3
+ */
4
+ import { DEFAULT_LOGO_COST } from '../constants.js';
5
+ import {
6
+ ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT,
7
+ ACTION_REQUEST_AI_ASSISTANT_FEATURE,
8
+ ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE,
9
+ ACTION_STORE_AI_ASSISTANT_FEATURE,
10
+ ASYNC_REQUEST_COUNTDOWN_INIT_VALUE,
11
+ FREE_PLAN_REQUESTS_LIMIT,
12
+ UNLIMITED_PLAN_REQUESTS_LIMIT,
13
+ ACTION_SET_TIER_PLANS_ENABLED,
14
+ ACTION_SET_SITE_DETAILS,
15
+ ACTION_SET_SELECTED_LOGO_INDEX,
16
+ ACTION_ADD_LOGO_TO_HISTORY,
17
+ ACTION_SAVE_SELECTED_LOGO,
18
+ ACTION_SET_IS_SAVING_LOGO_TO_LIBRARY,
19
+ ACTION_SET_IS_REQUESTING_IMAGE,
20
+ ACTION_SET_IS_APPLYING_LOGO,
21
+ ACTION_SET_IS_ENHANCING_PROMPT,
22
+ ACTION_SET_SITE_HISTORY,
23
+ ACTION_SET_FEATURE_FETCH_ERROR,
24
+ ACTION_SET_FIRST_LOGO_PROMPT_FETCH_ERROR,
25
+ ACTION_SET_ENHANCE_PROMPT_FETCH_ERROR,
26
+ ACTION_SET_LOGO_FETCH_ERROR,
27
+ ACTION_SET_SAVE_TO_LIBRARY_ERROR,
28
+ ACTION_SET_LOGO_UPDATE_ERROR,
29
+ ACTION_SET_CONTEXT,
30
+ } from './constants.js';
31
+ import INITIAL_STATE from './initial-state.js';
32
+ import type {
33
+ AiFeatureStateProps,
34
+ LogoGeneratorStateProp,
35
+ RequestError,
36
+ TierLimitProp,
37
+ } from './types.js';
38
+ import type { SiteDetails } from '../types.js';
39
+
40
+ /**
41
+ * Reducer for the Logo Generator store.
42
+ *
43
+ * @param {LogoGeneratorStateProp} state - The current state
44
+ * @param {object} action - The action to apply, as described by the properties below
45
+ * @param {string} action.type - The action type
46
+ * @param {AiFeatureStateProps} action.feature - The AI Assistant feature state
47
+ * @param {number} action.count - The number of requests to increase the counter by
48
+ * @param {boolean} action.requireUpgrade - Whether an upgrade is required
49
+ * @param {boolean} action.tierPlansEnabled - Whether tier plans are enabled
50
+ * @param {SiteDetails} action.siteDetails - The site details
51
+ * @param {number} action.selectedLogoIndex - The selected logo index
52
+ * @param {boolean} action.isSavingLogoToLibrary - Whether a logo is being saved to the library
53
+ * @param {boolean} action.isApplyingLogo - Whether a logo is being applied
54
+ * @param {object} action.logo - The logo to save, as described by the properties below
55
+ * @param {string} action.logo.url - The logo URL
56
+ * @param {string} action.logo.description - The logo description
57
+ * @param {number} action.mediaId - The media ID from backend
58
+ * @param {string} action.url - The URL to save
59
+ * @param {boolean} action.isRequestingImage - Whether an image is being requested
60
+ * @param {boolean} action.isEnhancingPrompt - Whether a prompt enhancement is being requested
61
+ * @param {Array< { url: string; description: string; mediaId?: number } >} action.history - The logo history
62
+ * @param {RequestError} action.error - The error to set
63
+ * @param {string} action.context - The context where the tool is being used
64
+ * @returns {LogoGeneratorStateProp} The new state
65
+ */
66
+ export default function reducer(
67
+ state = INITIAL_STATE,
68
+ action: {
69
+ type: string;
70
+ feature?: AiFeatureStateProps;
71
+ count?: number;
72
+ requireUpgrade?: boolean;
73
+ tierPlansEnabled?: boolean;
74
+ siteDetails?: SiteDetails;
75
+ selectedLogoIndex?: number;
76
+ isSavingLogoToLibrary?: boolean;
77
+ isApplyingLogo?: boolean;
78
+ logo?: { url: string; description: string };
79
+ mediaId?: number;
80
+ url?: string;
81
+ isRequestingImage?: boolean;
82
+ isEnhancingPrompt?: boolean;
83
+ history?: Array< { url: string; description: string; mediaId?: number } >;
84
+ error?: RequestError;
85
+ context?: string;
86
+ }
87
+ ) {
88
+ switch ( action.type ) {
89
+ case ACTION_REQUEST_AI_ASSISTANT_FEATURE:
90
+ return {
91
+ ...state,
92
+ _meta: {
93
+ ...( state._meta ?? {} ),
94
+ // Reset the error state when requesting the feature.
95
+ featureFetchError: null,
96
+ },
97
+ features: {
98
+ ...state.features,
99
+ aiAssistantFeature: {
100
+ ...state.features.aiAssistantFeature,
101
+ _meta: {
102
+ ...state?.features?.aiAssistantFeature?._meta,
103
+ isRequesting: true,
104
+ asyncRequestCountdown: ASYNC_REQUEST_COUNTDOWN_INIT_VALUE, // restore the countdown
105
+ asyncRequestTimerId: 0, // reset the timer id
106
+ },
107
+ },
108
+ },
109
+ };
110
+
111
+ case ACTION_STORE_AI_ASSISTANT_FEATURE: {
112
+ const defaultCosts = {
113
+ 'jetpack-ai-logo-generator': {
114
+ logo: DEFAULT_LOGO_COST,
115
+ },
116
+ };
117
+
118
+ return {
119
+ ...state,
120
+ features: {
121
+ ...state.features,
122
+ aiAssistantFeature: {
123
+ costs: defaultCosts,
124
+ ...action.feature,
125
+ // re evaluate requireUpgrade as the logo generator does not allow free usage
126
+ requireUpgrade:
127
+ action.feature?.requireUpgrade || action.feature?.currentTier?.value === 0,
128
+ _meta: {
129
+ ...state?.features?.aiAssistantFeature?._meta,
130
+ isRequesting: false,
131
+ },
132
+ },
133
+ },
134
+ };
135
+ }
136
+
137
+ case ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT: {
138
+ // Usage Period data
139
+ const usagePeriod = state?.features?.aiAssistantFeature?.usagePeriod || { requestsCount: 0 };
140
+
141
+ // Increase requests counters
142
+ const requestsCount =
143
+ ( state?.features?.aiAssistantFeature?.requestsCount || 0 ) + ( action.count ?? 1 );
144
+ usagePeriod.requestsCount += action.count ?? 1;
145
+
146
+ // Current tier value
147
+ const currentTierValue = state?.features?.aiAssistantFeature?.currentTier?.value;
148
+
149
+ const isFreeTierPlan =
150
+ ( typeof currentTierValue === 'undefined' &&
151
+ ! state?.features?.aiAssistantFeature?.hasFeature ) ||
152
+ currentTierValue === 0;
153
+
154
+ const isUnlimitedTierPlan =
155
+ ( typeof currentTierValue === 'undefined' &&
156
+ state?.features?.aiAssistantFeature?.hasFeature ) ||
157
+ currentTierValue === 1;
158
+
159
+ // Request limit defined with the current tier limit by default.
160
+ let requestsLimit: TierLimitProp =
161
+ state?.features?.aiAssistantFeature?.currentTier?.limit || FREE_PLAN_REQUESTS_LIMIT;
162
+
163
+ if ( isUnlimitedTierPlan ) {
164
+ requestsLimit = UNLIMITED_PLAN_REQUESTS_LIMIT;
165
+ } else if ( isFreeTierPlan ) {
166
+ requestsLimit = state?.features?.aiAssistantFeature?.requestsLimit as TierLimitProp;
167
+ }
168
+
169
+ const currentCount =
170
+ isUnlimitedTierPlan || isFreeTierPlan // @todo: update once tier data is available
171
+ ? requestsCount
172
+ : state?.features?.aiAssistantFeature?.usagePeriod?.requestsCount || 0;
173
+
174
+ /**
175
+ * Compute the AI Assistant Feature data optimistically,
176
+ * based on the Jetpack_AI_Helper::get_ai_assistance_feature() helper.
177
+ * @see _inc/lib/class-jetpack-ai-helper.php
178
+ */
179
+ const isOverLimit = currentCount >= requestsLimit;
180
+
181
+ // highest tier holds a soft limit so requireUpgrade is false on that case (nextTier null means highest tier)
182
+ const requireUpgrade =
183
+ isFreeTierPlan || ( isOverLimit && state?.features?.aiAssistantFeature?.nextTier !== null );
184
+
185
+ return {
186
+ ...state,
187
+ features: {
188
+ ...state.features,
189
+ aiAssistantFeature: {
190
+ ...state.features.aiAssistantFeature,
191
+ isOverLimit,
192
+ requestsCount,
193
+ requireUpgrade,
194
+ usagePeriod: { ...usagePeriod },
195
+ },
196
+ },
197
+ };
198
+ }
199
+
200
+ case ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE: {
201
+ /*
202
+ * If we require an upgrade, we are also over the limit;
203
+ * The opposite is not true, we can be over the limit without
204
+ * requiring an upgrade, for example when we are on the highest tier.
205
+ * In this case, we don't want to set isOverLimit to false.
206
+ */
207
+ return {
208
+ ...state,
209
+ features: {
210
+ ...state.features,
211
+ aiAssistantFeature: {
212
+ ...state.features.aiAssistantFeature,
213
+ requireUpgrade: action.requireUpgrade,
214
+ ...( action.requireUpgrade ? { isOverLimit: true } : {} ),
215
+ },
216
+ },
217
+ };
218
+ }
219
+
220
+ case ACTION_SET_TIER_PLANS_ENABLED: {
221
+ return {
222
+ ...state,
223
+ features: {
224
+ ...state.features,
225
+ aiAssistantFeature: {
226
+ ...state.features.aiAssistantFeature,
227
+ tierPlansEnabled: action.tierPlansEnabled,
228
+ },
229
+ },
230
+ };
231
+ }
232
+
233
+ case ACTION_SET_SITE_DETAILS: {
234
+ return {
235
+ ...state,
236
+ siteDetails: action.siteDetails,
237
+ };
238
+ }
239
+
240
+ case ACTION_SET_SELECTED_LOGO_INDEX: {
241
+ return {
242
+ ...state,
243
+ selectedLogoIndex: action.selectedLogoIndex,
244
+ };
245
+ }
246
+
247
+ case ACTION_ADD_LOGO_TO_HISTORY: {
248
+ const history = [ ...state.history, action.logo ];
249
+
250
+ return {
251
+ ...state,
252
+ history,
253
+ selectedLogoIndex: history.length - 1,
254
+ };
255
+ }
256
+
257
+ case ACTION_SET_IS_SAVING_LOGO_TO_LIBRARY: {
258
+ return {
259
+ ...state,
260
+ _meta: {
261
+ ...( state._meta ?? {} ),
262
+ isSavingLogoToLibrary: action.isSavingLogoToLibrary,
263
+ },
264
+ };
265
+ }
266
+
267
+ case ACTION_SET_IS_APPLYING_LOGO: {
268
+ return {
269
+ ...state,
270
+ _meta: {
271
+ ...( state._meta ?? {} ),
272
+ isApplyingLogo: action.isApplyingLogo,
273
+ },
274
+ };
275
+ }
276
+
277
+ case ACTION_SAVE_SELECTED_LOGO: {
278
+ const selectedLogo = state.history?.[ state.selectedLogoIndex ];
279
+
280
+ return {
281
+ ...state,
282
+ history: [
283
+ ...state.history.slice( 0, state.selectedLogoIndex ),
284
+ {
285
+ ...selectedLogo,
286
+ mediaId: action.mediaId,
287
+ url: action.url,
288
+ },
289
+ ...state.history.slice( state.selectedLogoIndex + 1 ),
290
+ ],
291
+ };
292
+ }
293
+
294
+ case ACTION_SET_IS_REQUESTING_IMAGE: {
295
+ return {
296
+ ...state,
297
+ _meta: {
298
+ ...( state._meta ?? {} ),
299
+ isRequestingImage: action.isRequestingImage,
300
+ },
301
+ };
302
+ }
303
+
304
+ case ACTION_SET_IS_ENHANCING_PROMPT: {
305
+ return {
306
+ ...state,
307
+ _meta: {
308
+ ...( state._meta ?? {} ),
309
+ isEnhancingPrompt: action.isEnhancingPrompt,
310
+ },
311
+ };
312
+ }
313
+
314
+ case ACTION_SET_SITE_HISTORY: {
315
+ return {
316
+ ...state,
317
+ history: action.history,
318
+ selectedLogoIndex: action.history?.length ? action.history.length - 1 : 0,
319
+ };
320
+ }
321
+
322
+ case ACTION_SET_FEATURE_FETCH_ERROR:
323
+ return {
324
+ ...state,
325
+ _meta: {
326
+ ...( state._meta ?? {} ),
327
+ featureFetchError: action.error,
328
+ },
329
+ };
330
+
331
+ case ACTION_SET_FIRST_LOGO_PROMPT_FETCH_ERROR:
332
+ return {
333
+ ...state,
334
+ _meta: {
335
+ ...( state._meta ?? {} ),
336
+ firstLogoPromptFetchError: action.error,
337
+ },
338
+ };
339
+
340
+ case ACTION_SET_ENHANCE_PROMPT_FETCH_ERROR:
341
+ return {
342
+ ...state,
343
+ _meta: {
344
+ ...( state._meta ?? {} ),
345
+ enhancePromptFetchError: action.error,
346
+ },
347
+ };
348
+
349
+ case ACTION_SET_LOGO_FETCH_ERROR:
350
+ return {
351
+ ...state,
352
+ _meta: {
353
+ ...( state._meta ?? {} ),
354
+ logoFetchError: action.error,
355
+ },
356
+ };
357
+
358
+ case ACTION_SET_SAVE_TO_LIBRARY_ERROR:
359
+ return {
360
+ ...state,
361
+ _meta: {
362
+ ...( state._meta ?? {} ),
363
+ saveToLibraryError: action.error,
364
+ },
365
+ };
366
+
367
+ case ACTION_SET_LOGO_UPDATE_ERROR:
368
+ return {
369
+ ...state,
370
+ _meta: {
371
+ ...( state._meta ?? {} ),
372
+ logoUpdateError: action.error,
373
+ },
374
+ };
375
+
376
+ case ACTION_SET_CONTEXT:
377
+ return {
378
+ ...state,
379
+ _meta: {
380
+ ...( state._meta ?? {} ),
381
+ context: action.context,
382
+ },
383
+ };
384
+ }
385
+
386
+ return state;
387
+ }
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Types
3
+ */
4
+ import { DEFAULT_LOGO_COST } from '../constants.js';
5
+ import type { AiFeatureProps, LogoGeneratorStateProp, Logo, RequestError } from './types.js';
6
+ import type { SiteDetails } from '../types.js';
7
+
8
+ const selectors = {
9
+ /**
10
+ * Return the AI Assistant feature.
11
+ * @param {LogoGeneratorStateProp} state - The app state tree.
12
+ * @returns {Partial<AiFeatureProps>} The AI Assistant feature data.
13
+ */
14
+ getAiAssistantFeature( state: LogoGeneratorStateProp ): Partial< AiFeatureProps > {
15
+ // Clean up the _meta property.
16
+ const data = { ...state.features.aiAssistantFeature };
17
+ delete data._meta;
18
+
19
+ return data;
20
+ },
21
+
22
+ /**
23
+ * Return the site details.
24
+ * @param {LogoGeneratorStateProp} state - The app state tree.
25
+ * @returns {Partial<SiteDetails> | undefined} The site details.
26
+ */
27
+ getSiteDetails( state: LogoGeneratorStateProp ): Partial< SiteDetails > | undefined {
28
+ return state.siteDetails;
29
+ },
30
+
31
+ /**
32
+ * Get the isRequesting flag for the AI Assistant feature.
33
+ * @param {LogoGeneratorStateProp} state - The app state tree.
34
+ * @returns {boolean} The isRequesting flag.
35
+ */
36
+ getIsRequestingAiAssistantFeature( state: LogoGeneratorStateProp ): boolean {
37
+ return state.features.aiAssistantFeature?._meta?.isRequesting ?? false;
38
+ },
39
+
40
+ /**
41
+ * Get the logos history.
42
+ * @param {LogoGeneratorStateProp} state - The app state tree.
43
+ * @returns {Array<Logo>} The logos history array.
44
+ */
45
+ getLogos( state: LogoGeneratorStateProp ): Array< Logo > {
46
+ return state.history ?? [];
47
+ },
48
+
49
+ /**
50
+ * Get the selected logo index.
51
+ * @param {LogoGeneratorStateProp} state - The app state tree.
52
+ * @returns {number | null} The selected logo index.
53
+ */
54
+ getSelectedLogoIndex( state: LogoGeneratorStateProp ): number | null {
55
+ return state.selectedLogoIndex ?? null;
56
+ },
57
+
58
+ /**
59
+ * Get the selected logo.
60
+ * @param {LogoGeneratorStateProp} state - The app state tree.
61
+ * @returns {Logo} The selected logo.
62
+ */
63
+ getSelectedLogo( state: LogoGeneratorStateProp ): Logo {
64
+ return state.history?.[ state.selectedLogoIndex ] ?? null;
65
+ },
66
+
67
+ /**
68
+ * Get the isSavingToLibrary flag.
69
+ * @param {LogoGeneratorStateProp} state - The app state tree.
70
+ * @returns {boolean} The isSavingToLibrary flag.
71
+ */
72
+ getIsSavingLogoToLibrary( state: LogoGeneratorStateProp ): boolean {
73
+ return state._meta?.isSavingLogoToLibrary ?? false;
74
+ },
75
+
76
+ /**
77
+ * Get the isApplyingLogo flag.
78
+ * @param {LogoGeneratorStateProp} state - The app state tree.
79
+ * @returns {boolean} The isApplyingLogo flag.
80
+ */
81
+ getIsApplyingLogo( state: LogoGeneratorStateProp ): boolean {
82
+ return state._meta?.isApplyingLogo ?? false;
83
+ },
84
+
85
+ /**
86
+ * Get the isEnhancingPrompt flag.
87
+ * @param {LogoGeneratorStateProp} state - The app state tree.
88
+ * @returns {boolean} The isEnhancingPrompt flag.
89
+ */
90
+ getIsEnhancingPrompt( state: LogoGeneratorStateProp ): boolean {
91
+ return state._meta?.isEnhancingPrompt ?? false;
92
+ },
93
+
94
+ /**
95
+ * Get the isRequestingImage flag.
96
+ * @param {LogoGeneratorStateProp} state - The app state tree.
97
+ * @returns {boolean} The isRequestingImage flag.
98
+ */
99
+ getIsRequestingImage( state: LogoGeneratorStateProp ): boolean {
100
+ return state._meta?.isRequestingImage ?? false;
101
+ },
102
+
103
+ /**
104
+ * Get an aggregated isBusy flag, based on the loading states of the app.
105
+ * @param {LogoGeneratorStateProp} state - The app state tree.
106
+ * @returns {boolean} The isBusy flag.
107
+ */
108
+ getIsBusy( state: LogoGeneratorStateProp ): boolean {
109
+ return (
110
+ selectors.getIsApplyingLogo( state ) ||
111
+ selectors.getIsSavingLogoToLibrary( state ) ||
112
+ selectors.getIsRequestingImage( state ) ||
113
+ selectors.getIsEnhancingPrompt( state )
114
+ );
115
+ },
116
+
117
+ /**
118
+ * Get the requireUpgrade value from aiAssistantFeature
119
+ * @param {LogoGeneratorStateProp} state - The app state tree.
120
+ * @returns {boolean} The requireUpgrade flag.
121
+ */
122
+ getRequireUpgrade( state: LogoGeneratorStateProp ): boolean {
123
+ const feature = state.features.aiAssistantFeature;
124
+ const logoCost = feature?.costs?.[ 'jetpack-ai-logo-generator' ]?.logo ?? DEFAULT_LOGO_COST;
125
+ const currentLimit = feature?.currentTier?.value || 0;
126
+ const currentUsage = feature?.usagePeriod?.requestsCount || 0;
127
+ const isUnlimited = currentLimit === 1;
128
+ const hasNoNextTier = ! feature?.nextTier; // If there is no next tier, the user cannot upgrade.
129
+
130
+ // Add a local check on top of the feature flag, based on the current usage and logo cost.
131
+ return (
132
+ state.features.aiAssistantFeature?.requireUpgrade ||
133
+ ( ! isUnlimited && ! hasNoNextTier && currentLimit - currentUsage < logoCost )
134
+ );
135
+ },
136
+
137
+ /**
138
+ * Get the featureFetchError value.
139
+ * @param {LogoGeneratorStateProp} state - The app state tree.
140
+ * @returns {RequestError} The featureFetchError value.
141
+ */
142
+ getFeatureFetchError( state: LogoGeneratorStateProp ): RequestError {
143
+ return state._meta?.featureFetchError ?? null;
144
+ },
145
+
146
+ /**
147
+ * Get the firstLogoPromptFetchError value.
148
+ * @param {LogoGeneratorStateProp} state - The app state tree.
149
+ * @returns {RequestError} The firstLogoPromptFetchError value.
150
+ */
151
+ getFirstLogoPromptFetchError( state: LogoGeneratorStateProp ): RequestError {
152
+ return state._meta?.firstLogoPromptFetchError ?? null;
153
+ },
154
+
155
+ /**
156
+ * Get the enhancePromptFetchError value.
157
+ * @param {LogoGeneratorStateProp} state - The app state tree.
158
+ * @returns {RequestError} The enhancePromptFetchError value.
159
+ */
160
+ getEnhancePromptFetchError( state: LogoGeneratorStateProp ): RequestError {
161
+ return state._meta?.enhancePromptFetchError ?? null;
162
+ },
163
+
164
+ /**
165
+ * Get the logoFetchError value.
166
+ * @param {LogoGeneratorStateProp} state - The app state tree.
167
+ * @returns {RequestError} The logoFetchError value.
168
+ */
169
+ getLogoFetchError( state: LogoGeneratorStateProp ): RequestError {
170
+ return state._meta?.logoFetchError ?? null;
171
+ },
172
+
173
+ /**
174
+ * Get the saveToLibraryError value.
175
+ * @param {LogoGeneratorStateProp} state - The app state tree.
176
+ * @returns {RequestError} The saveToLibraryError value.
177
+ */
178
+ getSaveToLibraryError( state: LogoGeneratorStateProp ): RequestError {
179
+ return state._meta?.saveToLibraryError ?? null;
180
+ },
181
+
182
+ /**
183
+ * Get the logoUpdateError value.
184
+ * @param {LogoGeneratorStateProp} state - The app state tree.
185
+ * @returns {RequestError} The logoUpdateError value.
186
+ */
187
+ getLogoUpdateError( state: LogoGeneratorStateProp ): RequestError {
188
+ return state._meta?.logoUpdateError ?? null;
189
+ },
190
+
191
+ /**
192
+ * Get the context value.
193
+ * @param {LogoGeneratorStateProp} state - The app state tree.
194
+ * @returns {string} The context value.
195
+ */
196
+ getContext( state: LogoGeneratorStateProp ): string {
197
+ return state._meta?.context ?? '';
198
+ },
199
+ };
200
+
201
+ export default selectors;