diff --git a/src/components/AvatarWithDisplayName.tsx b/src/components/AvatarWithDisplayName.tsx index 1de01ad5a01a..aa2e4603c85d 100644 --- a/src/components/AvatarWithDisplayName.tsx +++ b/src/components/AvatarWithDisplayName.tsx @@ -63,6 +63,9 @@ type AvatarWithDisplayNameProps = { /** Transactions inside report */ transactions?: TransactionListItemType[]; + + /** Whether to open the parent report link in the current tab if possible */ + openParentReportInCurrentTab?: boolean; }; const fallbackIcon: Icon = { @@ -156,6 +159,7 @@ function AvatarWithDisplayName({ shouldEnableDetailPageNavigation = false, shouldUseCustomSearchTitleName = false, transactions = [], + openParentReportInCurrentTab = false, }: AvatarWithDisplayNameProps) { const [parentReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID}`, {canEvict: false, canBeMissing: false}); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}) ?? CONST.EMPTY_OBJECT; @@ -267,6 +271,7 @@ function AvatarWithDisplayName({ parentReportID={report?.parentReportID} parentReportActionID={report?.parentReportActionID} pressableStyles={[styles.alignSelfStart, styles.mw100]} + openParentReportInCurrentTab={openParentReportInCurrentTab} /> )} {!!subtitle && ( diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx index 58c782bdd1bb..acd792ad8675 100755 --- a/src/components/HeaderWithBackButton/index.tsx +++ b/src/components/HeaderWithBackButton/index.tsx @@ -72,6 +72,7 @@ function HeaderWithBackButton({ progressBarPercentage, style, subTitleLink = '', + openParentReportInCurrentTab = false, }: HeaderWithBackButtonProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -101,6 +102,7 @@ function HeaderWithBackButton({ report={report} policy={policy} shouldEnableDetailPageNavigation={shouldEnableDetailPageNavigation} + openParentReportInCurrentTab={openParentReportInCurrentTab} /> ); } @@ -133,6 +135,7 @@ function HeaderWithBackButton({ title, titleColor, translate, + openParentReportInCurrentTab, ]); return ( diff --git a/src/components/HeaderWithBackButton/types.ts b/src/components/HeaderWithBackButton/types.ts index e5750757b13d..9e87e44f0456 100644 --- a/src/components/HeaderWithBackButton/types.ts +++ b/src/components/HeaderWithBackButton/types.ts @@ -156,6 +156,9 @@ type HeaderWithBackButtonProps = Partial & { /** The URL link associated with the attachment's subtitle, if available */ subTitleLink?: string; + + /** Whether to open the parent report link in the current tab if possible */ + openParentReportInCurrentTab?: boolean; }; export type {ThreeDotsMenuItem}; diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index 21d113030029..6f4378e99b99 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -254,6 +254,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre shouldDisplayHelpButton={!isReportInRHP} onBackButtonPress={onBackButtonPress} shouldEnableDetailPageNavigation + openParentReportInCurrentTab > {!shouldUseNarrowLayout && ( diff --git a/src/components/ParentNavigationSubtitle.tsx b/src/components/ParentNavigationSubtitle.tsx index 0c5e3b2f27fc..97919d2249e5 100644 --- a/src/components/ParentNavigationSubtitle.tsx +++ b/src/components/ParentNavigationSubtitle.tsx @@ -1,15 +1,21 @@ +import {useRoute} from '@react-navigation/native'; import React from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; +import useRootNavigationState from '@hooks/useRootNavigationState'; import useThemeStyles from '@hooks/useThemeStyles'; +import {isFullScreenName} from '@libs/Navigation/helpers/isNavigatorName'; import Navigation from '@libs/Navigation/Navigation'; +import type {SearchFullscreenNavigatorParamList} from '@libs/Navigation/types'; import {getReportAction, shouldReportActionBeVisible} from '@libs/ReportActionsUtils'; import {canUserPerformWriteAction as canUserPerformWriteActionReportUtils} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import type {ParentNavigationSummaryParams} from '@src/languages/params'; +import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; import Text from './Text'; @@ -24,14 +30,26 @@ type ParentNavigationSubtitleProps = { /** PressableWithoutFeedback additional styles */ pressableStyles?: StyleProp; + + /** Whether to open the parent report link in the current tab if possible */ + openParentReportInCurrentTab?: boolean; }; -function ParentNavigationSubtitle({parentNavigationSubtitleData, parentReportActionID, parentReportID = '', pressableStyles}: ParentNavigationSubtitleProps) { +function ParentNavigationSubtitle({ + parentNavigationSubtitleData, + parentReportActionID, + parentReportID = '', + pressableStyles, + openParentReportInCurrentTab = false, +}: ParentNavigationSubtitleProps) { + const currentRoute = useRoute(); const styles = useThemeStyles(); const {workspaceName, reportName} = parentNavigationSubtitleData; const {translate} = useLocalize(); - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`, {canBeMissing: false}); const canUserPerformWriteAction = canUserPerformWriteActionReportUtils(report); + const isReportInRHP = currentRoute.name === SCREENS.SEARCH.REPORT_RHP; + const currentFullScreenRoute = useRootNavigationState((state) => state?.routes?.findLast((route) => isFullScreenName(route.name))); // 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) if (!reportName) { @@ -43,6 +61,31 @@ function ParentNavigationSubtitle({parentNavigationSubtitleData, parentReportAct onPress={() => { const parentAction = getReportAction(parentReportID, parentReportActionID); const isVisibleAction = shouldReportActionBeVisible(parentAction, parentAction?.reportActionID ?? CONST.DEFAULT_NUMBER_ID, canUserPerformWriteAction); + + if (openParentReportInCurrentTab && isReportInRHP) { + // If the report is displayed in RHP in Reports tab, we want to stay in the current tab after opening the parent report + if (currentFullScreenRoute?.name === NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR) { + const lastRoute = currentFullScreenRoute?.state?.routes.at(-1); + if (lastRoute?.name === SCREENS.SEARCH.MONEY_REQUEST_REPORT) { + const moneyRequestReportID = (lastRoute?.params as SearchFullscreenNavigatorParamList[typeof SCREENS.SEARCH.MONEY_REQUEST_REPORT])?.reportID; + // If the parent report is already displayed underneath RHP, simply dismiss the modal + if (moneyRequestReportID === parentReportID) { + Navigation.dismissModal(); + return; + } + } + + Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID: parentReportID})); + return; + } + + // If the parent report is already displayed underneath RHP, simply dismiss the modal + if (Navigation.getTopmostReportId() === parentReportID) { + Navigation.dismissModal(); + return; + } + } + if (isVisibleAction) { Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(parentReportID, parentReportActionID)); } else { diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 329941b11d2f..647206705518 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -28,6 +28,7 @@ import getCurrentUrl from '@libs/Navigation/currentUrl'; import Navigation from '@libs/Navigation/Navigation'; import Animations from '@libs/Navigation/PlatformStackNavigation/navigationOptions/animation'; import Presentation from '@libs/Navigation/PlatformStackNavigation/navigationOptions/presentation'; +import type {PlatformStackNavigationOptions} from '@libs/Navigation/PlatformStackNavigation/types'; import type {AuthScreensParamList} from '@libs/Navigation/types'; import NetworkConnection from '@libs/NetworkConnection'; import onyxSubscribe from '@libs/onyxSubscribe'; @@ -59,7 +60,12 @@ import type {SelectedTimezone, Timezone} from '@src/types/onyx/PersonalDetails'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; import createRootStackNavigator from './createRootStackNavigator'; -import {reportsSplitsWithEnteringAnimation, settingsSplitWithEnteringAnimation, workspaceSplitsWithoutEnteringAnimation} from './createRootStackNavigator/GetStateForActionHandlers'; +import { + reportsSplitsWithEnteringAnimation, + searchFullscreenWithEnteringAnimation, + settingsSplitWithEnteringAnimation, + workspaceSplitsWithoutEnteringAnimation, +} from './createRootStackNavigator/GetStateForActionHandlers'; import defaultScreenOptions from './defaultScreenOptions'; import {ShareModalStackNavigator} from './ModalStackNavigators'; import ExplanationModalNavigator from './Navigators/ExplanationModalNavigator'; @@ -445,27 +451,30 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie }; }; - const getSplitNavigatorOptions = - (routesWithEnteringAnimation: Set) => + const getFullScreenNavigatorOptions = + (routesWithEnteringAnimation: Set, defaultOptions: PlatformStackNavigationOptions) => ({route}: {route: RouteProp}) => { // We don't need to do anything special for the wide screen. if (!shouldUseNarrowLayout) { - return rootNavigatorScreenOptions.splitNavigator; + return defaultOptions; } // On the narrow screen, we want to animate this navigator if pushed SplitNavigator includes desired screen const animationEnabled = routesWithEnteringAnimation.has(route.key); return { - ...rootNavigatorScreenOptions.splitNavigator, + ...defaultOptions, animation: animationEnabled ? Animations.SLIDE_FROM_RIGHT : Animations.NONE, }; }; // Animation is enabled when navigating to the report screen - const getReportsSplitNavigatorOptions = getSplitNavigatorOptions(reportsSplitsWithEnteringAnimation); + const getReportsSplitNavigatorOptions = getFullScreenNavigatorOptions(reportsSplitsWithEnteringAnimation, rootNavigatorScreenOptions.splitNavigator); // Animation is enabled when navigating to any screen different than SCREENS.SETTINGS.ROOT - const getSettingsSplitNavigatorOptions = getSplitNavigatorOptions(settingsSplitWithEnteringAnimation); + const getSettingsSplitNavigatorOptions = getFullScreenNavigatorOptions(settingsSplitWithEnteringAnimation, rootNavigatorScreenOptions.splitNavigator); + + // Animation is enabled when navigating to SCREENS.SEARCH.MONEY_REQUEST_REPORT + const getSearchFullscreenNavigatorOptions = getFullScreenNavigatorOptions(searchFullscreenWithEnteringAnimation, rootNavigatorScreenOptions.fullScreen); const clearStatus = () => { User.clearCustomStatus(); @@ -516,7 +525,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie /> (); const reportsSplitsWithEnteringAnimation = new Set(); const settingsSplitWithEnteringAnimation = new Set(); - +const searchFullscreenWithEnteringAnimation = new Set(); /** * Handles the OPEN_WORKSPACE_SPLIT action. * If the user is on other tab than settings and the workspace split is "remembered", this action will be called after pressing the settings tab. @@ -202,44 +201,23 @@ function handlePushSearchPageAction( action: PushActionType, configOptions: RouterConfigOptions, stackRouter: Router, CommonActions.Action | StackActionType>, - setActiveWorkspaceID: (workspaceID: string | undefined) => void, ) { - let updatedAction = action; - const currentParams = action.payload.params as RootNavigatorParamList[typeof NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR]; - if (currentParams?.screen === SCREENS.SEARCH.ROOT) { - const searchParams = currentParams?.params; - const queryJSON = SearchQueryUtils.buildSearchQueryJSON(searchParams.q); - if (!queryJSON) { - return null; - } + const stateWithSearchFullscreenNavigator = stackRouter.getStateForAction(state, action, configOptions); - if (!queryJSON.policyID) { - const policyID = getPolicyIDFromState(state as State); + if (!stateWithSearchFullscreenNavigator) { + Log.hmmm('[handlePushSettingsAction] SearchFullscreenNavigator has not been found in the navigation state.'); + return null; + } - if (policyID) { - queryJSON.policyID = policyID; - } else { - delete queryJSON.policyID; - } - } else { - setActiveWorkspaceID(queryJSON.policyID); - } + const lastFullScreenRoute = stateWithSearchFullscreenNavigator.routes.at(-1); + const actionPayloadScreen = action.payload?.params && 'screen' in action.payload.params ? action.payload?.params?.screen : undefined; - updatedAction = { - ...action, - payload: { - ...action.payload, - params: { - ...action.payload.params, - params: { - q: SearchQueryUtils.buildSearchQueryString(queryJSON), - }, - }, - }, - }; + // Transitioning to SCREENS.SEARCH.MONEY_REQUEST_REPORT should be animated + if (actionPayloadScreen === SCREENS.SEARCH.MONEY_REQUEST_REPORT && lastFullScreenRoute?.key) { + searchFullscreenWithEnteringAnimation.add(lastFullScreenRoute.key); } - return stackRouter.getStateForAction(state, updatedAction, configOptions); + return stateWithSearchFullscreenNavigator; } function handleReplaceReportsSplitNavigatorAction( @@ -309,6 +287,7 @@ export { handleSwitchPolicyIDAction, handleSwitchPolicyIDFromSearchAction, reportsSplitsWithEnteringAnimation, + searchFullscreenWithEnteringAnimation, settingsSplitWithEnteringAnimation, workspaceSplitsWithoutEnteringAnimation, }; diff --git a/src/libs/Navigation/AppNavigator/createRootStackNavigator/RootStackRouter.ts b/src/libs/Navigation/AppNavigator/createRootStackNavigator/RootStackRouter.ts index d6d5c13e35ae..3261029f2775 100644 --- a/src/libs/Navigation/AppNavigator/createRootStackNavigator/RootStackRouter.ts +++ b/src/libs/Navigation/AppNavigator/createRootStackNavigator/RootStackRouter.ts @@ -111,7 +111,7 @@ function RootStackRouter(options: RootStackNavigatorRouterOptions) { } if (action.payload.name === NAVIGATORS.SEARCH_FULLSCREEN_NAVIGATOR) { - return handlePushSearchPageAction(state, action, configOptions, stackRouter, setActiveWorkspaceID); + return handlePushSearchPageAction(state, action, configOptions, stackRouter); } if (action.payload.name === NAVIGATORS.SETTINGS_SPLIT_NAVIGATOR) {