redesign take 1
This commit is contained in:
1 parent
1143f8ca02
commit
e3a3c3fccf
19 files changed
+2783
-1493
No files matched your search
@@ -0,0 +1,129 @@
|
||||
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<ThemeContextType | undefined>(undefined);
|
||||
|
||||
const THEME_STORAGE_KEY = "@fitai:theme";
|
||||
|
||||
interface ThemeProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function ThemeProvider({ children }: ThemeProviderProps) {
|
||||
const systemColorScheme = useColorScheme();
|
||||
const [themeMode, setThemeMode] = useState<ThemeMode>("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 (
|
||||
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
Reference in new issue
Block a user