1import {
2 createNavigationContainerRef,
3 type NavigationAction,
4 type NavigationContainerRef,
5} from "@react-navigation/native";
6
7/**
8 * The app's single navigation container. Work that outlives a screen (a draft
9 * route's branch checkout, notification taps, quick actions, Connect
10 * onboarding) navigates through it instead of mounting hooks in the navigator.
11 */
12export const rootNavigationRef = createNavigationContainerRef<ReactNavigation.RootParamList>();
13
14/**
15 * Ref callback for the container. Android activity replacement mounts the new
16 * root before the old one unmounts, so the old container must not clear the
17 * ref the new one already owns.
18 */
19export function attachRootNavigation(
20 navigation: NavigationContainerRef<ReactNavigation.RootParamList> | null,
21): () => void {
22 rootNavigationRef.current = navigation;
23 return () => {
24 if (rootNavigationRef.current === navigation) rootNavigationRef.current = null;
25 };
26}
27
28const pendingUntilReady: Array<() => void> = [];
29
30/** Runs `navigate` now if the container is ready, otherwise once it becomes ready. */
31export function whenRootNavigationReady(navigate: () => void): void {
32 if (rootNavigationRef.isReady()) {
33 navigate();
34 return;
35 }
36 pendingUntilReady.push(navigate);
37}
38
39/** Wired to the container's `onReady`. */
40export function flushRootNavigationQueue(): void {
41 for (const navigate of pendingUntilReady.splice(0)) {
42 try {
43 navigate();
44 } catch (error) {
45 console.error("[navigation] queued navigation failed", error);
46 }
47 }
48}
49
50/**
51 * `useLinkTo` for app services that live outside the navigator: the same
52 * leading-'/' guard, then the href's action dispatched at the root once ready.
53 */
54export function linkToRootPath(href: string, toAction: (href: string) => NavigationAction): void {
55 if (!href.startsWith("/")) throw new Error(`The href must start with '/' (${href}).`);
56 whenRootNavigationReady(() => {
57 rootNavigationRef.dispatch({
58 target: rootNavigationRef.getRootState()?.key,
59 ...toAction(href),
60 });
61 });
62}