From 6c5488be708347f7b340fcf75d36f6bd86e99e68 Mon Sep 17 00:00:00 2001 From: wonfen Date: Thu, 15 May 2025 16:51:35 +0800 Subject: [PATCH] fix: theme color not following system preference --- src/components/layout/use-custom-theme.ts | 127 +++++++++++++--------- 1 file changed, 75 insertions(+), 52 deletions(-) diff --git a/src/components/layout/use-custom-theme.ts b/src/components/layout/use-custom-theme.ts index 0b5925bc..721a9f62 100644 --- a/src/components/layout/use-custom-theme.ts +++ b/src/components/layout/use-custom-theme.ts @@ -29,7 +29,7 @@ const getLanguagePackMap = (key: string) => * custom theme */ export const useCustomTheme = () => { - const appWindow: WebviewWindow = getCurrentWebviewWindow(); + const appWindow: WebviewWindow = useMemo(() => getCurrentWebviewWindow(), []); const { verge } = useVerge(); const { i18n } = useTranslation(); const { theme_mode, theme_setting } = verge ?? {}; @@ -37,45 +37,71 @@ export const useCustomTheme = () => { const setMode = useSetThemeMode(); useEffect(() => { - const themeModeSetting = ["light", "dark", "system"].includes(theme_mode!) - ? theme_mode! - : "light"; + if (theme_mode === "light" || theme_mode === "dark") { + setMode(theme_mode); + } + }, [theme_mode, setMode]); - if (themeModeSetting !== "system") { - setMode(themeModeSetting as 'light' | 'dark'); + useEffect(() => { + if (theme_mode !== "system") { return; } - appWindow.theme().then((systemTheme: 'light' | 'dark' | null) => { - if (systemTheme) { - setMode(systemTheme); + let isMounted = true; + + const timerId = setTimeout(() => { + if (!isMounted) return; + appWindow.theme().then((systemTheme) => { + if (isMounted && systemTheme) { + setMode(systemTheme); + } + }).catch(err => { + console.error("Failed to get initial system theme:", err); + }); + }, 0); + + const unlistenPromise = appWindow.onThemeChanged(({ payload }) => { + if (isMounted) { + setMode(payload); } }); - const unlisten = appWindow.onThemeChanged(({ payload }: { payload: 'light' | 'dark' }) => { - setMode(payload); - }); return () => { - unlisten.then((fn: () => void) => fn()); + isMounted = false; + clearTimeout(timerId); + unlistenPromise.then((unlistenFn) => { + if (typeof unlistenFn === 'function') { + unlistenFn(); + } + }).catch(err => { + console.error("Failed to unlisten from theme changes:", err); + }); }; - }, [theme_mode, setMode, i18n, theme_setting, appWindow]); + }, [theme_mode, appWindow, setMode]); useEffect(() => { - if (mode) { - appWindow.setTheme(mode as TauriOsTheme).catch((err: any) => { - console.error("Failed to set window theme:", err); + if (theme_mode === undefined) { + return; + } + + if (theme_mode === "system") { + appWindow.setTheme(null).catch((err) => { + console.error("Failed to set window theme to follow system (setTheme(null)):", err); + }); + } else if (mode) { + appWindow.setTheme(mode as TauriOsTheme).catch((err) => { + console.error(`Failed to set window theme to ${mode}:`, err); }); } - }, [mode, appWindow]); + }, [mode, appWindow, theme_mode]); const theme = useMemo(() => { const setting = theme_setting || {}; const dt = mode === "light" ? defaultTheme : defaultDarkTheme; - - let theme: MuiTheme; + let muiTheme: MuiTheme; try { - theme = createTheme( + muiTheme = createTheme( { breakpoints: { values: { xs: 0, sm: 650, md: 900, lg: 1200, xl: 1536 }, @@ -98,7 +124,6 @@ export const useCustomTheme = () => { }, shadows: Array(25).fill("none") as Shadows, typography: { - // todo fontFamily: setting.font_family ? `${setting.font_family}, ${dt.font_family}` : dt.font_family, @@ -106,9 +131,9 @@ export const useCustomTheme = () => { }, getLanguagePackMap(i18n.language), ); - } catch { - // fix #294 - theme = createTheme({ + } catch (e) { + console.error("Error creating MUI theme, falling back to defaults:", e); + muiTheme = createTheme({ breakpoints: { values: { xs: 0, sm: 650, md: 900, lg: 1200, xl: 1536 }, }, @@ -126,38 +151,36 @@ export const useCustomTheme = () => { }); } - // css - const backgroundColor = mode === "light" ? "#ECECEC" : "#2e303d"; - const selectColor = mode === "light" ? "#f5f5f5" : "#d5d5d5"; - const scrollColor = mode === "light" ? "#90939980" : "#3E3E3Eee"; - const dividerColor = - mode === "light" ? "rgba(0, 0, 0, 0.06)" : "rgba(255, 255, 255, 0.06)"; - const rootEle = document.documentElement; - rootEle.style.setProperty("--divider-color", dividerColor); - rootEle.style.setProperty("--background-color", backgroundColor); - rootEle.style.setProperty("--selection-color", selectColor); - rootEle.style.setProperty("--scroller-color", scrollColor); - rootEle.style.setProperty("--primary-main", theme.palette.primary.main); - rootEle.style.setProperty( - "--background-color-alpha", - alpha(theme.palette.primary.main, 0.1), - ); + if (rootEle) { + const backgroundColor = mode === "light" ? "#ECECEC" : "#2e303d"; + const selectColor = mode === "light" ? "#f5f5f5" : "#d5d5d5"; + const scrollColor = mode === "light" ? "#90939980" : "#3E3E3Eee"; + const dividerColor = + mode === "light" ? "rgba(0, 0, 0, 0.06)" : "rgba(255, 255, 255, 0.06)"; + rootEle.style.setProperty("--divider-color", dividerColor); + rootEle.style.setProperty("--background-color", backgroundColor); + rootEle.style.setProperty("--selection-color", selectColor); + rootEle.style.setProperty("--scroller-color", scrollColor); + rootEle.style.setProperty("--primary-main", muiTheme.palette.primary.main); + rootEle.style.setProperty( + "--background-color-alpha", + alpha(muiTheme.palette.primary.main, 0.1), + ); + } // inject css - let style = document.querySelector("style#verge-theme"); - if (!style) { - style = document.createElement("style"); - style.id = "verge-theme"; - document.head.appendChild(style!); + let styleElement = document.querySelector("style#verge-theme"); + if (!styleElement) { + styleElement = document.createElement("style"); + styleElement.id = "verge-theme"; + document.head.appendChild(styleElement!); } - if (style) { - style.innerHTML = setting.css_injection || ""; + if (styleElement) { + styleElement.innerHTML = setting.css_injection || ""; } - // update svg icon - const { palette } = theme; - + const { palette } = muiTheme; setTimeout(() => { const dom = document.querySelector("#Gradient2"); if (dom) { @@ -169,7 +192,7 @@ export const useCustomTheme = () => { } }, 0); - return theme; + return muiTheme; }, [mode, theme_setting, i18n.language]); return { theme };