remove gluestackk-ui

This commit is contained in:
Tran Anh Tuan
2025-11-19 14:23:17 +07:00
parent f3cf10e5e6
commit 742d8f6bcc
20 changed files with 1463 additions and 2624 deletions

View File

@@ -1,26 +1,30 @@
/**
* Theme Context Hook for managing app-wide theme state
* Supports Light, Dark, and System (automatic) modes
* Theme Context Hook for managing app-wide theme state.
* Supports Light, Dark, and System (automatic) modes across Expo platforms.
*
* IMPORTANT: Requires expo-system-ui plugin in app.json for Android support
* IMPORTANT: Requires expo-system-ui plugin in app.json for Android status bar support.
*/
import { Colors, ColorName } from "@/constants/theme";
import { ColorName, Colors } from "@/constants/theme";
import { getStorageItem, setStorageItem } from "@/utils/storage";
import {
createContext,
ReactNode,
useCallback,
useContext,
useEffect,
useMemo,
useState,
ReactNode,
} from "react";
import {
useColorScheme as useSystemColorScheme,
Appearance,
AppState,
AppStateStatus,
useColorScheme as useRNColorScheme,
} from "react-native";
type ThemeMode = "light" | "dark" | "system";
type ColorScheme = "light" | "dark";
export type ThemeMode = "light" | "dark" | "system";
export type ColorScheme = "light" | "dark";
interface ThemeContextType {
themeMode: ThemeMode;
@@ -28,146 +32,162 @@ interface ThemeContextType {
colors: typeof Colors.light;
setThemeMode: (mode: ThemeMode) => Promise<void>;
getColor: (colorName: ColorName) => string;
isHydrated: boolean;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
const THEME_STORAGE_KEY = "theme_mode";
const getSystemScheme = (): ColorScheme => {
const scheme = Appearance.getColorScheme();
// console.log("[Theme] Appearance.getColorScheme():", scheme);
return scheme === "dark" ? "dark" : "light";
};
const isThemeMode = (value: unknown): value is ThemeMode => {
return value === "light" || value === "dark" || value === "system";
};
export function ThemeProvider({ children }: { children: ReactNode }) {
// State để force re-render khi system theme thay đổi
const [systemTheme, setSystemTheme] = useState<ColorScheme>(() => {
const current = Appearance.getColorScheme();
console.log("[Theme] Initial system theme:", current);
return current === "dark" ? "dark" : "light";
});
// State lưu user's choice (light/dark/system)
const [systemScheme, setSystemScheme] =
useState<ColorScheme>(getSystemScheme);
const [themeMode, setThemeModeState] = useState<ThemeMode>("system");
const [isLoaded, setIsLoaded] = useState(false);
const [isHydrated, setIsHydrated] = useState(false);
// Listen vào system theme changes - đăng ký ngay từ đầu
const syncSystemScheme = useCallback(() => {
const next = getSystemScheme();
// console.log("[Theme] syncSystemScheme computed:", next);
setSystemScheme((current) => (current === next ? current : next));
}, []);
const rnScheme = useRNColorScheme();
useEffect(() => {
console.log("[Theme] Registering appearance listener");
if (!rnScheme) return;
const next = rnScheme === "dark" ? "dark" : "light";
// console.log("[Theme] useColorScheme hook emitted:", rnScheme);
setSystemScheme((current) => (current === next ? current : next));
}, [rnScheme]);
useEffect(() => {
const subscription = Appearance.addChangeListener(({ colorScheme }) => {
const newScheme = colorScheme === "dark" ? "dark" : "light";
console.log(
"[Theme] System theme changed to:",
newScheme,
"at",
new Date().toLocaleTimeString()
);
setSystemTheme(newScheme);
const next = colorScheme === "dark" ? "dark" : "light";
// console.log("[Theme] Appearance listener fired with:", colorScheme);
setSystemScheme((current) => (current === next ? current : next));
});
// Double check current theme khi mount
const currentScheme = Appearance.getColorScheme();
const current = currentScheme === "dark" ? "dark" : "light";
if (current !== systemTheme) {
console.log("[Theme] Syncing system theme on mount:", current);
setSystemTheme(current);
}
syncSystemScheme();
return () => {
console.log("[Theme] Removing appearance listener");
subscription.remove();
};
}, []);
// Xác định colorScheme cuối cùng
const colorScheme: ColorScheme =
themeMode === "system" ? systemTheme : themeMode;
const colors = Colors[colorScheme];
// Log để debug
useEffect(() => {
console.log(
"[Theme] Current state - Mode:",
themeMode,
"| Scheme:",
colorScheme,
"| System:",
systemTheme
);
}, [themeMode, colorScheme, systemTheme]);
}, [syncSystemScheme]);
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);
// console.log("[Theme] System scheme detected:", systemScheme);
}, [systemScheme]);
useEffect(() => {
const handleAppStateChange = (nextState: AppStateStatus) => {
if (nextState === "active") {
// console.log("[Theme] AppState active → scheduling system scheme sync");
setTimeout(() => {
// console.log("[Theme] AppState sync callback running");
syncSystemScheme();
}, 100);
}
};
loadThemeMode();
const subscription = AppState.addEventListener(
"change",
handleAppStateChange
);
return () => subscription.remove();
}, [syncSystemScheme]);
useEffect(() => {
let isMounted = true;
const hydrateThemeMode = async () => {
try {
const savedThemeMode = await getStorageItem(THEME_STORAGE_KEY);
if (isMounted && isThemeMode(savedThemeMode)) {
setThemeModeState(savedThemeMode);
}
} catch (error) {
console.warn("[Theme] Failed to load theme mode:", error);
} finally {
if (isMounted) {
setIsHydrated(true);
}
}
};
hydrateThemeMode();
return () => {
isMounted = false;
};
}, []);
const setThemeMode = async (mode: ThemeMode) => {
const colorScheme: ColorScheme =
themeMode === "system" ? systemScheme : themeMode;
const colors = useMemo(() => Colors[colorScheme], [colorScheme]);
const setThemeMode = useCallback(async (mode: ThemeMode) => {
setThemeModeState(mode);
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);
console.warn("[Theme] Failed to save theme mode:", error);
}
};
}, []);
const getColor = (colorName: ColorName): string => {
return colors[colorName] || colors.text;
};
useEffect(() => {
// console.log("[Theme] window defined:", typeof window !== "undefined");
}, []);
// Chờ theme load xong trước khi render
if (!isLoaded) {
// Render với default theme (system) khi đang load
return (
<ThemeContext.Provider
value={{
themeMode: "system",
colorScheme: systemTheme,
colors: Colors[systemTheme],
setThemeMode: async () => {},
getColor: (colorName: ColorName) =>
Colors[systemTheme][colorName] || Colors[systemTheme].text,
}}
>
{children}
</ThemeContext.Provider>
);
}
const getColor = useCallback(
(colorName: ColorName) => colors[colorName] ?? colors.text,
[colors]
);
const value: ThemeContextType = {
themeMode,
colorScheme,
colors,
setThemeMode,
getColor,
};
useEffect(() => {
// console.log("[Theme] Mode:", themeMode);
}, [themeMode]);
useEffect(() => {
// console.log("[Theme] Derived colorScheme:", colorScheme);
}, [colorScheme]);
const value = useMemo(
() => ({
themeMode,
colorScheme,
colors,
setThemeMode,
getColor,
isHydrated,
}),
[themeMode, colorScheme, colors, setThemeMode, getColor, isHydrated]
);
return (
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
);
}
export function useThemeContext(): ThemeContextType {
export function useTheme(): ThemeContextType {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error("useThemeContext must be used within a ThemeProvider");
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
}
// Legacy hook cho backward compatibility
export const useThemeContext = useTheme;
export function useColorScheme(): ColorScheme {
const { colorScheme } = useThemeContext();
return colorScheme;
return useTheme().colorScheme;
}