From ebce33dbb95c54efe2906c22b34b5bfa5f3e7ddf Mon Sep 17 00:00:00 2001
From: war-in <war-in@users.noreply.github.com>
Date: Tue, 18 Nov 2025 11:35:29 +0100
Subject: [PATCH 1/7] feat: emit modalClosed from RHP once closed

---
 src/CONST/index.ts                                          | 4 ++++
 .../AppNavigator/Navigators/RightModalNavigator.tsx         | 6 ++++--
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index eafa0bfe2ec5..ad9dafa7b83e 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -7397,6 +7397,10 @@ const CONST = {
         INVITE_ACCOUNTANT: 'inviteAccountant',
         ADD_EXPENSE_APPROVALS: 'addExpenseApprovals',
     },
+
+    MODAL_EVENTS: {
+        CLOSED: 'modalClosed'
+    },
 } as const;
 
 const CONTINUATION_DETECTION_SEARCH_FILTER_KEYS = [
diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx
index 1e2b5003d38a..67f73f107525 100644
--- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx
+++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx
@@ -1,9 +1,9 @@
 import type {NavigatorScreenParams} from '@react-navigation/native';
-import React, {useCallback, useContext, useMemo, useRef} from 'react';
+import React, {useCallback, useContext, useMemo, useRef, useEffect} from 'react';
 // We use Animated for all functionality related to wide RHP to make it easier
 // to interact with react-navigation components (e.g., CardContainer, interpolator), which also use Animated.
 // eslint-disable-next-line no-restricted-imports
-import {Animated, InteractionManager} from 'react-native';
+import {Animated, InteractionManager, DeviceEventEmitter} from 'react-native';
 import NoDropZone from '@components/DragAndDrop/NoDropZone';
 import {expandedRHPProgress, WideRHPContext} from '@components/WideRHPContextProvider';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
@@ -65,6 +65,8 @@ function RightModalNavigator({navigation, route}: RightModalNavigatorProps) {
         }, CONST.ANIMATED_TRANSITION);
     }, [navigation]);
 
+    useEffect(() => () => DeviceEventEmitter.emit(CONST.MODAL_EVENTS.CLOSED), []);
+
     return (
         <NarrowPaneContextProvider>
             <NoDropZone>

From 42f10f49d6e2d37a24357b684e87aabb55f5dc5c Mon Sep 17 00:00:00 2001
From: war-in <war-in@users.noreply.github.com>
Date: Tue, 18 Nov 2025 11:36:00 +0100
Subject: [PATCH 2/7] feat: fire callback after modal dismissed

---
 src/libs/Navigation/Navigation.ts | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts
index 96be23377cb1..489c24287bc3 100644
--- a/src/libs/Navigation/Navigation.ts
+++ b/src/libs/Navigation/Navigation.ts
@@ -3,7 +3,7 @@ import type {EventArg, NavigationAction, NavigationContainerEventMap, Navigation
 import {CommonActions, getPathFromState, StackActions} from '@react-navigation/native';
 // eslint-disable-next-line you-dont-need-lodash-underscore/omit
 import omit from 'lodash/omit';
-import {InteractionManager} from 'react-native';
+import {InteractionManager, DeviceEventEmitter} from 'react-native';
 import type {OnyxEntry} from 'react-native-onyx';
 import Onyx from 'react-native-onyx';
 import type {Writable} from 'type-fest';
@@ -33,7 +33,15 @@ import setNavigationActionToMicrotaskQueue from './helpers/setNavigationActionTo
 import {linkingConfig} from './linkingConfig';
 import {SPLIT_TO_SIDEBAR} from './linkingConfig/RELATIONS';
 import navigationRef from './navigationRef';
-import type {NavigationPartialRoute, NavigationRoute, NavigationStateRoute, ReportsSplitNavigatorParamList, RootNavigatorParamList, State} from './types';
+import type {
+    NavigationPartialRoute,
+    NavigationRef,
+    NavigationRoute,
+    NavigationStateRoute,
+    ReportsSplitNavigatorParamList,
+    RootNavigatorParamList,
+    State,
+} from './types';
 
 // Routes which are part of the flow to set up 2FA
 const SET_UP_2FA_ROUTES = new Set<Route>([
@@ -563,7 +571,7 @@ function getReportRouteByID(reportID?: string, routes: NavigationRoute[] = navig
  * For detailed information about dismissing modals,
  * see the NAVIGATION.md documentation.
  */
-const dismissModal = (ref = navigationRef) => {
+const dismissModal = ({ref = navigationRef, callback}: {ref?: NavigationRef; callback?: () => void} = {}) => {
     isNavigationReady().then(() => {
         ref.dispatch({type: CONST.NAVIGATION.ACTION_TYPE.DISMISS_MODAL});
         // Let React Navigation finish modal transition
@@ -571,6 +579,11 @@ const dismissModal = (ref = navigationRef) => {
         InteractionManager.runAfterInteractions(() => {
             fireModalDismissed();
         });
+
+        const subscription = DeviceEventEmitter.addListener(CONST.MODAL_EVENTS.CLOSED, () => {
+            subscription.remove();
+            callback?.();
+        });
     });
 };
 

From 04c241ca8a3283e7f194c413259719cdbdd641c4 Mon Sep 17 00:00:00 2001
From: war-in <war-in@users.noreply.github.com>
Date: Tue, 18 Nov 2025 11:36:38 +0100
Subject: [PATCH 3/7] refactor: remove InteractionManager from
 WorkspaceInviteMessagePage and pass callback to dismissModal

---
 src/pages/workspace/WorkspaceInviteMessagePage.tsx | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/src/pages/workspace/WorkspaceInviteMessagePage.tsx b/src/pages/workspace/WorkspaceInviteMessagePage.tsx
index 685a38100fab..d395933112b7 100644
--- a/src/pages/workspace/WorkspaceInviteMessagePage.tsx
+++ b/src/pages/workspace/WorkspaceInviteMessagePage.tsx
@@ -1,5 +1,5 @@
 import React, {useCallback, useEffect, useMemo, useState} from 'react';
-import {InteractionManager, Keyboard, View} from 'react-native';
+import {Keyboard, View} from 'react-native';
 import type {GestureResponderEvent} from 'react-native/Libraries/Types/CoreEventTypes';
 import FormProvider from '@components/Form/FormProvider';
 import InputWrapper from '@components/Form/InputWrapper';
@@ -139,11 +139,7 @@ function WorkspaceInviteMessagePage({policy, route, currentUserPersonalDetails}:
         }
 
         Navigation.setNavigationActionToMicrotaskQueue(() => {
-            Navigation.dismissModal();
-            // eslint-disable-next-line @typescript-eslint/no-deprecated
-            InteractionManager.runAfterInteractions(() => {
-                Navigation.navigate(ROUTES.WORKSPACE_MEMBERS.getRoute(route.params.policyID));
-            });
+            Navigation.dismissModal({callback: () => Navigation.navigate(ROUTES.WORKSPACE_MEMBERS.getRoute(route.params.policyID))});
         });
     };
 

From 2564ac5e9c87bb19c21bf4ce4edb52cb52c8baf7 Mon Sep 17 00:00:00 2001
From: war-in <war-in@users.noreply.github.com>
Date: Tue, 18 Nov 2025 11:43:56 +0100
Subject: [PATCH 4/7] chore: fix prettier

---
 src/CONST/index.ts                                   |  2 +-
 .../AppNavigator/Navigators/RightModalNavigator.tsx  |  4 ++--
 src/libs/Navigation/Navigation.ts                    | 12 ++----------
 3 files changed, 5 insertions(+), 13 deletions(-)

diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index ad9dafa7b83e..ac2cecf0bf19 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -7399,7 +7399,7 @@ const CONST = {
     },
 
     MODAL_EVENTS: {
-        CLOSED: 'modalClosed'
+        CLOSED: 'modalClosed',
     },
 } as const;
 
diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx
index 67f73f107525..8fa8204d6a3d 100644
--- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx
+++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx
@@ -1,9 +1,9 @@
 import type {NavigatorScreenParams} from '@react-navigation/native';
-import React, {useCallback, useContext, useMemo, useRef, useEffect} from 'react';
+import React, {useCallback, useContext, useEffect, useMemo, useRef} from 'react';
 // We use Animated for all functionality related to wide RHP to make it easier
 // to interact with react-navigation components (e.g., CardContainer, interpolator), which also use Animated.
 // eslint-disable-next-line no-restricted-imports
-import {Animated, InteractionManager, DeviceEventEmitter} from 'react-native';
+import {Animated, DeviceEventEmitter, InteractionManager} from 'react-native';
 import NoDropZone from '@components/DragAndDrop/NoDropZone';
 import {expandedRHPProgress, WideRHPContext} from '@components/WideRHPContextProvider';
 import useResponsiveLayout from '@hooks/useResponsiveLayout';
diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts
index 489c24287bc3..316238b013cc 100644
--- a/src/libs/Navigation/Navigation.ts
+++ b/src/libs/Navigation/Navigation.ts
@@ -3,7 +3,7 @@ import type {EventArg, NavigationAction, NavigationContainerEventMap, Navigation
 import {CommonActions, getPathFromState, StackActions} from '@react-navigation/native';
 // eslint-disable-next-line you-dont-need-lodash-underscore/omit
 import omit from 'lodash/omit';
-import {InteractionManager, DeviceEventEmitter} from 'react-native';
+import {DeviceEventEmitter, InteractionManager} from 'react-native';
 import type {OnyxEntry} from 'react-native-onyx';
 import Onyx from 'react-native-onyx';
 import type {Writable} from 'type-fest';
@@ -33,15 +33,7 @@ import setNavigationActionToMicrotaskQueue from './helpers/setNavigationActionTo
 import {linkingConfig} from './linkingConfig';
 import {SPLIT_TO_SIDEBAR} from './linkingConfig/RELATIONS';
 import navigationRef from './navigationRef';
-import type {
-    NavigationPartialRoute,
-    NavigationRef,
-    NavigationRoute,
-    NavigationStateRoute,
-    ReportsSplitNavigatorParamList,
-    RootNavigatorParamList,
-    State,
-} from './types';
+import type {NavigationPartialRoute, NavigationRef, NavigationRoute, NavigationStateRoute, ReportsSplitNavigatorParamList, RootNavigatorParamList, State} from './types';
 
 // Routes which are part of the flow to set up 2FA
 const SET_UP_2FA_ROUTES = new Set<Route>([

From 1a347c5a03d8695ebb5320f8d1039a8a193f2acd Mon Sep 17 00:00:00 2001
From: war-in <war-in@users.noreply.github.com>
Date: Thu, 20 Nov 2025 18:20:15 +0100
Subject: [PATCH 5/7] refactor: use dismissModal's callback instead of
 InteractionManager

---
 .../workspace/members/WorkspaceInviteMessageComponent.tsx | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/src/pages/workspace/members/WorkspaceInviteMessageComponent.tsx b/src/pages/workspace/members/WorkspaceInviteMessageComponent.tsx
index 598d105c2866..9f321afddbbf 100644
--- a/src/pages/workspace/members/WorkspaceInviteMessageComponent.tsx
+++ b/src/pages/workspace/members/WorkspaceInviteMessageComponent.tsx
@@ -1,5 +1,5 @@
 import React, {useCallback, useEffect, useMemo, useState} from 'react';
-import {InteractionManager, Keyboard, View} from 'react-native';
+import {Keyboard, View} from 'react-native';
 import type {OnyxEntry} from 'react-native-onyx';
 import type {GestureResponderEvent} from 'react-native/Libraries/Types/CoreEventTypes';
 import FormProvider from '@components/Form/FormProvider';
@@ -157,11 +157,7 @@ function WorkspaceInviteMessageComponent({
         }
 
         Navigation.setNavigationActionToMicrotaskQueue(() => {
-            Navigation.dismissModal();
-            // eslint-disable-next-line @typescript-eslint/no-deprecated
-            InteractionManager.runAfterInteractions(() => {
-                Navigation.navigate(ROUTES.WORKSPACE_MEMBERS.getRoute(policyID));
-            });
+            Navigation.dismissModal({callback: () => Navigation.navigate(ROUTES.WORKSPACE_MEMBERS.getRoute(policyID))});
         });
     };
 

From 280a68380ef1e614bf6bf54595e8e9a689efcafc Mon Sep 17 00:00:00 2001
From: war-in <war-in@users.noreply.github.com>
Date: Fri, 21 Nov 2025 12:00:26 +0100
Subject: [PATCH 6/7] refactor: move listener up to prevent race conditions

---
 src/libs/Navigation/Navigation.ts | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts
index 534e0885b8b3..ab47c2f23ac1 100644
--- a/src/libs/Navigation/Navigation.ts
+++ b/src/libs/Navigation/Navigation.ts
@@ -568,17 +568,19 @@ function getReportRouteByID(reportID?: string, routes: NavigationRoute[] = navig
  */
 const dismissModal = ({ref = navigationRef, callback}: {ref?: NavigationRef; callback?: () => void} = {}) => {
     isNavigationReady().then(() => {
+        if (callback) {
+            const subscription = DeviceEventEmitter.addListener(CONST.MODAL_EVENTS.CLOSED, () => {
+                subscription.remove();
+                callback();
+            });
+        }
+
         ref.dispatch({type: CONST.NAVIGATION.ACTION_TYPE.DISMISS_MODAL});
         // Let React Navigation finish modal transition
         // eslint-disable-next-line @typescript-eslint/no-deprecated
         InteractionManager.runAfterInteractions(() => {
             fireModalDismissed();
         });
-
-        const subscription = DeviceEventEmitter.addListener(CONST.MODAL_EVENTS.CLOSED, () => {
-            subscription.remove();
-            callback?.();
-        });
     });
 };
 

From 362115b4912ed8a684b21a4298b6a560b8b4f23c Mon Sep 17 00:00:00 2001
From: war-in <war-in@users.noreply.github.com>
Date: Fri, 21 Nov 2025 12:00:37 +0100
Subject: [PATCH 7/7] chore: add jsdoc

---
 src/libs/Navigation/Navigation.ts | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts
index ab47c2f23ac1..3dbb55d6f72b 100644
--- a/src/libs/Navigation/Navigation.ts
+++ b/src/libs/Navigation/Navigation.ts
@@ -563,6 +563,12 @@ function getReportRouteByID(reportID?: string, routes: NavigationRoute[] = navig
 
 /**
  * Closes the modal navigator (RHP, onboarding).
+ *
+ * @param options - Configuration object
+ * @param options.ref - Navigation ref to use (defaults to navigationRef)
+ * @param options.callback - Optional callback to execute after the modal has finished closing.
+ *                           The callback fires when RightModalNavigator unmounts.
+ *
  * For detailed information about dismissing modals,
  * see the NAVIGATION.md documentation.
  */
