@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.
- package/CHANGELOG.md +8 -0
- package/build/ask-question/sync.d.ts +2 -8
- package/build/ask-question/sync.js +20 -19
- package/build/hooks/use-image-generator/index.js +1 -1
- package/build/hooks/use-save-to-media-library/index.d.ts +12 -0
- package/build/hooks/use-save-to-media-library/index.js +74 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +5 -0
- package/build/libs/index.d.ts +1 -1
- package/build/libs/index.js +1 -1
- package/build/libs/markdown/index.d.ts +2 -2
- package/build/libs/markdown/index.js +2 -2
- package/build/libs/markdown/markdown-to-html.d.ts +8 -1
- package/build/libs/markdown/markdown-to-html.js +10 -1
- package/build/logo-generator/assets/icons/ai.d.ts +6 -0
- package/build/logo-generator/assets/icons/ai.js +8 -0
- package/build/logo-generator/assets/icons/check.d.ts +6 -0
- package/build/logo-generator/assets/icons/check.js +8 -0
- package/build/logo-generator/assets/icons/logo.d.ts +6 -0
- package/build/logo-generator/assets/icons/logo.js +8 -0
- package/build/logo-generator/assets/icons/media.d.ts +6 -0
- package/build/logo-generator/assets/icons/media.js +8 -0
- package/build/logo-generator/components/feature-fetch-failure-screen.d.ts +8 -0
- package/build/logo-generator/components/feature-fetch-failure-screen.js +10 -0
- package/build/logo-generator/components/first-load-screen.d.ts +5 -0
- package/build/logo-generator/components/first-load-screen.js +16 -0
- package/build/logo-generator/components/generator-modal.d.ts +7 -0
- package/build/logo-generator/components/generator-modal.js +184 -0
- package/build/logo-generator/components/history-carousel.d.ts +6 -0
- package/build/logo-generator/components/history-carousel.js +36 -0
- package/build/logo-generator/components/image-loader.d.ts +7 -0
- package/build/logo-generator/components/image-loader.js +12 -0
- package/build/logo-generator/components/logo-presenter.d.ts +4 -0
- package/build/logo-generator/components/logo-presenter.js +106 -0
- package/build/logo-generator/components/prompt.d.ts +5 -0
- package/build/logo-generator/components/prompt.js +96 -0
- package/build/logo-generator/components/upgrade-nudge.d.ts +2 -0
- package/build/logo-generator/components/upgrade-nudge.js +30 -0
- package/build/logo-generator/components/upgrade-screen.d.ts +9 -0
- package/build/logo-generator/components/upgrade-screen.js +24 -0
- package/build/logo-generator/components/visit-site-banner.d.ts +10 -0
- package/build/logo-generator/components/visit-site-banner.js +16 -0
- package/build/logo-generator/constants.d.ts +16 -0
- package/build/logo-generator/constants.js +19 -0
- package/build/logo-generator/hooks/use-checkout.d.ts +4 -0
- package/build/logo-generator/hooks/use-checkout.js +26 -0
- package/build/logo-generator/hooks/use-logo-generator.d.ts +46 -0
- package/build/logo-generator/hooks/use-logo-generator.js +286 -0
- package/build/logo-generator/hooks/use-request-errors.d.ts +16 -0
- package/build/logo-generator/hooks/use-request-errors.js +46 -0
- package/build/logo-generator/index.d.ts +1 -0
- package/build/logo-generator/index.js +1 -0
- package/build/logo-generator/lib/logo-storage.d.ts +58 -0
- package/build/logo-generator/lib/logo-storage.js +123 -0
- package/build/logo-generator/lib/media-exists.d.ts +12 -0
- package/build/logo-generator/lib/media-exists.js +33 -0
- package/build/logo-generator/lib/set-site-logo.d.ts +13 -0
- package/build/logo-generator/lib/set-site-logo.js +26 -0
- package/build/logo-generator/lib/wpcom-limited-request.d.ts +7 -0
- package/build/logo-generator/lib/wpcom-limited-request.js +33 -0
- package/build/logo-generator/store/actions.d.ts +105 -0
- package/build/logo-generator/store/actions.js +193 -0
- package/build/logo-generator/store/constants.d.ts +44 -0
- package/build/logo-generator/store/constants.js +44 -0
- package/build/logo-generator/store/index.d.ts +1 -0
- package/build/logo-generator/store/index.js +19 -0
- package/build/logo-generator/store/initial-state.d.ts +3 -0
- package/build/logo-generator/store/initial-state.js +40 -0
- package/build/logo-generator/store/reducer.d.ts +347 -0
- package/build/logo-generator/store/reducer.js +293 -0
- package/build/logo-generator/store/selectors.d.ts +119 -0
- package/build/logo-generator/store/selectors.js +173 -0
- package/build/logo-generator/store/types.d.ts +164 -0
- package/build/logo-generator/store/types.js +1 -0
- package/build/logo-generator/types.d.ts +82 -0
- package/build/logo-generator/types.js +1 -0
- package/build/types.d.ts +6 -0
- package/package.json +5 -3
- package/src/ask-question/sync.ts +22 -27
- package/src/hooks/use-image-generator/index.ts +1 -1
- package/src/hooks/use-save-to-media-library/index.ts +95 -0
- package/src/index.ts +6 -0
- package/src/libs/index.ts +1 -0
- package/src/libs/markdown/index.ts +2 -2
- package/src/libs/markdown/markdown-to-html.ts +20 -3
- package/src/logo-generator/assets/icons/ai.tsx +21 -0
- package/src/logo-generator/assets/icons/check.tsx +23 -0
- package/src/logo-generator/assets/icons/icons.scss +5 -0
- package/src/logo-generator/assets/icons/logo.tsx +23 -0
- package/src/logo-generator/assets/icons/media.tsx +24 -0
- package/src/logo-generator/assets/images/jetpack-logo.svg +4 -0
- package/src/logo-generator/assets/images/loader.gif +0 -0
- package/src/logo-generator/assets/index.d.ts +3 -0
- package/src/logo-generator/components/feature-fetch-failure-screen.tsx +35 -0
- package/src/logo-generator/components/first-load-screen.scss +12 -0
- package/src/logo-generator/components/first-load-screen.tsx +32 -0
- package/src/logo-generator/components/generator-modal.scss +92 -0
- package/src/logo-generator/components/generator-modal.tsx +291 -0
- package/src/logo-generator/components/history-carousel.scss +36 -0
- package/src/logo-generator/components/history-carousel.tsx +57 -0
- package/src/logo-generator/components/image-loader.tsx +22 -0
- package/src/logo-generator/components/logo-presenter.scss +116 -0
- package/src/logo-generator/components/logo-presenter.tsx +234 -0
- package/src/logo-generator/components/prompt.scss +102 -0
- package/src/logo-generator/components/prompt.tsx +211 -0
- package/src/logo-generator/components/upgrade-nudge.scss +43 -0
- package/src/logo-generator/components/upgrade-nudge.tsx +58 -0
- package/src/logo-generator/components/upgrade-screen.tsx +67 -0
- package/src/logo-generator/components/visit-site-banner.scss +29 -0
- package/src/logo-generator/components/visit-site-banner.tsx +50 -0
- package/src/logo-generator/constants.ts +22 -0
- package/src/logo-generator/hooks/use-checkout.ts +37 -0
- package/src/logo-generator/hooks/use-logo-generator.ts +389 -0
- package/src/logo-generator/hooks/use-request-errors.ts +70 -0
- package/src/logo-generator/index.ts +1 -0
- package/src/logo-generator/lib/logo-storage.ts +166 -0
- package/src/logo-generator/lib/media-exists.ts +42 -0
- package/src/logo-generator/lib/set-site-logo.ts +32 -0
- package/src/logo-generator/lib/wpcom-limited-request.ts +41 -0
- package/src/logo-generator/store/actions.ts +251 -0
- package/src/logo-generator/store/constants.ts +49 -0
- package/src/logo-generator/store/index.ts +25 -0
- package/src/logo-generator/store/initial-state.ts +43 -0
- package/src/logo-generator/store/reducer.ts +387 -0
- package/src/logo-generator/store/selectors.ts +201 -0
- package/src/logo-generator/store/types.ts +207 -0
- package/src/logo-generator/types.ts +97 -0
- package/src/types.ts +8 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useAnalytics } from '@automattic/jetpack-shared-extension-utils';
|
|
5
|
+
import { Modal, Button } from '@wordpress/components';
|
|
6
|
+
import { useDispatch } from '@wordpress/data';
|
|
7
|
+
import { __ } from '@wordpress/i18n';
|
|
8
|
+
import { external, Icon } from '@wordpress/icons';
|
|
9
|
+
import clsx from 'clsx';
|
|
10
|
+
import debugFactory from 'debug';
|
|
11
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
12
|
+
/**
|
|
13
|
+
* Internal dependencies
|
|
14
|
+
*/
|
|
15
|
+
import {
|
|
16
|
+
DEFAULT_LOGO_COST,
|
|
17
|
+
EVENT_MODAL_OPEN,
|
|
18
|
+
EVENT_FEEDBACK,
|
|
19
|
+
EVENT_MODAL_CLOSE,
|
|
20
|
+
EVENT_PLACEMENT_QUICK_LINKS,
|
|
21
|
+
EVENT_GENERATE,
|
|
22
|
+
} from '../constants.js';
|
|
23
|
+
import useLogoGenerator from '../hooks/use-logo-generator.js';
|
|
24
|
+
import useRequestErrors from '../hooks/use-request-errors.js';
|
|
25
|
+
import { isLogoHistoryEmpty, clearDeletedMedia } from '../lib/logo-storage.js';
|
|
26
|
+
import { STORE_NAME } from '../store/index.js';
|
|
27
|
+
import { FeatureFetchFailureScreen } from './feature-fetch-failure-screen.js';
|
|
28
|
+
import { FirstLoadScreen } from './first-load-screen.js';
|
|
29
|
+
import { HistoryCarousel } from './history-carousel.js';
|
|
30
|
+
import { LogoPresenter } from './logo-presenter.js';
|
|
31
|
+
import { Prompt } from './prompt.js';
|
|
32
|
+
import { UpgradeScreen } from './upgrade-screen.js';
|
|
33
|
+
import { VisitSiteBanner } from './visit-site-banner.js';
|
|
34
|
+
import './generator-modal.scss';
|
|
35
|
+
/**
|
|
36
|
+
* Types
|
|
37
|
+
*/
|
|
38
|
+
import type { GeneratorModalProps } from '../types.js';
|
|
39
|
+
import type React from 'react';
|
|
40
|
+
|
|
41
|
+
const debug = debugFactory( 'jetpack-ai-calypso:generator-modal' );
|
|
42
|
+
|
|
43
|
+
export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
|
|
44
|
+
isOpen,
|
|
45
|
+
onClose,
|
|
46
|
+
siteDetails,
|
|
47
|
+
context,
|
|
48
|
+
} ) => {
|
|
49
|
+
const { tracks } = useAnalytics();
|
|
50
|
+
const { recordEvent: recordTracksEvent } = tracks;
|
|
51
|
+
const { setSiteDetails, fetchAiAssistantFeature, loadLogoHistory } = useDispatch( STORE_NAME );
|
|
52
|
+
const [ loadingState, setLoadingState ] = useState<
|
|
53
|
+
'loadingFeature' | 'analyzing' | 'generating' | null
|
|
54
|
+
>( null );
|
|
55
|
+
const [ initialPrompt, setInitialPrompt ] = useState< string | undefined >();
|
|
56
|
+
const needsToHandleModalOpen = useRef< boolean >( true );
|
|
57
|
+
const requestedFeatureData = useRef< boolean >( false );
|
|
58
|
+
const [ needsFeature, setNeedsFeature ] = useState( false );
|
|
59
|
+
const [ needsMoreRequests, setNeedsMoreRequests ] = useState( false );
|
|
60
|
+
const [ upgradeURL, setUpgradeURL ] = useState( '' );
|
|
61
|
+
const { selectedLogo, getAiAssistantFeature, generateFirstPrompt, generateLogo, setContext } =
|
|
62
|
+
useLogoGenerator();
|
|
63
|
+
const { featureFetchError, firstLogoPromptFetchError, clearErrors } = useRequestErrors();
|
|
64
|
+
const siteId = siteDetails?.ID;
|
|
65
|
+
const siteURL = siteDetails?.URL;
|
|
66
|
+
const [ logoAccepted, setLogoAccepted ] = useState( false );
|
|
67
|
+
|
|
68
|
+
// First fetch the feature data so we have the most up-to-date info from the backend.
|
|
69
|
+
const feature = getAiAssistantFeature();
|
|
70
|
+
|
|
71
|
+
const generateFirstLogo = useCallback( async () => {
|
|
72
|
+
try {
|
|
73
|
+
// First generate the prompt based on the site's data.
|
|
74
|
+
setLoadingState( 'analyzing' );
|
|
75
|
+
recordTracksEvent( EVENT_GENERATE, { context, tool: 'first-prompt' } );
|
|
76
|
+
const prompt = await generateFirstPrompt();
|
|
77
|
+
setInitialPrompt( prompt );
|
|
78
|
+
|
|
79
|
+
// Then generate the logo based on the prompt.
|
|
80
|
+
setLoadingState( 'generating' );
|
|
81
|
+
await generateLogo( { prompt } );
|
|
82
|
+
setLoadingState( null );
|
|
83
|
+
} catch ( error ) {
|
|
84
|
+
debug( 'Error generating first logo', error );
|
|
85
|
+
setLoadingState( null );
|
|
86
|
+
}
|
|
87
|
+
}, [ context, generateFirstPrompt, generateLogo ] );
|
|
88
|
+
|
|
89
|
+
/*
|
|
90
|
+
* Called ONCE to check the feature data to make sure the site is allowed to do the generation.
|
|
91
|
+
* Also, checks site history and trigger a new generation in case there are no logos to present.
|
|
92
|
+
*/
|
|
93
|
+
const initializeModal = useCallback( async () => {
|
|
94
|
+
try {
|
|
95
|
+
const hasHistory = ! isLogoHistoryEmpty( String( siteId ) );
|
|
96
|
+
const logoCost = feature?.costs?.[ 'jetpack-ai-logo-generator' ]?.logo ?? DEFAULT_LOGO_COST;
|
|
97
|
+
const promptCreationCost = 1;
|
|
98
|
+
const currentLimit = feature?.currentTier?.value || 0;
|
|
99
|
+
const currentUsage = feature?.usagePeriod?.requestsCount || 0;
|
|
100
|
+
const isUnlimited = currentLimit === 1;
|
|
101
|
+
const hasNoNextTier = ! feature?.nextTier; // If there is no next tier, the user cannot upgrade.
|
|
102
|
+
|
|
103
|
+
// The user needs an upgrade immediately if they have no logos and not enough requests remaining for one prompt and one logo generation.
|
|
104
|
+
const siteNeedsMoreRequests =
|
|
105
|
+
! isUnlimited &&
|
|
106
|
+
! hasNoNextTier &&
|
|
107
|
+
! hasHistory &&
|
|
108
|
+
currentLimit - currentUsage < logoCost + promptCreationCost;
|
|
109
|
+
|
|
110
|
+
// If the site requires an upgrade, set the upgrade URL and show the upgrade screen immediately.
|
|
111
|
+
setNeedsFeature( ! feature?.hasFeature ?? true );
|
|
112
|
+
setNeedsMoreRequests( siteNeedsMoreRequests );
|
|
113
|
+
|
|
114
|
+
if ( ! feature?.hasFeature || siteNeedsMoreRequests ) {
|
|
115
|
+
const siteUpgradeURL = new URL(
|
|
116
|
+
`${ location.origin }/checkout/${ siteDetails?.domain }/${ feature?.nextTier?.slug }`
|
|
117
|
+
);
|
|
118
|
+
siteUpgradeURL.searchParams.set( 'redirect_to', location.href );
|
|
119
|
+
setUpgradeURL( siteUpgradeURL.toString() );
|
|
120
|
+
setLoadingState( null );
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Load the logo history and clear any deleted media.
|
|
125
|
+
await clearDeletedMedia( String( siteId ) );
|
|
126
|
+
loadLogoHistory( siteId );
|
|
127
|
+
|
|
128
|
+
// If there is any logo, we do not need to generate a first logo again.
|
|
129
|
+
if ( ! isLogoHistoryEmpty( String( siteId ) ) ) {
|
|
130
|
+
setLoadingState( null );
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// If the site does not require an upgrade and has no logos stored, generate the first prompt based on the site's data.
|
|
135
|
+
generateFirstLogo();
|
|
136
|
+
} catch ( error ) {
|
|
137
|
+
debug( 'Error fetching feature', error );
|
|
138
|
+
setLoadingState( null );
|
|
139
|
+
}
|
|
140
|
+
}, [
|
|
141
|
+
feature,
|
|
142
|
+
generateFirstLogo,
|
|
143
|
+
loadLogoHistory,
|
|
144
|
+
clearDeletedMedia,
|
|
145
|
+
isLogoHistoryEmpty,
|
|
146
|
+
siteId,
|
|
147
|
+
] );
|
|
148
|
+
|
|
149
|
+
const handleModalOpen = useCallback( async () => {
|
|
150
|
+
setContext( context );
|
|
151
|
+
recordTracksEvent( EVENT_MODAL_OPEN, { context, placement: EVENT_PLACEMENT_QUICK_LINKS } );
|
|
152
|
+
|
|
153
|
+
initializeModal();
|
|
154
|
+
}, [ setContext, context, initializeModal ] );
|
|
155
|
+
|
|
156
|
+
const closeModal = () => {
|
|
157
|
+
// Reset the state when the modal is closed, so we trigger the modal initialization again when it's opened.
|
|
158
|
+
needsToHandleModalOpen.current = true;
|
|
159
|
+
onClose();
|
|
160
|
+
setLoadingState( null );
|
|
161
|
+
setNeedsFeature( false );
|
|
162
|
+
setNeedsMoreRequests( false );
|
|
163
|
+
clearErrors();
|
|
164
|
+
setLogoAccepted( false );
|
|
165
|
+
recordTracksEvent( EVENT_MODAL_CLOSE, { context, placement: EVENT_PLACEMENT_QUICK_LINKS } );
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const handleApplyLogo = () => {
|
|
169
|
+
setLogoAccepted( true );
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const handleCloseAndReload = () => {
|
|
173
|
+
closeModal();
|
|
174
|
+
|
|
175
|
+
setTimeout( () => {
|
|
176
|
+
// Reload the page to update the logo.
|
|
177
|
+
window.location.reload();
|
|
178
|
+
}, 1000 );
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const handleFeedbackClick = () => {
|
|
182
|
+
recordTracksEvent( EVENT_FEEDBACK, { context } );
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// Set site details when siteId changes
|
|
186
|
+
useEffect( () => {
|
|
187
|
+
if ( siteId ) {
|
|
188
|
+
setSiteDetails( siteDetails );
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// When the site details are set, we need to fetch the feature data.
|
|
192
|
+
if ( ! requestedFeatureData.current ) {
|
|
193
|
+
requestedFeatureData.current = true;
|
|
194
|
+
fetchAiAssistantFeature();
|
|
195
|
+
}
|
|
196
|
+
}, [ siteId, siteDetails, setSiteDetails ] );
|
|
197
|
+
|
|
198
|
+
// Handles modal opening logic
|
|
199
|
+
useEffect( () => {
|
|
200
|
+
// While the modal is not open, the siteId is not set, or the feature data is not available, do nothing.
|
|
201
|
+
if ( ! isOpen || ! siteId || ! feature?.costs ) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Prevent multiple calls of the handleModalOpen function
|
|
206
|
+
if ( needsToHandleModalOpen.current ) {
|
|
207
|
+
needsToHandleModalOpen.current = false;
|
|
208
|
+
handleModalOpen();
|
|
209
|
+
}
|
|
210
|
+
}, [ isOpen, siteId, handleModalOpen, feature ] );
|
|
211
|
+
|
|
212
|
+
let body: React.ReactNode;
|
|
213
|
+
|
|
214
|
+
if ( loadingState ) {
|
|
215
|
+
body = <FirstLoadScreen state={ loadingState } />;
|
|
216
|
+
} else if ( featureFetchError || firstLogoPromptFetchError ) {
|
|
217
|
+
body = <FeatureFetchFailureScreen onCancel={ closeModal } onRetry={ initializeModal } />;
|
|
218
|
+
} else if ( needsFeature || needsMoreRequests ) {
|
|
219
|
+
body = (
|
|
220
|
+
<UpgradeScreen
|
|
221
|
+
onCancel={ closeModal }
|
|
222
|
+
upgradeURL={ upgradeURL }
|
|
223
|
+
reason={ needsFeature ? 'feature' : 'requests' }
|
|
224
|
+
/>
|
|
225
|
+
);
|
|
226
|
+
} else {
|
|
227
|
+
body = (
|
|
228
|
+
<>
|
|
229
|
+
{ ! logoAccepted && <Prompt initialPrompt={ initialPrompt } /> }
|
|
230
|
+
<LogoPresenter
|
|
231
|
+
logo={ selectedLogo }
|
|
232
|
+
onApplyLogo={ handleApplyLogo }
|
|
233
|
+
logoAccepted={ logoAccepted }
|
|
234
|
+
siteId={ String( siteId ) }
|
|
235
|
+
/>
|
|
236
|
+
{ logoAccepted ? (
|
|
237
|
+
<div className="jetpack-ai-logo-generator__accept">
|
|
238
|
+
<VisitSiteBanner siteURL={ siteURL } onVisitBlankTarget={ handleCloseAndReload } />
|
|
239
|
+
<div className="jetpack-ai-logo-generator__accept-actions">
|
|
240
|
+
<Button variant="link" onClick={ handleCloseAndReload }>
|
|
241
|
+
{ __( 'Close and refresh', 'jetpack-ai-client' ) }
|
|
242
|
+
</Button>
|
|
243
|
+
<Button href={ siteURL } variant="primary">
|
|
244
|
+
{ __( 'Visit site', 'jetpack-ai-client' ) }
|
|
245
|
+
</Button>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
) : (
|
|
249
|
+
<>
|
|
250
|
+
<HistoryCarousel />
|
|
251
|
+
<div className="jetpack-ai-logo-generator__footer">
|
|
252
|
+
<Button
|
|
253
|
+
variant="link"
|
|
254
|
+
className="jetpack-ai-logo-generator__feedback-button"
|
|
255
|
+
href="https://jetpack.com/redirect/?source=jetpack-ai-feedback"
|
|
256
|
+
target="_blank"
|
|
257
|
+
onClick={ handleFeedbackClick }
|
|
258
|
+
>
|
|
259
|
+
<span>{ __( 'Provide feedback', 'jetpack-ai-client' ) }</span>
|
|
260
|
+
<Icon icon={ external } className="icon" />
|
|
261
|
+
</Button>
|
|
262
|
+
</div>
|
|
263
|
+
</>
|
|
264
|
+
) }
|
|
265
|
+
</>
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return (
|
|
270
|
+
<>
|
|
271
|
+
{ isOpen && (
|
|
272
|
+
<Modal
|
|
273
|
+
className="jetpack-ai-logo-generator-modal"
|
|
274
|
+
onRequestClose={ logoAccepted ? handleCloseAndReload : closeModal }
|
|
275
|
+
shouldCloseOnClickOutside={ false }
|
|
276
|
+
shouldCloseOnEsc={ false }
|
|
277
|
+
title={ __( 'Jetpack AI Logo Generator', 'jetpack-ai-client' ) }
|
|
278
|
+
>
|
|
279
|
+
<div
|
|
280
|
+
className={ clsx( 'jetpack-ai-logo-generator-modal__body', {
|
|
281
|
+
'notice-modal':
|
|
282
|
+
needsFeature || needsMoreRequests || featureFetchError || firstLogoPromptFetchError,
|
|
283
|
+
} ) }
|
|
284
|
+
>
|
|
285
|
+
{ body }
|
|
286
|
+
</div>
|
|
287
|
+
</Modal>
|
|
288
|
+
) }
|
|
289
|
+
</>
|
|
290
|
+
);
|
|
291
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
@import '@automattic/jetpack-base-styles/root-variables';
|
|
2
|
+
|
|
3
|
+
.jetpack-ai-logo-generator__carousel {
|
|
4
|
+
display: flex;
|
|
5
|
+
gap: 8px;
|
|
6
|
+
overflow-x: auto;
|
|
7
|
+
flex-shrink: 0;
|
|
8
|
+
|
|
9
|
+
.components-button {
|
|
10
|
+
height: unset;
|
|
11
|
+
padding: unset;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@media (min-width: 700px) {
|
|
15
|
+
padding: 2px;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.jetpack-ai-logo-generator__carousel-logo {
|
|
20
|
+
display: flex;
|
|
21
|
+
justify-content: center;
|
|
22
|
+
align-items: center;
|
|
23
|
+
border: 1px solid var(--studio-gray-5, #dcdcde);
|
|
24
|
+
border-radius: 2px;
|
|
25
|
+
flex-shrink: 0;
|
|
26
|
+
|
|
27
|
+
&.is-selected {
|
|
28
|
+
border-color: var(--color-link, #3858e9);
|
|
29
|
+
border-width: 1.5px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
img {
|
|
33
|
+
width: 48px;
|
|
34
|
+
height: 48px;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useAnalytics } from '@automattic/jetpack-shared-extension-utils';
|
|
5
|
+
import { Button } from '@wordpress/components';
|
|
6
|
+
import clsx from 'clsx';
|
|
7
|
+
/**
|
|
8
|
+
* Internal dependencies
|
|
9
|
+
*/
|
|
10
|
+
import { EVENT_NAVIGATE } from '../constants.js';
|
|
11
|
+
import useLogoGenerator from '../hooks/use-logo-generator.js';
|
|
12
|
+
import './history-carousel.scss';
|
|
13
|
+
/**
|
|
14
|
+
* Types
|
|
15
|
+
*/
|
|
16
|
+
import type React from 'react';
|
|
17
|
+
|
|
18
|
+
export const HistoryCarousel: React.FC = () => {
|
|
19
|
+
const { tracks } = useAnalytics();
|
|
20
|
+
const { recordEvent: recordTracksEvent } = tracks;
|
|
21
|
+
const { logos, selectedLogo, setSelectedLogoIndex, context } = useLogoGenerator();
|
|
22
|
+
|
|
23
|
+
const handleClick = ( index: number ) => {
|
|
24
|
+
recordTracksEvent( EVENT_NAVIGATE, {
|
|
25
|
+
context,
|
|
26
|
+
logos_count: logos.length,
|
|
27
|
+
selected_logo: index + 1,
|
|
28
|
+
} );
|
|
29
|
+
setSelectedLogoIndex( index );
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const thumbnailFrom = ( url: string ): string => {
|
|
33
|
+
const thumbnailURL = new URL( url );
|
|
34
|
+
|
|
35
|
+
if ( ! thumbnailURL.searchParams.has( 'resize' ) ) {
|
|
36
|
+
thumbnailURL.searchParams.append( 'resize', '48,48' );
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return thumbnailURL.toString();
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div className="jetpack-ai-logo-generator__carousel">
|
|
44
|
+
{ logos.map( ( logo, index ) => (
|
|
45
|
+
<Button
|
|
46
|
+
key={ logo.url }
|
|
47
|
+
className={ clsx( 'jetpack-ai-logo-generator__carousel-logo', {
|
|
48
|
+
'is-selected': logo.url === selectedLogo.url,
|
|
49
|
+
} ) }
|
|
50
|
+
onClick={ () => handleClick( index ) }
|
|
51
|
+
>
|
|
52
|
+
<img src={ thumbnailFrom( logo.url ) } alt={ logo.description } />
|
|
53
|
+
</Button>
|
|
54
|
+
) ) }
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import clsx from 'clsx';
|
|
5
|
+
/**
|
|
6
|
+
* Internal dependencies
|
|
7
|
+
*/
|
|
8
|
+
import loader from '../assets/images/loader.gif';
|
|
9
|
+
/**
|
|
10
|
+
* Types
|
|
11
|
+
*/
|
|
12
|
+
import type React from 'react';
|
|
13
|
+
|
|
14
|
+
export const ImageLoader: React.FC< { className?: string } > = ( { className = null } ) => {
|
|
15
|
+
return (
|
|
16
|
+
<img
|
|
17
|
+
src={ loader }
|
|
18
|
+
alt="Loading"
|
|
19
|
+
className={ clsx( 'jetpack-ai-logo-generator-modal__loader', className ) }
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
@import '@automattic/jetpack-base-styles/root-variables';
|
|
2
|
+
|
|
3
|
+
.jetpack-ai-logo-generator-modal-presenter__wrapper {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
gap: 8px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.jetpack-ai-logo-generator-modal-presenter {
|
|
10
|
+
display: flex;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.jetpack-ai-logo-generator-modal-presenter__content {
|
|
14
|
+
border-radius: 4px;
|
|
15
|
+
background: var(--studio-gray-0, #f6f7f7);
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
flex-grow: 1;
|
|
19
|
+
max-height: 229px;
|
|
20
|
+
|
|
21
|
+
@media (max-width: 700px) {
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
max-height: unset;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.jetpack-ai-logo-generator-modal-presenter__rectangle {
|
|
28
|
+
position: relative;
|
|
29
|
+
width: 0;
|
|
30
|
+
|
|
31
|
+
&::after {
|
|
32
|
+
width: 15px;
|
|
33
|
+
height: 15px;
|
|
34
|
+
transform: rotate(-45deg);
|
|
35
|
+
background-color: var(--studio-gray-0, #f6f7f7);
|
|
36
|
+
content: "";
|
|
37
|
+
position: absolute;
|
|
38
|
+
top: -7.5px;
|
|
39
|
+
left: -66px;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.jetpack-ai-logo-generator-modal-presenter__loading-text {
|
|
44
|
+
flex-grow: 1;
|
|
45
|
+
text-align: center;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.jetpack-ai-logo-generator-modal-presenter__logo {
|
|
49
|
+
width: 198px;
|
|
50
|
+
height: 198px;
|
|
51
|
+
margin: 16px 30px 16px 16px;
|
|
52
|
+
|
|
53
|
+
@media (max-width: 700px) {
|
|
54
|
+
margin: 16px;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.jetpack-ai-logo-generator-modal-presenter__action-wrapper {
|
|
59
|
+
height: 100%;
|
|
60
|
+
flex-grow: 1;
|
|
61
|
+
margin-right: 32px;
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
padding: 16px 0;
|
|
65
|
+
box-sizing: border-box;
|
|
66
|
+
|
|
67
|
+
@media (max-width: 700px) {
|
|
68
|
+
margin-left: 32px;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.jetpack-ai-logo-generator-modal-presenter__description {
|
|
73
|
+
flex-grow: 1;
|
|
74
|
+
padding-top: 16px;
|
|
75
|
+
color: var(--studio-gray-50, #646970);
|
|
76
|
+
overflow-y: auto;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.jetpack-ai-logo-generator-modal-presenter__actions {
|
|
80
|
+
padding-top: 16px;
|
|
81
|
+
margin-top: 16px;
|
|
82
|
+
border-top: 1px solid var(--studio-gray-5, #dcdcde);
|
|
83
|
+
display: flex;
|
|
84
|
+
gap: 24px;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.jetpack-ai-logo-generator-modal-presenter__action {
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
|
|
91
|
+
&.components-button,
|
|
92
|
+
&.components-button:hover,
|
|
93
|
+
&.components-button:active {
|
|
94
|
+
color: var(--color-link, #3858e9);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.action-text {
|
|
98
|
+
font-size: var(--font-body-extra-small);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.jetpack-ai-logo-generator-icon {
|
|
102
|
+
margin-right: 8px;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.jetpack-ai-logo-generator-modal-presenter__success-wrapper {
|
|
107
|
+
display: flex;
|
|
108
|
+
flex-direction: column;
|
|
109
|
+
align-items: center;
|
|
110
|
+
gap: 16px;
|
|
111
|
+
width: 100%;
|
|
112
|
+
|
|
113
|
+
@media (max-width: 700px) {
|
|
114
|
+
padding-bottom: 16px;
|
|
115
|
+
}
|
|
116
|
+
}
|