PluginProbe
Extendify / 3.1.4
Extendify v3.1.4
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 / fetchers / get-design-build.js

get-design-build.js in Extendify 3.1.4, at src/AutoLaunch/fetchers/get-design-build.js

145 lines 4.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { isExternalLogo, uploadLogo } from '@auto-launch/fetchers/get-logo';
2 import { getThemeVariation } from '@auto-launch/fetchers/get-variation';
3 import {
4 getDesignBuildShape,
5 getLogoShape,
6 getPluginsShape,
7 getStyleShape,
8 } from '@auto-launch/fetchers/shape';
9 import { importBuiltPagesImages } from '@auto-launch/functions/get-imported-images';
10 import {
11 fetchWithTimeout,
12 retryTwice,
13 setStatus,
14 } from '@auto-launch/functions/helpers';
15 import { updateOption } from '@auto-launch/functions/wp';
16 import { useLaunchDataStore } from '@auto-launch/state/launch-data';
17 import { AI_HOST } from '@constants';
18 import { digest } from '@shared/api/digest';
19 import { __ } from '@wordpress/i18n';
20 import { mutate } from 'swr';
21
22 const fallback = null;
23 const headers = { 'Content-Type': 'application/json' };
24
25 // Left set, the launch screen waits forever on a design that never arrives.
26 const dropBuildId = () =>
27 useLaunchDataStore.setState((s) => ({
28 urlParams: { ...s.urlParams, 'build-id': '' },
29 }));
30
31 export const handleDesignBuild = async ({ urlParams }) => {
32 const buildId = urlParams?.['build-id'];
33 if (!buildId) return fallback;
34
35 // translators: this is for a action log UI. Keep it short
36 setStatus(__('Loading your design', 'extendify-local'));
37
38 const url = `${AI_HOST}/api/design/${encodeURIComponent(buildId)}`;
39 const response = await retryTwice(() =>
40 fetchWithTimeout(url, { headers }),
41 ).catch((error) => {
42 return { ok: false, statusText: error.message, status: 0 };
43 });
44
45 if (!response?.ok) {
46 digest({
47 error: {
48 message: response.statusText,
49 name: 'FetchError',
50 status: response.status,
51 },
52 details: { source: 'auto-launch', caller: 'handleDesignBuild' },
53 });
54 dropBuildId();
55 return fallback;
56 }
57
58 try {
59 const parsed = getDesignBuildShape.parse(await response.json());
60
61 // Stash the site profile
62 const profile = parsed.siteProfile;
63 await updateOption('extendify_site_profile', JSON.stringify(profile));
64 mutate('siteProfile', { siteProfile: profile }, false);
65
66 // Stash the site style and variation
67 const style = parsed.siteStyle;
68 const fonts =
69 style.fonts?.heading || style.fonts?.body ? style.fonts : null;
70 const variation = await getThemeVariation(
71 { slug: style.colorPalette, fonts },
72 { fallback: true },
73 );
74 const siteStyle = { ...style, variation };
75 await updateOption('extendify_site_style', siteStyle);
76 await updateOption('extendify_animation_settings', {
77 type: style.animation ?? 'fade',
78 speed: 'medium',
79 });
80 mutate('siteStyle', getStyleShape.parse({ siteStyle }), false);
81
82 // Stash the plugins
83 const sitePlugins = parsed.selectedPlugins;
84 mutate('sitePlugins', getPluginsShape.parse({ sitePlugins }), false);
85
86 // The logo upload swaps logoUrl for a Media Library URL without the
87 // logos-custom/ marker, so capture "is the brand's own logo" from the
88 // source URL now.
89 const hasExternalLogo = isExternalLogo(parsed.logoUrl);
90
91 // Sideload the built-page images, and upload the logo alongside when the
92 // build provided one. logoUrl is nullable; without it we skip the upload
93 // so the siteLogo step falls through to AI logo generation.
94 const uploads = [importBuiltPagesImages(parsed.builtPages)];
95 if (parsed.logoUrl) {
96 mutate(
97 'siteLogo',
98 getLogoShape.parse({ logoUrl: parsed.logoUrl }),
99 false,
100 );
101 uploads.push(uploadLogo(parsed.logoUrl, { external: hasExternalLogo }));
102 }
103 const [builtPages] = await Promise.all(uploads);
104
105 const designBuild = {
106 buildId,
107 ...parsed,
108 builtPages,
109 hasExternalLogo,
110 siteProfile: profile,
111 siteStyle,
112 };
113 // Exclude `pages` from the spread: the pages step owns it (templates with
114 // patterns); designBuild.pages (slug/name only) would clobber it.
115 const { pages, ...topLevel } = designBuild;
116 return { designBuild, ...topLevel };
117 } catch (e) {
118 digest({
119 error: e,
120 details: { source: 'auto-launch', caller: 'handleDesignBuild::parsing' },
121 });
122 console.error('handleDesignBuild:', e);
123 dropBuildId();
124 return fallback;
125 }
126 };
127
128 // Prepend the design build's hero to the fetched home template. Full-page
129 // builds skip /api/home entirely and are assembled in handleHome.
130 export const applyDesignBuildHero = (patterns, designBuild) => {
131 const builtHome = designBuild?.builtPages?.find((p) => p.slug === 'home');
132 if (!builtHome?.patterns?.length) return patterns;
133 return [...builtHome.patterns, ...patterns];
134 };
135
136 // Reorder pages to match the design build's page order; extras fall to the end.
137 export const applyDesignBuildOrder = (pages, designBuild) => {
138 const order = designBuild?.pages?.map((p) => p.slug) ?? [];
139 if (!order.length) return pages;
140 return [
141 ...order.map((slug) => pages.find((t) => t.slug === slug)).filter(Boolean),
142 ...pages.filter((t) => !order.includes(t.slug)),
143 ];
144 };
145