diff --git a/app/index.tsx b/app/index.tsx
index c52b0ef..f17fe96 100644
--- a/app/index.tsx
+++ b/app/index.tsx
@@ -1,17 +1,20 @@
-import { useCallback, useContext, useEffect } from 'react';
+import { useContext, useEffect } from 'react';
-import AsyncStorage from '@react-native-async-storage/async-storage';
+import { SplashScreen } from 'expo-router';
import { StatusBar, View } from 'react-native';
import { ThemeContext } from 'react-native-ficus-ui';
-import theme, { THEME_KEY } from '@/theme';
+import useAuthStore from '@/modules/auth/auth.store';
+import theme from '@/theme';
+
+// Prevent native splash screen from autohiding before App component declaration
+SplashScreen.preventAutoHideAsync();
const Index = () => {
const { setTheme } = useContext(ThemeContext);
-
- const loadTheme = useCallback(async () => {
- const themeValue = await AsyncStorage.getItem(THEME_KEY);
- if (themeValue === 'dark') {
+ useEffect(() => {
+ const appMode = useAuthStore.getState().appMode;
+ if (appMode === 'dark') {
setTheme(theme.dark);
StatusBar.setBarStyle('light-content');
} else {
@@ -20,10 +23,6 @@ const Index = () => {
}
}, []);
- useEffect(() => {
- loadTheme();
- }, [loadTheme]);
-
return ;
};
export default Index;
diff --git a/src/modules/auth/auth.hook.ts b/src/modules/auth/auth.hook.ts
index 8587561..555ed8d 100644
--- a/src/modules/auth/auth.hook.ts
+++ b/src/modules/auth/auth.hook.ts
@@ -1,6 +1,11 @@
-import { useLayoutEffect, useMemo } from 'react';
-
-import { useRootNavigationState, useRouter, useSegments } from 'expo-router';
+import { useLayoutEffect, useMemo, useRef } from 'react';
+
+import {
+ SplashScreen,
+ useRootNavigationState,
+ useRouter,
+ useSegments,
+} from 'expo-router';
import { useShallow } from 'zustand/react/shallow';
import useAuthStore from '@/modules/auth/auth.store';
@@ -11,7 +16,7 @@ const useProtectedRoute = () => {
const router = useRouter();
const isAuthentificated = useAuthStore(useShallow((state) => !!state.token));
const isHydrated = useAuthStore(useShallow((state) => state.isHydrated));
-
+ const currentRouteRef = useRef<'auth' | 'tabs' | null>(null);
const navigationKey = useMemo(() => {
return rootNavigationState?.key;
}, [rootNavigationState]);
@@ -22,11 +27,18 @@ const useProtectedRoute = () => {
if (!navigationKey || !isHydrated) {
return;
}
+ SplashScreen.hideAsync();
- if (!isAuthentificated && !inAuthGroup) {
+ if (
+ !isAuthentificated &&
+ !inAuthGroup &&
+ currentRouteRef.current !== 'auth'
+ ) {
router.replace('/onboarding');
- } else if (isAuthentificated && inAuthGroup) {
+ currentRouteRef.current = 'auth';
+ } else if (isAuthentificated && currentRouteRef.current !== 'tabs') {
router.replace('/(tabs)/home');
+ currentRouteRef.current = 'tabs';
}
}, [isAuthentificated, segments, navigationKey, isHydrated]);
};
diff --git a/src/modules/auth/auth.store.ts b/src/modules/auth/auth.store.ts
index 1997e42..4ba387a 100644
--- a/src/modules/auth/auth.store.ts
+++ b/src/modules/auth/auth.store.ts
@@ -1,4 +1,5 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
+import { Appearance } from 'react-native';
import { create } from 'zustand';
import { createJSONStorage, devtools, persist } from 'zustand/middleware';
@@ -8,16 +9,26 @@ import { AUTH_STORAGE_KEY } from '@/modules/auth/auth.constants';
type AuthState = {
token: string | null;
setToken: (newToken: string | null) => void;
+ appMode: 'light' | 'dark';
+ setAppMode: (newAppMode: 'light' | 'dark') => void;
logout: () => void;
isHydrated: boolean;
setIsHydrated: (isHydrated: boolean) => void;
};
+// Apperance is a React Native API that allows you to determine if the user prefers a dark or light mode
+const DEFAULT_SYSTEM_MODE =
+ Appearance.getColorScheme() === 'dark' ? 'dark' : 'light';
+
const useAuthStore = create()(
devtools(
persist(
(set) => ({
isHydrated: false,
+ appMode: DEFAULT_SYSTEM_MODE,
+ setAppMode: (newAppMode: 'light' | 'dark') => {
+ set({ appMode: newAppMode });
+ },
token: null,
setIsHydrated: (isHydrated: boolean) => {
set({ isHydrated });
@@ -33,13 +44,14 @@ const useAuthStore = create()(
{
name: AUTH_STORAGE_KEY,
storage: createJSONStorage(() => AsyncStorage), // Specifying the storage
- partialize: (state) => ({ token: state.token }), // Persist only the token
- onRehydrateStorage: () => (state, error) => { // Called when the storage is rehydrated
+ partialize: (state) => ({ token: state.token, appMode: state.appMode }), // Persist only the token and app mode
+ onRehydrateStorage: () => (state, error) => {
+ // Called when the storage is rehydrated
if (error) {
console.error(error);
}
state?.setIsHydrated?.(true);
- }
+ },
}
)
)
diff --git a/src/theme/useDarkMode.ts b/src/theme/useDarkMode.ts
index ea66650..dccc7c1 100644
--- a/src/theme/useDarkMode.ts
+++ b/src/theme/useDarkMode.ts
@@ -1,13 +1,14 @@
import { useCallback } from 'react';
-import AsyncStorage from '@react-native-async-storage/async-storage';
import { StatusBar } from 'react-native';
import { useTheme } from 'react-native-ficus-ui';
-import ficusThemes, { THEME_KEY } from '@/theme';
+import useAuthStore from '@/modules/auth/auth.store';
+import ficusThemes from '@/theme';
export const useDarkMode = () => {
const { theme, setTheme } = useTheme();
+ const setAppMode = useAuthStore((state) => state.setAppMode);
const colorModeValue = useCallback(
(lightValue: Value, darkValue: Value) =>
@@ -18,14 +19,14 @@ export const useDarkMode = () => {
const setColorMode = async (colorMode: 'dark' | 'light') => {
setTheme(ficusThemes[colorMode]);
StatusBar.setBarStyle(`${colorMode}-content`);
- await AsyncStorage.setItem(THEME_KEY, colorMode);
+ setAppMode(colorMode);
};
- const toggleColorMode = async () => {
+ const toggleColorMode = () => {
const newTheme = theme.name === 'dark' ? 'light' : 'dark';
StatusBar.setBarStyle(`${theme.name as 'dark' | 'light'}-content`);
setTheme(ficusThemes[newTheme]);
- await AsyncStorage.setItem(THEME_KEY, newTheme);
+ setAppMode(newTheme);
};
const getThemeColor = (color: `${string}.${ColorShade}`) => {