import AsyncStorage from '@react-native-async-storage/async-storage'; import { Appearance, AppState, AppStateStatus } from 'react-native'; import { create } from 'zustand'; import { useEffect, useRef } from 'react'; import { lightColors, darkColors, type AppColors } from '../theme/palettes'; import { buildPaperTheme } from '../theme/paperTheme'; import type { MD3Theme } from 'react-native-paper'; const STORAGE_KEY = 'app_theme_preference'; export type ThemePreference = 'light' | 'dark' | 'system'; export type ResolvedScheme = 'light' | 'dark'; function computeResolved(pref: ThemePreference, system: ResolvedScheme): ResolvedScheme { return pref === 'system' ? system : pref; } function buildState( preference: ThemePreference, systemScheme: ResolvedScheme ): { resolvedScheme: ResolvedScheme; colors: AppColors; paperTheme: MD3Theme; } { const resolvedScheme = computeResolved(preference, systemScheme); const isDark = resolvedScheme === 'dark'; const colors = isDark ? darkColors : lightColors; return { resolvedScheme, colors, paperTheme: buildPaperTheme(colors, isDark), }; } type ThemeState = { preference: ThemePreference; systemScheme: ResolvedScheme; hydrated: boolean; resolvedScheme: ResolvedScheme; colors: AppColors; paperTheme: MD3Theme; setPreference: (p: ThemePreference) => Promise; setSystemScheme: (s: ResolvedScheme) => void; hydrate: () => Promise; }; /** 获取系统主题 */ function getSystemScheme(): ResolvedScheme { const scheme = Appearance.getColorScheme(); return scheme === 'dark' ? 'dark' : 'light'; } const initialSystemScheme = getSystemScheme(); export const useThemeStore = create((set, get) => ({ preference: 'system', systemScheme: initialSystemScheme, hydrated: false, ...buildState('system', initialSystemScheme), setSystemScheme: (systemScheme) => { const { preference, systemScheme: cur } = get(); if (cur === systemScheme) return; set({ systemScheme, ...buildState(preference, systemScheme), }); }, setPreference: async (preference) => { try { await AsyncStorage.setItem(STORAGE_KEY, preference); } catch { /* ignore */ } const { systemScheme } = get(); set({ preference, ...buildState(preference, systemScheme), }); }, hydrate: async () => { let preference: ThemePreference = 'system'; try { const raw = await AsyncStorage.getItem(STORAGE_KEY); if (raw === 'light' || raw === 'dark' || raw === 'system') { preference = raw; } } catch { /* ignore */ } const systemScheme = getSystemScheme(); set({ preference, hydrated: true, systemScheme, ...buildState(preference, systemScheme), }); }, })); /** 在根布局中同步系统配色并恢复持久化偏好 */ export function ThemeBootstrap() { const setSystemScheme = useThemeStore((s) => s.setSystemScheme); const hydrate = useThemeStore((s) => s.hydrate); const appState = useRef(AppState.currentState); useEffect(() => { void hydrate(); }, [hydrate]); // 监听系统主题变化 useEffect(() => { const subscription = Appearance.addChangeListener(({ colorScheme }) => { const newScheme = colorScheme === 'dark' ? 'dark' : 'light'; setSystemScheme(newScheme); }); return () => { subscription.remove(); }; }, [setSystemScheme]); // 监听应用状态变化,从后台恢复时重新检测系统主题 useEffect(() => { const subscription = AppState.addEventListener('change', (nextAppState) => { if ( appState.current.match(/inactive|background/) && nextAppState === 'active' ) { setSystemScheme(getSystemScheme()); } appState.current = nextAppState; }); return () => { subscription.remove(); }; }, [setSystemScheme]); return null; } export function useAppColors() { return useThemeStore((s) => s.colors); } export function useThemePreference() { return useThemeStore((s) => s.preference); } export function useResolvedColorScheme() { return useThemeStore((s) => s.resolvedScheme); } export function useSetThemePreference() { return useThemeStore((s) => s.setPreference); } export function usePaperThemeFromStore() { return useThemeStore((s) => s.paperTheme); } // 导出调试函数 export { getSystemScheme };