mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-12 07:36:42 +08:00
feat: add system theme judgment
This commit is contained in:
@@ -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
@@ -1,7 +1,7 @@
|
||||
export type ThemeMode = 'light' | 'dark';
|
||||
|
||||
export type ThemeProviderProps = {
|
||||
defaultMode?: ThemeMode;
|
||||
defaultThemeMode?: ThemeMode;
|
||||
};
|
||||
|
||||
export type ThemeProviderValue = {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user