All files / components/layout RootLayout.native.tsx

0% Statements 0/26
0% Branches 0/16
0% Functions 0/5
0% Lines 0/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101                                                                                                                                                                                                         
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { Stack, router, useRootNavigationState, useSegments } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
import { StatusBar } from 'expo-status-bar';
import { useEffect, useRef } from 'react';
import 'react-native-reanimated';
import { StripeProvider } from '@stripe/stripe-react-native';
 
import { LoadingSpinner } from '@/components/ui/LoadingSpinner';
import { ToastProvider } from '@/components/ui/Toast';
import { AuthProvider, useAuth } from '@/contexts/AuthContext';
import { BeneficiaryProvider } from '@/contexts/BeneficiaryContext';
import { useColorScheme } from '@/hooks/use-color-scheme';
 
// Stripe publishable key (test mode) - must match backend STRIPE_PUBLISHABLE_KEY
const STRIPE_PUBLISHABLE_KEY = 'pk_test_51P3kdqP0gvUw6M9C7ixPQHqbPcvga4G5kAYx1h6QXQAt1psbrC2rrmOojW0fTeQzaxD1Q9RKS3zZ23MCvjjZpWLi00eCFWRHMk';
 
// Prevent auto-hide, ignore errors if splash not available
SplashScreen.preventAutoHideAsync().catch(() => {});
 
// Track if splash screen was hidden to prevent double-hiding
let splashHidden = false;
 
function RootLayoutNav() {
  const colorScheme = useColorScheme();
  const { isAuthenticated, isInitializing } = useAuth();
  const segments = useSegments();
  const navigationState = useRootNavigationState();
 
  // Track if initial redirect was done
  const hasInitialRedirect = useRef(false);
 
  useEffect(() => {
    // Wait for navigation to be ready
    if (!navigationState?.key) {
      return;
    }
 
    // Wait for INITIAL auth check to complete
    if (isInitializing) {
      return;
    }
 
    // Hide splash screen safely (only once)
    if (!splashHidden) {
      splashHidden = true;
      SplashScreen.hideAsync().catch(() => {});
    }
 
    const inAuthGroup = segments[0] === '(auth)';
 
    // INITIAL REDIRECT (only once after app starts):
    // - If not authenticated and not in auth → go to login
    if (!hasInitialRedirect.current) {
      hasInitialRedirect.current = true;
 
      if (!isAuthenticated && !inAuthGroup) {
        router.replace('/(auth)/login');
        return;
      }
    }
 
    // If not authenticated, do NOTHING - let the auth screens handle their own navigation
 
  }, [isAuthenticated, isInitializing, navigationState?.key]);
  // IMPORTANT: isLoading NOT in deps - we don't want to react to loading state changes
  // segments also not in deps - we don't want to react to navigation changes
 
  if (isInitializing) {
    return <LoadingSpinner fullScreen message="Loading..." />;
  }
 
  return (
    <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
      <Stack screenOptions={{ headerShown: false }}>
        <Stack.Screen name="(auth)" />
        <Stack.Screen name="(tabs)" />
        <Stack.Screen name="modal" options={{ presentation: 'modal', title: 'Modal' }} />
      </Stack>
      <StatusBar style="auto" />
    </ThemeProvider>
  );
}
 
export default function RootLayout() {
  return (
    <StripeProvider
      publishableKey={STRIPE_PUBLISHABLE_KEY}
      merchantIdentifier="merchant.com.wellnuo.app"
    >
      <AuthProvider>
        <BeneficiaryProvider>
          <ToastProvider>
            <RootLayoutNav />
          </ToastProvider>
        </BeneficiaryProvider>
      </AuthProvider>
    </StripeProvider>
  );
}