forked from akai/readest
86 lines
2.7 KiB
TypeScript
86 lines
2.7 KiB
TypeScript
import { create } from 'zustand';
|
|
import { getThemeCode, ThemeCode } from '@/utils/style';
|
|
import { Palette, ThemeMode } from '@/styles/themes';
|
|
import { isWebAppPlatform } from '@/services/environment';
|
|
|
|
interface ThemeState {
|
|
themeMode: ThemeMode;
|
|
themeColor: string;
|
|
systemIsDarkMode: boolean;
|
|
themeCode: ThemeCode;
|
|
isDarkMode: boolean;
|
|
setThemeMode: (mode: ThemeMode) => void;
|
|
setThemeColor: (color: string) => void;
|
|
updateAppTheme: (color: keyof Palette) => void;
|
|
}
|
|
|
|
const getInitialThemeMode = (): ThemeMode => {
|
|
if (typeof window !== 'undefined' && localStorage) {
|
|
return (localStorage.getItem('themeMode') as ThemeMode) || 'auto';
|
|
}
|
|
return 'auto';
|
|
};
|
|
|
|
const getInitialThemeColor = (): string => {
|
|
if (typeof window !== 'undefined' && localStorage) {
|
|
return localStorage.getItem('themeColor') || 'default';
|
|
}
|
|
return 'default';
|
|
};
|
|
|
|
export const useThemeStore = create<ThemeState>((set, get) => {
|
|
const initialThemeMode = getInitialThemeMode();
|
|
const initialThemeColor = getInitialThemeColor();
|
|
const systemIsDarkMode =
|
|
typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
const isDarkMode =
|
|
initialThemeMode === 'dark' || (initialThemeMode === 'auto' && systemIsDarkMode);
|
|
const themeCode = getThemeCode();
|
|
|
|
if (typeof window !== 'undefined') {
|
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
const handleSystemThemeChange = () => {
|
|
set({ systemIsDarkMode: mediaQuery.matches });
|
|
};
|
|
|
|
mediaQuery.addEventListener('change', handleSystemThemeChange);
|
|
}
|
|
|
|
return {
|
|
themeMode: initialThemeMode,
|
|
themeColor: initialThemeColor,
|
|
systemIsDarkMode,
|
|
isDarkMode,
|
|
themeCode,
|
|
setThemeMode: (mode) => {
|
|
if (typeof window !== 'undefined' && localStorage) {
|
|
localStorage.setItem('themeMode', mode);
|
|
}
|
|
const isDarkMode = mode === 'dark' || (mode === 'auto' && get().systemIsDarkMode);
|
|
document.documentElement.setAttribute(
|
|
'data-theme',
|
|
`${get().themeColor}-${isDarkMode ? 'dark' : 'light'}`,
|
|
);
|
|
set({ themeMode: mode, isDarkMode });
|
|
set({ themeCode: getThemeCode() });
|
|
},
|
|
setThemeColor: (color) => {
|
|
if (typeof window !== 'undefined' && localStorage) {
|
|
localStorage.setItem('themeColor', color);
|
|
}
|
|
document.documentElement.setAttribute(
|
|
'data-theme',
|
|
`${color}-${get().isDarkMode ? 'dark' : 'light'}`,
|
|
);
|
|
set({ themeColor: color });
|
|
set({ themeCode: getThemeCode() });
|
|
},
|
|
updateAppTheme: (color) => {
|
|
if (isWebAppPlatform()) {
|
|
const { palette } = get().themeCode;
|
|
document.querySelector('meta[name="theme-color"]')?.setAttribute('content', palette[color]);
|
|
}
|
|
},
|
|
};
|
|
});
|