From 67a2310ea41eb8605453164820f5d8fca032900b Mon Sep 17 00:00:00 2001 From: euvre <93761161+euvre@users.noreply.github.com> Date: Mon, 20 Jul 2026 15:46:47 +0800 Subject: [PATCH] fix: prevent share pages from overriding the user's theme preference (#17098) --- web/src/components/theme-provider.tsx | 41 ++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/web/src/components/theme-provider.tsx b/web/src/components/theme-provider.tsx index 286fb41ee1..2206d0b030 100644 --- a/web/src/components/theme-provider.tsx +++ b/web/src/components/theme-provider.tsx @@ -1,5 +1,12 @@ import { ThemeEnum } from '@/constants/common'; -import React, { createContext, useContext, useEffect, useState } from 'react'; +import React, { + createContext, + useCallback, + useContext, + useEffect, + useRef, + useState, +} from 'react'; type ThemeProviderProps = { children: React.ReactNode; @@ -9,7 +16,7 @@ type ThemeProviderProps = { type ThemeProviderState = { theme: ThemeEnum; - setTheme: (theme: ThemeEnum) => void; + setTheme: (theme: ThemeEnum, persist?: boolean) => void; }; const initialState: ThemeProviderState = { @@ -25,14 +32,22 @@ export function ThemeProvider({ storageKey = 'vite-ui-theme', ...props }: ThemeProviderProps) { - const [theme, setTheme] = useState( + const [theme, setThemeState] = useState( () => (localStorage.getItem(storageKey) as ThemeEnum) || defaultTheme, ); + const persistRef = useRef(true); + + const setTheme = useCallback((nextTheme: ThemeEnum, persist = true) => { + persistRef.current = persist; + setThemeState(nextTheme); + }, []); useEffect(() => { const root = window.document.documentElement; root.classList.remove(ThemeEnum.Light, ThemeEnum.Dark); - localStorage.setItem(storageKey, theme); + if (persistRef.current) { + localStorage.setItem(storageKey, theme); + } root.classList.add(theme); }, [storageKey, theme]); @@ -73,11 +88,23 @@ export function useSwitchToDarkThemeOnMount() { } export function useSyncThemeFromParams(theme: string | null) { - const { setTheme } = useTheme(); + const { theme: contextTheme, setTheme } = useTheme(); + const originalThemeRef = useRef(null); useEffect(() => { if (theme && (theme === ThemeEnum.Light || theme === ThemeEnum.Dark)) { - setTheme(theme as ThemeEnum); + if (originalThemeRef.current === null) { + originalThemeRef.current = contextTheme; + } + setTheme(theme as ThemeEnum, false); } - }, [theme, setTheme]); + }, [theme, contextTheme, setTheme]); + + useEffect(() => { + return () => { + if (originalThemeRef.current !== null) { + setTheme(originalThemeRef.current, false); + } + }; + }, [setTheme]); }