import { usePaletteOverride } from '@agent/hooks/usePaletteOverride'; import { usePalettes } from '@agent/hooks/usePalettes'; import { useThemeVariations } from '@agent/hooks/useThemeVariations'; import { useVariationOverride } from '@agent/hooks/useVariationOverride'; import { useChatStore } from '@agent/state/chat'; import { paletteDuotone } from '@shared/lib/palette-preview'; import { samplePalettes } from '@shared/lib/palettes'; import { useEffect, useMemo, useRef, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; const VISIBLE_LIMIT = 8; export const SelectThemeVariation = ({ onConfirm, onCancel }) => { const [css, setCss] = useState(''); const [selected, setSelected] = useState(null); const [duotoneTheme, setDuotoneTheme] = useState(null); const { palettes, css: paletteCss, preferred, isLoading: palettesLoading, } = usePalettes(); const onPalettes = Boolean(palettes?.length); const { variations, isLoading: variationsLoading } = useThemeVariations({ enabled: !palettesLoading && !onPalettes, }); const isLoading = palettesLoading || variationsLoading; // A palette layers over core's global styles; a variation replaces them. const { undoChange: undoPalette } = usePaletteOverride({ css: onPalettes ? css : '', duotoneTheme: onPalettes ? duotoneTheme : null, }); const { undoChange: undoVariation } = useVariationOverride({ css: onPalettes ? '' : css, duotoneTheme: onPalettes ? null : duotoneTheme, }); const undoChange = onPalettes ? undoPalette : undoVariation; const confirmed = useRef(false); const undo = useRef(undoChange); useEffect(() => { undo.current = undoChange; }, [undoChange]); useEffect(() => { return () => { if (!confirmed.current) undo.current(); }; }, []); const { addMessage, messages } = useChatStore(); // Drawn per mount, so the immutable SWR cache cannot freeze one set. const shown = useMemo( () => samplePalettes(palettes, preferred, VISIBLE_LIMIT), [palettes, preferred], ); const options = useMemo( () => onPalettes ? paletteOptions(shown, paletteCss) : themeOptions(variations), [onPalettes, shown, paletteCss, variations], ); const noOptions = options.length === 0; const handleConfirm = () => { if (!selected) return; const option = options.find(({ key }) => key === selected); if (!option) { // translators: A chat message shown to the user when their selected color variation cannot be applied const content = __( 'We were unable to apply your selected colors. Please try again.', 'extendify-local', ); addMessage('message', { role: 'assistant', content, error: true }); onCancel(); return; } confirmed.current = true; onConfirm({ data: option.data, shouldRefreshPage: true }); }; useEffect(() => { if (isLoading || !noOptions) return; const timer = setTimeout(() => onCancel(), 100); // translators: A chat message shown to the user const content = __( 'We were unable to find any colors for your theme', 'extendify-local', ); const last = messages.at(-1)?.details?.content; if (content === last) return () => clearTimeout(timer); addMessage('message', { role: 'assistant', content, error: true }); return () => clearTimeout(timer); }, [addMessage, onCancel, noOptions, messages, isLoading]); if (isLoading) { return (