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/Agent/workflows/theme/components/change-site-design/SelectSiteDesign.jsx +78 -35 3.1.4 → 3.2.2 View file →
@@ -1,17 +1,22 @@
1 +import { usePaletteOverride } from '@agent/hooks/usePaletteOverride';
2 +import { usePalettes } from '@agent/hooks/usePalettes';
1 3 import { useSiteVibesOverride } from '@agent/hooks/useSiteVibesOverride';
2 4 import { useSiteVibesVariations } from '@agent/hooks/useSiteVibesVariations';
3 5 import { useVariationOverride } from '@agent/hooks/useVariationOverride';
6 +import { refreshBlockHighlight } from '@agent/lib/block-highlight';
4 7 import { DesignOption } from '@agent/workflows/theme/components/change-site-design/DesignOption';
8 +import { fontsOnlyVariation } from '@agent/workflows/theme/components/change-site-design/utils/fontsOnlyVariation';
5 9 import { removeAnimationClasses } from '@agent/workflows/theme/components/change-site-design/utils/removeAnimationClasses';
6 10 import { handleSiteImages } from '@auto-launch/fetchers/get-images';
7 -import { handleSiteStrings } from '@auto-launch/fetchers/get-strings';
8 11 import { useUserSelectionStore } from '@launch/state/user-selections';
12 +import { paletteDuotone } from '@shared/lib/palette-preview';
13 +import { samplePalettes } from '@shared/lib/palettes';
9 14 import { safeParseJson } from '@shared/lib/parsing';
15 +import { normalizeSiteImages } from '@shared/lib/site-images';
10 16 import apiFetch from '@wordpress/api-fetch';
11 17 import { registerCoreBlocks } from '@wordpress/block-library';
12 18 import { getBlockTypes, parse, serialize } from '@wordpress/blocks';
13 -import { Spinner } from '@wordpress/components';
14 19 import { useDispatch } from '@wordpress/data';
15 20 import { useEffect, useMemo, useRef, useState } from '@wordpress/element';
16 21 import { __ } from '@wordpress/i18n';
17 22 import classnames from 'classnames';
@@ -57,25 +62,29 @@
57 62 const isAdmin = context?.adminPage;
58 63
59 64 const PAGE_SIZE = 5;
60 65
66 +const hasImages = ({ hero, general }) => hero.length > 0 || general.length > 0;
67 +
61 68 const resolveSiteImages = async ({ storedSiteImages, siteProfile }) => {
62 - if (storedSiteImages.length) return storedSiteImages;
69 + const stored = normalizeSiteImages(storedSiteImages);
70 + if (hasImages(stored)) return stored;
63 71
64 72 // A browser that didn't run Launch otherwise reuses one image on every option.
65 - const { siteImages: localized } = window.extSharedData;
66 - if (localized?.length) return localized;
73 + const localized = normalizeSiteImages(window.extSharedData.siteImages);
74 + if (hasImages(localized)) return localized;
67 75
68 76 if (siteProfile) {
69 77 const { siteImages } = await handleSiteImages({ siteProfile });
70 - if (siteImages.length) return siteImages;
78 + const fetched = normalizeSiteImages(siteImages);
79 + if (hasImages(fetched)) return fetched;
71 80 }
72 81
73 - const stored = await apiFetch({
82 + const saved = await apiFetch({
74 83 path: '/extendify/v1/shared/site-images',
75 84 }).catch(() => null);
76 85
77 - return stored?.siteImages ?? [];
86 + return normalizeSiteImages(saved?.siteImages);
78 87 };
79 88
80 89 export const SelectSiteDesign = ({ onConfirm, onCancel }) => {
81 90 const [visibleCount, setVisibleCount] = useState(PAGE_SIZE);
@@ -90,8 +99,9 @@
90 99
91 100 const [selectedColorAndFonts, setSelectedColorAndFonts] = useState();
92 101 const [selectedHeroPattern, setSelectedHeroPattern] = useState();
93 102 const [selectedVibe, setSelectedVibe] = useState();
103 + const [selectedPalette, setSelectedPalette] = useState();
94 104
95 105 const { updateSettings } = useDispatch('core/block-editor');
96 106
97 107 const injectedLinksRef = useRef([]);
@@ -118,14 +128,35 @@
118 128 });
119 129 injectedLinksRef.current = [];
120 130 };
121 131
132 + const {
133 + palettes,
134 + preferred,
135 + css: paletteCss,
136 + isLoading: isLoadingPalettes,
137 + } = usePalettes();
138 + const onPalettes = Boolean(palettes?.length);
139 + const shownPalettes = useMemo(
140 + () => samplePalettes(palettes, preferred),
141 + [palettes, preferred],
142 + );
143 + const paletteFor = (index) =>
144 + onPalettes ? shownPalettes[index % shownPalettes.length] : undefined;
145 +
122 146 const { undoChange: undoColorAndFontsChange } = useVariationOverride({
123 147 css: !isAdmin && selectedColorAndFonts?.css,
124 148 duotoneTheme:
125 - !isAdmin && selectedColorAndFonts?.settings?.color?.duotone?.theme,
149 + !isAdmin &&
150 + !onPalettes &&
151 + selectedColorAndFonts?.settings?.color?.duotone?.theme,
126 152 });
127 153
154 + const { undoChange: undoPaletteChange } = usePaletteOverride({
155 + css: (!isAdmin && paletteCss?.[selectedPalette?.slug]) || '',
156 + duotoneTheme: (!isAdmin && paletteDuotone(selectedPalette)) || null,
157 + });
158 +
128 159 const { undoChange: undoVibesChange } = useSiteVibesOverride({
129 160 css: !isAdmin && selectedVibe?.css,
130 161 slug: !isAdmin && selectedVibe?.slug,
131 162 });
@@ -133,9 +164,10 @@
133 164 const { data: vibesData, isLoading: isLoadingVibes } =
134 165 useSiteVibesVariations();
135 166
136 167 const vibes = useMemo(() => {
137 - if (isLoadingVibes) return null;
168 + // Null for a theme with no block variations, which .css would throw on.
169 + if (isLoadingVibes || !vibesData?.css) return [];
138 170
139 171 const vibes = Object.entries(vibesData.css)
140 172 .filter(([slug]) => slug !== vibesData.currentVibe)
141 173 .map(([slug, css]) => ({
@@ -140,8 +172,13 @@
140 172 .filter(([slug]) => slug !== vibesData.currentVibe)
141 173 .map(([slug, css]) => ({
142 174 slug,
143 175 css: css?.replaceAll(slug, 'natural-1'),
176 + // Css only adds, so a cloned page keeps the applied vibe's slots.
177 + previewCss: `${vibesData.resets?.[slug] ?? ''}${css ?? ''}`.replaceAll(
178 + slug,
179 + 'natural-1',
180 + ),
144 181 }))
145 182 .sort(() => Math.random() - 0.5);
146 183
147 184 return [...vibes, ...vibes.slice(0, 3)];
@@ -194,14 +231,8 @@
194 231 const storedSiteImages =
195 232 stored?.state?.siteImages ?? launchState?.siteImages?.siteImages ?? [];
196 233 const siteProfile = stored?.state?.siteProfile ?? launchState?.siteProfile;
197 234
198 - const storedDescription =
199 - description ??
200 - stored?.state?.heroDescription ??
201 - launchState?.siteStrings?.heroDescription ??
202 - null;
203 -
204 235 (async () => {
205 236 const siteImages = await resolveSiteImages({
206 237 storedSiteImages,
207 238 siteProfile,
@@ -206,13 +237,8 @@
206 237 storedSiteImages,
207 238 siteProfile,
208 239 });
209 240
210 - const resolvedDescription =
211 - storedDescription || !siteProfile
212 - ? storedDescription
213 - : (await handleSiteStrings({ siteProfile })).heroDescription;
214 -
215 241 apiFetch({
216 242 path: '/extendify/v1/agent/site-design-variations',
217 243 method: 'POST',
218 244 data: {
@@ -219,9 +245,9 @@
219 245 title,
220 246 images: domImages,
221 247 siteImages,
222 248 postId: context?.postId,
223 - description: resolvedDescription,
249 + description,
224 250 currentHeroPattern: heroPatternName,
225 251 source: 'change-site-design-workflow',
226 252 cta: {
227 253 label: cta?.textContent,
@@ -265,12 +291,21 @@
265 291
266 292 const undoChanges = () => {
267 293 undoHeroSectionChange();
268 294 undoColorAndFontsChange();
295 + undoPaletteChange();
269 296 undoVibesChange();
270 297 removeInjectedLinks();
298 + refreshBlockHighlight();
271 299 };
272 300
301 + const confirmed = useRef(false);
302 + useEffect(() => {
303 + return () => {
304 + if (!confirmed.current) undoChanges();
305 + };
306 + }, []);
307 +
273 308 const handleCancel = () => {
274 309 undoChanges();
275 310 onCancel();
276 311 };
@@ -278,8 +313,9 @@
278 313 const handleConfirm = async () => {
279 314 if (!selectedHeroPattern) return;
280 315
281 316 if (selectedHeroPattern.isCurrent) {
317 + confirmed.current = true;
282 318 onConfirm({
283 319 data: { postId: context?.postId },
284 320 shouldRefreshPage: false,
285 321 });
@@ -314,14 +350,18 @@
314 350 return parse(selectedHeroPattern.code)?.[0] || block;
315 351 }),
316 352 );
317 353
354 + confirmed.current = true;
318 355 onConfirm({
319 356 data: {
320 357 updatedPageBlocks,
321 358 postId,
322 359 vibeSlug: selectedVibe.slug,
323 - colorAndFontsVariation: selectedColorAndFonts,
360 + colorAndFontsVariation: onPalettes
361 + ? fontsOnlyVariation(selectedColorAndFonts)
362 + : selectedColorAndFonts,
363 + colorPalette: selectedPalette?.slug,
324 364 },
325 365 shouldRefreshPage: true,
326 366 });
327 367 } catch (error) {
@@ -330,18 +370,18 @@
330 370 setIsSaving(false);
331 371 }
332 372 };
333 373
334 - if (isLoading || isLoadingVibes) {
374 + if (isLoading || isLoadingVibes || isLoadingPalettes) {
335 375 return (
336 - <div className="flex justify-center flex-col gap-1">
337 - <Spinner />
376 + <div className="min-h-24 p-2 text-center text-sm">
377 + {__('Loading design options...', 'extendify-local')}
338 378 </div>
339 379 );
340 380 }
341 381
342 382 return (
343 - <div className="mb-4 ms-12 me-2 flex flex-col rounded-lg border border-gray-300 bg-gray-50">
383 + <div className="mb-4 ms-2 me-2 flex flex-col rounded-lg border border-gray-300 bg-gray-50">
344 384 <div className="rounded-lg border-b border-gray-300 bg-white">
345 385 <div className="flex flex-col gap-4 p-3">
346 386 {currentHeroHtml && (
347 387 <DesignOption
@@ -351,8 +391,9 @@
351 391 onClick={() => {
352 392 setSelectedHeroPattern(currentDesignOption);
353 393 setSelectedColorAndFonts(null);
354 394 setSelectedVibe(null);
395 + setSelectedPalette(null);
355 396
356 397 undoChanges();
357 398 }}
358 399 />
@@ -364,11 +405,13 @@
364 405 isSelected={selectedHeroPattern?.id === heroPattern.id}
365 406 styles={{
366 407 linkStyles: heroPattern.linkStyles,
367 408 colorAndFontsVariations: colorAndFontsVariations[i].css,
368 - duotoneTheme:
369 - colorAndFontsVariations[i]?.settings?.color?.duotone?.theme,
370 - vibes: vibes[i]?.css,
409 + paletteCss: paletteCss?.[paletteFor(i)?.slug],
410 + duotoneTheme: onPalettes
411 + ? paletteDuotone(paletteFor(i))
412 + : colorAndFontsVariations[i]?.settings?.color?.duotone?.theme,
413 + vibes: vibes[i]?.previewCss,
371 414 blockSupportsCss: heroPattern.blockSupportsCss,
372 415 }}
373 416 onClick={() => {
374 417 setSelectedHeroPattern(heroPattern);
@@ -373,8 +416,9 @@
373 416 onClick={() => {
374 417 setSelectedHeroPattern(heroPattern);
375 418 setSelectedColorAndFonts(colorAndFontsVariations[i]);
376 419 setSelectedVibe(vibes[i]);
420 + setSelectedPalette(paletteFor(i));
377 421
378 422 if (!isAdmin) {
379 423 const blockSupportsCss = heroPattern.blockSupportsCss;
380 424 if (blockSupportsCss) {
@@ -389,8 +433,9 @@
389 433
390 434 injectLinkStyles(heroPattern.linkStyles);
391 435
392 436 updateHeroSection(heroPattern.renderedHtml);
437 + refreshBlockHighlight();
393 438 }
394 439 }}
395 440 />
396 441 ))}
@@ -437,13 +482,11 @@
437 482 )}
438 483 disabled={!selectedHeroPattern || isSaving}
439 484 onClick={handleConfirm}
440 485 >
441 - {isSaving ? (
442 - <Spinner className="m-0" />
443 - ) : (
444 - __('Save', 'extendify-local')
445 - )}
486 + {isSaving
487 + ? __('Saving...', 'extendify-local')
488 + : __('Save', 'extendify-local')}
446 489 </button>
447 490 </div>
448 491 </div>
449 492 );