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

100% Statements 20/20
93.33% Branches 14/15
100% Functions 5/5
100% Lines 18/18

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                                  4x 2x 2x 2x     4x       4x 3x   2x 2x     4x           3x 3x 3x           1x             3x     3x     4x           2x    
import { InteractionManager } from 'react-native';
 
import { NavigationContainerRef } from '@react-navigation/native';
 
import { SCREENS } from '@repo/constants/screens';
 
import type { AppStackParamList } from '@/types/navigation';
 
import { sentryService } from '@/services/sentryService';
 
import { CATEGORY_DEEP_LINK_MAP, DEEP_LINK_PATHS, DEEP_LINK_PREFIX } from './constants';
 
export type NavigationRef = React.RefObject<NavigationContainerRef<AppStackParamList> | null>;
export type TimeoutRefsRef = React.MutableRefObject<TimeoutHandle[]>;
export type BooleanRef = React.MutableRefObject<boolean>;
export type TimeoutHandle = number | { cancel: () => void };
 
export const parseDeepLink = (url: string): string | null => {
  const escapedPrefix = DEEP_LINK_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  const match = url.match(new RegExp(`${escapedPrefix}request/detail/(.+)`));
  return match ? match[1] : null;
};
 
export const buildDeepLinkFromNotification = (data: {
  category: string;
  ticketId?: string;
}): string | null => {
  if (!data.category) return null;
  if (data.ticketId) return `${DEEP_LINK_PREFIX}${DEEP_LINK_PATHS.REQUEST_DETAIL}/${data.ticketId}`;
 
  const category = data.category.toLowerCase();
  return CATEGORY_DEEP_LINK_MAP[category] || `${DEEP_LINK_PREFIX}${DEEP_LINK_PATHS.HOME}`;
};
 
export const navigateToDetail = (
  navigationRef: NavigationRef,
  ticketId: string,
  timeoutRefs: TimeoutRefsRef,
  waitForInteractions = false,
) => {
  const navigate = () => {
    try {
      navigationRef.current?.navigate(SCREENS.REQUEST_DETAIL, {
        id: ticketId,
        fromDeepLink: true,
        refreshTimeStamp: Date.now(),
      });
    } catch (error) {
      sentryService.captureException(error, {
        tags: { component: 'deep_link', action: 'navigate_failed' },
        extra: { ticketId },
      });
    }
  };
 
  const handle = waitForInteractions
    ? InteractionManager.runAfterInteractions(navigate)
    : requestAnimationFrame(navigate);
  timeoutRefs.current.push(handle);
};
 
export const isReady = (
  loading: boolean,
  user: unknown,
  isReadyRef: BooleanRef,
  navRef: NavigationRef,
) => {
  return !loading && !!user && isReadyRef.current && !!navRef.current;
};