fix: tema kaydetme race condition düzeltildi
Önceki hata: ThemeTab her iki setter'ı da çağırıyordu (setSelectedTheme + setSelectedTweakcnTheme). Bunlar wrapper'a bağlıydı, her wrapper kendi saveThemePrefsAction'ını çağırıyordu. İkinci çağrı colorTheme:'' yazarak birincinin kaydını siliyordu. Düzeltme: - ThemeTab'a RAW React state setter'ları iletildi (wrapper değil) - ThemeTab'ın cross-clear mantığı olduğu gibi kaldı - Appwrite kaydı useEffect'e taşındı: React 18 olay yöneticisindeki tüm state güncellemelerini batch'ledikten SONRA tek seferde tetiklenir → selectedTheme ve selectedTweakcnTheme doğru nihai değerleriyle kaydedilir
This commit is contained in:
@@ -35,17 +35,29 @@ export function ThemeCustomizer({ open, onOpenChange, initialPrefs }: ThemeCusto
|
||||
const [importModalOpen, setImportModalOpen] = React.useState(false)
|
||||
const [importedTheme, setImportedTheme] = React.useState<ImportedTheme | null>(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 (
|
||||
<>
|
||||
<Sheet open={open} onOpenChange={onOpenChange} modal={false}>
|
||||
@@ -159,13 +155,16 @@ export function ThemeCustomizer({ open, onOpenChange, initialPrefs }: ThemeCusto
|
||||
</div>
|
||||
|
||||
<TabsContent value="theme" className="flex-1 mt-0">
|
||||
{/* 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. */}
|
||||
<ThemeTab
|
||||
selectedTheme={selectedTheme}
|
||||
setSelectedTheme={handleSetSelectedTheme}
|
||||
setSelectedTheme={setSelectedTheme}
|
||||
selectedTweakcnTheme={selectedTweakcnTheme}
|
||||
setSelectedTweakcnTheme={handleSetSelectedTweakcnTheme}
|
||||
setSelectedTweakcnTheme={setSelectedTweakcnTheme}
|
||||
selectedRadius={selectedRadius}
|
||||
setSelectedRadius={handleSetSelectedRadius}
|
||||
setSelectedRadius={setSelectedRadius}
|
||||
setImportedTheme={setImportedTheme}
|
||||
onImportClick={() => setImportModalOpen(true)}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user