diff --git a/src/ROUTES.ts b/src/ROUTES.ts index f585920b058f..a89baf76552a 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -128,6 +128,28 @@ const DYNAMIC_ROUTES = { SCREENS.REPORT_CHANGE_WORKSPACE.ROOT, ], }, + EDIT_REPORT_FIELD: { + path: 'edit/policyField/:policyID/:fieldID', + entryScreens: [SCREENS.REPORT, SCREENS.RIGHT_MODAL.SEARCH_REPORT, SCREENS.RIGHT_MODAL.EXPENSE_REPORT, SCREENS.RIGHT_MODAL.SEARCH_MONEY_REQUEST_REPORT, SCREENS.REPORT_DETAILS.ROOT], + getRoute: (policyID: string, fieldID: string) => `edit/policyField/${policyID}/${encodeURIComponent(fieldID)}` as const, + }, + PROFILE: { + path: 'a/:accountID', + entryScreens: ['*'], + getRoute: (accountID?: number, login?: string) => getUrlWithParams(`a/${accountID}`, {login}), + queryParams: ['login'], + }, + PROFILE_AVATAR: { + path: 'avatar/:accountID', + entryScreens: ['*'], + getRoute: (accountID: number) => `avatar/${accountID}` as const, + }, + NEW_REPORT_WORKSPACE_SELECTION: { + path: 'new-report-workspace-selection', + entryScreens: ['*'], + getRoute: (isMovingExpenses?: boolean) => `new-report-workspace-selection${isMovingExpenses ? '?isMovingExpenses=true' : ''}` as const, + queryParams: ['isMovingExpenses'], + }, NETSUITE_AUTO_SYNC: { path: 'netsuite-autosync', entryScreens: [SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_ADVANCED, SCREENS.WORKSPACE.ACCOUNTING.CARD_RECONCILIATION], @@ -407,7 +429,7 @@ const DYNAMIC_ROUTES = { }, NOTIFICATION_PREFERENCES: { path: 'notification-preferences', - entryScreens: [SCREENS.REPORT_SETTINGS.ROOT, SCREENS.PROFILE_ROOT], + entryScreens: [SCREENS.REPORT_SETTINGS.ROOT, SCREENS.DYNAMIC_PROFILE], getRoute: (reportID: string) => getUrlWithParams('notification-preferences', {reportID}), queryParams: ['reportID'], }, @@ -714,20 +736,6 @@ const ROUTES = { CONCIERGE: 'concierge', TRACK_EXPENSE: 'track-expense', SUBMIT_EXPENSE: 'submit-expense', - PROFILE: { - route: 'a/:accountID', - getRoute: (accountID?: number, backTo?: string, login?: string) => { - const baseRoute = getUrlWithBackToParam(`a/${accountID}`, backTo); - const loginParam = login ? `?login=${encodeURIComponent(login)}` : ''; - return `${baseRoute}${loginParam}` as const; - }, - }, - PROFILE_AVATAR: { - route: 'a/:accountID/avatar', - - getRoute: (accountID: number, backTo?: string) => getUrlWithBackToParam(`a/${accountID}/avatar` as const, backTo), - }, - // This is a special validation URL that will take the user to /workspace/new after validation. This is used // when linking users from e.com in order to share a session in this app. ENABLE_PAYMENTS: 'enable-payments', @@ -1173,19 +1181,6 @@ const ROUTES = { NEW_CHAT_EDIT_NAME: 'new/chat/confirm/name/edit', NEW_ROOM: 'new/room', - NEW_REPORT_WORKSPACE_SELECTION: { - route: 'new-report-workspace-selection', - getRoute: (isMovingExpenses?: boolean, backTo?: string) => { - const params = new URLSearchParams(); - if (isMovingExpenses) { - params.set('isMovingExpenses', 'true'); - } - const query = params.toString(); - const baseRoute = `new-report-workspace-selection${query ? `?${query}` : ''}` as const; - - return getUrlWithBackToParam(baseRoute, backTo); - }, - }, REPORT: 'r', REPORT_WITH_ID: { route: 'r/:reportID?/:reportActionID?', @@ -1240,19 +1235,6 @@ const ROUTES = { route: 'r/:threadReportID/edit/currency', getRoute: (threadReportID: string, currency: string, backTo: string) => `r/${threadReportID}/edit/currency?currency=${currency}&backTo=${backTo}` as const, }, - EDIT_REPORT_FIELD_REQUEST: { - route: 'r/:reportID/edit/policyField/:policyID/:fieldID', - getRoute: (reportID: string | undefined, policyID: string | undefined, fieldID: string, backTo?: string) => { - if (!policyID || !reportID) { - Log.warn('Invalid policyID or reportID is used to build the EDIT_REPORT_FIELD_REQUEST route', { - policyID, - reportID, - }); - } - - return getUrlWithBackToParam(`r/${reportID}/edit/policyField/${policyID}/${encodeURIComponent(fieldID)}` as const, backTo); - }, - }, REPORT_WITH_ID_DETAILS_SHARE_CODE: { route: 'r/:reportID/details/shareCode', getRoute: (reportID: string | undefined, backTo?: string) => { diff --git a/src/SCREENS.ts b/src/SCREENS.ts index c69f1d43e5e2..593a0cbca0ce 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -17,7 +17,7 @@ const PROTECTED_SCREENS = { const SCREENS = { ...PROTECTED_SCREENS, REPORT: 'Report', - PROFILE_AVATAR: 'ProfileAvatar', + DYNAMIC_PROFILE_AVATAR: 'Dynamic_Profile_Avatar', WORKSPACE_AVATAR: 'WorkspaceAvatar', WORKSPACE_DOCUMENT: 'WorkspaceDocument', REPORT_AVATAR: 'ReportAvatar', @@ -515,7 +515,7 @@ const SCREENS = { }, NEW_REPORT_WORKSPACE_SELECTION: { - ROOT: 'NewReportWorkspaceSelection_Root', + DYNAMIC_ROOT: 'Dynamic_NewReportWorkspaceSelection_Root', }, SET_DEFAULT_WORKSPACE: 'SetDefaultWorkspace', @@ -907,7 +907,7 @@ const SCREENS = { EDIT_REQUEST: { CURRENCY: 'EditRequest_Currency', - REPORT_FIELD: 'EditRequest_ReportField', + DYNAMIC_REPORT_FIELD: 'Dynamic_EditRequest_ReportField', }, NEW_CHAT: { @@ -970,7 +970,7 @@ const SCREENS = { WALLET_STATEMENT_ROOT: 'WalletStatement_Root', SIGN_IN_ROOT: 'SignIn_Root', DETAILS_ROOT: 'Details_Root', - PROFILE_ROOT: 'Profile_Root', + DYNAMIC_PROFILE: 'Dynamic_Profile_Root', AUTO_SUBMIT_ROOT: 'AutoSubmit_Modal_Root', DYNAMIC_CHANGE_POLICY_EDUCATIONAL_ROOT: 'DynamicChangePolicyEducational_Root', REPORT_DESCRIPTION_ROOT: 'Report_Description_Root', diff --git a/src/components/AvatarWithDisplayName.tsx b/src/components/AvatarWithDisplayName.tsx index e9c71f4bfd79..8052f683446b 100644 --- a/src/components/AvatarWithDisplayName.tsx +++ b/src/components/AvatarWithDisplayName.tsx @@ -13,6 +13,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils'; import {getHumanAgentAccountIDFromReportAction, getHumanAgentFirstName} from '@libs/ReportActionsUtils'; @@ -37,7 +38,7 @@ import { import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {Policy, Report} from '@src/types/onyx'; import {getButtonRole} from './Button/utils'; import DisplayNames from './DisplayNames'; @@ -232,7 +233,11 @@ function AvatarWithDisplayName({ const navigateToEditReportTitle = (event?: GestureResponderEvent | KeyboardEvent) => { event?.stopPropagation?.(); - Navigation.navigate(ROUTES.EDIT_REPORT_FIELD_REQUEST.getRoute(report?.reportID, report?.policyID, CONST.REPORT_FIELD_TITLE_FIELD_ID, Navigation.getReportRHPActiveRoute())); + if (!report?.policyID) { + return; + } + + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.EDIT_REPORT_FIELD.getRoute(report.policyID, CONST.REPORT_FIELD_TITLE_FIELD_ID))); }; const showActorDetails = () => { @@ -243,7 +248,7 @@ function AvatarWithDisplayName({ } if (isExpenseReport(report) && report?.ownerAccountID) { - Navigation.navigate(ROUTES.PROFILE.getRoute(report.ownerAccountID)); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(report.ownerAccountID))); return; } @@ -255,7 +260,7 @@ function AvatarWithDisplayName({ if (isChatThread(report)) { // In an ideal situation account ID won't be 0 if (actorAccountID.current && actorAccountID.current > 0) { - Navigation.navigate(ROUTES.PROFILE.getRoute(actorAccountID.current)); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(actorAccountID.current))); return; } } diff --git a/src/components/FrozenCardHeader.tsx b/src/components/FrozenCardHeader.tsx index dff851061cb2..c093de0cbd7f 100644 --- a/src/components/FrozenCardHeader.tsx +++ b/src/components/FrozenCardHeader.tsx @@ -7,11 +7,12 @@ import useOnyx from '@hooks/useOnyx'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; import Button from './Button'; import Icon from './Icon'; import Text from './Text'; @@ -55,7 +56,7 @@ function FrozenCardHeader({cardPreview, onUnfreezePress, onAskToUnfreezePress, c <> {adminFrozenTextPrefix} Navigation.navigate(ROUTES.PROFILE.getRoute(Number(frozenByAccountID), Navigation.getActiveRoute()))} + onPress={() => Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(Number(frozenByAccountID))))} style={[styles.textLabel, styles.link]} > {frozenByName} @@ -70,7 +71,7 @@ function FrozenCardHeader({cardPreview, onUnfreezePress, onAskToUnfreezePress, c <> {frozenNeedsUnfreezePrefix} Navigation.navigate(ROUTES.PROFILE.getRoute(Number(frozenByAccountID), Navigation.getActiveRoute()))} + onPress={() => Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(Number(frozenByAccountID))))} style={[styles.textLabel, styles.link]} > {frozenByName} diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx index 0f6dd62834e4..1b7a45e277a9 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx @@ -15,10 +15,11 @@ import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentU import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import {getAccountIDsByLogins, getDisplayNameOrDefault, getShortMentionIfFound} from '@libs/PersonalDetailsUtils'; import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; import asMutable from '@src/types/utils/asMutable'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -45,7 +46,7 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona accountID = parseInt(htmlAttribAccountID, 10); mentionDisplayText = formatPhoneNumber(user?.login ?? '') || getDisplayNameOrDefault(user); mentionDisplayText = getShortMentionIfFound(mentionDisplayText, htmlAttributeAccountID, currentUserPersonalDetails, user?.login ?? '') ?? ''; - navigationRoute = ROUTES.PROFILE.getRoute(accountID, Navigation.getReportRHPActiveRoute()); + navigationRoute = createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(accountID), Navigation.getReportRHPActiveRoute()); } else if ('data' in tnode && !isEmptyObject(tnode.data)) { tnodeClone = cloneDeep(tnode); // We need to remove the LTR unicode and leading @ from data as it is not part of the login @@ -57,13 +58,13 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona ); accountID = getAccountIDsByLogins([mentionDisplayText])?.at(0) ?? -1; - navigationRoute = ROUTES.PROFILE.getRoute(accountID, Navigation.getReportRHPActiveRoute(), mentionDisplayText); + navigationRoute = createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(accountID, mentionDisplayText), Navigation.getReportRHPActiveRoute()); mentionDisplayText = Str.removeSMSDomain(mentionDisplayText); } else if (!isEmpty(htmlAttribAccountID)) { // accountID not found in personal details and mention data not provided accountID = parseInt(htmlAttribAccountID, 10); mentionDisplayText = getDisplayNameOrDefault(); - navigationRoute = ROUTES.PROFILE.getRoute(accountID, Navigation.getReportRHPActiveRoute()); + navigationRoute = createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(accountID), Navigation.getReportRHPActiveRoute()); } else { // If neither an account ID or email is provided, don't render anything return null; @@ -86,10 +87,10 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona onPress={(event) => { event.preventDefault(); if (!isEmpty(htmlAttribAccountID)) { - Navigation.navigate(ROUTES.PROFILE.getRoute(parseInt(htmlAttribAccountID, 10), Navigation.getReportRHPActiveRoute())); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(parseInt(htmlAttribAccountID, 10)), Navigation.getReportRHPActiveRoute())); return; } - Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getReportRHPActiveRoute(), mentionDisplayText)); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(accountID, mentionDisplayText), Navigation.getReportRHPActiveRoute())); }} role={CONST.ROLE.LINK} accessibilityLabel={`/${navigationRoute}`} diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/UserDetailsRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/UserDetailsRenderer.tsx index 650d98de7071..6a66405617c4 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/UserDetailsRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/UserDetailsRenderer.tsx @@ -4,10 +4,11 @@ import {TNodeChildrenRenderer} from 'react-native-render-html'; import Text from '@components/Text'; import UserDetailsTooltip from '@components/UserDetailsTooltip'; import useThemeStyles from '@hooks/useThemeStyles'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import {isOptimisticPersonalDetail} from '@libs/ReportUtils'; import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; type UserDetailsRendererProps = CustomRendererProps; @@ -35,7 +36,7 @@ function UserDetailsRenderer({tnode, ...defaultRendererProps}: UserDetailsRender Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute()))} + onPress={() => Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(accountID)))} suppressHighlighting role={CONST.ROLE.LINK} > diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx index 7a607051dfe4..9013a6913925 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx @@ -454,7 +454,7 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps) hasNewestReportAction, setIsFloatingMessageCounterVisible, scrollToEnd: reportScrollManager.scrollToEnd, - resetKey: report.reportID, + resetKey: report?.reportID ?? reportIDFromRoute ?? '', }); /** diff --git a/src/components/MoneyRequestReportView/MoneyRequestViewReportFields.tsx b/src/components/MoneyRequestReportView/MoneyRequestViewReportFields.tsx index b952d5e05af6..c609af3806b9 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestViewReportFields.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestViewReportFields.tsx @@ -8,6 +8,7 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import useThemeStyles from '@hooks/useThemeStyles'; import {clearReportFieldKeyErrors} from '@libs/actions/Report'; import {resolveReportFieldValue} from '@libs/Formula'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import { getFieldViolation, @@ -23,7 +24,7 @@ import { } from '@libs/ReportUtils'; import type {ThemeStyles} from '@styles/index'; import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {Policy, PolicyReportField, Report, ReportViolationName} from '@src/types/onyx'; import type {PendingAction} from '@src/types/onyx/OnyxCommon'; @@ -62,7 +63,11 @@ function ReportFieldView(reportField: EnrichedPolicyReportField, report: OnyxEnt description={Str.UCFirst(reportField.name)} title={reportField.fieldValue} onPress={() => { - Navigation.navigate(ROUTES.EDIT_REPORT_FIELD_REQUEST.getRoute(report?.reportID, report?.policyID, reportField.fieldID, Navigation.getActiveRoute())); + if (!report?.policyID) { + return; + } + + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.EDIT_REPORT_FIELD.getRoute(report.policyID, reportField.fieldID))); }} shouldShowRightIcon={!reportField.isFieldDisabled} wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]} diff --git a/src/components/Navigation/QuickCreationActionsBar/index.tsx b/src/components/Navigation/QuickCreationActionsBar/index.tsx index 4b72d6d34926..5f183155b2ae 100644 --- a/src/components/Navigation/QuickCreationActionsBar/index.tsx +++ b/src/components/Navigation/QuickCreationActionsBar/index.tsx @@ -17,6 +17,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {startDistanceRequest, startMoneyRequest} from '@libs/actions/IOU/MoneyRequest'; import {createNewReport} from '@libs/actions/Report'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTopmostFullScreenRoute'; import Navigation from '@libs/Navigation/Navigation'; import {openTravelDotLink} from '@libs/openTravelDotLink'; @@ -26,7 +27,7 @@ import {generateReportID, hasViolations as hasViolationsReportUtils} from '@libs import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import {primaryLoginSelector} from '@src/selectors/Account'; import {groupPaidPoliciesWithExpenseChatEnabledSelector} from '@src/selectors/Policy'; import type * as OnyxTypes from '@src/types/onyx'; @@ -152,7 +153,7 @@ function QuickCreationActionsBar() { (shouldRestrictUserBillableActions(defaultChatEnabledPolicy, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed, currentUserPersonalDetails.accountID) && groupPoliciesWithChatEnabled.length > 1) ) { - Navigation.navigate(ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute()); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.NEW_REPORT_WORKSPACE_SELECTION.path)); return; } diff --git a/src/components/ReportActionAvatars/ReportActionAvatar.tsx b/src/components/ReportActionAvatars/ReportActionAvatar.tsx index 9eb56371e21e..b207033223e8 100644 --- a/src/components/ReportActionAvatars/ReportActionAvatar.tsx +++ b/src/components/ReportActionAvatars/ReportActionAvatar.tsx @@ -19,13 +19,14 @@ import useTheme from '@hooks/useTheme'; import useThemeIllustrations from '@hooks/useThemeIllustrations'; import useThemeStyles from '@hooks/useThemeStyles'; import {getCardFeedIcon} from '@libs/CardUtils'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import {getUserDetailTooltipText, sortIconsByName} from '@libs/ReportUtils'; import type {AvatarSource} from '@libs/UserAvatarUtils'; import Navigation from '@navigation/Navigation'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {CardFeed} from '@src/types/onyx/CardFeeds'; import type {Icon as IconType} from '@src/types/onyx/OnyxCommon'; @@ -85,7 +86,8 @@ function ProfileAvatar(props: Parameters[0] & {useProfileNavigati } return Navigation.navigate(ROUTES.WORKSPACE_AVATAR.getRoute(String(avatarID), firstLetter)); } - return Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(Number(avatarID), Navigation.getActiveRoute())); + + return Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE_AVATAR.getRoute(Number(avatarID)))); }; return ( diff --git a/src/components/ReportActionItem/MoneyReportView.tsx b/src/components/ReportActionItem/MoneyReportView.tsx index 23a815f49bc6..a7f743fc5264 100644 --- a/src/components/ReportActionItem/MoneyReportView.tsx +++ b/src/components/ReportActionItem/MoneyReportView.tsx @@ -20,6 +20,7 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {resolveReportFieldValue} from '@libs/Formula'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import {isPolicyTaxEnabled} from '@libs/PolicyUtils'; import { @@ -46,7 +47,7 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import {clearReportFieldKeyErrors} from '@src/libs/actions/Report'; -import ROUTES from '@src/ROUTES'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {Policy, Report} from '@src/types/onyx'; import type {PendingAction} from '@src/types/onyx/OnyxCommon'; @@ -201,9 +202,11 @@ function MoneyReportView({ description={Str.UCFirst(reportField.name)} title={fieldValue} onPress={() => { - Navigation.navigate( - ROUTES.EDIT_REPORT_FIELD_REQUEST.getRoute(report?.reportID, report?.policyID, reportField.fieldID, Navigation.getReportRHPActiveRoute()), - ); + if (!report?.policyID) { + return; + } + + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.EDIT_REPORT_FIELD.getRoute(report.policyID, reportField.fieldID))); }} shouldShowRightIcon={!isFieldDisabled} wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]} diff --git a/src/components/RoomHeaderAvatars.tsx b/src/components/RoomHeaderAvatars.tsx index 76e356fe8f9a..88106b0dfca8 100644 --- a/src/components/RoomHeaderAvatars.tsx +++ b/src/components/RoomHeaderAvatars.tsx @@ -5,11 +5,12 @@ import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import {clearAvatarErrors, updatePolicyRoomAvatar} from '@libs/actions/Report'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import {isUserCreatedPolicyRoom} from '@libs/ReportUtils'; import {isDefaultAvatar} from '@libs/UserAvatarUtils'; import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {Policy, Report} from '@src/types/onyx'; import type {Icon} from '@src/types/onyx/OnyxCommon'; import Avatar from './Avatar'; @@ -33,7 +34,7 @@ function RoomHeaderAvatars({icons, report, policy, participants, currentUserAcco } if (icon.id) { - Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(Number(icon.id), Navigation.getActiveRoute())); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE_AVATAR.getRoute(Number(icon.id)))); } }; diff --git a/src/components/Search/SearchPageHeader/SearchActionsBarCreateButton.tsx b/src/components/Search/SearchPageHeader/SearchActionsBarCreateButton.tsx index 54a894b843a9..215b34ed0e06 100644 --- a/src/components/Search/SearchPageHeader/SearchActionsBarCreateButton.tsx +++ b/src/components/Search/SearchPageHeader/SearchActionsBarCreateButton.tsx @@ -20,6 +20,7 @@ import {startDistanceRequest, startMoneyRequest} from '@libs/actions/IOU/MoneyRe import {createNewReport} from '@libs/actions/Report'; import getIconForAction from '@libs/getIconForAction'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTopmostFullScreenRoute'; import Navigation from '@libs/Navigation/Navigation'; import {getDefaultChatEnabledPolicy} from '@libs/PolicyUtils'; @@ -27,7 +28,7 @@ import {generateReportID, hasViolations as hasViolationsReportUtils} from '@libs import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import {groupPaidPoliciesWithExpenseChatEnabledSelector} from '@src/selectors/Policy'; import type * as OnyxTypes from '@src/types/onyx'; @@ -168,7 +169,7 @@ function SearchActionsBarCreateButton() { ) && groupPoliciesWithChatEnabled.length > 1) ) { - Navigation.navigate(ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute()); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.NEW_REPORT_WORKSPACE_SELECTION.path)); return; } diff --git a/src/hooks/useAvatarMenu.ts b/src/hooks/useAvatarMenu.ts index 0311c36acbb9..a5710d30b80e 100644 --- a/src/hooks/useAvatarMenu.ts +++ b/src/hooks/useAvatarMenu.ts @@ -1,8 +1,9 @@ import {useCallback, useContext} from 'react'; import type {DropdownOption} from '@components/ButtonWithDropdownMenu/types'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import AttachmentModalContext from '@pages/media/AttachmentModalScreen/AttachmentModalContext'; -import ROUTES from '@src/ROUTES'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {FileObject} from '@src/types/utils/Attachment'; import {useMemoizedLazyExpensifyIcons} from './useLazyAsset'; import useLocalize from './useLocalize'; @@ -75,7 +76,7 @@ function useAvatarMenu({shouldHideAvatarEdit, accountID, onImageRemoved, showAva text: translate('avatarWithImagePicker.viewPhoto'), onSelected: () => { attachmentContext.setCurrentAttachment({source, originalFileName}); - Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(accountID)); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE_AVATAR.getRoute(accountID))); }, }, ]; diff --git a/src/hooks/useCreateReport.tsx b/src/hooks/useCreateReport.tsx index 9f06a7c51f10..6494f32d01f4 100644 --- a/src/hooks/useCreateReport.tsx +++ b/src/hooks/useCreateReport.tsx @@ -1,13 +1,14 @@ import {useCallback} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import {getDefaultChatEnabledPolicy, isPaidGroupPolicy} from '@libs/PolicyUtils'; import {generateReportID} from '@libs/ReportUtils'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import useCreateEmptyReportConfirmation from './useCreateEmptyReportConfirmation'; @@ -104,7 +105,7 @@ export default function useCreateReport({onCreateReport, groupPoliciesWithChatEn !!workspaceIDForReportCreation && shouldRestrictUserBillableActions(defaultChatEnabledPolicy, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed, accountID); if (!workspaceIDForReportCreation || (isDefaultPersonal && hasMultipleNonPersonalWorkspaces) || (isDefaultBillingRestricted && hasMultipleNonPersonalWorkspaces)) { - Navigation.navigate(ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute()); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.NEW_REPORT_WORKSPACE_SELECTION.path)); return; } diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 9033a828e69c..8f07385d9a3c 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -237,7 +237,7 @@ function AuthScreens() { listeners={modalScreenListeners} /> ({ - [SCREENS.PROFILE_ROOT]: () => require('../../../../pages/ProfilePage').default, + [SCREENS.DYNAMIC_PROFILE]: () => require('../../../../pages/ProfilePage').default, }); const NewReportWorkspaceSelectionModalStackNavigator = createModalStackNavigator({ - [SCREENS.NEW_REPORT_WORKSPACE_SELECTION.ROOT]: () => require('../../../../pages/NewReportWorkspaceSelectionPage').default, + [SCREENS.NEW_REPORT_WORKSPACE_SELECTION.DYNAMIC_ROOT]: () => require('../../../../pages/DynamicNewReportWorkspaceSelectionPage').default, }); const ReportDetailsModalStackNavigator = createModalStackNavigator({ @@ -1036,7 +1036,7 @@ const FlagCommentStackNavigator = createModalStackNavigator({ - [SCREENS.EDIT_REQUEST.REPORT_FIELD]: () => require('../../../../pages/EditReportFieldPage').default, + [SCREENS.EDIT_REQUEST.DYNAMIC_REPORT_FIELD]: () => require('../../../../pages/DynamicEditReportFieldPage').default, }); const PrivateNotesModalStackNavigator = createModalStackNavigator({ diff --git a/src/libs/Navigation/AppNavigator/createRootStackNavigator/GetStateForActionHandlers.ts b/src/libs/Navigation/AppNavigator/createRootStackNavigator/GetStateForActionHandlers.ts index 8f8b75fe3907..0713262b1ece 100644 --- a/src/libs/Navigation/AppNavigator/createRootStackNavigator/GetStateForActionHandlers.ts +++ b/src/libs/Navigation/AppNavigator/createRootStackNavigator/GetStateForActionHandlers.ts @@ -41,7 +41,7 @@ const MODAL_ROUTES_TO_DISMISS = new Set([ SCREENS.TRANSACTION_RECEIPT, SCREENS.MONEY_REQUEST.RECEIPT_PREVIEW, SCREENS.MONEY_REQUEST.ODOMETER_PREVIEW, - SCREENS.PROFILE_AVATAR, + SCREENS.DYNAMIC_PROFILE_AVATAR, SCREENS.WORKSPACE_AVATAR, SCREENS.WORKSPACE_DOCUMENT, SCREENS.REPORT_AVATAR, diff --git a/src/libs/Navigation/linkingConfig/OldRoutes.ts b/src/libs/Navigation/linkingConfig/OldRoutes.ts index ddb94ca5f615..273b07c6b437 100644 --- a/src/libs/Navigation/linkingConfig/OldRoutes.ts +++ b/src/libs/Navigation/linkingConfig/OldRoutes.ts @@ -29,6 +29,7 @@ const oldRoutes: Record = { '/workspaces/*/accounting/quickbooks-desktop/export/out-of-pocket-expense/entity-select': '/workspaces/$1/accounting/quickbooks-desktop/export/qbd-out-of-pocket-expense/qbd-entity-select', '/flag/*/*': '/r/$1/flag/$1/$2', + '/a/*/avatar': '/avatar/$1', '/home-page': '/home', /* eslint-enable @typescript-eslint/naming-convention */ }; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 26fa32fffb62..a6e480926ee5 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -25,12 +25,7 @@ const config: LinkingOptions['config'] = { [SCREENS.SAML_SIGN_IN]: ROUTES.SAML_SIGN_IN, [SCREENS.REPORT_ATTACHMENTS]: ROUTES.REPORT_ATTACHMENTS.route, [SCREENS.REPORT_ADD_ATTACHMENT]: ROUTES.REPORT_ADD_ATTACHMENT.route, - [SCREENS.PROFILE_AVATAR]: { - path: ROUTES.PROFILE_AVATAR.route, - parse: { - accountID: Number, - }, - }, + [SCREENS.DYNAMIC_PROFILE_AVATAR]: DYNAMIC_ROUTES.PROFILE_AVATAR.path, [SCREENS.WORKSPACE_AVATAR]: ROUTES.WORKSPACE_AVATAR.route, [SCREENS.WORKSPACE_DOCUMENT]: ROUTES.WORKSPACE_DOCUMENT.route, [SCREENS.REPORT_AVATAR]: ROUTES.REPORT_AVATAR.route, @@ -1467,7 +1462,7 @@ const config: LinkingOptions['config'] = { }, [SCREENS.RIGHT_MODAL.NEW_REPORT_WORKSPACE_SELECTION]: { screens: { - [SCREENS.NEW_REPORT_WORKSPACE_SELECTION.ROOT]: ROUTES.NEW_REPORT_WORKSPACE_SELECTION.route, + [SCREENS.NEW_REPORT_WORKSPACE_SELECTION.DYNAMIC_ROOT]: DYNAMIC_ROUTES.NEW_REPORT_WORKSPACE_SELECTION.path, }, }, [SCREENS.RIGHT_MODAL.REPORT_DETAILS]: { @@ -1674,7 +1669,7 @@ const config: LinkingOptions['config'] = { }, [SCREENS.RIGHT_MODAL.PROFILE]: { screens: { - [SCREENS.PROFILE_ROOT]: ROUTES.PROFILE.route, + [SCREENS.DYNAMIC_PROFILE]: DYNAMIC_ROUTES.PROFILE.path, }, }, [SCREENS.RIGHT_MODAL.PARTICIPANTS]: { @@ -1910,9 +1905,7 @@ const config: LinkingOptions['config'] = { }, [SCREENS.RIGHT_MODAL.EDIT_REQUEST]: { screens: { - [SCREENS.EDIT_REQUEST.REPORT_FIELD]: { - path: ROUTES.EDIT_REPORT_FIELD_REQUEST.route, - }, + [SCREENS.EDIT_REQUEST.DYNAMIC_REPORT_FIELD]: DYNAMIC_ROUTES.EDIT_REPORT_FIELD.path, }, }, [SCREENS.RIGHT_MODAL.SIGN_IN]: { diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index d8012d8938d5..80991e7b8e9d 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1683,20 +1683,18 @@ type DetailsNavigatorParamList = { }; type ProfileNavigatorParamList = { - [SCREENS.PROFILE_ROOT]: { + [SCREENS.DYNAMIC_PROFILE]: { accountID: string; - reportID: string; + reportID?: string; login?: string; // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md - backTo: Routes; + backTo?: Routes; }; }; type NewReportWorkspaceSelectionNavigatorParamList = { - [SCREENS.NEW_REPORT_WORKSPACE_SELECTION.ROOT]: { + [SCREENS.NEW_REPORT_WORKSPACE_SELECTION.DYNAMIC_ROOT]: { isMovingExpenses?: boolean; - // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md - backTo?: Routes; }; }; @@ -2385,12 +2383,10 @@ type FlagCommentNavigatorParamList = { }; type EditRequestNavigatorParamList = { - [SCREENS.EDIT_REQUEST.REPORT_FIELD]: { + [SCREENS.EDIT_REQUEST.DYNAMIC_REPORT_FIELD]: { fieldID: string; reportID: string; policyID: string; - // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md - backTo?: Routes; }; }; @@ -3032,12 +3028,10 @@ type AttachmentModalScreensParamList = { shouldDisableSendButton?: boolean; onConfirm?: (file: FileObject | FileObject[]) => void; }; - [SCREENS.PROFILE_AVATAR]: AttachmentModalContainerModalProps & { + [SCREENS.DYNAMIC_PROFILE_AVATAR]: AttachmentModalContainerModalProps & { accountID: number; source?: AvatarSource; originalFileName?: string; - // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md - backTo?: Routes; }; [SCREENS.WORKSPACE_AVATAR]: AttachmentModalContainerModalProps & { policyID: string; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8f689fa18919..b9e7f7da6fc4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -31,7 +31,7 @@ import type {TranslationPaths} from '@src/languages/types'; import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type { BankAccountList, @@ -116,6 +116,7 @@ import Log from './Log'; import {isEmailPublicDomain} from './LoginUtils'; // eslint-disable-next-line import/no-cycle import {getForReportAction, getMovedReportID} from './ModifiedExpenseMessage'; +import createDynamicRoute from './Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import getReportURLForCurrentContext from './Navigation/helpers/getReportURLForCurrentContext'; import getStateFromPath from './Navigation/helpers/getStateFromPath'; import {isFullScreenName} from './Navigation/helpers/isNavigatorName'; @@ -6112,7 +6113,8 @@ function navigateToDetailsPage(report: OnyxEntry, backTo?: string, shoul const participantAccountID = getParticipantsAccountIDsForDisplay(report); if (isSelfDMReport || isOneOnOneChatReport) { - Navigation.navigate(ROUTES.PROFILE.getRoute(participantAccountID.at(0), isSelfDMReport || shouldUseActiveRoute ? Navigation.getActiveRoute() : backTo)); + const basePath = isSelfDMReport || shouldUseActiveRoute ? Navigation.getActiveRoute() : (backTo ?? ''); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(participantAccountID.at(0)), basePath)); return; } @@ -6129,7 +6131,7 @@ function goBackToDetailsPage(report: OnyxEntry, backTo?: string, shouldG const participantAccountID = getParticipantsAccountIDsForDisplay(report); if (isOneOnOneChatReport) { - Navigation.goBack(ROUTES.PROFILE.getRoute(participantAccountID.at(0), backTo)); + Navigation.goBack(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(participantAccountID.at(0)), backTo ?? '')); return; } @@ -6173,7 +6175,7 @@ function goBackFromPrivateNotes(report: OnyxEntry, accountID?: number, b const participantAccountIDs = getParticipantsAccountIDsForDisplay(report); if (isOneOnOneChat(report)) { - Navigation.goBack(ROUTES.PROFILE.getRoute(participantAccountIDs.at(0), backTo)); + Navigation.goBack(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(participantAccountIDs.at(0)), backTo ?? '')); return; } diff --git a/src/pages/EditReportFieldPage.tsx b/src/pages/DynamicEditReportFieldPage.tsx similarity index 94% rename from src/pages/EditReportFieldPage.tsx rename to src/pages/DynamicEditReportFieldPage.tsx index 284a7f67f57d..dd51e7f70178 100644 --- a/src/pages/EditReportFieldPage.tsx +++ b/src/pages/DynamicEditReportFieldPage.tsx @@ -11,6 +11,7 @@ import type {PopoverMenuItem} from '@components/PopoverMenu'; import ScreenWrapper from '@components/ScreenWrapper'; import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useDynamicBackPath from '@hooks/useDynamicBackPath'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -33,17 +34,19 @@ import { } from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {Policy, ReportAttributesDerivedValue} from '@src/types/onyx'; import EditReportFieldDate from './EditReportFieldDate'; import EditReportFieldDropdown from './EditReportFieldDropdown'; import EditReportFieldText from './EditReportFieldText'; -type EditReportFieldPageProps = PlatformStackScreenProps; +type DynamicEditReportFieldPageProps = PlatformStackScreenProps; -function EditReportFieldPage({route}: EditReportFieldPageProps) { - const {backTo, reportID, policyID} = route.params; +function DynamicEditReportFieldPage({route}: DynamicEditReportFieldPageProps) { + const {reportID, policyID} = route.params; const fieldKey = getReportFieldKey(route.params.fieldID); + const backPath = useDynamicBackPath(DYNAMIC_ROUTES.EDIT_REPORT_FIELD.path); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); const reportAttributesSelector = useCallback((attributes: OnyxEntry) => reportByIDsSelector([reportID])(attributes), [reportID]); @@ -91,7 +94,7 @@ function EditReportFieldPage({route}: EditReportFieldPageProps) { } const goBack = () => { - Navigation.goBack(backTo); + Navigation.navigate(backPath); }; const handleReportFieldDelete = async () => { @@ -224,4 +227,4 @@ function EditReportFieldPage({route}: EditReportFieldPageProps) { ); } -export default EditReportFieldPage; +export default DynamicEditReportFieldPage; diff --git a/src/pages/NewReportWorkspaceSelectionPage.tsx b/src/pages/DynamicNewReportWorkspaceSelectionPage.tsx similarity index 95% rename from src/pages/NewReportWorkspaceSelectionPage.tsx rename to src/pages/DynamicNewReportWorkspaceSelectionPage.tsx index a4bc47fe7c6c..30c1329802b5 100644 --- a/src/pages/NewReportWorkspaceSelectionPage.tsx +++ b/src/pages/DynamicNewReportWorkspaceSelectionPage.tsx @@ -14,6 +14,7 @@ import useCreateEmptyReportConfirmation from '@hooks/useCreateEmptyReportConfirm import useCreateNewReport from '@hooks/useCreateNewReport'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useDebouncedState from '@hooks/useDebouncedState'; +import useDynamicBackPath from '@hooks/useDynamicBackPath'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -28,7 +29,6 @@ import type {NewReportWorkspaceSelectionNavigatorParamList} from '@libs/Navigati import {getHeaderMessageForNonUserList} from '@libs/OptionsListUtils'; import {canSubmitPerDiemExpenseFromWorkspace, isPolicyAdmin, shouldShowPolicy} from '@libs/PolicyUtils'; import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; -import {buildCannedSearchQuery} from '@libs/SearchQueryUtils'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import {isPerDiemRequest} from '@libs/TransactionUtils'; import isRHPOnSearchMoneyRequestReportPage from '@navigation/helpers/isRHPOnSearchMoneyRequestReportPage'; @@ -37,7 +37,7 @@ import {changeTransactionsReport} from '@userActions/Transaction'; import {setNameValuePair} from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -47,10 +47,11 @@ type WorkspaceListItem = { isPolicyAdmin?: boolean; } & ListItem; -type NewReportWorkspaceSelectionPageProps = PlatformStackScreenProps; +type NewReportWorkspaceSelectionPageProps = PlatformStackScreenProps; -function NewReportWorkspaceSelectionPage({route}: NewReportWorkspaceSelectionPageProps) { - const {isMovingExpenses, backTo} = route.params ?? {}; +function DynamicNewReportWorkspaceSelectionPage({route}: NewReportWorkspaceSelectionPageProps) { + const {isMovingExpenses} = route.params ?? {}; + const backPath = useDynamicBackPath(DYNAMIC_ROUTES.NEW_REPORT_WORKSPACE_SELECTION.path); const {isOffline} = useNetwork(); const icons = useMemoizedLazyExpensifyIcons(['FallbackWorkspaceAvatar']); const {selectedTransactions, selectedTransactionIDs} = useSearchStateContext(); @@ -134,7 +135,7 @@ function NewReportWorkspaceSelectionPage({route}: NewReportWorkspaceSelectionPag }); Navigation.dismissModal(); - Navigation.goBack(backTo ?? ROUTES.SEARCH_ROOT.getRoute({query: buildCannedSearchQuery()})); + Navigation.goBack(backPath); return; } navigateToNewReport(optimisticReport.reportID); @@ -256,7 +257,7 @@ function NewReportWorkspaceSelectionPage({route}: NewReportWorkspaceSelectionPag <> Navigation.goBack(backPath)} /> {shouldShowLoadingIndicator ? ( @@ -283,4 +284,4 @@ function NewReportWorkspaceSelectionPage({route}: NewReportWorkspaceSelectionPag ); } -export default NewReportWorkspaceSelectionPage; +export default DynamicNewReportWorkspaceSelectionPage; diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index a9628326fbf3..55b2ee2d796b 100755 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -18,6 +18,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useDynamicBackPath from '@hooks/useDynamicBackPath'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; @@ -47,12 +48,13 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; +import type {Route} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {PersonalDetails, Report} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import mapOnyxCollectionItems from '@src/utils/mapOnyxCollectionItems'; -type ProfilePageProps = PlatformStackScreenProps; +type ProfilePageProps = PlatformStackScreenProps; /** * This function narrows down the data from Onyx to just the properties that we want to trigger a re-render of the component. This helps minimize re-rendering @@ -88,6 +90,7 @@ function ProfilePage({route}: ProfilePageProps) { const reportKey = isAnonymousUserSession() || !reportID ? (`${ONYXKEYS.COLLECTION.REPORT}0` as const) : (`${ONYXKEYS.COLLECTION.REPORT}${reportID}` as const); const [report] = useOnyx(reportKey); + const backPath = useDynamicBackPath(DYNAMIC_ROUTES.PROFILE.path); const styles = useThemeStyles(); const {translate, formatPhoneNumber} = useLocalize(); @@ -143,7 +146,7 @@ function ProfilePage({route}: ProfilePageProps) { const hasStatus = !!statusEmojiCode; const statusContent = `${statusEmojiCode} ${statusText}`; - const navigateBackTo = route?.params?.backTo; + const navigateBackTo = (backPath || ROUTES.HOME) as Route; const notificationPreferenceValue = getReportNotificationPreference(report); @@ -185,7 +188,7 @@ function ProfilePage({route}: ProfilePageProps) { Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(accountID, Navigation.getActiveRoute()))} + onPress={() => Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE_AVATAR.getRoute(accountID)))} accessibilityLabel={translate('common.profile')} accessibilityRole={CONST.ROLE.BUTTON} disabled={!hasAvatar} diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index b756668d2221..880bc799b7e1 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -832,15 +832,14 @@ function ReportDetailsPage({policy, report, route, reportMetadata, reportLoading shouldCheckActionAllowedOnPress={false} description={translate('task.title')} onPress={ - shouldShowEditableTitleField + shouldShowEditableTitleField && report.policyID ? () => { - let policyID = report.policyID; - + const policyID = report.policyID; if (!policyID) { - policyID = ''; + return; } - Navigation.navigate(ROUTES.EDIT_REPORT_FIELD_REQUEST.getRoute(report.reportID, policyID, CONST.REPORT_FIELD_TITLE_FIELD_ID, backTo)); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.EDIT_REPORT_FIELD.getRoute(policyID, CONST.REPORT_FIELD_TITLE_FIELD_ID))); } : undefined } diff --git a/src/pages/ReportParticipantDetailsPage.tsx b/src/pages/ReportParticipantDetailsPage.tsx index f3615ef7e100..d3c1cdf3dc76 100644 --- a/src/pages/ReportParticipantDetailsPage.tsx +++ b/src/pages/ReportParticipantDetailsPage.tsx @@ -18,6 +18,7 @@ import useOnyx from '@hooks/useOnyx'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import {removeFromGroupChat} from '@libs/actions/Report'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; import {isGroupChatAdmin} from '@libs/ReportUtils'; @@ -25,7 +26,7 @@ import Navigation from '@navigation/Navigation'; import type {ParticipantsNavigatorParamList} from '@navigation/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {PersonalDetails} from '@src/types/onyx'; import NotFoundPage from './ErrorPage/NotFoundPage'; @@ -61,7 +62,7 @@ function ReportParticipantDetails({report, route}: ReportParticipantDetailsPageP }; const navigateToProfile = () => { - Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute())); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(accountID))); }; const openRoleSelectionModal = () => { diff --git a/src/pages/ReportParticipantsPage.tsx b/src/pages/ReportParticipantsPage.tsx index 454201d61b17..a862c4aa0d34 100755 --- a/src/pages/ReportParticipantsPage.tsx +++ b/src/pages/ReportParticipantsPage.tsx @@ -32,6 +32,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import {openRoomMembersPage, removeFromGroupChat, updateGroupChatMemberRoles} from '@libs/actions/Report'; import {clearUserSearchPhrase} from '@libs/actions/RoomMembersUserSearchPhrase'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ParticipantsNavigatorParamList} from '@libs/Navigation/types'; @@ -54,7 +55,7 @@ import { import StringUtils from '@libs/StringUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {PersonalDetails} from '@src/types/onyx'; import type {WithReportOrNotFoundProps} from './inbox/report/withReportOrNotFound'; @@ -215,7 +216,7 @@ function ReportParticipantsPage({report, route}: ReportParticipantsPageProps) { Navigation.navigate(ROUTES.REPORT_PARTICIPANTS_DETAILS.getRoute(report.reportID, item.accountID, backTo)); return; } - Navigation.navigate(ROUTES.PROFILE.getRoute(item.accountID, Navigation.getActiveRoute())); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(item.accountID))); }; // Build participants list diff --git a/src/pages/RoomMemberDetailsPage.tsx b/src/pages/RoomMemberDetailsPage.tsx index 5756858c5fbd..34bb4f8750e5 100644 --- a/src/pages/RoomMemberDetailsPage.tsx +++ b/src/pages/RoomMemberDetailsPage.tsx @@ -15,6 +15,7 @@ import usePolicy from '@hooks/usePolicy'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import {removeFromRoom} from '@libs/actions/Report'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {RoomMembersNavigatorParamList} from '@libs/Navigation/types'; import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; @@ -23,7 +24,7 @@ import {isPolicyExpenseChat} from '@libs/ReportUtils'; import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {PersonalDetails} from '@src/types/onyx'; import NotFoundPage from './ErrorPage/NotFoundPage'; @@ -60,7 +61,7 @@ function RoomMemberDetailsPage({report, route}: RoomMemberDetailsPagePageProps) }; const navigateToProfile = () => { - Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute())); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(accountID))); }; if (!member) { diff --git a/src/pages/Search/SearchTransactionsChangeReport.tsx b/src/pages/Search/SearchTransactionsChangeReport.tsx index 1b2e0c472f71..61d6f0cca603 100644 --- a/src/pages/Search/SearchTransactionsChangeReport.tsx +++ b/src/pages/Search/SearchTransactionsChangeReport.tsx @@ -12,6 +12,7 @@ import usePermissions from '@hooks/usePermissions'; import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; import {createNewReport} from '@libs/actions/Report'; import {changeTransactionsReport} from '@libs/actions/Transaction'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import setNavigationActionToMicrotaskQueue from '@libs/Navigation/helpers/setNavigationActionToMicrotaskQueue'; import Navigation from '@libs/Navigation/Navigation'; import {generateReportID, getPersonalDetailsForAccountID, getReportOrDraftReport, hasViolations as hasViolationsReportUtils} from '@libs/ReportUtils'; @@ -19,7 +20,7 @@ import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import IOURequestEditReportCommon from '@pages/iou/request/step/IOURequestEditReportCommon'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {PersonalDetails, Transaction} from '@src/types/onyx'; type TransactionGroupListItem = ListItem & { @@ -151,7 +152,7 @@ function SearchTransactionsChangeReport() { } if (shouldSelectPolicy) { - Navigation.navigate(ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute(true)); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute(true))); return; } if (!policyForMovingExpensesID) { diff --git a/src/pages/ShareCodePage.tsx b/src/pages/ShareCodePage.tsx index a77ac01f00f3..387987ea4266 100644 --- a/src/pages/ShareCodePage.tsx +++ b/src/pages/ShareCodePage.tsx @@ -40,7 +40,7 @@ import addTrailingForwardSlash from '@libs/UrlUtils'; import {getAvatarURL} from '@libs/UserAvatarUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {Policy, Report} from '@src/types/onyx'; type ShareCodePageOnyxProps = { @@ -109,7 +109,7 @@ function ShareCodePage({report, policy, backTo}: ShareCodePageProps) { const urlWithTrailingSlash = addTrailingForwardSlash(environmentURL); const url = isReport ? `${urlWithTrailingSlash}${ROUTES.REPORT_WITH_ID.getRoute(report.reportID)}` - : `${urlWithTrailingSlash}${ROUTES.PROFILE.getRoute(currentUserPersonalDetails.accountID ?? CONST.DEFAULT_NUMBER_ID)}`; + : `${urlWithTrailingSlash}${DYNAMIC_ROUTES.PROFILE.getRoute(currentUserPersonalDetails.accountID ?? CONST.DEFAULT_NUMBER_ID)}`; const logo = isReport ? getLogoForWorkspace(report, policy) diff --git a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx index f89ec3795f65..0bb491991ac1 100644 --- a/src/pages/domain/BaseDomainMemberDetailsComponent.tsx +++ b/src/pages/domain/BaseDomainMemberDetailsComponent.tsx @@ -14,11 +14,12 @@ import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import {getDisplayNameOrDefault, getPhoneNumber} from '@libs/PersonalDetailsUtils'; import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {PersonalDetailsList} from '@src/types/onyx'; import DomainNotFoundPageWrapper from './DomainNotFoundPageWrapper'; @@ -97,7 +98,7 @@ function BaseDomainMemberDetailsComponent({domainAccountID, accountID, children, Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute()))} + onPress={() => Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(accountID)))} shouldShowRightIcon /> diff --git a/src/pages/inbox/report/ReactionList/BaseReactionList.tsx b/src/pages/inbox/report/ReactionList/BaseReactionList.tsx index 62c58765849c..386762c2ce33 100755 --- a/src/pages/inbox/report/ReactionList/BaseReactionList.tsx +++ b/src/pages/inbox/report/ReactionList/BaseReactionList.tsx @@ -6,10 +6,11 @@ import OptionRow from '@components/OptionRow'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import variables from '@styles/variables'; import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {PersonalDetails} from '@src/types/onyx'; import HeaderReactionList from './HeaderReactionList'; import type ReactionListProps from './types'; @@ -62,7 +63,7 @@ function BaseReactionList({hasUserReacted = false, users, isVisible = false, emo onSelectRow={() => { onClose?.(); Navigation.setNavigationActionToMicrotaskQueue(() => { - Navigation.navigate(ROUTES.PROFILE.getRoute(item.accountID)); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(item.accountID))); }); }} option={{ diff --git a/src/pages/inbox/report/ReportActionItemSingle.tsx b/src/pages/inbox/report/ReportActionItemSingle.tsx index 2c7c70dc5edf..36dd795bfe27 100644 --- a/src/pages/inbox/report/ReportActionItemSingle.tsx +++ b/src/pages/inbox/report/ReportActionItemSingle.tsx @@ -15,11 +15,12 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import ControlSelection from '@libs/ControlSelection'; import DateUtils from '@libs/DateUtils'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import {getDelegateAccountIDFromReportAction, getHumanAgentAccountIDFromReportAction, getManagerOnVacation, getModerationFlagState, getVacationer} from '@libs/ReportActionsUtils'; import {isOptimisticPersonalDetail} from '@libs/ReportUtils'; import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {Report, ReportAction} from '@src/types/onyx'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; import DelegateOnBehalfOfText from './DelegateOnBehalfOfText'; @@ -52,7 +53,7 @@ type ReportActionItemSingleProps = Partial & { }; const showUserDetails = (accountID: number | undefined) => { - Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute())); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(accountID))); }; const showWorkspaceDetails = (reportID: string | undefined) => { diff --git a/src/pages/iou/request/step/IOURequestEditReport.tsx b/src/pages/iou/request/step/IOURequestEditReport.tsx index 09a28958d9bc..6378bf846832 100644 --- a/src/pages/iou/request/step/IOURequestEditReport.tsx +++ b/src/pages/iou/request/step/IOURequestEditReport.tsx @@ -10,6 +10,7 @@ import usePermissions from '@hooks/usePermissions'; import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import {changeTransactionsReport} from '@libs/actions/Transaction'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import setNavigationActionToMicrotaskQueue from '@libs/Navigation/helpers/setNavigationActionToMicrotaskQueue'; import Navigation from '@libs/Navigation/Navigation'; import {getPersonalDetailsForAccountID, hasViolations as hasViolationsReportUtils} from '@libs/ReportUtils'; @@ -17,7 +18,7 @@ import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import {createNewReport} from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {PersonalDetails, Report} from '@src/types/onyx'; import IOURequestEditReportCommon from './IOURequestEditReportCommon'; @@ -158,7 +159,7 @@ function IOURequestEditReport({route}: IOURequestEditReportProps) { return; } if (shouldSelectPolicy) { - Navigation.navigate(ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute(true, backTo)); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute(true))); return; } handleCreateReport(); diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index c38f46985ea7..7d5bc2466182 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -12,13 +12,14 @@ import useReportOrReportDraft from '@hooks/useReportOrReportDraft'; import useRestartOnReceiptFailure from '@hooks/useRestartOnReceiptFailure'; import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep'; import {createNewReport} from '@libs/actions/Report'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils'; import {getPersonalDetailsForAccountID, getReportOrDraftReport, isPolicyExpenseChat, isReportOutstanding} from '@libs/ReportUtils'; import {isPerDiemRequest, isTimeRequest as isTimeRequestUtil} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {PersonalDetails, ReportAction, ReportActions} from '@src/types/onyx'; import IOURequestEditReportCommon from './IOURequestEditReportCommon'; @@ -170,7 +171,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { } if (shouldSelectPolicy) { setSelectedTransactions([transactionID]); - Navigation.navigate(ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute(true, backTo)); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute(true))); return; } handleCreateReport(); diff --git a/src/pages/media/AttachmentModalScreen/index.tsx b/src/pages/media/AttachmentModalScreen/index.tsx index 531ee1eeb0af..65fa97145d3b 100644 --- a/src/pages/media/AttachmentModalScreen/index.tsx +++ b/src/pages/media/AttachmentModalScreen/index.tsx @@ -57,11 +57,11 @@ function AttachmentModalScreen({route, ); } - if (route.name === SCREENS.PROFILE_AVATAR) { + if (route.name === SCREENS.DYNAMIC_PROFILE_AVATAR) { return ( } - navigation={navigation as NavigationType} + route={routeWithContext as RouteType} + navigation={navigation as NavigationType} /> ); } diff --git a/src/pages/media/AttachmentModalScreen/routes/ProfileAvatarModalContent.tsx b/src/pages/media/AttachmentModalScreen/routes/ProfileAvatarModalContent.tsx index fe5cfaf287ab..9905185c25d9 100644 --- a/src/pages/media/AttachmentModalScreen/routes/ProfileAvatarModalContent.tsx +++ b/src/pages/media/AttachmentModalScreen/routes/ProfileAvatarModalContent.tsx @@ -14,7 +14,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import useDownloadAttachment from './hooks/useDownloadAttachment'; -function ProfileAvatarModalContent({navigation, route}: AttachmentModalScreenProps) { +function ProfileAvatarModalContent({navigation, route}: AttachmentModalScreenProps) { const {accountID = CONST.DEFAULT_NUMBER_ID, source: tempSource, originalFileName: tempOriginalFileName} = route.params; const defaultAvatars = useDefaultAvatars(); diff --git a/src/pages/media/AttachmentModalScreen/types.ts b/src/pages/media/AttachmentModalScreen/types.ts index 0fd6cfd60ada..f662e6edf30f 100644 --- a/src/pages/media/AttachmentModalScreen/types.ts +++ b/src/pages/media/AttachmentModalScreen/types.ts @@ -25,7 +25,7 @@ type AttachmentModalScreenType = | typeof SCREENS.REPORT_ATTACHMENTS | typeof SCREENS.REPORT_ADD_ATTACHMENT | typeof SCREENS.REPORT_AVATAR - | typeof SCREENS.PROFILE_AVATAR + | typeof SCREENS.DYNAMIC_PROFILE_AVATAR | typeof SCREENS.WORKSPACE_AVATAR | typeof SCREENS.WORKSPACE_DOCUMENT | typeof SCREENS.TRANSACTION_RECEIPT diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index d5fea3a0ad46..fce0658525e5 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -377,7 +377,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers const openMemberDetails = useCallback( (item: MemberOption) => { if (!isPolicyAdmin || !isPaidGroupPolicy(policy)) { - Navigation.navigate(ROUTES.PROFILE.getRoute(item.accountID, Navigation.getActiveRoute())); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(item.accountID))); return; } clearWorkspaceOwnerChangeFlow(policyID); diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index 997ba28868ee..1a4beb00b9f1 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -32,6 +32,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {setPolicyPreventSelfApproval} from '@libs/actions/Policy/Policy'; import {removeApprovalWorkflow as removeApprovalWorkflowAction, updateApprovalWorkflow} from '@libs/actions/Workflow'; import {getAllCardsForWorkspace, getCardFeedIcon, getCardFeedWithDomainID, getPlaidInstitutionIconUrl, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import navigateAfterInteraction from '@libs/Navigation/navigateAfterInteraction'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getDisplayNameOrDefault, getPhoneNumber} from '@libs/PersonalDetailsUtils'; @@ -48,7 +49,7 @@ import variables from '@styles/variables'; import {clearWorkspaceOwnerChangeFlow, openPolicyMemberProfilePage, removeMembers} from '@userActions/Policy/Member'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {CompanyCardFeed, CompanyCardFeedWithDomainID, Card as MemberCard, PersonalDetails, PersonalDetailsList} from '@src/types/onyx'; @@ -236,7 +237,7 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM }; const navigateToProfile = () => { - Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute())); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(accountID))); }; const navigateToDetails = (card: MemberCard) => { diff --git a/tests/navigation/getMatchingNewRouteTest.ts b/tests/navigation/getMatchingNewRouteTest.ts index 7ae901d3be40..6e702a8e3887 100644 --- a/tests/navigation/getMatchingNewRouteTest.ts +++ b/tests/navigation/getMatchingNewRouteTest.ts @@ -87,6 +87,14 @@ describe('getBestMatchingPath', () => { expect(getMatchingNewRoute('/travel/upgrade?backTo=/home')).toBe('/travel/travel-upgrade?backTo=/home'); }); + it('redirects legacy profile avatar path to new avatar route', () => { + expect(getMatchingNewRoute('/a/123/avatar')).toBe('/avatar/123'); + }); + + it('preserves query params when redirecting legacy profile avatar path', () => { + expect(getMatchingNewRoute('/a/123/avatar?backTo=/home')).toBe('/avatar/123?backTo=/home'); + }); + it('does not redirect paths that look similar but do not match migrated patterns', () => { expect(getMatchingNewRoute('/r/123/settings/visibility')).toBe(undefined); expect(getMatchingNewRoute('/workspaces/abc/overview/plan')).toBe(undefined); diff --git a/tests/ui/NewReportWorkspaceSelectionPageTest.tsx b/tests/ui/NewReportWorkspaceSelectionPageTest.tsx index c1953816cdd8..9fbc037b26a1 100644 --- a/tests/ui/NewReportWorkspaceSelectionPageTest.tsx +++ b/tests/ui/NewReportWorkspaceSelectionPageTest.tsx @@ -9,7 +9,7 @@ import OnyxListItemProvider from '@components/OnyxListItemProvider'; import {createNewReport} from '@libs/actions/Report'; import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator'; import type {NewReportWorkspaceSelectionNavigatorParamList} from '@libs/Navigation/types'; -import NewReportWorkspaceSelectionPage from '@pages/NewReportWorkspaceSelectionPage'; +import DynamicNewReportWorkspaceSelectionPage from '@pages/DynamicNewReportWorkspaceSelectionPage'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; @@ -62,8 +62,8 @@ function renderPage() { diff --git a/tests/ui/components/SearchActionsBarCreateButtonTest.tsx b/tests/ui/components/SearchActionsBarCreateButtonTest.tsx index add8172dea29..1f687d870aeb 100644 --- a/tests/ui/components/SearchActionsBarCreateButtonTest.tsx +++ b/tests/ui/components/SearchActionsBarCreateButtonTest.tsx @@ -10,10 +10,11 @@ import SearchActionsBarCreateButton from '@components/Search/SearchPageHeader/Se import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; import {createNewReport} from '@libs/actions/Report'; import interceptAnonymousUser from '@libs/interceptAnonymousUser'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import {translateLocal} from '../../utils/TestHelper'; import waitForBatchedUpdatesWithAct from '../../utils/waitForBatchedUpdatesWithAct'; @@ -195,7 +196,7 @@ describe('SearchActionsBarCreateButton', () => { await waitForBatchedUpdatesWithAct(); // Then it navigates to workspace selection - expect(mockNavigate).toHaveBeenCalledWith(ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute()); + expect(mockNavigate).toHaveBeenCalledWith(createDynamicRoute(DYNAMIC_ROUTES.NEW_REPORT_WORKSPACE_SELECTION.path)); }); it('should create report directly when a single default workspace exists', async () => { @@ -305,7 +306,7 @@ describe('SearchActionsBarCreateButton', () => { await waitForBatchedUpdatesWithAct(); // Then it navigates to workspace selection since there are multiple workspaces and the default is restricted - expect(mockNavigate).toHaveBeenCalledWith(ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute()); + expect(mockNavigate).toHaveBeenCalledWith(createDynamicRoute(DYNAMIC_ROUTES.NEW_REPORT_WORKSPACE_SELECTION.path)); }); it('should navigate to restricted action page when owner billing is restricted and only one workspace exists', async () => { diff --git a/tests/unit/MentionUserRendererTest.tsx b/tests/unit/MentionUserRendererTest.tsx index f11d5d1ae3b9..8e3ae65b1e93 100644 --- a/tests/unit/MentionUserRendererTest.tsx +++ b/tests/unit/MentionUserRendererTest.tsx @@ -8,23 +8,27 @@ import MentionUserRenderer from '@components/HTMLEngineProvider/HTMLRenderers/Me import OnyxListItemProvider from '@components/OnyxListItemProvider'; import {ShowContextMenuActionsContext, ShowContextMenuStateContext} from '@components/ShowContextMenuContext'; import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import {showContextMenu} from '@pages/inbox/report/ContextMenu/ReportActionContextMenu'; import CONST from '@src/CONST'; import IntlStore from '@src/languages/IntlStore'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {PersonalDetails, Report, ReportAction} from '@src/types/onyx'; import {translateLocal} from '../utils/TestHelper'; // Mock Navigation to avoid actual navigation calls jest.mock('@libs/Navigation/Navigation', () => ({ + getActiveRoute: jest.fn(), getReportRHPActiveRoute: jest.fn(), getActiveRouteWithoutParams: jest.fn(() => ''), isNavigationReady: jest.fn(() => Promise.resolve()), navigate: jest.fn(), })); +const MOCK_BASE_ROUTE = 'r/123'; + // Mock showContextMenu to verify it's called with correct parameters jest.mock('@pages/inbox/report/ContextMenu/ReportActionContextMenu', () => ({ showContextMenu: jest.fn(), @@ -167,6 +171,8 @@ describe('MentionUserRenderer', () => { mockPersonalDetails = {}; IntlStore.load(CONST.LOCALES.DEFAULT); jest.clearAllMocks(); + (Navigation.getActiveRoute as jest.Mock).mockReturnValue(MOCK_BASE_ROUTE); + (Navigation.getReportRHPActiveRoute as jest.Mock).mockReturnValue(MOCK_BASE_ROUTE); }); test('renders phone number (not displayName) when user has phone login', () => { @@ -235,7 +241,7 @@ describe('MentionUserRenderer', () => { renderMention({tnode}); const mention = screen.getByTestId('mention-user'); fireEvent(mention, 'press', {preventDefault: jest.fn()}); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.PROFILE.getRoute(103)); + expect(Navigation.navigate).toHaveBeenCalledWith(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(103), MOCK_BASE_ROUTE)); }); test('navigates with mention text as fallback when no accountID', () => { @@ -259,7 +265,7 @@ describe('MentionUserRenderer', () => { // Verify navigation to own profile works const mention = screen.getByTestId('mention-user'); fireEvent(mention, 'press', {preventDefault: jest.fn()}); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.PROFILE.getRoute(1)); + expect(Navigation.navigate).toHaveBeenCalledWith(createDynamicRoute(DYNAMIC_ROUTES.PROFILE.getRoute(1), MOCK_BASE_ROUTE)); }); test('uses displayName when login is empty', () => { diff --git a/tests/unit/useCreateReportTest.tsx b/tests/unit/useCreateReportTest.tsx index a39e2dff81de..339653de6565 100644 --- a/tests/unit/useCreateReportTest.tsx +++ b/tests/unit/useCreateReportTest.tsx @@ -5,7 +5,7 @@ import useOnyx from '@hooks/useOnyx'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type {Policy} from '@src/types/onyx'; // ── Mocks ────────────────────────────────────────────────────────────────────── @@ -45,6 +45,7 @@ jest.mock('@libs/interceptAnonymousUser', () => jest.fn((cb: () => void) => cb() jest.mock('@libs/Navigation/Navigation', () => ({ navigate: jest.fn(), + getActiveRoute: jest.fn(() => ''), })); const reportIDCounter = {value: 100}; @@ -135,7 +136,7 @@ describe('useCreateReport', () => { result.current.createReport(); }); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute()); + expect(Navigation.navigate).toHaveBeenCalledWith(DYNAMIC_ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute()); expect(onCreateReport).not.toHaveBeenCalled(); }); @@ -161,7 +162,7 @@ describe('useCreateReport', () => { result.current.createReport(); }); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute()); + expect(Navigation.navigate).toHaveBeenCalledWith(DYNAMIC_ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute()); }); it('navigates to workspace selector when default is personal and there are 2+ non-personal workspaces', () => { @@ -188,7 +189,7 @@ describe('useCreateReport', () => { result.current.createReport(); }); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute()); + expect(Navigation.navigate).toHaveBeenCalledWith(DYNAMIC_ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute()); expect(onCreateReport).not.toHaveBeenCalled(); }); @@ -212,7 +213,7 @@ describe('useCreateReport', () => { result.current.createReport(); }); - expect(Navigation.navigate).not.toHaveBeenCalledWith(ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute()); + expect(Navigation.navigate).not.toHaveBeenCalledWith(DYNAMIC_ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute()); expect(onCreateReport).toHaveBeenCalledWith(false); }); @@ -240,7 +241,7 @@ describe('useCreateReport', () => { result.current.createReport(); }); - expect(Navigation.navigate).not.toHaveBeenCalledWith(ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute()); + expect(Navigation.navigate).not.toHaveBeenCalledWith(DYNAMIC_ROUTES.NEW_REPORT_WORKSPACE_SELECTION.getRoute()); expect(onCreateReport).toHaveBeenCalledWith(false); }); });