All files / apps/host/src/navigation index.tsx

90% Statements 27/30
75% Branches 6/8
66.66% Functions 6/9
90% Lines 27/30

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 102 103 104 105 106                                        1x 11x 11x   11x 11x 11x     11x   11x 5x     11x           11x 11x   11x   11x 9x                 11x 5x 5x     5x       11x 6x 6x 6x     11x 1x             10x                                     1x              
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ActivityIndicator, StyleSheet, View } from 'react-native';
 
import { NavigationContainerRef } from '@react-navigation/native';
 
import { CoreNavigation } from '@repo/core/navigation/CoreNavigation';
 
import { useAuth } from '@/contexts/AuthContext';
import { DeepLinkProvider } from '@/contexts/DeepLinkContext';
import { FabProvider } from '@/contexts/FabContext';
 
import { linkingConfig, useNavigateDeepLinks } from '@/hooks/deeplink';
 
import type { AppStackParamList } from '@/types/navigation';
 
import { AuthNavigator } from './AuthStackNavigation';
import { Navigation as AppNavigator } from './Navigation';
 
type TimeoutHandle = number | { cancel: () => void };
 
export const Navigation = () => {
  const { user, loading } = useAuth();
  const prevUserRef = useRef(user);
 
  const navigationRef = useRef<NavigationContainerRef<AppStackParamList>>(null);
  const isNavigationReadyRef = useRef(false);
  const timeoutRefsRef = useRef<TimeoutHandle[]>([]);
 
  // Derive isLoggedOut during render to avoid one-render delay from state update
  const isLoggedOut = !!prevUserRef.current && !user;
 
  useEffect(() => {
    prevUserRef.current = user;
  }, [user]);
 
  const { processPendingDeepLink, setPendingDeepLink, navigateFromDeepLink } = useNavigateDeepLinks(
    navigationRef,
    isNavigationReadyRef,
    timeoutRefsRef,
  );
 
  const contextValueRef = useRef({ setPendingDeepLink, navigateFromDeepLink });
  contextValueRef.current = { setPendingDeepLink, navigateFromDeepLink };
 
  const [isNavigationReadyState, setIsNavigationReadyState] = useState(false);
 
  const contextValue = useMemo(
    () => ({
      setPendingDeepLink: (url: string) => contextValueRef.current.setPendingDeepLink(url),
      navigateFromDeepLink: (url: string) => contextValueRef.current.navigateFromDeepLink(url),
      navigationRef,
      isNavigationReady: isNavigationReadyState,
    }),
    [isNavigationReadyState],
  );
 
  useEffect(() => {
    return () => {
      timeoutRefsRef.current.forEach(handle => {
        typeof handle === 'number' ? cancelAnimationFrame(handle) : handle.cancel();
      });
      timeoutRefsRef.current = [];
    };
  }, []);
 
  const handleNavigationReady = useCallback(() => {
    isNavigationReadyRef.current = true;
    setIsNavigationReadyState(true);
    processPendingDeepLink();
  }, [processPendingDeepLink]);
 
  if (loading) {
    return (
      <View style={styles.loadingContainer} testID="loading-indicator">
        <ActivityIndicator size="large" />
      </View>
    );
  }
 
  return (
    <DeepLinkProvider value={contextValue}>
      <CoreNavigation
        navigationRef={navigationRef}
        linking={linkingConfig}
        onReady={handleNavigationReady}
      >
        {user ? (
          <FabProvider>
            <AppNavigator />
          </FabProvider>
        ) : (
          <AuthNavigator isLoggedOut={isLoggedOut} />
        )}
      </CoreNavigation>
    </DeepLinkProvider>
  );
};
 
const styles = StyleSheet.create({
  loadingContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
});