@@ -5,6 +5,9 @@ import {usePopoverActions, usePopoverState} from '@components/PopoverProvider';
55import PopoverWithoutOverlay from '@components/PopoverWithoutOverlay' ;
66import useResponsiveLayout from '@hooks/useResponsiveLayout' ;
77import useSidePanelState from '@hooks/useSidePanelState' ;
8+ import subscribeToRootNavigation from '@libs/Navigation/helpers/subscribeToRootNavigation' ;
9+ import Navigation from '@libs/Navigation/Navigation' ;
10+ import navigationRef from '@libs/Navigation/navigationRef' ;
811import TooltipRefManager from '@libs/TooltipRefManager' ;
912import CONST from '@src/CONST' ;
1013import type PopoverProps from './types' ;
@@ -55,20 +58,41 @@ function Popover(props: PopoverProps) {
5558 React . useEffect ( ( ) => {
5659 // When this Popover manages its own back-guard (`shouldHandleNavigationBack`), the Modal-level
5760 // history sync (useSyncModalWithHistory) closes it on browser Back and consumes the entry. This
58- // listener only covers the other case: dismissing the popover when the browser navigation changes
59- // for some other reason, without intercepting that navigation.
60- if ( ! shouldCloseWhenBrowserNavigationChanged || props . shouldHandleNavigationBack ) {
61+ // listener only covers the other case: dismissing the popover when the active navigation route
62+ // changes, without intercepting that navigation.
63+ //
64+ // We subscribe to React Navigation state events rather than raw `popstate` so that
65+ // `navigationRef.getCurrentRoute()` is already fresh when the callback fires. Sentinel-only
66+ // history changes (e.g. a nested YearPickerModal opening/closing) do NOT change the focused
67+ // route key, so the calendar popover stays open. A real navigation away changes the key and
68+ // closes the popover.
69+ if ( ! shouldCloseWhenBrowserNavigationChanged || props . shouldHandleNavigationBack || ! isVisible ) {
6170 return ;
6271 }
63- const listener = ( ) => {
64- if ( ! isVisible ) {
72+
73+ let isActive = true ;
74+ let baselineKey : string | undefined ;
75+ // Holds the unsubscribe function once the subscription is set up asynchronously.
76+ const unsubscribeRef : { current : ( ( ) => void ) | undefined } = { current : undefined } ;
77+
78+ Navigation . isNavigationReady ( ) . then ( ( ) => {
79+ if ( ! isActive ) {
6580 return ;
6681 }
67- onClose ?.( ) ;
68- } ;
69- window . addEventListener ( 'popstate' , listener ) ;
82+ baselineKey = navigationRef . getCurrentRoute ( ) ?. key ;
83+ unsubscribeRef . current = subscribeToRootNavigation ( ( ) => {
84+ if ( ! isActive || baselineKey === undefined ) {
85+ return ;
86+ }
87+ if ( navigationRef . getCurrentRoute ( ) ?. key !== baselineKey ) {
88+ onClose ?.( ) ;
89+ }
90+ } ) ;
91+ } ) ;
92+
7093 return ( ) => {
71- window . removeEventListener ( 'popstate' , listener ) ;
94+ isActive = false ;
95+ unsubscribeRef . current ?.( ) ;
7296 } ;
7397 } , [ onClose , isVisible , shouldCloseWhenBrowserNavigationChanged , props . shouldHandleNavigationBack ] ) ;
7498
0 commit comments