import AsyncStorage from '@react-native-async-storage/async-storage'; import { useColorScheme } from 'react-native'; import { create } from 'zustand'; import { useEffect } 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; }; const initialSystem: ResolvedScheme = 'light'; export const useThemeStore = create((set, get) => ({ preference: 'system', systemScheme: initialSystem, hydrated: false, ...buildState('system', initialSystem), 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 } = get(); set({ preference, hydrated: true, ...buildState(preference, systemScheme), }); }, })); /** 在根布局中同步系统配色并恢复持久化偏好 */ export function ThemeBootstrap() { const system = useColorScheme(); const setSystemScheme = useThemeStore((s) => s.setSystemScheme); const hydrate = useThemeStore((s) => s.hydrate); useEffect(() => { void hydrate(); }, [hydrate]); useEffect(() => { setSystemScheme(system === 'dark' ? 'dark' : 'light'); }, [system, 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); }