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