Files
physical-expo/context/ThemeContext.tsx
2026-01-12 15:31:47 +08:00

116 lines
3.0 KiB
TypeScript

import AsyncStorage from "@react-native-async-storage/async-storage";
import React, { createContext, useContext, useEffect, useState } from "react";
import { useColorScheme as useDeviceColorScheme } from "react-native";
type Theme = "light" | "dark";
interface ThemeContextType {
theme: Theme;
toggleTheme: () => void;
setTheme: (theme: Theme) => void;
systemTheme: Theme;
isSystemTheme: boolean;
useSystemTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
const THEME_STORAGE_KEY = "user_theme_preference";
export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const deviceColorScheme = useDeviceColorScheme();
const systemTheme = deviceColorScheme === "dark" ? "dark" : "light";
// State to hold the current theme
const [theme, setThemeState] = useState<Theme>(systemTheme);
// State to track if we are following system theme
const [isSystemTheme, setIsSystemTheme] = useState(true);
useEffect(() => {
loadThemePersistence();
}, []);
useEffect(() => {
// If we are following system theme, update when device theme changes
if (isSystemTheme) {
setThemeState(systemTheme);
}
}, [systemTheme, isSystemTheme]);
const loadThemePersistence = async () => {
try {
const storedTheme = await AsyncStorage.getItem(THEME_STORAGE_KEY);
if (storedTheme) {
if (storedTheme === "system") {
setIsSystemTheme(true);
setThemeState(systemTheme);
} else {
setIsSystemTheme(false);
setThemeState(storedTheme as Theme);
}
} else {
// Default to system
setIsSystemTheme(true);
setThemeState(systemTheme);
}
} catch (error) {
console.warn("Failed to load theme preference", error);
// Fallback to system
setIsSystemTheme(true);
setThemeState(systemTheme);
}
};
const saveThemePreference = async (newTheme: Theme | "system") => {
try {
await AsyncStorage.setItem(THEME_STORAGE_KEY, newTheme);
} catch (error) {
console.warn("Failed to save theme preference", error);
}
};
const toggleTheme = () => {
const newTheme = theme === "light" ? "dark" : "light";
setIsSystemTheme(false);
setThemeState(newTheme);
saveThemePreference(newTheme);
};
const setTheme = (newTheme: Theme) => {
setIsSystemTheme(false);
setThemeState(newTheme);
saveThemePreference(newTheme);
};
const useSystemTheme = () => {
setIsSystemTheme(true);
setThemeState(systemTheme);
saveThemePreference("system");
};
return (
<ThemeContext.Provider
value={{
theme,
toggleTheme,
setTheme,
systemTheme,
isSystemTheme,
useSystemTheme,
}}
>
{children}
</ThemeContext.Provider>
);
};
export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
};