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 / Launch / pages / CreatingSite.jsx

CreatingSite.jsx in Extendify 3.1.6, at src/Launch/pages/CreatingSite.jsx

771 lines 22.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { Transition } from '@headlessui/react';
2 import { getPageTemplates } from '@launch/api/DataApi';
3 import { importTemporaryProducts } from '@launch/api/WooCommerce';
4 import {
5 addPageLinksToNav,
6 addSectionLinksToNav,
7 createNavigation,
8 getActivePlugins,
9 getOption,
10 getPageById,
11 installFontFamilies,
12 postLaunchFunctions,
13 prefetchAssistData,
14 updateNavAttributes,
15 updateOption,
16 updatePageTitlePattern,
17 updateTemplatePart,
18 } from '@launch/api/WPApi';
19 import { PagesSkeleton } from '@launch/components/CreatingSite/PageSkeleton';
20 import { useConfetti } from '@launch/hooks/useConfetti';
21 import { useSiteLogo } from '@launch/hooks/useSiteLogo';
22 import { useWarnOnLeave } from '@launch/hooks/useWarnOnLeave';
23 import {
24 updateButtonLinks,
25 updateSinglePageLinksToSections,
26 } from '@launch/lib/linkPages';
27 import { uploadLogo } from '@launch/lib/logo';
28 import {
29 addImprintPage,
30 createBlogSampleData,
31 createWpPages,
32 generateCustomPageContent,
33 replacePlaceholderPatterns,
34 setHelloWorldFeaturedImage,
35 updateGlobalStyleVariant,
36 updateNaturalVibeStyles,
37 } from '@launch/lib/wp';
38 import { usePagesStore } from '@launch/state/Pages';
39 import { usePagesSelectionStore } from '@launch/state/pages-selections';
40 import { useUserSelectionStore } from '@launch/state/user-selections';
41 import { Logo, Spinner } from '@launch/svg';
42 import { buildRecommendedPagesParams } from '@launch/utils/buildRecommendedPagesParams';
43 import { getPartnerPlugins, recordPluginActivity } from '@shared/api/DataApi';
44 import { activatePlugin, installPlugin } from '@shared/api/wp';
45 import { pageNames } from '@shared/lib/pages';
46 import {
47 deepMerge,
48 retryOperation,
49 waitFor200Response,
50 wasPluginInstalled,
51 } from '@shared/lib/utils';
52 import { useAIConsentStore } from '@shared/state/ai-consent';
53 import { dispatch, select } from '@wordpress/data';
54 import { useCallback, useEffect, useState } from '@wordpress/element';
55 import { __ } from '@wordpress/i18n';
56 import { colord } from 'colord';
57
58 const {
59 homeUrl,
60 adminUrl,
61 partnerLogo,
62 partnerName,
63 showImprint,
64 wpLanguage,
65 installedPluginsSlugs,
66 } = window.extSharedData;
67
68 export const CreatingSite = () => {
69 const [isShowing] = useState(true);
70 const [confettiReady, setConfettiReady] = useState(false);
71 const [confettiColors, setConfettiColors] = useState(['#ffffff']);
72 const [warnOnLeaveReady, setWarnOnLeaveReady] = useState(true);
73 const [loadAdmin, setLoadAdmin] = useState(false);
74 const {
75 siteType,
76 siteInformation,
77 siteStructure,
78 sitePlugins,
79 variation,
80 siteProfile,
81 siteStrings,
82 siteImages,
83 CTALink,
84 siteObjective,
85 siteQA,
86 urlParameters,
87 setSiteStrings,
88 } = useUserSelectionStore();
89 const { pages, style, removeAll, add } = usePagesSelectionStore();
90 const [info, setInfo] = useState([
91 __('Preparing to build your site', 'extendify-local'),
92 ]);
93 const [infoDesc, setInfoDesc] = useState([
94 // Because useSiteLogo causes a noticeable delay, we set this initial desc to match
95 __('Generating a site logo', 'extendify-local'),
96 ]);
97 const inform = (msg) => setInfo((info) => [msg, ...info]);
98 const informDesc = (msg) => setInfoDesc((infoDesc) => [msg, ...infoDesc]);
99 const [pagesToAnimate, setPagesToAnimate] = useState([]);
100 const { setPage } = usePagesStore();
101 const customFontFamilies =
102 variation?.settings?.typography?.fontFamilies?.custom;
103 const { setUserGaveConsent } = useAIConsentStore();
104 const { loading: logoLoading, logoUrl } = useSiteLogo();
105 const redirectUrl =
106 // on landing pages for some users, we redirect to home_url
107 (window.extOnbData?.redirectToWebsite &&
108 siteObjective === 'landing-page') ||
109 window.extSharedData?.showAIAgents
110 ? `${homeUrl}?extendify-launch-success=1`
111 : `${adminUrl}admin.php?page=extendify-assist&extendify-launch-success=1`;
112 const shouldLoadPages =
113 urlParameters?.skip?.includes('pages') && siteStructure === 'multi-page';
114 const [pagesLoaded, setPagesLoaded] = useState(false);
115
116 useWarnOnLeave(warnOnLeaveReady);
117
118 const loadRecommendedPages = useCallback(async () => {
119 const recommended = await getPageTemplates(buildRecommendedPagesParams());
120 removeAll('pages');
121 recommended.recommended.forEach((page) => {
122 add('pages', page);
123 });
124 setPagesLoaded(true);
125 }, [removeAll, add]);
126
127 useEffect(() => {
128 if (!shouldLoadPages) return;
129 if (pagesLoaded) return;
130 loadRecommendedPages().catch(console.error);
131 }, [shouldLoadPages, pagesLoaded, loadRecommendedPages]);
132
133 const doEverything = useCallback(async () => {
134 try {
135 const blogQuestion = siteQA?.questions?.find(
136 (question) => question.id === 'blog',
137 );
138 const hasBlogGoal = blogQuestion
139 ? (blogQuestion?.answerUser ?? blogQuestion?.answerAI) === 'yes'
140 : siteObjective === 'blog' || false;
141 const needsImprintPage = Array.isArray(showImprint)
142 ? showImprint.includes(wpLanguage ?? '') &&
143 siteProfile?.aiSiteCategory === 'Business'
144 : false;
145
146 await uploadLogo(logoUrl, { forceReplace: true });
147
148 await updateOption('permalink_structure', '/%postname%/');
149 await waitFor200Response();
150 inform(__('Applying your website styles', 'extendify-local'));
151 informDesc(__('Creating a beautiful website', 'extendify-local'));
152 await new Promise((resolve) => setTimeout(resolve, 1000));
153
154 // If they are launching the site, it means they agreed to the terms
155 setUserGaveConsent(true);
156
157 if (siteInformation.title) {
158 await updateOption('blogname', siteInformation.title);
159 }
160
161 await waitFor200Response();
162 // TODO: Refactor to assume 0default for site type
163 const siteTypeUpdated = {
164 ...(siteType ?? {}),
165 // Override with the ai site type if it exists
166 name: siteProfile?.aiSiteType ?? siteType.name,
167 };
168
169 await updateOption(
170 'extendify_siteType',
171 // Only persist the site type if the slug exists
172 siteType?.slug ? siteTypeUpdated : {},
173 );
174
175 await waitFor200Response();
176 // Install font families that are not in the theme.
177 if (customFontFamilies?.length) {
178 const installedFontFamilies =
179 await installFontFamilies(customFontFamilies);
180 await updateGlobalStyleVariant(
181 deepMerge(
182 variation,
183 // We set to null first to reset the field.
184 { settings: { typography: { fontFamilies: { custom: null } } } },
185 // We add the installed font families here to activate them.
186 {
187 settings: {
188 typography: {
189 fontFamilies: {
190 custom: installedFontFamilies.filter(Boolean),
191 },
192 },
193 },
194 },
195 ) ?? {},
196 );
197 } else {
198 await updateGlobalStyleVariant(variation);
199 }
200
201 await waitFor200Response();
202
203 const selectedVibe = style?.siteStyle?.vibe;
204 const selectedAnimation = style?.siteStyle?.animation;
205 if (selectedVibe && selectedVibe !== 'natural-1') {
206 inform(
207 // translators: "site style" refers to the structural aesthetic style for the site.
208 __('Applying site style', 'extendify-local'),
209 );
210 informDesc(
211 // translators: "site style" refers to the structural aesthetic style for the site.
212 __('Customizing your site style', 'extendify-local'),
213 );
214 await updateNaturalVibeStyles(selectedVibe);
215 await new Promise((resolve) => setTimeout(resolve, 1000));
216 }
217
218 const navigationId = await createNavigation();
219
220 let headerCode = updateNavAttributes(style?.headerCode, {
221 ref: navigationId,
222 });
223 if (siteObjective === 'landing-page') {
224 // remove the header navigation from the landing page
225 headerCode = headerCode
226 .replace(/<!--\s*wp:navigation\b[^>]*.*\/-->/gis, '')
227 .replace(
228 /<!--\s*wp:social-links\b[^>]*>.*?<!--\s*\/wp:social-links\s*-->/gis,
229 '',
230 );
231 }
232
233 let footerCode = style?.footerCode;
234 let footerNavigationId = null;
235 let footerNavPages = [];
236
237 if (needsImprintPage) {
238 footerNavigationId = await createNavigation(
239 'content',
240 __('Footer Navigation', 'extendify-local'),
241 'footer-navigation',
242 );
243 footerCode = updateNavAttributes(footerCode, {
244 ref: footerNavigationId,
245 });
246 }
247
248 await waitFor200Response();
249 await updateTemplatePart('extendable/header', headerCode);
250
251 await waitFor200Response();
252 await updateTemplatePart('extendable/footer', footerCode);
253
254 inform(__('Populating data', 'extendify-local'));
255 informDesc(__('Personalizing your experience', 'extendify-local'));
256 await prefetchAssistData();
257 await waitFor200Response();
258 await new Promise((resolve) => setTimeout(resolve, 1000));
259
260 // Store the site vibes, colorPalette and fonts
261 await updateOption(
262 'extendify_siteStyle',
263 style?.siteStyle || {
264 vibe: 'standard',
265 fonts: { heading: {}, body: {} },
266 colorPalette: null,
267 },
268 );
269
270 // Store animation settings if present
271 if (selectedAnimation) {
272 await waitFor200Response();
273 // Handle both string and object formats
274 const animationSettings =
275 typeof selectedAnimation === 'string'
276 ? { type: selectedAnimation, speed: 'medium' }
277 : selectedAnimation;
278 await updateOption('extendify_animation_settings', animationSettings);
279 }
280
281 // Add required plugins to the end of the list to give them lower priority
282 // when filtering out duplicates.
283 const sortedPlugins = [...sitePlugins]
284 // Remove duplicates
285 .reduce((acc, plugin) => {
286 const found = acc.find(
287 ({ wordpressSlug: s }) => s === plugin.wordpressSlug,
288 );
289 if (!found) acc.push(plugin);
290 return acc;
291 }, [])
292 // We add give to the front. See here why:
293 // https://github.com/extendify/company-product/issues/713
294 .sort(({ wordpressSlug }) => (wordpressSlug === 'give' ? -1 : 1));
295
296 if (sortedPlugins?.length) {
297 inform(__('Preparing site functionality', 'extendify-local'));
298 await new Promise((resolve) => setTimeout(resolve, 1000));
299
300 const pluginInstallMessages = [
301 __('Getting everything ready', 'extendify-local'),
302 __('Enhancing your site', 'extendify-local'),
303 __('Setting up essential tools', 'extendify-local'),
304 ];
305 informDesc(pluginInstallMessages?.at(-1));
306 for (const [index, plugin] of sortedPlugins.entries()) {
307 const slug = plugin?.wordpressSlug;
308 // Don't install if already installed
309 if (!installedPluginsSlugs?.includes(slug)) {
310 await retryOperation(() => installPlugin(slug), {
311 maxAttempts: 2,
312 }).catch(console.error);
313
314 recordPluginActivity({ slug, source: 'launch' });
315 if (index % 2 === 1) {
316 // skip first message
317 const i = Math.floor(index / 2) % pluginInstallMessages.length;
318 informDesc(pluginInstallMessages[i]);
319 }
320 }
321
322 await retryOperation(() => activatePlugin(slug), {
323 maxAttempts: 2,
324 }).catch(console.error);
325 }
326 }
327
328 const homePage = {
329 name: pageNames.home.title,
330 id: 'home',
331 patterns: style.patterns,
332 slug: 'home',
333 };
334 // Home's blog section leaves posts unreachable without a posts page.
335 const needsBlog =
336 hasBlogGoal ||
337 homePage.patterns?.some((pattern) =>
338 pattern.patternTypes?.includes('blog-section'),
339 );
340 const blogPage =
341 needsBlog && !pages.some(({ slug }) => slug === 'blog')
342 ? {
343 name: pageNames.blog.title,
344 id: 'blog',
345 patterns: [],
346 slug: 'blog',
347 }
348 : null;
349
350 await waitFor200Response();
351
352 inform(__('Adding page content', 'extendify-local'));
353 informDesc(__('Starting off with a full website', 'extendify-local'));
354 await new Promise((resolve) => setTimeout(resolve, 1000));
355 await waitFor200Response();
356
357 if (siteProfile.aiDescription) {
358 informDesc(__('Creating pages with custom content', 'extendify-local'));
359 [homePage, ...pages].forEach((page) => {
360 setPagesToAnimate((previous) => [...previous, page.name]);
361 });
362 }
363
364 const pagesWithoutPageTitlePattern = pages.map((page) => ({
365 ...page,
366 patterns: page.patterns.filter(
367 (p) => !p.patternTypes?.includes('page-title'),
368 ),
369 }));
370
371 // Update the page-with-title template with the selected page-title pattern
372 const firstPageTitlePattern = pages?.[0]?.patterns?.find((p) =>
373 p.patternTypes?.includes('page-title'),
374 );
375
376 const hasPageWithTitleTemplate = firstPageTitlePattern
377 ? await updatePageTitlePattern(firstPageTitlePattern.code)
378 : false;
379
380 const pagesToUse = hasPageWithTitleTemplate
381 ? pagesWithoutPageTitlePattern
382 : pages;
383
384 const pagesToCreate = [...pagesToUse, homePage, blogPage].filter(Boolean);
385
386 const pagesWithReplacedPatterns = [];
387 // Run these one page at a time so we don't end up with duplicate dependency issues
388 for (const page of pagesToCreate) {
389 const updatedPage = {
390 ...page,
391 patterns: await replacePlaceholderPatterns(page.patterns),
392 };
393 pagesWithReplacedPatterns.push(updatedPage);
394 }
395
396 const pagesWithCustomContent = await generateCustomPageContent(
397 pagesWithReplacedPatterns,
398 {
399 sitePlugins,
400 siteType: siteTypeUpdated.name,
401 siteInformation,
402 },
403 siteProfile,
404 );
405
406 const createdHomePage = pagesWithCustomContent.find(
407 (p) => p.slug === 'home',
408 );
409 const heroPattern = createdHomePage?.patterns?.find((p) =>
410 p.patternTypes?.includes('hero-header'),
411 );
412 const pMatch = heroPattern?.code?.match(/<p[^>]*>([\s\S]*?)<\/p>/);
413 const heroDesc = pMatch?.[1]?.replace(/<[^>]+>/g, '').trim();
414 setSiteStrings({
415 ...siteStrings,
416 heroDescription: heroDesc || siteStrings?.heroDescription,
417 });
418
419 const createdPages = await createWpPages(pagesWithCustomContent, {
420 stickyNav:
421 siteStructure === 'single-page' && siteObjective !== 'landing-page',
422 });
423
424 if (needsBlog) {
425 informDesc(__('Creating blog sample data', 'extendify-local'));
426 await createBlogSampleData(siteStrings, siteImages);
427 }
428
429 await waitFor200Response();
430 if (siteImages?.siteImages) {
431 await setHelloWorldFeaturedImage(siteImages.siteImages);
432 }
433
434 if (needsImprintPage) {
435 informDesc(__('Adding imprint page', 'extendify-local'));
436 const createdImprintPage = await addImprintPage(style?.siteStyle);
437 if (createdImprintPage) {
438 createdPages.push(createdImprintPage);
439 footerNavPages = [
440 {
441 name: createdImprintPage.title.rendered,
442 slug: createdImprintPage.originalSlug,
443 id: createdImprintPage.originalSlug,
444 patterns: [],
445 },
446 ];
447 }
448 }
449
450 setPagesToAnimate([]);
451 await waitFor200Response();
452 informDesc(__('Setting up site layout', 'extendify-local'));
453
454 const navPagesMultiPageSite = [...pages, blogPage, homePage].filter(
455 Boolean,
456 );
457
458 const pluginPages = [];
459
460 // Fetch active plugins after installing plugins
461 const { data: activePlugins } = await getActivePlugins();
462
463 // Add plugin related pages only if plugin is active
464 if (wasPluginInstalled(activePlugins, 'woocommerce')) {
465 const shopPageId = await getOption('woocommerce_shop_page_id');
466 const shopPage = shopPageId
467 ? await getPageById(shopPageId).catch(() => null)
468 : null;
469
470 if (shopPage) {
471 pluginPages.push(shopPage);
472 }
473
474 informDesc(__('Importing shop sample data', 'extendify-local'));
475 try {
476 await importTemporaryProducts();
477 } catch (e) {
478 console.error('Error importing temporary products', e);
479 }
480
481 // If we installed any plugins above, and a partner has supported plugins
482 // linked to those plugins, we should install them here. For example:
483 // A German specific WooCommerce plugin in case WooCommerce is installed.
484 const partnerPlugins = await getPartnerPlugins('products').catch(
485 () => null,
486 );
487
488 if (partnerPlugins) {
489 informDesc(__('Installing supporting plugins', 'extendify-local'));
490 for (const plugin of partnerPlugins) {
491 if (!wasPluginInstalled(activePlugins, plugin)) {
492 const maxAttempts = 2;
493 await retryOperation(() => installPlugin(plugin), {
494 maxAttempts,
495 }).catch(console.error);
496
497 recordPluginActivity({ slug: plugin, source: 'launch' });
498
499 await retryOperation(() => activatePlugin(plugin), {
500 maxAttempts,
501 }).catch(console.error);
502 }
503 }
504 }
505 }
506
507 if (wasPluginInstalled(activePlugins, 'the-events-calendar')) {
508 const eventsPage = {
509 title: {
510 rendered: __('Events', 'extendify-local'),
511 },
512 slug: 'events',
513 link: `${homeUrl}/events`,
514 };
515
516 pluginPages.push(eventsPage);
517 }
518
519 if (wasPluginInstalled(activePlugins, 'wpforms-lite')) {
520 await updateOption('wpforms_activation_redirect', 'skip');
521 }
522
523 if (wasPluginInstalled(activePlugins, 'all-in-one-seo-pack')) {
524 await updateOption('aioseo_activation_redirect', 'skip');
525 }
526
527 if (wasPluginInstalled(activePlugins, 'google-analytics-for-wordpress')) {
528 await updateOption(
529 '_transient__monsterinsights_activation_redirect',
530 null,
531 );
532 }
533
534 const pagesWithLinksUpdated =
535 siteStructure === 'single-page'
536 ? await updateSinglePageLinksToSections(
537 createdPages,
538 pagesWithCustomContent,
539 {
540 linkOverride: CTALink,
541 siteObjective,
542 },
543 )
544 : await updateButtonLinks(createdPages, pluginPages);
545
546 if (siteObjective !== 'landing-page') {
547 if (siteStructure === 'single-page') {
548 await addSectionLinksToNav(
549 navigationId,
550 homePage?.patterns,
551 pluginPages,
552 createdPages,
553 );
554 } else {
555 await addPageLinksToNav(
556 navigationId,
557 navPagesMultiPageSite,
558 pagesWithLinksUpdated,
559 pluginPages,
560 );
561 }
562 if (footerNavigationId) {
563 await addPageLinksToNav(
564 footerNavigationId,
565 footerNavPages,
566 pagesWithLinksUpdated,
567 [],
568 );
569 }
570 }
571
572 await waitFor200Response();
573
574 const renderingModes =
575 select('core/preferences').get('core', 'renderingModes') || {};
576
577 if (renderingModes?.extendable?.page !== 'template-locked') {
578 dispatch('core/preferences').set('core', 'renderingModes', {
579 ...renderingModes,
580 extendable: {
581 ...(renderingModes.extendable || {}),
582 page: 'template-locked',
583 },
584 });
585 }
586
587 inform(__('Setting up your Site Assistant', 'extendify-local'));
588 informDesc(__('Helping you to succeed', 'extendify-local'));
589 await new Promise((resolve) => setTimeout(resolve, 1000));
590 await waitFor200Response();
591 inform(__('Your website has been created!', 'extendify-local'));
592 informDesc(__('Redirecting in 3, 2, 1...', 'extendify-local'));
593 // fire confetti here
594 setConfettiReady(true);
595 setWarnOnLeaveReady(false);
596 await new Promise((resolve) => setTimeout(resolve, 2500));
597
598 await waitFor200Response();
599 } catch (e) {
600 console.error(e);
601 // if the error is 4xx, we should stop trying and prompt them to reload
602 if (e.status >= 400 && e.status < 500) {
603 setWarnOnLeaveReady(false);
604 const alertMsg = __(
605 'We encountered a server error we cannot recover from. Please reload the page and try again.',
606 'extendify-local',
607 );
608 alert(alertMsg);
609 location.href = adminUrl;
610 }
611 await new Promise((resolve) => setTimeout(resolve, 2000));
612 return doEverything();
613 }
614 }, [
615 pages,
616 style,
617 siteType,
618 siteInformation,
619 setPagesToAnimate,
620 siteStructure,
621 variation,
622 siteProfile,
623 siteStrings,
624 siteImages,
625 customFontFamilies,
626 setUserGaveConsent,
627 siteObjective,
628 CTALink,
629 logoUrl,
630 sitePlugins,
631 siteQA,
632 setSiteStrings,
633 ]);
634
635 useEffect(() => {
636 if (logoLoading) return;
637 if (shouldLoadPages && !pagesLoaded) return;
638 doEverything().then(async () => {
639 setPage(0);
640 // This will trigger the post launch php functions.
641 await postLaunchFunctions();
642 // This loads the admin in the background to run php functions in admin context
643 setLoadAdmin(true);
644 await waitFor200Response();
645 window.location.replace(redirectUrl);
646 });
647 }, [
648 doEverything,
649 setPage,
650 logoLoading,
651 redirectUrl,
652 shouldLoadPages,
653 pagesLoaded,
654 ]);
655
656 useEffect(() => {
657 const documentStyles = window.getComputedStyle(document.body);
658 const partnerBg = documentStyles?.getPropertyValue('--ext-banner-main');
659 const partnerText = documentStyles?.getPropertyValue('--ext-banner-text');
660 if (partnerBg) {
661 setConfettiColors([
662 colord(partnerBg).darken(0.3).toHex(),
663 colord(partnerText).alpha(0.5).toHex(),
664 colord(partnerBg).lighten(0.2).toHex(),
665 ]);
666 }
667 }, []);
668
669 useConfetti(
670 {
671 particleCount: 3,
672 angle: 320,
673 spread: 220,
674 origin: { x: 0, y: 0 },
675 colors: confettiColors,
676 },
677 2500,
678 confettiReady,
679 );
680
681 return (
682 <>
683 <Transition
684 as="div"
685 show={isShowing}
686 appear={true}
687 enter="transition-all ease-in-out duration-500"
688 enterFrom="md:w-40vw md:max-w-md"
689 enterTo="md:w-full md:max-w-full"
690 className="flex shrink-0 flex-col justify-between bg-banner-main px-10 py-12 text-banner-text md:h-screen"
691 >
692 <div className="max-w-prose">
693 <div className="md:min-h-48">
694 {partnerLogo ? (
695 <div className="mb-8">
696 <img
697 style={{ maxWidth: '200px' }}
698 src={partnerLogo}
699 alt={partnerName ?? ''}
700 />
701 </div>
702 ) : (
703 <Logo className="logo mb-8 w-32 text-banner-text sm:w-40" />
704 )}
705 <div data-test="message-area">
706 {info.map((step, index) => {
707 if (index) return null;
708 return (
709 <Transition
710 as="div"
711 appear={true}
712 show={isShowing}
713 enter="transition-opacity duration-1000"
714 enterFrom="opacity-0"
715 enterTo="opacity-100"
716 leave="transition-opacity duration-1000"
717 leaveFrom="opacity-100"
718 leaveTo="opacity-0"
719 className="flex items-center space-x-4 text-4xl"
720 key={step}
721 >
722 {step}
723 </Transition>
724 );
725 })}
726 <div className="mt-6 flex items-center space-x-4">
727 <Spinner className="spin rtl:ml-3" />
728 {infoDesc.map((step, index) => {
729 if (index) return null;
730 return (
731 <Transition
732 as="div"
733 appear={true}
734 show={isShowing}
735 enter="transition-opacity duration-1000"
736 enterFrom="opacity-0"
737 enterTo="opacity-100"
738 leave="transition-opacity duration-1000"
739 leaveFrom="opacity-100"
740 leaveTo="opacity-0"
741 className="text-lg"
742 key={step}
743 >
744 {step}
745 </Transition>
746 );
747 })}
748 </div>
749 {pagesToAnimate.length > 0 ? (
750 <PagesSkeleton pages={pagesToAnimate} />
751 ) : null}
752 </div>
753 </div>
754 </div>
755 </Transition>
756 {loadAdmin ? <AdminLoader /> : null}
757 </>
758 );
759 };
760
761 // iframe that loads the admin in the background to make sure
762 // all php functions that require admin context work properly.
763 const AdminLoader = () => (
764 <iframe
765 title="Admin Loader"
766 src={adminUrl}
767 style={{ display: 'none' }}
768 sandbox="allow-same-origin allow-scripts allow-forms"
769 />
770 );
771