diff options
Diffstat (limited to 'apps/mobile/app/_layout.tsx')
| -rw-r--r-- | apps/mobile/app/_layout.tsx | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/apps/mobile/app/_layout.tsx b/apps/mobile/app/_layout.tsx new file mode 100644 index 0000000..2c030b6 --- /dev/null +++ b/apps/mobile/app/_layout.tsx @@ -0,0 +1,111 @@ +import React, { useEffect } from 'react'; +import { Stack, useRouter, useSegments } from 'expo-router'; +import { StatusBar } from 'expo-status-bar'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { useColorScheme } from 'react-native'; +import { Colors } from '../constants/Colors'; +import { useAuthStore, setupAuthListener } from '../stores/authStore'; +import { LoadingScreen } from '../components/LoadingScreen'; + +// Create a client +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 5000, // Data is fresh for 5 seconds + retry: 2, // Retry failed requests twice + refetchOnWindowFocus: false, // Don't refetch on app focus (mobile) + }, + mutations: { + retry: 1, + }, + }, +}); + +/** + * Auth state handler component + * Redirects users based on authentication status + */ +function AuthStateHandler({ children }: { children: React.ReactNode }) { + const router = useRouter(); + const segments = useSegments(); + const session = useAuthStore((state) => state.session); + const isInitialized = useAuthStore((state) => state.isInitialized); + const initialize = useAuthStore((state) => state.initialize); + + // Initialize auth state on mount + useEffect(() => { + initialize(); + }, [initialize]); + + // Setup auth listener on mount + useEffect(() => { + const unsubscribe = setupAuthListener(); + return unsubscribe; + }, []); + + // Handle auth-based routing + useEffect(() => { + if (!isInitialized) return; + + const inAuthGroup = segments[0] === '(auth)'; + const isAuthenticated = session !== null; + + if (!isAuthenticated && !inAuthGroup) { + // Redirect to login if not authenticated and not already on auth screens + router.replace('/(auth)/login'); + } else if (isAuthenticated && inAuthGroup) { + // Redirect to main app if authenticated and on auth screens + router.replace('/(tabs)'); + } + }, [session, segments, isInitialized, router]); + + // Show loading screen while initializing + if (!isInitialized) { + return <LoadingScreen message="Checking authentication..." />; + } + + return <>{children}</>; +} + +export default function RootLayout() { + const colorScheme = useColorScheme() ?? 'light'; + const colors = Colors[colorScheme]; + + return ( + <QueryClientProvider client={queryClient}> + <StatusBar style={colorScheme === 'dark' ? 'light' : 'dark'} /> + <AuthStateHandler> + <Stack + screenOptions={{ + headerStyle: { + backgroundColor: colors.background, + }, + headerTintColor: colors.text, + headerTitleStyle: { + fontWeight: '600', + }, + contentStyle: { + backgroundColor: colors.background, + }, + }} + > + <Stack.Screen + name="(auth)" + options={{ headerShown: false }} + /> + <Stack.Screen + name="(tabs)" + options={{ headerShown: false }} + /> + <Stack.Screen + name="task/[id]" + options={{ + presentation: 'card', + headerBackTitle: 'Back', + }} + /> + </Stack> + </AuthStateHandler> + </QueryClientProvider> + ); +} |
