Files
jrshikoku/lib/rootNavigation.ts

89 lines
2.7 KiB
TypeScript

import { createNavigationContainerRef, StackActions } from "@react-navigation/native";
export const rootNavigationRef = createNavigationContainerRef<any>();
export const lastObservedRootRouteRef: { current: string | null } = { current: null };
export const startupExplicitTargetRef: { current: boolean } = { current: false };
const startupPendingResolversRef: { current: number } = { current: 2 };
/** positions タブの Stack.Navigator navigation を登録するグローバルref */
export const positionsStackNavRef: { current: any } = { current: null };
export const positionsLifecycleRef: {
current: {
isUnstable: boolean;
blockTabExit: boolean;
resetBeforeLeave: (() => void) | null;
deactivateStack: (() => void) | null;
};
} = {
current: {
isUnstable: false,
blockTabExit: false,
resetBeforeLeave: null,
deactivateStack: null,
},
};
/**
* 遷移先タブのネストスタックを一度 popToTop してからナビゲートする。
* ウィジェットや外部リンクからの遷移時に、既存の開いている画面を閉じてから目的の画面へ移動するために使用する。
*/
export function stackAwareNavigate(tabName: string, params?: any) {
if (!rootNavigationRef.isReady()) return;
const doNavigate = () => {
if (params !== undefined) {
rootNavigationRef.navigate(tabName, params);
} else {
rootNavigationRef.navigate(tabName as any);
}
};
// positions タブは直接 stackNavRef を使って確実に popToTop する
if (tabName === "positions" && positionsStackNavRef.current) {
const stackNav = positionsStackNavRef.current;
if ((stackNav.getState()?.index ?? 0) > 0) {
stackNav.popToTop();
setTimeout(doNavigate, 350);
} else {
doNavigate();
}
return;
}
const state = rootNavigationRef.getState();
const tabRoute = state?.routes?.find((r: any) => r.name === tabName);
if (tabRoute?.state && (tabRoute.state.index ?? 0) > 0) {
rootNavigationRef.dispatch({
...StackActions.popToTop(),
target: tabRoute.state.key,
});
// popToTop のアニメーション完了後に navigate を実行
setTimeout(doNavigate, 350);
} else {
doNavigate();
}
}
export function markStartupExplicitTarget() {
startupExplicitTargetRef.current = true;
}
export function resolveStartupNavigationSource() {
startupPendingResolversRef.current = Math.max(
0,
startupPendingResolversRef.current - 1
);
}
export async function waitForStartupNavigationResolution(timeoutMs = 2000) {
const startedAt = Date.now();
while (startupPendingResolversRef.current > 0) {
if (Date.now() - startedAt >= timeoutMs) {
break;
}
await new Promise((resolve) => setTimeout(resolve, 50));
}
}