/** * Theme Context Hook for managing app-wide theme state * Supports Light, Dark, and System (automatic) modes * * IMPORTANT: Requires expo-system-ui plugin in app.json for Android support */ import { Colors, ColorName } from "@/constants/theme"; import { getStorageItem, setStorageItem } from "@/utils/storage"; import { createContext, useContext, useEffect, useState, ReactNode, } from "react"; import { useColorScheme as useSystemColorScheme } from "react-native"; type ThemeMode = "light" | "dark" | "system"; type ColorScheme = "light" | "dark"; interface ThemeContextType { themeMode: ThemeMode; colorScheme: ColorScheme; colors: typeof Colors.light; setThemeMode: (mode: ThemeMode) => Promise; getColor: (colorName: ColorName) => string; } const ThemeContext = createContext(undefined); const THEME_STORAGE_KEY = "theme_mode"; export function ThemeProvider({ children }: { children: ReactNode }) { // Dùng useColorScheme từ react-native để detect theme thiết bị const deviceColorScheme = useSystemColorScheme(); // "light" | "dark" | null | undefined // State lưu user's choice (light/dark/system) const [themeMode, setThemeModeState] = useState("system"); const [isLoaded, setIsLoaded] = useState(false); // Xác định colorScheme cuối cùng const colorScheme: ColorScheme = themeMode === "system" ? deviceColorScheme === "dark" ? "dark" : "light" : themeMode; const colors = Colors[colorScheme]; useEffect(() => { const loadThemeMode = async () => { try { const savedThemeMode = await getStorageItem(THEME_STORAGE_KEY); if ( savedThemeMode && ["light", "dark", "system"].includes(savedThemeMode) ) { setThemeModeState(savedThemeMode as ThemeMode); } } catch (error) { console.warn("Failed to load theme mode:", error); } finally { setIsLoaded(true); } }; loadThemeMode(); }, []); const setThemeMode = async (mode: ThemeMode) => { try { setThemeModeState(mode); await setStorageItem(THEME_STORAGE_KEY, mode); console.log("[Theme] Changed to:", mode); } catch (error) { console.warn("Failed to save theme mode:", error); } }; const getColor = (colorName: ColorName): string => { return colors[colorName] || colors.text; }; // Chờ theme load xong trước khi render if (!isLoaded) { // Render với default theme (system) khi đang load const defaultScheme: ColorScheme = deviceColorScheme === "dark" ? "dark" : "light"; return ( {}, getColor: (colorName: ColorName) => Colors[defaultScheme][colorName] || Colors[defaultScheme].text, }} > {children} ); } const value: ThemeContextType = { themeMode, colorScheme, colors, setThemeMode, getColor, }; return ( {children} ); } export function useThemeContext(): ThemeContextType { const context = useContext(ThemeContext); if (context === undefined) { throw new Error("useThemeContext must be used within a ThemeProvider"); } return context; } // Legacy hook cho backward compatibility export function useColorScheme(): ColorScheme { const { colorScheme } = useThemeContext(); return colorScheme; }