From 7890252dd642fe19f073747c5e5db5c921a73b45 Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Wed, 23 Mar 2022 17:48:56 +0530 Subject: [PATCH 001/331] fix: localize currency symbol --- src/pages/iou/IOUCurrencySelection.js | 17 +++- src/pages/iou/steps/IOUAmountPage.js | 110 ++++++++++++++++++-------- 2 files changed, 93 insertions(+), 34 deletions(-) diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index 09ad555d5c04..b4005367a4a7 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -81,6 +81,7 @@ class IOUCurrencySelection extends Component { this.getSections = this.getSections.bind(this); this.confirmCurrencySelection = this.confirmCurrencySelection.bind(this); this.changeSearchValue = this.changeSearchValue.bind(this); + this.getlocalizedCurrencySymbol = this.getlocalizedCurrencySymbol.bind(this); } componentDidMount() { @@ -113,13 +114,27 @@ class IOUCurrencySelection extends Component { getCurrencyOptions() { const currencyListKeys = _.keys(this.props.currencyList); const currencyOptions = _.map(currencyListKeys, currencyCode => ({ - text: `${currencyCode} - ${this.props.currencyList[currencyCode].symbol}`, + text: `${currencyCode} - ${this.getlocalizedCurrencySymbol(currencyCode)}`, searchText: `${currencyCode} ${this.props.currencyList[currencyCode].symbol}`, currencyCode, })); return currencyOptions; } + /** + * Get localized currency symbol for SO4217 Code + * @param {String} currencyCode + * @return {String} + */ + getlocalizedCurrencySymbol(currencyCode) { + const parts = new Intl.NumberFormat(this.props.preferredLocale, { + style: 'currency', + currency: currencyCode, + }).formatToParts(0); + const currencySymbol = _.find(parts, part => part.type === 'currency').value; + return currencySymbol; + } + /** * Function which toggles a currency in the list * diff --git a/src/pages/iou/steps/IOUAmountPage.js b/src/pages/iou/steps/IOUAmountPage.js index 5ebccbbcb6ee..02870f54f915 100755 --- a/src/pages/iou/steps/IOUAmountPage.js +++ b/src/pages/iou/steps/IOUAmountPage.js @@ -31,18 +31,6 @@ const propTypes = { /** Callback to inform parent modal of success */ onStepComplete: PropTypes.func.isRequired, - /** The currency list constant object from Onyx */ - currencyList: PropTypes.objectOf(PropTypes.shape({ - /** Symbol for the currency */ - symbol: PropTypes.string, - - /** Name of the currency */ - name: PropTypes.string, - - /** ISO4217 Code for the currency */ - ISO4217: PropTypes.string, - })).isRequired, - /** Previously selected amount to show if the user comes back to this screen */ selectedAmount: PropTypes.string.isRequired, @@ -75,6 +63,8 @@ class IOUAmountPage extends React.Component { this.updateAmount = this.updateAmount.bind(this); this.stripCommaFromAmount = this.stripCommaFromAmount.bind(this); this.focusTextInput = this.focusTextInput.bind(this); + this.getlocalizedCurrencySymbol = this.getlocalizedCurrencySymbol.bind(this); + this.isCurrencySymbolToLeft = this.isCurrencySymbolToLeft.bind(this); this.state = { amount: props.selectedAmount, @@ -93,6 +83,37 @@ class IOUAmountPage extends React.Component { this.focusTextInput(); } + /** + * Get localized currency symbol for SO4217 Code + * @param {String} currencyCode + * @return {String} + */ + getlocalizedCurrencySymbol(currencyCode) { + const parts = new Intl.NumberFormat(this.props.preferredLocale, { + style: 'currency', + currency: currencyCode, + }).formatToParts(0); + const currencySymbol = _.find(parts, part => part.type === 'currency').value; + return currencySymbol; + } + + /** + * Is currency symbol to left + * @return {Boolean} + */ + isCurrencySymbolToLeft() { + const parts = new Intl.NumberFormat(this.props.preferredLocale, { + style: 'currency', + currency: this.props.iou.selectedCurrencyCode, + }).formatToParts(0); + + // The first element of parts will be type: currency for all currency + // Where it starts with symbol and the other will have it at last + // If it is not the first, it must be at last + const isLeft = parts[0].type === 'currency'; + return isLeft; + } + /** * Focus text input */ @@ -206,6 +227,7 @@ class IOUAmountPage extends React.Component { } render() { + const currencySymbol = this.getlocalizedCurrencySymbol(this.props.iou.selectedCurrencyCode); const formattedAmount = this.replaceAllDigits(this.state.amount, this.props.toLocaleDigit); return ( <> @@ -217,26 +239,49 @@ class IOUAmountPage extends React.Component { styles.justifyContentCenter, ]} > - Navigation.navigate(this.props.hasMultipleParticipants - ? ROUTES.getIouBillCurrencyRoute(this.props.reportID) - : ROUTES.getIouRequestCurrencyRoute(this.props.reportID))} - > - - {lodashGet(this.props.currencyList, [this.props.iou.selectedCurrencyCode, 'symbol'])} - - - this.textInput = el} - value={formattedAmount} - placeholder={this.props.numberFormat(0)} - keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD} - /> + {this.isCurrencySymbolToLeft() ? ( + <> + Navigation.navigate(this.props.hasMultipleParticipants + ? ROUTES.getIouBillCurrencyRoute(this.props.reportID) + : ROUTES.getIouRequestCurrencyRoute(this.props.reportID))} + > + {currencySymbol} + + this.textInput = el} + value={formattedAmount} + placeholder={this.props.numberFormat(0)} + keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD} + /> + + ) : ( + <> + this.textInput = el} + value={formattedAmount} + placeholder={this.props.numberFormat(0)} + keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD} + /> + Navigation.navigate(this.props.hasMultipleParticipants + ? ROUTES.getIouBillCurrencyRoute(this.props.reportID) + : ROUTES.getIouRequestCurrencyRoute(this.props.reportID))} + > + {currencySymbol} + + + )} {canUseTouchScreen() @@ -266,7 +311,6 @@ IOUAmountPage.defaultProps = defaultProps; export default compose( withLocalize, withOnyx({ - currencyList: {key: ONYXKEYS.CURRENCY_LIST}, iou: {key: ONYXKEYS.IOU}, }), )(IOUAmountPage); From 3fa9bc3fea5402670747186201cc0bfad2c37e69 Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Mon, 28 Mar 2022 11:31:55 +0530 Subject: [PATCH 002/331] refactor: move common function to hoc component --- src/components/withLocalize.js | 31 +++++++++++++++++++++++++++ src/pages/iou/IOUCurrencySelection.js | 17 +-------------- src/pages/iou/steps/IOUAmountPage.js | 29 +++++++------------------ 3 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/components/withLocalize.js b/src/components/withLocalize.js index 1fb7c9ca9dc8..5426cbcf1e56 100755 --- a/src/components/withLocalize.js +++ b/src/components/withLocalize.js @@ -1,6 +1,7 @@ import React, {createContext, forwardRef} from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; +import _ from 'underscore'; import getComponentDisplayName from '../libs/getComponentDisplayName'; import ONYXKEYS from '../ONYXKEYS'; import * as Localize from '../libs/Localize'; @@ -19,6 +20,9 @@ const withLocalizePropTypes = { /** Formats number formatted according to locale and options */ numberFormat: PropTypes.func.isRequired, + /** Formats number to parts according to locale and options */ + formatToParts: PropTypes.func.isRequired, + /** Converts a timestamp into a localized string representation that's relative to current moment in time */ timestampToRelative: PropTypes.func.isRequired, @@ -34,6 +38,9 @@ const withLocalizePropTypes = { /** Gets the standard digit corresponding to a locale digit */ fromLocaleDigit: PropTypes.func.isRequired, + /** Get localized currency symbol for SO4217 Code */ + toLocalizedCurrencySymbol: PropTypes.func.isRequired, + /** Gets the locale digit corresponding to a standard digit */ toLocaleDigit: PropTypes.func.isRequired, }; @@ -59,12 +66,14 @@ class LocaleContextProvider extends React.Component { return { translate: this.translate.bind(this), numberFormat: this.numberFormat.bind(this), + formatToParts: this.formatToParts.bind(this), timestampToRelative: this.timestampToRelative.bind(this), timestampToDateTime: this.timestampToDateTime.bind(this), fromLocalPhone: this.fromLocalPhone.bind(this), toLocalPhone: this.toLocalPhone.bind(this), fromLocaleDigit: this.fromLocaleDigit.bind(this), toLocaleDigit: this.toLocaleDigit.bind(this), + toLocalizedCurrencySymbol: this.toLocalizedCurrencySymbol.bind(this), preferredLocale: this.props.preferredLocale, }; } @@ -87,6 +96,15 @@ class LocaleContextProvider extends React.Component { return NumberFormatUtils.format(this.props.preferredLocale, number, options); } + /** + * @param {Number} number + * @param {Intl.NumberFormatOptions} options + * @returns {Array} + */ + formatToParts(number, options) { + return NumberFormatUtils.formatToParts(this.props.preferredLocale, number, options); + } + /** * @param {Number} timestamp * @returns {String} @@ -140,6 +158,19 @@ class LocaleContextProvider extends React.Component { return LocaleDigitUtils.fromLocaleDigit(this.props.preferredLocale, localeDigit); } + /** + * @param {String} currencyCode + * @return {String} + */ + toLocalizedCurrencySymbol(currencyCode) { + const parts = NumberFormatUtils.formatToParts(this.props.preferredLocale, 0, { + style: 'currency', + currency: currencyCode, + }); + const currencySymbol = _.find(parts, part => part.type === 'currency').value; + return currencySymbol; + } + render() { return ( diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index b4005367a4a7..65161f36ea62 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -81,7 +81,6 @@ class IOUCurrencySelection extends Component { this.getSections = this.getSections.bind(this); this.confirmCurrencySelection = this.confirmCurrencySelection.bind(this); this.changeSearchValue = this.changeSearchValue.bind(this); - this.getlocalizedCurrencySymbol = this.getlocalizedCurrencySymbol.bind(this); } componentDidMount() { @@ -114,27 +113,13 @@ class IOUCurrencySelection extends Component { getCurrencyOptions() { const currencyListKeys = _.keys(this.props.currencyList); const currencyOptions = _.map(currencyListKeys, currencyCode => ({ - text: `${currencyCode} - ${this.getlocalizedCurrencySymbol(currencyCode)}`, + text: `${currencyCode} - ${this.props.toLocalizedCurrencySymbol(currencyCode)}`, searchText: `${currencyCode} ${this.props.currencyList[currencyCode].symbol}`, currencyCode, })); return currencyOptions; } - /** - * Get localized currency symbol for SO4217 Code - * @param {String} currencyCode - * @return {String} - */ - getlocalizedCurrencySymbol(currencyCode) { - const parts = new Intl.NumberFormat(this.props.preferredLocale, { - style: 'currency', - currency: currencyCode, - }).formatToParts(0); - const currencySymbol = _.find(parts, part => part.type === 'currency').value; - return currencySymbol; - } - /** * Function which toggles a currency in the list * diff --git a/src/pages/iou/steps/IOUAmountPage.js b/src/pages/iou/steps/IOUAmountPage.js index 02870f54f915..00fc6079d5cc 100755 --- a/src/pages/iou/steps/IOUAmountPage.js +++ b/src/pages/iou/steps/IOUAmountPage.js @@ -63,7 +63,6 @@ class IOUAmountPage extends React.Component { this.updateAmount = this.updateAmount.bind(this); this.stripCommaFromAmount = this.stripCommaFromAmount.bind(this); this.focusTextInput = this.focusTextInput.bind(this); - this.getlocalizedCurrencySymbol = this.getlocalizedCurrencySymbol.bind(this); this.isCurrencySymbolToLeft = this.isCurrencySymbolToLeft.bind(this); this.state = { @@ -83,29 +82,16 @@ class IOUAmountPage extends React.Component { this.focusTextInput(); } - /** - * Get localized currency symbol for SO4217 Code - * @param {String} currencyCode - * @return {String} - */ - getlocalizedCurrencySymbol(currencyCode) { - const parts = new Intl.NumberFormat(this.props.preferredLocale, { - style: 'currency', - currency: currencyCode, - }).formatToParts(0); - const currencySymbol = _.find(parts, part => part.type === 'currency').value; - return currencySymbol; - } - /** * Is currency symbol to left + * @param {String} currencyCode * @return {Boolean} */ - isCurrencySymbolToLeft() { - const parts = new Intl.NumberFormat(this.props.preferredLocale, { + isCurrencySymbolToLeft(currencyCode) { + const parts = this.props.formatToParts(0, { style: 'currency', - currency: this.props.iou.selectedCurrencyCode, - }).formatToParts(0); + currency: currencyCode, + }); // The first element of parts will be type: currency for all currency // Where it starts with symbol and the other will have it at last @@ -227,8 +213,9 @@ class IOUAmountPage extends React.Component { } render() { - const currencySymbol = this.getlocalizedCurrencySymbol(this.props.iou.selectedCurrencyCode); + const currencySymbol = this.props.toLocalizedCurrencySymbol(this.props.iou.selectedCurrencyCode); const formattedAmount = this.replaceAllDigits(this.state.amount, this.props.toLocaleDigit); + const isCurrencySymbolToLeft = this.isCurrencySymbolToLeft(this.props.iou.selectedCurrencyCode); return ( <> - {this.isCurrencySymbolToLeft() ? ( + {isCurrencySymbolToLeft ? ( <> Navigation.navigate(this.props.hasMultipleParticipants ? ROUTES.getIouBillCurrencyRoute(this.props.reportID) From fad813cca80d6138efd372999a90ebc1bbd73763 Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Mon, 28 Mar 2022 15:43:48 +0530 Subject: [PATCH 003/331] refactor: rename variable names --- src/components/withLocalize.js | 30 +++++++++++++-------------- src/pages/iou/IOUCurrencySelection.js | 2 +- src/pages/iou/steps/IOUAmountPage.js | 10 ++++----- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/components/withLocalize.js b/src/components/withLocalize.js index 5426cbcf1e56..6d1175a2e4db 100755 --- a/src/components/withLocalize.js +++ b/src/components/withLocalize.js @@ -39,7 +39,7 @@ const withLocalizePropTypes = { fromLocaleDigit: PropTypes.func.isRequired, /** Get localized currency symbol for SO4217 Code */ - toLocalizedCurrencySymbol: PropTypes.func.isRequired, + getLocalizedCurrencySymbol: PropTypes.func.isRequired, /** Gets the locale digit corresponding to a standard digit */ toLocaleDigit: PropTypes.func.isRequired, @@ -73,11 +73,24 @@ class LocaleContextProvider extends React.Component { toLocalPhone: this.toLocalPhone.bind(this), fromLocaleDigit: this.fromLocaleDigit.bind(this), toLocaleDigit: this.toLocaleDigit.bind(this), - toLocalizedCurrencySymbol: this.toLocalizedCurrencySymbol.bind(this), + getLocalizedCurrencySymbol: this.getLocalizedCurrencySymbol.bind(this), preferredLocale: this.props.preferredLocale, }; } + /** + * @param {String} currencyCode + * @return {String} + */ + getLocalizedCurrencySymbol(currencyCode) { + const parts = NumberFormatUtils.formatToParts(this.props.preferredLocale, 0, { + style: 'currency', + currency: currencyCode, + }); + const currencySymbol = _.find(parts, part => part.type === 'currency').value; + return currencySymbol; + } + /** * @param {String} phrase * @param {Object} [variables] @@ -158,19 +171,6 @@ class LocaleContextProvider extends React.Component { return LocaleDigitUtils.fromLocaleDigit(this.props.preferredLocale, localeDigit); } - /** - * @param {String} currencyCode - * @return {String} - */ - toLocalizedCurrencySymbol(currencyCode) { - const parts = NumberFormatUtils.formatToParts(this.props.preferredLocale, 0, { - style: 'currency', - currency: currencyCode, - }); - const currencySymbol = _.find(parts, part => part.type === 'currency').value; - return currencySymbol; - } - render() { return ( diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index 65161f36ea62..fe103da3261b 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -113,7 +113,7 @@ class IOUCurrencySelection extends Component { getCurrencyOptions() { const currencyListKeys = _.keys(this.props.currencyList); const currencyOptions = _.map(currencyListKeys, currencyCode => ({ - text: `${currencyCode} - ${this.props.toLocalizedCurrencySymbol(currencyCode)}`, + text: `${currencyCode} - ${this.props.getLocalizedCurrencySymbol(currencyCode)}`, searchText: `${currencyCode} ${this.props.currencyList[currencyCode].symbol}`, currencyCode, })); diff --git a/src/pages/iou/steps/IOUAmountPage.js b/src/pages/iou/steps/IOUAmountPage.js index 00fc6079d5cc..1ae97c9718d0 100755 --- a/src/pages/iou/steps/IOUAmountPage.js +++ b/src/pages/iou/steps/IOUAmountPage.js @@ -63,7 +63,7 @@ class IOUAmountPage extends React.Component { this.updateAmount = this.updateAmount.bind(this); this.stripCommaFromAmount = this.stripCommaFromAmount.bind(this); this.focusTextInput = this.focusTextInput.bind(this); - this.isCurrencySymbolToLeft = this.isCurrencySymbolToLeft.bind(this); + this.isCurrencySymbolLTR = this.isCurrencySymbolLTR.bind(this); this.state = { amount: props.selectedAmount, @@ -87,7 +87,7 @@ class IOUAmountPage extends React.Component { * @param {String} currencyCode * @return {Boolean} */ - isCurrencySymbolToLeft(currencyCode) { + isCurrencySymbolLTR(currencyCode) { const parts = this.props.formatToParts(0, { style: 'currency', currency: currencyCode, @@ -213,9 +213,9 @@ class IOUAmountPage extends React.Component { } render() { - const currencySymbol = this.props.toLocalizedCurrencySymbol(this.props.iou.selectedCurrencyCode); + const currencySymbol = this.props.getLocalizedCurrencySymbol(this.props.iou.selectedCurrencyCode); const formattedAmount = this.replaceAllDigits(this.state.amount, this.props.toLocaleDigit); - const isCurrencySymbolToLeft = this.isCurrencySymbolToLeft(this.props.iou.selectedCurrencyCode); + const isCurrencySymbolLTR = this.isCurrencySymbolLTR(this.props.iou.selectedCurrencyCode); return ( <> - {isCurrencySymbolToLeft ? ( + {isCurrencySymbolLTR ? ( <> Navigation.navigate(this.props.hasMultipleParticipants ? ROUTES.getIouBillCurrencyRoute(this.props.reportID) From 0d56d96fab8d916f32ffd91403bee203a1092e27 Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Mon, 28 Mar 2022 15:54:13 +0530 Subject: [PATCH 004/331] refactor: ltr symbol component --- src/pages/iou/steps/IOUAmountPage.js | 69 +++++++++++----------------- 1 file changed, 26 insertions(+), 43 deletions(-) diff --git a/src/pages/iou/steps/IOUAmountPage.js b/src/pages/iou/steps/IOUAmountPage.js index 1ae97c9718d0..4aeec7931e37 100755 --- a/src/pages/iou/steps/IOUAmountPage.js +++ b/src/pages/iou/steps/IOUAmountPage.js @@ -216,6 +216,31 @@ class IOUAmountPage extends React.Component { const currencySymbol = this.props.getLocalizedCurrencySymbol(this.props.iou.selectedCurrencyCode); const formattedAmount = this.replaceAllDigits(this.state.amount, this.props.toLocaleDigit); const isCurrencySymbolLTR = this.isCurrencySymbolLTR(this.props.iou.selectedCurrencyCode); + + const currencySymbolElement = ( + Navigation.navigate(this.props.hasMultipleParticipants + ? ROUTES.getIouBillCurrencyRoute(this.props.reportID) + : ROUTES.getIouRequestCurrencyRoute(this.props.reportID))} + > + {currencySymbol} + + ); + + const amountTextInput = ( + this.textInput = el} + value={formattedAmount} + placeholder={this.props.numberFormat(0)} + keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD} + /> + ); + return ( <> - {isCurrencySymbolLTR ? ( - <> - Navigation.navigate(this.props.hasMultipleParticipants - ? ROUTES.getIouBillCurrencyRoute(this.props.reportID) - : ROUTES.getIouRequestCurrencyRoute(this.props.reportID))} - > - {currencySymbol} - - this.textInput = el} - value={formattedAmount} - placeholder={this.props.numberFormat(0)} - keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD} - /> - - ) : ( - <> - this.textInput = el} - value={formattedAmount} - placeholder={this.props.numberFormat(0)} - keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD} - /> - Navigation.navigate(this.props.hasMultipleParticipants - ? ROUTES.getIouBillCurrencyRoute(this.props.reportID) - : ROUTES.getIouRequestCurrencyRoute(this.props.reportID))} - > - {currencySymbol} - - - )} + {isCurrencySymbolLTR ? [currencySymbolElement, amountTextInput] : [amountTextInput, currencySymbolElement]} {canUseTouchScreen() From 1f5012287d6bcd579343f2e3847c58784d6dc29c Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Thu, 31 Mar 2022 11:12:17 +0530 Subject: [PATCH 005/331] feat: add currency symbol utils --- src/libs/CurrencySymbolUtils.js | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/libs/CurrencySymbolUtils.js diff --git a/src/libs/CurrencySymbolUtils.js b/src/libs/CurrencySymbolUtils.js new file mode 100644 index 000000000000..86d28dda2e18 --- /dev/null +++ b/src/libs/CurrencySymbolUtils.js @@ -0,0 +1,41 @@ +import _ from 'underscore'; +import * as NumberFormatUtils from './NumberFormatUtils'; + +/** + * Get localized currency symbol for SO4217 Code + * @param {String} preferredLocale + * @param {String} currencyCode + * @return {String} + */ +function getLocalizedCurrencySymbol(preferredLocale, currencyCode) { + const parts = NumberFormatUtils.formatToParts(preferredLocale, 0, { + style: 'currency', + currency: currencyCode, + }); + const currencySymbol = _.find(parts, part => part.type === 'currency').value; + return currencySymbol; +} + +/** + * Is currency symbol to left + * @param {String} preferredLocale + * @param {String} currencyCode + * @return {Boolean} + */ +function isCurrencySymbolLTR(preferredLocale, currencyCode) { + const parts = NumberFormatUtils.formatToParts(preferredLocale, 0, { + style: 'currency', + currency: currencyCode, + }); + + // The first element of parts will be type: currency for all currency + // Where it starts with symbol and the other will have it at last + // If it is not the first, it must be at last + const isLeft = parts[0].type === 'currency'; + return isLeft; +} + +export { + getLocalizedCurrencySymbol, + isCurrencySymbolLTR, +}; From df003d6955719390c603fa9a7de732c1debd726f Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Thu, 31 Mar 2022 11:16:36 +0530 Subject: [PATCH 006/331] refactor: currency utils functions --- src/components/withLocalize.js | 31 --------------------------- src/pages/iou/IOUCurrencySelection.js | 3 ++- src/pages/iou/steps/IOUAmountPage.js | 24 +++------------------ 3 files changed, 5 insertions(+), 53 deletions(-) diff --git a/src/components/withLocalize.js b/src/components/withLocalize.js index 6d1175a2e4db..1fb7c9ca9dc8 100755 --- a/src/components/withLocalize.js +++ b/src/components/withLocalize.js @@ -1,7 +1,6 @@ import React, {createContext, forwardRef} from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; import getComponentDisplayName from '../libs/getComponentDisplayName'; import ONYXKEYS from '../ONYXKEYS'; import * as Localize from '../libs/Localize'; @@ -20,9 +19,6 @@ const withLocalizePropTypes = { /** Formats number formatted according to locale and options */ numberFormat: PropTypes.func.isRequired, - /** Formats number to parts according to locale and options */ - formatToParts: PropTypes.func.isRequired, - /** Converts a timestamp into a localized string representation that's relative to current moment in time */ timestampToRelative: PropTypes.func.isRequired, @@ -38,9 +34,6 @@ const withLocalizePropTypes = { /** Gets the standard digit corresponding to a locale digit */ fromLocaleDigit: PropTypes.func.isRequired, - /** Get localized currency symbol for SO4217 Code */ - getLocalizedCurrencySymbol: PropTypes.func.isRequired, - /** Gets the locale digit corresponding to a standard digit */ toLocaleDigit: PropTypes.func.isRequired, }; @@ -66,31 +59,16 @@ class LocaleContextProvider extends React.Component { return { translate: this.translate.bind(this), numberFormat: this.numberFormat.bind(this), - formatToParts: this.formatToParts.bind(this), timestampToRelative: this.timestampToRelative.bind(this), timestampToDateTime: this.timestampToDateTime.bind(this), fromLocalPhone: this.fromLocalPhone.bind(this), toLocalPhone: this.toLocalPhone.bind(this), fromLocaleDigit: this.fromLocaleDigit.bind(this), toLocaleDigit: this.toLocaleDigit.bind(this), - getLocalizedCurrencySymbol: this.getLocalizedCurrencySymbol.bind(this), preferredLocale: this.props.preferredLocale, }; } - /** - * @param {String} currencyCode - * @return {String} - */ - getLocalizedCurrencySymbol(currencyCode) { - const parts = NumberFormatUtils.formatToParts(this.props.preferredLocale, 0, { - style: 'currency', - currency: currencyCode, - }); - const currencySymbol = _.find(parts, part => part.type === 'currency').value; - return currencySymbol; - } - /** * @param {String} phrase * @param {Object} [variables] @@ -109,15 +87,6 @@ class LocaleContextProvider extends React.Component { return NumberFormatUtils.format(this.props.preferredLocale, number, options); } - /** - * @param {Number} number - * @param {Intl.NumberFormatOptions} options - * @returns {Array} - */ - formatToParts(number, options) { - return NumberFormatUtils.formatToParts(this.props.preferredLocale, number, options); - } - /** * @param {Number} timestamp * @returns {String} diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index fe103da3261b..4b3d488c5c07 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -20,6 +20,7 @@ import KeyboardAvoidingView from '../../components/KeyboardAvoidingView'; import Button from '../../components/Button'; import FixedFooter from '../../components/FixedFooter'; import * as IOU from '../../libs/actions/IOU'; +import * as CurrencySymbolUtils from '../../libs/CurrencySymbolUtils'; /** * IOU Currency selection for selecting currency @@ -113,7 +114,7 @@ class IOUCurrencySelection extends Component { getCurrencyOptions() { const currencyListKeys = _.keys(this.props.currencyList); const currencyOptions = _.map(currencyListKeys, currencyCode => ({ - text: `${currencyCode} - ${this.props.getLocalizedCurrencySymbol(currencyCode)}`, + text: `${currencyCode} - ${CurrencySymbolUtils.getLocalizedCurrencySymbol(this.props.preferredLocale, currencyCode)}`, searchText: `${currencyCode} ${this.props.currencyList[currencyCode].symbol}`, currencyCode, })); diff --git a/src/pages/iou/steps/IOUAmountPage.js b/src/pages/iou/steps/IOUAmountPage.js index 4aeec7931e37..40507812b0f3 100755 --- a/src/pages/iou/steps/IOUAmountPage.js +++ b/src/pages/iou/steps/IOUAmountPage.js @@ -20,6 +20,7 @@ import Text from '../../../components/Text'; import CONST from '../../../CONST'; import TextInput from '../../../components/TextInput'; import canUseTouchScreen from '../../../libs/canUseTouchscreen'; +import * as CurrencySymbolUtils from '../../../libs/CurrencySymbolUtils'; const propTypes = { /** Whether or not this IOU has multiple participants */ @@ -63,7 +64,6 @@ class IOUAmountPage extends React.Component { this.updateAmount = this.updateAmount.bind(this); this.stripCommaFromAmount = this.stripCommaFromAmount.bind(this); this.focusTextInput = this.focusTextInput.bind(this); - this.isCurrencySymbolLTR = this.isCurrencySymbolLTR.bind(this); this.state = { amount: props.selectedAmount, @@ -82,24 +82,6 @@ class IOUAmountPage extends React.Component { this.focusTextInput(); } - /** - * Is currency symbol to left - * @param {String} currencyCode - * @return {Boolean} - */ - isCurrencySymbolLTR(currencyCode) { - const parts = this.props.formatToParts(0, { - style: 'currency', - currency: currencyCode, - }); - - // The first element of parts will be type: currency for all currency - // Where it starts with symbol and the other will have it at last - // If it is not the first, it must be at last - const isLeft = parts[0].type === 'currency'; - return isLeft; - } - /** * Focus text input */ @@ -213,9 +195,9 @@ class IOUAmountPage extends React.Component { } render() { - const currencySymbol = this.props.getLocalizedCurrencySymbol(this.props.iou.selectedCurrencyCode); const formattedAmount = this.replaceAllDigits(this.state.amount, this.props.toLocaleDigit); - const isCurrencySymbolLTR = this.isCurrencySymbolLTR(this.props.iou.selectedCurrencyCode); + const currencySymbol = CurrencySymbolUtils.getLocalizedCurrencySymbol(this.props.preferredLocale, this.props.iou.selectedCurrencyCode); + const isCurrencySymbolLTR = CurrencySymbolUtils.isCurrencySymbolLTR(this.props.preferredLocale, this.props.iou.selectedCurrencyCode); const currencySymbolElement = ( Navigation.navigate(this.props.hasMultipleParticipants From 3cd30fac61944ffa5661b77906356af665682853 Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Fri, 1 Apr 2022 14:11:35 +0530 Subject: [PATCH 007/331] test: add CurrencySymbolUtilsTest --- tests/unit/CurrencySymbolUtilsTest.js | 36 ++ tests/unit/currencyList.json | 855 ++++++++++++++++++++++++++ 2 files changed, 891 insertions(+) create mode 100644 tests/unit/CurrencySymbolUtilsTest.js create mode 100644 tests/unit/currencyList.json diff --git a/tests/unit/CurrencySymbolUtilsTest.js b/tests/unit/CurrencySymbolUtilsTest.js new file mode 100644 index 000000000000..ff3ec6772d32 --- /dev/null +++ b/tests/unit/CurrencySymbolUtilsTest.js @@ -0,0 +1,36 @@ +const _ = require('underscore'); +import * as CurrencySymbolUtils from '../../src/libs/CurrencySymbolUtils'; +import currencyList from './currencyList.json'; + +const currencyCodeList = _.keys(currencyList); + +const AVAILABLE_LOCALES = ['en', 'es']; + +// Contains item [isLeft, locale, currencyCode] +const symbolPositions = [ + [true, 'en', 'USD'], + [false, 'es', 'USD'] +]; + + +describe('CurrencySymbolUtils', () => { + describe('getLocalizedCurrencySymbol', () => { + + test.each(AVAILABLE_LOCALES)('returns non empty string for all currencyCode with preferredLocale %s', (prefrredLocale) => { + _.forEach(currencyCodeList, (currencyCode) => { + const localizedSymbol = CurrencySymbolUtils.getLocalizedCurrencySymbol(prefrredLocale, currencyCode); + + expect(typeof localizedSymbol).toBe('string'); + expect(localizedSymbol.length).toBeGreaterThan(0); + }); + }); + }); + + describe('isCurrencySymbolLTR', () => { + + test.each(symbolPositions)('returns %s for prefrredLocale %s and currencyCode %s', (isLeft, locale, currencyCode) => { + const isSymbolLeft = CurrencySymbolUtils.isCurrencySymbolLTR(locale, currencyCode); + expect(isSymbolLeft).toBe(isLeft); + }); + }); +}); diff --git a/tests/unit/currencyList.json b/tests/unit/currencyList.json new file mode 100644 index 000000000000..740b3caf2b28 --- /dev/null +++ b/tests/unit/currencyList.json @@ -0,0 +1,855 @@ +{ + "AED": { + "symbol": "Dhs", + "name": "UAE Dirham", + "ISO4217": "784" + }, + "AFN": { + "symbol": "Af", + "name": "Afghan Afghani", + "ISO4217": "971" + }, + "ALL": { + "symbol": "ALL", + "name": "Albanian Lek", + "ISO4217": "008" + }, + "AMD": { + "symbol": "դր", + "name": "Armenian Dram", + "ISO4217": "051" + }, + "ANG": { + "symbol": "NAƒ", + "name": "Neth Antilles Guilder", + "ISO4217": "532" + }, + "AOA": { + "symbol": "Kz", + "name": "Angolan Kwanza", + "ISO4217": "973" + }, + "ARS": { + "symbol": "AR$", + "name": "Argentine Peso", + "ISO4217": "032" + }, + "AUD": { + "symbol": "A$", + "name": "Australian Dollar", + "ISO4217": "036" + }, + "AWG": { + "symbol": "ƒ", + "name": "Aruba Florin", + "ISO4217": "533" + }, + "AZN": { + "symbol": "man", + "name": "Azerbaijani Manat", + "ISO4217": "944" + }, + "BAM": { + "symbol": "KM", + "name": "Bosnia And Herzegovina Convertible Mark", + "ISO4217": "977" + }, + "BBD": { + "symbol": "Bds$", + "name": "Barbados Dollar", + "ISO4217": "052" + }, + "BDT": { + "symbol": "Tk", + "name": "Bangladesh Taka", + "ISO4217": "050" + }, + "BGN": { + "symbol": "лв", + "name": "Bulgarian Lev", + "ISO4217": "975" + }, + "BHD": { + "symbol": "BHD", + "name": "Bahraini Dinar", + "ISO4217": "048" + }, + "BIF": { + "symbol": "FBu", + "name": "Burundi Franc", + "decimals": 0, + "ISO4217": "108" + }, + "BMD": { + "symbol": "BD$", + "name": "Bermuda Dollar", + "ISO4217": "060" + }, + "BND": { + "symbol": "BN$", + "name": "Brunei Dollar", + "ISO4217": "096" + }, + "BOB": { + "symbol": "Bs", + "name": "Bolivian Boliviano", + "ISO4217": "068" + }, + "BRL": { + "symbol": "R$", + "name": "Brazilian Real", + "ISO4217": "986" + }, + "BSD": { + "symbol": "BS$", + "name": "Bahamian Dollar", + "ISO4217": "044" + }, + "BTN": { + "symbol": "Nu.", + "name": "Bhutan Ngultrum", + "ISO4217": "064" + }, + "BWP": { + "symbol": "P", + "name": "Botswana Pula", + "ISO4217": "072" + }, + "BYN": { + "symbol": "BR", + "name": "Belarus Ruble", + "ISO4217": "933" + }, + "BYR": { + "symbol": "BR", + "name": "Belarus Ruble", + "retired": true, + "retirementDate": "2016-07-01", + "ISO4217": "974" + }, + "BZD": { + "symbol": "BZ$", + "name": "Belize Dollar", + "ISO4217": "084" + }, + "CAD": { + "symbol": "C$", + "name": "Canadian Dollar", + "ISO4217": "124" + }, + "CDF": { + "symbol": "CDF", + "name": "Congolese Franc", + "ISO4217": "976" + }, + "CHF": { + "symbol": "CHF", + "name": "Swiss Franc", + "ISO4217": "756" + }, + "CLP": { + "symbol": "Ch$", + "name": "Chilean Peso", + "decimals": 0, + "ISO4217": "152" + }, + "CNY": { + "symbol": "¥", + "name": "Chinese Yuan", + "ISO4217": "156" + }, + "COP": { + "symbol": "Col$", + "name": "Colombian Peso", + "decimals": 0, + "ISO4217": "170" + }, + "CRC": { + "symbol": "CR₡", + "name": "Costa Rica Colon", + "ISO4217": "188" + }, + "CUC": { + "symbol": "CUC", + "name": "Cuban Convertible Peso", + "ISO4217": "931" + }, + "CUP": { + "symbol": "$MN", + "name": "Cuban Peso", + "ISO4217": "192" + }, + "CVE": { + "symbol": "Esc", + "name": "Cape Verde Escudo", + "ISO4217": "132" + }, + "CZK": { + "symbol": "Kč", + "name": "Czech Koruna", + "ISO4217": "203" + }, + "DJF": { + "symbol": "Fdj", + "name": "Dijibouti Franc", + "decimals": 0, + "ISO4217": "262" + }, + "DKK": { + "symbol": "Dkr", + "name": "Danish Krone", + "ISO4217": "208" + }, + "DOP": { + "symbol": "RD$", + "name": "Dominican Peso", + "ISO4217": "214" + }, + "DZD": { + "symbol": "DZD", + "name": "Algerian Dinar", + "ISO4217": "012" + }, + "EEK": { + "symbol": "KR", + "name": "Estonian Kroon", + "ISO4217": "", + "retired": true + }, + "EGP": { + "symbol": "EGP", + "name": "Egyptian Pound", + "ISO4217": "818" + }, + "ERN": { + "symbol": "Nfk", + "name": "Eritrea Nakfa", + "ISO4217": "232" + }, + "ETB": { + "symbol": "Br", + "name": "Ethiopian Birr", + "ISO4217": "230" + }, + "EUR": { + "symbol": "€", + "name": "Euro", + "ISO4217": "978" + }, + "FJD": { + "symbol": "FJ$", + "name": "Fiji Dollar", + "ISO4217": "242" + }, + "FKP": { + "symbol": "FK£", + "name": "Falkland Islands Pound", + "ISO4217": "238" + }, + "GBP": { + "symbol": "£", + "name": "British Pound", + "ISO4217": "826" + }, + "GEL": { + "symbol": "ლ", + "name": "Georgian Lari", + "ISO4217": "981" + }, + "GHS": { + "symbol": "₵", + "name": "Ghanaian Cedi", + "ISO4217": "936" + }, + "GIP": { + "symbol": "£G", + "name": "Gibraltar Pound", + "ISO4217": "292" + }, + "GMD": { + "symbol": "D", + "name": "Gambian Dalasi", + "ISO4217": "270" + }, + "GNF": { + "symbol": "FG", + "name": "Guinea Franc", + "decimals": 0, + "ISO4217": "324" + }, + "GTQ": { + "symbol": "Q", + "name": "Guatemala Quetzal", + "ISO4217": "320" + }, + "GYD": { + "symbol": "GY$", + "name": "Guyana Dollar", + "ISO4217": "328" + }, + "HKD": { + "symbol": "HK$", + "name": "Hong Kong Dollar", + "ISO4217": "344" + }, + "HNL": { + "symbol": "HNL", + "name": "Honduras Lempira", + "ISO4217": "340" + }, + "HRK": { + "symbol": "kn", + "name": "Croatian Kuna", + "ISO4217": "191" + }, + "HTG": { + "symbol": "G", + "name": "Haiti Gourde", + "ISO4217": "332" + }, + "HUF": { + "symbol": "Ft", + "name": "Hungarian Forint", + "ISO4217": "348" + }, + "IDR": { + "symbol": "Rp", + "name": "Indonesian Rupiah", + "ISO4217": "360" + }, + "ILS": { + "symbol": "₪", + "name": "Israeli Shekel", + "ISO4217": "376" + }, + "INR": { + "symbol": "₹", + "name": "Indian Rupee", + "ISO4217": "356" + }, + "IQD": { + "symbol": "IQD", + "name": "Iraqi Dinar", + "ISO4217": "368" + }, + "IRR": { + "symbol": "﷼", + "name": "Iran Rial", + "ISO4217": "364" + }, + "ISK": { + "symbol": "kr", + "name": "Iceland Krona", + "decimals": 0, + "ISO4217": "352" + }, + "JMD": { + "symbol": "J$", + "name": "Jamaican Dollar", + "ISO4217": "388" + }, + "JOD": { + "symbol": "JOD", + "name": "Jordanian Dinar", + "ISO4217": "400" + }, + "JPY": { + "symbol": "¥", + "name": "Japanese Yen", + "decimals": 0, + "ISO4217": "392" + }, + "KES": { + "symbol": "KSh", + "name": "Kenyan Shilling", + "ISO4217": "404" + }, + "KGS": { + "symbol": "KGS", + "name": "Kyrgyzstani Som", + "ISO4217": "417" + }, + "KHR": { + "symbol": "KHR", + "name": "Cambodia Riel", + "ISO4217": "116" + }, + "KMF": { + "symbol": "CF", + "name": "Comoros Franc", + "ISO4217": "174" + }, + "KPW": { + "symbol": "KP₩", + "name": "North Korean Won", + "ISO4217": "408" + }, + "KRW": { + "symbol": "₩", + "name": "Korean Won", + "ISO4217": "410" + }, + "KWD": { + "symbol": "KWD", + "name": "Kuwaiti Dinar", + "ISO4217": "414" + }, + "KYD": { + "symbol": "CI$", + "name": "Cayman Islands Dollar", + "ISO4217": "136" + }, + "KZT": { + "symbol": "〒", + "name": "Kazakhstan Tenge", + "ISO4217": "398" + }, + "LAK": { + "symbol": "₭", + "name": "Lao Kip", + "ISO4217": "418" + }, + "LBP": { + "symbol": "LBP", + "name": "Lebanese Pound", + "ISO4217": "422" + }, + "LKR": { + "symbol": "SL₨", + "name": "Sri Lanka Rupee", + "ISO4217": "144" + }, + "LRD": { + "symbol": "L$", + "name": "Liberian Dollar", + "ISO4217": "430" + }, + "LSL": { + "symbol": "M", + "name": "Lesotho Loti", + "ISO4217": "426" + }, + "LTL": { + "symbol": "Lt", + "name": "Lithuanian Lita", + "retirementDate": "2015-08-22", + "retired": true, + "ISO4217": "440" + }, + "LVL": { + "symbol": "Ls", + "name": "Latvian Lat", + "ISO4217": "428", + "retired": true + }, + "LYD": { + "symbol": "LYD", + "name": "Libyan Dinar", + "ISO4217": "434" + }, + "MAD": { + "symbol": "MAD", + "name": "Moroccan Dirham", + "ISO4217": "504" + }, + "MDL": { + "symbol": "MDL", + "name": "Moldovan Leu", + "ISO4217": "498" + }, + "MGA": { + "symbol": "MGA", + "name": "Malagasy Ariary", + "ISO4217": "969" + }, + "MKD": { + "symbol": "ден", + "name": "Macedonian Denar", + "ISO4217": "807" + }, + "MMK": { + "symbol": "Ks", + "name": "Myanmar Kyat", + "ISO4217": "104" + }, + "MNT": { + "symbol": "₮", + "name": "Mongolian Tugrik", + "ISO4217": "496" + }, + "MOP": { + "symbol": "MOP$", + "name": "Macau Pataca", + "ISO4217": "446" + }, + "MRO": { + "symbol": "UM", + "name": "Mauritania Ougulya", + "decimals": 0, + "retired": true, + "retirementDate": "2018-07-11", + "ISO4217": "478" + }, + "MRU": { + "symbol": "UM", + "name": "Mauritania Ougulya", + "decimals": 0, + "ISO4217": "" + }, + "MUR": { + "symbol": "Rs", + "name": "Mauritius Rupee", + "ISO4217": "480" + }, + "MVR": { + "symbol": "Rf", + "name": "Maldives Rufiyaa", + "ISO4217": "462" + }, + "MWK": { + "symbol": "MK", + "name": "Malawi Kwacha", + "ISO4217": "454" + }, + "MXN": { + "symbol": "Mex$", + "name": "Mexican Peso", + "ISO4217": "484" + }, + "MYR": { + "symbol": "RM", + "name": "Malaysian Ringgit", + "ISO4217": "458" + }, + "MZN": { + "symbol": "MTn", + "name": "Mozambican Metical", + "ISO4217": "943" + }, + "NAD": { + "symbol": "N$", + "name": "Namibian Dollar", + "ISO4217": "516" + }, + "NGN": { + "symbol": "₦", + "name": "Nigerian Naira", + "ISO4217": "566" + }, + "NIO": { + "symbol": "NIO", + "name": "Nicaragua Cordoba", + "ISO4217": "558" + }, + "NOK": { + "symbol": "Nkr", + "name": "Norwegian Krone", + "ISO4217": "578" + }, + "NPR": { + "symbol": "₨", + "name": "Nepalese Rupee", + "ISO4217": "524" + }, + "NZD": { + "symbol": "NZ$", + "name": "New Zealand Dollar", + "ISO4217": "554" + }, + "OMR": { + "symbol": "OMR", + "name": "Omani Rial", + "ISO4217": "512" + }, + "PAB": { + "symbol": "B", + "name": "Panama Balboa", + "ISO4217": "590" + }, + "PEN": { + "symbol": "S/.", + "name": "Peruvian Nuevo Sol", + "ISO4217": "604" + }, + "PGK": { + "symbol": "K", + "name": "Papua New Guinea Kina", + "ISO4217": "598" + }, + "PHP": { + "symbol": "₱", + "name": "Philippine Peso", + "ISO4217": "608" + }, + "PKR": { + "symbol": "Rs", + "name": "Pakistani Rupee", + "ISO4217": "586" + }, + "PLN": { + "symbol": "zł", + "name": "Polish Zloty", + "ISO4217": "985" + }, + "PYG": { + "symbol": "₲", + "name": "Paraguayan Guarani", + "ISO4217": "600" + }, + "QAR": { + "symbol": "QAR", + "name": "Qatar Rial", + "ISO4217": "634" + }, + "RON": { + "symbol": "RON", + "name": "Romanian New Leu", + "ISO4217": "946" + }, + "RSD": { + "symbol": "РСД", + "name": "Serbian Dinar", + "ISO4217": "941" + }, + "RUB": { + "symbol": "₽", + "name": "Russian Rouble", + "ISO4217": "643" + }, + "RWF": { + "symbol": "RF", + "name": "Rwanda Franc", + "decimals": 0, + "ISO4217": "646" + }, + "SAR": { + "symbol": "SAR", + "name": "Saudi Arabian Riyal", + "ISO4217": "682" + }, + "SBD": { + "symbol": "SI$", + "name": "Solomon Islands Dollar", + "ISO4217": "090" + }, + "SCR": { + "symbol": "SR", + "name": "Seychelles Rupee", + "ISO4217": "690" + }, + "SDG": { + "symbol": "SDG", + "name": "Sudanese Pound", + "ISO4217": "938" + }, + "SEK": { + "symbol": "Skr", + "name": "Swedish Krona", + "ISO4217": "752" + }, + "SGD": { + "symbol": "S$", + "name": "Singapore Dollar", + "ISO4217": "702" + }, + "SHP": { + "symbol": "£S", + "name": "St Helena Pound", + "ISO4217": "654" + }, + "SLL": { + "symbol": "Le", + "name": "Sierra Leone Leone", + "ISO4217": "694" + }, + "SOS": { + "symbol": "So.", + "name": "Somali Shilling", + "ISO4217": "706" + }, + "SRD": { + "symbol": "SRD", + "name": "Surinamese Dollar", + "ISO4217": "968" + }, + "STD": { + "symbol": "Db", + "name": "Sao Tome Dobra", + "retired": true, + "retirementDate": "2018-07-11", + "ISO4217": "678" + }, + "STN": { + "symbol": "Db", + "name": "Sao Tome Dobra", + "ISO4217": "" + }, + "SVC": { + "symbol": "SVC", + "name": "El Salvador Colon", + "ISO4217": "222" + }, + "SYP": { + "symbol": "SYP", + "name": "Syrian Pound", + "ISO4217": "760" + }, + "SZL": { + "symbol": "E", + "name": "Swaziland Lilageni", + "ISO4217": "748" + }, + "THB": { + "symbol": "฿", + "name": "Thai Baht", + "ISO4217": "764" + }, + "TJS": { + "symbol": "TJS", + "name": "Tajikistani Somoni", + "ISO4217": "972" + }, + "TMT": { + "symbol": "m", + "name": "Turkmenistani Manat", + "ISO4217": "934" + }, + "TND": { + "symbol": "TND", + "name": "Tunisian Dinar", + "ISO4217": "788" + }, + "TOP": { + "symbol": "T$", + "name": "Tonga Pa'ang", + "ISO4217": "776" + }, + "TRY": { + "symbol": "TL", + "name": "Turkish Lira", + "ISO4217": "949" + }, + "TTD": { + "symbol": "TT$", + "name": "Trinidad & Tobago Dollar", + "ISO4217": "780" + }, + "TWD": { + "symbol": "NT$", + "name": "Taiwan Dollar", + "ISO4217": "901" + }, + "TZS": { + "symbol": "TZS", + "name": "Tanzanian Shilling", + "ISO4217": "834" + }, + "UAH": { + "symbol": "₴", + "name": "Ukraine Hryvnia", + "ISO4217": "980" + }, + "UGX": { + "symbol": "USh", + "name": "Ugandan Shilling", + "decimals": 0, + "ISO4217": "800" + }, + "USD": { + "symbol": "$", + "name": "United States Dollar", + "ISO4217": "840" + }, + "UYU": { + "symbol": "$U", + "name": "Uruguayan New Peso", + "ISO4217": "858" + }, + "UZS": { + "symbol": "UZS", + "name": "Uzbekistani Som", + "ISO4217": "860" + }, + "VEB": { + "symbol": "Bs.", + "name": "Venezuelan Bolivar", + "retired": true, + "retirementDate": "2008-02-01", + "ISO4217": "" + }, + "VEF": { + "symbol": "Bs.F", + "name": "Venezuelan Bolivar Fuerte", + "retired": true, + "retirementDate": "2018-08-20", + "ISO4217": "937" + }, + "VES": { + "symbol": "Bs.S", + "name": "Venezuelan Bolivar Soberano", + "ISO4217": "928" + }, + "VND": { + "symbol": "₫", + "name": "Vietnam Dong", + "decimals": 0, + "ISO4217": "704" + }, + "VUV": { + "symbol": "Vt", + "name": "Vanuatu Vatu", + "ISO4217": "548" + }, + "WST": { + "symbol": "WS$", + "name": "Samoa Tala", + "ISO4217": "882" + }, + "XAF": { + "symbol": "FCFA", + "name": "CFA Franc (BEAC)", + "decimals": 0, + "ISO4217": "950" + }, + "XCD": { + "symbol": "EC$", + "name": "East Caribbean Dollar", + "ISO4217": "951" + }, + "XOF": { + "symbol": "CFA", + "name": "CFA Franc (BCEAO)", + "decimals": 0, + "ISO4217": "952" + }, + "XPF": { + "symbol": "XPF", + "name": "Pacific Franc", + "decimals": 0, + "ISO4217": "953" + }, + "YER": { + "symbol": "YER", + "name": "Yemen Riyal", + "ISO4217": "886" + }, + "ZAR": { + "symbol": "R", + "name": "South African Rand", + "ISO4217": "710" + }, + "ZMK": { + "symbol": "ZK", + "name": "Zambian Kwacha", + "retired": true, + "retirementDate": "2013-01-01", + "ISO4217": "894" + }, + "ZMW": { + "symbol": "ZMW", + "name": "Zambian Kwacha", + "cacheBurst": 1, + "ISO4217": "967" + } +} + \ No newline at end of file From 155dc0216fdab5354c2f1fe1348e7d24f4055a2b Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Fri, 1 Apr 2022 14:14:54 +0530 Subject: [PATCH 008/331] test: replace require to import --- tests/unit/CurrencySymbolUtilsTest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/CurrencySymbolUtilsTest.js b/tests/unit/CurrencySymbolUtilsTest.js index ff3ec6772d32..483c1f2decfc 100644 --- a/tests/unit/CurrencySymbolUtilsTest.js +++ b/tests/unit/CurrencySymbolUtilsTest.js @@ -1,4 +1,4 @@ -const _ = require('underscore'); +import _ from 'underscore'; import * as CurrencySymbolUtils from '../../src/libs/CurrencySymbolUtils'; import currencyList from './currencyList.json'; From 3e57e26e43b3671189b1408f872e04c59baa7f56 Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Fri, 1 Apr 2022 14:21:29 +0530 Subject: [PATCH 009/331] test: fix lint error --- tests/unit/CurrencySymbolUtilsTest.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/CurrencySymbolUtilsTest.js b/tests/unit/CurrencySymbolUtilsTest.js index 483c1f2decfc..0a01eb0e0c80 100644 --- a/tests/unit/CurrencySymbolUtilsTest.js +++ b/tests/unit/CurrencySymbolUtilsTest.js @@ -12,7 +12,6 @@ const symbolPositions = [ [false, 'es', 'USD'] ]; - describe('CurrencySymbolUtils', () => { describe('getLocalizedCurrencySymbol', () => { From 6a0e40069caeb9b456bb8ff0d5f942acfbea97ff Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Fri, 1 Apr 2022 14:27:27 +0530 Subject: [PATCH 010/331] test: fix lint error --- tests/unit/CurrencySymbolUtilsTest.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/unit/CurrencySymbolUtilsTest.js b/tests/unit/CurrencySymbolUtilsTest.js index 0a01eb0e0c80..ae38925aa2c9 100644 --- a/tests/unit/CurrencySymbolUtilsTest.js +++ b/tests/unit/CurrencySymbolUtilsTest.js @@ -9,12 +9,11 @@ const AVAILABLE_LOCALES = ['en', 'es']; // Contains item [isLeft, locale, currencyCode] const symbolPositions = [ [true, 'en', 'USD'], - [false, 'es', 'USD'] + [false, 'es', 'USD'], ]; describe('CurrencySymbolUtils', () => { describe('getLocalizedCurrencySymbol', () => { - test.each(AVAILABLE_LOCALES)('returns non empty string for all currencyCode with preferredLocale %s', (prefrredLocale) => { _.forEach(currencyCodeList, (currencyCode) => { const localizedSymbol = CurrencySymbolUtils.getLocalizedCurrencySymbol(prefrredLocale, currencyCode); @@ -26,7 +25,6 @@ describe('CurrencySymbolUtils', () => { }); describe('isCurrencySymbolLTR', () => { - test.each(symbolPositions)('returns %s for prefrredLocale %s and currencyCode %s', (isLeft, locale, currencyCode) => { const isSymbolLeft = CurrencySymbolUtils.isCurrencySymbolLTR(locale, currencyCode); expect(isSymbolLeft).toBe(isLeft); From 84bcdeb341633c44c81334d8caac138e1064d8ea Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Fri, 1 Apr 2022 15:26:25 +0530 Subject: [PATCH 011/331] refactor: CurrencySymbolUtils --- src/libs/CurrencySymbolUtils.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libs/CurrencySymbolUtils.js b/src/libs/CurrencySymbolUtils.js index 86d28dda2e18..6bcdb1a5f9ba 100644 --- a/src/libs/CurrencySymbolUtils.js +++ b/src/libs/CurrencySymbolUtils.js @@ -12,8 +12,7 @@ function getLocalizedCurrencySymbol(preferredLocale, currencyCode) { style: 'currency', currency: currencyCode, }); - const currencySymbol = _.find(parts, part => part.type === 'currency').value; - return currencySymbol; + return _.find(parts, part => part.type === 'currency').value; } /** @@ -31,8 +30,7 @@ function isCurrencySymbolLTR(preferredLocale, currencyCode) { // The first element of parts will be type: currency for all currency // Where it starts with symbol and the other will have it at last // If it is not the first, it must be at last - const isLeft = parts[0].type === 'currency'; - return isLeft; + return parts[0].type === 'currency'; } export { From 696b7d5dccb627df9a5f98d2658fb53f468089d3 Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Fri, 1 Apr 2022 15:35:30 +0530 Subject: [PATCH 012/331] test: refactor names --- tests/unit/CurrencySymbolUtilsTest.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/unit/CurrencySymbolUtilsTest.js b/tests/unit/CurrencySymbolUtilsTest.js index ae38925aa2c9..4f4a2994b9e7 100644 --- a/tests/unit/CurrencySymbolUtilsTest.js +++ b/tests/unit/CurrencySymbolUtilsTest.js @@ -3,7 +3,6 @@ import * as CurrencySymbolUtils from '../../src/libs/CurrencySymbolUtils'; import currencyList from './currencyList.json'; const currencyCodeList = _.keys(currencyList); - const AVAILABLE_LOCALES = ['en', 'es']; // Contains item [isLeft, locale, currencyCode] @@ -14,18 +13,17 @@ const symbolPositions = [ describe('CurrencySymbolUtils', () => { describe('getLocalizedCurrencySymbol', () => { - test.each(AVAILABLE_LOCALES)('returns non empty string for all currencyCode with preferredLocale %s', (prefrredLocale) => { + test.each(AVAILABLE_LOCALES)('Returns non empty string for all currencyCode with preferredLocale %s', (prefrredLocale) => { _.forEach(currencyCodeList, (currencyCode) => { const localizedSymbol = CurrencySymbolUtils.getLocalizedCurrencySymbol(prefrredLocale, currencyCode); - expect(typeof localizedSymbol).toBe('string'); - expect(localizedSymbol.length).toBeGreaterThan(0); + expect(localizedSymbol).toBeTruthy(); }); }); }); describe('isCurrencySymbolLTR', () => { - test.each(symbolPositions)('returns %s for prefrredLocale %s and currencyCode %s', (isLeft, locale, currencyCode) => { + test.each(symbolPositions)('Returns %s for prefrredLocale %s and currencyCode %s', (isLeft, locale, currencyCode) => { const isSymbolLeft = CurrencySymbolUtils.isCurrencySymbolLTR(locale, currencyCode); expect(isSymbolLeft).toBe(isLeft); }); From 3ef60e8703b71b794e9a96df2837b24edb7b6ba9 Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Fri, 1 Apr 2022 15:39:14 +0530 Subject: [PATCH 013/331] docs: update comments --- src/libs/CurrencySymbolUtils.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/CurrencySymbolUtils.js b/src/libs/CurrencySymbolUtils.js index 6bcdb1a5f9ba..e535af20a35b 100644 --- a/src/libs/CurrencySymbolUtils.js +++ b/src/libs/CurrencySymbolUtils.js @@ -2,10 +2,10 @@ import _ from 'underscore'; import * as NumberFormatUtils from './NumberFormatUtils'; /** - * Get localized currency symbol for SO4217 Code + * Get localized currency symbol for currency(ISO 4217) Code * @param {String} preferredLocale * @param {String} currencyCode - * @return {String} + * @returns {String} */ function getLocalizedCurrencySymbol(preferredLocale, currencyCode) { const parts = NumberFormatUtils.formatToParts(preferredLocale, 0, { @@ -16,10 +16,10 @@ function getLocalizedCurrencySymbol(preferredLocale, currencyCode) { } /** - * Is currency symbol to left + * Whether the currency symbol is left-to-right. * @param {String} preferredLocale * @param {String} currencyCode - * @return {Boolean} + * @returns {Boolean} */ function isCurrencySymbolLTR(preferredLocale, currencyCode) { const parts = NumberFormatUtils.formatToParts(preferredLocale, 0, { From 5992dfc74e665866451d7256157e4c0e303f027b Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Fri, 1 Apr 2022 21:52:08 +0530 Subject: [PATCH 014/331] docs: update comment --- src/libs/CurrencySymbolUtils.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/CurrencySymbolUtils.js b/src/libs/CurrencySymbolUtils.js index e535af20a35b..bbe37939fd17 100644 --- a/src/libs/CurrencySymbolUtils.js +++ b/src/libs/CurrencySymbolUtils.js @@ -27,9 +27,7 @@ function isCurrencySymbolLTR(preferredLocale, currencyCode) { currency: currencyCode, }); - // The first element of parts will be type: currency for all currency - // Where it starts with symbol and the other will have it at last - // If it is not the first, it must be at last + // Currency is LTR when the first part is of currency type. return parts[0].type === 'currency'; } From 0275158c76cd04454ed2365262db4f4ea5219212 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 4 Apr 2022 15:23:32 -0700 Subject: [PATCH 015/331] Once logged in, wait for betas before navigating --- src/pages/LogInWithShortLivedTokenPage.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index 5e091ead6f83..c406e47e01e2 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -10,6 +10,9 @@ import Navigation from '../libs/Navigation/Navigation'; import Log from '../libs/Log'; const propTypes = { + /** List of betas available to current user */ + betas: PropTypes.arrayOf(PropTypes.string), + /** The parameters needed to authenticate with a short lived token are in the URL */ route: PropTypes.shape({ /** The name of the route */ @@ -42,6 +45,7 @@ const propTypes = { }; const defaultProps = { + betas: null, route: { params: {}, }, @@ -74,6 +78,21 @@ class LogInWithShortLivedTokenPage extends Component { Log.info('[LoginWithShortLivedTokenPage] exitTo is workspace/new - handling new workspace creation in AuthScreens'); return; } + this.navigateToExitRoute(); + } + + componentDidUpdate() { + this.navigateToExitRoute(); + } + + navigateToExitRoute() { + if (!this.props.betas) { + // Wait to navigate until the betas are loaded. Some pages like ReimbursementAccountPage require betas, so keep loading until they are available. + return; + } + + // exitTo is URI encoded because it could contain a variable number of slashes (i.e. "workspace/new" vs "workspace//card") + const exitTo = decodeURIComponent(lodashGet(this.props.route.params, 'exitTo', '')); // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` @@ -111,6 +130,9 @@ LogInWithShortLivedTokenPage.propTypes = propTypes; LogInWithShortLivedTokenPage.defaultProps = defaultProps; export default withOnyx({ + betas: { + key: ONYXKEYS.BETAS, + }, session: { key: ONYXKEYS.SESSION, }, From ae4f49946b030c1607969fb58a855ee93223baa8 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 5 Apr 2022 09:54:50 -0700 Subject: [PATCH 016/331] Remove extra update to session and accountID Prevent multiple updates to the session and store the accountID as int --- src/libs/actions/Session/index.js | 7 +------ src/pages/LogInWithShortLivedTokenPage.js | 3 +-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index 719a0a7be7dc..9e57592d9093 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -255,19 +255,14 @@ function signIn(password, twoFactorAuthCode) { /** * Uses a short lived authToken to continue a user's session from OldDot * - * @param {String} accountID * @param {String} email * @param {String} shortLivedToken * @param {String} exitTo */ -function signInWithShortLivedToken(accountID, email, shortLivedToken) { +function signInWithShortLivedToken(email, shortLivedToken) { Onyx.merge(ONYXKEYS.ACCOUNT, {...CONST.DEFAULT_ACCOUNT_DATA, loading: true}); createTemporaryLogin(shortLivedToken, email).then((response) => { - Onyx.merge(ONYXKEYS.SESSION, { - accountID, - email, - }); if (response.jsonCode === 200) { User.getUserDetails(); Onyx.merge(ONYXKEYS.ACCOUNT, {success: true}); diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index c406e47e01e2..dbb5763ec2a2 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -54,14 +54,13 @@ const defaultProps = { class LogInWithShortLivedTokenPage extends Component { componentDidMount() { - const accountID = lodashGet(this.props.route.params, 'accountID', ''); const email = lodashGet(this.props.route.params, 'email', ''); const shortLivedToken = lodashGet(this.props.route.params, 'shortLivedToken', ''); const isUserSignedIn = this.props.session && this.props.session.authToken; if (!isUserSignedIn) { Log.info('[LoginWithShortLivedTokenPage] User not signed in - signing in with short lived token'); - Session.signInWithShortLivedToken(accountID, email, shortLivedToken); + Session.signInWithShortLivedToken(email, shortLivedToken); return; } From edfc7e6b1025964a6a345f86e14b094635070774 Mon Sep 17 00:00:00 2001 From: Amal Nazeem Date: Fri, 8 Apr 2022 15:46:48 -0400 Subject: [PATCH 017/331] Use latest name from Onyx in policyExpenseChats --- src/libs/actions/Report.js | 9 ++------- src/libs/reportUtils.js | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index d98f62c4aa21..c6ab565a29de 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -160,14 +160,9 @@ function getChatReportName(fullReport, chatType) { return LoginUtils.getEmailWithoutMergedAccountPrefix(fullReport.reportName); } + // We can store whatever title for the report object since we'll create the real name based on the latest data in Onyx if (ReportUtils.isPolicyExpenseChat({chatType})) { - return `${LoginUtils.getEmailWithoutMergedAccountPrefix(fullReport.reportName)}${(ReportUtils.isArchivedRoom({ - chatType, - stateNum: fullReport.state, - statusNum: fullReport.status, - }) - ? ` (${Localize.translateLocal('common.archived')})` - : '')}`; + return fullReport.reportName; } const {sharedReportList} = fullReport; diff --git a/src/libs/reportUtils.js b/src/libs/reportUtils.js index 6ca7ecf24530..6b9c5f4c2502 100644 --- a/src/libs/reportUtils.js +++ b/src/libs/reportUtils.js @@ -5,6 +5,8 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '../ONYXKEYS'; import CONST from '../CONST'; import * as Localize from './Localize'; +import * as PersonalDetails from './actions/PersonalDetails'; + let sessionEmail; Onyx.connect({ @@ -189,6 +191,19 @@ function getPolicyName(report, policies) { return lodashGet(policies, [`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, 'name'], defaultValue); } +/** + * Get the title for a policyExpenseChat + * @param {Object} report + * @param {Object} policies must have Onyxkey prefix (i.e. 'policy_') for keys + * @param {Object} personalDetails must be keyed by email + */ +function getPolicyExpenseChatTitle(report, policies) { + if (report.isOwnPolicyExpenseChat) { + return getPolicyName(report, policies); + } + return PersonalDetails.getDisplayName(report.ownerEmail); +} + /** * Get either the policyName or domainName the chat is tied to * @param {Object} report @@ -320,6 +335,7 @@ export { isChatRoom, getChatRoomSubtitle, getPolicyName, + getPolicyExpenseChatTitle, isArchivedRoom, isConciergeChatReport, hasExpensifyEmails, From 2c273c3614e588159945f661251c804f70449c55 Mon Sep 17 00:00:00 2001 From: Amal Nazeem Date: Wed, 13 Apr 2022 20:58:20 -0400 Subject: [PATCH 018/331] Use policyExpenseChat title in HeaderView --- src/pages/home/HeaderView.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index dd7c58ac2440..a698d3bf00f3 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -88,9 +88,14 @@ const HeaderView = (props) => { ); const isChatRoom = ReportUtils.isChatRoom(props.report); const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(props.report); - const title = (isChatRoom || isPolicyExpenseChat) - ? props.report.reportName - : _.map(displayNamesWithTooltips, ({displayName}) => displayName).join(', '); + let title; + if (isChatRoom) { + title = props.report.reportName; + } else if (isPolicyExpenseChat) { + title = ReportUtils.getPolicyExpenseChatName(props.report); + } else { + title = _.map(displayNamesWithTooltips, ({displayName}) => displayName).join(', '); + } const subtitle = ReportUtils.getChatRoomSubtitle(props.report, props.policies); const isConcierge = participants.length === 1 && _.contains(participants, CONST.EMAIL.CONCIERGE); From 0c5ecbe27da33709d2fa36308c154d9d44240fdd Mon Sep 17 00:00:00 2001 From: Amal Nazeem Date: Thu, 14 Apr 2022 15:11:57 -0400 Subject: [PATCH 019/331] Move function to avoid cyclic dependency --- src/libs/actions/Report.js | 17 ++++++++++++++++- src/libs/reportUtils.js | 15 --------------- src/pages/home/HeaderView.js | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 8472fa0d6b37..0a4d7ecf801e 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -160,7 +160,8 @@ function getChatReportName(fullReport, chatType) { return LoginUtils.getEmailWithoutMergedAccountPrefix(fullReport.reportName); } - // We can store whatever title for the report object since we'll create the real name based on the latest data in Onyx + // We can store whatever title for the report object since we'll grab the latest most accurate + // name using getPolicyExpenseChatTitle if (ReportUtils.isPolicyExpenseChat({chatType})) { return fullReport.reportName; } @@ -1560,6 +1561,19 @@ function renameReport(reportID, reportName) { .finally(() => Onyx.set(ONYXKEYS.IS_LOADING_RENAME_POLICY_ROOM, false)); } +/** + * Get the title for a policyExpenseChat + * @param {Object} report + * @param {Object} policies must have Onyxkey prefix (i.e. 'policy_') for keys + */ + function getPolicyExpenseChatTitle(report, policies) { + if (report.isOwnPolicyExpenseChat) { + return ReportUtils.getPolicyName(report, policies); + } + return PersonalDetails.getDisplayName(report.ownerEmail); +} + + export { fetchAllReports, fetchActions, @@ -1591,4 +1605,5 @@ export { createPolicyRoom, renameReport, getLastReadSequenceNumber, + getPolicyExpenseChatTitle, }; diff --git a/src/libs/reportUtils.js b/src/libs/reportUtils.js index 6b9c5f4c2502..599371f13622 100644 --- a/src/libs/reportUtils.js +++ b/src/libs/reportUtils.js @@ -5,7 +5,6 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '../ONYXKEYS'; import CONST from '../CONST'; import * as Localize from './Localize'; -import * as PersonalDetails from './actions/PersonalDetails'; let sessionEmail; @@ -191,19 +190,6 @@ function getPolicyName(report, policies) { return lodashGet(policies, [`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, 'name'], defaultValue); } -/** - * Get the title for a policyExpenseChat - * @param {Object} report - * @param {Object} policies must have Onyxkey prefix (i.e. 'policy_') for keys - * @param {Object} personalDetails must be keyed by email - */ -function getPolicyExpenseChatTitle(report, policies) { - if (report.isOwnPolicyExpenseChat) { - return getPolicyName(report, policies); - } - return PersonalDetails.getDisplayName(report.ownerEmail); -} - /** * Get either the policyName or domainName the chat is tied to * @param {Object} report @@ -335,7 +321,6 @@ export { isChatRoom, getChatRoomSubtitle, getPolicyName, - getPolicyExpenseChatTitle, isArchivedRoom, isConciergeChatReport, hasExpensifyEmails, diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index a698d3bf00f3..69cac77ee405 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -92,7 +92,7 @@ const HeaderView = (props) => { if (isChatRoom) { title = props.report.reportName; } else if (isPolicyExpenseChat) { - title = ReportUtils.getPolicyExpenseChatName(props.report); + title = Report.getPolicyExpenseChatTitle(props.report); } else { title = _.map(displayNamesWithTooltips, ({displayName}) => displayName).join(', '); } From f79a57339abade36ab5e76f6ef8b321367e51f3b Mon Sep 17 00:00:00 2001 From: Amal Nazeem Date: Thu, 14 Apr 2022 15:34:47 -0400 Subject: [PATCH 020/331] Fix policyExpenseChat name in LHN --- src/libs/OptionsListUtils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 9907efae229e..9549cb785daf 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -6,6 +6,7 @@ import lodashOrderBy from 'lodash/orderBy'; import Str from 'expensify-common/lib/str'; import ONYXKEYS from '../ONYXKEYS'; import CONST from '../CONST'; +import * as Report from './actions/Report'; import * as ReportUtils from './reportUtils'; import * as Localize from './Localize'; import Permissions from './Permissions'; @@ -275,7 +276,7 @@ function createOption(personalDetailList, report, { let text; let alternateText; if (isChatRoom || isPolicyExpenseChat) { - text = (isArchivedRoom && report.isOwnPolicyExpenseChat) ? report.oldPolicyName : lodashGet(report, ['reportName'], ''); + text = isPolicyExpenseChat ? Report.getPolicyExpenseChatTitle(report, policies) : lodashGet(report, ['reportName'], ''); alternateText = (showChatPreviewLine && !forcePolicyNamePreview && lastMessageText) ? lastMessageText : subtitle; From 5038ade921db93a5276ef9ba96807f61c2bd6893 Mon Sep 17 00:00:00 2001 From: Amal Nazeem Date: Thu, 14 Apr 2022 15:38:58 -0400 Subject: [PATCH 021/331] Style fixes for whitespace and returntype --- src/libs/actions/Report.js | 4 ++-- src/libs/reportUtils.js | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 0a4d7ecf801e..b2df617acd64 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1565,15 +1565,15 @@ function renameReport(reportID, reportName) { * Get the title for a policyExpenseChat * @param {Object} report * @param {Object} policies must have Onyxkey prefix (i.e. 'policy_') for keys + * @returns {String} */ - function getPolicyExpenseChatTitle(report, policies) { +function getPolicyExpenseChatTitle(report, policies) { if (report.isOwnPolicyExpenseChat) { return ReportUtils.getPolicyName(report, policies); } return PersonalDetails.getDisplayName(report.ownerEmail); } - export { fetchAllReports, fetchActions, diff --git a/src/libs/reportUtils.js b/src/libs/reportUtils.js index 599371f13622..6ca7ecf24530 100644 --- a/src/libs/reportUtils.js +++ b/src/libs/reportUtils.js @@ -6,7 +6,6 @@ import ONYXKEYS from '../ONYXKEYS'; import CONST from '../CONST'; import * as Localize from './Localize'; - let sessionEmail; Onyx.connect({ key: ONYXKEYS.SESSION, From 29d6837e1d2be5dac9ec10deceb1d94bd282ef3f Mon Sep 17 00:00:00 2001 From: Mohammad Luthfi Fathur Rahman Date: Thu, 28 Apr 2022 01:27:41 +0700 Subject: [PATCH 022/331] update expensify-common --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index aff74f152689..a64603200ddc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23774,8 +23774,8 @@ } }, "expensify-common": { - "version": "git+https://github.com/Expensify/expensify-common.git#15237e012770048e8413dc08d4c36bc12304f3e8", - "from": "git+https://github.com/Expensify/expensify-common.git#15237e012770048e8413dc08d4c36bc12304f3e8", + "version": "git+https://github.com/Expensify/expensify-common.git#ba82405c287890e4e4987ed6333defc78dfcde9b", + "from": "git+https://github.com/Expensify/expensify-common.git#ba82405c287890e4e4987ed6333defc78dfcde9b", "requires": { "classnames": "2.3.1", "clipboard": "2.0.4", diff --git a/package.json b/package.json index 016aafef395c..00c370ebefc7 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "dom-serializer": "^0.2.2", "domhandler": "^4.3.0", "dotenv": "^8.2.0", - "expensify-common": "git+https://github.com/Expensify/expensify-common.git#15237e012770048e8413dc08d4c36bc12304f3e8", + "expensify-common": "git+https://github.com/Expensify/expensify-common.git#ba82405c287890e4e4987ed6333defc78dfcde9b", "fbjs": "^3.0.2", "file-loader": "^6.0.0", "html-entities": "^1.3.1", From 042bb4619b2fee2852c69b287374cdf7ffcde706 Mon Sep 17 00:00:00 2001 From: Mohammad Luthfi Fathur Rahman Date: Thu, 28 Apr 2022 01:27:57 +0700 Subject: [PATCH 023/331] fix last message text in LHN includes text and no space above quoted text --- src/libs/actions/Report.js | 14 ++++++++------ src/libs/actions/ReportActions.js | 8 +++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 37d4c54cd2a4..849da2c06d11 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -197,10 +197,9 @@ function getSimplifiedReportObject(report) { if (report.reportActionCount > 0) { // We are removing any html tags from the message html since we cannot access the text version of any comments as // the report only has the raw reportActionList and not the processed version returned by Report_GetHistory - // We convert the line-breaks in html to space ' ' before striping the tags - lastMessageText = lastActionMessage - .replace(/((]*>)+)/gi, ' ') - .replace(/(<([^>]+)>)/gi, ''); + // We convert the line-breaks and blockquote closing tag in html to space ' ' before striping the tags + const parser = new ExpensiMark(); + lastMessageText = parser.htmlToText(lastActionMessage); lastMessageText = ReportUtils.formatReportLastMessageText(lastMessageText); } @@ -600,11 +599,14 @@ function updateReportWithNewAction( setLocalLastRead(reportID, newMaxSequenceNumber); } - let messageText = lodashGet(reportAction, ['message', 0, 'text'], ''); + let messageText = lodashGet(reportAction, ['message', 0, 'html'], ''); if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.RENAMED) { messageText = lodashGet(reportAction, 'originalMessage.html', ''); } + const parser = new ExpensiMark(); + messageText = parser.htmlToText(messageText); + // Always merge the reportID into Onyx // If the report doesn't exist in Onyx yet, then all the rest of the data will be filled out // by handleReportChanged @@ -1093,7 +1095,7 @@ function addAction(reportID, text, file) { // Remove HTML from text when applying optimistic offline comment const textForNewComment = isAttachment ? '[Attachment]' - : htmlForNewComment.replace(/((]*>)+)/gi, ' ').replace(/<[^>]*>?/gm, ''); + : parser.htmlToText(htmlForNewComment); // Update the report in Onyx to have the new sequence number Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { diff --git a/src/libs/actions/ReportActions.js b/src/libs/actions/ReportActions.js index 780fe2f79e4e..90fd926af7fd 100644 --- a/src/libs/actions/ReportActions.js +++ b/src/libs/actions/ReportActions.js @@ -1,6 +1,7 @@ import _ from 'underscore'; import Onyx from 'react-native-onyx'; import lodashGet from 'lodash/get'; +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import ONYXKEYS from '../../ONYXKEYS'; import * as CollectionUtils from '../CollectionUtils'; import CONST from '../../CONST'; @@ -102,13 +103,14 @@ function getDeletedCommentsCount(reportID, sequenceNumber) { * @return {String} */ function getLastVisibleMessageText(reportID) { + const parser = new ExpensiMark(); const lastMessageIndex = _.findLastIndex(reportActions[reportID], action => ( !ReportUtils.isDeletedAction(action) )); + const htmlText = lodashGet(reportActions, [reportID, lastMessageIndex, 'message', 0, 'html'], ''); + const messageText = parser.htmlToText(htmlText); - return ReportUtils.formatReportLastMessageText( - lodashGet(reportActions, [reportID, lastMessageIndex, 'message', 0, 'text'], ''), - ); + return ReportUtils.formatReportLastMessageText(messageText); } export { From 37dfb3bf0ea26988478529ec1484341c5b296a73 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Thu, 28 Apr 2022 11:42:35 -0700 Subject: [PATCH 024/331] Remove beta check on reimbursement account page Everyone is allowed to view this page so don't navigate away if the betas haven't loaded yet --- .../ReimbursementAccountPage.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js index b5ef196ff100..60107f7926fa 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js @@ -5,12 +5,10 @@ import {withOnyx} from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; import {View} from 'react-native'; import PropTypes from 'prop-types'; -import Log from '../../libs/Log'; import ScreenWrapper from '../../components/ScreenWrapper'; import * as BankAccounts from '../../libs/actions/BankAccounts'; import ONYXKEYS from '../../ONYXKEYS'; import ReimbursementAccountLoadingIndicator from '../../components/ReimbursementAccountLoadingIndicator'; -import Permissions from '../../libs/Permissions'; import Navigation from '../../libs/Navigation/Navigation'; import CONST from '../../CONST'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; @@ -33,9 +31,6 @@ import reimbursementAccountPropTypes from './reimbursementAccountPropTypes'; import WorkspaceResetBankAccountModal from '../workspace/WorkspaceResetBankAccountModal'; const propTypes = { - /** List of betas */ - betas: PropTypes.arrayOf(PropTypes.string).isRequired, - /** ACH data for the withdrawal account actively being set up */ reimbursementAccount: reimbursementAccountPropTypes, @@ -145,12 +140,6 @@ class ReimbursementAccountPage extends React.Component { } render() { - if (!Permissions.canUseFreePlan(this.props.betas)) { - Log.info('Not showing new bank account page because user is not on free plan beta'); - Navigation.dismissModal(); - return null; - } - // The SetupWithdrawalAccount flow allows us to continue the flow from various points depending on where the // user left off. This view will refer to the achData as the single source of truth to determine which route to // display. We can also specify a specific route to navigate to via route params when the component first @@ -250,9 +239,6 @@ export default compose( session: { key: ONYXKEYS.SESSION, }, - betas: { - key: ONYXKEYS.BETAS, - }, plaidLinkToken: { key: ONYXKEYS.PLAID_LINK_TOKEN, }, From f5ac630d21f34e8231faba8acd9480e009ba3081 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Thu, 28 Apr 2022 11:45:01 -0700 Subject: [PATCH 025/331] Don't wait for betas before navigating --- src/pages/LogInWithShortLivedTokenPage.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index dbb5763ec2a2..b44d11f21b7e 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -10,9 +10,6 @@ import Navigation from '../libs/Navigation/Navigation'; import Log from '../libs/Log'; const propTypes = { - /** List of betas available to current user */ - betas: PropTypes.arrayOf(PropTypes.string), - /** The parameters needed to authenticate with a short lived token are in the URL */ route: PropTypes.shape({ /** The name of the route */ @@ -45,7 +42,6 @@ const propTypes = { }; const defaultProps = { - betas: null, route: { params: {}, }, @@ -85,11 +81,6 @@ class LogInWithShortLivedTokenPage extends Component { } navigateToExitRoute() { - if (!this.props.betas) { - // Wait to navigate until the betas are loaded. Some pages like ReimbursementAccountPage require betas, so keep loading until they are available. - return; - } - // exitTo is URI encoded because it could contain a variable number of slashes (i.e. "workspace/new" vs "workspace//card") const exitTo = decodeURIComponent(lodashGet(this.props.route.params, 'exitTo', '')); @@ -129,9 +120,6 @@ LogInWithShortLivedTokenPage.propTypes = propTypes; LogInWithShortLivedTokenPage.defaultProps = defaultProps; export default withOnyx({ - betas: { - key: ONYXKEYS.BETAS, - }, session: { key: ONYXKEYS.SESSION, }, From 1ae8690f8cb06071e09a29acc34fe64b88539b03 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 29 Apr 2022 15:38:16 -0700 Subject: [PATCH 026/331] Separate transition page to log out the old user --- src/pages/LogOutOldUserPage.js | 70 ++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/pages/LogOutOldUserPage.js diff --git a/src/pages/LogOutOldUserPage.js b/src/pages/LogOutOldUserPage.js new file mode 100644 index 000000000000..0189df47f935 --- /dev/null +++ b/src/pages/LogOutOldUserPage.js @@ -0,0 +1,70 @@ +import React, {Component} from 'react'; +import lodashGet from 'lodash/get'; +import PropTypes from 'prop-types'; +import {withOnyx} from 'react-native-onyx'; +import ONYXKEYS from '../ONYXKEYS'; +import * as Session from '../libs/actions/Session'; +import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; +import Log from '../libs/Log'; + +const propTypes = { + /** The parameters needed to authenticate with a short lived token are in the URL */ + route: PropTypes.shape({ + /** The name of the route */ + name: PropTypes.string, + + /** Unique key associated with the route */ + key: PropTypes.string, + + /** Each parameter passed via the URL */ + params: PropTypes.shape({ + /** AccountID associated with the validation link */ + accountID: PropTypes.string, + + /** Short lived token */ + shortLivedToken: PropTypes.string, + + /** URL to exit to */ + exitTo: PropTypes.string, + }), + }), + + /** The data about the current session which will be set once the user is authenticated and we return to this component as an AuthScreen */ + session: PropTypes.shape({ + /** The authToken for the current session */ + authToken: PropTypes.string, + + /** The authToken for the current session */ + email: PropTypes.string, + }), +}; + +const defaultProps = { + route: { + params: {}, + }, + session: {}, +}; + +class LogOutOldUserPage extends Component { + componentDidMount() { + const email = lodashGet(this.props.route.params, 'email', ''); + if (this.props.session && this.props.session.email !== email) { + Log.info('[LogOutOldUserPage] Different user signed in - signing out'); + Session.signOutAndRedirectToSignIn(); + } + } + + render() { + return ; + } +} + +LogOutOldUserPage.propTypes = propTypes; +LogOutOldUserPage.defaultProps = defaultProps; + +export default withOnyx({ + session: { + key: ONYXKEYS.SESSION, + }, +})(LogOutOldUserPage); From 732517db64b702706d944b04a83773e9b3436b00 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 29 Apr 2022 15:43:01 -0700 Subject: [PATCH 027/331] Only log in the transitioning user --- src/pages/LogInWithShortLivedTokenPage.js | 80 ++--------------------- 1 file changed, 4 insertions(+), 76 deletions(-) diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index b44d11f21b7e..60648e934814 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -1,12 +1,8 @@ import React, {Component} from 'react'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import {withOnyx} from 'react-native-onyx'; -import ROUTES from '../ROUTES'; -import ONYXKEYS from '../ONYXKEYS'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; -import Navigation from '../libs/Navigation/Navigation'; import Log from '../libs/Log'; const propTypes = { @@ -30,85 +26,21 @@ const propTypes = { exitTo: PropTypes.string, }), }), - - /** The data about the current session which will be set once the user is authenticated and we return to this component as an AuthScreen */ - session: PropTypes.shape({ - /** The authToken for the current session */ - authToken: PropTypes.string, - - /** The authToken for the current session */ - email: PropTypes.string, - }), }; const defaultProps = { route: { params: {}, }, - session: {}, }; class LogInWithShortLivedTokenPage extends Component { componentDidMount() { + const accountID = lodashGet(this.props.route.params, 'accountID', ''); const email = lodashGet(this.props.route.params, 'email', ''); const shortLivedToken = lodashGet(this.props.route.params, 'shortLivedToken', ''); - - const isUserSignedIn = this.props.session && this.props.session.authToken; - if (!isUserSignedIn) { - Log.info('[LoginWithShortLivedTokenPage] User not signed in - signing in with short lived token'); - Session.signInWithShortLivedToken(email, shortLivedToken); - return; - } - - if (this.signOutIfNeeded(email)) { - return; - } - - Log.info('[LoginWithShortLivedTokenPage] User is signed in'); - - // exitTo is URI encoded because it could contain a variable number of slashes (i.e. "workspace/new" vs "workspace//card") - const exitTo = decodeURIComponent(lodashGet(this.props.route.params, 'exitTo', '')); - if (exitTo === ROUTES.WORKSPACE_NEW) { - // New workspace creation is handled in AuthScreens, not in its own screen - Log.info('[LoginWithShortLivedTokenPage] exitTo is workspace/new - handling new workspace creation in AuthScreens'); - return; - } - this.navigateToExitRoute(); - } - - componentDidUpdate() { - this.navigateToExitRoute(); - } - - navigateToExitRoute() { - // exitTo is URI encoded because it could contain a variable number of slashes (i.e. "workspace/new" vs "workspace//card") - const exitTo = decodeURIComponent(lodashGet(this.props.route.params, 'exitTo', '')); - - // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, - // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` - // and by calling dismissModal(), the /transition/... route is removed from history so the user will get taken to `/` if they cancel out of the new workspace modal. - Log.info('[LoginWithShortLivedTokenPage] Dismissing LoginWithShortLivedTokenPage and navigating to exitTo'); - Navigation.dismissModal(); - Navigation.navigate(exitTo); - } - - /** - * If the user is trying to transition with a different account than the one - * they are currently signed in as we will sign them out, clear Onyx, - * and cancel all network requests. This component will mount again from - * PublicScreens and since they are no longer signed in, a request will be - * made to sign them in with their new account. - * @param {String} email The user's email passed as a route param. - * @returns {Boolean} - */ - signOutIfNeeded(email) { - if (this.props.session && this.props.session.email === email) { - return false; - } - - Log.info('[LoginWithShortLivedTokenPage] Different user signed in - signing out'); - Session.signOutAndRedirectToSignIn(); - return true; + Log.info('[LoginWithShortLivedTokenPage] signing in the transitioning user'); + Session.signInWithShortLivedToken(accountID, email, shortLivedToken); } render() { @@ -119,8 +51,4 @@ class LogInWithShortLivedTokenPage extends Component { LogInWithShortLivedTokenPage.propTypes = propTypes; LogInWithShortLivedTokenPage.defaultProps = defaultProps; -export default withOnyx({ - session: { - key: ONYXKEYS.SESSION, - }, -})(LogInWithShortLivedTokenPage); +export default LogInWithShortLivedTokenPage; From c4a6a67fefe74291e438d64cc7c48928fab9c8d6 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 29 Apr 2022 16:19:03 -0700 Subject: [PATCH 028/331] Separate transition pages for the transition route --- src/ROUTES.js | 2 +- src/SCREENS.js | 2 +- src/libs/Navigation/AppNavigator/AuthScreens.js | 8 ++++---- src/libs/Navigation/AppNavigator/PublicScreens.js | 2 +- src/libs/Navigation/linkingConfig.js | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ROUTES.js b/src/ROUTES.js index 54b27b273347..84be0d68aa7f 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -81,7 +81,7 @@ export default { getReportDetailsRoute: reportID => `r/${reportID}/details`, REPORT_SETTINGS: 'r/:reportID/settings', getReportSettingsRoute: reportID => `r/${reportID}/settings`, - LOGIN_WITH_SHORT_LIVED_TOKEN: 'transition', + TRANSITION: 'transition', VALIDATE_LOGIN: 'v/:accountID/:validateCode', GET_ASSISTANCE: 'get-assistance/:taskID', getGetAssistanceRoute: taskID => `get-assistance/${taskID}`, diff --git a/src/SCREENS.js b/src/SCREENS.js index 7b92986721a4..3f523838220f 100644 --- a/src/SCREENS.js +++ b/src/SCREENS.js @@ -6,5 +6,5 @@ export default { HOME: 'Home', LOADING: 'Loading', REPORT: 'Report', - LOG_IN_WITH_SHORT_LIVED_TOKEN: 'LogInWithShortLivedToken', + TRANSITION: 'Transition', }; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index c64a5f790da4..cc50a7d271b3 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -38,11 +38,11 @@ import MainDrawerNavigator from './MainDrawerNavigator'; import * as ModalStackNavigators from './ModalStackNavigators'; import SCREENS from '../../../SCREENS'; import Timers from '../../Timers'; -import LogInWithShortLivedTokenPage from '../../../pages/LogInWithShortLivedTokenPage'; import ValidateLoginPage from '../../../pages/ValidateLoginPage'; import defaultScreenOptions from './defaultScreenOptions'; import * as App from '../../actions/App'; import * as Session from '../../actions/Session'; +import LogOutOldUserPage from '../../../pages/LogOutOldUserPage'; Onyx.connect({ key: ONYXKEYS.MY_PERSONAL_DETAILS, @@ -198,7 +198,7 @@ class AuthScreens extends React.Component { const email = params.get('email'); const isLoggingInAsNewUser = !_.isNull(this.props.session.email) && (email !== this.props.session.email); return !isLoggingInAsNewUser - && Str.startsWith(path, Str.normalizeUrl(ROUTES.LOGIN_WITH_SHORT_LIVED_TOKEN)) + && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) && exitTo === ROUTES.WORKSPACE_NEW; } @@ -257,9 +257,9 @@ class AuthScreens extends React.Component { component={ValidateLoginPage} /> {/* These are the various modal routes */} diff --git a/src/libs/Navigation/AppNavigator/PublicScreens.js b/src/libs/Navigation/AppNavigator/PublicScreens.js index 2db9f91c47d2..f7094377268d 100644 --- a/src/libs/Navigation/AppNavigator/PublicScreens.js +++ b/src/libs/Navigation/AppNavigator/PublicScreens.js @@ -17,7 +17,7 @@ const PublicScreens = () => ( component={SignInPage} /> diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 5b781d5b6477..bab85efec766 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -27,7 +27,7 @@ export default { // Main Routes SetPassword: ROUTES.SET_PASSWORD_WITH_VALIDATE_CODE, ValidateLogin: ROUTES.VALIDATE_LOGIN, - [SCREENS.LOG_IN_WITH_SHORT_LIVED_TOKEN]: ROUTES.LOGIN_WITH_SHORT_LIVED_TOKEN, + [SCREENS.TRANSITION]: ROUTES.TRANSITION, // Modal Screens Settings: { From 908a938a2f3a0eb4791e5044f8e7c4c17a83eca6 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 29 Apr 2022 17:12:28 -0700 Subject: [PATCH 029/331] Navigate to transition exit from the auth screens --- .../Navigation/AppNavigator/AuthScreens.js | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index cc50a7d271b3..678fd9f0326c 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -30,6 +30,7 @@ import * as Policy from '../../actions/Policy'; import modalCardStyleInterpolator from './modalCardStyleInterpolator'; import createCustomModalStackNavigator from './createCustomModalStackNavigator'; import * as BankAccounts from '../../actions/BankAccounts'; +import Log from '../../Log'; // Main drawer navigator import MainDrawerNavigator from './MainDrawerNavigator'; @@ -131,12 +132,25 @@ class AuthScreens extends React.Component { // Load policies, maybe creating a new policy first. Linking.getInitialURL() .then((url) => { - if (this.shouldCreateFreePolicy(url)) { + if (!url) { + return; + } + const path = new URL(url).pathname; + const params = new URLSearchParams(url); + const exitTo = params.get('exitTo'); + const email = params.get('email'); + const isLoggingInAsNewUser = this.props.session && this.props.session.email !== email; + const shouldCreateFreePolicy = !isLoggingInAsNewUser + && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) + && exitTo === ROUTES.WORKSPACE_NEW; + if (shouldCreateFreePolicy) { Policy.createAndGetPolicyList(); return; } - Policy.getPolicyList(); + if (!isLoggingInAsNewUser && exitTo) { + this.navigateToExitRoute(exitTo); + } }); // Refresh the personal details, timezone and betas every 30 minutes @@ -184,22 +198,17 @@ class AuthScreens extends React.Component { } /** - * @param {String} [url] - * @returns {Boolean} + * Navigate to the transition exit route + * + * @param {String} exitTo */ - shouldCreateFreePolicy(url = '') { - if (!url) { - return false; - } - - const path = new URL(url).pathname; - const params = new URLSearchParams(url); - const exitTo = params.get('exitTo'); - const email = params.get('email'); - const isLoggingInAsNewUser = !_.isNull(this.props.session.email) && (email !== this.props.session.email); - return !isLoggingInAsNewUser - && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) - && exitTo === ROUTES.WORKSPACE_NEW; + navigateToExitRoute(exitTo) { + // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, + // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` + // and by calling dismissModal(), the /transition/... route is removed from history so the user will get taken to `/` if they cancel out of the new workspace modal. + Log.info('[AuthScreens] Dismissing LogOutOldUserPage and navigating to the transition exit route'); + Navigation.dismissModal(); + Navigation.navigate(exitTo); } render() { From 4c65e1edd803d1d40e0a16c3c52dc51ead31d51f Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 2 May 2022 09:39:14 -0700 Subject: [PATCH 030/331] Remove extra param from signInWithShortLivedToken --- src/pages/LogInWithShortLivedTokenPage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index 60648e934814..4b4e778d8b38 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -36,11 +36,10 @@ const defaultProps = { class LogInWithShortLivedTokenPage extends Component { componentDidMount() { - const accountID = lodashGet(this.props.route.params, 'accountID', ''); const email = lodashGet(this.props.route.params, 'email', ''); const shortLivedToken = lodashGet(this.props.route.params, 'shortLivedToken', ''); Log.info('[LoginWithShortLivedTokenPage] signing in the transitioning user'); - Session.signInWithShortLivedToken(accountID, email, shortLivedToken); + Session.signInWithShortLivedToken(email, shortLivedToken); } render() { From d1800a56659a62b3cb6c93c2fb68be4aa4e9fbb1 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 2 May 2022 13:05:32 -0700 Subject: [PATCH 031/331] Navigate in transition page so navigation is ready --- .../Navigation/AppNavigator/AuthScreens.js | 42 ++++++++----------- src/pages/LogOutOldUserPage.js | 18 ++++++++ 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 678fd9f0326c..b63e0b8dd29f 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -132,25 +132,12 @@ class AuthScreens extends React.Component { // Load policies, maybe creating a new policy first. Linking.getInitialURL() .then((url) => { - if (!url) { - return; - } - const path = new URL(url).pathname; - const params = new URLSearchParams(url); - const exitTo = params.get('exitTo'); - const email = params.get('email'); - const isLoggingInAsNewUser = this.props.session && this.props.session.email !== email; - const shouldCreateFreePolicy = !isLoggingInAsNewUser - && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) - && exitTo === ROUTES.WORKSPACE_NEW; - if (shouldCreateFreePolicy) { + if (this.shouldCreateFreePolicy(url)) { Policy.createAndGetPolicyList(); return; } + Policy.getPolicyList(); - if (!isLoggingInAsNewUser && exitTo) { - this.navigateToExitRoute(exitTo); - } }); // Refresh the personal details, timezone and betas every 30 minutes @@ -198,17 +185,22 @@ class AuthScreens extends React.Component { } /** - * Navigate to the transition exit route - * - * @param {String} exitTo + * @param {String} [url] + * @returns {Boolean} */ - navigateToExitRoute(exitTo) { - // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, - // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` - // and by calling dismissModal(), the /transition/... route is removed from history so the user will get taken to `/` if they cancel out of the new workspace modal. - Log.info('[AuthScreens] Dismissing LogOutOldUserPage and navigating to the transition exit route'); - Navigation.dismissModal(); - Navigation.navigate(exitTo); + shouldCreateFreePolicy(url = '') { + if (!url) { + return false; + } + + const path = new URL(url).pathname; + const params = new URLSearchParams(url); + const exitTo = params.get('exitTo'); + const email = params.get('email'); + const isLoggingInAsNewUser = !_.isNull(this.props.session.email) && (email !== this.props.session.email); + return !isLoggingInAsNewUser + && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) + && exitTo === ROUTES.WORKSPACE_NEW; } render() { diff --git a/src/pages/LogOutOldUserPage.js b/src/pages/LogOutOldUserPage.js index 0189df47f935..a031c73edce0 100644 --- a/src/pages/LogOutOldUserPage.js +++ b/src/pages/LogOutOldUserPage.js @@ -6,6 +6,8 @@ import ONYXKEYS from '../ONYXKEYS'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; import Log from '../libs/Log'; +import Navigation from '../libs/Navigation/Navigation'; +import ROUTES from '../ROUTES'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ @@ -52,7 +54,23 @@ class LogOutOldUserPage extends Component { if (this.props.session && this.props.session.email !== email) { Log.info('[LogOutOldUserPage] Different user signed in - signing out'); Session.signOutAndRedirectToSignIn(); + return; } + + // exitTo is URI encoded because it could contain a variable number of slashes (i.e. "workspace/new" vs "workspace//card") + const exitTo = decodeURIComponent(lodashGet(this.props.route.params, 'exitTo', '')); + if (exitTo === ROUTES.WORKSPACE_NEW) { + // New workspace creation is handled in AuthScreens, not in its own screen + Log.info('[LoginWithShortLivedTokenPage] exitTo is workspace/new - handling new workspace creation in AuthScreens'); + return; + } + + // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, + // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` + // and by calling dismissModal(), the /transition/... route is removed from history so the user will get taken to `/` if they cancel out of the new workspace modal. + Log.info('[LoginWithShortLivedTokenPage] Dismissing LoginWithShortLivedTokenPage and navigating to exitTo'); + Navigation.dismissModal(); + Navigation.navigate(exitTo); } render() { From 9ac316a7d8a6e6b96cd9e24414071ffd97481873 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 2 May 2022 15:04:25 -0700 Subject: [PATCH 032/331] Simplify the comment about dismissing the modal --- src/pages/LogOutOldUserPage.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/LogOutOldUserPage.js b/src/pages/LogOutOldUserPage.js index a031c73edce0..2626948d86c6 100644 --- a/src/pages/LogOutOldUserPage.js +++ b/src/pages/LogOutOldUserPage.js @@ -65,9 +65,7 @@ class LogOutOldUserPage extends Component { return; } - // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, - // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` - // and by calling dismissModal(), the /transition/... route is removed from history so the user will get taken to `/` if they cancel out of the new workspace modal. + // We must call dismissModal() to remove the /transition route from history Log.info('[LoginWithShortLivedTokenPage] Dismissing LoginWithShortLivedTokenPage and navigating to exitTo'); Navigation.dismissModal(); Navigation.navigate(exitTo); From e6478682f5ff0295a67a33739f45e9b00be665cb Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 2 May 2022 15:38:25 -0700 Subject: [PATCH 033/331] Revert "Navigate in transition page so navigation is ready" This reverts commit d1800a56659a62b3cb6c93c2fb68be4aa4e9fbb1. --- .../Navigation/AppNavigator/AuthScreens.js | 42 +++++++++++-------- src/pages/LogOutOldUserPage.js | 16 ------- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index b63e0b8dd29f..678fd9f0326c 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -132,12 +132,25 @@ class AuthScreens extends React.Component { // Load policies, maybe creating a new policy first. Linking.getInitialURL() .then((url) => { - if (this.shouldCreateFreePolicy(url)) { + if (!url) { + return; + } + const path = new URL(url).pathname; + const params = new URLSearchParams(url); + const exitTo = params.get('exitTo'); + const email = params.get('email'); + const isLoggingInAsNewUser = this.props.session && this.props.session.email !== email; + const shouldCreateFreePolicy = !isLoggingInAsNewUser + && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) + && exitTo === ROUTES.WORKSPACE_NEW; + if (shouldCreateFreePolicy) { Policy.createAndGetPolicyList(); return; } - Policy.getPolicyList(); + if (!isLoggingInAsNewUser && exitTo) { + this.navigateToExitRoute(exitTo); + } }); // Refresh the personal details, timezone and betas every 30 minutes @@ -185,22 +198,17 @@ class AuthScreens extends React.Component { } /** - * @param {String} [url] - * @returns {Boolean} + * Navigate to the transition exit route + * + * @param {String} exitTo */ - shouldCreateFreePolicy(url = '') { - if (!url) { - return false; - } - - const path = new URL(url).pathname; - const params = new URLSearchParams(url); - const exitTo = params.get('exitTo'); - const email = params.get('email'); - const isLoggingInAsNewUser = !_.isNull(this.props.session.email) && (email !== this.props.session.email); - return !isLoggingInAsNewUser - && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) - && exitTo === ROUTES.WORKSPACE_NEW; + navigateToExitRoute(exitTo) { + // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, + // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` + // and by calling dismissModal(), the /transition/... route is removed from history so the user will get taken to `/` if they cancel out of the new workspace modal. + Log.info('[AuthScreens] Dismissing LogOutOldUserPage and navigating to the transition exit route'); + Navigation.dismissModal(); + Navigation.navigate(exitTo); } render() { diff --git a/src/pages/LogOutOldUserPage.js b/src/pages/LogOutOldUserPage.js index 2626948d86c6..0189df47f935 100644 --- a/src/pages/LogOutOldUserPage.js +++ b/src/pages/LogOutOldUserPage.js @@ -6,8 +6,6 @@ import ONYXKEYS from '../ONYXKEYS'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; import Log from '../libs/Log'; -import Navigation from '../libs/Navigation/Navigation'; -import ROUTES from '../ROUTES'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ @@ -54,21 +52,7 @@ class LogOutOldUserPage extends Component { if (this.props.session && this.props.session.email !== email) { Log.info('[LogOutOldUserPage] Different user signed in - signing out'); Session.signOutAndRedirectToSignIn(); - return; } - - // exitTo is URI encoded because it could contain a variable number of slashes (i.e. "workspace/new" vs "workspace//card") - const exitTo = decodeURIComponent(lodashGet(this.props.route.params, 'exitTo', '')); - if (exitTo === ROUTES.WORKSPACE_NEW) { - // New workspace creation is handled in AuthScreens, not in its own screen - Log.info('[LoginWithShortLivedTokenPage] exitTo is workspace/new - handling new workspace creation in AuthScreens'); - return; - } - - // We must call dismissModal() to remove the /transition route from history - Log.info('[LoginWithShortLivedTokenPage] Dismissing LoginWithShortLivedTokenPage and navigating to exitTo'); - Navigation.dismissModal(); - Navigation.navigate(exitTo); } render() { From 019dba97a548382c6b7a7317a4f8cb785dd46df7 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 2 May 2022 16:21:38 -0700 Subject: [PATCH 034/331] Navigate in AuthScreens when navigation is ready --- .../Navigation/AppNavigator/AuthScreens.js | 30 ++++++++++--------- src/libs/Navigation/Navigation.js | 5 ++++ src/pages/LogOutOldUserPage.js | 4 +++ 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 678fd9f0326c..1e2c3d678af6 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -135,22 +135,24 @@ class AuthScreens extends React.Component { if (!url) { return; } - const path = new URL(url).pathname; - const params = new URLSearchParams(url); - const exitTo = params.get('exitTo'); - const email = params.get('email'); - const isLoggingInAsNewUser = this.props.session && this.props.session.email !== email; - const shouldCreateFreePolicy = !isLoggingInAsNewUser + Navigation.isNavigationReady().then(() => { + const path = new URL(url).pathname; + const params = new URLSearchParams(url); + const exitTo = params.get('exitTo'); + const email = params.get('email'); + const isLoggingInAsNewUser = this.props.session && this.props.session.email !== email; + const shouldCreateFreePolicy = !isLoggingInAsNewUser && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) && exitTo === ROUTES.WORKSPACE_NEW; - if (shouldCreateFreePolicy) { - Policy.createAndGetPolicyList(); - return; - } - Policy.getPolicyList(); - if (!isLoggingInAsNewUser && exitTo) { - this.navigateToExitRoute(exitTo); - } + if (shouldCreateFreePolicy) { + Policy.createAndGetPolicyList(); + return; + } + Policy.getPolicyList(); + if (!isLoggingInAsNewUser && exitTo) { + this.navigateToExitRoute(exitTo); + } + }); }); // Refresh the personal details, timezone and betas every 30 minutes diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index 360afa67afc9..143e59c4eb1c 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -11,6 +11,9 @@ import CustomActions from './CustomActions'; import ONYXKEYS from '../../ONYXKEYS'; import linkingConfig from './linkingConfig'; import navigationRef from './navigationRef'; +import createOnReadyTask from '../createOnReadyTask'; + +const [isNavigationReady, setIsNavigationReady] = createOnReadyTask(); let isLoggedIn = false; Onyx.connect({ @@ -227,6 +230,8 @@ export default { closeDrawer, getDefaultDrawerState, setDidTapNotification, + isNavigationReady, + setIsNavigationReady, }; export { diff --git a/src/pages/LogOutOldUserPage.js b/src/pages/LogOutOldUserPage.js index 0189df47f935..2013f89bc0ab 100644 --- a/src/pages/LogOutOldUserPage.js +++ b/src/pages/LogOutOldUserPage.js @@ -6,6 +6,7 @@ import ONYXKEYS from '../ONYXKEYS'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; import Log from '../libs/Log'; +import Navigation from '../libs/Navigation/Navigation'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ @@ -53,6 +54,9 @@ class LogOutOldUserPage extends Component { Log.info('[LogOutOldUserPage] Different user signed in - signing out'); Session.signOutAndRedirectToSignIn(); } + + // Set isNavigationReady so that we can navigate in the AuthScreens + Navigation.setIsNavigationReady(); } render() { From 56a79d29120ffa19d27a64fee9972d73973f1baf Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 21 Mar 2022 19:55:32 -0700 Subject: [PATCH 035/331] Fix create login requests before Network is ready --- src/libs/actions/Session/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index a38d5d9a25e0..c5cc82e6e1da 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -170,9 +170,10 @@ function fetchAccountDetails(login) { * * @param {String} authToken * @param {String} email + * @param {Boolean} shouldProcessImmediately * @return {Promise} */ -function createTemporaryLogin(authToken, email) { +function createTemporaryLogin(authToken, email, shouldProcessImmediately = true) { const autoGeneratedLogin = Str.guid('expensify.cash-'); const autoGeneratedPassword = Str.guid(); @@ -184,6 +185,7 @@ function createTemporaryLogin(authToken, email) { partnerUserSecret: autoGeneratedPassword, shouldRetry: false, forceNetworkRequest: true, + shouldProcessImmediately, email, includeEncryptedAuthToken: true, }) @@ -262,7 +264,7 @@ function signIn(password, twoFactorAuthCode) { function signInWithShortLivedToken(email, shortLivedToken) { Onyx.merge(ONYXKEYS.ACCOUNT, {...CONST.DEFAULT_ACCOUNT_DATA, loading: true}); - createTemporaryLogin(shortLivedToken, email).then((response) => { + createTemporaryLogin(shortLivedToken, email, false).then((response) => { if (response.jsonCode === 200) { User.getUserDetails(); Onyx.merge(ONYXKEYS.ACCOUNT, {success: true}); From 4fb557ff6f3407c62143c0bf1fbe9c9451fd8932 Mon Sep 17 00:00:00 2001 From: sahil Date: Thu, 5 May 2022 02:58:10 +0530 Subject: [PATCH 036/331] add minimum width --- src/components/ReportTransaction.js | 2 +- src/styles/styles.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/ReportTransaction.js b/src/components/ReportTransaction.js index 599fa482c4b0..8572270e9a13 100644 --- a/src/components/ReportTransaction.js +++ b/src/components/ReportTransaction.js @@ -93,7 +93,7 @@ class ReportTransaction extends Component { styles.buttonSmall, styles.chatItemComposeSecondaryRowOffset, styles.mb3, - this.isBeingRejected() ? styles.w20 : styles.wAuto, + styles.iouRejectButton, ]} onPress={this.rejectTransaction} > diff --git a/src/styles/styles.js b/src/styles/styles.js index d022567aa233..b074dbbe361b 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -2519,6 +2519,10 @@ const styles = { marginBottom: 40, padding: 16, }, + + iouRejectButton: { + minWidth: 69, + }, }; export default styles; From 646deb171e82a06ae5fd468334e067ab491ac52c Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Fri, 6 May 2022 01:24:04 +0500 Subject: [PATCH 037/331] fix: incorporated the new image params --- .../HTMLRenderers/ImageRenderer.js | 5 ++++ src/components/ThumbnailImage.js | 17 ++++++++--- .../home/report/ReportActionItemFragment.js | 29 +++++++------------ 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js index 49e2e72829d4..97577b9eaea2 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js @@ -43,6 +43,10 @@ const ImageRenderer = (props) => { Config.EXPENSIFY.EXPENSIFY_URL, Config.EXPENSIFY.URL_API_ROOT, ); + const imageSize = { + width: htmlAttribs['data-expensify-width'] ? parseInt(htmlAttribs['data-expensify-width'], 10) : undefined, + height: htmlAttribs['data-expensify-height'] ? parseInt(htmlAttribs['data-expensify-height'], 10) : undefined, + }; return ( { previewSourceURL={previewSource} style={styles.webViewStyles.tagStyles.img} isAuthTokenRequired={isAttachment} + imageSize={imageSize} /> )} diff --git a/src/components/ThumbnailImage.js b/src/components/ThumbnailImage.js index 9d15be907014..7051491a7579 100644 --- a/src/components/ThumbnailImage.js +++ b/src/components/ThumbnailImage.js @@ -19,11 +19,14 @@ const propTypes = { /** Do the urls require an authToken? */ isAuthTokenRequired: PropTypes.bool.isRequired, + imageSize: PropTypes.objectOf(PropTypes.number), + ...windowDimensionsPropTypes, }; const defaultProps = { style: {}, + imageSize: {width: 200, height: 200}, }; class ThumbnailImage extends PureComponent { @@ -31,14 +34,16 @@ class ThumbnailImage extends PureComponent { super(props); this.updateImageSize = this.updateImageSize.bind(this); - + const {width, height} = this.calculateThumbnailImageSize(props.imageSize); this.state = { - thumbnailWidth: 200, - thumbnailHeight: 200, + thumbnailWidth: width || defaultProps.imageSize.width, + thumbnailHeight: height || defaultProps.imageSize.height, }; } - updateImageSize({width, height}) { + calculateThumbnailImageSize({width, height}) { + if (!width || !height) { return {}; } + // Width of the thumbnail works better as a constant than it does // a percentage of the screen width since it is relative to each screen // Note: Clamp minimum width 40px to support touch device @@ -54,7 +59,11 @@ class ThumbnailImage extends PureComponent { } else { thumbnailScreenHeight = Math.round(thumbnailScreenWidth * aspectRatio); } + return {width: thumbnailScreenWidth, height: thumbnailScreenHeight}; + } + updateImageSize({width, height}) { + const {width: thumbnailScreenWidth, height: thumbnailScreenHeight} = this.calculateThumbnailImageSize({width, height}); this.setState({thumbnailWidth: thumbnailScreenWidth, thumbnailHeight: Math.max(40, thumbnailScreenHeight)}); } diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index 194a865636f7..fac432c74a23 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -1,5 +1,5 @@ import React, {memo} from 'react'; -import {ActivityIndicator, ImageBackground, View} from 'react-native'; +import {ActivityIndicator, View} from 'react-native'; import PropTypes from 'prop-types'; import Str from 'expensify-common/lib/str'; import reportActionFragmentPropTypes from './reportActionFragmentPropTypes'; @@ -68,32 +68,22 @@ const defaultProps = { const ReportActionItemFragment = (props) => { switch (props.fragment.type) { - case 'COMMENT': + case 'COMMENT': { // If this is an attachment placeholder, return the placeholder component if (props.isAttachment && props.loading) { return ( - - {Str.isImage(props.attachmentInfo.name) - ? ( - - - - ) : ( + Str.isImage(props.attachmentInfo.name) + ? ( + `} /> + ) : ( + - )} - + + ) ); } @@ -119,6 +109,7 @@ const ReportActionItemFragment = (props) => { )} ); + } case 'TEXT': return ( From 8a950850652e8965ce2084d6d530620676263734 Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Sat, 7 May 2022 01:38:38 +0500 Subject: [PATCH 038/331] fix: default props problem --- .../HTMLRenderers/ImageRenderer.js | 10 +++++----- src/components/ThumbnailImage.js | 13 ++++++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js index 97577b9eaea2..650e387a436b 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js @@ -43,10 +43,9 @@ const ImageRenderer = (props) => { Config.EXPENSIFY.EXPENSIFY_URL, Config.EXPENSIFY.URL_API_ROOT, ); - const imageSize = { - width: htmlAttribs['data-expensify-width'] ? parseInt(htmlAttribs['data-expensify-width'], 10) : undefined, - height: htmlAttribs['data-expensify-height'] ? parseInt(htmlAttribs['data-expensify-height'], 10) : undefined, - }; + + const imageWidth = htmlAttribs['data-expensify-width'] ? parseInt(htmlAttribs['data-expensify-width'], 10) : undefined; + const imageHeight = htmlAttribs['data-expensify-height'] ? parseInt(htmlAttribs['data-expensify-height'], 10) : undefined; return ( { previewSourceURL={previewSource} style={styles.webViewStyles.tagStyles.img} isAuthTokenRequired={isAttachment} - imageSize={imageSize} + imageWidth={imageWidth} + imageHeight={imageHeight} /> )} diff --git a/src/components/ThumbnailImage.js b/src/components/ThumbnailImage.js index 7051491a7579..4ee1417ff2b0 100644 --- a/src/components/ThumbnailImage.js +++ b/src/components/ThumbnailImage.js @@ -19,14 +19,17 @@ const propTypes = { /** Do the urls require an authToken? */ isAuthTokenRequired: PropTypes.bool.isRequired, - imageSize: PropTypes.objectOf(PropTypes.number), + /** Image size */ + imageWidth: PropTypes.number, + imageHeight: PropTypes.number, ...windowDimensionsPropTypes, }; const defaultProps = { style: {}, - imageSize: {width: 200, height: 200}, + imageWidth: 200, + imageHeight: 200, }; class ThumbnailImage extends PureComponent { @@ -34,10 +37,10 @@ class ThumbnailImage extends PureComponent { super(props); this.updateImageSize = this.updateImageSize.bind(this); - const {width, height} = this.calculateThumbnailImageSize(props.imageSize); + const {width, height} = this.calculateThumbnailImageSize({width: props.imageWidth, height: props.imageHeight}); this.state = { - thumbnailWidth: width || defaultProps.imageSize.width, - thumbnailHeight: height || defaultProps.imageSize.height, + thumbnailWidth: width, + thumbnailHeight: height, }; } From 328f385f431ba2018daf2a9d94e34d38b5cd4075 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 6 May 2022 16:28:36 -0700 Subject: [PATCH 039/331] Use the new createOnReadyTask for navigation --- src/libs/Navigation/Navigation.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index 143e59c4eb1c..fe5b1e54dbe6 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -13,7 +13,7 @@ import linkingConfig from './linkingConfig'; import navigationRef from './navigationRef'; import createOnReadyTask from '../createOnReadyTask'; -const [isNavigationReady, setIsNavigationReady] = createOnReadyTask(); +const navigationReadyTask = createOnReadyTask(); let isLoggedIn = false; Onyx.connect({ @@ -45,6 +45,7 @@ function canNavigate(methodName, params = {}) { } Log.hmmm(`[Navigation] ${methodName} failed because navigation ref was not yet ready`, params); + navigationReadyTask.reset(); return false; } @@ -191,6 +192,17 @@ function isActiveRoute(routePath) { return getActiveRoute().substring(1) === routePath; } +/** + * @returns {Boolean} isNavigationReady + */ +function isNavigationReady() { + return navigationReadyTask.isNavigationReady; +} + +function setIsNavigationReady() { + navigationReadyTask.setIsNavigationReady(); +} + /** * Alternative to the `Navigation.dismissModal()` function that we can use inside * the render function of other components to avoid breaking React rules about side-effects. From f4b40d4fcaaab9291cca539111803eebeed9e8a1 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 6 May 2022 16:33:55 -0700 Subject: [PATCH 040/331] Revert "Fix create login requests before Network is ready" This reverts commit 56a79d29120ffa19d27a64fee9972d73973f1baf. --- src/libs/actions/Session/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index 5fa77be776c9..009c32915441 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -177,10 +177,9 @@ function fetchAccountDetails(login) { * * @param {String} authToken * @param {String} email - * @param {Boolean} shouldProcessImmediately * @return {Promise} */ -function createTemporaryLogin(authToken, email, shouldProcessImmediately = true) { +function createTemporaryLogin(authToken, email) { const autoGeneratedLogin = Str.guid('expensify.cash-'); const autoGeneratedPassword = Str.guid(); @@ -192,7 +191,6 @@ function createTemporaryLogin(authToken, email, shouldProcessImmediately = true) partnerUserSecret: autoGeneratedPassword, shouldRetry: false, forceNetworkRequest: true, - shouldProcessImmediately, email, includeEncryptedAuthToken: true, }) From 8ccf60f59f20b85af5f6bce232ea0670a49fb3ea Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Fri, 6 May 2022 16:53:35 -0700 Subject: [PATCH 041/331] Call the proper navigationReadyTask methods --- src/libs/Navigation/Navigation.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index fe5b1e54dbe6..f9d9e815fcbd 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -196,11 +196,11 @@ function isActiveRoute(routePath) { * @returns {Boolean} isNavigationReady */ function isNavigationReady() { - return navigationReadyTask.isNavigationReady; + return navigationReadyTask.isReady(); } function setIsNavigationReady() { - navigationReadyTask.setIsNavigationReady(); + navigationReadyTask.setIsReady(); } /** From 60635af5db0458968f817de7eca25d13f29fdb7b Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 13:51:50 -0700 Subject: [PATCH 042/331] Fix don't show create menu on workspace/new --- src/libs/actions/Welcome.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Welcome.js b/src/libs/actions/Welcome.js index 58935bcf54bd..cd2390a216de 100644 --- a/src/libs/actions/Welcome.js +++ b/src/libs/actions/Welcome.js @@ -9,6 +9,7 @@ import ONYXKEYS from '../../ONYXKEYS'; import NameValuePair from './NameValuePair'; import CONST from '../../CONST'; import createOnReadyTask from '../createOnReadyTask'; +import SCREENS from '../../SCREENS'; const readyTask = createOnReadyTask(); @@ -109,8 +110,8 @@ function show({routes, showCreateMenu}) { // If we are rendering the SidebarScreen at the same time as a workspace route that means we've already created a workspace via workspace/new and should not open the global // create menu right now. const topRouteName = lodashGet(_.last(routes), 'name', ''); - const loginWithShortLivedTokenRoute = _.find(routes, route => route.name === 'LogInWithShortLivedToken'); - const exitingToWorkspaceRoute = lodashGet(loginWithShortLivedTokenRoute, 'params.exitTo', '') === 'workspace/new'; + const transitionRoute = _.find(routes, route => route.name === SCREENS.TRANSITION); + const exitingToWorkspaceRoute = lodashGet(transitionRoute, 'params.exitTo', '') === 'workspace/new'; const isDisplayingWorkspaceRoute = topRouteName.toLowerCase().includes('workspace') || exitingToWorkspaceRoute; // If user is not already an admin of a free policy and we are not navigating them to their workspace or creating a new workspace via workspace/new then From 96e7fd4b7aba091fec8e801ddb0d9e9f2f6177e1 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 13:57:18 -0700 Subject: [PATCH 043/331] Simplify comment and remove log lines --- src/libs/Navigation/AppNavigator/AuthScreens.js | 5 +---- src/pages/LogInWithShortLivedTokenPage.js | 2 -- src/pages/LogOutOldUserPage.js | 2 -- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 20df19f93378..b82027e14b3d 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -185,10 +185,7 @@ class AuthScreens extends React.Component { * @param {String} exitTo */ navigateToExitRoute(exitTo) { - // In order to navigate to a modal, we first have to dismiss the current modal. Without dismissing the current modal, if the user cancels out of the workspace modal, - // then they will be routed back to /transition////workspace//card and we don't want that. We want them to go back to `/` - // and by calling dismissModal(), the /transition/... route is removed from history so the user will get taken to `/` if they cancel out of the new workspace modal. - Log.info('[AuthScreens] Dismissing LogOutOldUserPage and navigating to the transition exit route'); + // We must call dismissModal() to remove the /transition route from history Navigation.dismissModal(); Navigation.navigate(exitTo); } diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index 4b4e778d8b38..4090e3bf0714 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -3,7 +3,6 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; -import Log from '../libs/Log'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ @@ -38,7 +37,6 @@ class LogInWithShortLivedTokenPage extends Component { componentDidMount() { const email = lodashGet(this.props.route.params, 'email', ''); const shortLivedToken = lodashGet(this.props.route.params, 'shortLivedToken', ''); - Log.info('[LoginWithShortLivedTokenPage] signing in the transitioning user'); Session.signInWithShortLivedToken(email, shortLivedToken); } diff --git a/src/pages/LogOutOldUserPage.js b/src/pages/LogOutOldUserPage.js index 2013f89bc0ab..1047b0d51cc1 100644 --- a/src/pages/LogOutOldUserPage.js +++ b/src/pages/LogOutOldUserPage.js @@ -5,7 +5,6 @@ import {withOnyx} from 'react-native-onyx'; import ONYXKEYS from '../ONYXKEYS'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; -import Log from '../libs/Log'; import Navigation from '../libs/Navigation/Navigation'; const propTypes = { @@ -51,7 +50,6 @@ class LogOutOldUserPage extends Component { componentDidMount() { const email = lodashGet(this.props.route.params, 'email', ''); if (this.props.session && this.props.session.email !== email) { - Log.info('[LogOutOldUserPage] Different user signed in - signing out'); Session.signOutAndRedirectToSignIn(); } From d47dce8443f8f1678e09c88c11f8d9c9646b0595 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 14:01:46 -0700 Subject: [PATCH 044/331] Rename LogOutOldUserPage to LogOutPreviousUserPage --- src/libs/Navigation/AppNavigator/AuthScreens.js | 4 ++-- .../{LogOutOldUserPage.js => LogOutPreviousUserPage.js} | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) rename src/pages/{LogOutOldUserPage.js => LogOutPreviousUserPage.js} (91%) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index b82027e14b3d..c3c070d6bf10 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -39,7 +39,7 @@ import ValidateLoginPage from '../../../pages/ValidateLoginPage'; import defaultScreenOptions from './defaultScreenOptions'; import * as App from '../../actions/App'; import * as Session from '../../actions/Session'; -import LogOutOldUserPage from '../../../pages/LogOutOldUserPage'; +import LogOutPreviousUserPage from '../../../pages/LogOutPreviousUserPage'; import networkPropTypes from '../../../components/networkPropTypes'; import {withNetwork} from '../../../components/OnyxProvider'; @@ -247,7 +247,7 @@ class AuthScreens extends React.Component { {/* These are the various modal routes */} diff --git a/src/pages/LogOutOldUserPage.js b/src/pages/LogOutPreviousUserPage.js similarity index 91% rename from src/pages/LogOutOldUserPage.js rename to src/pages/LogOutPreviousUserPage.js index 1047b0d51cc1..c1521b6da4d1 100644 --- a/src/pages/LogOutOldUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -46,7 +46,7 @@ const defaultProps = { session: {}, }; -class LogOutOldUserPage extends Component { +class LogOutPreviousUserPage extends Component { componentDidMount() { const email = lodashGet(this.props.route.params, 'email', ''); if (this.props.session && this.props.session.email !== email) { @@ -62,11 +62,11 @@ class LogOutOldUserPage extends Component { } } -LogOutOldUserPage.propTypes = propTypes; -LogOutOldUserPage.defaultProps = defaultProps; +LogOutPreviousUserPage.propTypes = propTypes; +LogOutPreviousUserPage.defaultProps = defaultProps; export default withOnyx({ session: { key: ONYXKEYS.SESSION, }, -})(LogOutOldUserPage); +})(LogOutPreviousUserPage); From 6dd45735ed97ca64a63eea6d1641e9eb5fe975e3 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 14:21:47 -0700 Subject: [PATCH 045/331] Only set navigation ready when we are logged in. --- src/pages/LogOutPreviousUserPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index c1521b6da4d1..395809641381 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -51,6 +51,7 @@ class LogOutPreviousUserPage extends Component { const email = lodashGet(this.props.route.params, 'email', ''); if (this.props.session && this.props.session.email !== email) { Session.signOutAndRedirectToSignIn(); + return; } // Set isNavigationReady so that we can navigate in the AuthScreens From b490551844d7842b29654e288d074bacd3ec227b Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 14:29:03 -0700 Subject: [PATCH 046/331] Clean up default props and their use --- src/pages/LogOutPreviousUserPage.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 395809641381..e534bba2d2c5 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -40,16 +40,15 @@ const propTypes = { }; const defaultProps = { - route: { - params: {}, - }, - session: {}, + route: null, + session: null, }; class LogOutPreviousUserPage extends Component { componentDidMount() { - const email = lodashGet(this.props.route.params, 'email', ''); - if (this.props.session && this.props.session.email !== email) { + const paramsEmail = lodashGet(this.props, 'route.params.email', null); + const sessionEmail = lodashGet(this.props.session, 'email', ''); + if (paramsEmail !== sessionEmail) { Session.signOutAndRedirectToSignIn(); return; } From 174e012b46cd09b90dc647717854f50387d8edf0 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 14:32:29 -0700 Subject: [PATCH 047/331] Fix shouldCreateFreePolicy indentation --- src/libs/Navigation/AppNavigator/AuthScreens.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index c3c070d6bf10..15996f49d30d 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -122,8 +122,8 @@ class AuthScreens extends React.Component { const email = params.get('email'); const isLoggingInAsNewUser = this.props.session && this.props.session.email !== email; const shouldCreateFreePolicy = !isLoggingInAsNewUser - && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) - && exitTo === ROUTES.WORKSPACE_NEW; + && Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION)) + && exitTo === ROUTES.WORKSPACE_NEW; if (shouldCreateFreePolicy) { Policy.createAndGetPolicyList(); return; From 607e48dfe372184a93af47fc8f628a366ab2eb3e Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 15:06:54 -0700 Subject: [PATCH 048/331] Remove unused Log import --- src/libs/Navigation/AppNavigator/AuthScreens.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 15996f49d30d..2d2c14258e41 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -26,7 +26,6 @@ import * as Modal from '../../actions/Modal'; import * as Policy from '../../actions/Policy'; import modalCardStyleInterpolator from './modalCardStyleInterpolator'; import createCustomModalStackNavigator from './createCustomModalStackNavigator'; -import Log from '../../Log'; // Main drawer navigator import MainDrawerNavigator from './MainDrawerNavigator'; From dd54853e7295bd50aa1ca79daf64dac6a225f39c Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Mon, 9 May 2022 16:08:24 -0700 Subject: [PATCH 049/331] Clean up propTypes and props more --- src/pages/LogInWithShortLivedTokenPage.js | 23 ++++++----------------- src/pages/LogOutPreviousUserPage.js | 21 +++------------------ 2 files changed, 9 insertions(+), 35 deletions(-) diff --git a/src/pages/LogInWithShortLivedTokenPage.js b/src/pages/LogInWithShortLivedTokenPage.js index 4090e3bf0714..808e76984671 100644 --- a/src/pages/LogInWithShortLivedTokenPage.js +++ b/src/pages/LogInWithShortLivedTokenPage.js @@ -7,36 +7,25 @@ import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ route: PropTypes.shape({ - /** The name of the route */ - name: PropTypes.string, - - /** Unique key associated with the route */ - key: PropTypes.string, - /** Each parameter passed via the URL */ params: PropTypes.shape({ - /** AccountID associated with the validation link */ - accountID: PropTypes.string, - - /** Short lived token */ + /** Short lived token to sign in a user */ shortLivedToken: PropTypes.string, - /** URL to exit to */ - exitTo: PropTypes.string, + /** The email of the transitioning user */ + email: PropTypes.string, }), }), }; const defaultProps = { - route: { - params: {}, - }, + route: null, }; class LogInWithShortLivedTokenPage extends Component { componentDidMount() { - const email = lodashGet(this.props.route.params, 'email', ''); - const shortLivedToken = lodashGet(this.props.route.params, 'shortLivedToken', ''); + const email = lodashGet(this.props, 'route.params.email', ''); + const shortLivedToken = lodashGet(this.props, 'route.params.shortLivedToken', ''); Session.signInWithShortLivedToken(email, shortLivedToken); } diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index e534bba2d2c5..0a4f521b3180 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -10,31 +10,16 @@ import Navigation from '../libs/Navigation/Navigation'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ route: PropTypes.shape({ - /** The name of the route */ - name: PropTypes.string, - - /** Unique key associated with the route */ - key: PropTypes.string, - /** Each parameter passed via the URL */ params: PropTypes.shape({ - /** AccountID associated with the validation link */ - accountID: PropTypes.string, - - /** Short lived token */ - shortLivedToken: PropTypes.string, - - /** URL to exit to */ - exitTo: PropTypes.string, + /** The email of the transitioning user */ + email: PropTypes.string, }), }), /** The data about the current session which will be set once the user is authenticated and we return to this component as an AuthScreen */ session: PropTypes.shape({ - /** The authToken for the current session */ - authToken: PropTypes.string, - - /** The authToken for the current session */ + /** The user's email for the current session */ email: PropTypes.string, }), }; From 671f2d206eb0f86744fb1f0aeaa0ad5718524b4b Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Tue, 10 May 2022 17:36:27 +0530 Subject: [PATCH 050/331] add comment for currencyList.json --- tests/unit/CurrencySymbolUtilsTest.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/unit/CurrencySymbolUtilsTest.js b/tests/unit/CurrencySymbolUtilsTest.js index 4f4a2994b9e7..0fa69dd7494e 100644 --- a/tests/unit/CurrencySymbolUtilsTest.js +++ b/tests/unit/CurrencySymbolUtilsTest.js @@ -1,3 +1,9 @@ +// currencyList can get outdated, you can get the latest list from server +// src/libs/API.js +// GetCurrencyList().then(data => console.log(data.currencyList)); +// it will output the json in console, you can copy it and use any external json formatter +// finally update currencyList.json + import _ from 'underscore'; import * as CurrencySymbolUtils from '../../src/libs/CurrencySymbolUtils'; import currencyList from './currencyList.json'; @@ -29,3 +35,4 @@ describe('CurrencySymbolUtils', () => { }); }); }); + From 7c4aa7e3aa647f75469ce0d572ce51f77a3d6636 Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Tue, 10 May 2022 20:53:45 +0530 Subject: [PATCH 051/331] refactor: IOUAmountPage --- src/components/AmountTextInput.js | 45 +++++++++++++++ src/components/CurrencySymbolButton.js | 23 ++++++++ src/components/TextInputWithCurrencySymbol.js | 55 +++++++++++++++++++ src/pages/iou/steps/IOUAmountPage.js | 40 ++++---------- 4 files changed, 135 insertions(+), 28 deletions(-) create mode 100644 src/components/AmountTextInput.js create mode 100644 src/components/CurrencySymbolButton.js create mode 100644 src/components/TextInputWithCurrencySymbol.js diff --git a/src/components/AmountTextInput.js b/src/components/AmountTextInput.js new file mode 100644 index 000000000000..304e79d0bfec --- /dev/null +++ b/src/components/AmountTextInput.js @@ -0,0 +1,45 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import TextInput from './TextInput'; +import styles from '../styles/styles'; +import CONST from '../CONST'; + +const propTypes = { + formattedAmount: PropTypes.string.isRequired, + forwardedRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({current: PropTypes.instanceOf(React.Component)}), + ]), + onChangeAmount: PropTypes.func.isRequired, + placeholder: PropTypes.string.isRequired, +}; + +const defaultProps = { + forwardedRef: undefined, +}; + +function AmountTextInput(props) { + return ( + + ); +} + +AmountTextInput.propTypes = propTypes; +AmountTextInput.defaultProps = defaultProps; +AmountTextInput.displayName = 'AmountTextInput'; + +export default React.forwardRef((props, ref) => ( + // eslint-disable-next-line react/jsx-props-no-spreading + +)); diff --git a/src/components/CurrencySymbolButton.js b/src/components/CurrencySymbolButton.js new file mode 100644 index 000000000000..46e8fab1a048 --- /dev/null +++ b/src/components/CurrencySymbolButton.js @@ -0,0 +1,23 @@ +import React from 'react'; +import {TouchableOpacity} from 'react-native'; +import PropTypes from 'prop-types'; +import Text from './Text'; +import styles from '../styles/styles'; + +const propTypes = { + currencySymbol: PropTypes.string.isRequired, + onCurrencyButtonPress: PropTypes.func.isRequired, +}; + +function CurrencySymbolButton(props) { + return ( + + {props.currencySymbol} + + ); +} + +CurrencySymbolButton.propTypes = propTypes; +CurrencySymbolButton.displayName = 'CurrencySymbolButton'; + +export default CurrencySymbolButton; diff --git a/src/components/TextInputWithCurrencySymbol.js b/src/components/TextInputWithCurrencySymbol.js new file mode 100644 index 000000000000..27063188d37b --- /dev/null +++ b/src/components/TextInputWithCurrencySymbol.js @@ -0,0 +1,55 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import AmountTextInput from './AmountTextInput'; +import CurrencySymbolButton from './CurrencySymbolButton'; + +const propTypes = { + currencySymbol: PropTypes.string.isRequired, + forwardedRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({current: PropTypes.instanceOf(React.Component)}), + ]), + formattedAmount: PropTypes.string.isRequired, + isCurrencySymbolLTR: PropTypes.bool, + onChangeAmount: PropTypes.func, + onCurrencyButtonPress: PropTypes.func, + placeholder: PropTypes.string.isRequired, +}; + +const defaultProps = { + forwardedRef: undefined, + isCurrencySymbolLTR: true, + onChangeAmount: () => {}, + onCurrencyButtonPress: () => {}, +}; + +function TextInputWithCurrencySymbol(props) { + const currencySymbolButton = ( + + ); + + const amountTextInput = ( + + ); + + return props.isCurrencySymbolLTR + ? [currencySymbolButton, amountTextInput] + : [amountTextInput, currencySymbolButton]; +} + +TextInputWithCurrencySymbol.propTypes = propTypes; +TextInputWithCurrencySymbol.defaultProps = defaultProps; +TextInputWithCurrencySymbol.displayName = 'TextInputWithCurrencySymbol'; + +export default React.forwardRef((props, ref) => ( + // eslint-disable-next-line react/jsx-props-no-spreading + +)); diff --git a/src/pages/iou/steps/IOUAmountPage.js b/src/pages/iou/steps/IOUAmountPage.js index d15911b19e50..38999874184c 100755 --- a/src/pages/iou/steps/IOUAmountPage.js +++ b/src/pages/iou/steps/IOUAmountPage.js @@ -1,7 +1,6 @@ import React from 'react'; import { View, - TouchableOpacity, InteractionManager, } from 'react-native'; import PropTypes from 'prop-types'; @@ -16,11 +15,10 @@ import ROUTES from '../../../ROUTES'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; import compose from '../../../libs/compose'; import Button from '../../../components/Button'; -import Text from '../../../components/Text'; import CONST from '../../../CONST'; -import TextInput from '../../../components/TextInput'; import canUseTouchScreen from '../../../libs/canUseTouchscreen'; import * as CurrencySymbolUtils from '../../../libs/CurrencySymbolUtils'; +import TextInputWithCurrencySymbol from '../../../components/TextInputWithCurrencySymbol'; const propTypes = { /** Whether or not this IOU has multiple participants */ @@ -210,30 +208,6 @@ class IOUAmountPage extends React.Component { const currencySymbol = CurrencySymbolUtils.getLocalizedCurrencySymbol(this.props.preferredLocale, this.props.iou.selectedCurrencyCode); const isCurrencySymbolLTR = CurrencySymbolUtils.isCurrencySymbolLTR(this.props.preferredLocale, this.props.iou.selectedCurrencyCode); - const currencySymbolElement = ( - Navigation.navigate(this.props.hasMultipleParticipants - ? ROUTES.getIouBillCurrencyRoute(this.props.reportID) - : ROUTES.getIouRequestCurrencyRoute(this.props.reportID))} - > - {currencySymbol} - - ); - - const amountTextInput = ( - this.textInput = el} - value={formattedAmount} - placeholder={this.props.numberFormat(0)} - keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD} - /> - ); - return ( <> - {isCurrencySymbolLTR ? [currencySymbolElement, amountTextInput] : [amountTextInput, currencySymbolElement]} + Navigation.navigate(this.props.hasMultipleParticipants + ? ROUTES.getIouBillCurrencyRoute(this.props.reportID) + : ROUTES.getIouRequestCurrencyRoute(this.props.reportID))} + placeholder={this.props.numberFormat(0)} + ref={el => this.textInput = el} + /> {canUseTouchScreen() From 50e38f7447f9cd13b3b624d170eefdbe7b0fe2ad Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Wed, 11 May 2022 02:22:43 +0500 Subject: [PATCH 052/331] fix: more comments --- .../HTMLEngineProvider/HTMLRenderers/ImageRenderer.js | 1 + src/components/ThumbnailImage.js | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js index 650e387a436b..de7aafa20c03 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js @@ -49,6 +49,7 @@ const ImageRenderer = (props) => { return ( Date: Wed, 11 May 2022 02:24:17 +0500 Subject: [PATCH 053/331] fix: removed stale code --- src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js index de7aafa20c03..650e387a436b 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/ImageRenderer.js @@ -49,7 +49,6 @@ const ImageRenderer = (props) => { return ( Date: Thu, 12 May 2022 21:03:00 +0530 Subject: [PATCH 054/331] update comment in tests/unit/CurrencySymbolUtilsTest.js Co-authored-by: Rajat Parashar --- tests/unit/CurrencySymbolUtilsTest.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/unit/CurrencySymbolUtilsTest.js b/tests/unit/CurrencySymbolUtilsTest.js index 0fa69dd7494e..d2967bae3ae7 100644 --- a/tests/unit/CurrencySymbolUtilsTest.js +++ b/tests/unit/CurrencySymbolUtilsTest.js @@ -1,8 +1,4 @@ -// currencyList can get outdated, you can get the latest list from server -// src/libs/API.js -// GetCurrencyList().then(data => console.log(data.currencyList)); -// it will output the json in console, you can copy it and use any external json formatter -// finally update currencyList.json +// Taken from GetCurrencyList API. import _ from 'underscore'; import * as CurrencySymbolUtils from '../../src/libs/CurrencySymbolUtils'; From 184102246a478ed484ad4c51cd36922ef7a4b925 Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Thu, 12 May 2022 21:19:44 +0530 Subject: [PATCH 055/331] refactor: TextInputWithCurrencySymbol --- src/components/TextInputWithCurrencySymbol.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/components/TextInputWithCurrencySymbol.js b/src/components/TextInputWithCurrencySymbol.js index 27063188d37b..b2ab33573ea6 100644 --- a/src/components/TextInputWithCurrencySymbol.js +++ b/src/components/TextInputWithCurrencySymbol.js @@ -40,9 +40,21 @@ function TextInputWithCurrencySymbol(props) { /> ); - return props.isCurrencySymbolLTR - ? [currencySymbolButton, amountTextInput] - : [amountTextInput, currencySymbolButton]; + if (props.isCurrencySymbolLTR) { + return ( + <> + {currencySymbolButton} + {amountTextInput} + + ); + } + + return ( + <> + {amountTextInput} + {currencySymbolButton} + + ); } TextInputWithCurrencySymbol.propTypes = propTypes; From 6147523e22cc65356542a2fcd50654eef2915d74 Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Thu, 12 May 2022 21:25:06 +0530 Subject: [PATCH 056/331] refactor: move inline function to class method --- src/pages/iou/steps/IOUAmountPage.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/steps/IOUAmountPage.js b/src/pages/iou/steps/IOUAmountPage.js index 38999874184c..d879e337c075 100755 --- a/src/pages/iou/steps/IOUAmountPage.js +++ b/src/pages/iou/steps/IOUAmountPage.js @@ -63,6 +63,7 @@ class IOUAmountPage extends React.Component { this.stripCommaFromAmount = this.stripCommaFromAmount.bind(this); this.focusTextInput = this.focusTextInput.bind(this); this.focusEmptyInput = this.focusEmptyInput.bind(this); + this.navigateToCurrencySelectionPage = this.navigateToCurrencySelectionPage.bind(this); this.state = { amount: props.selectedAmount, @@ -203,6 +204,12 @@ class IOUAmountPage extends React.Component { .value(); } + navigateToCurrencySelectionPage() { + Navigation.navigate(this.props.hasMultipleParticipants + ? ROUTES.getIouBillCurrencyRoute(this.props.reportID) + : ROUTES.getIouRequestCurrencyRoute(this.props.reportID)); + } + render() { const formattedAmount = this.replaceAllDigits(this.state.amount, this.props.toLocaleDigit); const currencySymbol = CurrencySymbolUtils.getLocalizedCurrencySymbol(this.props.preferredLocale, this.props.iou.selectedCurrencyCode); @@ -223,9 +230,7 @@ class IOUAmountPage extends React.Component { formattedAmount={formattedAmount} isCurrencySymbolLTR={isCurrencySymbolLTR} onChangeAmount={this.updateAmount} - onCurrencyButtonPress={() => Navigation.navigate(this.props.hasMultipleParticipants - ? ROUTES.getIouBillCurrencyRoute(this.props.reportID) - : ROUTES.getIouRequestCurrencyRoute(this.props.reportID))} + onCurrencyButtonPress={this.navigateToCurrencySelectionPage} placeholder={this.props.numberFormat(0)} ref={el => this.textInput = el} /> From 8e9764f1336a5a307481c44654920751a1d96a7f Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Thu, 12 May 2022 21:38:13 +0530 Subject: [PATCH 057/331] refactor: IOUAmountPage --- src/components/TextInputWithCurrencySymbol.js | 13 ++++++++----- src/pages/iou/steps/IOUAmountPage.js | 7 ++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/TextInputWithCurrencySymbol.js b/src/components/TextInputWithCurrencySymbol.js index b2ab33573ea6..2d4b88fedf6a 100644 --- a/src/components/TextInputWithCurrencySymbol.js +++ b/src/components/TextInputWithCurrencySymbol.js @@ -2,31 +2,34 @@ import React from 'react'; import PropTypes from 'prop-types'; import AmountTextInput from './AmountTextInput'; import CurrencySymbolButton from './CurrencySymbolButton'; +import * as CurrencySymbolUtils from '../libs/CurrencySymbolUtils'; const propTypes = { - currencySymbol: PropTypes.string.isRequired, forwardedRef: PropTypes.oneOfType([ PropTypes.func, PropTypes.shape({current: PropTypes.instanceOf(React.Component)}), ]), formattedAmount: PropTypes.string.isRequired, - isCurrencySymbolLTR: PropTypes.bool, onChangeAmount: PropTypes.func, onCurrencyButtonPress: PropTypes.func, placeholder: PropTypes.string.isRequired, + preferredLocale: PropTypes.string.isRequired, + selectedCurrencyCode: PropTypes.string.isRequired, }; const defaultProps = { forwardedRef: undefined, - isCurrencySymbolLTR: true, onChangeAmount: () => {}, onCurrencyButtonPress: () => {}, }; function TextInputWithCurrencySymbol(props) { + const currencySymbol = CurrencySymbolUtils.getLocalizedCurrencySymbol(props.preferredLocale, props.selectedCurrencyCode); + const isCurrencySymbolLTR = CurrencySymbolUtils.isCurrencySymbolLTR(props.preferredLocale, props.selectedCurrencyCode); + const currencySymbolButton = ( ); @@ -40,7 +43,7 @@ function TextInputWithCurrencySymbol(props) { /> ); - if (props.isCurrencySymbolLTR) { + if (isCurrencySymbolLTR) { return ( <> {currencySymbolButton} diff --git a/src/pages/iou/steps/IOUAmountPage.js b/src/pages/iou/steps/IOUAmountPage.js index d879e337c075..8589f431c68f 100755 --- a/src/pages/iou/steps/IOUAmountPage.js +++ b/src/pages/iou/steps/IOUAmountPage.js @@ -17,7 +17,6 @@ import compose from '../../../libs/compose'; import Button from '../../../components/Button'; import CONST from '../../../CONST'; import canUseTouchScreen from '../../../libs/canUseTouchscreen'; -import * as CurrencySymbolUtils from '../../../libs/CurrencySymbolUtils'; import TextInputWithCurrencySymbol from '../../../components/TextInputWithCurrencySymbol'; const propTypes = { @@ -212,8 +211,6 @@ class IOUAmountPage extends React.Component { render() { const formattedAmount = this.replaceAllDigits(this.state.amount, this.props.toLocaleDigit); - const currencySymbol = CurrencySymbolUtils.getLocalizedCurrencySymbol(this.props.preferredLocale, this.props.iou.selectedCurrencyCode); - const isCurrencySymbolLTR = CurrencySymbolUtils.isCurrencySymbolLTR(this.props.preferredLocale, this.props.iou.selectedCurrencyCode); return ( <> @@ -226,13 +223,13 @@ class IOUAmountPage extends React.Component { ]} > this.textInput = el} + selectedCurrencyCode={this.props.iou.selectedCurrencyCode} /> From 46148a3882592c64d7a81fed69d07e4c2af75601 Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Thu, 12 May 2022 22:07:12 +0530 Subject: [PATCH 058/331] docs: add jsdocs --- src/components/AmountTextInput.js | 7 +++++++ src/components/CurrencySymbolButton.js | 3 +++ src/components/TextInputWithCurrencySymbol.js | 13 +++++++++++++ 3 files changed, 23 insertions(+) diff --git a/src/components/AmountTextInput.js b/src/components/AmountTextInput.js index 304e79d0bfec..2182f3a89031 100644 --- a/src/components/AmountTextInput.js +++ b/src/components/AmountTextInput.js @@ -5,12 +5,19 @@ import styles from '../styles/styles'; import CONST from '../CONST'; const propTypes = { + /** Formatted amount in local currency */ formattedAmount: PropTypes.string.isRequired, + + /** A ref to forward to amount text input */ forwardedRef: PropTypes.oneOfType([ PropTypes.func, PropTypes.shape({current: PropTypes.instanceOf(React.Component)}), ]), + + /** Function to call when amount in text input is changed */ onChangeAmount: PropTypes.func.isRequired, + + /** Placeholder value for amount text input */ placeholder: PropTypes.string.isRequired, }; diff --git a/src/components/CurrencySymbolButton.js b/src/components/CurrencySymbolButton.js index 46e8fab1a048..85a6ad8e4083 100644 --- a/src/components/CurrencySymbolButton.js +++ b/src/components/CurrencySymbolButton.js @@ -5,7 +5,10 @@ import Text from './Text'; import styles from '../styles/styles'; const propTypes = { + /** Currency symbol of selected currency */ currencySymbol: PropTypes.string.isRequired, + + /** Function to call when currency button is pressed */ onCurrencyButtonPress: PropTypes.func.isRequired, }; diff --git a/src/components/TextInputWithCurrencySymbol.js b/src/components/TextInputWithCurrencySymbol.js index 2d4b88fedf6a..4bbc2ee5b9e0 100644 --- a/src/components/TextInputWithCurrencySymbol.js +++ b/src/components/TextInputWithCurrencySymbol.js @@ -5,15 +5,28 @@ import CurrencySymbolButton from './CurrencySymbolButton'; import * as CurrencySymbolUtils from '../libs/CurrencySymbolUtils'; const propTypes = { + /** A ref to forward to amount text input */ forwardedRef: PropTypes.oneOfType([ PropTypes.func, PropTypes.shape({current: PropTypes.instanceOf(React.Component)}), ]), + + /** Formatted amount in local currency */ formattedAmount: PropTypes.string.isRequired, + + /** Function to call when amount in text input is changed */ onChangeAmount: PropTypes.func, + + /** Function to call when currency button is pressed */ onCurrencyButtonPress: PropTypes.func, + + /** Placeholder value for amount text input */ placeholder: PropTypes.string.isRequired, + + /** Preferred locale of the user */ preferredLocale: PropTypes.string.isRequired, + + /** Currency code of user's selected currency */ selectedCurrencyCode: PropTypes.string.isRequired, }; From a5bb9917a79dee941cd404c45b816a43d92cf281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Metehan=20=C3=96zyurt?= Date: Sun, 15 May 2022 10:33:29 +0300 Subject: [PATCH 059/331] dragOverListener method added to SidebarScreen --- src/pages/home/sidebar/SidebarScreen.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/pages/home/sidebar/SidebarScreen.js b/src/pages/home/sidebar/SidebarScreen.js index d44c40fc75b4..5f29461fbe50 100755 --- a/src/pages/home/sidebar/SidebarScreen.js +++ b/src/pages/home/sidebar/SidebarScreen.js @@ -47,6 +47,7 @@ class SidebarScreen extends Component { this.startTimer = this.startTimer.bind(this); this.navigateToSettings = this.navigateToSettings.bind(this); this.showCreateMenu = this.showCreateMenu.bind(this); + this.dragOverListener = this.dragOverListener.bind(this); this.state = { isCreateMenuActive: false, @@ -68,6 +69,7 @@ class SidebarScreen extends Component { this.setState({ isCreateMenuActive: true, }); + document.addEventListener('dragover', this.dragOverListener); } /** @@ -83,6 +85,7 @@ class SidebarScreen extends Component { * Selecting an item on CreateMenu or closing it by clicking outside of the modal component */ hideCreateMenu() { + document.removeEventListener('dragover', this.dragOverListener); this.setState({ isCreateMenuActive: false, }); @@ -96,6 +99,15 @@ class SidebarScreen extends Component { Performance.markStart(CONST.TIMING.SWITCH_REPORT); } + /** + * Method called when dragover events window + * + * @param {Object} e native Event + */ + dragOverListener() { + this.hideCreateMenu(); + } + render() { // Workspaces are policies with type === 'free' const workspaces = _.filter(this.props.allPolicies, policy => policy && policy.type === CONST.POLICY.TYPE.FREE); From 7573440200573abd9c2e0d12c21bf7754481a692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Metehan=20=C3=96zyurt?= Date: Sun, 15 May 2022 11:59:55 +0300 Subject: [PATCH 060/331] document changed to window.document and conditions added for unnecessary bind and listeners --- src/pages/home/sidebar/SidebarScreen.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/pages/home/sidebar/SidebarScreen.js b/src/pages/home/sidebar/SidebarScreen.js index 5f29461fbe50..abc3c53d58c3 100755 --- a/src/pages/home/sidebar/SidebarScreen.js +++ b/src/pages/home/sidebar/SidebarScreen.js @@ -47,7 +47,9 @@ class SidebarScreen extends Component { this.startTimer = this.startTimer.bind(this); this.navigateToSettings = this.navigateToSettings.bind(this); this.showCreateMenu = this.showCreateMenu.bind(this); - this.dragOverListener = this.dragOverListener.bind(this); + if (window.document) { + this.dragOverListener = this.dragOverListener.bind(this); + } this.state = { isCreateMenuActive: false, @@ -69,7 +71,9 @@ class SidebarScreen extends Component { this.setState({ isCreateMenuActive: true, }); - document.addEventListener('dragover', this.dragOverListener); + if (window.document) { + window.document.addEventListener('dragover', this.dragOverListener); + } } /** @@ -85,7 +89,9 @@ class SidebarScreen extends Component { * Selecting an item on CreateMenu or closing it by clicking outside of the modal component */ hideCreateMenu() { - document.removeEventListener('dragover', this.dragOverListener); + if (window.document) { + window.document.removeEventListener('dragover', this.dragOverListener); + } this.setState({ isCreateMenuActive: false, }); From c06b30433c32802deefc9448899149888688dd72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Metehan=20=C3=96zyurt?= Date: Mon, 16 May 2022 09:13:27 +0300 Subject: [PATCH 061/331] BaseSidebarScreen.js, index.js and index.native.js created. --- .../BaseSidebarScreen.js} | 64 ++++++++++--------- src/pages/home/sidebar/SidebarScreen/index.js | 40 ++++++++++++ .../sidebar/SidebarScreen/index.native.js | 11 ++++ 3 files changed, 86 insertions(+), 29 deletions(-) rename src/pages/home/sidebar/{SidebarScreen.js => SidebarScreen/BaseSidebarScreen.js} (83%) mode change 100755 => 100644 create mode 100755 src/pages/home/sidebar/SidebarScreen/index.js create mode 100755 src/pages/home/sidebar/SidebarScreen/index.native.js diff --git a/src/pages/home/sidebar/SidebarScreen.js b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js old mode 100755 new mode 100644 similarity index 83% rename from src/pages/home/sidebar/SidebarScreen.js rename to src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js index abc3c53d58c3..265f3aa3da15 --- a/src/pages/home/sidebar/SidebarScreen.js +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js @@ -5,26 +5,33 @@ import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; import {withNavigation} from '@react-navigation/compat'; -import styles from '../../../styles/styles'; -import SidebarLinks from './SidebarLinks'; -import PopoverMenu from '../../../components/PopoverMenu'; -import FAB from '../../../components/FAB'; -import ScreenWrapper from '../../../components/ScreenWrapper'; -import Navigation from '../../../libs/Navigation/Navigation'; -import ROUTES from '../../../ROUTES'; -import Timing from '../../../libs/actions/Timing'; -import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; -import CONST from '../../../CONST'; -import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; -import compose from '../../../libs/compose'; -import * as Expensicons from '../../../components/Icon/Expensicons'; -import Permissions from '../../../libs/Permissions'; -import ONYXKEYS from '../../../ONYXKEYS'; -import * as Policy from '../../../libs/actions/Policy'; -import Performance from '../../../libs/Performance'; -import * as Welcome from '../../../libs/actions/Welcome'; +import styles from '../../../../styles/styles'; +import SidebarLinks from '../SidebarLinks'; +import PopoverMenu from '../../../../components/PopoverMenu'; +import FAB from '../../../../components/FAB'; +import ScreenWrapper from '../../../../components/ScreenWrapper'; +import Navigation from '../../../../libs/Navigation/Navigation'; +import ROUTES from '../../../../ROUTES'; +import Timing from '../../../../libs/actions/Timing'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; +import CONST from '../../../../CONST'; +import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; +import compose from '../../../../libs/compose'; +import * as Expensicons from '../../../../components/Icon/Expensicons'; +import Permissions from '../../../../libs/Permissions'; +import ONYXKEYS from '../../../../ONYXKEYS'; +import * as Policy from '../../../../libs/actions/Policy'; +import Performance from '../../../../libs/Performance'; +import * as Welcome from '../../../../libs/actions/Welcome'; const propTypes = { + + /* Create Listener callback */ + afterShowCreateMenu: PropTypes.func, + + /* Remove Listener callback */ + beforeHideCreateMenu: PropTypes.func, + /* Beta features list */ betas: PropTypes.arrayOf(PropTypes.string).isRequired, @@ -36,10 +43,12 @@ const propTypes = { ...withLocalizePropTypes, }; const defaultProps = { + beforeHideCreateMenu: () => {}, + afterShowCreateMenu: () => {}, isCreatingWorkspace: false, }; -class SidebarScreen extends Component { +class BaseSidebarScreen extends Component { constructor(props) { super(props); @@ -47,9 +56,6 @@ class SidebarScreen extends Component { this.startTimer = this.startTimer.bind(this); this.navigateToSettings = this.navigateToSettings.bind(this); this.showCreateMenu = this.showCreateMenu.bind(this); - if (window.document) { - this.dragOverListener = this.dragOverListener.bind(this); - } this.state = { isCreateMenuActive: false, @@ -71,8 +77,8 @@ class SidebarScreen extends Component { this.setState({ isCreateMenuActive: true, }); - if (window.document) { - window.document.addEventListener('dragover', this.dragOverListener); + if (this.props.afterShowCreateMenu) { + this.props.afterShowCreateMenu(this); } } @@ -89,8 +95,8 @@ class SidebarScreen extends Component { * Selecting an item on CreateMenu or closing it by clicking outside of the modal component */ hideCreateMenu() { - if (window.document) { - window.document.removeEventListener('dragover', this.dragOverListener); + if (this.props.beforeHideCreateMenu) { + this.props.beforeHideCreateMenu(); } this.setState({ isCreateMenuActive: false, @@ -202,8 +208,8 @@ class SidebarScreen extends Component { } } -SidebarScreen.propTypes = propTypes; -SidebarScreen.defaultProps = defaultProps; +BaseSidebarScreen.propTypes = propTypes; +BaseSidebarScreen.defaultProps = defaultProps; export default compose( withNavigation, @@ -220,4 +226,4 @@ export default compose( key: ONYXKEYS.IS_CREATING_WORKSPACE, }, }), -)(SidebarScreen); +)(BaseSidebarScreen); diff --git a/src/pages/home/sidebar/SidebarScreen/index.js b/src/pages/home/sidebar/SidebarScreen/index.js new file mode 100755 index 000000000000..aa9264996352 --- /dev/null +++ b/src/pages/home/sidebar/SidebarScreen/index.js @@ -0,0 +1,40 @@ +import React, {PureComponent} from 'react'; +import BaseSidebarScreen from './BaseSidebarScreen'; + +class SidebarScreen extends PureComponent { + /** + * Method create event listener and bind. + * @param {BaseSidebarScreen} baseComponent + */ + createDragoverListener= (baseComponent) => { + this.dragOverListener = this.dragOverListener.bind(baseComponent); + document.addEventListener('dragover', this.dragOverListener); + } + + /** + * Method remove event listener. + */ + removeDragoverListener= () => { + document.removeEventListener('dragover', this.dragOverListener); + } + + /** + * Method called when dragover events on document. + */ + dragOverListener() { + this.hideCreateMenu(); + } + + render() { + return ( + + ); + } +} + +export default SidebarScreen; diff --git a/src/pages/home/sidebar/SidebarScreen/index.native.js b/src/pages/home/sidebar/SidebarScreen/index.native.js new file mode 100755 index 000000000000..712e6926e92b --- /dev/null +++ b/src/pages/home/sidebar/SidebarScreen/index.native.js @@ -0,0 +1,11 @@ +import React, {PureComponent} from 'react'; +import BaseSidebarScreen from './BaseSidebarScreen'; + +class SidebarScreen extends PureComponent { + render() { + // eslint-disable-next-line react/jsx-props-no-spreading + return (); + } +} + +export default SidebarScreen; From 765af5bf3c88f09cd99b26be8a1da33639dff20d Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 16 May 2022 17:25:41 -0600 Subject: [PATCH 062/331] import Form and use it --- src/pages/settings/Payments/AddDebitCardPage.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/pages/settings/Payments/AddDebitCardPage.js b/src/pages/settings/Payments/AddDebitCardPage.js index 7ce2c06adf32..516091e6b292 100644 --- a/src/pages/settings/Payments/AddDebitCardPage.js +++ b/src/pages/settings/Payments/AddDebitCardPage.js @@ -24,7 +24,7 @@ import ONYXKEYS from '../../../ONYXKEYS'; import compose from '../../../libs/compose'; import AddressSearch from '../../../components/AddressSearch'; import * as ComponentUtils from '../../../libs/ComponentUtils'; -import FormScrollView from '../../../components/FormScrollView'; +import Form from '../../../components/Form'; const propTypes = { addDebitCardForm: PropTypes.shape({ @@ -225,8 +225,11 @@ class DebitCardPage extends Component { onBackButtonPress={() => Navigation.goBack()} onCloseButtonPress={() => Navigation.dismissModal(true)} /> - this.form = el} +
{}} + onSubmit={() => {}} + submitButtonText="Save" > - + ); From 10d7ac2c784f56e808cd9662d78fd7d91d34f84e Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 16 May 2022 17:32:28 -0600 Subject: [PATCH 063/331] clean up some inputs --- .../settings/Payments/AddDebitCardPage.js | 201 ++++++++---------- 1 file changed, 83 insertions(+), 118 deletions(-) diff --git a/src/pages/settings/Payments/AddDebitCardPage.js b/src/pages/settings/Payments/AddDebitCardPage.js index 516091e6b292..b3e03444c46f 100644 --- a/src/pages/settings/Payments/AddDebitCardPage.js +++ b/src/pages/settings/Payments/AddDebitCardPage.js @@ -230,132 +230,97 @@ class DebitCardPage extends Component { validate={() => {}} onSubmit={() => {}} submitButtonText="Save" + style={[styles.mh5, styles.mb5]} > - - this.clearErrorAndSetValue('nameOnCard', nameOnCard)} - value={this.state.nameOnCard} - errorText={this.getErrorText('nameOnCard')} - /> - this.clearErrorAndSetValue('cardNumber', cardNumber)} - value={this.state.cardNumber} - errorText={this.getErrorText('cardNumber')} - keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD} - /> - - - - - - this.clearErrorAndSetValue('securityCode', securityCode)} - value={this.state.securityCode} - maxLength={4} - errorText={this.getErrorText('securityCode')} - keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD} - /> - + + + + + - { - const renamedFields = { - street: 'addressStreet', - state: 'addressState', - zipCode: 'addressZipCode', - }; - _.each(values, (value, inputKey) => { - if (inputKey === 'city') { - return; - } - const renamedInputKey = lodashGet(renamedFields, inputKey, inputKey); - this.clearErrorAndSetValue(renamedInputKey, value); - }); - }} - errorText={this.getErrorText('addressStreet')} - /> - - - this.clearErrorAndSetValue('addressZipCode', value)} - value={this.state.addressZipCode} - errorText={this.getErrorText('addressZipCode')} - maxLength={CONST.BANK_ACCOUNT.MAX_LENGTH.ZIP_CODE} - /> - - - this.clearErrorAndSetValue('addressState', value)} - value={this.state.addressState} - errorText={this.getErrorText('addressState')} - /> - + + - + + { + const renamedFields = { + street: 'addressStreet', + state: 'addressState', + zipCode: 'addressZipCode', + }; + _.each(values, (value, inputKey) => { + if (inputKey === 'city') { + return; + } + const renamedInputKey = lodashGet(renamedFields, inputKey, inputKey); + this.clearErrorAndSetValue(renamedInputKey, value); + }); + }} + errorText={this.getErrorText('addressStreet')} + /> + + this.clearErrorAndSetValue('password', password)} - value={this.state.password} - errorText={this.getErrorText('password')} - textContentType="password" - autoCompleteType={ComponentUtils.PASSWORD_AUTOCOMPLETE_TYPE} - secureTextEntry + label={this.props.translate('common.zip')} + keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD} + onChangeText={value => this.clearErrorAndSetValue('addressZipCode', value)} + value={this.state.addressZipCode} + errorText={this.getErrorText('addressZipCode')} + maxLength={CONST.BANK_ACCOUNT.MAX_LENGTH.ZIP_CODE} + /> + + + this.clearErrorAndSetValue('addressState', value)} + value={this.state.addressState} + errorText={this.getErrorText('addressState')} /> - { - this.setState(prevState => ({ - acceptedTerms: !prevState.acceptedTerms, - errors: { - ...prevState.errors, - acceptedTerms: false, - }, - })); - }} - LabelComponent={() => ( - <> - {`${this.props.translate('common.iAcceptThe')}`} - - {`${this.props.translate('addDebitCardPage.expensifyTermsOfService')}`} - - - )} - style={styles.mt4} - errorText={this.getErrorText('acceptedTerms')} + + + this.clearErrorAndSetValue('password', password)} + value={this.state.password} + errorText={this.getErrorText('password')} + textContentType="password" + autoCompleteType={ComponentUtils.PASSWORD_AUTOCOMPLETE_TYPE} + secureTextEntry /> - {!_.isEmpty(this.props.addDebitCardForm.error) && ( - - - {this.props.addDebitCardForm.error} - - - )} - { - this.form.scrollTo({y: 0, animated: true}); - }} - isLoading={this.props.addDebitCardForm.submitting} + ( + <> + {`${this.props.translate('common.iAcceptThe')}`} + + {`${this.props.translate('addDebitCardPage.expensifyTermsOfService')}`} + + + )} + style={[styles.mt4, styles.mb5]} /> From 939bf7c4804cfbbbc27127400f775ffb94ad12ff Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 16 May 2022 17:51:08 -0600 Subject: [PATCH 064/331] update validate --- .../settings/Payments/AddDebitCardPage.js | 62 +++++++++---------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/src/pages/settings/Payments/AddDebitCardPage.js b/src/pages/settings/Payments/AddDebitCardPage.js index b3e03444c46f..8440cefd7f07 100644 --- a/src/pages/settings/Payments/AddDebitCardPage.js +++ b/src/pages/settings/Payments/AddDebitCardPage.js @@ -117,50 +117,46 @@ class DebitCardPage extends Component { /** * @returns {Boolean} */ - validate() { + validate(values) { const errors = {}; - if (!ValidationUtils.isValidCardName(this.state.nameOnCard)) { - errors.nameOnCard = true; - } - if (!ValidationUtils.isValidDebitCard(this.state.cardNumber.replace(/ /g, ''))) { - errors.cardNumber = true; + if (!ValidationUtils.isValidCardName(values.nameOnCard)) { + errors.nameOnCard = this.props.translate('addDebitCardPage.error.invalidName'); } - if (!ValidationUtils.isValidExpirationDate(this.state.expirationDate)) { - errors.expirationDate = true; + if (!ValidationUtils.isValidDebitCard(values.cardNumber.replace(/ /g, ''))) { + errors.cardNumber = this.props.translate('addDebitCardPage.error.debitCardNumber'); } - if (!ValidationUtils.isValidSecurityCode(this.state.securityCode)) { - errors.securityCode = true; + if (!ValidationUtils.isValidExpirationDate(values.expirationDate)) { + errors.expirationDate = this.props.translate('addDebitCardPage.error.expirationDate'); } - if (!ValidationUtils.isValidAddress(this.state.addressStreet)) { - errors.addressStreet = true; + if (!ValidationUtils.isValidSecurityCode(values.securityCode)) { + errors.securityCode = this.props.translate('addDebitCardPage.error.securityCode'); } - if (!ValidationUtils.isValidZipCode(this.state.addressZipCode)) { - errors.addressZipCode = true; - } + // if (!ValidationUtils.isValidAddress(this.state.addressStreet)) { + // errors.addressStreet = this.props.translate('addDebitCardPage.error.invalidName'); + // } - if (!this.state.addressState) { - errors.addressState = true; - } + // if (!ValidationUtils.isValidZipCode(this.state.addressZipCode)) { + // errors.addressZipCode = this.props.translate('addDebitCardPage.error.invalidName'); + // } - if (_.isEmpty(this.state.password.trim())) { - errors.password = true; - } + // if (!this.state.addressState) { + // errors.addressState = this.props.translate('addDebitCardPage.error.invalidName'); + // } - if (!this.state.acceptedTerms) { - errors.acceptedTerms = true; - } + // if (_.isEmpty(this.state.password.trim())) { + // errors.password = this.props.translate('addDebitCardPage.error.invalidName'); + // } - const hasErrors = _.size(errors) > 0; - this.setState({ - errors, - shouldShowAlertPrompt: hasErrors, - }); - return !hasErrors; + // if (!this.state.acceptedTerms) { + // errors.acceptedTerms = this.props.translate('addDebitCardPage.error.invalidName'); + // } + + return errors; } submit() { @@ -227,7 +223,7 @@ class DebitCardPage extends Component { />
{}} + validate={this.validate} onSubmit={() => {}} submitButtonText="Save" style={[styles.mh5, styles.mb5]} @@ -245,7 +241,7 @@ class DebitCardPage extends Component { Date: Tue, 17 May 2022 16:25:05 +0530 Subject: [PATCH 065/331] style: format code Co-authored-by: Rajat Parashar --- src/pages/iou/steps/IOUAmountPage.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/steps/IOUAmountPage.js b/src/pages/iou/steps/IOUAmountPage.js index 8589f431c68f..d85d42d76c35 100755 --- a/src/pages/iou/steps/IOUAmountPage.js +++ b/src/pages/iou/steps/IOUAmountPage.js @@ -204,9 +204,11 @@ class IOUAmountPage extends React.Component { } navigateToCurrencySelectionPage() { - Navigation.navigate(this.props.hasMultipleParticipants + Navigation.navigate( + this.props.hasMultipleParticipants ? ROUTES.getIouBillCurrencyRoute(this.props.reportID) - : ROUTES.getIouRequestCurrencyRoute(this.props.reportID)); + : ROUTES.getIouRequestCurrencyRoute(this.props.reportID) + ); } render() { From 355e65d377bbb69519dfef620e5291cc9132b6cd Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Tue, 17 May 2022 17:37:29 +0530 Subject: [PATCH 066/331] refactor: remove unused code --- src/pages/iou/steps/IOUAmountPage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/iou/steps/IOUAmountPage.js b/src/pages/iou/steps/IOUAmountPage.js index fdac934536da..29a8e5cc3b22 100755 --- a/src/pages/iou/steps/IOUAmountPage.js +++ b/src/pages/iou/steps/IOUAmountPage.js @@ -61,7 +61,6 @@ class IOUAmountPage extends React.Component { this.updateAmount = this.updateAmount.bind(this); this.stripCommaFromAmount = this.stripCommaFromAmount.bind(this); this.focusTextInput = this.focusTextInput.bind(this); - this.focusEmptyInput = this.focusEmptyInput.bind(this); this.navigateToCurrencySelectionPage = this.navigateToCurrencySelectionPage.bind(this); this.state = { From c25bf6457b8398cce1d194ddb404c57426501dc9 Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Tue, 17 May 2022 17:53:00 +0530 Subject: [PATCH 067/331] style: fix eslint error --- src/pages/iou/steps/IOUAmountPage.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/steps/IOUAmountPage.js b/src/pages/iou/steps/IOUAmountPage.js index 36cd7ee68b8d..ad4060ee8f7c 100755 --- a/src/pages/iou/steps/IOUAmountPage.js +++ b/src/pages/iou/steps/IOUAmountPage.js @@ -194,10 +194,10 @@ class IOUAmountPage extends React.Component { navigateToCurrencySelectionPage() { Navigation.navigate( - this.props.hasMultipleParticipants - ? ROUTES.getIouBillCurrencyRoute(this.props.reportID) - : ROUTES.getIouRequestCurrencyRoute(this.props.reportID) - ); + this.props.hasMultipleParticipants + ? ROUTES.getIouBillCurrencyRoute(this.props.reportID) + : ROUTES.getIouRequestCurrencyRoute(this.props.reportID), + ); } render() { From 133a62e84981b23257951baa9c0152d63fecde2e Mon Sep 17 00:00:00 2001 From: Md Neyaz Ahmad Date: Tue, 17 May 2022 18:02:50 +0530 Subject: [PATCH 068/331] chore: update comment --- tests/unit/CurrencySymbolUtilsTest.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/CurrencySymbolUtilsTest.js b/tests/unit/CurrencySymbolUtilsTest.js index d2967bae3ae7..4c89b473cae5 100644 --- a/tests/unit/CurrencySymbolUtilsTest.js +++ b/tests/unit/CurrencySymbolUtilsTest.js @@ -1,7 +1,7 @@ -// Taken from GetCurrencyList API. - import _ from 'underscore'; import * as CurrencySymbolUtils from '../../src/libs/CurrencySymbolUtils'; + +// Taken from GetCurrencyList API. import currencyList from './currencyList.json'; const currencyCodeList = _.keys(currencyList); From 468c5a053140103bad2723529891f6f398e2bdc5 Mon Sep 17 00:00:00 2001 From: Neil Marcellini Date: Tue, 17 May 2022 09:16:46 -0700 Subject: [PATCH 069/331] Use a NavigationReadyDetector in the navigator --- .../AppNavigator/NavigationReadyDetector.js | 14 ++++++++++++++ src/libs/Navigation/AppNavigator/index.js | 6 +++++- src/pages/LogOutPreviousUserPage.js | 5 ----- 3 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 src/libs/Navigation/AppNavigator/NavigationReadyDetector.js diff --git a/src/libs/Navigation/AppNavigator/NavigationReadyDetector.js b/src/libs/Navigation/AppNavigator/NavigationReadyDetector.js new file mode 100644 index 000000000000..8b23947bc95d --- /dev/null +++ b/src/libs/Navigation/AppNavigator/NavigationReadyDetector.js @@ -0,0 +1,14 @@ +import React from 'react'; +import Navigation from '../Navigation'; + +class NavigationReadyDetector extends React.Component { + componentDidUpdate() { + Navigation.setIsNavigationReady(); + } + + render() { + return null; + } +} + +export default NavigationReadyDetector; diff --git a/src/libs/Navigation/AppNavigator/index.js b/src/libs/Navigation/AppNavigator/index.js index 6a7b910ebaef..ac8180d3261d 100644 --- a/src/libs/Navigation/AppNavigator/index.js +++ b/src/libs/Navigation/AppNavigator/index.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import PublicScreens from './PublicScreens'; import AuthScreens from './AuthScreens'; +import NavigationReadyDetector from './NavigationReadyDetector'; const propTypes = { /** If we have an authToken this is true */ @@ -13,7 +14,10 @@ const AppNavigator = props => ( ? ( // These are the protected screens and only accessible when an authToken is present - + <> + + + ) : ( diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 0a4f521b3180..8b2891ec615e 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -5,7 +5,6 @@ import {withOnyx} from 'react-native-onyx'; import ONYXKEYS from '../ONYXKEYS'; import * as Session from '../libs/actions/Session'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; -import Navigation from '../libs/Navigation/Navigation'; const propTypes = { /** The parameters needed to authenticate with a short lived token are in the URL */ @@ -35,11 +34,7 @@ class LogOutPreviousUserPage extends Component { const sessionEmail = lodashGet(this.props.session, 'email', ''); if (paramsEmail !== sessionEmail) { Session.signOutAndRedirectToSignIn(); - return; } - - // Set isNavigationReady so that we can navigate in the AuthScreens - Navigation.setIsNavigationReady(); } render() { From 842b0a7bd3f9baa06fd6e41ef7b1239d53444efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Metehan=20=C3=96zyurt?= Date: Tue, 17 May 2022 20:10:46 +0300 Subject: [PATCH 070/331] index.js and index.native.js files changed to function component, index.js using ref now --- .../SidebarScreen/BaseSidebarScreen.js | 28 ++++------ src/pages/home/sidebar/SidebarScreen/index.js | 51 ++++++++----------- .../sidebar/SidebarScreen/index.native.js | 12 ++--- 3 files changed, 33 insertions(+), 58 deletions(-) diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js index 265f3aa3da15..066e5253f680 100644 --- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js @@ -27,10 +27,10 @@ import * as Welcome from '../../../../libs/actions/Welcome'; const propTypes = { /* Create Listener callback */ - afterShowCreateMenu: PropTypes.func, + onShowCreateMenu: PropTypes.func, /* Remove Listener callback */ - beforeHideCreateMenu: PropTypes.func, + onHideCreateMenu: PropTypes.func, /* Beta features list */ betas: PropTypes.arrayOf(PropTypes.string).isRequired, @@ -43,8 +43,8 @@ const propTypes = { ...withLocalizePropTypes, }; const defaultProps = { - beforeHideCreateMenu: () => {}, - afterShowCreateMenu: () => {}, + onHideCreateMenu: () => {}, + onShowCreateMenu: () => {}, isCreatingWorkspace: false, }; @@ -68,6 +68,9 @@ class BaseSidebarScreen extends Component { const routes = lodashGet(this.props.navigation.getState(), 'routes', []); Welcome.show({routes, showCreateMenu: this.showCreateMenu}); + if (this.props.innerRef) { + this.props.innerRef.current = this; + } } /** @@ -77,9 +80,7 @@ class BaseSidebarScreen extends Component { this.setState({ isCreateMenuActive: true, }); - if (this.props.afterShowCreateMenu) { - this.props.afterShowCreateMenu(this); - } + this.props.onShowCreateMenu(); } /** @@ -95,9 +96,7 @@ class BaseSidebarScreen extends Component { * Selecting an item on CreateMenu or closing it by clicking outside of the modal component */ hideCreateMenu() { - if (this.props.beforeHideCreateMenu) { - this.props.beforeHideCreateMenu(); - } + this.props.onHideCreateMenu(); this.setState({ isCreateMenuActive: false, }); @@ -111,15 +110,6 @@ class BaseSidebarScreen extends Component { Performance.markStart(CONST.TIMING.SWITCH_REPORT); } - /** - * Method called when dragover events window - * - * @param {Object} e native Event - */ - dragOverListener() { - this.hideCreateMenu(); - } - render() { // Workspaces are policies with type === 'free' const workspaces = _.filter(this.props.allPolicies, policy => policy && policy.type === CONST.POLICY.TYPE.FREE); diff --git a/src/pages/home/sidebar/SidebarScreen/index.js b/src/pages/home/sidebar/SidebarScreen/index.js index aa9264996352..8842769f9d74 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.js +++ b/src/pages/home/sidebar/SidebarScreen/index.js @@ -1,40 +1,29 @@ -import React, {PureComponent} from 'react'; +import React, {useRef} from 'react'; import BaseSidebarScreen from './BaseSidebarScreen'; -class SidebarScreen extends PureComponent { - /** - * Method create event listener and bind. - * @param {BaseSidebarScreen} baseComponent - */ - createDragoverListener= (baseComponent) => { - this.dragOverListener = this.dragOverListener.bind(baseComponent); - document.addEventListener('dragover', this.dragOverListener); - } +export default function (props) { + const BaseSidebarScreenRef = useRef(null); /** - * Method remove event listener. + * Method create event listener */ - removeDragoverListener= () => { - document.removeEventListener('dragover', this.dragOverListener); - } + const createDragoverListener = () => { + document.addEventListener('dragover', BaseSidebarScreenRef.current.hideCreateMenu); + }; /** - * Method called when dragover events on document. + * Method remove event listener. */ - dragOverListener() { - this.hideCreateMenu(); - } - - render() { - return ( - - ); - } + const removeDragoverListener = () => { + document.removeEventListener('dragover', BaseSidebarScreenRef.current.hideCreateMenu); + }; + return ( + + ); } - -export default SidebarScreen; diff --git a/src/pages/home/sidebar/SidebarScreen/index.native.js b/src/pages/home/sidebar/SidebarScreen/index.native.js index 712e6926e92b..94e843577644 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.native.js +++ b/src/pages/home/sidebar/SidebarScreen/index.native.js @@ -1,11 +1,7 @@ -import React, {PureComponent} from 'react'; +import React from 'react'; import BaseSidebarScreen from './BaseSidebarScreen'; -class SidebarScreen extends PureComponent { - render() { - // eslint-disable-next-line react/jsx-props-no-spreading - return (); - } +export default function SidebarScreen(props) { + // eslint-disable-next-line react/jsx-props-no-spreading + return ; } - -export default SidebarScreen; From a1bdf8545d5de68120d45185e9b54b0ffa086d1e Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 17 May 2022 15:24:09 -0600 Subject: [PATCH 071/331] refactor validate function --- .../settings/Payments/AddDebitCardPage.js | 64 +++++++++---------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/src/pages/settings/Payments/AddDebitCardPage.js b/src/pages/settings/Payments/AddDebitCardPage.js index 8440cefd7f07..2b1e43919246 100644 --- a/src/pages/settings/Payments/AddDebitCardPage.js +++ b/src/pages/settings/Payments/AddDebitCardPage.js @@ -19,7 +19,6 @@ import CheckboxWithLabel from '../../../components/CheckboxWithLabel'; import StatePicker from '../../../components/StatePicker'; import TextInput from '../../../components/TextInput'; import CONST from '../../../CONST'; -import FormAlertWithSubmitButton from '../../../components/FormAlertWithSubmitButton'; import ONYXKEYS from '../../../ONYXKEYS'; import compose from '../../../libs/compose'; import AddressSearch from '../../../components/AddressSearch'; @@ -93,6 +92,7 @@ class DebitCardPage extends Component { this.clearErrorAndSetValue = this.clearErrorAndSetValue.bind(this); this.getErrorText = this.getErrorText.bind(this); this.addOrRemoveSlashToExpiryDate = this.addOrRemoveSlashToExpiryDate.bind(this); + this.validate = this.validate.bind(this); } /** @@ -120,41 +120,41 @@ class DebitCardPage extends Component { validate(values) { const errors = {}; - if (!ValidationUtils.isValidCardName(values.nameOnCard)) { + if (!values.nameOnCard || !ValidationUtils.isValidCardName(values.nameOnCard)) { errors.nameOnCard = this.props.translate('addDebitCardPage.error.invalidName'); } - if (!ValidationUtils.isValidDebitCard(values.cardNumber.replace(/ /g, ''))) { + if (!values.cardNumber || !ValidationUtils.isValidDebitCard(values.cardNumber.replace(/ /g, ''))) { errors.cardNumber = this.props.translate('addDebitCardPage.error.debitCardNumber'); } - if (!ValidationUtils.isValidExpirationDate(values.expirationDate)) { + if (!values.expirationDate || !ValidationUtils.isValidExpirationDate(values.expirationDate)) { errors.expirationDate = this.props.translate('addDebitCardPage.error.expirationDate'); } - if (!ValidationUtils.isValidSecurityCode(values.securityCode)) { + if (!values.securityCode || !ValidationUtils.isValidSecurityCode(values.securityCode)) { errors.securityCode = this.props.translate('addDebitCardPage.error.securityCode'); } - // if (!ValidationUtils.isValidAddress(this.state.addressStreet)) { - // errors.addressStreet = this.props.translate('addDebitCardPage.error.invalidName'); - // } + if (!values.addressStreet || !ValidationUtils.isValidAddress(values.addressStreet)) { + errors.addressStreet = this.props.translate('addDebitCardPage.error.addressStreet'); + } - // if (!ValidationUtils.isValidZipCode(this.state.addressZipCode)) { - // errors.addressZipCode = this.props.translate('addDebitCardPage.error.invalidName'); - // } + if (!values.addressZipCode || !ValidationUtils.isValidZipCode(values.addressZipCode)) { + errors.addressZipCode = this.props.translate('addDebitCardPage.error.addressZipCode'); + } - // if (!this.state.addressState) { - // errors.addressState = this.props.translate('addDebitCardPage.error.invalidName'); - // } + if (!values.addressState || !values.addressState) { + errors.addressState = this.props.translate('addDebitCardPage.error.addressState'); + } - // if (_.isEmpty(this.state.password.trim())) { - // errors.password = this.props.translate('addDebitCardPage.error.invalidName'); - // } + if (!values.password || _.isEmpty(values.password.trim())) { + errors.password = this.props.translate('addDebitCardPage.error.password'); + } - // if (!this.state.acceptedTerms) { - // errors.acceptedTerms = this.props.translate('addDebitCardPage.error.invalidName'); - // } + if (!values.acceptedTerms) { + errors.acceptedTerms = this.props.translate('common.error.acceptedTerms'); + } return errors; } @@ -222,11 +222,11 @@ class DebitCardPage extends Component { onCloseButtonPress={() => Navigation.dismissModal(true)} /> {}} - submitButtonText="Save" - style={[styles.mh5, styles.mb5]} + formID="test" + validate={this.validate} + onSubmit={() => {}} + submitButtonText="Save" + style={[styles.mh5, styles.mb5]} > { @@ -279,28 +279,22 @@ class DebitCardPage extends Component { this.clearErrorAndSetValue('addressZipCode', value)} - value={this.state.addressZipCode} - errorText={this.getErrorText('addressZipCode')} maxLength={CONST.BANK_ACCOUNT.MAX_LENGTH.ZIP_CODE} /> this.clearErrorAndSetValue('addressState', value)} - value={this.state.addressState} - errorText={this.getErrorText('addressState')} + inputID="addressState" /> this.clearErrorAndSetValue('password', password)} - value={this.state.password} - errorText={this.getErrorText('password')} textContentType="password" autoCompleteType={ComponentUtils.PASSWORD_AUTOCOMPLETE_TYPE} secureTextEntry From 2661834fbf50dbbbdab92c17efefaf678f749fd6 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 17 May 2022 15:31:02 -0600 Subject: [PATCH 072/331] refactor onyxkey --- src/ONYXKEYS.js | 8 +++++--- src/libs/actions/PaymentMethods.js | 6 +++--- src/pages/settings/Payments/AddDebitCardPage.js | 11 ++++------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js index e3ec98b6052e..cb91d280c1a2 100755 --- a/src/ONYXKEYS.js +++ b/src/ONYXKEYS.js @@ -155,9 +155,6 @@ export default { // Set when we are loading payment methods IS_LOADING_PAYMENT_METHODS: 'isLoadingPaymentMethods', - // Stores values for the add debit card form - ADD_DEBIT_CARD_FORM: 'addDebitCardForm', - // Stores values for the request call form REQUEST_CALL_FORM: 'requestCallForm', @@ -193,4 +190,9 @@ export default { // Validating Email? USER_SIGN_UP: 'userSignUp', + + // List of Form ids + FORMS: { + ADD_DEBIT_CARD_FORM: 'addDebitCardForm', + } }; diff --git a/src/libs/actions/PaymentMethods.js b/src/libs/actions/PaymentMethods.js index fe7405dfd388..f60440da23a6 100644 --- a/src/libs/actions/PaymentMethods.js +++ b/src/libs/actions/PaymentMethods.js @@ -140,7 +140,7 @@ function addBillingCard(params) { const cardMonth = CardUtils.getMonthFromExpirationDateString(params.expirationDate); const cardYear = CardUtils.getYearFromExpirationDateString(params.expirationDate); - Onyx.merge(ONYXKEYS.ADD_DEBIT_CARD_FORM, {submitting: true}); + Onyx.merge(ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM, {submitting: true}); API.AddBillingCard({ cardNumber: params.cardNumber, cardYear, @@ -176,7 +176,7 @@ function addBillingCard(params) { errorMessage = response.message ? response.message : Localize.translateLocal('addDebitCardPage.error.genericFailureMessage'); } - Onyx.merge(ONYXKEYS.ADD_DEBIT_CARD_FORM, { + Onyx.merge(ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM, { submitting: false, error: errorMessage, }); @@ -187,7 +187,7 @@ function addBillingCard(params) { * Resets the values for the add debit card form back to their initial states */ function clearDebitCardFormErrorAndSubmit() { - Onyx.set(ONYXKEYS.ADD_DEBIT_CARD_FORM, { + Onyx.set(ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM, { submitting: false, error: '', }); diff --git a/src/pages/settings/Payments/AddDebitCardPage.js b/src/pages/settings/Payments/AddDebitCardPage.js index 2b1e43919246..5c13d796ccc6 100644 --- a/src/pages/settings/Payments/AddDebitCardPage.js +++ b/src/pages/settings/Payments/AddDebitCardPage.js @@ -159,11 +159,8 @@ class DebitCardPage extends Component { return errors; } - submit() { - if (!this.validate()) { - return; - } - PaymentMethods.addBillingCard(this.state); + submit(values) { + PaymentMethods.addBillingCard(values); } /** @@ -222,7 +219,7 @@ class DebitCardPage extends Component { onCloseButtonPress={() => Navigation.dismissModal(true)} /> {}} submitButtonText="Save" @@ -325,7 +322,7 @@ DebitCardPage.defaultProps = defaultProps; export default compose( withOnyx({ addDebitCardForm: { - key: ONYXKEYS.ADD_DEBIT_CARD_FORM, + key: ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM, }, }), withLocalize, From c46dcd93796d768c8a233631bda1fcdb81ed2c7e Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 17 May 2022 15:35:22 -0600 Subject: [PATCH 073/331] refactor addBillingCard --- src/libs/actions/PaymentMethods.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/PaymentMethods.js b/src/libs/actions/PaymentMethods.js index f60440da23a6..65bd6093bcb0 100644 --- a/src/libs/actions/PaymentMethods.js +++ b/src/libs/actions/PaymentMethods.js @@ -140,7 +140,6 @@ function addBillingCard(params) { const cardMonth = CardUtils.getMonthFromExpirationDateString(params.expirationDate); const cardYear = CardUtils.getYearFromExpirationDateString(params.expirationDate); - Onyx.merge(ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM, {submitting: true}); API.AddBillingCard({ cardNumber: params.cardNumber, cardYear, @@ -152,7 +151,7 @@ function addBillingCard(params) { isP2PDebitCard: true, password: params.password, }).then(((response) => { - let errorMessage = ''; + let serverErrorMessage = ''; if (response.jsonCode === 200) { const cardObject = { additionalData: { @@ -173,12 +172,12 @@ function addBillingCard(params) { Growl.show(Localize.translateLocal('addDebitCardPage.growlMessageOnSave'), CONST.GROWL.SUCCESS, 3000); continueSetup(); } else { - errorMessage = response.message ? response.message : Localize.translateLocal('addDebitCardPage.error.genericFailureMessage'); + serverErrorMessage = response.message ? response.message : Localize.translateLocal('addDebitCardPage.error.genericFailureMessage'); } Onyx.merge(ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM, { - submitting: false, - error: errorMessage, + isSubmitting: false, + serverErrorMessage, }); })); } From c221851dd208f305c8328d185f49f49d6261873b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Metehan=20=C3=96zyurt?= Date: Wed, 18 May 2022 00:56:27 +0300 Subject: [PATCH 074/331] sidebarPropTypes.js created,displayName and propTypes added for SidebarScreen. --- .../SidebarScreen/BaseSidebarScreen.js | 25 ++------------ src/pages/home/sidebar/SidebarScreen/index.js | 31 +++++++++++++++-- .../sidebar/SidebarScreen/index.native.js | 33 ++++++++++++++++--- .../sidebar/SidebarScreen/sidebarPropTypes.js | 17 ++++++++++ 4 files changed, 78 insertions(+), 28 deletions(-) create mode 100644 src/pages/home/sidebar/SidebarScreen/sidebarPropTypes.js diff --git a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js index 066e5253f680..5ec708b1f5b5 100644 --- a/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js +++ b/src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js @@ -2,9 +2,7 @@ import lodashGet from 'lodash/get'; import _ from 'underscore'; import React, {Component} from 'react'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; -import {withNavigation} from '@react-navigation/compat'; import styles from '../../../../styles/styles'; import SidebarLinks from '../SidebarLinks'; import PopoverMenu from '../../../../components/PopoverMenu'; @@ -13,13 +11,11 @@ import ScreenWrapper from '../../../../components/ScreenWrapper'; import Navigation from '../../../../libs/Navigation/Navigation'; import ROUTES from '../../../../ROUTES'; import Timing from '../../../../libs/actions/Timing'; -import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; +import {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; import CONST from '../../../../CONST'; -import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; -import compose from '../../../../libs/compose'; +import {withLocalizePropTypes} from '../../../../components/withLocalize'; import * as Expensicons from '../../../../components/Icon/Expensicons'; import Permissions from '../../../../libs/Permissions'; -import ONYXKEYS from '../../../../ONYXKEYS'; import * as Policy from '../../../../libs/actions/Policy'; import Performance from '../../../../libs/Performance'; import * as Welcome from '../../../../libs/actions/Welcome'; @@ -201,19 +197,4 @@ class BaseSidebarScreen extends Component { BaseSidebarScreen.propTypes = propTypes; BaseSidebarScreen.defaultProps = defaultProps; -export default compose( - withNavigation, - withLocalize, - withWindowDimensions, - withOnyx({ - allPolicies: { - key: ONYXKEYS.COLLECTION.POLICY, - }, - betas: { - key: ONYXKEYS.BETAS, - }, - isCreatingWorkspace: { - key: ONYXKEYS.IS_CREATING_WORKSPACE, - }, - }), -)(BaseSidebarScreen); +export default BaseSidebarScreen; diff --git a/src/pages/home/sidebar/SidebarScreen/index.js b/src/pages/home/sidebar/SidebarScreen/index.js index 8842769f9d74..7acfd5af622f 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.js +++ b/src/pages/home/sidebar/SidebarScreen/index.js @@ -1,7 +1,14 @@ import React, {useRef} from 'react'; +import {withNavigation} from '@react-navigation/compat'; +import {withOnyx} from 'react-native-onyx'; +import compose from '../../../../libs/compose'; +import withWindowDimensions from '../../../../components/withWindowDimensions'; +import withLocalize from '../../../../components/withLocalize'; +import ONYXKEYS from '../../../../ONYXKEYS'; +import propTypes from './sidebarPropTypes'; import BaseSidebarScreen from './BaseSidebarScreen'; -export default function (props) { +const SidebarScreen = (props) => { const BaseSidebarScreenRef = useRef(null); /** @@ -26,4 +33,24 @@ export default function (props) { {...props} /> ); -} +}; + +SidebarScreen.propTypes = propTypes; +SidebarScreen.displayName = 'SidebarScreen'; + +export default compose( + withNavigation, + withLocalize, + withWindowDimensions, + withOnyx({ + allPolicies: { + key: ONYXKEYS.COLLECTION.POLICY, + }, + betas: { + key: ONYXKEYS.BETAS, + }, + isCreatingWorkspace: { + key: ONYXKEYS.IS_CREATING_WORKSPACE, + }, + }), +)(SidebarScreen); diff --git a/src/pages/home/sidebar/SidebarScreen/index.native.js b/src/pages/home/sidebar/SidebarScreen/index.native.js index 94e843577644..1d34b8514f6b 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.native.js +++ b/src/pages/home/sidebar/SidebarScreen/index.native.js @@ -1,7 +1,32 @@ import React from 'react'; +import {withNavigation} from '@react-navigation/compat'; +import {withOnyx} from 'react-native-onyx'; +import compose from '../../../../libs/compose'; +import withWindowDimensions from '../../../../components/withWindowDimensions'; +import withLocalize from '../../../../components/withLocalize'; +import ONYXKEYS from '../../../../ONYXKEYS'; +import propTypes from './sidebarPropTypes'; import BaseSidebarScreen from './BaseSidebarScreen'; -export default function SidebarScreen(props) { - // eslint-disable-next-line react/jsx-props-no-spreading - return ; -} +// eslint-disable-next-line react/jsx-props-no-spreading +const SidebarScreen = props => ; + +SidebarScreen.propTypes = propTypes; +SidebarScreen.displayName = 'SidebarScreen'; + +export default compose( + withNavigation, + withLocalize, + withWindowDimensions, + withOnyx({ + allPolicies: { + key: ONYXKEYS.COLLECTION.POLICY, + }, + betas: { + key: ONYXKEYS.BETAS, + }, + isCreatingWorkspace: { + key: ONYXKEYS.IS_CREATING_WORKSPACE, + }, + }), +)(SidebarScreen); diff --git a/src/pages/home/sidebar/SidebarScreen/sidebarPropTypes.js b/src/pages/home/sidebar/SidebarScreen/sidebarPropTypes.js new file mode 100644 index 000000000000..5e18bcdd7c6a --- /dev/null +++ b/src/pages/home/sidebar/SidebarScreen/sidebarPropTypes.js @@ -0,0 +1,17 @@ +import PropTypes from 'prop-types'; +import {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; +import {withLocalizePropTypes} from '../../../../components/withLocalize'; + +const propTypes = { + /* Beta features list */ + betas: PropTypes.arrayOf(PropTypes.string).isRequired, + + /* Is workspace is being created by the user? */ + isCreatingWorkspace: PropTypes.bool, + + ...windowDimensionsPropTypes, + + ...withLocalizePropTypes, +}; + +export default propTypes; From 37dc938c73d2606aa25c3e59a6197e9e83ed8eb4 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 17 May 2022 16:13:39 -0600 Subject: [PATCH 075/331] fix addressSearch style --- .../settings/Payments/AddDebitCardPage.js | 130 ++---------------- 1 file changed, 11 insertions(+), 119 deletions(-) diff --git a/src/pages/settings/Payments/AddDebitCardPage.js b/src/pages/settings/Payments/AddDebitCardPage.js index 5c13d796ccc6..a6ab91f6657a 100644 --- a/src/pages/settings/Payments/AddDebitCardPage.js +++ b/src/pages/settings/Payments/AddDebitCardPage.js @@ -3,8 +3,6 @@ import {View} from 'react-native'; import lodashGet from 'lodash/get'; import lodashEndsWith from 'lodash/endsWith'; import _ from 'underscore'; -import {withOnyx} from 'react-native-onyx'; -import PropTypes from 'prop-types'; import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; import Navigation from '../../../libs/Navigation/Navigation'; import ScreenWrapper from '../../../components/ScreenWrapper'; @@ -20,77 +18,20 @@ import StatePicker from '../../../components/StatePicker'; import TextInput from '../../../components/TextInput'; import CONST from '../../../CONST'; import ONYXKEYS from '../../../ONYXKEYS'; -import compose from '../../../libs/compose'; import AddressSearch from '../../../components/AddressSearch'; import * as ComponentUtils from '../../../libs/ComponentUtils'; import Form from '../../../components/Form'; const propTypes = { - addDebitCardForm: PropTypes.shape({ - /** Error message from API call */ - error: PropTypes.string, - - /** Whether or not the form is submitting */ - submitting: PropTypes.bool, - }), - /* Onyx Props */ ...withLocalizePropTypes, }; -const defaultProps = { - addDebitCardForm: { - error: '', - submitting: false, - }, -}; - class DebitCardPage extends Component { constructor(props) { super(props); - this.state = { - nameOnCard: '', - cardNumber: '', - expirationDate: '', - securityCode: '', - addressStreet: '', - addressState: '', - addressZipCode: '', - acceptedTerms: false, - password: '', - errors: {}, - shouldShowAlertPrompt: false, - }; - - this.requiredFields = [ - 'nameOnCard', - 'cardNumber', - 'expirationDate', - 'securityCode', - 'addressStreet', - 'addressState', - 'addressZipCode', - 'password', - 'acceptedTerms', - ]; - - // Map a field to the key of the error's translation - this.errorTranslationKeys = { - nameOnCard: 'addDebitCardPage.error.invalidName', - cardNumber: 'addDebitCardPage.error.debitCardNumber', - expirationDate: 'addDebitCardPage.error.expirationDate', - securityCode: 'addDebitCardPage.error.securityCode', - addressStreet: 'addDebitCardPage.error.addressStreet', - addressState: 'addDebitCardPage.error.addressState', - addressZipCode: 'addDebitCardPage.error.addressZipCode', - acceptedTerms: 'common.error.acceptedTerms', - password: 'addDebitCardPage.error.password', - }; - this.submit = this.submit.bind(this); - this.clearErrorAndSetValue = this.clearErrorAndSetValue.bind(this); - this.getErrorText = this.getErrorText.bind(this); this.addOrRemoveSlashToExpiryDate = this.addOrRemoveSlashToExpiryDate.bind(this); this.validate = this.validate.bind(this); } @@ -102,18 +43,6 @@ class DebitCardPage extends Component { PaymentMethods.clearDebitCardFormErrorAndSubmit(); } - /** - * @param {String} inputKey - * @returns {String} - */ - getErrorText(inputKey) { - if (!lodashGet(this.state.errors, inputKey, false)) { - return ''; - } - - return this.props.translate(this.errorTranslationKeys[inputKey]); - } - /** * @returns {Boolean} */ @@ -163,22 +92,6 @@ class DebitCardPage extends Component { PaymentMethods.addBillingCard(values); } - /** - * Clear the error associated to inputKey if found and store the inputKey new value in the state. - * - * @param {String} inputKey - * @param {String} value - */ - clearErrorAndSetValue(inputKey, value) { - this.setState(prevState => ({ - [inputKey]: value, - errors: { - ...prevState.errors, - [inputKey]: false, - }, - })); - } - /** * @param {String} inputExpiryDate */ @@ -219,11 +132,11 @@ class DebitCardPage extends Component { onCloseButtonPress={() => Navigation.dismissModal(true)} /> {}} submitButtonText="Save" - style={[styles.mh5, styles.mb5]} + style={[styles.mh5, styles.flexGrow1]} > - { - const renamedFields = { - street: 'addressStreet', - state: 'addressState', - zipCode: 'addressZipCode', - }; - _.each(values, (value, inputKey) => { - if (inputKey === 'city') { - return; - } - const renamedInputKey = lodashGet(renamedFields, inputKey, inputKey); - this.clearErrorAndSetValue(renamedInputKey, value); - }); - }} - errorText={this.getErrorText('addressStreet')} - /> + + + )} - style={[styles.mt4, styles.mb5]} + style={[styles.mt4]} /> @@ -317,13 +217,5 @@ class DebitCardPage extends Component { } DebitCardPage.propTypes = propTypes; -DebitCardPage.defaultProps = defaultProps; -export default compose( - withOnyx({ - addDebitCardForm: { - key: ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM, - }, - }), - withLocalize, -)(DebitCardPage); +export default withLocalize(DebitCardPage); From db3ca86c16025f1aa5172a78bd5284841d89598b Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 17 May 2022 16:40:52 -0600 Subject: [PATCH 076/331] save checkbox draft --- src/pages/settings/Payments/AddDebitCardPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/settings/Payments/AddDebitCardPage.js b/src/pages/settings/Payments/AddDebitCardPage.js index a6ab91f6657a..2119dfb3b217 100644 --- a/src/pages/settings/Payments/AddDebitCardPage.js +++ b/src/pages/settings/Payments/AddDebitCardPage.js @@ -208,6 +208,7 @@ class DebitCardPage extends Component { )} style={[styles.mt4]} + shouldSaveDraft /> From 5482f8ded67e6bd1c1a8bf2935bfd10fa9f9db6b Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Tue, 17 May 2022 16:47:11 -0600 Subject: [PATCH 077/331] fix lint errors --- src/ONYXKEYS.js | 2 +- src/pages/settings/Payments/AddDebitCardPage.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js index cb91d280c1a2..9e198d07aad7 100755 --- a/src/ONYXKEYS.js +++ b/src/ONYXKEYS.js @@ -194,5 +194,5 @@ export default { // List of Form ids FORMS: { ADD_DEBIT_CARD_FORM: 'addDebitCardForm', - } + }, }; diff --git a/src/pages/settings/Payments/AddDebitCardPage.js b/src/pages/settings/Payments/AddDebitCardPage.js index 2119dfb3b217..bae1a2ba8333 100644 --- a/src/pages/settings/Payments/AddDebitCardPage.js +++ b/src/pages/settings/Payments/AddDebitCardPage.js @@ -1,6 +1,5 @@ import React, {Component} from 'react'; import {View} from 'react-native'; -import lodashGet from 'lodash/get'; import lodashEndsWith from 'lodash/endsWith'; import _ from 'underscore'; import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; @@ -44,6 +43,7 @@ class DebitCardPage extends Component { } /** + * @param {Object} values - form input values passed by the Form component * @returns {Boolean} */ validate(values) { @@ -88,6 +88,9 @@ class DebitCardPage extends Component { return errors; } + /** + * @param {Object} values - form input values passed by the Form component + */ submit(values) { PaymentMethods.addBillingCard(values); } From f1a598d874abba3a709b40e3a07ec999f65a2995 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 17 May 2022 12:49:37 -1000 Subject: [PATCH 078/331] add diff --- src/pages/home/report/ReportActionsView.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index ef0520b143f9..dc5d30c8dcff 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -31,6 +31,7 @@ import ReportActionsList from './ReportActionsList'; import CopySelectionHelper from '../../../components/CopySelectionHelper'; import EmojiPicker from '../../../components/EmojiPicker/EmojiPicker'; import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; +import * as Session from '../../../libs/actions/Session'; const propTypes = { /** The ID of the report actions will be created for */ @@ -117,6 +118,8 @@ class ReportActionsView extends React.Component { } componentDidMount() { + Session.setShouldShowComposeInput(true); + this.appStateChangeListener = AppState.addEventListener('change', () => { if (!Visibility.isVisible() || this.props.isDrawerOpen) { return; @@ -243,6 +246,7 @@ class ReportActionsView extends React.Component { if (shouldRecordMaxAction && reportBecomeVisible) { this.updateNewMarkerPosition(this.props.report.unreadActionCount); Report.updateLastReadActionID(this.props.reportID); + Session.setShouldShowComposeInput(true); } } From 06386dbceb2b20b91bfa38cd44ccb6dfbb7110a2 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 17 May 2022 13:06:10 -1000 Subject: [PATCH 079/331] add changes --- src/pages/home/report/ReportActionItemMessageEdit.js | 3 +++ src/pages/home/report/ReportActionsView.js | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pages/home/report/ReportActionItemMessageEdit.js b/src/pages/home/report/ReportActionItemMessageEdit.js index 3a5db8e5bd1a..88955221a1db 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.js +++ b/src/pages/home/report/ReportActionItemMessageEdit.js @@ -210,6 +210,9 @@ class ReportActionItemMessageEdit extends React.Component { ReportScrollManager.scrollToIndex({animated: true, index: this.props.index}, true); toggleReportActionComposeView(false, VirtualKeyboard.shouldAssumeIsOpen()); }} + onBlur={() => { + toggleReportActionComposeView(true, VirtualKeyboard.shouldAssumeIsOpen()); + }} selection={this.state.selection} onSelectionChange={this.onSelectionChange} /> diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index dc5d30c8dcff..ef0520b143f9 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -31,7 +31,6 @@ import ReportActionsList from './ReportActionsList'; import CopySelectionHelper from '../../../components/CopySelectionHelper'; import EmojiPicker from '../../../components/EmojiPicker/EmojiPicker'; import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; -import * as Session from '../../../libs/actions/Session'; const propTypes = { /** The ID of the report actions will be created for */ @@ -118,8 +117,6 @@ class ReportActionsView extends React.Component { } componentDidMount() { - Session.setShouldShowComposeInput(true); - this.appStateChangeListener = AppState.addEventListener('change', () => { if (!Visibility.isVisible() || this.props.isDrawerOpen) { return; @@ -246,7 +243,6 @@ class ReportActionsView extends React.Component { if (shouldRecordMaxAction && reportBecomeVisible) { this.updateNewMarkerPosition(this.props.report.unreadActionCount); Report.updateLastReadActionID(this.props.reportID); - Session.setShouldShowComposeInput(true); } } From 43fb53c9af0b16b3181da42f78142ce34fa0a5e7 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 17 May 2022 15:55:36 -1000 Subject: [PATCH 080/331] remove logs --- package-lock.json | 229 ++++++++++++++++-- package.json | 1 + src/components/Button.js | 5 + src/libs/EmojiUtils.js | 3 +- .../home/report/ReportActionItemFragment.js | 5 +- .../report/ReportActionItemMessageEdit.js | 13 +- 6 files changed, 232 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3652910fd625..6d9570d885b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24755,7 +24755,6 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -24767,7 +24766,6 @@ "version": "1.19.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", @@ -24794,14 +24792,12 @@ "has-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" }, "internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, "requires": { "get-intrinsic": "^1.1.0", "has": "^1.0.3", @@ -24811,20 +24807,17 @@ "is-callable": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" }, "is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, "requires": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -24834,7 +24827,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -24842,14 +24834,12 @@ "object-inspect": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", - "dev": true + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" }, "object.assign": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, "requires": { "call-bind": "^1.0.0", "define-properties": "^1.1.3", @@ -24861,7 +24851,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "requires": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -24872,7 +24861,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -24882,7 +24870,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -24899,8 +24886,7 @@ "functions-have-names": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.2.tgz", - "integrity": "sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA==", - "dev": true + "integrity": "sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA==" }, "fuse.js": { "version": "3.6.1", @@ -25366,6 +25352,14 @@ } } }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "requires": { + "get-intrinsic": "^1.1.1" + } + }, "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", @@ -39681,6 +39675,203 @@ } } }, + "string.prototype.replaceall": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.replaceall/-/string.prototype.replaceall-1.0.6.tgz", + "integrity": "sha512-OA8VDhE7ssNFlyoDXUHxw6V5cjgPrtosyJKqJX5i1P5tV9eUynsbhx1yz0g+Ye4fjFwAxhKLxt8GSRx2Aqc+Sw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.2", + "is-regex": "^1.1.4" + }, + "dependencies": { + "es-abstract": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } + }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "dependencies": { + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + } + } + }, + "string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "dependencies": { + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + } + } + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + } + } + }, "string.prototype.trim": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.5.tgz", diff --git a/package.json b/package.json index 2c55c635b2ab..58029c6e90d7 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,7 @@ "rn-fetch-blob": "^0.12.0", "save": "^2.4.0", "shim-keyboard-event-key": "^1.0.3", + "string.prototype.replaceall": "^1.0.6", "underscore": "^1.13.1", "urbanairship-react-native": "^11.0.2" }, diff --git a/src/components/Button.js b/src/components/Button.js index 85254d4c1e75..d02542438baf 100644 --- a/src/components/Button.js +++ b/src/components/Button.js @@ -103,6 +103,9 @@ const propTypes = { /** Whether Button is on active screen */ isFocused: PropTypes.bool.isRequired, + + /** Id to use for this button */ + nativeID: PropTypes.string, }; const defaultProps = { @@ -133,6 +136,7 @@ const defaultProps = { shouldRemoveRightBorderRadius: false, shouldRemoveLeftBorderRadius: false, shouldEnableHapticFeedback: false, + nativeID: '', }; class Button extends Component { @@ -250,6 +254,7 @@ class Button extends Component { this.props.isDisabled ? styles.cursorDisabled : {}, ...this.additionalStyles, ]} + nativeID={this.props.nativeID} > {({pressed, hovered}) => ( { // If the only difference between fragment.text and fragment.html is
tags // we replace them with line breaks and render it as text, not as html. // This is done to render emojis with line breaks between them as text. - const differByLineBreaksOnly = props.fragment.html.replaceAll('
', ' ') === props.fragment.text; + const differByLineBreaksOnly = replaceAll(props.fragment.html, '
', ' ') === props.fragment.text; if (differByLineBreaksOnly) { - const textWithLineBreaks = props.fragment.html.replaceAll('
', '\n'); + const textWithLineBreaks = replaceAll(props.fragment.html, '
', '\n'); // eslint-disable-next-line no-param-reassign props.fragment = {...props.fragment, text: textWithLineBreaks, html: textWithLineBreaks}; } diff --git a/src/pages/home/report/ReportActionItemMessageEdit.js b/src/pages/home/report/ReportActionItemMessageEdit.js index 88955221a1db..839c4a9488c0 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.js +++ b/src/pages/home/report/ReportActionItemMessageEdit.js @@ -210,7 +210,12 @@ class ReportActionItemMessageEdit extends React.Component { ReportScrollManager.scrollToIndex({animated: true, index: this.props.index}, true); toggleReportActionComposeView(false, VirtualKeyboard.shouldAssumeIsOpen()); }} - onBlur={() => { + onBlur={({nativeEvent}) => { + // Return to prevent re-render when save button is pressed which cancels the onPress event by re-rendering + if (nativeEvent.relatedTarget.id === 'saveButton') { + return; + } + toggleReportActionComposeView(true, VirtualKeyboard.shouldAssumeIsOpen()); }} selection={this.state.selection} @@ -235,8 +240,12 @@ class ReportActionItemMessageEdit extends React.Component {