All files / apps/host/src/hooks/deeplink useNavigateDeepLinks.ts

92.15% Statements 47/51
65.38% Branches 17/26
100% Functions 13/13
92% Lines 46/50

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 107 108 109 110 111 112 113 114 115 116 117 118 119 120                                        3x         10x 10x   10x 18x 5x 5x 5x 5x         10x 10x   10x   4x 4x 4x       6x           10x     5x 3x 3x                   5x               10x 10x     10x 10x 2x 1x 1x 1x 1x     1x 1x     10x     10x 1x 1x     10x 2x   2x 1x 1x 1x 1x 1x     1x     10x    
import { useCallback, useEffect, useRef } from 'react';
import { Linking } from 'react-native';
 
import { useAuth } from '@/contexts/AuthContext';
 
import { extractNotificationData } from '@/services/notification/helpers';
import { notificationService } from '@/services/notification/notificationService';
import { sentryService } from '@/services/sentryService';
 
import { DEEP_LINK_PREFIX } from './constants';
import {
  type BooleanRef,
  buildDeepLinkFromNotification,
  isReady,
  navigateToDetail,
  type NavigationRef,
  parseDeepLink,
  type TimeoutRefsRef,
} from './helpers';
 
export const useNavigateDeepLinks = (
  navigationRef: NavigationRef,
  isNavigationReadyRef: BooleanRef,
  timeoutRefsRef: TimeoutRefsRef,
) => {
  const { user, loading } = useAuth();
  const pendingDeepLinkRef = useRef<string | null>(null);
 
  const processPendingDeepLink = useCallback(() => {
    if (isReady(loading, user, isNavigationReadyRef, navigationRef) && pendingDeepLinkRef.current) {
      const ticketId = parseDeepLink(pendingDeepLinkRef.current);
      Eif (ticketId) {
        navigateToDetail(navigationRef, ticketId, timeoutRefsRef, true);
        pendingDeepLinkRef.current = null;
      }
    }
  }, [loading, user, isNavigationReadyRef, navigationRef, timeoutRefsRef]);
 
  useEffect(() => {
    Eif (!loading) {
      // Check for initial deep link URL
      Linking.getInitialURL()
        .then(url => {
          Eif (url?.startsWith(DEEP_LINK_PREFIX)) {
            pendingDeepLinkRef.current = url;
            processPendingDeepLink();
          }
        })
        .catch(error => {
          sentryService.captureException(error, {
            tags: { component: 'deep_link', action: 'get_initial_url_failed' },
          });
        });
 
      // Check for initial notification (when app opened from killed state)
      notificationService
        .getInitialNotification()
        .then(notification => {
          if (notification?.data) {
            const notificationData = extractNotificationData(notification.data);
            Iif (notificationData?.category && notificationData.ticketId) {
              const deepLink = buildDeepLinkFromNotification(notificationData);
              if (deepLink) {
                pendingDeepLinkRef.current = deepLink;
                processPendingDeepLink();
              }
            }
          }
        })
        .catch(error => {
          sentryService.captureException(error, {
            tags: { component: 'deep_link', action: 'get_initial_notification_failed' },
          });
        });
    }
  }, [loading, processPendingDeepLink]);
 
  // Process pending deep link when auth becomes ready
  useEffect(() => {
    processPendingDeepLink();
  }, [loading, user, processPendingDeepLink]);
 
  useEffect(() => {
    const subscription = Linking.addEventListener('url', ({ url }) => {
      if (isReady(loading, user, isNavigationReadyRef, navigationRef)) {
        const ticketId = parseDeepLink(url);
        Eif (ticketId) {
          navigateToDetail(navigationRef, ticketId, timeoutRefsRef, false);
          return;
        }
      }
      pendingDeepLinkRef.current = url;
      processPendingDeepLink();
    });
 
    return () => subscription.remove();
  }, [loading, user, isNavigationReadyRef, navigationRef, timeoutRefsRef, processPendingDeepLink]);
 
  const setPendingDeepLink = (url: string) => {
    pendingDeepLinkRef.current = url;
    processPendingDeepLink();
  };
 
  const navigateFromDeepLink = (url: string) => {
    pendingDeepLinkRef.current = url;
 
    if (isReady(loading, user, isNavigationReadyRef, navigationRef)) {
      const ticketId = parseDeepLink(url);
      Eif (ticketId) {
        navigateToDetail(navigationRef, ticketId, timeoutRefsRef, false);
        pendingDeepLinkRef.current = null;
        return;
      }
    }
    processPendingDeepLink();
  };
 
  return { processPendingDeepLink, setPendingDeepLink, navigateFromDeepLink };
};