11import type { NavigationAction } from '@react-navigation/native' ;
2- import { useIsFocused , useNavigation } from '@react-navigation/native' ;
2+ import { useNavigation } from '@react-navigation/native' ;
33import { useCallback , useEffect , useRef } from 'react' ;
44import { ModalActions } from '@components/Modal/Global/ModalContext' ;
55import useBeforeRemove from '@hooks/useBeforeRemove' ;
66import useConfirmModal from '@hooks/useConfirmModal' ;
77import useLocalize from '@hooks/useLocalize' ;
8+ import Log from '@libs/Log' ;
89import setNavigationActionToMicrotaskQueue from '@libs/Navigation/helpers/setNavigationActionToMicrotaskQueue' ;
910import navigateAfterInteraction from '@libs/Navigation/navigateAfterInteraction' ;
1011import navigationRef from '@libs/Navigation/navigationRef' ;
1112import type { PlatformStackNavigationProp } from '@libs/Navigation/PlatformStackNavigation/types' ;
1213import type { RootNavigatorParamList } from '@libs/Navigation/types' ;
1314import type UseDiscardChangesConfirmationOptions from './types' ;
1415
15- function useDiscardChangesConfirmation ( { getHasUnsavedChanges, onCancel, onVisibilityChange, isEnabled = true } : UseDiscardChangesConfirmationOptions ) {
16+ function useDiscardChangesConfirmation ( { getHasUnsavedChanges, onCancel, onVisibilityChange, onConfirm } : UseDiscardChangesConfirmationOptions ) {
1617 const navigation = useNavigation < PlatformStackNavigationProp < RootNavigatorParamList > > ( ) ;
17- const isFocused = useIsFocused ( ) ;
1818 const { translate} = useLocalize ( ) ;
19- const { showConfirmModal, closeModal } = useConfirmModal ( ) ;
19+ const { showConfirmModal} = useConfirmModal ( ) ;
2020 const blockedNavigationAction = useRef < NavigationAction > ( undefined ) ;
2121 const shouldNavigateBack = useRef ( false ) ;
22- const isDiscardModalOpenRef = useRef ( false ) ;
2322
2423 const navigateBack = useCallback ( ( ) => {
2524 if ( blockedNavigationAction . current ) {
@@ -34,7 +33,6 @@ function useDiscardChangesConfirmation({getHasUnsavedChanges, onCancel, onVisibi
3433
3534 const showDiscardModal = useCallback ( ( ) => {
3635 onVisibilityChange ?.( true ) ;
37- isDiscardModalOpenRef . current = true ;
3836 showConfirmModal ( {
3937 title : translate ( 'discardChangesConfirmation.title' ) ,
4038 prompt : translate ( 'discardChangesConfirmation.body' ) ,
@@ -43,43 +41,41 @@ function useDiscardChangesConfirmation({getHasUnsavedChanges, onCancel, onVisibi
4341 cancelText : translate ( 'common.cancel' ) ,
4442 shouldIgnoreBackHandlerDuringTransition : true ,
4543 } ) . then ( ( result ) => {
46- isDiscardModalOpenRef . current = false ;
4744 onVisibilityChange ?.( false ) ;
4845 if ( result . action === ModalActions . CONFIRM ) {
49- setNavigationActionToMicrotaskQueue ( navigateBack ) ;
46+ Promise . resolve ( )
47+ . then ( ( ) => onConfirm ?.( ) )
48+ . then ( ( ) => {
49+ setNavigationActionToMicrotaskQueue ( navigateBack ) ;
50+ } )
51+ . catch ( ( error : unknown ) => {
52+ Log . warn ( '[useDiscardChangesConfirmation] Failed to run onConfirm callback' , { error} ) ;
53+ blockedNavigationAction . current = undefined ;
54+ shouldNavigateBack . current = false ;
55+ } ) ;
5056 } else {
5157 blockedNavigationAction . current = undefined ;
5258 shouldNavigateBack . current = false ;
5359 onCancel ?.( ) ;
5460 }
5561 } ) ;
56- } , [ showConfirmModal , translate , navigateBack , onCancel , onVisibilityChange ] ) ;
62+ } , [ showConfirmModal , translate , navigateBack , onCancel , onConfirm , onVisibilityChange ] ) ;
5763
58- useBeforeRemove (
59- useCallback (
60- ( e ) => {
61- if ( ! isEnabled || ! isFocused || ! getHasUnsavedChanges ( ) || shouldNavigateBack . current ) {
62- return ;
63- }
64-
65- e . preventDefault ( ) ;
66- blockedNavigationAction . current = e . data . action ;
67- navigateAfterInteraction ( showDiscardModal ) ;
68- } ,
69- [ getHasUnsavedChanges , isFocused , isEnabled , showDiscardModal ] ,
70- ) ,
71- isEnabled && isFocused ,
72- ) ;
64+ useBeforeRemove ( ( e ) => {
65+ if ( ! getHasUnsavedChanges ( ) || shouldNavigateBack . current ) {
66+ return ;
67+ }
68+ e . preventDefault ( ) ;
69+ blockedNavigationAction . current = e . data . action ;
70+ navigateAfterInteraction ( showDiscardModal ) ;
71+ } ) ;
7372
7473 /**
7574 * We cannot programmatically stop the browser's back navigation like react-navigation's beforeRemove.
7675 * Events like popstate and transitionStart are triggered AFTER the back navigation has already completed.
7776 * So we need to go forward to get back to the current page.
7877 */
7978 useEffect ( ( ) => {
80- if ( ! isEnabled || ! isFocused ) {
81- return undefined ;
82- }
8379 const unsubscribe = navigation . addListener ( 'transitionStart' , ( { data : { closing} } ) => {
8480 // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
8581 if ( ! getHasUnsavedChanges ( ) ) {
@@ -95,20 +91,7 @@ function useDiscardChangesConfirmation({getHasUnsavedChanges, onCancel, onVisibi
9591 } ) ;
9692
9793 return unsubscribe ;
98- } , [ navigation , getHasUnsavedChanges , isFocused , isEnabled , showDiscardModal ] ) ;
99-
100- /**
101- * When the screen loses focus (or is disabled) while the discard modal is open,
102- * close the modal and reset refs so we don't leave the modal visible or stale state.
103- */
104- useEffect ( ( ) => {
105- if ( ( isFocused && isEnabled ) || ! isDiscardModalOpenRef . current ) {
106- return ;
107- }
108- closeModal ( ) ;
109- blockedNavigationAction . current = undefined ;
110- shouldNavigateBack . current = false ;
111- } , [ isFocused , isEnabled , closeModal ] ) ;
94+ } , [ navigation , getHasUnsavedChanges , showDiscardModal ] ) ;
11295}
11396
11497export default useDiscardChangesConfirmation ;
0 commit comments