From bdeccb1fc437fa36465a30df81df934eea116717 Mon Sep 17 00:00:00 2001 From: X Developer Date: Tue, 21 Jul 2026 08:23:44 +0430 Subject: [PATCH] Fix cropped map after editing odometer in Distance flow --- src/libs/Navigation/OnyxTabNavigator.tsx | 83 +++++++++++++------ .../iou/request/DistanceRequestStartPage.tsx | 5 +- 2 files changed, 61 insertions(+), 27 deletions(-) diff --git a/src/libs/Navigation/OnyxTabNavigator.tsx b/src/libs/Navigation/OnyxTabNavigator.tsx index 72bac08f6692..1d35be5453be 100644 --- a/src/libs/Navigation/OnyxTabNavigator.tsx +++ b/src/libs/Navigation/OnyxTabNavigator.tsx @@ -19,6 +19,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {SelectedTabRequest} from '@src/types/onyx'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; +import KeyboardUtils from '@src/utils/keyboard'; import type {MaterialTopTabNavigationEventMap} from '@react-navigation/material-top-tabs'; import type {EventArg, EventMapCore, NavigationProp, NavigationState, ParamListBase, ScreenListeners} from '@react-navigation/native'; @@ -26,7 +27,7 @@ import type {EventArg, EventMapCore, NavigationProp, NavigationState, ParamListB import {createMaterialTopTabNavigator} from '@react-navigation/material-top-tabs'; import {TabActions, useRoute} from '@react-navigation/native'; import React, {useCallback, useContext, useEffect, useRef, useState} from 'react'; -import {StyleSheet, View} from 'react-native'; +import {Keyboard, StyleSheet, View} from 'react-native'; import type {RegisterTabSwitchGuard, TabSwitchGuard} from './TabSwitchGuardContext'; @@ -71,6 +72,9 @@ type OnyxTabNavigatorProps = Child /** Whether tabs should have equal width */ equalWidth?: boolean; + + /** Whether to wait for the keyboard to close before switching tabs */ + shouldDismissKeyboardBeforeTabSwitch?: boolean; }; const TopTab = createMaterialTopTabNavigator(); @@ -113,6 +117,7 @@ function OnyxTabNavigator({ lazyLoadEnabled = false, onTabSelect, equalWidth = false, + shouldDismissKeyboardBeforeTabSwitch = false, ...rest }: OnyxTabNavigatorProps) { const styles = useThemeStyles(); @@ -154,6 +159,7 @@ function OnyxTabNavigator({ // Tab-switch discard guards, keyed by tab name. Tab screens register via `useDiscardChangesConfirmation`. const guardsRef = useRef>(new Map()); const isDiscardModalOpenRef = useRef(false); + const isTabSwitchPendingRef = useRef(false); const registerTabGuard: RegisterTabSwitchGuard = (guard) => { guardsRef.current.set(guard.tabName, guard); @@ -166,43 +172,68 @@ function OnyxTabNavigator({ }; }; + const runAfterKeyboardDismiss = (callback: () => void) => { + if (!shouldDismissKeyboardBeforeTabSwitch || !Keyboard.isVisible()) { + callback(); + return; + } + + isTabSwitchPendingRef.current = true; + KeyboardUtils.dismiss() + .then(callback) + .finally(() => { + isTabSwitchPendingRef.current = false; + }); + }; + const handleTabPress = (navigation: NavigationProp, event: EventArg<'tabPress', true, undefined>) => { - if (isDiscardModalOpenRef.current) { + if (isDiscardModalOpenRef.current || isTabSwitchPendingRef.current) { event.preventDefault(); return; } const navState = navigation.getState(); const currentRouteName = navState.routes.at(navState.index)?.name; - const guard = currentRouteName ? guardsRef.current.get(currentRouteName) : undefined; - if (!guard || !guard.getHasUnsavedChanges()) { - return; - } const targetRoute = navState.routes.find((tabRoute) => tabRoute.key === event.target); if (!targetRoute || targetRoute.name === currentRouteName) { return; } - event.preventDefault(); - isDiscardModalOpenRef.current = true; - showConfirmModal({ - ...getDiscardChangesModalConfig(translate), - shouldIgnoreBackHandlerDuringTransition: true, - }).then((result) => { - isDiscardModalOpenRef.current = false; - if (result.action !== ModalActions.CONFIRM) { - guard.onCancel?.(); + const guard = currentRouteName ? guardsRef.current.get(currentRouteName) : undefined; + if (!guard || !guard.getHasUnsavedChanges()) { + if (!shouldDismissKeyboardBeforeTabSwitch || !Keyboard.isVisible()) { return; } - // User confirmed: always jump to the target tab, even if onDiscard fails, rather than stranding them with no feedback. - Promise.resolve() - .then(() => guard.onDiscard()) - .catch((error: unknown) => { - Log.warn('[OnyxTabNavigator] Failed to run tab-switch onDiscard callback', {error}); - Growl.error(translate('common.genericErrorMessage')); - }) - .then(() => { - navigation.dispatch(TabActions.jumpTo(targetRoute.name)); - }); - }); + + event.preventDefault(); + runAfterKeyboardDismiss(() => navigation.dispatch(TabActions.jumpTo(targetRoute.name))); + return; + } + event.preventDefault(); + isDiscardModalOpenRef.current = true; + + const showDiscardModal = () => { + showConfirmModal({ + ...getDiscardChangesModalConfig(translate), + shouldIgnoreBackHandlerDuringTransition: true, + }).then((result) => { + isDiscardModalOpenRef.current = false; + if (result.action !== ModalActions.CONFIRM) { + guard.onCancel?.(); + return; + } + // User confirmed: always jump to the target tab, even if onDiscard fails, rather than stranding them with no feedback. + Promise.resolve() + .then(() => guard.onDiscard()) + .catch((error: unknown) => { + Log.warn('[OnyxTabNavigator] Failed to run tab-switch onDiscard callback', {error}); + Growl.error(translate('common.genericErrorMessage')); + }) + .then(() => { + runAfterKeyboardDismiss(() => navigation.dispatch(TabActions.jumpTo(targetRoute.name))); + }); + }); + }; + + runAfterKeyboardDismiss(showDiscardModal); }; /** diff --git a/src/pages/iou/request/DistanceRequestStartPage.tsx b/src/pages/iou/request/DistanceRequestStartPage.tsx index 1f2202d02864..cf6a3fdb68d8 100644 --- a/src/pages/iou/request/DistanceRequestStartPage.tsx +++ b/src/pages/iou/request/DistanceRequestStartPage.tsx @@ -3,6 +3,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import TabSelector from '@components/TabSelector/TabSelector'; +import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePolicyForTransaction from '@hooks/usePolicyForTransaction'; @@ -56,6 +57,7 @@ function DistanceRequestStartPage({ const [lastDistanceExpenseType] = useOnyx(ONYXKEYS.NVP_LAST_DISTANCE_EXPENSE_TYPE); const isLoadingSelectedTab = isLoadingOnyxValue(selectedTabResult); const isTrackDistanceExpense = iouType === CONST.IOU.TYPE.TRACK; + const {isKeyboardShown} = useKeyboardState(); const tabTitles = { [CONST.IOU.TYPE.REQUEST]: translate('iou.trackDistance'), @@ -112,7 +114,7 @@ function DistanceRequestStartPage({ accessVariants={[CONST.IOU.ACCESS_VARIANTS.CREATE]} > {() => (