@@ -12,7 +12,10 @@ import {
1212 getNavigationKind ,
1313 shouldSkipPathNavigation ,
1414} from './navigation-state'
15- import { getNativeBackCallbackDecision } from './native-back-sync'
15+ import {
16+ getNativeBackCallbackDecision ,
17+ shouldCompleteNativeBackSyncOnVisiblePath ,
18+ } from './native-back-sync'
1619import { createDebugLogger } from './debug-log'
1720import type {
1821 NavigationGuard ,
@@ -46,7 +49,6 @@ export function NativeScriptRouterProvider(props: NativeScriptRouterProviderProp
4649 let suppressedNativeBackCallbacks = 0
4750 let nativeBackSyncInFlight = false
4851 let nativeBackSyncFromPath : string | null = null
49- let nativeBackSyncTimeoutId : ReturnType < typeof setTimeout > | undefined
5052 let queuedNativeBackCount = 0
5153 let skipNextFrameBackNavigation = false
5254 // Shared guard to prevent circular updates between router and Frame
@@ -298,30 +300,26 @@ export function NativeScriptRouterProvider(props: NativeScriptRouterProviderProp
298300 acquireGuard ( 'native_back_callback' )
299301 log ( '[NSRouter] native back pop -> router.history.back()' )
300302 router . history . back ( )
301-
302- if ( nativeBackSyncTimeoutId ) {
303- clearTimeout ( nativeBackSyncTimeoutId )
304- }
305-
306- // Failsafe: if router state does not settle quickly, unlock and continue.
307- nativeBackSyncTimeoutId = setTimeout ( ( ) => {
308- if ( ! nativeBackSyncInFlight ) {
309- return
310- }
311-
312- log ( '[NSRouter] native back sync timeout; forcing in-flight release' )
313- nativeBackSyncInFlight = false
314- nativeBackSyncFromPath = null
315- if ( guard . lockReason === 'native_back_callback' ) {
316- releaseGuard ( )
317- }
318- setTimeout ( tryDrainQueuedNativeBack , 0 )
319- } , 250 )
320303 }
321304
322305 const reconcileRouterToVisiblePath = ( visiblePath : string ) => {
323306 const activePath = router . state . location . pathname
324307 if ( visiblePath === activePath ) {
308+ // Native visibility has aligned with router path, so any in-flight native
309+ // back sync can be deterministically completed from this event.
310+ if ( shouldCompleteNativeBackSyncOnVisiblePath ( {
311+ inFlight : nativeBackSyncInFlight ,
312+ visiblePath,
313+ activePath,
314+ } ) ) {
315+ nativeBackSyncInFlight = false
316+ nativeBackSyncFromPath = null
317+ if ( guard . lockReason === 'native_back_callback' ) {
318+ log ( '[NSRouter] native back sync completed on visible-path alignment' )
319+ releaseGuard ( )
320+ }
321+ setTimeout ( tryDrainQueuedNativeBack , 0 )
322+ }
325323 return
326324 }
327325
@@ -337,17 +335,42 @@ export function NativeScriptRouterProvider(props: NativeScriptRouterProviderProp
337335 nativeBackSyncFromPath = null
338336 queuedNativeBackCount = 0
339337 skipNextFrameBackNavigation = false
340- if ( nativeBackSyncTimeoutId ) {
341- clearTimeout ( nativeBackSyncTimeoutId )
342- nativeBackSyncTimeoutId = undefined
343- }
344338
345339 acquireGuard ( 'native_visible_path_reconcile' )
346340 router . navigate ( {
347341 to : visiblePath ,
348342 replace : true ,
349343 } as any )
350- setTimeout ( releaseGuard , 0 )
344+
345+ const settlePromise = router . load ?.( )
346+
347+ if ( settlePromise && typeof settlePromise . then === 'function' ) {
348+ settlePromise
349+ . then ( ( ) => {
350+ log (
351+ '[NSRouter] visible-path reconcile settled:' ,
352+ 'status=' ,
353+ router . state . status ,
354+ 'path=' ,
355+ router . state . location . pathname ,
356+ )
357+ } )
358+ . catch ( ( err : any ) => {
359+ console . error ( '[NSRouter] visible-path reconcile load rejected:' , err )
360+ } )
361+ . finally ( ( ) => {
362+ if ( guard . lockReason === 'native_visible_path_reconcile' ) {
363+ releaseGuard ( )
364+ }
365+ setTimeout ( tryDrainQueuedNativeBack , 0 )
366+ } )
367+ return
368+ }
369+
370+ if ( guard . lockReason === 'native_visible_path_reconcile' ) {
371+ releaseGuard ( )
372+ }
373+ setTimeout ( tryDrainQueuedNativeBack , 0 )
351374 }
352375
353376 const tryDrainQueuedNativeBack = ( ) => {
@@ -392,10 +415,6 @@ export function NativeScriptRouterProvider(props: NativeScriptRouterProviderProp
392415 const cleanupBack = setupBackHandler ( router , ( ) => frameRef , guard )
393416
394417 onCleanup ( ( ) => {
395- if ( nativeBackSyncTimeoutId ) {
396- clearTimeout ( nativeBackSyncTimeoutId )
397- nativeBackSyncTimeoutId = undefined
398- }
399418 closeModalFromRouterState ( )
400419 unsub ( )
401420 cleanupBack ( )
@@ -424,10 +443,6 @@ export function NativeScriptRouterProvider(props: NativeScriptRouterProviderProp
424443 ) {
425444 nativeBackSyncInFlight = false
426445 nativeBackSyncFromPath = null
427- if ( nativeBackSyncTimeoutId ) {
428- clearTimeout ( nativeBackSyncTimeoutId )
429- nativeBackSyncTimeoutId = undefined
430- }
431446 if ( guard . lockReason === 'native_back_callback' ) {
432447 log ( '[NSRouter] native back callback releasing guard' )
433448 releaseGuard ( )
0 commit comments