diff --git a/src/libs/actions/TransactionEdit.ts b/src/libs/actions/TransactionEdit.ts index b1710aa72cbb..c980106fb036 100644 --- a/src/libs/actions/TransactionEdit.ts +++ b/src/libs/actions/TransactionEdit.ts @@ -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) { - 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) { + Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, backupTransaction); } -export {createBackupTransaction, removeBackupTransaction, restoreOriginalTransactionFromBackup}; +export default {restoreOriginalTransactionFromBackup}; diff --git a/src/pages/iou/request/step/IOURequestStepDistance.js b/src/pages/iou/request/step/IOURequestStepDistance.js index dad610cbc636..1fa4a87ea3a4 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.js +++ b/src/pages/iou/request/step/IOURequestStepDistance.js @@ -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'; @@ -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'; @@ -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({ @@ -57,7 +52,6 @@ function IOURequestStepDistance({ params: {action, iouType, reportID, transactionID, backTo}, }, transaction, - transactionBackup, }) { const styles = useThemeStyles(); const {isOffline} = useNetwork(); @@ -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(); @@ -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({}); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const navigateBack = () => { Navigation.goBack(backTo); }; @@ -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 @@ -213,6 +237,7 @@ function IOURequestStepDistance({ hasRouteError, isLoadingRoute, isLoading, + isCreatingNewRequest, isEditing, navigateToNextStep, transactionBackup, @@ -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);