feat: read local theme on page load (#1114)

Co-authored-by: zqran <uuxnet@gmail.com>
This commit is contained in:
Himself65
2023-02-19 16:38:43 +08:00
committed by GitHub
co-authored by zqran
parent 736fbff41a
commit aa1de57d96
2 changed files with 43 additions and 14 deletions
+41 -14
View File
@@ -1,5 +1,4 @@
import { import {
Theme,
ThemeMode, ThemeMode,
ThemeProviderProps, ThemeProviderProps,
ThemeProviderValue, ThemeProviderValue,
@@ -17,7 +16,15 @@ import {
ThemeProvider as MuiThemeProvider, ThemeProvider as MuiThemeProvider,
} from '@mui/material/styles'; } from '@mui/material/styles';
import type { PropsWithChildren } from 'react'; 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'; import useCurrentPageMeta from '@/hooks/use-current-page-meta';
@@ -35,16 +42,31 @@ export const ThemeProvider = ({
defaultTheme = 'light', defaultTheme = 'light',
children, children,
}: PropsWithChildren<ThemeProviderProps>) => { }: PropsWithChildren<ThemeProviderProps>) => {
const [theme, setTheme] = useState<Theme>(defaultTheme); const localStorageThemeMode = useSyncExternalStore<ThemeMode>(
const [mode, setMode] = useState<ThemeMode>('auto'); useCallback(cb => {
localStorageThemeHelper.callback.add(cb);
return () => {
localStorageThemeHelper.callback.delete(cb);
};
}, []),
useCallback(() => localStorageThemeHelper.get() ?? 'light', []),
useCallback(() => defaultTheme, [defaultTheme])
);
const [mode, setMode] = useState<ThemeMode>(defaultTheme);
if (localStorageThemeMode !== mode) {
setMode(localStorageThemeMode);
}
const { mode: editorMode = 'page' } = useCurrentPageMeta() || {}; const { mode: editorMode = 'page' } = useCurrentPageMeta() || {};
const themeStyle = const themeStyle =
theme === 'light' ? getLightTheme(editorMode) : getDarkTheme(editorMode); mode === 'light' ? getLightTheme(editorMode) : getDarkTheme(editorMode);
const changeMode = (themeMode: ThemeMode) => { const changeMode = useCallback(
themeMode !== mode && setMode(themeMode); (themeMode: ThemeMode) => {
// Remember the theme mode which user selected for next time themeMode !== mode && setMode(themeMode);
localStorageThemeHelper.set(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 ==================== // ===================== A temporary solution, just use system theme and not remember the user selected ====================
useEffect(() => { useEffect(() => {
@@ -57,9 +79,9 @@ export const ThemeProvider = ({
}); });
}, []); }, []);
useEffect(() => { // useEffect(() => {
setTheme(mode === 'auto' ? theme : mode); // setTheme(mode === 'auto' ? theme : mode);
}, [mode, setTheme, theme]); // }, [mode, setTheme, theme]);
// ===================== ==================== // ===================== ====================
// useEffect(() => { // useEffect(() => {
@@ -93,7 +115,12 @@ export const ThemeProvider = ({
return ( return (
// Use MuiThemeProvider is just because some Transitions in Mui components need it // Use MuiThemeProvider is just because some Transitions in Mui components need it
<MuiThemeProvider theme={muiTheme}> <MuiThemeProvider theme={muiTheme}>
<ThemeContext.Provider value={{ mode, changeMode, theme: themeStyle }}> <ThemeContext.Provider
value={useMemo(
() => ({ mode, changeMode, theme: themeStyle }),
[changeMode, mode, themeStyle]
)}
>
<Global <Global
styles={css` styles={css`
:root { :root {
@@ -2,11 +2,13 @@ import { ThemeMode } from '../types';
export class LocalStorageThemeHelper { export class LocalStorageThemeHelper {
name = 'Affine-theme-mode'; name = 'Affine-theme-mode';
callback = new Set<() => void>();
get = (): ThemeMode | null => { get = (): ThemeMode | null => {
return localStorage.getItem(this.name) as ThemeMode | null; return localStorage.getItem(this.name) as ThemeMode | null;
}; };
set = (mode: ThemeMode) => { set = (mode: ThemeMode) => {
localStorage.setItem(this.name, mode); localStorage.setItem(this.name, mode);
this.callback.forEach(cb => cb());
}; };
} }