import { type NavigationContainerRefWithCurrent } from '@react-navigation/native'; import { reportScreenChangeOnNative } from '../nativeCommunication'; import { ScreenChangePayload, ScreenHierarchyElement, } from '../ScreenChangePayload'; import { compareHierarchies, getScreenHierarchyFromRootState, } from './screenHierarchyUtils'; /** * Tracks navigation changes by subscribing to React Navigation events. */ export default class ReactNavigationTracker { private previousScreenHierarchy: ScreenHierarchyElement[]; private stopTrackingFunction?: () => void; constructor( private readonly navigationContainerRef: NavigationContainerRefWithCurrent ) { this.previousScreenHierarchy = []; } startTracking() { this.stopTrackingFunction = this.navigationContainerRef.addListener( 'state', () => { this.handleNavigationStateChange(); } ); // Handle the initial state this.handleNavigationStateChange(); } stopTracking() { this.stopTrackingFunction?.(); this.stopTrackingFunction = undefined; } private handleNavigationStateChange() { const currentScreenHierarchy = getScreenHierarchyFromRootState( this.navigationContainerRef ); const changes = compareHierarchies( currentScreenHierarchy, this.previousScreenHierarchy ); this.reportDisappearingScreens( this.previousScreenHierarchy, changes.commonPrefixLength ); this.reportAppearingScreens( currentScreenHierarchy, changes.commonPrefixLength ); this.previousScreenHierarchy = currentScreenHierarchy; } private reportDisappearingScreens( previousScreenHierarchy: ScreenHierarchyElement[], commonPrefixLength: number ) { for ( let i = previousScreenHierarchy.length - 1; i >= commonPrefixLength; i-- ) { const screenHierarchyUpToDisappearingScreen = previousScreenHierarchy.slice(0, i + 1); const screenDisappearPayload = new ScreenChangePayload( screenHierarchyUpToDisappearingScreen, 'disappear', 'auto' ); reportScreenChangeOnNative(screenDisappearPayload); } } private reportAppearingScreens( currentScreenHierarchy: ScreenHierarchyElement[], commonPrefixLength: number ) { for (let i = commonPrefixLength; i < currentScreenHierarchy.length; i++) { const screenHierarchyUpToAppearingScreen = currentScreenHierarchy.slice( 0, i + 1 ); const screenAppearPayload = new ScreenChangePayload( screenHierarchyUpToAppearingScreen, 'appear', 'auto' ); reportScreenChangeOnNative(screenAppearPayload); } } }