Skip to content

Commit

Permalink
Merge pull request Expensify#51519 from wildan-m/wildan/fix/48630-cal…
Browse files Browse the repository at this point in the history
…culate-route-for-pending-trx-backup

Fix receipt disappears when dismissing distance editor after receipt is generated
  • Loading branch information
Gonals authored Oct 31, 2024
2 parents eb3ff5a + 198ccb3 commit d032ddf
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 13 deletions.
6 changes: 6 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1183,7 +1183,13 @@ const CONST = {
PENDING: 'Pending',
POSTED: 'Posted',
},
STATE: {
CURRENT: 'current',
DRAFT: 'draft',
BACKUP: 'backup',
},
},

MCC_GROUPS: {
AIRLINES: 'Airlines',
COMMUTER: 'Commuter',
Expand Down
14 changes: 10 additions & 4 deletions src/hooks/useFetchRoute.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import isEqual from 'lodash/isEqual';
import {useEffect} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import * as IOUUtils from '@libs/IOUUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import * as TransactionAction from '@userActions/Transaction';
import type {IOUAction} from '@src/CONST';
import CONST from '@src/CONST';
import type {Transaction} from '@src/types/onyx';
import type {WaypointCollection} from '@src/types/onyx/Transaction';
import type TransactionState from '@src/types/utils/TransactionStateType';
import useNetwork from './useNetwork';
import usePrevious from './usePrevious';

export default function useFetchRoute(transaction: OnyxEntry<Transaction>, waypoints: WaypointCollection | undefined, action: IOUAction) {
export default function useFetchRoute(
transaction: OnyxEntry<Transaction>,
waypoints: WaypointCollection | undefined,
action: IOUAction,
transactionState: TransactionState = CONST.TRANSACTION.STATE.CURRENT,
) {
const {isOffline} = useNetwork();
const hasRouteError = !!transaction?.errorFields?.route;
const hasRoute = TransactionUtils.hasRoute(transaction);
Expand All @@ -27,8 +33,8 @@ export default function useFetchRoute(transaction: OnyxEntry<Transaction>, waypo
return;
}

TransactionAction.getRoute(transaction.transactionID, validatedWaypoints, IOUUtils.shouldUseTransactionDraft(action));
}, [shouldFetchRoute, transaction?.transactionID, validatedWaypoints, isOffline, action]);
TransactionAction.getRoute(transaction.transactionID, validatedWaypoints, transactionState);
}, [shouldFetchRoute, transaction?.transactionID, validatedWaypoints, isOffline, action, transactionState]);

return {shouldFetchRoute, validatedWaypoints};
}
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,7 @@ const READ_COMMANDS = {
SEND_PERFORMANCE_TIMING: 'SendPerformanceTiming',
GET_ROUTE: 'GetRoute',
GET_ROUTE_FOR_DRAFT: 'GetRouteForDraft',
GET_ROUTE_FOR_BACKUP: 'GetRouteForBackup',
GET_STATEMENT_PDF: 'GetStatementPDF',
OPEN_ONFIDO_FLOW: 'OpenOnfidoFlow',
OPEN_INITIAL_SETTINGS_PAGE: 'OpenInitialSettingsPage',
Expand Down Expand Up @@ -971,6 +972,7 @@ type ReadCommandParameters = {
[READ_COMMANDS.SEND_PERFORMANCE_TIMING]: Parameters.SendPerformanceTimingParams;
[READ_COMMANDS.GET_ROUTE]: Parameters.GetRouteParams;
[READ_COMMANDS.GET_ROUTE_FOR_DRAFT]: Parameters.GetRouteParams;
[READ_COMMANDS.GET_ROUTE_FOR_BACKUP]: Parameters.GetRouteParams;
[READ_COMMANDS.GET_STATEMENT_PDF]: Parameters.GetStatementPDFParams;
[READ_COMMANDS.OPEN_ONFIDO_FLOW]: null;
[READ_COMMANDS.OPEN_INITIAL_SETTINGS_PAGE]: null;
Expand Down
52 changes: 45 additions & 7 deletions src/libs/actions/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
import type {PersonalDetails, RecentWaypoint, ReportAction, ReportActions, ReviewDuplicates, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx';
import type {OnyxData} from '@src/types/onyx/Request';
import type {WaypointCollection} from '@src/types/onyx/Transaction';
import type TransactionState from '@src/types/utils/TransactionStateType';

let recentWaypoints: RecentWaypoint[] = [];
Onyx.connect({
Expand Down Expand Up @@ -203,13 +204,27 @@ function removeWaypoint(transaction: OnyxEntry<Transaction>, currentIndex: strin
return Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction?.transactionID}`, newTransaction);
}

function getOnyxDataForRouteRequest(transactionID: string, isDraft = false): OnyxData {
function getOnyxDataForRouteRequest(transactionID: string, transactionState: TransactionState = CONST.TRANSACTION.STATE.CURRENT): OnyxData {
let keyPrefix;
switch (transactionState) {
case CONST.TRANSACTION.STATE.DRAFT:
keyPrefix = ONYXKEYS.COLLECTION.TRANSACTION_DRAFT;
break;
case CONST.TRANSACTION.STATE.BACKUP:
keyPrefix = ONYXKEYS.COLLECTION.TRANSACTION_BACKUP;
break;
case CONST.TRANSACTION.STATE.CURRENT:
default:
keyPrefix = ONYXKEYS.COLLECTION.TRANSACTION;
break;
}

return {
optimisticData: [
{
// Clears any potentially stale error messages from fetching the route
onyxMethod: Onyx.METHOD.MERGE,
key: `${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`,
key: `${keyPrefix}${transactionID}`,
value: {
comment: {
isLoading: true,
Expand All @@ -224,18 +239,26 @@ function getOnyxDataForRouteRequest(transactionID: string, isDraft = false): Ony
successData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`,
key: `${keyPrefix}${transactionID}`,
value: {
comment: {
isLoading: false,
},
// When the user opens the distance request editor and changes the connection from offline to online,
// the transaction's pendingFields and pendingAction will be removed, but not transactionBackup.
// We clear the pendingFields and pendingAction for the backup here to ensure consistency with the transaction.
// Without this, the map will not be clickable if the user dismisses the distance request editor without saving.
...(transactionState === CONST.TRANSACTION.STATE.BACKUP && {
pendingFields: {waypoints: null},
pendingAction: null,
}),
},
},
],
failureData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`,
key: `${keyPrefix}${transactionID}`,
value: {
comment: {
isLoading: false,
Expand Down Expand Up @@ -264,15 +287,30 @@ function sanitizeRecentWaypoints(waypoints: WaypointCollection): WaypointCollect
* Gets the route for a set of waypoints
* Used so we can generate a map view of the provided waypoints
*/
function getRoute(transactionID: string, waypoints: WaypointCollection, isDraft: boolean) {

function getRoute(transactionID: string, waypoints: WaypointCollection, routeType: TransactionState = CONST.TRANSACTION.STATE.CURRENT) {
const parameters: GetRouteParams = {
transactionID,
waypoints: JSON.stringify(sanitizeRecentWaypoints(waypoints)),
};

API.read(isDraft ? READ_COMMANDS.GET_ROUTE_FOR_DRAFT : READ_COMMANDS.GET_ROUTE, parameters, getOnyxDataForRouteRequest(transactionID, isDraft));
}
let command;
switch (routeType) {
case CONST.TRANSACTION.STATE.DRAFT:
command = READ_COMMANDS.GET_ROUTE_FOR_DRAFT;
break;
case CONST.TRANSACTION.STATE.CURRENT:
command = READ_COMMANDS.GET_ROUTE;
break;
case CONST.TRANSACTION.STATE.BACKUP:
command = READ_COMMANDS.GET_ROUTE_FOR_BACKUP;
break;
default:
throw new Error('Invalid route type');
}

API.read(command, parameters, getOnyxDataForRouteRequest(transactionID, routeType));
}
/**
* Updates all waypoints stored in the transaction specified by the provided transactionID.
*
Expand Down
2 changes: 1 addition & 1 deletion src/pages/iou/request/step/IOURequestStepConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ function IOURequestStepConfirmation({
const isPolicyExpenseChat = useMemo(() => participants?.some((participant) => participant.isPolicyExpenseChat), [participants]);
const formHasBeenSubmitted = useRef(false);

useFetchRoute(transaction, transaction?.comment?.waypoints, action);
useFetchRoute(transaction, transaction?.comment?.waypoints, action, IOUUtils.shouldUseTransactionDraft(action) ? CONST.TRANSACTION.STATE.DRAFT : CONST.TRANSACTION.STATE.CURRENT);

useEffect(() => {
const policyExpenseChat = participants?.find((participant) => participant.isPolicyExpenseChat);
Expand Down
20 changes: 19 additions & 1 deletion src/pages/iou/request/step/IOURequestStepDistance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import useNetwork from '@hooks/useNetwork';
import usePolicy from '@hooks/usePolicy';
import usePrevious from '@hooks/usePrevious';
import useThemeStyles from '@hooks/useThemeStyles';
import * as Report from '@libs/actions/Report';
import DistanceRequestUtils from '@libs/DistanceRequestUtils';
import type {MileageRate} from '@libs/DistanceRequestUtils';
import * as ErrorUtils from '@libs/ErrorUtils';
Expand Down Expand Up @@ -78,7 +79,18 @@ function IOURequestStepDistance({
},
[optimisticWaypoints, transaction],
);
const {shouldFetchRoute, validatedWaypoints} = useFetchRoute(transaction, waypoints, action);

const backupWaypoints = transactionBackup?.pendingFields?.waypoints ? transactionBackup?.comment?.waypoints : undefined;
// When online, fetch the backup route to ensure the map is populated even if the user does not save the transaction.
// Fetch the backup route first to ensure the backup transaction map is updated before the main transaction map.
// This prevents a scenario where the main map loads, the user dismisses the map editor, and the backup map has not yet loaded due to delay.
useFetchRoute(transactionBackup, backupWaypoints, action, CONST.TRANSACTION.STATE.BACKUP);
const {shouldFetchRoute, validatedWaypoints} = useFetchRoute(
transaction,
waypoints,
action,
IOUUtils.shouldUseTransactionDraft(action) ? CONST.TRANSACTION.STATE.DRAFT : CONST.TRANSACTION.STATE.CURRENT,
);
const waypointsList = Object.keys(waypoints);
const previousWaypoints = usePrevious(waypoints);
const numberOfWaypoints = Object.keys(waypoints).length;
Expand Down Expand Up @@ -213,6 +225,12 @@ function IOURequestStepDistance({
return;
}
TransactionEdit.restoreOriginalTransactionFromBackup(transaction?.transactionID ?? '-1', IOUUtils.shouldUseTransactionDraft(action));

// If the user opens IOURequestStepDistance in offline mode and then goes online, re-open the report to fill in missing fields from the transaction backup
if (!transaction?.reportID) {
return;
}
Report.openReport(transaction?.reportID);
};
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, []);
Expand Down
6 changes: 6 additions & 0 deletions src/types/utils/TransactionStateType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type {ValueOf} from 'type-fest';
import type CONST from '@src/CONST';

type TransactionStateType = ValueOf<typeof CONST.TRANSACTION.STATE>;

export default TransactionStateType;

0 comments on commit d032ddf

Please sign in to comment.