From aa1de57d96df0a78ab95cb6ad54928705f72dd94 Mon Sep 17 00:00:00 2001 From: Himself65 Date: Sun, 19 Feb 2023 02:38:43 -0600 Subject: [PATCH] feat: read local theme on page load (#1114) Co-authored-by: zqran --- apps/web/src/providers/ThemeProvider.tsx | 55 ++++++++++++++----- .../styles/utils/localStorageThemeHelper.ts | 2 + 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/apps/web/src/providers/ThemeProvider.tsx b/apps/web/src/providers/ThemeProvider.tsx index e4568fe66b..e875e79418 100644 --- a/apps/web/src/providers/ThemeProvider.tsx +++ b/apps/web/src/providers/ThemeProvider.tsx @@ -1,5 +1,4 @@ import { - Theme, ThemeMode, ThemeProviderProps, ThemeProviderValue, @@ -17,7 +16,15 @@ import { ThemeProvider as MuiThemeProvider, } from '@mui/material/styles'; import type { PropsWithChildren } from 'react'; -import { createContext, useContext, useEffect, useState } from 'react'; +import { + createContext, + useCallback, + useContext, + useEffect, + useMemo, + useState, + useSyncExternalStore, +} from 'react'; import useCurrentPageMeta from '@/hooks/use-current-page-meta'; @@ -35,16 +42,31 @@ export const ThemeProvider = ({ defaultTheme = 'light', children, }: PropsWithChildren) => { - const [theme, setTheme] = useState(defaultTheme); - const [mode, setMode] = useState('auto'); + const localStorageThemeMode = useSyncExternalStore( + useCallback(cb => { + localStorageThemeHelper.callback.add(cb); + return () => { + localStorageThemeHelper.callback.delete(cb); + }; + }, []), + useCallback(() => localStorageThemeHelper.get() ?? 'light', []), + useCallback(() => defaultTheme, [defaultTheme]) + ); + const [mode, setMode] = useState(defaultTheme); + if (localStorageThemeMode !== mode) { + setMode(localStorageThemeMode); + } const { mode: editorMode = 'page' } = useCurrentPageMeta() || {}; const themeStyle = - theme === 'light' ? getLightTheme(editorMode) : getDarkTheme(editorMode); - const changeMode = (themeMode: ThemeMode) => { - themeMode !== mode && setMode(themeMode); - // Remember the theme mode which user selected for next time - localStorageThemeHelper.set(themeMode); - }; + mode === 'light' ? getLightTheme(editorMode) : getDarkTheme(editorMode); + const changeMode = useCallback( + (themeMode: ThemeMode) => { + themeMode !== mode && setMode(themeMode); + // Remember the theme mode which user selected for next time + localStorageThemeHelper.set(themeMode); + }, + [mode] + ); // ===================== A temporary solution, just use system theme and not remember the user selected ==================== useEffect(() => { @@ -57,9 +79,9 @@ export const ThemeProvider = ({ }); }, []); - useEffect(() => { - setTheme(mode === 'auto' ? theme : mode); - }, [mode, setTheme, theme]); + // useEffect(() => { + // setTheme(mode === 'auto' ? theme : mode); + // }, [mode, setTheme, theme]); // ===================== ==================== // useEffect(() => { @@ -93,7 +115,12 @@ export const ThemeProvider = ({ return ( // Use MuiThemeProvider is just because some Transitions in Mui components need it - + ({ mode, changeMode, theme: themeStyle }), + [changeMode, mode, themeStyle] + )} + > void>(); get = (): ThemeMode | null => { return localStorage.getItem(this.name) as ThemeMode | null; }; set = (mode: ThemeMode) => { localStorage.setItem(this.name, mode); + this.callback.forEach(cb => cb()); }; }