From 287aaed50bdbab15629acb4bcfcc9e4d713b6c86 Mon Sep 17 00:00:00 2001 From: someone-here Date: Mon, 25 Sep 2023 18:50:19 +0530 Subject: [PATCH 01/10] Limit decimals in select currencies --- src/libs/MoneyRequestUtils.ts | 17 ++++-- src/pages/iou/steps/MoneyRequestAmountForm.js | 53 ++++++++++++------- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/libs/MoneyRequestUtils.ts b/src/libs/MoneyRequestUtils.ts index b8a6a3da303f..fc02bdf2a4f6 100644 --- a/src/libs/MoneyRequestUtils.ts +++ b/src/libs/MoneyRequestUtils.ts @@ -15,6 +15,13 @@ function stripSpacesFromAmount(amount: string): string { return amount.replace(/\s+/g, ''); } +/** + * Strip decimals from the amount + */ +function stripDecimalsFromAmount(amount: string): string { + return amount.replace(/\.\d*/, ''); +} + /** * Adds a leading zero to the amount if user entered just the decimal separator * @@ -42,8 +49,12 @@ function calculateAmountLength(amount: string): number { /** * Check if amount is a decimal up to 3 digits */ -function validateAmount(amount: string): boolean { - const decimalNumberRegex = new RegExp(/^\d+(,\d+)*(\.\d{0,2})?$/, 'i'); +function validateAmount(amount: string, decimals: number): boolean { + const regexString = + decimals === 0 + ? `^\\d+(,\\d+)?$` // don't allow decimal point if decimals === 0 + : `\\d+(,\\d+)*(\\.\\d{0,${decimals}})?$`; // Allow the decimal point and the desired number of digits after the point + const decimalNumberRegex = new RegExp(regexString, 'i'); return amount === '' || (decimalNumberRegex.test(amount) && calculateAmountLength(amount) <= CONST.IOU.AMOUNT_MAX_LENGTH); } @@ -78,4 +89,4 @@ function isScanRequest(selectedTab: ValueOf): boolean { return selectedTab === CONST.TAB.SCAN; } -export {stripCommaFromAmount, stripSpacesFromAmount, addLeadingZero, validateAmount, replaceAllDigits, isDistanceRequest, isScanRequest}; +export {stripCommaFromAmount, stripDecimalsFromAmount, stripSpacesFromAmount, addLeadingZero, validateAmount, replaceAllDigits, isDistanceRequest, isScanRequest}; diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js index e08fd5bde881..126112d61080 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.js +++ b/src/pages/iou/steps/MoneyRequestAmountForm.js @@ -1,4 +1,4 @@ -import React, {useEffect, useState, useCallback, useRef} from 'react'; +import React, {useEffect, useState, useCallback, useRef, useMemo} from 'react'; import {ScrollView, View} from 'react-native'; import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; @@ -70,6 +70,7 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu const textInput = useRef(null); + const decimals = useMemo(() => CurrencyUtils.getCurrencyDecimals(currency), [currency]); const selectedAmountAsString = amount ? CurrencyUtils.convertToFrontendAmount(amount).toString() : ''; const [currentAmount, setCurrentAmount] = useState(selectedAmountAsString); @@ -123,26 +124,38 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu * Sets the selection and the amount accordingly to the value passed to the input * @param {String} newAmount - Changed amount from user input */ - const setNewAmount = (newAmount) => { - // Remove spaces from the newAmount value because Safari on iOS adds spaces when pasting a copied value - // More info: https://github.com/Expensify/App/issues/16974 - const newAmountWithoutSpaces = MoneyRequestUtils.stripSpacesFromAmount(newAmount); - // Use a shallow copy of selection to trigger setSelection - // More info: https://github.com/Expensify/App/issues/16385 - if (!MoneyRequestUtils.validateAmount(newAmountWithoutSpaces)) { - setSelection((prevSelection) => ({...prevSelection})); + const setNewAmount = useCallback( + (newAmount) => { + // Remove spaces from the newAmount value because Safari on iOS adds spaces when pasting a copied value + // More info: https://github.com/Expensify/App/issues/16974 + const newAmountWithoutSpaces = MoneyRequestUtils.stripSpacesFromAmount(newAmount); + // Use a shallow copy of selection to trigger setSelection + // More info: https://github.com/Expensify/App/issues/16385 + if (!MoneyRequestUtils.validateAmount(newAmountWithoutSpaces, decimals)) { + setSelection((prevSelection) => ({...prevSelection})); + return; + } + const checkInvalidAmount = isAmountValid(newAmountWithoutSpaces); + setIsInvalidAmount(checkInvalidAmount); + setFormError(checkInvalidAmount ? 'iou.error.invalidAmount' : ''); + setCurrentAmount((prevAmount) => { + const strippedAmount = MoneyRequestUtils.stripCommaFromAmount(newAmountWithoutSpaces); + const isForwardDelete = prevAmount.length > strippedAmount.length && forwardDeletePressedRef.current; + setSelection((prevSelection) => getNewSelection(prevSelection, isForwardDelete ? strippedAmount.length : prevAmount.length, strippedAmount.length)); + return strippedAmount; + }); + }, + [decimals, forwardDeletePressedRef], + ); + + useEffect(() => { + if (MoneyRequestUtils.validateAmount(currentAmount, decimals)) { return; } - const checkInvalidAmount = isAmountValid(newAmountWithoutSpaces); - setIsInvalidAmount(checkInvalidAmount); - setFormError(checkInvalidAmount ? 'iou.error.invalidAmount' : ''); - setCurrentAmount((prevAmount) => { - const strippedAmount = MoneyRequestUtils.stripCommaFromAmount(newAmountWithoutSpaces); - const isForwardDelete = prevAmount.length > strippedAmount.length && forwardDeletePressedRef.current; - setSelection((prevSelection) => getNewSelection(prevSelection, isForwardDelete ? strippedAmount.length : prevAmount.length, strippedAmount.length)); - return strippedAmount; - }); - }; + setNewAmount(MoneyRequestUtils.stripDecimalsFromAmount(currentAmount)); + // we want to check validation only when the currency changes + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [currency]); /** * Update amount with number or Backspace pressed for BigNumberPad. @@ -167,7 +180,7 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu const newAmount = MoneyRequestUtils.addLeadingZero(`${currentAmount.substring(0, selection.start)}${key}${currentAmount.substring(selection.end)}`); setNewAmount(newAmount); }, - [currentAmount, selection, shouldUpdateSelection], + [currentAmount, selection, shouldUpdateSelection, setNewAmount], ); /** From d536cd16e8492f8198b057ab0669fa581d03e1aa Mon Sep 17 00:00:00 2001 From: Esh Tanya Gupta <77237602+esh-g@users.noreply.github.com> Date: Thu, 28 Sep 2023 19:20:14 +0530 Subject: [PATCH 02/10] Update src/libs/MoneyRequestUtils.ts Co-authored-by: Rajat Parashar --- src/libs/MoneyRequestUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/MoneyRequestUtils.ts b/src/libs/MoneyRequestUtils.ts index fc02bdf2a4f6..cbb14dec98fa 100644 --- a/src/libs/MoneyRequestUtils.ts +++ b/src/libs/MoneyRequestUtils.ts @@ -19,7 +19,7 @@ function stripSpacesFromAmount(amount: string): string { * Strip decimals from the amount */ function stripDecimalsFromAmount(amount: string): string { - return amount.replace(/\.\d*/, ''); + return amount.replace(/\.\d*$/, ''); } /** From ce34d60f270d7595c1ec7225f912dbfba762b21f Mon Sep 17 00:00:00 2001 From: Esh Tanya Gupta <77237602+esh-g@users.noreply.github.com> Date: Thu, 28 Sep 2023 19:21:46 +0530 Subject: [PATCH 03/10] Update src/pages/iou/steps/MoneyRequestAmountForm.js Co-authored-by: Rajat Parashar --- src/pages/iou/steps/MoneyRequestAmountForm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js index 126112d61080..e91ad03598f1 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.js +++ b/src/pages/iou/steps/MoneyRequestAmountForm.js @@ -145,7 +145,7 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu return strippedAmount; }); }, - [decimals, forwardDeletePressedRef], + [decimals], ); useEffect(() => { From 930fa1a72fd77c8ce42e02dd9507eb2e1cbcf602 Mon Sep 17 00:00:00 2001 From: Esh Tanya Gupta <77237602+esh-g@users.noreply.github.com> Date: Thu, 28 Sep 2023 19:22:31 +0530 Subject: [PATCH 04/10] Update src/libs/MoneyRequestUtils.ts Co-authored-by: Rajat Parashar --- src/libs/MoneyRequestUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/MoneyRequestUtils.ts b/src/libs/MoneyRequestUtils.ts index cbb14dec98fa..1819ec45c3c9 100644 --- a/src/libs/MoneyRequestUtils.ts +++ b/src/libs/MoneyRequestUtils.ts @@ -52,7 +52,7 @@ function calculateAmountLength(amount: string): number { function validateAmount(amount: string, decimals: number): boolean { const regexString = decimals === 0 - ? `^\\d+(,\\d+)?$` // don't allow decimal point if decimals === 0 + ? `^\\d+(,\\d+)?$` // Don't allow decimal point if decimals === 0 : `\\d+(,\\d+)*(\\.\\d{0,${decimals}})?$`; // Allow the decimal point and the desired number of digits after the point const decimalNumberRegex = new RegExp(regexString, 'i'); return amount === '' || (decimalNumberRegex.test(amount) && calculateAmountLength(amount) <= CONST.IOU.AMOUNT_MAX_LENGTH); From 8db37e2b1ab44409c9f2c22a0b4f173d47ca2021 Mon Sep 17 00:00:00 2001 From: someone-here Date: Thu, 28 Sep 2023 19:56:13 +0530 Subject: [PATCH 05/10] Fixed regex to not include trailing 0s --- src/libs/MoneyRequestUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/MoneyRequestUtils.ts b/src/libs/MoneyRequestUtils.ts index 1819ec45c3c9..4259278746c4 100644 --- a/src/libs/MoneyRequestUtils.ts +++ b/src/libs/MoneyRequestUtils.ts @@ -53,7 +53,7 @@ function validateAmount(amount: string, decimals: number): boolean { const regexString = decimals === 0 ? `^\\d+(,\\d+)?$` // Don't allow decimal point if decimals === 0 - : `\\d+(,\\d+)*(\\.\\d{0,${decimals}})?$`; // Allow the decimal point and the desired number of digits after the point + : `^\\d+(,\\d+)*(\\.\\d{0,${decimals}})?$`; // Allow the decimal point and the desired number of digits after the point const decimalNumberRegex = new RegExp(regexString, 'i'); return amount === '' || (decimalNumberRegex.test(amount) && calculateAmountLength(amount) <= CONST.IOU.AMOUNT_MAX_LENGTH); } From a0b02f3d5488ed145586c5839288abe5d624ec53 Mon Sep 17 00:00:00 2001 From: Esh Tanya Gupta <77237602+esh-g@users.noreply.github.com> Date: Thu, 28 Sep 2023 20:09:49 +0530 Subject: [PATCH 06/10] Update src/libs/MoneyRequestUtils.ts Co-authored-by: Rajat Parashar --- src/libs/MoneyRequestUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/MoneyRequestUtils.ts b/src/libs/MoneyRequestUtils.ts index 4259278746c4..c41fe73947d1 100644 --- a/src/libs/MoneyRequestUtils.ts +++ b/src/libs/MoneyRequestUtils.ts @@ -52,7 +52,7 @@ function calculateAmountLength(amount: string): number { function validateAmount(amount: string, decimals: number): boolean { const regexString = decimals === 0 - ? `^\\d+(,\\d+)?$` // Don't allow decimal point if decimals === 0 + ? `^\\d+(,\\d+)*$` // Don't allow decimal point if decimals === 0 : `^\\d+(,\\d+)*(\\.\\d{0,${decimals}})?$`; // Allow the decimal point and the desired number of digits after the point const decimalNumberRegex = new RegExp(regexString, 'i'); return amount === '' || (decimalNumberRegex.test(amount) && calculateAmountLength(amount) <= CONST.IOU.AMOUNT_MAX_LENGTH); From 63bc59133b213aeaf9ef966f447cfb2a6f2d812b Mon Sep 17 00:00:00 2001 From: someone-here Date: Thu, 28 Sep 2023 20:17:10 +0530 Subject: [PATCH 07/10] Added comments --- src/pages/iou/steps/MoneyRequestAmountForm.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js index e91ad03598f1..19c74246a2c1 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.js +++ b/src/pages/iou/steps/MoneyRequestAmountForm.js @@ -148,11 +148,16 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu [decimals], ); + // Modifies the amount to match the decimals for changed currency. useEffect(() => { + // If the changed currency supports decimals, we can return if (MoneyRequestUtils.validateAmount(currentAmount, decimals)) { return; } + + // If the changed currency doesn't support decimals, we can strip the decimals setNewAmount(MoneyRequestUtils.stripDecimalsFromAmount(currentAmount)); + // we want to check validation only when the currency changes // eslint-disable-next-line react-hooks/exhaustive-deps }, [currency]); From ff1f08d1304ee180cfb6f35230f3430d4c6eb77e Mon Sep 17 00:00:00 2001 From: someone-here Date: Thu, 28 Sep 2023 20:30:08 +0530 Subject: [PATCH 08/10] Fixed lint --- src/pages/iou/steps/MoneyRequestAmountForm.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js index 19c74246a2c1..792e1a883a19 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.js +++ b/src/pages/iou/steps/MoneyRequestAmountForm.js @@ -154,10 +154,10 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu if (MoneyRequestUtils.validateAmount(currentAmount, decimals)) { return; } - + // If the changed currency doesn't support decimals, we can strip the decimals setNewAmount(MoneyRequestUtils.stripDecimalsFromAmount(currentAmount)); - + // we want to check validation only when the currency changes // eslint-disable-next-line react-hooks/exhaustive-deps }, [currency]); From 6ea45f7501f8ffbd2fd507a1056e2b6cb0d3411f Mon Sep 17 00:00:00 2001 From: someone-here Date: Fri, 29 Sep 2023 18:33:46 +0530 Subject: [PATCH 09/10] Removed useMemo --- src/pages/iou/steps/MoneyRequestAmountForm.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js index 792e1a883a19..91481be82211 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.js +++ b/src/pages/iou/steps/MoneyRequestAmountForm.js @@ -1,4 +1,4 @@ -import React, {useEffect, useState, useCallback, useRef, useMemo} from 'react'; +import React, {useEffect, useState, useCallback, useRef} from 'react'; import {ScrollView, View} from 'react-native'; import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; @@ -70,7 +70,7 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu const textInput = useRef(null); - const decimals = useMemo(() => CurrencyUtils.getCurrencyDecimals(currency), [currency]); + const decimals = CurrencyUtils.getCurrencyDecimals(currency); const selectedAmountAsString = amount ? CurrencyUtils.convertToFrontendAmount(amount).toString() : ''; const [currentAmount, setCurrentAmount] = useState(selectedAmountAsString); From 7e180d7a0506d6252dbe4bb78f303fd890246aae Mon Sep 17 00:00:00 2001 From: someone-here Date: Mon, 2 Oct 2023 17:27:17 +0530 Subject: [PATCH 10/10] Fixed dependency array for decimals --- src/pages/iou/steps/MoneyRequestAmountForm.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js index 91481be82211..5079cb9a3d82 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.js +++ b/src/pages/iou/steps/MoneyRequestAmountForm.js @@ -158,9 +158,9 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu // If the changed currency doesn't support decimals, we can strip the decimals setNewAmount(MoneyRequestUtils.stripDecimalsFromAmount(currentAmount)); - // we want to check validation only when the currency changes + // we want to update only when decimals change (setNewAmount also changes when decimals change). // eslint-disable-next-line react-hooks/exhaustive-deps - }, [currency]); + }, [setNewAmount]); /** * Update amount with number or Backspace pressed for BigNumberPad.