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

Fixed currency selection on confirm step #34075

Merged
merged 9 commits into from
Feb 5, 2024
23 changes: 21 additions & 2 deletions src/libs/actions/IOU.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,13 @@ function clearMoneyRequest(transactionID) {
* @param {String} transactionID
* @param {Number} amount
* @param {String} currency
* @param {Boolean} [removeOriginalCurrency]
*/
function setMoneyRequestAmount_temporaryForRefactor(transactionID, amount, currency) {
function setMoneyRequestAmount_temporaryForRefactor(transactionID, amount, currency, removeOriginalCurrency = false) {
if (removeOriginalCurrency) {
Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {amount, currency, originalCurrency: null});
return;
}
Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {amount, currency});
}

Expand All @@ -193,11 +198,24 @@ function setMoneyRequestCreated_temporaryForRefactor(transactionID, created) {
/**
* @param {String} transactionID
* @param {String} currency
* @param {Boolean} [removeOriginalCurrency]
*/
function setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency) {
function setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency, removeOriginalCurrency = false) {
if (removeOriginalCurrency) {
Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {currency, originalCurrency: null});
return;
}
Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {currency});
}

/**
* @param {String} transactionID
* @param {String} originalCurrency
*/
function setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, originalCurrency) {
Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {originalCurrency});
}

/**
* @param {String} transactionID
* @param {String} comment
Expand Down Expand Up @@ -3669,6 +3687,7 @@ export {
setMoneyRequestCategory_temporaryForRefactor,
setMoneyRequestCreated_temporaryForRefactor,
setMoneyRequestCurrency_temporaryForRefactor,
setMoneyRequestOriginalCurrency_temporaryForRefactor,
setMoneyRequestDescription_temporaryForRefactor,
setMoneyRequestMerchant_temporaryForRefactor,
setMoneyRequestParticipants_temporaryForRefactor,
Expand Down
28 changes: 23 additions & 5 deletions src/pages/iou/request/step/IOURequestStepAmount.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useFocusEffect} from '@react-navigation/native';
import PropTypes from 'prop-types';
import React, {useCallback, useRef} from 'react';
import React, {useCallback, useEffect, useRef} from 'react';
import {withOnyx} from 'react-native-onyx';
import taxPropTypes from '@components/taxPropTypes';
import transactionPropTypes from '@components/transactionPropTypes';
Expand Down Expand Up @@ -59,18 +59,19 @@ const getTaxAmount = (transaction, defaultTaxValue, amount) => {
function IOURequestStepAmount({
report,
route: {
params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency},
params: {iouType, reportID, transactionID, backTo},
},
transaction,
transaction: {currency: originalCurrency},
transaction: {currency},
policyTaxRates,
policy,
}) {
const {translate} = useLocalize();
const textInput = useRef(null);
const focusTimeoutRef = useRef(null);
const isSaveButtonPressed = useRef(false);
const originalCurrency = useRef(null);
const iouRequestType = getRequestType(transaction);
const currency = selectedCurrency || originalCurrency;

const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report));
const isTaxTrackingEnabled = isPolicyExpenseChat && policy.isTaxTrackingEnabled;
Expand All @@ -87,6 +88,22 @@ function IOURequestStepAmount({
}, []),
);

useEffect(() => {
if (transaction.originalCurrency) {
originalCurrency.current = transaction.originalCurrency;
} else {
originalCurrency.current = currency;
IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currency);
}
return () => {
if (isSaveButtonPressed.current) {
return;
}
IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, originalCurrency.current, true);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking is it possible if we don't need an additional useRef and modify current IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor to pass a 3rd params like current implementation.

How about?

useEffect(() => {
    IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currency);
    return () => {
        if (isSaveButtonPressed.current) {
            return;
        }
        IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, transaction.originalCurrency);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, it was creating some problems as it was keeping old values in mount / unmount.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we can always reset originalCurrency on unmount.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let keep it this way as it helps me with cases of refresh (it will call mount again, and will set wrong value on refresh).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @shubham1206agra I will try to test & complete review checklist tomorrow


const navigateBack = () => {
Navigation.goBack(backTo || ROUTES.HOME);
};
Expand All @@ -99,6 +116,7 @@ function IOURequestStepAmount({
* @param {Number} amount
*/
const navigateToNextPage = ({amount}) => {
isSaveButtonPressed.current = true;
const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount));

if ((iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL || backTo) && isTaxTrackingEnabled) {
Expand All @@ -107,7 +125,7 @@ function IOURequestStepAmount({
IOU.setMoneyRequestTaxAmount(transaction.transactionID, taxAmountInSmallestCurrencyUnits);
}

IOU.setMoneyRequestAmount_temporaryForRefactor(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD);
IOU.setMoneyRequestAmount_temporaryForRefactor(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD, true);

if (backTo) {
Navigation.goBack(backTo);
Expand Down
82 changes: 50 additions & 32 deletions src/pages/iou/request/step/IOURequestStepConfirmation.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import categoryPropTypes from '@components/categoryPropTypes';
import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
import MoneyRequestConfirmationList from '@components/MoneyTemporaryForRefactorRequestConfirmationList';
Expand Down Expand Up @@ -103,6 +104,15 @@ function IOURequestStepConfirmation({
);
const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)), [report]);

useEffect(() => {
if (!transaction || !transaction.originalCurrency) {
return;
}
// If user somehow lands on this page without the currency reset, then reset it here.
IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, transaction.originalCurrency, true);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
const policyExpenseChat = _.find(participants, (participant) => participant.isPolicyExpenseChat);
if (policyExpenseChat) {
Expand Down Expand Up @@ -318,6 +328,10 @@ function IOURequestStepConfirmation({
IOU.setMoneyRequestBillable_temporaryForRefactor(transactionID, billable);
};

// This loading indicator is shown because the transaction originalCurrency is being updated later than the component mounts.
// To prevent the component from rendering with the wrong currency, we show a loading indicator until the correct currency is set.
const isLoading = !!(transaction && transaction.originalCurrency);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to leave comment here to explain why we have this code

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shubham1206agra could you help to leave a comment here, other than that it looks good to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks mate


return (
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
Expand All @@ -339,38 +353,42 @@ function IOURequestStepConfirmation({
},
]}
/>
<MoneyRequestConfirmationList
transaction={transaction}
hasMultipleParticipants={iouType === CONST.IOU.TYPE.SPLIT}
selectedParticipants={participants}
iouAmount={transaction.amount}
iouComment={lodashGet(transaction, 'comment.comment', '')}
iouCurrencyCode={transaction.currency}
iouIsBillable={transaction.billable}
onToggleBillable={setBillable}
iouCategory={transaction.category}
iouTag={transaction.tag}
onConfirm={createTransaction}
onSendMoney={sendMoney}
onSelectParticipant={addNewParticipant}
receiptPath={receiptPath}
receiptFilename={receiptFilename}
iouType={iouType}
reportID={reportID}
isPolicyExpenseChat={isPolicyExpenseChat}
// The participants can only be modified when the action is initiated from directly within a group chat and not the floating-action-button.
// This is because when there is a group of people, say they are on a trip, and you have some shared expenses with some of the people,
// but not all of them (maybe someone skipped out on dinner). Then it's nice to be able to select/deselect people from the group chat bill
// split rather than forcing the user to create a new group, just for that expense. The reportID is empty, when the action was initiated from
// the floating-action-button (since it is something that exists outside the context of a report).
canModifyParticipants={!transaction.isFromGlobalCreate}
policyID={report.policyID}
bankAccountRoute={ReportUtils.getBankAccountRoute(report)}
iouMerchant={transaction.merchant}
iouCreated={transaction.created}
isDistanceRequest={requestType === CONST.IOU.REQUEST_TYPE.DISTANCE}
shouldShowSmartScanFields={_.isEmpty(lodashGet(transaction, 'receipt.source', ''))}
/>
{isLoading ? (
<FullScreenLoadingIndicator />
) : (
<MoneyRequestConfirmationList
transaction={transaction}
hasMultipleParticipants={iouType === CONST.IOU.TYPE.SPLIT}
selectedParticipants={participants}
iouAmount={transaction.amount}
iouComment={lodashGet(transaction, 'comment.comment', '')}
iouCurrencyCode={transaction.currency}
iouIsBillable={transaction.billable}
onToggleBillable={setBillable}
iouCategory={transaction.category}
iouTag={transaction.tag}
onConfirm={createTransaction}
onSendMoney={sendMoney}
onSelectParticipant={addNewParticipant}
receiptPath={receiptPath}
receiptFilename={receiptFilename}
iouType={iouType}
reportID={reportID}
isPolicyExpenseChat={isPolicyExpenseChat}
// The participants can only be modified when the action is initiated from directly within a group chat and not the floating-action-button.
// This is because when there is a group of people, say they are on a trip, and you have some shared expenses with some of the people,
// but not all of them (maybe someone skipped out on dinner). Then it's nice to be able to select/deselect people from the group chat bill
// split rather than forcing the user to create a new group, just for that expense. The reportID is empty, when the action was initiated from
// the floating-action-button (since it is something that exists outside the context of a report).
canModifyParticipants={!transaction.isFromGlobalCreate}
policyID={report.policyID}
bankAccountRoute={ReportUtils.getBankAccountRoute(report)}
iouMerchant={transaction.merchant}
iouCreated={transaction.created}
isDistanceRequest={requestType === CONST.IOU.REQUEST_TYPE.DISTANCE}
shouldShowSmartScanFields={_.isEmpty(lodashGet(transaction, 'receipt.source', ''))}
/>
)}
</View>
)}
</ScreenWrapper>
Expand Down
14 changes: 4 additions & 10 deletions src/pages/iou/request/step/IOURequestStepCurrency.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,14 @@ function IOURequestStepCurrency({
const [searchValue, setSearchValue] = useState('');
const optionsSelectorRef = useRef();

const navigateBack = (selectedCurrency = undefined) => {
const navigateBack = () => {
// If the currency selection was done from the confirmation step (eg. + > request money > manual > confirm > amount > currency)
// then the user needs taken back to the confirmation page instead of the initial amount page. This is because the route params
// are only able to handle one backTo param at a time and the user needs to go back to the amount page before going back
// to the confirmation page
if (pageIndex === 'confirm') {
const routeToAmountPageWithConfirmationAsBackTo = getUrlWithBackToParam(backTo, `/${ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, transactionID, reportID)}`);
if (selectedCurrency) {
Navigation.navigate(`${routeToAmountPageWithConfirmationAsBackTo}&currency=${selectedCurrency}`);
} else {
Navigation.goBack(routeToAmountPageWithConfirmationAsBackTo);
}
Navigation.goBack(routeToAmountPageWithConfirmationAsBackTo);
return;
}
Navigation.goBack(backTo || ROUTES.HOME);
Expand All @@ -82,10 +78,8 @@ function IOURequestStepCurrency({
*/
const confirmCurrencySelection = (option) => {
Keyboard.dismiss();
if (pageIndex !== 'confirm') {
IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, option.currencyCode);
}
navigateBack(option.currencyCode);
IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, option.currencyCode);
navigateBack();
};

const {sections, headerMessage, initiallyFocusedOptionKey} = useMemo(() => {
Expand Down
30 changes: 24 additions & 6 deletions src/pages/iou/request/step/IOURequestStepTaxAmountPage.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {useFocusEffect} from '@react-navigation/native';
import React, {useCallback, useRef} from 'react';
import React, {useCallback, useEffect, useRef} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
Expand Down Expand Up @@ -53,10 +53,10 @@ const getTaxAmount = (transaction, defaultTaxValue) => {

function IOURequestStepTaxAmountPage({
route: {
params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency},
params: {iouType, reportID, transactionID, backTo},
},
transaction,
transaction: {currency: originalCurrency},
transaction: {currency},
report,
policyTaxRates,
}) {
Expand All @@ -65,10 +65,27 @@ function IOURequestStepTaxAmountPage({
const textInput = useRef(null);
const isEditing = Navigation.getActiveRoute().includes('taxAmount');

const currency = selectedCurrency || originalCurrency;

const focusTimeoutRef = useRef(null);

const isSaveButtonPressed = useRef(false);
const originalCurrency = useRef(null);

useEffect(() => {
if (transaction.originalCurrency) {
originalCurrency.current = transaction.originalCurrency;
} else {
originalCurrency.current = currency;
IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currency);
}
return () => {
if (isSaveButtonPressed.current) {
return;
}
IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, originalCurrency.current, true);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useFocusEffect(
useCallback(() => {
focusTimeoutRef.current = setTimeout(() => textInput.current && textInput.current.focus(), CONST.ANIMATED_TRANSITION);
Expand All @@ -93,10 +110,11 @@ function IOURequestStepTaxAmountPage({
};

const updateTaxAmount = (currentAmount) => {
isSaveButtonPressed.current = true;
const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(currentAmount.amount));
IOU.setMoneyRequestTaxAmount(transactionID, amountInSmallestCurrencyUnits);

IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency || CONST.CURRENCY.USD);
IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency || CONST.CURRENCY.USD, true);

if (backTo) {
Navigation.goBack(backTo);
Expand Down
Loading