diff --git a/src/pages/iou/request/step/DiscardChangesConfirmation/index.native.tsx b/src/pages/iou/request/step/DiscardChangesConfirmation/index.native.tsx index da853ae17a960..53c68c0c965a3 100644 --- a/src/pages/iou/request/step/DiscardChangesConfirmation/index.native.tsx +++ b/src/pages/iou/request/step/DiscardChangesConfirmation/index.native.tsx @@ -6,7 +6,7 @@ import useLocalize from '@hooks/useLocalize'; import navigationRef from '@libs/Navigation/navigationRef'; import type DiscardChangesConfirmationProps from './types'; -function DiscardChangesConfirmation({getHasUnsavedChanges, isEnabled = true}: DiscardChangesConfirmationProps) { +function DiscardChangesConfirmation({getHasUnsavedChanges, onVisibilityChange, isEnabled = true}: DiscardChangesConfirmationProps) { const {translate} = useLocalize(); const isFocused = useIsFocused(); const [isVisible, setIsVisible] = useState(false); @@ -16,12 +16,23 @@ function DiscardChangesConfirmation({getHasUnsavedChanges, isEnabled = true}: Di const hasUnsavedChanges = isEnabled && isFocused && getHasUnsavedChanges(); const shouldPrevent = hasUnsavedChanges && !shouldAllowNavigation.current; + const setModalVisible = useCallback( + (nextVisible: boolean) => { + setIsVisible(nextVisible); + onVisibilityChange?.(nextVisible); + }, + [onVisibilityChange], + ); + usePreventRemove( shouldPrevent, - useCallback(({data}) => { - blockedNavigationAction.current = data.action; - setIsVisible(true); - }, []), + useCallback( + ({data}) => { + blockedNavigationAction.current = data.action; + setModalVisible(true); + }, + [setModalVisible], + ), ); return ( @@ -33,7 +44,7 @@ function DiscardChangesConfirmation({getHasUnsavedChanges, isEnabled = true}: Di confirmText={translate('discardChangesConfirmation.confirmText')} cancelText={translate('common.cancel')} onConfirm={() => { - setIsVisible(false); + setModalVisible(false); shouldAllowNavigation.current = true; if (blockedNavigationAction.current) { navigationRef.current?.dispatch(blockedNavigationAction.current); @@ -43,7 +54,7 @@ function DiscardChangesConfirmation({getHasUnsavedChanges, isEnabled = true}: Di } }} onCancel={() => { - setIsVisible(false); + setModalVisible(false); blockedNavigationAction.current = undefined; }} shouldHandleNavigationBack diff --git a/src/pages/iou/request/step/DiscardChangesConfirmation/index.tsx b/src/pages/iou/request/step/DiscardChangesConfirmation/index.tsx index 40a2ab96c88c9..97346763cd279 100644 --- a/src/pages/iou/request/step/DiscardChangesConfirmation/index.tsx +++ b/src/pages/iou/request/step/DiscardChangesConfirmation/index.tsx @@ -11,7 +11,7 @@ import type {PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNa import type {RootNavigatorParamList} from '@libs/Navigation/types'; import type DiscardChangesConfirmationProps from './types'; -function DiscardChangesConfirmation({getHasUnsavedChanges, onCancel, isEnabled = true}: DiscardChangesConfirmationProps) { +function DiscardChangesConfirmation({getHasUnsavedChanges, onVisibilityChange, onCancel, isEnabled = true}: DiscardChangesConfirmationProps) { const navigation = useNavigation>(); const isFocused = useIsFocused(); const {translate} = useLocalize(); @@ -20,6 +20,14 @@ function DiscardChangesConfirmation({getHasUnsavedChanges, onCancel, isEnabled = const shouldNavigateBack = useRef(false); const isConfirmed = useRef(false); + const setModalVisible = useCallback( + (nextVisible: boolean) => { + setIsVisible(nextVisible); + onVisibilityChange?.(nextVisible); + }, + [onVisibilityChange], + ); + useBeforeRemove( useCallback( (e) => { @@ -29,9 +37,9 @@ function DiscardChangesConfirmation({getHasUnsavedChanges, onCancel, isEnabled = e.preventDefault(); blockedNavigationAction.current = e.data.action; - navigateAfterInteraction(() => setIsVisible((prev) => !prev)); + navigateAfterInteraction(() => setModalVisible(true)); }, - [getHasUnsavedChanges, isFocused, isEnabled], + [getHasUnsavedChanges, isFocused, isEnabled, setModalVisible], ), isEnabled && isFocused, ); @@ -59,20 +67,20 @@ function DiscardChangesConfirmation({getHasUnsavedChanges, onCancel, isEnabled = } // Navigation.navigate() rerenders the current page and resets its states window.history.go(1); - navigateAfterInteraction(() => setIsVisible((prev) => !prev)); + navigateAfterInteraction(() => setModalVisible(true)); }); return unsubscribe; - }, [navigation, getHasUnsavedChanges, isFocused, isEnabled]); + }, [navigation, getHasUnsavedChanges, isFocused, isEnabled, setModalVisible]); useEffect(() => { if ((isFocused && isEnabled) || !isVisible) { return; } - setIsVisible(false); + setModalVisible(false); blockedNavigationAction.current = undefined; shouldNavigateBack.current = false; - }, [isFocused, isVisible, isEnabled]); + }, [isFocused, isVisible, isEnabled, setModalVisible]); const navigateBack = useCallback(() => { if (blockedNavigationAction.current) { @@ -95,10 +103,10 @@ function DiscardChangesConfirmation({getHasUnsavedChanges, onCancel, isEnabled = cancelText={translate('common.cancel')} onConfirm={() => { isConfirmed.current = true; - setIsVisible(false); + setModalVisible(false); }} onCancel={() => { - setIsVisible(false); + setModalVisible(false); blockedNavigationAction.current = undefined; shouldNavigateBack.current = false; }} diff --git a/src/pages/iou/request/step/DiscardChangesConfirmation/types.ts b/src/pages/iou/request/step/DiscardChangesConfirmation/types.ts index 95ee6cfa4da44..40a7b6e43bfc7 100644 --- a/src/pages/iou/request/step/DiscardChangesConfirmation/types.ts +++ b/src/pages/iou/request/step/DiscardChangesConfirmation/types.ts @@ -1,6 +1,14 @@ type DiscardChangesConfirmationProps = { + /** Checks if there are any unsaved changes */ getHasUnsavedChanges: () => boolean; + + /** Notifies when the discard modal visibility changes */ + onVisibilityChange?: (isVisible: boolean) => void; + + /** Called when the modal is closed without confirming */ onCancel?: () => void; + + /** Whether the discard confirmation should be enabled */ isEnabled?: boolean; }; diff --git a/src/pages/iou/request/step/IOURequestStepDescription.tsx b/src/pages/iou/request/step/IOURequestStepDescription.tsx index 4db2941d75847..16d413ead0979 100644 --- a/src/pages/iou/request/step/IOURequestStepDescription.tsx +++ b/src/pages/iou/request/step/IOURequestStepDescription.tsx @@ -75,6 +75,7 @@ function IOURequestStepDescription({ const [currentDescription, setCurrentDescription] = useState(currentDescriptionInMarkdown); const [isSaved, setIsSaved] = useState(false); + const [isDiscardModalVisible, setIsDiscardModalVisible] = useState(false); const shouldNavigateAfterSaveRef = useRef(false); useRestartOnReceiptFailure(transaction, reportID, iouType, action); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); @@ -211,6 +212,7 @@ function IOURequestStepDescription({ label={translate('moneyRequestConfirmationList.whatsItFor')} accessibilityLabel={translate('moneyRequestConfirmationList.whatsItFor')} role={CONST.ROLE.PRESENTATION} + editable={!isDiscardModalVisible} autoGrowHeight maxAutoGrowHeight={variables.textInputAutoGrowMaxHeight} shouldSubmitForm @@ -222,6 +224,7 @@ function IOURequestStepDescription({ { // eslint-disable-next-line @typescript-eslint/no-deprecated InteractionManager.runAfterInteractions(() => { diff --git a/src/pages/iou/request/step/IOURequestStepMerchant.tsx b/src/pages/iou/request/step/IOURequestStepMerchant.tsx index bc8c04eceaffd..79980158253f0 100644 --- a/src/pages/iou/request/step/IOURequestStepMerchant.tsx +++ b/src/pages/iou/request/step/IOURequestStepMerchant.tsx @@ -64,6 +64,7 @@ function IOURequestStepMerchant({ const initialMerchant = isEmptyMerchant ? '' : merchant; const [currentMerchant, setCurrentMerchant] = useState(initialMerchant); const [isSaved, setIsSaved] = useState(false); + const [isDiscardModalVisible, setIsDiscardModalVisible] = useState(false); const shouldNavigateAfterSaveRef = useRef(false); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const currentUserAccountIDParam = currentUserPersonalDetails.accountID; @@ -188,11 +189,13 @@ function IOURequestStepMerchant({ label={translate('common.merchant')} accessibilityLabel={translate('common.merchant')} role={CONST.ROLE.PRESENTATION} + editable={!isDiscardModalVisible} ref={inputCallbackRef} /> { // eslint-disable-next-line @typescript-eslint/no-deprecated InteractionManager.runAfterInteractions(() => {