import type { NavigationContainerRefWithCurrent, NavigationState, PartialState, } from '@react-navigation/native'; import { ScreenHierarchyElement } from '../ScreenChangePayload'; /** * Compares screen hierarchies and returns information about what changed. */ export function compareHierarchies( currentScreenHierarchy: ScreenHierarchyElement[], previousScreenHierarchy: ScreenHierarchyElement[] ) { const commonPrefixLength = findCommonPrefixLength( currentScreenHierarchy, previousScreenHierarchy ); return { commonPrefixLength, disappearingScreens: previousScreenHierarchy.slice(commonPrefixLength), appearingScreens: currentScreenHierarchy.slice(commonPrefixLength), }; } /** * Extracts screen hierarchy from the navigation state. */ export function getScreenHierarchyFromRootState( navigationContainerRef: NavigationContainerRefWithCurrent ): ScreenHierarchyElement[] { const rootState = navigationContainerRef.getRootState(); const rootNavigator: ScreenHierarchyElement[] = rootState?.type ? [new ScreenHierarchyElement('RootNavigator', rootState.type)] : []; return getScreenHierarchyRecursively(rootState, rootNavigator); } function findCommonPrefixLength( current: ScreenHierarchyElement[], previous: ScreenHierarchyElement[] ): number { const maxPossiblePrefixLength = Math.min(current.length, previous.length); for (let i = 0; i < maxPossiblePrefixLength; i++) { const currentScreen = current[i]; const previousScreen = previous[i]; if (currentScreen?.screenName !== previousScreen?.screenName) { return i; } } return maxPossiblePrefixLength; } function getScreenHierarchyRecursively( state: NavigationState | PartialState, screenHierarchy: ScreenHierarchyElement[] = [] ): ScreenHierarchyElement[] { if (state?.index === undefined) { return screenHierarchy; } const route = state?.routes?.[state.index]; if (!route) return screenHierarchy; let screenType = route?.state?.type ?? 'screen'; const updatedHierarchy = [ ...screenHierarchy, new ScreenHierarchyElement(route.name, screenType), ]; if (route.state) { return getScreenHierarchyRecursively(route.state, updatedHierarchy); } return updatedHierarchy; }