import { STORAGE_KEYS } from '@/common/constants/storageKeys'; import { Language, LanguagePreference, setLanguage as setLangCore } from '@/common/strings'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { getLocales } from 'expo-localization'; import { ReactNode, createContext, useCallback, useContext, useEffect, useState } from 'react'; import { useColorScheme } from 'react-native'; export type Theme = 'light' | 'dark' | 'system'; interface PreferencesContextValue { theme: Theme; isDarkMode: boolean; setTheme: (theme: Theme) => void; enabledCastleFeatures: string[]; toggleCastleFeature: (feature: string) => void; hapticFeedback: boolean; setHapticFeedback: (enabled: boolean) => void; languagePreference: LanguagePreference; setLanguage: (lang: LanguagePreference) => void; } const PreferencesContext = createContext(null); function getDeviceLanguage(): Language { const locales = getLocales(); if (locales && locales.length > 0 && locales[0].languageCode === 'ko') { return 'ko'; } return 'en'; } export function PreferencesProvider({ children }: { children: ReactNode }) { const systemScheme = useColorScheme(); const [theme, setThemeState] = useState('system'); const [enabledCastleFeatures, setEnabledCastleFeatures] = useState([]); const [hapticFeedback, setHapticFeedbackState] = useState(true); const [languagePreference, setLanguagePreferenceState] = useState('system'); const [loaded, setLoaded] = useState(false); useEffect(() => { const langToSet = languagePreference === 'system' ? getDeviceLanguage() : languagePreference; setLangCore(langToSet); }, [languagePreference]); useEffect(() => { Promise.all([ AsyncStorage.getItem(STORAGE_KEYS.THEME), AsyncStorage.getItem(STORAGE_KEYS.CASTLE_FEATURES), AsyncStorage.getItem(STORAGE_KEYS.HAPTIC), AsyncStorage.getItem(STORAGE_KEYS.LANGUAGE), ]) .then(([storedTheme, storedCastleFeatures, storedHaptic, storedLang]) => { if (storedTheme === 'light' || storedTheme === 'dark' || storedTheme === 'system') { setThemeState(storedTheme); } if (storedCastleFeatures) { try { setEnabledCastleFeatures(JSON.parse(storedCastleFeatures)); } catch { void 0; } } setHapticFeedbackState(storedHaptic !== 'false'); if (storedLang === 'en' || storedLang === 'ko' || storedLang === 'system') { setLanguagePreferenceState(storedLang); } }) .catch(e => { console.error('Failed to load preferences', e); }) .finally(() => setLoaded(true)); }, []); const setTheme = useCallback((newTheme: Theme) => { setThemeState(newTheme); AsyncStorage.setItem(STORAGE_KEYS.THEME, newTheme); }, []); const toggleCastleFeature = useCallback((feature: string) => { setEnabledCastleFeatures(prev => { const next = prev.includes(feature) ? prev.filter(f => f !== feature) : [...prev, feature]; AsyncStorage.setItem(STORAGE_KEYS.CASTLE_FEATURES, JSON.stringify(next)); return next; }); }, []); const setHapticFeedback = useCallback((enabled: boolean) => { setHapticFeedbackState(enabled); AsyncStorage.setItem(STORAGE_KEYS.HAPTIC, String(enabled)); }, []); const setLanguage = useCallback((pref: LanguagePreference) => { setLanguagePreferenceState(pref); AsyncStorage.setItem(STORAGE_KEYS.LANGUAGE, pref); }, []); const isDarkMode = theme === 'system' ? systemScheme === 'dark' : theme === 'dark'; if (!loaded) return null; return ( {children} ); } export function usePreferences() { const context = useContext(PreferencesContext); if (!context) throw new Error('usePreferences must be used within PreferencesProvider'); return context; }