import { Appearance } from "react-native"; import React, { ReactNode, useCallback, useEffect, useReducer } from "react"; import StatusBar from "../components/StatusBar"; import { ThemeName, ThemeSchema, themeColors, StatusBarVariant } from "../constants/Colors"; import ThemeContext, { ThemeReducer } from "../context/themeContext"; const themeReducer: ThemeReducer = (state, action) => { if (action.type === 'TOGGLE_THEME_LIGHT_AND_DARK') { if (!action?.payload?.colorScheme) { const themeSchema = state.colorScheme === "dark" ? "light" : "dark"; return { ...state, colorScheme: themeSchema, currentColorScheme: action?.payload?.currentColorScheme || state.currentColorScheme, currentTheme: themeColors.find(theme => theme.name === state.themeName)![themeSchema], } }; const themeSchema = action?.payload?.colorScheme; return { ...state, colorScheme: themeSchema, currentColorScheme: action?.payload?.currentColorScheme || state.currentColorScheme, currentTheme: themeColors.find(theme => theme.name === state.themeName)![themeSchema], } } if (action.type === 'CHANGE_THEME') { if (!action?.payload || !action.payload?.themeName) return state; return { ...state, themeName: action.payload?.themeName || state.themeName, currentTheme: themeColors.find(theme => theme.name === action.payload?.themeName)![state.colorScheme], } } if (action.type === 'SET_INITIAL_THEME') { if (!action?.payload) return state; return { ...state, themeName: action.payload?.themeName || state.themeName, colorScheme: action.payload?.colorScheme || state.colorScheme, currentColorScheme: action.payload?.currentColorScheme || state.currentColorScheme, statusBarColor: action.payload?.statusBarColor || state.statusBarColor, currentTheme: themeColors.find(theme => theme.name === action.payload?.themeName)![action.payload?.colorScheme || state.colorScheme], } } if (action.type === 'CHANGE_STATUSBAR_COLOR') { if (!action?.payload?.statusBarColor) return state; return { ...state, statusBarColor: action?.payload?.statusBarColor, } } return state; } const ThemeProvider: React.FC<{ children: ReactNode, initialScheme?: ThemeSchema, themeName?: ThemeName, enableThemedStatusBar?: boolean }> = ({ children, initialScheme = "system", themeName = "Grey", enableThemedStatusBar = false }) => { const appliedSchema = initialScheme === "system" ? Appearance.getColorScheme() as "dark" | "light" : initialScheme; const [state, dispatch] = useReducer(themeReducer, { colorScheme: appliedSchema, themeName: themeName, statusBarColor: 'default', currentColorScheme: initialScheme, currentTheme: themeColors[0][appliedSchema] }); const toggleTheme = useCallback((_themeSchema?: ThemeSchema) => { if (!_themeSchema) { dispatch({ type: 'TOGGLE_THEME_LIGHT_AND_DARK', payload: {} }); return; } if (typeof _themeSchema !== "string" || !["dark", "light", "system"].includes(_themeSchema)) { console.error("Invalid theme schema"); return; } if (_themeSchema == "system") { dispatch({ type: 'TOGGLE_THEME_LIGHT_AND_DARK', payload: { currentColorScheme: "system", colorScheme: Appearance.getColorScheme() as any } }); return; } dispatch({ type: 'TOGGLE_THEME_LIGHT_AND_DARK', payload: { colorScheme: _themeSchema, currentColorScheme: _themeSchema } }); return; }, []); const changeTheme = useCallback((_themeName: ThemeName) => { dispatch({ type: 'CHANGE_THEME', payload: { themeName: _themeName } }); }, []); const changeStatusBarColor = useCallback((themeName: StatusBarVariant) => { dispatch({ type: "CHANGE_STATUSBAR_COLOR", payload: { statusBarColor: themeName } }); }, []); const initialTheme = useCallback(({ colorScheme, themeName }: { colorScheme?: ThemeSchema, themeName?: ThemeName, }) => { if (colorScheme === "system") { dispatch({ type: 'SET_INITIAL_THEME', payload: { currentColorScheme: "system", colorScheme: Appearance.getColorScheme() as any, themeName: themeName || "Zinc" } }); } else { dispatch({ type: 'SET_INITIAL_THEME', payload: { currentColorScheme: colorScheme || "system", colorScheme: colorScheme || Appearance.getColorScheme() as any, themeName: themeName || "Zinc" } }); } }, []); useEffect(() => { const unSubscribe = Appearance.addChangeListener(({ colorScheme: cs }) => { // console.log("colorScheme", cs) if (state.currentColorScheme === "system") { dispatch({ type: 'TOGGLE_THEME_LIGHT_AND_DARK', payload: { colorScheme: cs as any, currentColorScheme: 'system' } }); }; }); return () => { unSubscribe.remove(); }; }, [state.currentColorScheme]); return ( {enableThemedStatusBar ? : <>} {children} ) }; export default ThemeProvider;