@automattic/jetpack-ai-client 0.14.6 → 0.16.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 +17 -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 +177 -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 +100 -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 +9 -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 +25 -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 +84 -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 +280 -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 +219 -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 +54 -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 +30 -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 +99 -0
  128. package/src/types.ts +8 -0
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Types
3
+ */
4
+ import { Logo } from '../store/types.js';
5
+ import { RemoveFromStorageProps, SaveToStorageProps, UpdateInStorageProps } from '../types.js';
6
+ import { mediaExists } from './media-exists.js';
7
+
8
+ const MAX_LOGOS = 10;
9
+
10
+ /**
11
+ * Add an entry to the site's logo history.
12
+ *
13
+ * @param {SaveToStorageProps} saveToStorageProps - The properties to save to storage
14
+ * @param {SaveToStorageProps.siteId} saveToStorageProps.siteId - The site ID
15
+ * @param {SaveToStorageProps.url} saveToStorageProps.url - The URL of the logo
16
+ * @param {SaveToStorageProps.description} saveToStorageProps.description - The description of the logo, based on the prompt used to generate it
17
+ * @param {SaveToStorageProps.mediaId} saveToStorageProps.mediaId - The media ID of the logo on the backend
18
+ *
19
+ * @returns {Logo} The logo that was saved
20
+ */
21
+ export function stashLogo( { siteId, url, description, mediaId }: SaveToStorageProps ) {
22
+ const storedContent = getSiteLogoHistory( siteId );
23
+
24
+ const logo: Logo = {
25
+ url,
26
+ description,
27
+ mediaId,
28
+ };
29
+
30
+ storedContent.push( logo );
31
+
32
+ localStorage.setItem(
33
+ `logo-history-${ siteId }`,
34
+ JSON.stringify( storedContent.slice( -MAX_LOGOS ) )
35
+ );
36
+
37
+ return logo;
38
+ }
39
+
40
+ /**
41
+ * Update an entry in the site's logo history.
42
+ *
43
+ * @param {UpdateInStorageProps} updateInStorageProps - The properties to update in storage
44
+ * @param {UpdateInStorageProps.siteId} updateInStorageProps.siteId - The site ID
45
+ * @param {UpdateInStorageProps.url} updateInStorageProps.url - The URL of the logo to update
46
+ * @param {UpdateInStorageProps.newUrl} updateInStorageProps.newUrl - The new URL of the logo
47
+ * @param {UpdateInStorageProps.mediaId} updateInStorageProps.mediaId - The new media ID of the logo
48
+ * @returns {Logo} The logo that was updated
49
+ */
50
+ export function updateLogo( { siteId, url, newUrl, mediaId }: UpdateInStorageProps ) {
51
+ const storedContent = getSiteLogoHistory( siteId );
52
+
53
+ const index = storedContent.findIndex( logo => logo.url === url );
54
+
55
+ if ( index > -1 ) {
56
+ storedContent[ index ].url = newUrl;
57
+ storedContent[ index ].mediaId = mediaId;
58
+ }
59
+
60
+ localStorage.setItem(
61
+ `logo-history-${ siteId }`,
62
+ JSON.stringify( storedContent.slice( -MAX_LOGOS ) )
63
+ );
64
+
65
+ return storedContent[ index ];
66
+ }
67
+
68
+ /**
69
+ * Get the logo history for a site.
70
+ *
71
+ * @param {string} siteId - The site ID to get the logo history for
72
+ * @returns {Logo[]} The logo history for the site
73
+ */
74
+ export function getSiteLogoHistory( siteId: string ) {
75
+ const storedString = localStorage.getItem( `logo-history-${ siteId }` );
76
+ let storedContent: Logo[] = storedString ? JSON.parse( storedString ) : [];
77
+
78
+ // Ensure that the stored content is an array
79
+ if ( ! Array.isArray( storedContent ) ) {
80
+ storedContent = [];
81
+ }
82
+
83
+ // Ensure a maximum of 10 logos are stored
84
+ storedContent = storedContent.slice( -MAX_LOGOS );
85
+
86
+ // Ensure that the stored content is an array of Logo objects
87
+ storedContent = storedContent
88
+ .filter( logo => {
89
+ return (
90
+ typeof logo === 'object' &&
91
+ typeof logo.url === 'string' &&
92
+ typeof logo.description === 'string'
93
+ );
94
+ } )
95
+ .map( logo => ( {
96
+ url: logo.url,
97
+ description: logo.description,
98
+ mediaId: logo.mediaId,
99
+ } ) );
100
+
101
+ return storedContent;
102
+ }
103
+
104
+ /**
105
+ * Check if the logo history for a site is empty.
106
+ *
107
+ * @param {string }siteId - The site ID to check the logo history for
108
+ * @returns {boolean} Whether the logo history for the site is empty
109
+ */
110
+ export function isLogoHistoryEmpty( siteId: string ) {
111
+ const storedContent = getSiteLogoHistory( siteId );
112
+
113
+ return storedContent.length === 0;
114
+ }
115
+
116
+ /**
117
+ * Remove an entry from the site's logo history.
118
+ *
119
+ * @param {RemoveFromStorageProps} removeFromStorageProps - The properties to remove from storage
120
+ * @param {RemoveFromStorageProps.siteId} removeFromStorageProps.siteId - The site ID
121
+ * @param {RemoveFromStorageProps.mediaId} removeFromStorageProps.mediaId - The media ID of the logo to remove
122
+ * @returns {void}
123
+ */
124
+ export function removeLogo( { siteId, mediaId }: RemoveFromStorageProps ) {
125
+ const storedContent = getSiteLogoHistory( siteId );
126
+ const index = storedContent.findIndex( logo => logo.mediaId === mediaId );
127
+
128
+ if ( index === -1 ) {
129
+ return;
130
+ }
131
+
132
+ storedContent.splice( index, 1 );
133
+ localStorage.setItem( `logo-history-${ siteId }`, JSON.stringify( storedContent ) );
134
+ }
135
+
136
+ /**
137
+ * Clear deleted media from the site's logo history, checking if the media still exists on the backend.
138
+ *
139
+ * @param {string} siteId - The site ID to clear deleted media for
140
+ * @returns {Promise<void>}
141
+ */
142
+ export async function clearDeletedMedia( siteId: string ) {
143
+ const storedContent = getSiteLogoHistory( siteId );
144
+
145
+ const checks = storedContent
146
+ .filter( ( { mediaId } ) => mediaId !== undefined )
147
+ .map(
148
+ ( { mediaId } ) =>
149
+ new Promise( ( resolve, reject ) => {
150
+ mediaExists( { siteId, mediaId } )
151
+ .then( exists => resolve( { mediaId, exists } ) )
152
+ .catch( error => reject( error ) );
153
+ } )
154
+ );
155
+
156
+ try {
157
+ const responses = ( await Promise.all( checks ) ) as {
158
+ mediaId: Logo[ 'mediaId' ];
159
+ exists: boolean;
160
+ }[];
161
+
162
+ responses
163
+ .filter( ( { exists } ) => ! exists )
164
+ .forEach( ( { mediaId } ) => removeLogo( { siteId, mediaId } ) );
165
+ } catch ( error ) {} // Assume that the media exists if there was a network error and do nothing to avoid data loss.
166
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import apiFetch from '../../api-fetch/index.js';
5
+ /**
6
+ * Types
7
+ */
8
+ import type { CheckMediaProps } from '../types.js';
9
+
10
+ /**
11
+ * Uses the media information to confirm it exists or not on the server.
12
+ *
13
+ * @param {CheckMediaProps} checkMediaProps - the media details to check
14
+ * @param {CheckMediaProps.mediaId} checkMediaProps.mediaId - the id of the media to check
15
+ * @returns {Promise<boolean>} - true if the media exists, false otherwise
16
+ */
17
+ export async function mediaExists( { mediaId }: CheckMediaProps ): Promise< boolean > {
18
+ const id = Number( mediaId );
19
+
20
+ if ( Number.isNaN( id ) ) {
21
+ return false;
22
+ }
23
+
24
+ try {
25
+ // Using apiFetch directly here because we don't want to limit the number of concurrent media checks
26
+ // We store at most 10 logos in the local storage, so the number of concurrent requests should be limited
27
+ await apiFetch( {
28
+ path: `/wp/v2/media/${ Number( mediaId ) }`,
29
+ method: 'GET',
30
+ } );
31
+
32
+ return true;
33
+ } catch ( error ) {
34
+ const status = ( error as { data?: { status?: number } } )?.data?.status;
35
+
36
+ if ( status === 404 ) {
37
+ return false;
38
+ }
39
+
40
+ throw error;
41
+ }
42
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import wpcomLimitedRequest from './wpcom-limited-request.js';
5
+ /**
6
+ * Types
7
+ */
8
+ import type { SetSiteLogoProps, SetSiteLogoResponseProps } from '../types.js';
9
+
10
+ /**
11
+ * Set the site logo using a backend request.
12
+ *
13
+ * @param {SetSiteLogoProps} setSiteLogoProps - The properties to set the site logo
14
+ * @param {SetSiteLogoProps.siteId} setSiteLogoProps.siteId - The site ID
15
+ * @param {SetSiteLogoProps.imageId} setSiteLogoProps.imageId - The image ID to set as the site logo
16
+ * @returns {Promise<SetSiteLogoResponseProps>} The response from the request
17
+ */
18
+ export async function setSiteLogo( { siteId, imageId }: SetSiteLogoProps ) {
19
+ const body = {
20
+ site_logo: imageId,
21
+ site_icon: imageId,
22
+ };
23
+
24
+ return wpcomLimitedRequest< SetSiteLogoResponseProps >( {
25
+ path: `/sites/${ String( siteId ) }/settings`,
26
+ apiVersion: 'v2',
27
+ apiNamespace: 'wp/v2',
28
+ body,
29
+ query: 'source=jetpack-ai',
30
+ method: 'POST',
31
+ } );
32
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import apiFetch from '../../api-fetch/index.js';
5
+ /**
6
+ * Types
7
+ */
8
+
9
+ const MAX_CONCURRENT_REQUESTS = 5;
10
+
11
+ let concurrentCounter = 0;
12
+
13
+ /**
14
+ * Concurrency-limited request to wpcom-proxy-request.
15
+ * @param { object } params - The request params, as expected by apiFetch.
16
+ * @returns { Promise } The response.
17
+ * @throws { Error } If there are too many concurrent requests.
18
+ */
19
+ export default async function wpcomLimitedRequest< T >( params: object ): Promise< T > {
20
+ concurrentCounter += 1;
21
+
22
+ if ( concurrentCounter > MAX_CONCURRENT_REQUESTS ) {
23
+ concurrentCounter -= 1;
24
+ throw new Error( 'Too many requests' );
25
+ }
26
+
27
+ return apiFetch< T >( params ).finally( () => {
28
+ concurrentCounter -= 1;
29
+ } );
30
+ }
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { getSiteLogoHistory } from '../lib/logo-storage.js';
5
+ import wpcomLimitedRequest from '../lib/wpcom-limited-request.js';
6
+ /**
7
+ * Types & Constants
8
+ */
9
+ import {
10
+ ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT,
11
+ ACTION_REQUEST_AI_ASSISTANT_FEATURE,
12
+ ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE,
13
+ ACTION_SET_SITE_DETAILS,
14
+ ACTION_STORE_AI_ASSISTANT_FEATURE,
15
+ ACTION_SET_TIER_PLANS_ENABLED,
16
+ ACTION_SET_SELECTED_LOGO_INDEX,
17
+ ACTION_ADD_LOGO_TO_HISTORY,
18
+ ACTION_SET_IS_SAVING_LOGO_TO_LIBRARY,
19
+ ACTION_SAVE_SELECTED_LOGO,
20
+ ACTION_SET_IS_REQUESTING_IMAGE,
21
+ ACTION_SET_IS_APPLYING_LOGO,
22
+ ACTION_SET_IS_ENHANCING_PROMPT,
23
+ ACTION_SET_SITE_HISTORY,
24
+ ACTION_SET_FEATURE_FETCH_ERROR,
25
+ ACTION_SET_FIRST_LOGO_PROMPT_FETCH_ERROR,
26
+ ACTION_SET_ENHANCE_PROMPT_FETCH_ERROR,
27
+ ACTION_SET_LOGO_FETCH_ERROR,
28
+ ACTION_SET_LOGO_UPDATE_ERROR,
29
+ ACTION_SET_SAVE_TO_LIBRARY_ERROR,
30
+ ACTION_SET_CONTEXT,
31
+ } from './constants.js';
32
+ import type {
33
+ AiFeatureProps,
34
+ AiAssistantFeatureEndpointResponseProps,
35
+ Logo,
36
+ RequestError,
37
+ } from './types.js';
38
+ import type { SiteDetails } from '../types.js';
39
+
40
+ /**
41
+ * Map the response from the `sites/$site/ai-assistant-feature`
42
+ * endpoint to the AI Assistant feature props.
43
+ * @param { AiAssistantFeatureEndpointResponseProps } response - The response from the endpoint.
44
+ * @returns { AiFeatureProps } The AI Assistant feature props.
45
+ */
46
+ export function mapAiFeatureResponseToAiFeatureProps(
47
+ response: AiAssistantFeatureEndpointResponseProps
48
+ ): AiFeatureProps {
49
+ return {
50
+ hasFeature: !! response[ 'has-feature' ],
51
+ isOverLimit: !! response[ 'is-over-limit' ],
52
+ requestsCount: response[ 'requests-count' ],
53
+ requestsLimit: response[ 'requests-limit' ],
54
+ requireUpgrade: !! response[ 'site-require-upgrade' ],
55
+ errorMessage: response[ 'error-message' ],
56
+ errorCode: response[ 'error-code' ],
57
+ upgradeType: response[ 'upgrade-type' ],
58
+ usagePeriod: {
59
+ currentStart: response[ 'usage-period' ]?.[ 'current-start' ],
60
+ nextStart: response[ 'usage-period' ]?.[ 'next-start' ],
61
+ requestsCount: response[ 'usage-period' ]?.[ 'requests-count' ] || 0,
62
+ },
63
+ currentTier: response[ 'current-tier' ],
64
+ nextTier: response[ 'next-tier' ],
65
+ tierPlansEnabled: !! response[ 'tier-plans-enabled' ],
66
+ costs: response.costs,
67
+ };
68
+ }
69
+
70
+ const actions = {
71
+ storeAiAssistantFeature( feature: AiFeatureProps ) {
72
+ return {
73
+ type: ACTION_STORE_AI_ASSISTANT_FEATURE,
74
+ feature,
75
+ };
76
+ },
77
+
78
+ /**
79
+ * Thunk action to fetch the AI Assistant feature from the API.
80
+ * @returns {Function} The thunk action.
81
+ */
82
+ fetchAiAssistantFeature() {
83
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
+ return async ( { dispatch }: { dispatch: any } ) => {
85
+ // Dispatch isFetching action.
86
+ dispatch( { type: ACTION_REQUEST_AI_ASSISTANT_FEATURE } );
87
+
88
+ try {
89
+ const response: AiAssistantFeatureEndpointResponseProps = await wpcomLimitedRequest( {
90
+ path: '/wpcom/v2/jetpack-ai/ai-assistant-feature',
91
+ query: 'force=wpcom',
92
+ } );
93
+
94
+ // Store the feature in the store.
95
+ dispatch(
96
+ actions.storeAiAssistantFeature( mapAiFeatureResponseToAiFeatureProps( response ) )
97
+ );
98
+ } catch ( err ) {
99
+ // Mark the fetch as failed.
100
+ dispatch( { type: ACTION_SET_FEATURE_FETCH_ERROR, error: err } );
101
+ }
102
+ };
103
+ },
104
+
105
+ /**
106
+ * This thunk action is used to increase
107
+ * the requests count for the current usage period.
108
+ * @param {number} count - The number of requests to increase. Default is 1.
109
+ * @returns {Function} The thunk action.
110
+ */
111
+ increaseAiAssistantRequestsCount( count: number = 1 ) {
112
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
113
+ return ( { dispatch }: { dispatch: any } ) => {
114
+ dispatch( {
115
+ type: ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT,
116
+ count,
117
+ } );
118
+ };
119
+ },
120
+
121
+ setAiAssistantFeatureRequireUpgrade( requireUpgrade: boolean = true ) {
122
+ return {
123
+ type: ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE,
124
+ requireUpgrade,
125
+ };
126
+ },
127
+
128
+ setTierPlansEnabled( tierPlansEnabled: boolean = true ) {
129
+ return {
130
+ type: ACTION_SET_TIER_PLANS_ENABLED,
131
+ tierPlansEnabled,
132
+ };
133
+ },
134
+
135
+ setSiteDetails( siteDetails: SiteDetails ) {
136
+ return {
137
+ type: ACTION_SET_SITE_DETAILS,
138
+ siteDetails,
139
+ };
140
+ },
141
+
142
+ setSelectedLogoIndex( selectedLogoIndex: number ) {
143
+ return {
144
+ type: ACTION_SET_SELECTED_LOGO_INDEX,
145
+ selectedLogoIndex,
146
+ };
147
+ },
148
+
149
+ addLogoToHistory( logo: Logo ) {
150
+ return {
151
+ type: ACTION_ADD_LOGO_TO_HISTORY,
152
+ logo,
153
+ };
154
+ },
155
+
156
+ setIsSavingLogoToLibrary( isSavingLogoToLibrary: boolean ) {
157
+ return {
158
+ type: ACTION_SET_IS_SAVING_LOGO_TO_LIBRARY,
159
+ isSavingLogoToLibrary,
160
+ };
161
+ },
162
+
163
+ setIsApplyingLogo( isApplyingLogo: boolean ) {
164
+ return {
165
+ type: ACTION_SET_IS_APPLYING_LOGO,
166
+ isApplyingLogo,
167
+ };
168
+ },
169
+
170
+ updateSelectedLogo( mediaId: string, url: string ) {
171
+ return {
172
+ type: ACTION_SAVE_SELECTED_LOGO,
173
+ mediaId,
174
+ url,
175
+ };
176
+ },
177
+
178
+ setIsRequestingImage( isRequestingImage: boolean ) {
179
+ return {
180
+ type: ACTION_SET_IS_REQUESTING_IMAGE,
181
+ isRequestingImage,
182
+ };
183
+ },
184
+
185
+ setIsEnhancingPrompt( isEnhancingPrompt: boolean ) {
186
+ return {
187
+ type: ACTION_SET_IS_ENHANCING_PROMPT,
188
+ isEnhancingPrompt,
189
+ };
190
+ },
191
+
192
+ loadLogoHistory( siteId: string ) {
193
+ const history = getSiteLogoHistory( siteId );
194
+
195
+ return {
196
+ type: ACTION_SET_SITE_HISTORY,
197
+ history,
198
+ };
199
+ },
200
+
201
+ setFeatureFetchError( error: RequestError ) {
202
+ return {
203
+ type: ACTION_SET_FEATURE_FETCH_ERROR,
204
+ error,
205
+ };
206
+ },
207
+
208
+ setFirstLogoPromptFetchError( error: RequestError ) {
209
+ return {
210
+ type: ACTION_SET_FIRST_LOGO_PROMPT_FETCH_ERROR,
211
+ error,
212
+ };
213
+ },
214
+
215
+ setEnhancePromptFetchError( error: RequestError ) {
216
+ return {
217
+ type: ACTION_SET_ENHANCE_PROMPT_FETCH_ERROR,
218
+ error,
219
+ };
220
+ },
221
+
222
+ setLogoFetchError( error: RequestError ) {
223
+ return {
224
+ type: ACTION_SET_LOGO_FETCH_ERROR,
225
+ error,
226
+ };
227
+ },
228
+
229
+ setSaveToLibraryError( error: RequestError ) {
230
+ return {
231
+ type: ACTION_SET_SAVE_TO_LIBRARY_ERROR,
232
+ error,
233
+ };
234
+ },
235
+
236
+ setLogoUpdateError( error: RequestError ) {
237
+ return {
238
+ type: ACTION_SET_LOGO_UPDATE_ERROR,
239
+ error,
240
+ };
241
+ },
242
+
243
+ setContext( context: string ) {
244
+ return {
245
+ type: ACTION_SET_CONTEXT,
246
+ context,
247
+ };
248
+ },
249
+ };
250
+
251
+ export default actions;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * AI Assistant feature actions
3
+ */
4
+ export const ACTION_STORE_AI_ASSISTANT_FEATURE = 'STORE_AI_ASSISTANT_FEATURE';
5
+ export const ACTION_REQUEST_AI_ASSISTANT_FEATURE = 'REQUEST_AI_ASSISTANT_FEATURE';
6
+ export const ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT = 'INCREASE_AI_ASSISTANT_REQUESTS_COUNT';
7
+ export const ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE =
8
+ 'SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE';
9
+ export const ACTION_SET_TIER_PLANS_ENABLED = 'SET_TIER_PLANS_ENABLED';
10
+ export const ACTION_SET_SITE_DETAILS = 'SET_SITE_DETAILS';
11
+
12
+ /**
13
+ * Endpoints
14
+ */
15
+ export const ENDPOINT_AI_ASSISTANT_FEATURE = '/wpcom/v2/jetpack-ai/ai-assistant-feature';
16
+
17
+ /**
18
+ * New AI Assistant feature async request
19
+ */
20
+ export const FREE_PLAN_REQUESTS_LIMIT = 20;
21
+ export const UNLIMITED_PLAN_REQUESTS_LIMIT = 999999999;
22
+ export const ASYNC_REQUEST_COUNTDOWN_INIT_VALUE = 3;
23
+ export const NEW_ASYNC_REQUEST_TIMER_INTERVAL = 5000;
24
+ export const ACTION_DECREASE_NEW_ASYNC_REQUEST_COUNTDOWN = 'DECREASE_NEW_ASYNC_REQUEST_COUNTDOWN';
25
+ export const ACTION_ENQUEUE_ASYNC_REQUEST = 'ENQUEUE_ASYNC_COUNTDOWN_REQUEST';
26
+ export const ACTION_DEQUEUE_ASYNC_REQUEST = 'DEQUEUE_ASYNC_COUNTDOWN_REQUEST';
27
+
28
+ /**
29
+ * Logo generator actions
30
+ */
31
+ export const ACTION_SET_CONTEXT = 'SET_CONTEXT';
32
+ export const ACTION_SET_SELECTED_LOGO_INDEX = 'SET_SELECTED_LOGO_INDEX';
33
+ export const ACTION_ADD_LOGO_TO_HISTORY = 'ADD_LOGO_TO_HISTORY';
34
+ export const ACTION_SET_IS_SAVING_LOGO_TO_LIBRARY = 'SET_IS_SAVING_LOGO_TO_LIBRARY';
35
+ export const ACTION_SET_IS_APPLYING_LOGO = 'SET_IS_APPLYING_LOGO';
36
+ export const ACTION_SAVE_SELECTED_LOGO = 'SAVE_SELECTED_LOGO';
37
+ export const ACTION_SET_IS_REQUESTING_IMAGE = 'SET_IS_REQUESTING_IMAGE';
38
+ export const ACTION_SET_IS_ENHANCING_PROMPT = 'SET_IS_ENHANCING_PROMPT';
39
+ export const ACTION_SET_SITE_HISTORY = 'SET_SITE_HISTORY';
40
+
41
+ /**
42
+ * Logo generator error actions
43
+ */
44
+ export const ACTION_SET_FEATURE_FETCH_ERROR = 'SET_FEATURE_FETCH_ERROR';
45
+ export const ACTION_SET_FIRST_LOGO_PROMPT_FETCH_ERROR = 'SET_FIRST_LOGO_PROMPT_FETCH_ERROR';
46
+ export const ACTION_SET_ENHANCE_PROMPT_FETCH_ERROR = 'SET_ENHANCE_PROMPT_FETCH_ERROR';
47
+ export const ACTION_SET_LOGO_FETCH_ERROR = 'SET_LOGO_FETCH_ERROR';
48
+ export const ACTION_SET_SAVE_TO_LIBRARY_ERROR = 'SET_SAVE_TO_LIBRARY_ERROR';
49
+ export const ACTION_SET_LOGO_UPDATE_ERROR = 'SET_LOGO_UPDATE_ERROR';
@@ -0,0 +1,25 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { createReduxStore, register } from '@wordpress/data';
5
+ /**
6
+ * Internal dependencies
7
+ */
8
+ import actions from './actions.js';
9
+ import reducer from './reducer.js';
10
+ import selectors from './selectors.js';
11
+
12
+ export const STORE_NAME = 'jetpack-ai/logo-generator';
13
+
14
+ const jetpackAiLogoGeneratorStore = createReduxStore( STORE_NAME, {
15
+ // @ts-expect-error -- TSCONVERSION
16
+ __experimentalUseThunks: true,
17
+
18
+ actions,
19
+
20
+ reducer,
21
+
22
+ selectors,
23
+ } );
24
+
25
+ register( jetpackAiLogoGeneratorStore );
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Types & Constants
3
+ */
4
+ import { ASYNC_REQUEST_COUNTDOWN_INIT_VALUE, FREE_PLAN_REQUESTS_LIMIT } from './constants.js';
5
+ import { LogoGeneratorStateProp } from './types.js';
6
+
7
+ const INITIAL_STATE: LogoGeneratorStateProp = {
8
+ siteDetails: {},
9
+ features: {
10
+ aiAssistantFeature: {
11
+ hasFeature: true,
12
+ isOverLimit: false,
13
+ requestsCount: 0,
14
+ requestsLimit: FREE_PLAN_REQUESTS_LIMIT,
15
+ requireUpgrade: false,
16
+ errorMessage: '',
17
+ errorCode: '',
18
+ upgradeType: 'default',
19
+ currentTier: {
20
+ slug: 'ai-assistant-tier-free',
21
+ value: 0,
22
+ limit: 20,
23
+ },
24
+ usagePeriod: {
25
+ currentStart: '',
26
+ nextStart: '',
27
+ requestsCount: 0,
28
+ },
29
+ nextTier: null,
30
+ tierPlansEnabled: false,
31
+ _meta: {
32
+ isRequesting: false,
33
+ asyncRequestCountdown: ASYNC_REQUEST_COUNTDOWN_INIT_VALUE,
34
+ asyncRequestTimerId: 0,
35
+ isRequestingImage: false,
36
+ },
37
+ },
38
+ },
39
+ history: [],
40
+ selectedLogoIndex: 0,
41
+ };
42
+
43
+ export default INITIAL_STATE;