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 +95 -33 3.1.0 → 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,8 +62,31 @@
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 +
68 +const resolveSiteImages = async ({ storedSiteImages, siteProfile }) => {
69 + const stored = normalizeSiteImages(storedSiteImages);
70 + if (hasImages(stored)) return stored;
71 +
72 + // A browser that didn't run Launch otherwise reuses one image on every option.
73 + const localized = normalizeSiteImages(window.extSharedData.siteImages);
74 + if (hasImages(localized)) return localized;
75 +
76 + if (siteProfile) {
77 + const { siteImages } = await handleSiteImages({ siteProfile });
78 + const fetched = normalizeSiteImages(siteImages);
79 + if (hasImages(fetched)) return fetched;
80 + }
81 +
82 + const saved = await apiFetch({
83 + path: '/extendify/v1/shared/site-images',
84 + }).catch(() => null);
85 +
86 + return normalizeSiteImages(saved?.siteImages);
87 +};
88 +
61 89 export const SelectSiteDesign = ({ onConfirm, onCancel }) => {
62 90 const [visibleCount, setVisibleCount] = useState(PAGE_SIZE);
63 91 const [isLoading, setIsLoading] = useState(false);
64 92 const [isSaving, setIsSaving] = useState(false);
@@ -71,8 +99,9 @@
71 99
72 100 const [selectedColorAndFonts, setSelectedColorAndFonts] = useState();
73 101 const [selectedHeroPattern, setSelectedHeroPattern] = useState();
74 102 const [selectedVibe, setSelectedVibe] = useState();
103 + const [selectedPalette, setSelectedPalette] = useState();
75 104
76 105 const { updateSettings } = useDispatch('core/block-editor');
77 106
78 107 const injectedLinksRef = useRef([]);
@@ -99,14 +128,35 @@
99 128 });
100 129 injectedLinksRef.current = [];
101 130 };
102 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 +
103 146 const { undoChange: undoColorAndFontsChange } = useVariationOverride({
104 147 css: !isAdmin && selectedColorAndFonts?.css,
105 148 duotoneTheme:
106 - !isAdmin && selectedColorAndFonts?.settings?.color?.duotone?.theme,
149 + !isAdmin &&
150 + !onPalettes &&
151 + selectedColorAndFonts?.settings?.color?.duotone?.theme,
107 152 });
108 153
154 + const { undoChange: undoPaletteChange } = usePaletteOverride({
155 + css: (!isAdmin && paletteCss?.[selectedPalette?.slug]) || '',
156 + duotoneTheme: (!isAdmin && paletteDuotone(selectedPalette)) || null,
157 + });
158 +
109 159 const { undoChange: undoVibesChange } = useSiteVibesOverride({
110 160 css: !isAdmin && selectedVibe?.css,
111 161 slug: !isAdmin && selectedVibe?.slug,
112 162 });
@@ -114,9 +164,10 @@
114 164 const { data: vibesData, isLoading: isLoadingVibes } =
115 165 useSiteVibesVariations();
116 166
117 167 const vibes = useMemo(() => {
118 - if (isLoadingVibes) return null;
168 + // Null for a theme with no block variations, which .css would throw on.
169 + if (isLoadingVibes || !vibesData?.css) return [];
119 170
120 171 const vibes = Object.entries(vibesData.css)
121 172 .filter(([slug]) => slug !== vibesData.currentVibe)
122 173 .map(([slug, css]) => ({
@@ -121,8 +172,13 @@
121 172 .filter(([slug]) => slug !== vibesData.currentVibe)
122 173 .map(([slug, css]) => ({
123 174 slug,
124 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 + ),
125 181 }))
126 182 .sort(() => Math.random() - 0.5);
127 183
128 184 return [...vibes, ...vibes.slice(0, 3)];
@@ -175,25 +231,14 @@
175 231 const storedSiteImages =
176 232 stored?.state?.siteImages ?? launchState?.siteImages?.siteImages ?? [];
177 233 const siteProfile = stored?.state?.siteProfile ?? launchState?.siteProfile;
178 234
179 - const storedDescription =
180 - description ??
181 - stored?.state?.heroDescription ??
182 - launchState?.siteStrings?.heroDescription ??
183 - null;
184 -
185 235 (async () => {
186 - const siteImages =
187 - storedSiteImages.length > 0 || !siteProfile
188 - ? storedSiteImages
189 - : (await handleSiteImages({ siteProfile })).siteImages;
236 + const siteImages = await resolveSiteImages({
237 + storedSiteImages,
238 + siteProfile,
239 + });
190 240
191 - const resolvedDescription =
192 - storedDescription || !siteProfile
193 - ? storedDescription
194 - : (await handleSiteStrings({ siteProfile })).heroDescription;
195 -
196 241 apiFetch({
197 242 path: '/extendify/v1/agent/site-design-variations',
198 243 method: 'POST',
199 244 data: {
@@ -200,9 +245,9 @@
200 245 title,
201 246 images: domImages,
202 247 siteImages,
203 248 postId: context?.postId,
204 - description: resolvedDescription,
249 + description,
205 250 currentHeroPattern: heroPatternName,
206 251 source: 'change-site-design-workflow',
207 252 cta: {
208 253 label: cta?.textContent,
@@ -246,12 +291,21 @@
246 291
247 292 const undoChanges = () => {
248 293 undoHeroSectionChange();
249 294 undoColorAndFontsChange();
295 + undoPaletteChange();
250 296 undoVibesChange();
251 297 removeInjectedLinks();
298 + refreshBlockHighlight();
252 299 };
253 300
301 + const confirmed = useRef(false);
302 + useEffect(() => {
303 + return () => {
304 + if (!confirmed.current) undoChanges();
305 + };
306 + }, []);
307 +
254 308 const handleCancel = () => {
255 309 undoChanges();
256 310 onCancel();
257 311 };
@@ -259,8 +313,9 @@
259 313 const handleConfirm = async () => {
260 314 if (!selectedHeroPattern) return;
261 315
262 316 if (selectedHeroPattern.isCurrent) {
317 + confirmed.current = true;
263 318 onConfirm({
264 319 data: { postId: context?.postId },
265 320 shouldRefreshPage: false,
266 321 });
@@ -295,14 +350,18 @@
295 350 return parse(selectedHeroPattern.code)?.[0] || block;
296 351 }),
297 352 );
298 353
354 + confirmed.current = true;
299 355 onConfirm({
300 356 data: {
301 357 updatedPageBlocks,
302 358 postId,
303 359 vibeSlug: selectedVibe.slug,
304 - colorAndFontsVariation: selectedColorAndFonts,
360 + colorAndFontsVariation: onPalettes
361 + ? fontsOnlyVariation(selectedColorAndFonts)
362 + : selectedColorAndFonts,
363 + colorPalette: selectedPalette?.slug,
305 364 },
306 365 shouldRefreshPage: true,
307 366 });
308 367 } catch (error) {
@@ -311,18 +370,18 @@
311 370 setIsSaving(false);
312 371 }
313 372 };
314 373
315 - if (isLoading || isLoadingVibes) {
374 + if (isLoading || isLoadingVibes || isLoadingPalettes) {
316 375 return (
317 - <div className="flex justify-center flex-col gap-1">
318 - <Spinner />
376 + <div className="min-h-24 p-2 text-center text-sm">
377 + {__('Loading design options...', 'extendify-local')}
319 378 </div>
320 379 );
321 380 }
322 381
323 382 return (
324 - <div className="mb-4 ml-10 mr-2 flex flex-col rounded-lg border border-gray-300 bg-gray-50 rtl:ml-2 rtl:mr-10">
383 + <div className="mb-4 ms-2 me-2 flex flex-col rounded-lg border border-gray-300 bg-gray-50">
325 384 <div className="rounded-lg border-b border-gray-300 bg-white">
326 385 <div className="flex flex-col gap-4 p-3">
327 386 {currentHeroHtml && (
328 387 <DesignOption
@@ -332,8 +391,9 @@
332 391 onClick={() => {
333 392 setSelectedHeroPattern(currentDesignOption);
334 393 setSelectedColorAndFonts(null);
335 394 setSelectedVibe(null);
395 + setSelectedPalette(null);
336 396
337 397 undoChanges();
338 398 }}
339 399 />
@@ -345,11 +405,13 @@
345 405 isSelected={selectedHeroPattern?.id === heroPattern.id}
346 406 styles={{
347 407 linkStyles: heroPattern.linkStyles,
348 408 colorAndFontsVariations: colorAndFontsVariations[i].css,
349 - duotoneTheme:
350 - colorAndFontsVariations[i]?.settings?.color?.duotone?.theme,
351 - 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,
352 414 blockSupportsCss: heroPattern.blockSupportsCss,
353 415 }}
354 416 onClick={() => {
355 417 setSelectedHeroPattern(heroPattern);
@@ -354,8 +416,9 @@
354 416 onClick={() => {
355 417 setSelectedHeroPattern(heroPattern);
356 418 setSelectedColorAndFonts(colorAndFontsVariations[i]);
357 419 setSelectedVibe(vibes[i]);
420 + setSelectedPalette(paletteFor(i));
358 421
359 422 if (!isAdmin) {
360 423 const blockSupportsCss = heroPattern.blockSupportsCss;
361 424 if (blockSupportsCss) {
@@ -370,8 +433,9 @@
370 433
371 434 injectLinkStyles(heroPattern.linkStyles);
372 435
373 436 updateHeroSection(heroPattern.renderedHtml);
437 + refreshBlockHighlight();
374 438 }
375 439 }}
376 440 />
377 441 ))}
@@ -418,13 +482,11 @@
418 482 )}
419 483 disabled={!selectedHeroPattern || isSaving}
420 484 onClick={handleConfirm}
421 485 >
422 - {isSaving ? (
423 - <Spinner className="m-0" />
424 - ) : (
425 - __('Save', 'extendify-local')
426 - )}
486 + {isSaving
487 + ? __('Saving...', 'extendify-local')
488 + : __('Save', 'extendify-local')}
427 489 </button>
428 490 </div>
429 491 </div>
430 492 );