From 008c0d83e350ec77f231c3d8c6cf0e276ddf5323 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Wed, 28 Sep 2022 10:49:02 +0800 Subject: [PATCH] feat: add auto mode in theme selection --- src/pages/index.tsx | 7 ++++++ src/styles/themeProvider.tsx | 49 ++++++++++++++++++------------------ src/styles/types.ts | 5 ++-- src/styles/utils.ts | 6 +++-- 4 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 66d8048e7e..031ef59d08 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -21,6 +21,13 @@ const Home: NextPage = () => { > current theme mode :{mode}(click to change) + ); }; diff --git a/src/styles/themeProvider.tsx b/src/styles/themeProvider.tsx index 27f94ca682..1aa488e380 100644 --- a/src/styles/themeProvider.tsx +++ b/src/styles/themeProvider.tsx @@ -3,11 +3,16 @@ import { Global, css, } from '@emotion/react'; -import { createContext, useEffect, useRef, useState } from 'react'; +import { createContext, useEffect, useState } from 'react'; import type { PropsWithChildren } from 'react'; -import { ThemeMode, ThemeProviderProps, ThemeProviderValue } from './types'; +import { + Theme, + ThemeMode, + ThemeProviderProps, + ThemeProviderValue, +} from './types'; import { lightTheme, darkTheme, globalThemeConstant } from './theme'; -import { SystemTheme, LocalStorageThemeMode } from '@/styles/utils'; +import { SystemTheme, localStorageThemeMode } from '@/styles/utils'; export const ThemeContext = createContext({ mode: 'light', @@ -16,57 +21,53 @@ export const ThemeContext = createContext({ }); export const ThemeProvider = ({ - defaultThemeMode = 'light', + defaultTheme = 'light', children, }: PropsWithChildren) => { - const { current: localStorageThemeMode } = useRef( - new LocalStorageThemeMode() - ); - - const [mode, setMode] = useState(defaultThemeMode); - const theme = mode === 'dark' ? darkTheme : lightTheme; + const [theme, setTheme] = useState(defaultTheme); + const [mode, setMode] = useState('auto'); + const themeStyle = theme === 'light' ? lightTheme : darkTheme; const changeMode = (themeMode: ThemeMode) => { - setMode(themeMode); + themeMode !== mode && setMode(themeMode); // Remember the theme mode which user selected for next time localStorageThemeMode.set(themeMode); }; useEffect(() => { const systemTheme = new SystemTheme(); - - // TODO: System theme mode and user theme mode which selected need to be prioritized - // If user has selected a theme mode, use it const selectedThemeMode = localStorageThemeMode.get(); - if (selectedThemeMode) { - defaultThemeMode !== selectedThemeMode && setMode(selectedThemeMode); + + const themeMode = selectedThemeMode || mode; + if (themeMode === 'auto') { + setTheme(systemTheme.get()); } else { - // If user has not selected a theme mode, use system theme - const systemThemeMode = systemTheme.get(); - defaultThemeMode !== systemThemeMode && setMode(systemThemeMode); + setTheme(themeMode); } // When system theme changed, change the theme mode systemTheme.onChange(() => { // TODO: There may be should be provided a way to let user choose whether to - setMode(systemTheme.get()); + if (mode === 'auto') { + setTheme(systemTheme.get()); + } }); return () => { systemTheme.dispose(); }; - }, []); + }, [mode]); return ( - + - {children} + {children} ); }; diff --git a/src/styles/types.ts b/src/styles/types.ts index ccfb39290a..95c5bba951 100644 --- a/src/styles/types.ts +++ b/src/styles/types.ts @@ -1,7 +1,8 @@ -export type ThemeMode = 'light' | 'dark'; +export type Theme = 'light' | 'dark'; +export type ThemeMode = Theme | 'auto'; export type ThemeProviderProps = { - defaultThemeMode?: ThemeMode; + defaultTheme?: Theme; }; export type ThemeProviderValue = { diff --git a/src/styles/utils.ts b/src/styles/utils.ts index bdc0ef8f18..dd04d4fb0b 100644 --- a/src/styles/utils.ts +++ b/src/styles/utils.ts @@ -1,4 +1,4 @@ -import { ThemeMode } from '@/styles/types'; +import { Theme, ThemeMode } from '@/styles/types'; export class SystemTheme { media: MediaQueryList = window.matchMedia('(prefers-color-scheme: light)'); @@ -11,7 +11,7 @@ export class SystemTheme { this.media.addEventListener('change', this.eventHandler); } - get = (): ThemeMode => { + get = (): Theme => { if (typeof window === 'undefined') { return 'light'; } @@ -37,3 +37,5 @@ export class LocalStorageThemeMode { localStorage.setItem(this.name, mode); }; } + +export const localStorageThemeMode = new LocalStorageThemeMode();