Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,14 @@ const DYNAMIC_ROUTES = {
],
getRoute: (cardID: string) => `missing-personal-details/${cardID}/confirm-magic-code` as const,
},
MONEY_REQUEST_STEP_TIME: {
path: 'per-diem-time',
entryScreens: [SCREENS.MONEY_REQUEST.STEP_DESTINATION, SCREENS.MONEY_REQUEST.CREATE],
},
MONEY_REQUEST_STEP_TIME_EDIT: {
path: 'per-diem-time-edit',
entryScreens: [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION],
},
PROFILE: {
path: 'a/:accountID',
entryScreens: ['*'],
Expand Down Expand Up @@ -1943,11 +1951,6 @@ const ROUTES = {
getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backToReport?: string, backTo = '') =>
getUrlWithBackToParam(`${action as string}/${iouType as string}/destination/${transactionID}/${reportID}${backToReport ? `/${backToReport}` : ''}`, backTo),
},
MONEY_REQUEST_STEP_TIME: {
route: ':action/:iouType/time/:transactionID/:reportID/:backToReport?',
getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backToReport?: string, backTo = '') =>
getUrlWithBackToParam(`${action as string}/${iouType as string}/time/${transactionID}/${reportID}${backToReport ? `/${backToReport}` : ''}`, backTo),
},
MONEY_REQUEST_STEP_SUBRATE: {
route: ':action/:iouType/subrate/:transactionID/:reportID/:backToReport?/:pageIndex',
getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backToReport?: string, backTo = '') =>
Expand All @@ -1958,11 +1961,6 @@ const ROUTES = {
getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') =>
getUrlWithBackToParam(`${action as string}/${iouType as string}/destination/${transactionID}/${reportID}/edit`, backTo),
},
MONEY_REQUEST_STEP_TIME_EDIT: {
route: ':action/:iouType/time/:transactionID/:reportID/edit',
getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') =>
getUrlWithBackToParam(`${action as string}/${iouType as string}/time/${transactionID}/${reportID}/edit`, backTo),
},
MONEY_REQUEST_STEP_SUBRATE_EDIT: {
route: ':action/:iouType/subrate/:transactionID/:reportID/edit/:pageIndex',
getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, pageIndex = 0, backTo = '') =>
Expand Down
4 changes: 2 additions & 2 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,10 +356,10 @@ const SCREENS = {
STEP_ATTENDEES: 'Money_Request_Attendee',
STEP_ACCOUNTANT: 'Money_Request_Accountant',
STEP_DESTINATION: 'Money_Request_Destination',
STEP_TIME: 'Money_Request_Time',
DYNAMIC_STEP_TIME: 'Dynamic_Money_Request_Time',
STEP_SUBRATE: 'Money_Request_SubRate',
STEP_DESTINATION_EDIT: 'Money_Request_Destination_Edit',
STEP_TIME_EDIT: 'Money_Request_Time_Edit',
DYNAMIC_STEP_TIME_EDIT: 'Dynamic_Money_Request_Time_Edit',
STEP_SUBRATE_EDIT: 'Money_Request_SubRate_Edit',
STEP_REPORT: 'Money_Request_Report',
EDIT_REPORT: 'Money_Request_Edit_Report',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';

import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute';
import Navigation from '@libs/Navigation/Navigation';
import {getDestinationForDisplay, getSubratesFields, getSubratesForDisplay, getTimeDifferenceIntervals, getTimeForDisplay} from '@libs/PerDiemRequestUtils';

import CONST from '@src/CONST';
import type {IOUAction, IOUType} from '@src/CONST';
import ROUTES from '@src/ROUTES';
import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';
import type {CustomUnit} from '@src/types/onyx/Policy';

Expand Down Expand Up @@ -125,7 +126,7 @@ function PerDiemFields({perDiemCustomUnit, transaction, isReadOnly, didConfirm,
if (!transactionID) {
return;
}
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TIME_EDIT.getRoute(action, iouType, transactionID, reportID));
Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.MONEY_REQUEST_STEP_TIME_EDIT.path));
}}
disabled={didConfirm}
interactive={!isReadOnly}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,10 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator<MoneyRequestNa
[SCREENS.MONEY_REQUEST.STEP_ACCOUNTANT]: () => require<ReactComponentModule>('../../../../pages/iou/request/step/IOURequestStepAccountant').default,
[SCREENS.MONEY_REQUEST.STEP_UPGRADE]: () => require<ReactComponentModule>('../../../../pages/iou/request/step/IOURequestStepUpgrade').default,
[SCREENS.MONEY_REQUEST.STEP_DESTINATION]: () => require<ReactComponentModule>('../../../../pages/iou/request/step/IOURequestStepDestination').default,
[SCREENS.MONEY_REQUEST.STEP_TIME]: () => require<ReactComponentModule>('../../../../pages/iou/request/step/IOURequestStepTime').default,
[SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME]: () => require<ReactComponentModule>('../../../../pages/iou/request/step/DynamicIOURequestStepTime').default,
[SCREENS.MONEY_REQUEST.STEP_SUBRATE]: () => require<ReactComponentModule>('../../../../pages/iou/request/step/IOURequestStepSubrate').default,
[SCREENS.MONEY_REQUEST.STEP_DESTINATION_EDIT]: () => require<ReactComponentModule>('../../../../pages/iou/request/step/IOURequestStepDestination').default,
[SCREENS.MONEY_REQUEST.STEP_TIME_EDIT]: () => require<ReactComponentModule>('../../../../pages/iou/request/step/IOURequestStepTime').default,
[SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME_EDIT]: () => require<ReactComponentModule>('../../../../pages/iou/request/step/DynamicIOURequestStepTime').default,
[SCREENS.MONEY_REQUEST.STEP_SUBRATE_EDIT]: () => require<ReactComponentModule>('../../../../pages/iou/request/step/IOURequestStepSubrate').default,
[SCREENS.MONEY_REQUEST.RECEIPT_VIEW]: () => require<ReactComponentModule>('../../../../pages/iou/request/step/IOURequestStepScan/ReceiptView').default,
[SCREENS.MONEY_REQUEST.SPLIT_EXPENSE]: () => require<ReactComponentModule>('../../../../pages/iou/SplitExpensePage').default,
Expand Down
6 changes: 6 additions & 0 deletions src/libs/Navigation/linkingConfig/OldRoutes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
const oldRoutes: Record<string, string> = {
/* eslint-disable @typescript-eslint/naming-convention */
// Per diem time step migrated to dynamic routes (#83850). Dynamic suffixes are namespaced `per-diem-*` because
// dynamic route paths must be globally unique across DYNAMIC_ROUTES (react-navigation flattens them). Edit variant
// redirects to the confirmation-based suffix; the wizard variant to the destination-based suffix. Query is not
// preserved here (the trailing wildcard would swallow it) - acceptable because callers no longer append `?backTo=`.
'/*/*/time/*/*/edit': '/$1/$2/confirmation/$3/$4/per-diem-time-edit',
'/*/*/time/*/*': '/$1/$2/destination/$3/$4/per-diem-time',
'/settings/*/category/*/edit': '/settings/$1/categories/category-settings/$2/category-edit',
'/settings/*/category/*': '/settings/$1/categories/category-settings/$2',
'/settings/*/tags/*/edit': '/settings/$1/tags/settings-tags-edit/$2',
Expand Down
4 changes: 2 additions & 2 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1785,10 +1785,10 @@ const config: LinkingOptions<RootNavigatorParamList>['config'] = {
[SCREENS.MONEY_REQUEST.STEP_ACCOUNTANT]: ROUTES.MONEY_REQUEST_ACCOUNTANT.route,
[SCREENS.MONEY_REQUEST.STEP_UPGRADE]: ROUTES.MONEY_REQUEST_UPGRADE.route,
[SCREENS.MONEY_REQUEST.STEP_DESTINATION]: ROUTES.MONEY_REQUEST_STEP_DESTINATION.route,
[SCREENS.MONEY_REQUEST.STEP_TIME]: ROUTES.MONEY_REQUEST_STEP_TIME.route,
[SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME]: DYNAMIC_ROUTES.MONEY_REQUEST_STEP_TIME.path,
[SCREENS.MONEY_REQUEST.STEP_SUBRATE]: ROUTES.MONEY_REQUEST_STEP_SUBRATE.route,
[SCREENS.MONEY_REQUEST.STEP_DESTINATION_EDIT]: ROUTES.MONEY_REQUEST_STEP_DESTINATION_EDIT.route,
[SCREENS.MONEY_REQUEST.STEP_TIME_EDIT]: ROUTES.MONEY_REQUEST_STEP_TIME_EDIT.route,
[SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME_EDIT]: DYNAMIC_ROUTES.MONEY_REQUEST_STEP_TIME_EDIT.path,
[SCREENS.MONEY_REQUEST.STEP_SUBRATE_EDIT]: ROUTES.MONEY_REQUEST_STEP_SUBRATE_EDIT.route,
[SCREENS.MONEY_REQUEST.STEP_TIME_RATE]: ROUTES.MONEY_REQUEST_STEP_TIME_RATE.route,
[SCREENS.MONEY_REQUEST.STEP_HOURS]: ROUTES.MONEY_REQUEST_STEP_HOURS.route,
Expand Down
8 changes: 2 additions & 6 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2277,14 +2277,12 @@ type MoneyRequestNavigatorParamList = {
// 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 | undefined;
};
[SCREENS.MONEY_REQUEST.STEP_TIME]: {
[SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME]: {
action: IOUAction;
iouType: Exclude<IOUType, typeof CONST.IOU.TYPE.REQUEST | typeof CONST.IOU.TYPE.SEND>;
transactionID: string;
reportID: string;
backToReport?: 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 | undefined;
};
[SCREENS.MONEY_REQUEST.STEP_SUBRATE]: {
iouType: Exclude<IOUType, typeof CONST.IOU.TYPE.REQUEST | typeof CONST.IOU.TYPE.SEND>;
Expand All @@ -2304,14 +2302,12 @@ type MoneyRequestNavigatorParamList = {
// 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 | undefined;
};
[SCREENS.MONEY_REQUEST.STEP_TIME_EDIT]: {
[SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME_EDIT]: {
action: IOUAction;
iouType: Exclude<IOUType, typeof CONST.IOU.TYPE.REQUEST | typeof CONST.IOU.TYPE.SEND>;
transactionID: string;
reportID: string;
backToReport?: 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 | undefined;
};
[SCREENS.MONEY_REQUEST.STEP_SUBRATE_EDIT]: {
iouType: Exclude<IOUType, typeof CONST.IOU.TYPE.REQUEST | typeof CONST.IOU.TYPE.SEND>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,30 @@ import type {FormOnyxValues} from '@components/Form/types';
import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import TimeModalPicker from '@components/TimeModalPicker';

import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useDynamicBackPath from '@hooks/useDynamicBackPath';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import usePolicyForTransaction from '@hooks/usePolicyForTransaction';
import useThemeStyles from '@hooks/useThemeStyles';

import DateUtils from '@libs/DateUtils';
import {addErrorMessage} from '@libs/ErrorUtils';
import {isValidMoneyRequestType} from '@libs/IOUUtils';
import Navigation from '@libs/Navigation/Navigation';
import {getActivePoliciesWithExpenseChatAndPerDiemEnabledAndHasRates} from '@libs/PolicyUtils';
import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan';

import {getIOURequestPolicyID, setMoneyRequestDateAttribute} from '@userActions/IOU/MoneyRequest';

import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES';
import SCREENS from '@src/SCREENS';
import INPUT_IDS from '@src/types/form/MoneyRequestTimeForm';
import type * as OnyxTypes from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';

import type {OnyxEntry} from 'react-native-onyx';

import React, {useMemo} from 'react';
import React from 'react';
import {View} from 'react-native';

import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound';
Expand All @@ -39,7 +37,7 @@ import StepScreenWrapper from './StepScreenWrapper';
import withFullTransactionOrNotFound from './withFullTransactionOrNotFound';
import withWritableReportOrNotFound from './withWritableReportOrNotFound';

type IOURequestStepTimeProps = WithWritableReportOrNotFoundProps<typeof SCREENS.MONEY_REQUEST.STEP_TIME | typeof SCREENS.MONEY_REQUEST.STEP_TIME_EDIT> & {
type DynamicIOURequestStepTimeProps = WithWritableReportOrNotFoundProps<typeof SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME | typeof SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME_EDIT> & {
/** Holds data related to Money Request view state, rather than the underlying Money Request data. */
transaction: OnyxEntry<OnyxTypes.Transaction>;

Expand All @@ -50,17 +48,16 @@ type IOURequestStepTimeProps = WithWritableReportOrNotFoundProps<typeof SCREENS.
report: OnyxEntry<Report>;
};

function IOURequestStepTime({
function DynamicIOURequestStepTime({
route: {
params: {action, iouType, reportID, transactionID, backTo, backToReport},
params: {action, iouType, reportID, transactionID, backToReport},
name,
},
transaction,
isLoadingTransaction,
report,
}: IOURequestStepTimeProps) {
}: DynamicIOURequestStepTimeProps) {
const styles = useThemeStyles();
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
const iouPolicyID = getIOURequestPolicyID(transaction, report);
const {policy} = usePolicyForTransaction({
transaction,
Expand All @@ -74,37 +71,17 @@ function IOURequestStepTime({
const currentDateAttributes = transaction?.comment?.customUnit?.attributes?.dates;
const currentStartDate = currentDateAttributes?.start ? DateUtils.extractDate(currentDateAttributes.start) : undefined;
const currentEndDate = currentDateAttributes?.end ? DateUtils.extractDate(currentDateAttributes.end) : undefined;
const isEditPage = name === SCREENS.MONEY_REQUEST.STEP_TIME_EDIT;
const isEditPage = name === SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME_EDIT;

const shouldShowNotFound = !isValidMoneyRequestType(iouType) || isEmptyObject(policy) || (isEditPage && isEmptyObject(transaction?.comment?.customUnit));
const {login: currentUserLogin} = useCurrentUserPersonalDetails();
const policiesWithPerDiemEnabled = useMemo(() => getActivePoliciesWithExpenseChatAndPerDiemEnabledAndHasRates(allPolicies, currentUserLogin), [allPolicies, currentUserLogin]);
const hasMoreThanOnePolicyWithPerDiemEnabled = policiesWithPerDiemEnabled.length > 1;

const navigateBack = () => {
if (isEditPage) {
Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, iouType, transactionID, reportID));
return;
}

if (backTo) {
Navigation.goBack(backTo);
return;
}
// Back removes this step's dynamic suffix from the current URL. In edit mode the suffix (`time-edit`) sits on the
// confirmation base, so back returns to confirmation. In the wizard flow the suffix (`time`) sits on the destination
// base (multi-policy) or the start base (single-policy), so back returns to whichever preceding step was appended to.
const backPath = useDynamicBackPath(isEditPage ? DYNAMIC_ROUTES.MONEY_REQUEST_STEP_TIME_EDIT.path : DYNAMIC_ROUTES.MONEY_REQUEST_STEP_TIME.path);

if (transaction?.isFromGlobalCreate || iouType === CONST.IOU.TYPE.TRACK) {
// We want to navigate to destination step only when the first step was the workspace selector.
// If there is only one policy with per diem enabled, we want to navigate back to the start step because there is no separate destination step in that flow.
if (hasMoreThanOnePolicyWithPerDiemEnabled) {
Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_DESTINATION.getRoute(action, iouType, transactionID, reportID, backToReport));
return;
}

// If there is only one per diem policy, we can't override the reportID that is already on the stack to make sure we go back to the right screen.
Navigation.goBack();
}

Navigation.goBack(ROUTES.MONEY_REQUEST_CREATE_TAB_PER_DIEM.getRoute(action, iouType, transactionID, reportID));
const navigateBack = () => {
Navigation.goBack(backPath);
};

const validate = (value: FormOnyxValues<typeof ONYXKEYS.FORMS.MONEY_REQUEST_TIME_FORM>) => {
Expand Down Expand Up @@ -161,11 +138,11 @@ function IOURequestStepTime({

return (
<StepScreenWrapper
headerTitle={backTo ? translate('iou.time') : tabTitles[iouType]}
headerTitle={isEditPage ? translate('iou.time') : tabTitles[iouType]}
onBackButtonPress={navigateBack}
shouldShowNotFoundPage={shouldShowNotFound}
shouldShowWrapper
testID="IOURequestStepTime"
testID="DynamicIOURequestStepTime"
includeSafeAreaPaddingBottom
>
<FormProvider
Expand Down Expand Up @@ -211,8 +188,8 @@ function IOURequestStepTime({
);
}

const IOURequestStepTimeWithFullTransactionOrNotFound = withFullTransactionOrNotFound(IOURequestStepTime);
const DynamicIOURequestStepTimeWithFullTransactionOrNotFound = withFullTransactionOrNotFound(DynamicIOURequestStepTime);

const IOURequestStepTimeWithWritableReportOrNotFound = withWritableReportOrNotFound(IOURequestStepTimeWithFullTransactionOrNotFound);
const DynamicIOURequestStepTimeWithWritableReportOrNotFound = withWritableReportOrNotFound(DynamicIOURequestStepTimeWithFullTransactionOrNotFound);

export default IOURequestStepTimeWithWritableReportOrNotFound;
export default DynamicIOURequestStepTimeWithWritableReportOrNotFound;
10 changes: 8 additions & 2 deletions src/pages/iou/request/step/IOURequestStepDestination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import {fetchPerDiemRates} from '@libs/actions/Policy/PerDiem';
import {setTransactionReport} from '@libs/actions/Transaction';
import {getInitialPerDiemTargetReport} from '@libs/IOUUtils';
import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute';
import Navigation from '@libs/Navigation/Navigation';
import {getPerDiemCustomUnit, getPolicyByCustomUnitID, isPolicyAdmin} from '@libs/PolicyUtils';
import {findSelfDMReportID, getPolicyExpenseChat} from '@libs/ReportUtils';
Expand All @@ -40,7 +41,7 @@ import {clearSubrates} from '@userActions/IOU/PerDiem';

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 {Report} from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
Expand Down Expand Up @@ -158,7 +159,12 @@ function IOURequestStepDestination({
if (backTo) {
navigateBack();
} else {
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TIME.getRoute(action, targetIouType, transactionID, targetReport?.reportID ?? reportID, backToReport));
// Time is a dynamic route: build it on the start base when the destination is shown inline on the start page
// (single per-diem policy) and on the destination base otherwise, so Time's back returns to the right step.
const timeBase = openedFromStartPage
? ROUTES.MONEY_REQUEST_CREATE.getRoute(action, targetIouType, transactionID, targetReport?.reportID ?? reportID, backToReport)
: ROUTES.MONEY_REQUEST_STEP_DESTINATION.getRoute(action, targetIouType, transactionID, targetReport?.reportID ?? reportID, backToReport);
Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.MONEY_REQUEST_STEP_TIME.path, timeBase));
}
};

Expand Down
Loading
Loading