Skip to content

Commit d4347a6

Browse files
authored
Merge pull request #61774 from software-mansion-labs/better-expense-report-view/go-to-parent
[Better Expense Report View] Update the go to parent logic in Reports tab
2 parents 060c78b + 485074d commit d4347a6

File tree

8 files changed

+89
-46
lines changed

8 files changed

+89
-46
lines changed

src/components/AvatarWithDisplayName.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ type AvatarWithDisplayNameProps = {
6363

6464
/** Transactions inside report */
6565
transactions?: TransactionListItemType[];
66+
67+
/** Whether to open the parent report link in the current tab if possible */
68+
openParentReportInCurrentTab?: boolean;
6669
};
6770

6871
const fallbackIcon: Icon = {
@@ -156,6 +159,7 @@ function AvatarWithDisplayName({
156159
shouldEnableDetailPageNavigation = false,
157160
shouldUseCustomSearchTitleName = false,
158161
transactions = [],
162+
openParentReportInCurrentTab = false,
159163
}: AvatarWithDisplayNameProps) {
160164
const [parentReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID}`, {canEvict: false, canBeMissing: false});
161165
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}) ?? CONST.EMPTY_OBJECT;
@@ -267,6 +271,7 @@ function AvatarWithDisplayName({
267271
parentReportID={report?.parentReportID}
268272
parentReportActionID={report?.parentReportActionID}
269273
pressableStyles={[styles.alignSelfStart, styles.mw100]}
274+
openParentReportInCurrentTab={openParentReportInCurrentTab}
270275
/>
271276
)}
272277
{!!subtitle && (

src/components/HeaderWithBackButton/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ function HeaderWithBackButton({
7272
progressBarPercentage,
7373
style,
7474
subTitleLink = '',
75+
openParentReportInCurrentTab = false,
7576
}: HeaderWithBackButtonProps) {
7677
const theme = useTheme();
7778
const styles = useThemeStyles();
@@ -101,6 +102,7 @@ function HeaderWithBackButton({
101102
report={report}
102103
policy={policy}
103104
shouldEnableDetailPageNavigation={shouldEnableDetailPageNavigation}
105+
openParentReportInCurrentTab={openParentReportInCurrentTab}
104106
/>
105107
);
106108
}
@@ -133,6 +135,7 @@ function HeaderWithBackButton({
133135
title,
134136
titleColor,
135137
translate,
138+
openParentReportInCurrentTab,
136139
]);
137140

138141
return (

src/components/HeaderWithBackButton/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ type HeaderWithBackButtonProps = Partial<ChildrenProps> & {
156156

157157
/** The URL link associated with the attachment's subtitle, if available */
158158
subTitleLink?: string;
159+
160+
/** Whether to open the parent report link in the current tab if possible */
161+
openParentReportInCurrentTab?: boolean;
159162
};
160163

161164
export type {ThreeDotsMenuItem};

src/components/MoneyRequestHeader.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
254254
shouldDisplayHelpButton={!isReportInRHP}
255255
onBackButtonPress={onBackButtonPress}
256256
shouldEnableDetailPageNavigation
257+
openParentReportInCurrentTab
257258
>
258259
{!shouldUseNarrowLayout && (
259260
<View style={[styles.flexRow, styles.gap2]}>

src/components/ParentNavigationSubtitle.tsx

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
1+
import {useRoute} from '@react-navigation/native';
12
import React from 'react';
23
import type {StyleProp, ViewStyle} from 'react-native';
34
import {useOnyx} from 'react-native-onyx';
45
import useLocalize from '@hooks/useLocalize';
6+
import useRootNavigationState from '@hooks/useRootNavigationState';
57
import useThemeStyles from '@hooks/useThemeStyles';
8+
import {isFullScreenName} from '@libs/Navigation/helpers/isNavigatorName';
69
import Navigation from '@libs/Navigation/Navigation';
10+
import type {SearchFullscreenNavigatorParamList} from '@libs/Navigation/types';
711
import {getReportAction, shouldReportActionBeVisible} from '@libs/ReportActionsUtils';
812
import {canUserPerformWriteAction as canUserPerformWriteActionReportUtils} from '@libs/ReportUtils';
913
import CONST from '@src/CONST';
1014
import type {ParentNavigationSummaryParams} from '@src/languages/params';
15+
import NAVIGATORS from '@src/NAVIGATORS';
1116
import ONYXKEYS from '@src/ONYXKEYS';
1217
import ROUTES from '@src/ROUTES';
18+
import SCREENS from '@src/SCREENS';
1319
import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback';
1420
import Text from './Text';
1521

@@ -24,14 +30,26 @@ type ParentNavigationSubtitleProps = {
2430

2531
/** PressableWithoutFeedback additional styles */
2632
pressableStyles?: StyleProp<ViewStyle>;
33+
34+
/** Whether to open the parent report link in the current tab if possible */
35+
openParentReportInCurrentTab?: boolean;
2736
};
2837

29-
function ParentNavigationSubtitle({parentNavigationSubtitleData, parentReportActionID, parentReportID = '', pressableStyles}: ParentNavigationSubtitleProps) {
38+
function ParentNavigationSubtitle({
39+
parentNavigationSubtitleData,
40+
parentReportActionID,
41+
parentReportID = '',
42+
pressableStyles,
43+
openParentReportInCurrentTab = false,
44+
}: ParentNavigationSubtitleProps) {
45+
const currentRoute = useRoute();
3046
const styles = useThemeStyles();
3147
const {workspaceName, reportName} = parentNavigationSubtitleData;
3248
const {translate} = useLocalize();
33-
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`);
49+
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`, {canBeMissing: false});
3450
const canUserPerformWriteAction = canUserPerformWriteActionReportUtils(report);
51+
const isReportInRHP = currentRoute.name === SCREENS.SEARCH.REPORT_RHP;
52+
const currentFullScreenRoute = useRootNavigationState((state) => state?.routes?.findLast((route) => isFullScreenName(route.name)));
3553

3654
// We should not display the parent navigation subtitle if the user does not have access to the parent chat (the reportName is empty in this case)
3755
if (!reportName) {
@@ -43,6 +61,31 @@ function ParentNavigationSubtitle({parentNavigationSubtitleData, parentReportAct
4361
onPress={() => {
4462
const parentAction = getReportAction(parentReportID, parentReportActionID);
4563
const isVisibleAction = shouldReportActionBeVisible(parentAction, parentAction?.reportActionID ?? CONST.DEFAULT_NUMBER_ID, canUserPerformWriteAction);
64+
65+
if (openParentReportInCurrentTab && isReportInRHP) {
66+
// If the report is displayed in RHP in Reports tab, we want to stay in the current tab after opening the parent report
67+
if (currentFullScreenRoute?.name === NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR) {
68+
const lastRoute = currentFullScreenRoute?.state?.routes.at(-1);
69+
if (lastRoute?.name === SCREENS.SEARCH.MONEY_REQUEST_REPORT) {
70+
const moneyRequestReportID = (lastRoute?.params as SearchFullscreenNavigatorParamList[typeof SCREENS.SEARCH.MONEY_REQUEST_REPORT])?.reportID;
71+
// If the parent report is already displayed underneath RHP, simply dismiss the modal
72+
if (moneyRequestReportID === parentReportID) {
73+
Navigation.dismissModal();
74+
return;
75+
}
76+
}
77+
78+
Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID: parentReportID}));
79+
return;
80+
}
81+
82+
// If the parent report is already displayed underneath RHP, simply dismiss the modal
83+
if (Navigation.getTopmostReportId() === parentReportID) {
84+
Navigation.dismissModal();
85+
return;
86+
}
87+
}
88+
4689
if (isVisibleAction) {
4790
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(parentReportID, parentReportActionID));
4891
} else {

src/libs/Navigation/AppNavigator/AuthScreens.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import getCurrentUrl from '@libs/Navigation/currentUrl';
3030
import Navigation from '@libs/Navigation/Navigation';
3131
import Animations from '@libs/Navigation/PlatformStackNavigation/navigationOptions/animation';
3232
import Presentation from '@libs/Navigation/PlatformStackNavigation/navigationOptions/presentation';
33+
import type {PlatformStackNavigationOptions} from '@libs/Navigation/PlatformStackNavigation/types';
3334
import type {AuthScreensParamList} from '@libs/Navigation/types';
3435
import NetworkConnection from '@libs/NetworkConnection';
3536
import onyxSubscribe from '@libs/onyxSubscribe';
@@ -61,7 +62,12 @@ import type {SelectedTimezone, Timezone} from '@src/types/onyx/PersonalDetails';
6162
import {isEmptyObject} from '@src/types/utils/EmptyObject';
6263
import type ReactComponentModule from '@src/types/utils/ReactComponentModule';
6364
import createRootStackNavigator from './createRootStackNavigator';
64-
import {reportsSplitsWithEnteringAnimation, settingsSplitWithEnteringAnimation, workspaceSplitsWithoutEnteringAnimation} from './createRootStackNavigator/GetStateForActionHandlers';
65+
import {
66+
reportsSplitsWithEnteringAnimation,
67+
searchFullscreenWithEnteringAnimation,
68+
settingsSplitWithEnteringAnimation,
69+
workspaceSplitsWithoutEnteringAnimation,
70+
} from './createRootStackNavigator/GetStateForActionHandlers';
6571
import defaultScreenOptions from './defaultScreenOptions';
6672
import {ShareModalStackNavigator} from './ModalStackNavigators';
6773
import ExplanationModalNavigator from './Navigators/ExplanationModalNavigator';
@@ -462,27 +468,30 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
462468
};
463469
};
464470

465-
const getSplitNavigatorOptions =
466-
(routesWithEnteringAnimation: Set<string>) =>
471+
const getFullScreenNavigatorOptions =
472+
(routesWithEnteringAnimation: Set<string>, defaultOptions: PlatformStackNavigationOptions) =>
467473
({route}: {route: RouteProp<AuthScreensParamList>}) => {
468474
// We don't need to do anything special for the wide screen.
469475
if (!shouldUseNarrowLayout) {
470-
return rootNavigatorScreenOptions.splitNavigator;
476+
return defaultOptions;
471477
}
472478
// On the narrow screen, we want to animate this navigator if pushed SplitNavigator includes desired screen
473479
const animationEnabled = routesWithEnteringAnimation.has(route.key);
474480

475481
return {
476-
...rootNavigatorScreenOptions.splitNavigator,
482+
...defaultOptions,
477483
animation: animationEnabled ? Animations.SLIDE_FROM_RIGHT : Animations.NONE,
478484
};
479485
};
480486

481487
// Animation is enabled when navigating to the report screen
482-
const getReportsSplitNavigatorOptions = getSplitNavigatorOptions(reportsSplitsWithEnteringAnimation);
488+
const getReportsSplitNavigatorOptions = getFullScreenNavigatorOptions(reportsSplitsWithEnteringAnimation, rootNavigatorScreenOptions.splitNavigator);
483489

484490
// Animation is enabled when navigating to any screen different than SCREENS.SETTINGS.ROOT
485-
const getSettingsSplitNavigatorOptions = getSplitNavigatorOptions(settingsSplitWithEnteringAnimation);
491+
const getSettingsSplitNavigatorOptions = getFullScreenNavigatorOptions(settingsSplitWithEnteringAnimation, rootNavigatorScreenOptions.splitNavigator);
492+
493+
// Animation is enabled when navigating to SCREENS.SEARCH.MONEY_REQUEST_REPORT
494+
const getSearchFullscreenNavigatorOptions = getFullScreenNavigatorOptions(searchFullscreenWithEnteringAnimation, rootNavigatorScreenOptions.fullScreen);
486495

487496
const clearStatus = () => {
488497
User.clearCustomStatus();
@@ -533,7 +542,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
533542
/>
534543
<RootStack.Screen
535544
name={NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR}
536-
options={rootNavigatorScreenOptions.fullScreen}
545+
options={getSearchFullscreenNavigatorOptions}
537546
getComponent={loadSearchNavigator}
538547
/>
539548
<RootStack.Screen

src/libs/Navigation/AppNavigator/createRootStackNavigator/GetStateForActionHandlers.ts

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import type {CommonActions, RouterConfigOptions, StackActionType, StackNavigatio
22
import {StackActions} from '@react-navigation/native';
33
import type {ParamListBase, Router} from '@react-navigation/routers';
44
import Log from '@libs/Log';
5-
import getPolicyIDFromState from '@libs/Navigation/helpers/getPolicyIDFromState';
6-
import type {RootNavigatorParamList, State} from '@libs/Navigation/types';
5+
import type {RootNavigatorParamList} from '@libs/Navigation/types';
76
import * as SearchQueryUtils from '@libs/SearchQueryUtils';
87
import NAVIGATORS from '@src/NAVIGATORS';
98
import SCREENS from '@src/SCREENS';
@@ -29,7 +28,7 @@ const MODAL_ROUTES_TO_DISMISS: string[] = [
2928
const workspaceSplitsWithoutEnteringAnimation = new Set<string>();
3029
const reportsSplitsWithEnteringAnimation = new Set<string>();
3130
const settingsSplitWithEnteringAnimation = new Set<string>();
32-
31+
const searchFullscreenWithEnteringAnimation = new Set<string>();
3332
/**
3433
* Handles the OPEN_WORKSPACE_SPLIT action.
3534
* If the user is on other tab than workspaces and the workspace split is "remembered", this action will be called after pressing the settings tab.
@@ -202,44 +201,23 @@ function handlePushSearchPageAction(
202201
action: PushActionType,
203202
configOptions: RouterConfigOptions,
204203
stackRouter: Router<StackNavigationState<ParamListBase>, CommonActions.Action | StackActionType>,
205-
setActiveWorkspaceID: (workspaceID: string | undefined) => void,
206204
) {
207-
let updatedAction = action;
208-
const currentParams = action.payload.params as RootNavigatorParamList[typeof NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR];
209-
if (currentParams?.screen === SCREENS.SEARCH.ROOT) {
210-
const searchParams = currentParams?.params;
211-
const queryJSON = SearchQueryUtils.buildSearchQueryJSON(searchParams.q);
212-
if (!queryJSON) {
213-
return null;
214-
}
205+
const stateWithSearchFullscreenNavigator = stackRouter.getStateForAction(state, action, configOptions);
215206

216-
if (!queryJSON.policyID) {
217-
const policyID = getPolicyIDFromState(state as State<RootNavigatorParamList>);
207+
if (!stateWithSearchFullscreenNavigator) {
208+
Log.hmmm('[handlePushSettingsAction] SearchFullscreenNavigator has not been found in the navigation state.');
209+
return null;
210+
}
218211

219-
if (policyID) {
220-
queryJSON.policyID = policyID;
221-
} else {
222-
delete queryJSON.policyID;
223-
}
224-
} else {
225-
setActiveWorkspaceID(queryJSON.policyID);
226-
}
212+
const lastFullScreenRoute = stateWithSearchFullscreenNavigator.routes.at(-1);
213+
const actionPayloadScreen = action.payload?.params && 'screen' in action.payload.params ? action.payload?.params?.screen : undefined;
227214

228-
updatedAction = {
229-
...action,
230-
payload: {
231-
...action.payload,
232-
params: {
233-
...action.payload.params,
234-
params: {
235-
q: SearchQueryUtils.buildSearchQueryString(queryJSON),
236-
},
237-
},
238-
},
239-
};
215+
// Transitioning to SCREENS.SEARCH.MONEY_REQUEST_REPORT should be animated
216+
if (actionPayloadScreen === SCREENS.SEARCH.MONEY_REQUEST_REPORT && lastFullScreenRoute?.key) {
217+
searchFullscreenWithEnteringAnimation.add(lastFullScreenRoute.key);
240218
}
241219

242-
return stackRouter.getStateForAction(state, updatedAction, configOptions);
220+
return stateWithSearchFullscreenNavigator;
243221
}
244222

245223
function handleReplaceReportsSplitNavigatorAction(
@@ -309,6 +287,7 @@ export {
309287
handleSwitchPolicyIDAction,
310288
handleSwitchPolicyIDFromSearchAction,
311289
reportsSplitsWithEnteringAnimation,
290+
searchFullscreenWithEnteringAnimation,
312291
settingsSplitWithEnteringAnimation,
313292
workspaceSplitsWithoutEnteringAnimation,
314293
};

src/libs/Navigation/AppNavigator/createRootStackNavigator/RootStackRouter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ function RootStackRouter(options: RootStackNavigatorRouterOptions) {
111111
}
112112

113113
if (action.payload.name === NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR) {
114-
return handlePushSearchPageAction(state, action, configOptions, stackRouter, setActiveWorkspaceID);
114+
return handlePushSearchPageAction(state, action, configOptions, stackRouter);
115115
}
116116

117117
if (action.payload.name === NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR) {

0 commit comments

Comments
 (0)