From 869f21ee2efaefed6a5aa4fbd417c25df8dec02a Mon Sep 17 00:00:00 2001 From: soryu Date: Sun, 18 Jan 2026 17:44:50 +0000 Subject: Add React Native mobile app for Makima (#3) * [WIP] Heartbeat checkpoint - 2026-01-18 02:58:27 UTC * feat(mobile): complete mobile app integration and verification - Add ThemeColors type export to Colors.ts for type safety - Export SUPABASE_URL from supabase.ts and use environment variables - Update .env.example with correct default URLs - Add comprehensive README.md with setup instructions Verified: - TypeScript compiles without errors - App exports successfully for iOS and Android - All screens accessible (login, dashboard, tasks, settings, task detail) - Auth flow working with Zustand store and Supabase Co-Authored-By: Claude Opus 4.5 * Task completion checkpoint --------- Co-authored-by: Claude Opus 4.5 --- apps/mobile/app/_layout.tsx | 111 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 apps/mobile/app/_layout.tsx (limited to 'apps/mobile/app/_layout.tsx') 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 ; + } + + return <>{children}; +} + +export default function RootLayout() { + const colorScheme = useColorScheme() ?? 'light'; + const colors = Colors[colorScheme]; + + return ( + + + + + + + + + + + ); +} -- cgit v1.2.3