72 lines
2.3 KiB
TypeScript
72 lines
2.3 KiB
TypeScript
/**
|
||
* 管理后台的外观偏好:语言(中/英)与主题(浅色/深色)。
|
||
* 保存在 localStorage,刷新后保持,管理员与普通用户共用。
|
||
*/
|
||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||
import type { ReactNode } from 'react';
|
||
|
||
import { translate } from './adminI18n';
|
||
import type { AdminLang } from './adminI18n';
|
||
|
||
export type AdminThemeMode = 'light' | 'dark';
|
||
|
||
interface AdminPrefs {
|
||
lang: AdminLang;
|
||
themeMode: AdminThemeMode;
|
||
setLang: (lang: AdminLang) => void;
|
||
setThemeMode: (mode: AdminThemeMode) => void;
|
||
/** 按当前语言翻译文案(未收录时原样返回) */
|
||
t: (text: string) => string;
|
||
}
|
||
|
||
const STORAGE_KEY = 'cosmo_admin_prefs';
|
||
const AdminPrefsContext = createContext<AdminPrefs | null>(null);
|
||
|
||
function readStoredPrefs(): { lang: AdminLang; themeMode: AdminThemeMode } {
|
||
const fallback = { lang: 'zh' as AdminLang, themeMode: 'light' as AdminThemeMode };
|
||
try {
|
||
const raw = localStorage.getItem(STORAGE_KEY);
|
||
if (!raw) return fallback;
|
||
const parsed = JSON.parse(raw) as Partial<{ lang: AdminLang; themeMode: AdminThemeMode }>;
|
||
return {
|
||
lang: parsed.lang === 'en' ? 'en' : 'zh',
|
||
themeMode: parsed.themeMode === 'dark' ? 'dark' : 'light',
|
||
};
|
||
} catch {
|
||
return fallback;
|
||
}
|
||
}
|
||
|
||
export function AdminPrefsProvider({ children }: { children: ReactNode }) {
|
||
const [prefs, setPrefs] = useState(readStoredPrefs);
|
||
|
||
useEffect(() => {
|
||
localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs));
|
||
}, [prefs]);
|
||
|
||
const setLang = useCallback((lang: AdminLang) => setPrefs((current) => ({ ...current, lang })), []);
|
||
const setThemeMode = useCallback(
|
||
(themeMode: AdminThemeMode) => setPrefs((current) => ({ ...current, themeMode })),
|
||
[],
|
||
);
|
||
|
||
const value = useMemo<AdminPrefs>(
|
||
() => ({
|
||
lang: prefs.lang,
|
||
themeMode: prefs.themeMode,
|
||
setLang,
|
||
setThemeMode,
|
||
t: (text: string) => translate(text, prefs.lang),
|
||
}),
|
||
[prefs.lang, prefs.themeMode, setLang, setThemeMode],
|
||
);
|
||
|
||
return <AdminPrefsContext.Provider value={value}>{children}</AdminPrefsContext.Provider>;
|
||
}
|
||
|
||
export function useAdminPrefs(): AdminPrefs {
|
||
const context = useContext(AdminPrefsContext);
|
||
if (!context) throw new Error('useAdminPrefs must be used inside AdminPrefsProvider');
|
||
return context;
|
||
}
|