feat: add system theme judgment

This commit is contained in:
QiShaoXuan
2022-09-27 17:19:23 +08:00
parent 163f6cea08
commit fb828c9cf4
3 changed files with 76 additions and 4 deletions
+36 -3
View File
@@ -3,10 +3,11 @@ import {
Global,
css,
} from '@emotion/react';
import { createContext, useState } from 'react';
import { createContext, useEffect, useRef, useState } from 'react';
import type { PropsWithChildren } from 'react';
import { ThemeMode, ThemeProviderProps, ThemeProviderValue } from './types';
import { lightTheme, darkTheme, globalThemeConstant } from './theme';
import { SystemTheme, LocalStorageThemeMode } from '@/styles/utils';
export const ThemeContext = createContext<ThemeProviderValue>({
mode: 'light',
@@ -15,15 +16,47 @@ export const ThemeContext = createContext<ThemeProviderValue>({
});
export const ThemeProvider = ({
defaultMode = 'light',
defaultThemeMode = 'light',
children,
}: PropsWithChildren<ThemeProviderProps>) => {
const [mode, setMode] = useState<ThemeMode>(defaultMode);
const { current: localStorageThemeMode } = useRef(
new LocalStorageThemeMode()
);
const [mode, setMode] = useState<ThemeMode>(defaultThemeMode);
const theme = mode === 'dark' ? darkTheme : lightTheme;
const changeMode = (themeMode: ThemeMode) => {
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);
} else {
// If user has not selected a theme mode, use system theme
const systemThemeMode = systemTheme.get();
defaultThemeMode !== systemThemeMode && setMode(systemThemeMode);
}
// 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());
});
return () => {
systemTheme.dispose();
};
}, []);
return (
<ThemeContext.Provider value={{ mode, changeMode, theme }}>
<Global
+1 -1
View File
@@ -1,7 +1,7 @@
export type ThemeMode = 'light' | 'dark';
export type ThemeProviderProps = {
defaultMode?: ThemeMode;
defaultThemeMode?: ThemeMode;
};
export type ThemeProviderValue = {
+39
View File
@@ -0,0 +1,39 @@
import { ThemeMode } from '@/styles/types';
export class SystemTheme {
media: MediaQueryList = window.matchMedia('(prefers-color-scheme: light)');
eventList: Array<(e: Event) => void> = [];
eventHandler = (e: Event) => {
this.eventList.forEach(fn => fn(e));
};
constructor() {
this.media.addEventListener('change', this.eventHandler);
}
get = (): ThemeMode => {
if (typeof window === 'undefined') {
return 'light';
}
return this.media.matches ? 'light' : 'dark';
};
onChange = (callback: () => void) => {
this.eventList.push(callback);
};
dispose = () => {
this.eventList = [];
this.media.removeEventListener('change', this.eventHandler);
};
}
export class LocalStorageThemeMode {
name = 'Affine-theme-mode';
get = (): ThemeMode | null => {
return localStorage.getItem(this.name) as ThemeMode | null;
};
set = (mode: ThemeMode) => {
localStorage.setItem(this.name, mode);
};
}