PluginProbe
Extendify / 3.1.6
Extendify v3.1.6
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / src / AutoLaunch / LaunchPage.jsx

LaunchPage.jsx in Extendify 3.1.6, at src/AutoLaunch/LaunchPage.jsx

268 lines 8.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { ExtendifyCodeConnector } from '@auto-launch/components/ExtendifyCodeConnector';
2 import { Launch } from '@auto-launch/components/Launch';
3 import { LaunchUpdate } from '@auto-launch/components/LaunchUpdate';
4 import { Logo } from '@auto-launch/components/Logo';
5 import { MigrateChoice } from '@auto-launch/components/MigrateChoice';
6 import { MovingGradient } from '@auto-launch/components/MovingGradients';
7 import { NeedsTheme } from '@auto-launch/components/NeedsTheme';
8 import { RestartLaunchModal } from '@auto-launch/components/RestartLaunchModal';
9 import { ViewportPulse } from '@auto-launch/components/ViewportPulse';
10 import { getAbTest } from '@auto-launch/functions/getAbTest';
11 import { preLaunchFunctions } from '@auto-launch/functions/setup';
12 import { updateOption } from '@auto-launch/functions/wp';
13 import { useLaunchDataStore } from '@auto-launch/state/launch-data';
14 import { digest } from '@shared/api/digest';
15 import { registerCoreBlocks } from '@wordpress/block-library';
16 import { getBlockTypes } from '@wordpress/blocks';
17 import { useSelect } from '@wordpress/data';
18 import { useEffect, useRef, useState } from '@wordpress/element';
19 import { __ } from '@wordpress/i18n';
20 import { chevronLeft, Icon } from '@wordpress/icons';
21 import classNames from 'classnames';
22 import { AnimatePresence, motion } from 'framer-motion';
23 import { checkIn, reportRestApiStatus } from './functions/insights';
24
25 export const LaunchPage = () => {
26 const theme = useSelect((select) => select('core').getCurrentTheme());
27 // Checking `theme` here makes sure the data is populated
28 const needsTheme = theme && theme?.textdomain !== 'extendable';
29
30 const oldPages = window.extLaunchData.resetSiteInformation.pagesIds ?? [];
31 const needsToReset = oldPages.length > 0;
32
33 const pluginUpdateNeeded = Boolean(window.extLaunchData?.pluginUpdateNeeded);
34 const themeUpdateNeeded = Boolean(window.extLaunchData?.themeUpdateNeeded);
35 const pluginUpdateViaRest = Boolean(
36 window.extLaunchData?.pluginUpdateViaRest,
37 );
38 const launchUpdateNeeded = pluginUpdateNeeded || themeUpdateNeeded;
39 const launchUpdateAttempt = window.extLaunchData?.launchUpdateAttempt ?? 0;
40 const launchUpdateStale = window.extLaunchData?.launchUpdateStale ?? {};
41 const launchUpdateGaveUp = Object.keys(launchUpdateStale).length > 0;
42
43 const {
44 title,
45 descriptionRaw,
46 go,
47 urlParams,
48 designBuild,
49 setData,
50 showExtendifyCodeScreen,
51 } = useLaunchDataStore();
52 const skipDescription =
53 Boolean(urlParams?.['build-id']) ||
54 designBuild ||
55 ((title || descriptionRaw) && go);
56 const showConnector = !skipDescription && showExtendifyCodeScreen;
57 const showExitLink =
58 !skipDescription && !window.extLaunchData?.hideAutoLaunchExitLink;
59 const inMigrateVariant =
60 getAbTest('AutoLaunch.MigrateScreen').variant === 'B';
61 const [choosingMigration, setChoosingMigration] = useState(inMigrateVariant);
62 const showMigrateChoice = !skipDescription && choosingMigration;
63
64 const containerRef = useRef(null);
65
66 useEffect(() => {
67 if (launchUpdateNeeded) return;
68 if (launchUpdateGaveUp) {
69 digest({
70 error: new Error('Launch went ahead without the pending update'),
71 details: {
72 source: 'auto-launch',
73 caller: 'launch-update',
74 stale: launchUpdateStale,
75 },
76 });
77 }
78 // translators: Launch is a noun.
79 document.title = __('Launch - AI-Powered Web Creation', 'extendify-local');
80 updateOption('extendify_launch_loaded', new Date().toISOString());
81 // We load core blocks so we can parse them
82 if (getBlockTypes().length === 0) registerCoreBlocks();
83
84 preLaunchFunctions();
85 checkIn({ stage: 'launch_page' });
86 reportRestApiStatus();
87 }, [launchUpdateNeeded, launchUpdateGaveUp]);
88
89 if (launchUpdateNeeded) {
90 return (
91 <Wrapper>
92 <div className="bg-white w-full max-w-xl rounded-lg border border-design-main/60 relative z-10">
93 <LaunchUpdate
94 pluginUpdateNeeded={pluginUpdateNeeded}
95 themeUpdateNeeded={themeUpdateNeeded}
96 pluginUpdateViaRest={pluginUpdateViaRest}
97 pluginSlug={window.extLaunchData?.pluginSlug}
98 attempt={launchUpdateAttempt}
99 />
100 </div>
101 </Wrapper>
102 );
103 }
104
105 if (needsTheme) {
106 return (
107 <Wrapper>
108 <div className="bg-white w-full max-w-3xl rounded-lg border border-design-main/60 relative z-10">
109 <NeedsTheme />
110 </div>
111 </Wrapper>
112 );
113 }
114
115 if (needsToReset) {
116 return (
117 <Wrapper>
118 <div className="w-full max-w-2xl rounded-3xl border bg-gray-100/80 backdrop-blur-2xl shadow-md relative z-10 border-gray-300">
119 <RestartLaunchModal pages={oldPages} />
120 </div>
121 </Wrapper>
122 );
123 }
124
125 return (
126 <Wrapper
127 footer={
128 showConnector ? (
129 <BackLink onClick={() => setData('showExtendifyCodeScreen', false)} />
130 ) : showExitLink ? (
131 <ExitLink />
132 ) : null
133 }
134 >
135 <AnimatePresence mode="wait" initial={false}>
136 <TheTitle
137 key={showMigrateChoice ? 'migrate' : 'description'}
138 // The connector renders its own heading, so hide the shared one.
139 hide={skipDescription || showConnector}
140 migrate={showMigrateChoice}
141 />
142 </AnimatePresence>
143 <div
144 ref={containerRef}
145 className={classNames('w-full relative z-10', {
146 'max-w-3xl': inMigrateVariant || showConnector,
147 'max-w-2xl': !inMigrateVariant && !showConnector,
148 'md:h-72.75': inMigrateVariant && !skipDescription,
149 })}
150 >
151 <AnimatePresence mode="wait">
152 {showConnector && (
153 <ExtendifyCodeConnector
154 key="extendify-code"
155 onProceed={() => setData('go', true)}
156 />
157 )}
158 {!showConnector && showMigrateChoice && (
159 <MigrateChoice
160 key="migrate-choice"
161 onBuildNew={() => setChoosingMigration(false)}
162 />
163 )}
164 {!showConnector && !showMigrateChoice && (
165 <Launch
166 key={skipDescription ? 'description-launch' : 'creating-launch'}
167 skipDescription={skipDescription}
168 lastHeight={containerRef.current?.offsetHeight}
169 />
170 )}
171 </AnimatePresence>
172 </div>
173 </Wrapper>
174 );
175 };
176
177 const Wrapper = ({ children, footer }) => {
178 const { pulse } = useLaunchDataStore();
179
180 return (
181 <div style={{ zIndex: 99999 + 1 }} className="fixed inset-0 bg-white">
182 <div className="relative h-dvh bg-banner-main text-banner-text text-base overflow-y-auto">
183 <div className="relative z-10 min-h-dvh w-full flex flex-col items-center justify-between p-6">
184 <div className="w-full flex flex-col items-center gap-5 md:gap-8 m-auto">
185 <div className="mb-4">
186 <Logo />
187 </div>
188 {children}
189 </div>
190 {footer && <div className="w-full pt-8 shrink-0">{footer}</div>}
191 </div>
192 </div>
193 <MovingGradient />
194 {pulse ? <ViewportPulse /> : null}
195 </div>
196 );
197 };
198
199 const ExitLink = () => {
200 return (
201 <a
202 className="inline-flex items-center gap-0.5 text-sm text-banner-text opacity-70 hover:opacity-100 transition-opacity"
203 href={window.extSharedData.adminUrl}
204 onClick={() => checkIn({ stage: 'exit_to_wp_admin' })}
205 >
206 <Icon fill="currentColor" icon={chevronLeft} size={20} />
207 {__('WP Admin Dashboard', 'extendify-local')}
208 </a>
209 );
210 };
211
212 const BackLink = ({ onClick }) => {
213 return (
214 <button
215 type="button"
216 onClick={onClick}
217 className="inline-flex items-center gap-0.5 border-0 bg-transparent cursor-pointer text-sm text-banner-text opacity-70 hover:opacity-100 transition-opacity"
218 >
219 <Icon fill="currentColor" icon={chevronLeft} size={20} />
220 {__('Back', 'extendify-local')}
221 </button>
222 );
223 };
224
225 const TheTitle = ({ hide, migrate }) => {
226 const useOldHeader =
227 getAbTest('AutoLaunch.HeaderParagraphOld').variant === 'B';
228 if (hide) return null;
229
230 const headingClass =
231 'text-xl md:text-2xl text-pretty text-banner-text font-semibold p-0 m-0 text-center';
232 const transition = {
233 animate: { opacity: 1 },
234 exit: { opacity: 0 },
235 transition: { duration: 0.4 },
236 };
237
238 if (migrate) {
239 return (
240 <motion.h2 className={headingClass} {...transition}>
241 {__('How would you like to start your website?', 'extendify-local')}
242 </motion.h2>
243 );
244 }
245
246 if (useOldHeader) {
247 return (
248 <motion.div className="flex flex-col items-center gap-2" {...transition}>
249 <h2 className={headingClass}>
250 {__('Tell Us About Your Website', 'extendify-local')}
251 </h2>
252 <p className="text-sm md:text-base text-pretty text-banner-text opacity-70 p-0 m-0 text-center max-w-xl">
253 {__(
254 "Share your vision, and we'll craft a website that's perfectly tailored to your needs, ready to launch in no time.",
255 'extendify-local',
256 )}
257 </p>
258 </motion.div>
259 );
260 }
261
262 return (
263 <motion.h2 className={headingClass} {...transition}>
264 {__('Describe the website you want to build', 'extendify-local')}
265 </motion.h2>
266 );
267 };
268