PluginProbe
Extendify / 3.2.2
Extendify v3.2.2
3.2.2 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 All 128 releases
← All changes | src/AutoLaunch/hooks/useCreateSite.js +155 -90 3.0.6 → 3.2.2 View file →
@@ -1,7 +1,8 @@
1 1 import { handleDesignBuild } from '@auto-launch/fetchers/get-design-build';
2 2 import { handleHome } from '@auto-launch/fetchers/get-home';
3 3 import { handleSiteImages } from '@auto-launch/fetchers/get-images';
4 +import { handleLaunchDecisions } from '@auto-launch/fetchers/get-launch-decisions';
4 5 import { handleSiteLogo } from '@auto-launch/fetchers/get-logo';
5 6 import { handlePages } from '@auto-launch/fetchers/get-pages';
6 7 import { handleSitePlugins } from '@auto-launch/fetchers/get-plugins';
7 8 import { handleSiteProfile } from '@auto-launch/fetchers/get-profile';
@@ -18,10 +19,13 @@
18 19 updateSinglePageLinksToSections,
19 20 } from '@auto-launch/functions/links';
20 21 import {
21 22 addPageLinksToNav,
23 + addSectionLinksFromDesign,
22 24 addSectionLinksToNav,
23 25 createNavigation,
26 + injectNavExtras,
27 + injectWooCommerceIcons,
24 28 updateNavAttributes,
25 29 } from '@auto-launch/functions/nav';
26 30 import {
27 31 addImprintPage,
@@ -26,8 +30,9 @@
26 30 import {
27 31 addImprintPage,
28 32 createWpPages,
29 33 getPagesToCreate,
34 + isBlogPage,
30 35 PLUGIN_OWNED_PAGES,
31 36 setHelloWorldFeaturedImage,
32 37 updatePageTitlePattern,
33 38 } from '@auto-launch/functions/pages';
@@ -32,24 +37,26 @@
32 37 updatePageTitlePattern,
33 38 } from '@auto-launch/functions/pages';
34 39 import { generatePageContent } from '@auto-launch/functions/patterns';
35 40 import {
36 - activatePlugin,
37 41 alreadyActive,
42 + ensurePluginsActive,
38 43 getActivePlugins,
39 - installPlugin,
40 44 replacePlaceholderPatterns,
45 + reportFailedDependencies,
46 + reportInactivePlugins,
41 47 } from '@auto-launch/functions/plugins';
42 48 import {
43 49 postLaunchFunctions,
44 50 prefetchAssistData,
45 51 } from '@auto-launch/functions/setup';
52 +import { applySocialProfiles } from '@auto-launch/functions/social-links';
46 53 import {
47 54 setThemeRenderingMode,
48 55 updateTemplatePart,
49 56 updateVariation,
50 57 } from '@auto-launch/functions/theme';
51 -import { computeVibeAdjustedBlocks } from '@auto-launch/functions/vibes';
58 +import { computeVibeAdjustments } from '@auto-launch/functions/vibes';
52 59 import {
53 60 createBlogSampleData,
54 61 getOption,
55 62 getPageById,
@@ -57,22 +64,22 @@
57 64 updateOption,
58 65 } from '@auto-launch/functions/wp';
59 66 import { useWarnOnLeave } from '@auto-launch/hooks/useWarnOnLeave';
60 67 import { useLaunchDataStore } from '@auto-launch/state/launch-data';
68 +import { launchStrings } from '@auto-launch/strings';
61 69 import { digest } from '@shared/api/digest';
70 +import { siteImageUrls } from '@shared/lib/site-images';
62 71 import { useAIConsentStore } from '@shared/state/ai-consent';
63 72 import { useEffect, useRef, useState } from '@wordpress/element';
64 73 import { __ } from '@wordpress/i18n';
65 74 import useSWRImmutable from 'swr/immutable';
66 75
67 -const { homeUrl, showImprint, wpLanguage, installedPluginsSlugs } =
68 - window.extSharedData;
76 +const { homeUrl, showImprint, wpLanguage } = window.extSharedData;
69 77
70 78 // TODO: I think a good strategy is "if something fails, try to refetch some state"
71 79
72 80 export const useCreateSite = () => {
73 - // All the data we need to finish
74 - const { setErrorMessage, addStatusMessage, needToStall, ...data } =
81 + const { setErrorMessage, addStatusMessage, needToStall, setData, ...data } =
75 82 useLaunchDataStore();
76 83 const { setUserGaveConsent } = useAIConsentStore();
77 84 const homeStretch = useRef(false);
78 85 const [warnOnReload, setWarnOnReload] = useState(!needToStall());
@@ -113,8 +120,11 @@
113 120 useRunStep(
114 121 'siteLogo',
115 122 () => {
116 123 if (!data.siteProfile?.title) return null;
124 + // Logo was already uploaded by handleDesignBuild;
125 + // generating an AI logo here is not necessary.
126 + if (data.designBuild?.logoUrl) return null;
117 127 return data;
118 128 },
119 129 async (params) => {
120 130 checkIn({ stage: 'get_logo' });
@@ -122,13 +132,12 @@
122 132 },
123 133 );
124 134
125 135 // needs: siteProfile
126 - // provides:sitePlugins: [{name, wordpressSlug}]
136 + // provides: sitePlugins: [{name, wordpressSlug}]
127 137 useRunStep(
128 138 'sitePlugins',
129 139 () => {
130 - // We just need the site profile, which has this
131 140 if (!data.siteProfile?.title) return null;
132 141 return data;
133 142 },
134 143 async (params) => {
@@ -138,24 +147,17 @@
138 147 );
139 148
140 149 useEffect(() => {
141 150 // Start installing the partner plugins asap
142 - if (!data.sitePlugins) return;
151 + if (!data.sitePlugins?.length) return;
143 152
144 - const pluginsToInstall = data.sitePlugins.filter(
145 - ({ wordpressSlug: slug }) => !installedPluginsSlugs?.includes(slug),
146 - );
147 - if (pluginsToInstall.length === 0) return;
148 153 setStatus(
149 154 // translators: this is for a action log UI. Keep it short
150 - __('Setting up functionality for your website', 'extendify-local'),
155 + launchStrings().statusFunctionality,
151 156 );
152 - (async function install() {
153 - for (const { wordpressSlug: slug } of pluginsToInstall) {
154 - const p = await installPlugin(slug);
155 - await activatePlugin(p?.plugin ?? slug);
156 - }
157 - })();
157 + ensurePluginsActive(
158 + data.sitePlugins.map(({ wordpressSlug }) => wordpressSlug),
159 + );
158 160 }, [data.sitePlugins]);
159 161
160 162 // needs: siteProfile
161 163 // provides: style: {}
@@ -161,9 +163,8 @@
161 163 // provides: style: {}
162 164 useRunStep(
163 165 'siteStyle',
164 166 () => {
165 - // We just need the site profile, which has this
166 167 if (!data.siteProfile?.title) return null;
167 168 return data;
168 169 },
169 170 async (params) => {
@@ -176,9 +177,8 @@
176 177 // provides: aiHeaders: [], aiBlogTitles: []
177 178 useRunStep(
178 179 'siteStrings',
179 180 () => {
180 - // We just need the site profile, which has this
181 181 if (!data.siteProfile?.title) return null;
182 182 return data;
183 183 },
184 184 async (params) => {
@@ -191,9 +191,8 @@
191 191 // provides: siteImages: []
192 192 useRunStep(
193 193 'siteImages',
194 194 () => {
195 - // We just need the site profile, which has this
196 195 if (!data.siteProfile?.title) return null;
197 196 return data;
198 197 },
199 198 async (params) => {
@@ -201,14 +200,27 @@
201 200 return await handleSiteImages(params);
202 201 },
203 202 );
204 203
204 + // needs: siteProfile
205 + // provides: launchDecisions: { navExtras, navButtonLabel }
206 + useRunStep(
207 + 'launchDecisions',
208 + () => {
209 + if (!data.siteProfile?.title) return null;
210 + return data;
211 + },
212 + async (params) => {
213 + checkIn({ stage: 'get_launch_decisions' });
214 + return await handleLaunchDecisions(params);
215 + },
216 + );
217 +
205 218 // needs: siteProfile, sitePlugins, siteStyle, siteImages, aiHeaders
206 219 // provides: home: { id, slug, patterns, siteStyle }
207 220 useRunStep(
208 221 'home',
209 222 () => {
210 - // Checking various data from calls above
211 223 const ok = [
212 224 data.siteProfile,
213 225 data.siteStyle,
214 226 data.siteImages,
@@ -222,14 +234,13 @@
222 234 return await handleHome(params);
223 235 },
224 236 );
225 237
226 - // siteProfile, sitePlugins, siteStyle, siteImages
238 + // needs: siteProfile, sitePlugins, siteStyle, siteImages
227 239 // provides: pages: [{ id, slug, name, patterns, siteStyle }]
228 240 useRunStep(
229 241 'pages',
230 242 () => {
231 - // Checking various data from calls above
232 243 const ok = [
233 244 data.siteProfile,
234 245 data.siteStyle,
235 246 data.siteImages,
@@ -256,14 +267,11 @@
256 267 checkIn({ stage: 'set_general', siteProfile, sitePlugins, siteStyle });
257 268
258 269 const { title } = siteProfile;
259 270 // translators: this is for a action log UI. Keep it short
260 - addStatusMessage(__('Adding admin configurations', 'extendify-local'));
261 - // update permalinks
271 + addStatusMessage(launchStrings().statusAdmin);
262 272 await updateOption('permalink_structure', '/%postname%/');
263 - // make sure consent is set
264 273 setUserGaveConsent(true);
265 - // Update title
266 274 if (title) await updateOption('blogname', title);
267 275 },
268 276 );
269 277
@@ -301,8 +309,9 @@
301 309 siteStyle,
302 310 aiBlogTitles,
303 311 siteImages,
304 312 designBuild,
313 + launchDecisions,
305 314 } = data;
306 315 // pages could be [] and pass here, that's ok
307 316 if (!home || !pages) return;
308 317 if (homeStretch.current) return;
@@ -308,10 +317,23 @@
308 317 if (homeStretch.current) return;
309 318 homeStretch.current = true;
310 319 (async () => {
311 320 const { objective, structure, category } = siteProfile;
321 + const builtHome = designBuild?.builtPages?.find((p) => p.slug === 'home');
322 + // Must match get-home.js's full-page test; if they drift, the home is
323 + // built one way and planted another.
324 + const isSinglePageDesign =
325 + structure === 'single-page' &&
326 + Boolean(builtHome?.fullPage && builtHome.patterns?.length) &&
327 + Boolean(designBuild?.pages?.length);
328 + const imageUrls = siteImageUrls(siteImages);
329 + const intendedPlugins = (sitePlugins ?? []).map(
330 + ({ wordpressSlug }) => wordpressSlug,
331 + );
312 332
313 - // Do they need an imprint page?
333 + // Guarantee plugins are active before the pattern imports below rely on them.
334 + await ensurePluginsActive(intendedPlugins);
335 +
314 336 const needsImprint = Array.isArray(showImprint)
315 337 ? showImprint.includes(wpLanguage ?? '') && category === 'Business'
316 338 : false;
317 339
@@ -320,9 +342,9 @@
320 342 let variation = siteStyle?.variation;
321 343 if (customFonts?.length) {
322 344 checkIn({ stage: 'install_fonts' });
323 345 // translators: this is for a action log UI. Keep it short
324 - addStatusMessage(__('Installing fonts locally', 'extendify-local'));
346 + addStatusMessage(launchStrings().statusFonts);
325 347 const installed = await installFontFamilies(customFonts).catch(
326 348 () => [],
327 349 );
328 350 variation = mergeFontsIntoVariation(siteStyle.variation, installed);
@@ -329,19 +351,15 @@
329 351 }
330 352
331 353 if (siteStyle?.vibe && siteStyle.vibe !== 'natural-1') {
332 354 // translators: vibe in this context is a noun - the feeling of their site design.
333 - addStatusMessage(__('Setting the website style', 'extendify-local'));
355 + addStatusMessage(launchStrings().statusSiteStyle);
334 356 checkIn({ stage: 'compute_vibe' });
335 - const vibeBlocks = await computeVibeAdjustedBlocks(
357 + const vibe = await computeVibeAdjustments(
336 358 siteStyle.vibe,
359 + variation,
337 360 ).catch(() => null);
338 - if (vibeBlocks) {
339 - variation = {
340 - ...variation,
341 - styles: { ...variation.styles, blocks: vibeBlocks },
342 - };
343 - }
361 + if (vibe) variation = { ...variation, ...vibe };
344 362 }
345 363
346 364 checkIn({ stage: 'set_vibe' });
347 365 await updateVariation(variation);
@@ -346,9 +364,9 @@
346 364 checkIn({ stage: 'set_vibe' });
347 365 await updateVariation(variation);
348 366
349 367 // navigation menu
350 - addStatusMessage(__('Working on the navigation', 'extendify-local'));
368 + addStatusMessage(launchStrings().statusNavigation);
351 369 const { id: headerNavId } = await createNavigation({
352 370 title: __('Header Navigation', 'extendify-local'),
353 371 slug: 'site-navigation',
354 372 });
@@ -354,12 +372,13 @@
354 372 });
355 373 let headerCode = updateNavAttributes(home.headerCode || '', {
356 374 ref: headerNavId,
357 375 });
376 + headerCode = injectNavExtras(headerCode, launchDecisions);
358 377 // remove the header navigation from the landing page
359 378 if (objective === 'landing-page') {
360 379 // translators: this is for a action log UI. Keep it short
361 - addStatusMessage(__('Perfecting a landing page', 'extendify-local'));
380 + addStatusMessage(launchStrings().statusLanding);
362 381 const social =
363 382 /<!--\s*wp:social-links\b[^>]*>.*?<!--\s*\/wp:social-links\s*-->/gis;
364 383 headerCode = headerCode
365 384 .replace(/<!--\s*wp:navigation\b[^>]*.*\/-->/gis, '')
@@ -364,23 +383,27 @@
364 383 headerCode = headerCode
365 384 .replace(/<!--\s*wp:navigation\b[^>]*.*\/-->/gis, '')
366 385 .replace(social, '');
367 386 }
368 - if (typeof siteProfile.phoneNumber === 'string') {
369 - headerCode = headerCode.replaceAll(
370 - // Hardcoded in the template
371 - '206-555-0100',
372 - siteProfile.phoneNumber ||
373 - // translators: Use a number that is appropriate for the locale. It does not need to be this exact number. This is a placeholder phone number. For example, in pt_BR you could use (11) 91234-5678.
374 - __('206-555-0100', 'extendify-local'),
375 - );
376 - }
387 + headerCode = headerCode.replaceAll(
388 + '206-555-0100',
389 + (typeof siteProfile.phoneNumber === 'string' &&
390 + siteProfile.phoneNumber) ||
391 + // translators: Use a number that is appropriate for the locale. It does not need to be this exact number. This is a placeholder phone number. For example, in pt_BR you could use (11) 91234-5678.
392 + __('206-555-0100', 'extendify-local'),
393 + );
377 394 checkIn({ stage: 'set_navigation' });
378 - await updateTemplatePart('extendable/header', headerCode);
379 395
380 396 // footer
381 397 let footerNavId = null;
382 398 let footerCode = home.footerCode || '';
399 + // The logo already carries the brand, so the site title would be redundant.
400 + if (designBuild?.hasExternalLogo && footerCode.includes('wp:site-logo')) {
401 + footerCode = footerCode.replace(
402 + /\s*<!--\s*wp:site-title[\s\S]*?\/-->/g,
403 + '',
404 + );
405 + }
383 406 if (needsImprint) {
384 407 const nav = await createNavigation({
385 408 title: __('Footer Navigation', 'extendify-local'),
386 409 slug: 'footer-navigation',
@@ -387,14 +410,15 @@
387 410 });
388 411 footerNavId = nav.id;
389 412 footerCode = updateNavAttributes(footerCode, { ref: footerNavId });
390 413 }
414 + footerCode = applySocialProfiles(footerCode, siteProfile.socialProfiles);
391 415 checkIn({ stage: 'set_footer' });
392 416 await updateTemplatePart('extendable/footer', footerCode);
393 417
394 418 // pages
395 419 // translators: this is for a action log UI. Keep it short
396 - addStatusMessage(__('Creating pages', 'extendify-local'));
420 + addStatusMessage(launchStrings().statusCreatingPages);
397 421 const pagesToCreate = getPagesToCreate(data);
398 422 const titlePattern = pages?.[0]?.patterns?.find((p) =>
399 423 p.patternTypes?.includes('page-title'),
400 424 );
@@ -403,9 +427,9 @@
403 427 await updatePageTitlePattern(titlePattern.code);
404 428 }
405 429
406 430 const activePlugins = await getActivePlugins();
407 - // This lets us keep plugin pages in th enav but skip making the page
431 + // Keep plugin pages in the nav but skip creating the page itself.
408 432 const reservedSlugs = new Set(
409 433 PLUGIN_OWNED_PAGES.filter(
410 434 ({ plugin }) =>
411 435 sitePlugins.some((p) => p.wordpressSlug === plugin) ||
@@ -429,25 +453,43 @@
429 453 }
430 454 checkIn({ stage: 'generate_page_content' });
431 455 const customPages = await generatePageContent(pagesReplaced, data);
432 456
433 - const stickyNav =
434 - structure === 'single-page' && objective !== 'landing-page';
435 - const createdPagesWP = await createWpPages(customPages, { stickyNav });
457 + // Update heroDescription to the actual AI-rewritten hero content
458 + const homePage = customPages.find((p) => p.slug === 'home');
459 + const heroPattern = homePage?.patterns?.find((p) =>
460 + p.patternTypes?.includes('hero-header'),
461 + );
462 + const pMatch = heroPattern?.code?.match(/<p[^>]*>([\s\S]*?)<\/p>/);
463 + const heroDesc = pMatch?.[1]?.replace(/<[^>]+>/g, '').trim();
464 + const heroDescription = heroDesc || data.heroDescription;
465 + setData('heroDescription', heroDescription);
466 + if (heroDescription) {
467 + await updateOption('extendify_hero_description', heroDescription).catch(
468 + () => null,
469 + );
470 + }
471 +
472 + const createdPagesWP = await createWpPages(customPages, {
473 + skipSectionIds: isSinglePageDesign,
474 + });
436 475 // Aux pages
437 - const hasBlogPattern = home?.patterns?.some((pattern) =>
476 + const blogPattern = home?.patterns?.find((pattern) =>
438 477 pattern.patternTypes.includes('blog-section'),
439 478 );
440 - if (objective === 'blog' || hasBlogPattern) {
479 + if (siteProfile.blog || blogPattern) {
441 480 checkIn({ stage: 'create_blog_sample_data' });
442 481 // translators: this is for a action log UI. Keep it short
443 - addStatusMessage(__('Creating blog sample data', 'extendify-local'));
444 - await createBlogSampleData({ aiBlogTitles }, siteImages);
482 + addStatusMessage(launchStrings().statusBlog);
483 + await createBlogSampleData(
484 + { aiBlogTitles },
485 + imageUrls,
486 + blogPattern?.blogImages,
487 + );
445 488 }
446 - // If we have site images then set up the hello world image
447 - if (siteImages?.length) {
489 + if (imageUrls.length) {
448 490 checkIn({ stage: 'set_hello_world_image' });
449 - await setHelloWorldFeaturedImage(siteImages);
491 + await setHelloWorldFeaturedImage(imageUrls);
450 492 }
451 493
452 494 let imprint = {};
453 495 if (needsImprint) {
@@ -459,9 +501,9 @@
459 501 if (alreadyActive(activePlugins, 'woocommerce')) {
460 502 checkIn({ stage: 'import_woocommerce_products' });
461 503 addStatusMessage(
462 504 // translators: this is for a action log UI. Keep it short
463 - __('Setting up your online store', 'extendify-local'),
505 + launchStrings().statusStore,
464 506 );
465 507 await apiFetchWithTimeout({
466 508 path: '/extendify/v1/auto-launch/import-woocommerce',
467 509 }).catch(() => null);
@@ -476,18 +518,36 @@
476 518 link: `${homeUrl}/events`,
477 519 });
478 520 }
479 521
522 + // The design's page list omits the posts page like it omits shop, so the
523 + // nav needs it here — but it's ours to create, not a PLUGIN_OWNED_PAGES.
524 + pluginPages.push(...createdPagesWP.filter(isBlogPage));
525 +
480 526 // Adding pages to the nav
481 527 checkIn({ stage: 'set_page_links' });
482 - const pagesWithLinksUpdated =
483 - structure === 'single-page'
484 - ? await updateSinglePageLinksToSections(createdPagesWP, customPages, {
485 - objective,
486 - activePlugins,
487 - landingPageCTALink: siteProfile.landingPageCTALink,
488 - })
489 - : await updateButtonLinks(createdPagesWP, pluginPages);
528 + let linksResult = { wpPages: createdPagesWP, headerCode };
529 + if (!isSinglePageDesign) {
530 + linksResult =
531 + structure === 'single-page'
532 + ? await updateSinglePageLinksToSections(
533 + createdPagesWP,
534 + customPages,
535 + {
536 + objective,
537 + activePlugins,
538 + landingPageCTALink: siteProfile.landingPageCTALink,
539 + },
540 + headerCode,
541 + )
542 + : await updateButtonLinks(createdPagesWP, pluginPages, headerCode);
543 + }
544 + const pagesWithLinksUpdated = linksResult.wpPages;
545 + headerCode = linksResult.headerCode;
546 + if (alreadyActive(activePlugins, 'woocommerce')) {
547 + headerCode = injectWooCommerceIcons(headerCode);
548 + }
549 + await updateTemplatePart('extendable/header', headerCode);
490 550 const footerNavPages = [];
491 551 if (footerNavId && imprint?.title) {
492 552 const { originalSlug, title } = imprint;
493 553 footerNavPages.push({
@@ -500,9 +560,20 @@
500 560
501 561 checkIn({ stage: 'set_navigation_links' });
502 562 if (objective !== 'landing-page') {
503 563 const orderedSlugs = designBuild?.pages?.map((p) => p.slug) ?? [];
504 - if (structure === 'single-page') {
564 + // The tag only exists where the sections came back matched 1:1 to our list;
565 + // read it off what was planted, so menu and section ids can't disagree.
566 + const designOwnsNav =
567 + isSinglePageDesign ||
568 + Boolean(homePage?.patterns?.some(({ navSlug }) => navSlug));
569 + if (designOwnsNav) {
570 + await addSectionLinksFromDesign(
571 + headerNavId,
572 + designBuild.pages,
573 + pluginPages,
574 + );
575 + } else if (structure === 'single-page') {
505 576 await addSectionLinksToNav(
506 577 headerNavId,
507 578 home?.patterns,
508 579 pluginPages,
@@ -534,14 +605,18 @@
534 605 await prefetchAssistData();
535 606 checkIn({ stage: 'final_steps' });
536 607 await setThemeRenderingMode('template-locked');
537 608 await postLaunchFunctions();
538 - if (siteImages?.length) {
609 + if (imageUrls.length) {
539 610 await storeSiteImages(siteImages).catch(() => null);
540 611 }
541 612 // translators: this is for a action log UI. Keep it short
542 - addStatusMessage(__('All done!', 'extendify-local'));
543 - await checkIn({ stage: 'finished', siteProfile, sitePlugins, siteStyle });
613 + addStatusMessage(launchStrings().statusDone);
614 + await Promise.all([
615 + reportInactivePlugins(intendedPlugins).catch(() => null),
616 + reportFailedDependencies(pagesReplaced),
617 + checkIn({ stage: 'finished', siteProfile, sitePlugins, siteStyle }),
618 + ]);
544 619 setWarnOnReload(false);
545 620 setDone(true);
546 621 })().catch((error) => {
547 622 console.error(error);
@@ -551,14 +626,9 @@
551 626 });
552 627 // if we error here we can try again by resetting the home stretch and stalling again to refetch data
553 628 homeStretch.current = false;
554 629 needToStall(true);
555 - setErrorMessage(
556 - __(
557 - 'Something went wrong during the final steps. We will try again but you may need to refresh the page.',
558 - 'extendify-local',
559 - ),
560 - );
630 + setErrorMessage(launchStrings().errorFinalSteps);
561 631 });
562 632 }, [data, needToStall, setUserGaveConsent]);
563 633
564 634 return { done };
@@ -582,12 +652,7 @@
582 652 useEffect(() => {
583 653 if (!error || needToStall()) return;
584 654 console.error(error);
585 655 digest({ error, details: { source: 'auto-launch', caller: 'run-step' } });
586 - setErrorMessage(
587 - __(
588 - 'Having some trouble with this step. Trying again...',
589 - 'extendify-local',
590 - ),
591 - );
656 + setErrorMessage(launchStrings().errorStep);
592 657 }, [error, setErrorMessage, needToStall]);
593 658 };