summaryrefslogtreecommitdiff
path: root/apps/mobile/app/_layout.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'apps/mobile/app/_layout.tsx')
-rw-r--r--apps/mobile/app/_layout.tsx111
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>
+ );
+}