diff --git a/src/components/InvertedFlatList/BaseInvertedFlatList.js b/src/components/InvertedFlatList/BaseInvertedFlatList.js index 802ae373d22a..90b4e02e2e4a 100644 --- a/src/components/InvertedFlatList/BaseInvertedFlatList.js +++ b/src/components/InvertedFlatList/BaseInvertedFlatList.js @@ -4,7 +4,8 @@ import {FlatList as NativeFlatlist, View} from 'react-native'; import _ from 'underscore'; import FlatList from '@components/FlatList'; import * as CollectionUtils from '@libs/CollectionUtils'; -import variables from '@styles/variables'; + +const AUTOSCROLL_TO_TOP_THRESHOLD = 128; const propTypes = { /** Same as FlatList can be any array of anything */ @@ -136,7 +137,7 @@ function BaseInvertedFlatList(props) { windowSize={15} maintainVisibleContentPosition={{ minIndexForVisible: 0, - autoscrollToTopThreshold: variables.listItemHeightNormal, + autoscrollToTopThreshold: AUTOSCROLL_TO_TOP_THRESHOLD, }} inverted /> diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index 7564955020fa..e12cb5545240 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -1,4 +1,4 @@ -import {getActionFromState} from '@react-navigation/core'; +import {findFocusedRoute, getActionFromState} from '@react-navigation/core'; import {CommonActions, getPathFromState, StackActions} from '@react-navigation/native'; import _ from 'lodash'; import lodashGet from 'lodash/get'; @@ -73,6 +73,31 @@ const getActiveRouteIndex = function (route, index) { return index; }; +/** + * Gets distance from the path in root navigator. In other words how much screen you have to pop to get to the route with this path. + * The search is limited to 5 screens from the top for performance reasons. + * @param {String} path - Path that you are looking for. + * @return {Number} - Returns distance to path or -1 if the path is not found in root navigator. + */ +function getDistanceFromPathInRootNavigator(path) { + let currentState = navigationRef.getRootState(); + + for (let index = 0; index < 5; index++) { + if (!currentState.routes.length) { + break; + } + + const pathFromState = getPathFromState(currentState, linkingConfig.config); + if (path === pathFromState.substring(1)) { + return index; + } + + currentState = {...currentState, routes: currentState.routes.slice(0, -1), index: currentState.index - 1}; + } + + return -1; +} + /** * Main navigation method for redirecting to a route. * @param {String} route @@ -114,7 +139,6 @@ function goBack(fallbackRoute, shouldEnforceFallback = false, shouldPopToTop = f } const isFirstRouteInNavigator = !getActiveRouteIndex(navigationRef.current.getState()); - if (isFirstRouteInNavigator) { const rootState = navigationRef.getRootState(); const lastRoute = _.last(rootState.routes); @@ -130,6 +154,21 @@ function goBack(fallbackRoute, shouldEnforceFallback = false, shouldPopToTop = f return; } + const isCentralPaneFocused = findFocusedRoute(navigationRef.current.getState()).name === NAVIGATORS.CENTRAL_PANE_NAVIGATOR; + const distanceFromPathInRootNavigator = getDistanceFromPathInRootNavigator(fallbackRoute); + + // Allow CentralPane to use UP with fallback route if the path is not found in root navigator. + if (isCentralPaneFocused && fallbackRoute && distanceFromPathInRootNavigator === -1) { + navigate(fallbackRoute, CONST.NAVIGATION.TYPE.FORCED_UP); + return; + } + + // Add posibility to go back more than one screen in root navigator if that screen is on the stack. + if (isCentralPaneFocused && fallbackRoute && distanceFromPathInRootNavigator > 0) { + navigationRef.current.dispatch(StackActions.pop(distanceFromPathInRootNavigator)); + return; + } + navigationRef.current.goBack(); } diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 86b933a6897d..347fe69236d6 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -2188,15 +2188,13 @@ function deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView // STEP 7: Navigate the user depending on which page they are on and which resources were deleted if (isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) { // Pop the deleted report screen before navigating. This prevents navigating to the Concierge chat due to the missing report. - Navigation.goBack(ROUTES.HOME); - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(iouReport.reportID)); + Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(iouReport.reportID)); return; } if (shouldDeleteIOUReport) { // Pop the deleted report screen before navigating. This prevents navigating to the Concierge chat due to the missing report. - Navigation.goBack(ROUTES.HOME); - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(iouReport.chatReportID)); + Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(iouReport.chatReportID)); } } diff --git a/tests/actions/IOUTest.js b/tests/actions/IOUTest.js index 76098d72f52e..1c258f6e0f37 100644 --- a/tests/actions/IOUTest.js +++ b/tests/actions/IOUTest.js @@ -2635,14 +2635,14 @@ describe('actions/IOU', () => { // Then we expect to navigate to the iou report - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.REPORT_WITH_ID.getRoute(IOU_REPORT_ID)); + expect(Navigation.goBack).toHaveBeenCalledWith(ROUTES.REPORT_WITH_ID.getRoute(IOU_REPORT_ID)); }); it('navigate the user correctly to the chat Report when appropriate', () => { // When we delete the money request and we should delete the IOU report IOU.deleteMoneyRequest(transaction.transactionID, createIOUAction, false); // Then we expect to navigate to the chat report - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.REPORT_WITH_ID.getRoute(chatReport.reportID)); + expect(Navigation.goBack).toHaveBeenCalledWith(ROUTES.REPORT_WITH_ID.getRoute(chatReport.reportID)); }); });