import React, { createContext, useContext, useState, useEffect, ReactNode, } from "react"; import { useColorScheme } from "react-native"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { ColorScheme, lightColors, darkColors } from "../styles/colors"; import { TypographyPresets, createTypographyPresets, } from "../styles/typography"; type ThemeMode = "light" | "dark" | "system"; type ActiveTheme = "light" | "dark"; interface ThemeContextType { // Current active theme theme: ActiveTheme; // User's theme preference themeMode: ThemeMode; // Active color scheme colors: ColorScheme; // Typography presets typography: TypographyPresets; // Theme actions setTheme: (mode: ThemeMode) => void; toggleTheme: () => void; } const ThemeContext = createContext(undefined); const THEME_STORAGE_KEY = "@fitai:theme"; interface ThemeProviderProps { children: ReactNode; } export function ThemeProvider({ children }: ThemeProviderProps) { const systemColorScheme = useColorScheme(); const [themeMode, setThemeMode] = useState("system"); const [isLoading, setIsLoading] = useState(true); // Determine active theme based on mode and system preference const getActiveTheme = (): ActiveTheme => { if (themeMode === "system") { return systemColorScheme === "dark" ? "dark" : "light"; } return themeMode; }; const activeTheme = getActiveTheme(); const colors = activeTheme === "dark" ? darkColors : lightColors; const typography = createTypographyPresets( colors.textPrimary, colors.textSecondary, colors.textTertiary, ); // Load saved theme preference on mount useEffect(() => { const loadTheme = async () => { try { const savedTheme = await AsyncStorage.getItem(THEME_STORAGE_KEY); if (savedTheme && ["light", "dark", "system"].includes(savedTheme)) { setThemeMode(savedTheme as ThemeMode); } } catch (error) { console.error("Failed to load theme preference:", error); } finally { setIsLoading(false); } }; loadTheme(); }, []); // Save theme preference when it changes const setTheme = async (mode: ThemeMode) => { try { setThemeMode(mode); await AsyncStorage.setItem(THEME_STORAGE_KEY, mode); } catch (error) { console.error("Failed to save theme preference:", error); } }; // Toggle between light and dark (sets explicit mode, not system) const toggleTheme = () => { const newMode = activeTheme === "dark" ? "light" : "dark"; setTheme(newMode); }; // Don't render children until theme is loaded if (isLoading) { return null; } const value: ThemeContextType = { theme: activeTheme, themeMode, colors, typography, setTheme, toggleTheme, }; return ( {children} ); } /** * Hook to access theme context * @throws Error if used outside ThemeProvider */ export function useTheme() { const context = useContext(ThemeContext); if (context === undefined) { throw new Error("useTheme must be used within a ThemeProvider"); } return context; }