Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix regression #38911

Closed
wants to merge 5 commits into from
Closed
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
38 changes: 3 additions & 35 deletions src/libs/actions/TransactionEdit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,8 @@ import type {OnyxEntry} from 'react-native-onyx';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Transaction} from '@src/types/onyx';

/**
* Makes a backup copy of a transaction object that can be restored when the user cancels editing a transaction.
*/
function createBackupTransaction(transaction: OnyxEntry<Transaction>) {
if (!transaction) {
return;
}

const newTransaction = {
...transaction,
};

// Use set so that it will always fully overwrite any backup transaction that could have existed before
Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transaction.transactionID}`, newTransaction);
}

/**
* Removes a transaction from Onyx that was only used temporary in the edit flow
*/
function removeBackupTransaction(transactionID: string) {
Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, null);
}

function restoreOriginalTransactionFromBackup(transactionID: string) {
const connectionID = Onyx.connect({
key: `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`,
callback: (backupTransaction) => {
Onyx.disconnect(connectionID);

// Use set to completely overwrite the original transaction
Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, backupTransaction);
removeBackupTransaction(transactionID);
},
});
function restoreOriginalTransactionFromBackup(transactionID: string, backupTransaction: OnyxEntry<Transaction>) {
Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, backupTransaction);
}

export {createBackupTransaction, removeBackupTransaction, restoreOriginalTransactionFromBackup};
export default {restoreOriginalTransactionFromBackup};
51 changes: 34 additions & 17 deletions src/pages/iou/request/step/IOURequestStepDistance.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import lodashGet from 'lodash/get';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import Button from '@components/Button';
import DistanceRequestFooter from '@components/DistanceRequest/DistanceRequestFooter';
Expand All @@ -22,8 +21,8 @@ import variables from '@styles/variables';
import * as IOU from '@userActions/IOU';
import * as MapboxToken from '@userActions/MapboxToken';
import * as Transaction from '@userActions/Transaction';
import TransactionEdit from '@userActions/TransactionEdit';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes';
import StepScreenWrapper from './StepScreenWrapper';
Expand All @@ -40,15 +39,11 @@ const propTypes = {

/** The transaction object being modified in Onyx */
transaction: transactionPropTypes,

/** backup version of the original transaction */
transactionBackup: transactionPropTypes,
};

const defaultProps = {
report: {},
transaction: {},
transactionBackup: {},
};

function IOURequestStepDistance({
Expand All @@ -57,7 +52,6 @@ function IOURequestStepDistance({
params: {action, iouType, reportID, transactionID, backTo},
},
transaction,
transactionBackup,
}) {
const styles = useThemeStyles();
const {isOffline} = useNetwork();
Expand Down Expand Up @@ -85,6 +79,8 @@ function IOURequestStepDistance({
const atLeastTwoDifferentWaypointsError = useMemo(() => _.size(validatedWaypoints) < 2, [validatedWaypoints]);
const isEditing = action === CONST.IOU.ACTION.EDIT;
const isCreatingNewRequest = Navigation.getActiveRoute().includes('start');
const transactionWasSaved = useRef(false);
const [transactionBackup, setTransactionBackup] = useState({});

useEffect(() => {
MapboxToken.init();
Expand Down Expand Up @@ -112,6 +108,30 @@ function IOURequestStepDistance({
setShouldShowAtLeastTwoDifferentWaypointsError(false);
}, [atLeastTwoDifferentWaypointsError, duplicateWaypointsError, hasRouteError, isLoading, isLoadingRoute, nonEmptyWaypointsCount, transaction]);

useEffect(() => {
if (isCreatingNewRequest) {
return () => {};
}
// This effect runs when the component is mounted and unmounted. It's purpose is to be able to properly
// discard changes if the user cancels out of making any changes. This is accomplished by backing up the
// original transaction, letting the user modify the current transaction, and then if the user ever
// cancels out of the modal without saving changes, the original transaction is restored from the backup.

// On mount, create the backup transaction.
setTransactionBackup(transaction);

return () => {
// If the user cancels out of the modal without without saving changes, then the original transaction
// needs to be restored from the backup so that all changes are removed.
if (transactionWasSaved.current || _.isEqual(transaction, transactionBackup)) {
return;
}
TransactionEdit.restoreOriginalTransactionFromBackup(lodashGet(transaction, 'transactionID', ''), transactionBackup);
setTransactionBackup({});
DylanDylann marked this conversation as resolved.
Show resolved Hide resolved
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const navigateBack = () => {
Navigation.goBack(backTo);
};
Expand Down Expand Up @@ -187,10 +207,14 @@ function IOURequestStepDistance({

const submitWaypoints = useCallback(() => {
// If there is any error or loading state, don't let user go to next page.
if (duplicateWaypointsError || atLeastTwoDifferentWaypointsError || hasRouteError || isLoadingRoute || isLoading) {
if (duplicateWaypointsError || atLeastTwoDifferentWaypointsError || hasRouteError || isLoadingRoute || (!isEditing && isLoading)) {
setShouldShowAtLeastTwoDifferentWaypointsError(true);
return;
}
if (!isCreatingNewRequest) {
transactionWasSaved.current = true;
}

if (isEditing) {
// If nothing was changed, simply go to transaction thread
// We compare only addresses because numbers are rounded while backup
Expand All @@ -213,6 +237,7 @@ function IOURequestStepDistance({
hasRouteError,
isLoadingRoute,
isLoading,
isCreatingNewRequest,
isEditing,
navigateToNextStep,
transactionBackup,
Expand Down Expand Up @@ -287,12 +312,4 @@ IOURequestStepDistance.displayName = 'IOURequestStepDistance';
IOURequestStepDistance.propTypes = propTypes;
IOURequestStepDistance.defaultProps = defaultProps;

export default compose(
withWritableReportOrNotFound,
withFullTransactionOrNotFound,
withOnyx({
transactionBackup: {
key: (props) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${props.transactionID}`,
},
}),
)(IOURequestStepDistance);
export default compose(withWritableReportOrNotFound, withFullTransactionOrNotFound)(IOURequestStepDistance);
Loading