diff --git a/src/components/theme-customizer/index.tsx b/src/components/theme-customizer/index.tsx index cea7c44..7ba2550 100644 --- a/src/components/theme-customizer/index.tsx +++ b/src/components/theme-customizer/index.tsx @@ -35,17 +35,29 @@ export function ThemeCustomizer({ open, onOpenChange, initialPrefs }: ThemeCusto const [importModalOpen, setImportModalOpen] = React.useState(false) const [importedTheme, setImportedTheme] = React.useState(null) - // Save dark/light mode to Appwrite when it changes (skip first mount) - const themeMountRef = React.useRef(false) + // --- Appwrite persistence via useEffect (fires ONCE after React batches state updates) --- + + const darkModeSaveMountRef = React.useRef(false) React.useEffect(() => { - if (!themeMountRef.current) { themeMountRef.current = true; return } + if (!darkModeSaveMountRef.current) { darkModeSaveMountRef.current = true; return } void saveThemePrefsAction({ theme }) }, [theme]) - // Save sidebar config to Appwrite when it changes (skip first mount) - const sidebarMountRef = React.useRef(false) + const colorThemeSaveMountRef = React.useRef(false) React.useEffect(() => { - if (!sidebarMountRef.current) { sidebarMountRef.current = true; return } + if (!colorThemeSaveMountRef.current) { colorThemeSaveMountRef.current = true; return } + void saveThemePrefsAction({ colorTheme: selectedTheme, tweakcnTheme: selectedTweakcnTheme }) + }, [selectedTheme, selectedTweakcnTheme]) + + const radiusSaveMountRef = React.useRef(false) + React.useEffect(() => { + if (!radiusSaveMountRef.current) { radiusSaveMountRef.current = true; return } + void saveThemePrefsAction({ radius: selectedRadius }) + }, [selectedRadius]) + + const sidebarSaveMountRef = React.useRef(false) + React.useEffect(() => { + if (!sidebarSaveMountRef.current) { sidebarSaveMountRef.current = true; return } void saveThemePrefsAction({ sidebarVariant: sidebarConfig.variant, sidebarCollapsible: sidebarConfig.collapsible, @@ -53,6 +65,8 @@ export function ThemeCustomizer({ open, onOpenChange, initialPrefs }: ThemeCusto }) }, [sidebarConfig.variant, sidebarConfig.collapsible, sidebarConfig.side]) + // --- Theme reset --- + const handleReset = () => { setSelectedTheme("default") setSelectedTweakcnTheme("") @@ -61,7 +75,7 @@ export function ThemeCustomizer({ open, onOpenChange, initialPrefs }: ThemeCusto setBrandColorsValues({}) resetTheme() applyRadius("0.5rem") - void saveThemePrefsAction({ colorTheme: "default", tweakcnTheme: "", radius: "0.5rem" }) + // colorThemeSaveEffect and radiusSaveEffect will fire via the state changes above } const handleImport = (themeData: ImportedTheme) => { @@ -69,7 +83,7 @@ export function ThemeCustomizer({ open, onOpenChange, initialPrefs }: ThemeCusto setSelectedTheme("") setSelectedTweakcnTheme("") applyImportedTheme(themeData, isDarkMode) - // Imported themes have no stable ID to save + // Imported themes have no stable identifier to persist } // Re-apply when dark/light toggles @@ -84,24 +98,6 @@ export function ThemeCustomizer({ open, onOpenChange, initialPrefs }: ThemeCusto } }, [isDarkMode, importedTheme, selectedTheme, selectedTweakcnTheme, applyImportedTheme, applyTheme, applyTweakcnTheme]) - // Wrappers that also persist to Appwrite - const handleSetSelectedTheme = (value: string) => { - setSelectedTheme(value) - setSelectedTweakcnTheme("") - void saveThemePrefsAction({ colorTheme: value, tweakcnTheme: "" }) - } - - const handleSetSelectedTweakcnTheme = (value: string) => { - setSelectedTweakcnTheme(value) - setSelectedTheme("") - void saveThemePrefsAction({ tweakcnTheme: value, colorTheme: "" }) - } - - const handleSetSelectedRadius = (value: string) => { - setSelectedRadius(value) - void saveThemePrefsAction({ radius: value }) - } - return ( <> @@ -159,13 +155,16 @@ export function ThemeCustomizer({ open, onOpenChange, initialPrefs }: ThemeCusto + {/* Pass RAW state setters — ThemeTab handles cross-clearing itself. + Appwrite persistence is handled by the useEffects above, which fire + once after React batches all state updates in the event handler. */} setImportModalOpen(true)} />