cosmo/frontend/src/pages/admin/AdminPrefsContext.tsx

72 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 管理后台的外观偏好:语言(中/英)与主题(浅色/深色)。
* 保存在 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;
}