From 13d29fc931e5e9d3646d3c7033501f86c275d3fe Mon Sep 17 00:00:00 2001 From: Mateusz Titz Date: Tue, 24 Sep 2024 11:15:18 +0200 Subject: [PATCH] Add backTo param handling when opening Report from Search --- src/ROUTES.ts | 14 +++++++---- src/components/PromotedActionsBar.tsx | 2 +- src/components/Search/index.tsx | 8 ++++--- .../SelectionList/Search/ReportListItem.tsx | 4 +++- src/components/SelectionList/types.ts | 5 ++++ .../linkingConfig/getAdaptedStateFromPath.ts | 24 +++++++++---------- src/libs/SearchUtils.ts | 20 ++++++++++++---- 7 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index c0ec944b71e1..3d24e3a2dafc 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -59,11 +59,9 @@ const ROUTES = { SEARCH_ADVANCED_FILTERS_IN: 'search/filters/in', SEARCH_REPORT: { route: 'search/view/:reportID/:reportActionID?', - getRoute: (reportID: string, reportActionID?: string) => { - if (reportActionID) { - return `search/view/${reportID}/${reportActionID}` as const; - } - return `search/view/${reportID}` as const; + getRoute: ({reportID, reportActionID, backTo}: {reportID: string; reportActionID?: string; backTo?: string}) => { + const baseRoute = reportActionID ? (`search/view/${reportID}/${reportActionID}` as const) : (`search/view/${reportID}` as const); + return getUrlWithBackToParam(baseRoute, backTo); }, }, TRANSACTION_HOLD_REASON_RHP: 'search/hold', @@ -1552,6 +1550,12 @@ type Route = { type RoutesValidationError = 'Error: One or more routes defined within `ROUTES` have not correctly used `as const` in their `getRoute` function return value.'; +/** + * Represents all routes in the app as a union of literal strings. + * + * If TS throws on this line, it implies that one or more routes defined within `ROUTES` have not correctly used + * `as const` in their `getRoute` function return value. + */ // eslint-disable-next-line @typescript-eslint/no-unused-vars type RouteIsPlainString = AssertTypesNotEqual; diff --git a/src/components/PromotedActionsBar.tsx b/src/components/PromotedActionsBar.tsx index c374259f0447..a2e3566b159d 100644 --- a/src/components/PromotedActionsBar.tsx +++ b/src/components/PromotedActionsBar.tsx @@ -97,7 +97,7 @@ const PromotedActions = { return; } - ReportUtils.changeMoneyRequestHoldStatus(reportAction, ROUTES.SEARCH_REPORT.getRoute(targetedReportID)); + ReportUtils.changeMoneyRequestHoldStatus(reportAction, ROUTES.SEARCH_REPORT.getRoute({reportID: targetedReportID})); }, }), } satisfies PromotedActionsType; diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 4e23c0883f84..9553300f86d1 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -227,7 +227,7 @@ function Search({queryJSON}: SearchProps) { } const ListItem = SearchUtils.getListItem(type, status); - const data = SearchUtils.getSections(type, status, searchResults.data, searchResults.search); + const data = SearchUtils.getSections(type, status, searchResults.data, searchResults.search, queryJSON.inputQuery); const sortedData = SearchUtils.getSortedSections(type, status, data, sortBy, sortOrder); const sortedSelectedData = sortedData.map((item) => mapToItemWithSelectionInfo(item, selectedTransactions, canSelectMultiple)); @@ -294,13 +294,15 @@ function Search({queryJSON}: SearchProps) { SearchActions.createTransactionThread(hash, item.transactionID, reportID, item.moneyRequestReportActionID); } + const backTo = ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: queryJSON.inputQuery}); + if (SearchUtils.isReportActionListItemType(item)) { const reportActionID = item.reportActionID; - Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(reportID, reportActionID)); + Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID, reportActionID, backTo})); return; } - Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(reportID)); + Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID, backTo})); }; const fetchMoreResults = () => { diff --git a/src/components/SelectionList/Search/ReportListItem.tsx b/src/components/SelectionList/Search/ReportListItem.tsx index a0b96547bcd8..63705bda90f1 100644 --- a/src/components/SelectionList/Search/ReportListItem.tsx +++ b/src/components/SelectionList/Search/ReportListItem.tsx @@ -84,7 +84,9 @@ function ReportListItem({ }; const openReportInRHP = (transactionItem: TransactionListItemType) => { - Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(transactionItem.transactionThreadReportID)); + const backTo = ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: reportItem.currentSearchQuery}); + + Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: transactionItem.transactionThreadReportID, backTo})); }; if (!reportItem?.reportName && reportItem.transactions.length > 1) { diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index c1085c71e0f6..c6c53efd8cfe 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -1,5 +1,6 @@ import type {MutableRefObject, ReactElement, ReactNode} from 'react'; import type {GestureResponderEvent, InputModeOptions, LayoutChangeEvent, SectionListData, StyleProp, TextInput, TextStyle, ViewStyle} from 'react-native'; +import type {SearchQueryString} from '@components/Search/types'; import type {BrickRoad} from '@libs/WorkspacesSettingsUtils'; // eslint-disable-next-line no-restricted-imports import type CursorStyles from '@styles/utils/cursor/types'; @@ -233,7 +234,11 @@ type ReportListItemType = ListItem & /** The personal details of the user paying the request */ to: SearchPersonalDetails; + /** List of transactions that belong to this report */ transactions: TransactionListItemType[]; + + /** The current search query that was used to display these report items */ + currentSearchQuery: SearchQueryString; }; type ListItemProps = CommonListItemProps & { diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index 2c96e5796309..f92b133d719a 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -114,22 +114,22 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat if (route.params && 'backTo' in route.params && typeof route.params.backTo === 'string') { const stateForBackTo = getStateFromPath(route.params.backTo, config); if (stateForBackTo) { - // eslint-disable-next-line @typescript-eslint/no-shadow - const rhpNavigator = stateForBackTo.routes.find((route) => route.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR); - - const centralPaneOrFullScreenNavigator = stateForBackTo.routes.find( - // eslint-disable-next-line @typescript-eslint/no-shadow - (route) => isCentralPaneName(route.name) || route.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR, - ); - // If there is rhpNavigator in the state generated for backTo url, we want to get root route matching to this rhp screen. + const rhpNavigator = stateForBackTo.routes.find((rt) => rt.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR); if (rhpNavigator && rhpNavigator.state) { return getMatchingRootRouteForRHPRoute(findFocusedRoute(stateForBackTo) as NavigationPartialRoute); } - // If we know that backTo targets the root route (central pane or full screen) we want to use it. - if (centralPaneOrFullScreenNavigator && centralPaneOrFullScreenNavigator.state) { - return centralPaneOrFullScreenNavigator as NavigationPartialRoute; + // If we know that backTo targets the root route (full screen) we want to use it. + const fullScreenNavigator = stateForBackTo.routes.find((rt) => rt.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR); + if (fullScreenNavigator && fullScreenNavigator.state) { + return fullScreenNavigator as NavigationPartialRoute; + } + + // If we know that backTo targets a central pane screen we want to use it. + const centralPaneScreen = stateForBackTo.routes.find((rt) => isCentralPaneName(rt.name)); + if (centralPaneScreen) { + return centralPaneScreen as NavigationPartialRoute; } } } @@ -191,7 +191,7 @@ function getAdaptedState(state: PartialState if (focusedRHPRoute) { let matchingRootRoute = getMatchingRootRouteForRHPRoute(focusedRHPRoute); const isRHPScreenOpenedFromLHN = focusedRHPRoute?.name && RHP_SCREENS_OPENED_FROM_LHN.includes(focusedRHPRoute?.name as RHPScreenOpenedFromLHN); - // This may happen if this RHP doens't have a route that should be under the overlay defined. + // This may happen if this RHP doesn't have a route that should be under the overlay defined. if (!matchingRootRoute || isRHPScreenOpenedFromLHN) { metainfo.isCentralPaneAndBottomTabMandatory = false; metainfo.isFullScreenNavigatorMandatory = false; diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 9760ff80ca19..eedb08b30924 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -252,7 +252,7 @@ function getIOUReportName(data: OnyxTypes.SearchResults['data'], reportItem: Sea return reportItem.reportName; } -function getReportSections(data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search']): ReportListItemType[] { +function getReportSections(data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search'], currentSearchQuery: SearchQueryString): ReportListItemType[] { const shouldShowMerchant = getShouldShowMerchant(data); const doesDataContainAPastYearTransaction = shouldShowYear(data); @@ -271,6 +271,7 @@ function getReportSections(data: OnyxTypes.SearchResults['data'], metadata: Onyx from: data.personalDetailsList?.[reportItem.accountID ?? -1], to: reportItem.managerID ? data.personalDetailsList?.[reportItem.managerID] : emptyPersonalDetails, transactions, + currentSearchQuery, reportName: isIOUReport ? getIOUReportName(data, reportItem) : reportItem.reportName, }; } else if (isTransactionEntry(key)) { @@ -313,21 +314,30 @@ function getListItem(type: SearchDataTypes, status: SearchStatus): ListItemType< if (type === CONST.SEARCH.DATA_TYPES.CHAT) { return ChatListItem; } - return status === CONST.SEARCH.STATUS.EXPENSE.ALL ? TransactionListItem : ReportListItem; + if (status === CONST.SEARCH.STATUS.EXPENSE.ALL) { + return TransactionListItem; + } + return ReportListItem; } -function getSections(type: SearchDataTypes, status: SearchStatus, data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search']) { +function getSections(type: SearchDataTypes, status: SearchStatus, data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search'], currentSearchQuery: SearchQueryString) { if (type === CONST.SEARCH.DATA_TYPES.CHAT) { return getReportActionsSections(data); } - return status === CONST.SEARCH.STATUS.EXPENSE.ALL ? getTransactionsSections(data, metadata) : getReportSections(data, metadata); + if (status === CONST.SEARCH.STATUS.EXPENSE.ALL) { + return getTransactionsSections(data, metadata); + } + return getReportSections(data, metadata, currentSearchQuery); } function getSortedSections(type: SearchDataTypes, status: SearchStatus, data: ListItemDataType, sortBy?: SearchColumnType, sortOrder?: SortOrder) { if (type === CONST.SEARCH.DATA_TYPES.CHAT) { return getSortedReportActionData(data as ReportActionListItemType[]); } - return status === CONST.SEARCH.STATUS.EXPENSE.ALL ? getSortedTransactionData(data as TransactionListItemType[], sortBy, sortOrder) : getSortedReportData(data as ReportListItemType[]); + if (status === CONST.SEARCH.STATUS.EXPENSE.ALL) { + return getSortedTransactionData(data as TransactionListItemType[], sortBy, sortOrder); + } + return getSortedReportData(data as ReportListItemType[]); } function getSortedTransactionData(data: TransactionListItemType[], sortBy?: SearchColumnType, sortOrder?: SortOrder) {