From c58619312f99ed7b337c39622f872a5265273b09 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 25 Apr 2023 13:32:46 +0800 Subject: [PATCH 0001/1188] refactor iou page by seperating each step as individual screen --- src/ROUTES.js | 34 +- .../MoneyRequestConfirmationList.js | 94 +--- src/libs/IOUUtils.js | 14 + .../Navigation/AppNavigator/AuthScreens.js | 16 +- .../AppNavigator/ModalStackNavigators.js | 63 +-- src/libs/Navigation/linkingConfig.js | 21 +- src/libs/actions/IOU.js | 20 - src/pages/home/report/ReportActionCompose.js | 8 +- .../FloatingActionButtonAndPopover.js | 6 +- src/pages/iou/IOUBillPage.js | 7 - src/pages/iou/IOUCurrencySelection.js | 5 +- src/pages/iou/IOURequestPage.js | 7 - src/pages/iou/IOUSendPage.js | 8 - src/pages/iou/MoneyRequestDescriptionPage.js | 39 +- src/pages/iou/MoneyRequestModal.js | 477 ------------------ src/pages/iou/steps/MoneyRequestAmountPage.js | 249 +++++---- .../iou/steps/MoneyRequestConfirmPage.js | 236 +++++++-- .../MoneyRequestParticipantsPage.js | 123 ++--- .../MoneyRequestParticipantsSelector.js | 2 +- .../MoneyRequestParticipantsSplitSelector.js | 2 +- src/pages/iou/withMoneyRequest.js | 134 +++++ tests/unit/IOUUtilsTest.js | 12 + 22 files changed, 681 insertions(+), 896 deletions(-) delete mode 100644 src/pages/iou/IOUBillPage.js delete mode 100644 src/pages/iou/IOURequestPage.js delete mode 100644 src/pages/iou/IOUSendPage.js delete mode 100644 src/pages/iou/MoneyRequestModal.js create mode 100644 src/pages/iou/withMoneyRequest.js diff --git a/src/ROUTES.js b/src/ROUTES.js index 117cd0e6c85a..40ca85985635 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -10,9 +10,6 @@ const IOU_REQUEST = 'request/new'; const IOU_BILL = 'split/new'; const IOU_SEND = 'send/new'; const IOU_DETAILS = 'iou/details'; -const IOU_REQUEST_CURRENCY = `${IOU_REQUEST}/currency`; -const IOU_BILL_CURRENCY = `${IOU_BILL}/currency`; -const IOU_SEND_CURRENCY = `${IOU_SEND}/currency`; const NEW_TASK = 'new/task'; const SETTINGS_PERSONAL_DETAILS = 'settings/profile/personal-details'; const SETTINGS_CONTACT_METHODS = 'settings/profile/contact-methods'; @@ -69,22 +66,21 @@ export default { IOU_REQUEST, IOU_BILL, IOU_SEND, - IOU_REQUEST_WITH_REPORT_ID: `${IOU_REQUEST}/:reportID?`, - IOU_BILL_WITH_REPORT_ID: `${IOU_BILL}/:reportID?`, - IOU_SEND_WITH_REPORT_ID: `${IOU_SEND}/:reportID?`, - getIouRequestRoute: reportID => `${IOU_REQUEST}/${reportID}`, - getIouSplitRoute: reportID => `${IOU_BILL}/${reportID}`, - getIOUSendRoute: reportID => `${IOU_SEND}/${reportID}`, - IOU_BILL_CURRENCY: `${IOU_BILL_CURRENCY}/:reportID?`, - IOU_REQUEST_CURRENCY: `${IOU_REQUEST_CURRENCY}/:reportID?`, - MONEY_REQUEST_DESCRIPTION: `${IOU_REQUEST}/description`, - IOU_SEND_CURRENCY: `${IOU_SEND_CURRENCY}/:reportID?`, - IOU_SEND_ADD_BANK_ACCOUNT: `${IOU_SEND}/add-bank-account`, - IOU_SEND_ADD_DEBIT_CARD: `${IOU_SEND}/add-debit-card`, - IOU_SEND_ENABLE_PAYMENTS: `${IOU_SEND}/enable-payments`, - getIouRequestCurrencyRoute: reportID => `${IOU_REQUEST_CURRENCY}/${reportID}`, - getIouBillCurrencyRoute: reportID => `${IOU_BILL_CURRENCY}/${reportID}`, - getIouSendCurrencyRoute: reportID => `${IOU_SEND_CURRENCY}/${reportID}`, + MONEY_REQUEST: ':iouType/new/:reportID?', + MONEY_REQUEST_AMOUNT: ':iouType/new/amount/:reportID?', + MONEY_REQUEST_PARTICIPANTS: ':iouType/new/participants/:reportID?', + MONEY_REQUEST_CONFIRMATION: ':iouType/new/confirmation/:reportID?', + MONEY_REQUEST_CURRENCY: ':iouType/new/currency/:reportID?', + MONEY_REQUEST_DESCRIPTION: ':iouType/new/description/:reportID?', + IOU_SEND_ADD_BANK_ACCOUNT: 'send/new/add-bank-account', + IOU_SEND_ADD_DEBIT_CARD: 'send/new/add-debit-card', + IOU_SEND_ENABLE_PAYMENTS: 'send/new/enable-payments', + getMoneyRequestRoute: (iouType, reportID = '') => `${iouType}/new/${reportID}`, + getMoneyRequestAmountRoute: (iouType, reportID = '') => `${iouType}/new/amount/${reportID}`, + getMoneyRequestParticipantsRoute: (iouType, reportID = '') => `${iouType}/new/participants/${reportID}`, + getMoneyRequestConfirmationRoute: (iouType, reportID = '') => `${iouType}/new/confirmation/${reportID}`, + getMoneyRequestCurrencyRoute: (iouType, reportID = '') => `${iouType}/new/currency/${reportID}`, + getMoneyRequestDescriptionRoute: (iouType, reportID = '') => `${iouType}/new/description/${reportID}`, IOU_DETAILS, IOU_DETAILS_ADD_BANK_ACCOUNT: `${IOU_DETAILS}/add-bank-account`, IOU_DETAILS_ADD_DEBIT_CARD: `${IOU_DETAILS}/add-debit-card`, diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index ea4be4cb4985..5414d435e547 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -18,7 +18,7 @@ import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, import * as IOUUtils from '../libs/IOUUtils'; import MenuItemWithTopDescription from './MenuItemWithTopDescription'; import Navigation from '../libs/Navigation/Navigation'; -import optionPropTypes from './optionPropTypes'; +import withMoneyRequest, {moneyRequestPropTypes} from '../pages/iou/withMoneyRequest'; const propTypes = { /** Callback to inform parent modal of success */ @@ -30,15 +30,9 @@ const propTypes = { /** Should we request a single or multiple participant selection from user */ hasMultipleParticipants: PropTypes.bool.isRequired, - /** IOU amount */ - iouAmount: PropTypes.string.isRequired, - /** IOU type */ iouType: PropTypes.string, - /** Selected participants from MoneyRequestModal with login */ - participants: PropTypes.arrayOf(optionPropTypes).isRequired, - /** Can the participants be modified or not */ canModifyParticipants: PropTypes.bool, @@ -50,29 +44,15 @@ const propTypes = { /* Onyx Props */ - /** Holds data related to IOU view state, rather than the underlying IOU data. */ - iou: PropTypes.shape({ - - /** Whether or not the IOU step is loading (creating the IOU Report) */ - loading: PropTypes.bool, - - // Selected Currency Code of the current IOU - selectedCurrencyCode: PropTypes.string, - }), - /** Current user session */ session: PropTypes.shape({ email: PropTypes.string.isRequired, }), - /** Callback function to navigate to a provided step in the MoneyRequestModal flow */ - navigateToStep: PropTypes.func.isRequired, + ...moneyRequestPropTypes, }; const defaultProps = { - iou: { - selectedCurrencyCode: CONST.CURRENCY.USD, - }, iouType: CONST.IOU.MONEY_REQUEST_TYPE.REQUEST, canModifyParticipants: false, session: { @@ -84,15 +64,6 @@ const defaultProps = { class MoneyRequestConfirmationList extends Component { constructor(props) { super(props); - - const formattedParticipants = _.map(this.getParticipantsWithAmount(props.participants), participant => ({ - ...participant, selected: true, - })); - - this.state = { - participants: formattedParticipants, - }; - this.toggleOption = this.toggleOption.bind(this); this.confirm = this.confirm.bind(this); } @@ -105,8 +76,8 @@ class MoneyRequestConfirmationList extends Component { return [{ text: this.props.translate(this.props.hasMultipleParticipants ? 'iou.split' : 'iou.request', { amount: this.props.numberFormat( - this.props.iouAmount, - {style: 'currency', currency: this.props.iou.selectedCurrencyCode}, + this.props.amount, + {style: 'currency', currency: this.props.currency}, ), }), value: this.props.hasMultipleParticipants ? CONST.IOU.MONEY_REQUEST_TYPE.SPLIT : CONST.IOU.MONEY_REQUEST_TYPE.REQUEST, @@ -118,7 +89,7 @@ class MoneyRequestConfirmationList extends Component { * @returns {Array} */ getSelectedParticipants() { - return _.filter(this.state.participants, participant => participant.selected); + return _.filter(this.props.participants, participant => participant.selected); } /** @@ -126,7 +97,7 @@ class MoneyRequestConfirmationList extends Component { * @returns {Array} */ getUnselectedParticipants() { - return _.filter(this.state.participants, participant => !participant.selected); + return _.filter(this.props.participants, participant => !participant.selected); } /** @@ -135,27 +106,17 @@ class MoneyRequestConfirmationList extends Component { * @returns {Array} */ getParticipantsWithAmount(participants) { - const iouAmount = IOUUtils.calculateAmount(participants, this.props.iouAmount, this.props.iou.selectedCurrencyCode); + const iouAmount = IOUUtils.calculateAmount(participants, this.props.amount, this.props.currency); return OptionsListUtils.getIOUConfirmationOptionsFromParticipants( participants, this.props.numberFormat(iouAmount / 100, { style: 'currency', - currency: this.props.iou.selectedCurrencyCode, + currency: this.props.currency, }), ); } - /** - * Returns the participants without amount - * - * @param {Array} participants - * @returns {Array} - */ - getParticipantsWithoutAmount(participants) { - return _.map(participants, option => _.omit(option, 'descriptiveText')); - } - /** * Returns the sections needed for the OptionsSelector * @@ -168,15 +129,14 @@ class MoneyRequestConfirmationList extends Component { const unselectedParticipants = this.getUnselectedParticipants(); const formattedSelectedParticipants = this.getParticipantsWithAmount(selectedParticipants); - const formattedUnselectedParticipants = this.getParticipantsWithoutAmount(unselectedParticipants); - const formattedParticipants = _.union(formattedSelectedParticipants, formattedUnselectedParticipants); + const formattedParticipants = _.union(formattedSelectedParticipants, unselectedParticipants); - const myIOUAmount = IOUUtils.calculateAmount(selectedParticipants, this.props.iouAmount, this.props.iou.selectedCurrencyCode, true); + const myIOUAmount = IOUUtils.calculateAmount(selectedParticipants, this.props.amount, this.props.currency, true); const formattedMyPersonalDetails = OptionsListUtils.getIOUConfirmationOptionsFromMyPersonalDetail( this.props.currentUserPersonalDetails, this.props.numberFormat(myIOUAmount / 100, { style: 'currency', - currency: this.props.iou.selectedCurrencyCode, + currency: this.props.currency, }), ); @@ -193,7 +153,7 @@ class MoneyRequestConfirmationList extends Component { indexOffset: 1, }); } else { - const formattedParticipants = this.getParticipantsWithoutAmount(this.props.participants); + const formattedParticipants = this.props.participants; sections.push({ title: this.props.translate('common.to'), data: formattedParticipants, @@ -229,15 +189,13 @@ class MoneyRequestConfirmationList extends Component { return; } - this.setState((prevState) => { - const newParticipants = _.map(prevState.participants, (participant) => { - if (participant.login === option.login) { - return {...participant, selected: !participant.selected}; - } - return participant; - }); - return {participants: newParticipants}; + const newParticipants = _.map(this.props.participants, (participant) => { + if (participant.login === option.login) { + return {...participant, selected: !participant.selected}; + } + return participant; }); + this.props.setParticipants(newParticipants); } /** @@ -265,11 +223,11 @@ class MoneyRequestConfirmationList extends Component { const selectedParticipants = this.getSelectedParticipants(); const shouldShowSettlementButton = this.props.iouType === CONST.IOU.MONEY_REQUEST_TYPE.SEND; const shouldDisableButton = selectedParticipants.length === 0; - const recipient = this.state.participants[0]; + const recipient = this.props.participants[0] || {}; const canModifyParticipants = this.props.canModifyParticipants && this.props.hasMultipleParticipants; - const formattedAmount = this.props.numberFormat(this.props.iouAmount, { + const formattedAmount = this.props.numberFormat(this.props.amount, { style: 'currency', - currency: this.props.iou.selectedCurrencyCode, + currency: this.props.currency, }); return ( @@ -296,7 +254,7 @@ class MoneyRequestConfirmationList extends Component { enablePaymentsRoute={ROUTES.IOU_SEND_ENABLE_PAYMENTS} addBankAccountRoute={ROUTES.IOU_SEND_ADD_BANK_ACCOUNT} addDebitCardRoute={ROUTES.IOU_SEND_ADD_DEBIT_CARD} - currency={this.props.iou.selectedCurrencyCode} + currency={this.props.currency} /> ) : ( this.props.navigateToStep(0)} + onPress={() => Navigation.navigate(ROUTES.getMoneyRequestAmountRoute(this.props.iouType, this.props.reportID))} style={styles.moneyRequestMenuItem} titleStyle={styles.moneyRequestConfirmationAmount} /> Navigation.navigate(ROUTES.MONEY_REQUEST_DESCRIPTION)} + onPress={() => Navigation.navigate(ROUTES.getMoneyRequestDescriptionRoute(this.props.iouType, this.props.reportID))} style={styles.moneyRequestMenuItem} /> @@ -335,8 +293,8 @@ export default compose( withLocalize, withWindowDimensions, withCurrentUserPersonalDetails, + withMoneyRequest, withOnyx({ - iou: {key: ONYXKEYS.IOU}, session: { key: ONYXKEYS.SESSION, }, diff --git a/src/libs/IOUUtils.js b/src/libs/IOUUtils.js index 1151f9c77225..05be0f3d3ea0 100644 --- a/src/libs/IOUUtils.js +++ b/src/libs/IOUUtils.js @@ -178,6 +178,19 @@ function isIOUReportPendingCurrencyConversion(reportActions, iouReport) { return hasPendingRequests; } +/** + * Checks if the iou type is one of request, send, or split. + * @param {String} iouType + * @returns {Boolean} + */ +function isValidType(iouType) { + return [ + CONST.IOU.MONEY_REQUEST_TYPE.REQUEST, + CONST.IOU.MONEY_REQUEST_TYPE.SEND, + CONST.IOU.MONEY_REQUEST_TYPE.SPLIT, + ].includes(iouType); +} + export { calculateAmount, updateIOUOwnerAndTotal, @@ -185,4 +198,5 @@ export { isIOUReportPendingCurrencyConversion, getCurrencyUnit, getCurrencyDecimals, + isValidType, }; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index f033f5a8a243..bd5a10d4e609 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -283,9 +283,9 @@ class AuthScreens extends React.Component { listeners={modalScreenListeners} /> - - ( + const children = ( @@ -31,37 +33,45 @@ function createModalStackNavigator(screens) { ))} ); + + return () => (Provider ? {children} : children); } // We use getComponent/require syntax so that file used by screens are not loaded until we need them. -const IOUBillStackNavigator = createModalStackNavigator([{ +const MoneyRequestModalStackNavigator = createModalStackNavigator([{ getComponent: () => { - const IOUBillPage = require('../../../pages/iou/IOUBillPage').default; - return IOUBillPage; + const MoneyRequestAmountPage = require('../../../pages/iou/steps/MoneyRequestAmountPage').default; + return MoneyRequestAmountPage; }, - name: 'IOU_Bill_Root', + name: 'Money_Request', }, { getComponent: () => { - const IOUCurrencySelection = require('../../../pages/iou/IOUCurrencySelection').default; - return IOUCurrencySelection; + const MoneyRequestEditAmountPage = require('../../../pages/iou/steps/MoneyRequestAmountPage').default; + return MoneyRequestEditAmountPage; }, - name: 'IOU_Bill_Currency', -}]); - -const IOURequestModalStackNavigator = createModalStackNavigator([{ + name: 'Money_Request_Amount', +}, +{ + getComponent: () => { + const MoneyRequestParticipantsPage = require('../../../pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage').default; + return MoneyRequestParticipantsPage; + }, + name: 'Money_Request_Participants', +}, +{ getComponent: () => { - const IOURequestPage = require('../../../pages/iou/IOURequestPage').default; - return IOURequestPage; + const MoneyRequestConfirmPage = require('../../../pages/iou/steps/MoneyRequestConfirmPage').default; + return MoneyRequestConfirmPage; }, - name: 'IOU_Request_Root', + name: 'Money_Request_Confirmation', }, { getComponent: () => { const IOUCurrencySelection = require('../../../pages/iou/IOUCurrencySelection').default; return IOUCurrencySelection; }, - name: 'IOU_Request_Currency', + name: 'Money_Request_Currency', }, { getComponent: () => { @@ -69,21 +79,6 @@ const IOURequestModalStackNavigator = createModalStackNavigator([{ return MoneyRequestDescriptionPage; }, name: 'Money_Request_Description', -}]); - -const IOUSendModalStackNavigator = createModalStackNavigator([{ - getComponent: () => { - const IOUSendPage = require('../../../pages/iou/IOUSendPage').default; - return IOUSendPage; - }, - name: 'IOU_Send_Root', -}, -{ - getComponent: () => { - const IOUCurrencySelection = require('../../../pages/iou/IOUCurrencySelection').default; - return IOUCurrencySelection; - }, - name: 'IOU_Send_Currency', }, { getComponent: () => { @@ -105,7 +100,7 @@ const IOUSendModalStackNavigator = createModalStackNavigator([{ return EnablePaymentsPage; }, name: 'IOU_Send_Enable_Payments', -}]); +}], MoneyRequestProvider); const IOUDetailsModalStackNavigator = createModalStackNavigator([{ getComponent: () => { @@ -541,9 +536,7 @@ const YearPickerStackNavigator = createModalStackNavigator([{ }]); export { - IOUBillStackNavigator, - IOURequestModalStackNavigator, - IOUSendModalStackNavigator, + MoneyRequestModalStackNavigator, IOUDetailsModalStackNavigator, DetailsModalStackNavigator, ReportDetailsModalStackNavigator, diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index f9c1361538ed..28fe687367f9 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -227,23 +227,14 @@ export default { ReportParticipants_Details: ROUTES.REPORT_PARTICIPANT, }, }, - IOU_Request: { + MoneyRequest: { screens: { - IOU_Request_Root: ROUTES.IOU_REQUEST_WITH_REPORT_ID, - IOU_Request_Currency: ROUTES.IOU_REQUEST_CURRENCY, + Money_Request: ROUTES.MONEY_REQUEST, + Money_Request_Amount: ROUTES.MONEY_REQUEST_AMOUNT, + Money_Request_Participants: ROUTES.MONEY_REQUEST_PARTICIPANTS, + Money_Request_Confirmation: ROUTES.MONEY_REQUEST_CONFIRMATION, + Money_Request_Currency: ROUTES.MONEY_REQUEST_CURRENCY, Money_Request_Description: ROUTES.MONEY_REQUEST_DESCRIPTION, - }, - }, - IOU_Bill: { - screens: { - IOU_Bill_Root: ROUTES.IOU_BILL_WITH_REPORT_ID, - IOU_Bill_Currency: ROUTES.IOU_BILL_CURRENCY, - }, - }, - IOU_Send: { - screens: { - IOU_Send_Root: ROUTES.IOU_SEND_WITH_REPORT_ID, - IOU_Send_Currency: ROUTES.IOU_SEND_CURRENCY, IOU_Send_Enable_Payments: ROUTES.IOU_SEND_ENABLE_PAYMENTS, IOU_Send_Add_Bank_Account: ROUTES.IOU_SEND_ADD_BANK_ACCOUNT, IOU_Send_Add_Debit_Card: ROUTES.IOU_SEND_ADD_DEBIT_CARD, diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 050c1b0e7f6b..d2623ed3193c 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -627,24 +627,6 @@ function cancelMoneyRequest(chatReportID, iouReportID, type, moneyRequestAction) Navigation.navigate(ROUTES.getReportRoute(chatReportID)); } -/** - * Sets IOU'S selected currency - * - * @param {String} selectedCurrencyCode - */ -function setIOUSelectedCurrency(selectedCurrencyCode) { - Onyx.merge(ONYXKEYS.IOU, {selectedCurrencyCode}); -} - -/** - * Sets Money Request description - * - * @param {String} comment - */ -function setMoneyRequestDescription(comment) { - Onyx.merge(ONYXKEYS.IOU, {comment: comment.trim()}); -} - /** * @param {Number} amount * @param {String} submitterPayPalMeAddress @@ -1010,8 +992,6 @@ export { sendMoneyViaPaypal, payMoneyRequestElsewhere, payMoneyRequestViaPaypal, - setIOUSelectedCurrency, - setMoneyRequestDescription, sendMoneyWithWallet, payMoneyRequestWithWallet, }; diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 0e984f5a0aa8..dee721894fe6 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -361,20 +361,20 @@ class ReportActionCompose extends React.Component { [CONST.IOU.MONEY_REQUEST_TYPE.SPLIT]: { icon: Expensicons.Receipt, text: this.props.translate('iou.splitBill'), - onSelected: () => Navigation.navigate(ROUTES.getIouSplitRoute(this.props.reportID)), }, [CONST.IOU.MONEY_REQUEST_TYPE.REQUEST]: { icon: Expensicons.MoneyCircle, text: this.props.translate('iou.requestMoney'), - onSelected: () => Navigation.navigate(ROUTES.getIouRequestRoute(this.props.reportID)), }, [CONST.IOU.MONEY_REQUEST_TYPE.SEND]: { icon: Expensicons.Send, text: this.props.translate('iou.sendMoney'), - onSelected: () => Navigation.navigate(ROUTES.getIOUSendRoute(this.props.reportID)), }, }; - return _.map(ReportUtils.getMoneyRequestOptions(this.props.report, reportParticipants, this.props.betas), option => options[option]); + return _.map(ReportUtils.getMoneyRequestOptions(this.props.report, reportParticipants, this.props.betas), option => ({ + ...options[option], + onSelected: () => Navigation.navigate(ROUTES.getMoneyRequestRoute(option, this.props.reportID)), + })); } /** diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js index ebff5f4e6a51..815014bf5a04 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js @@ -194,21 +194,21 @@ class FloatingActionButtonAndPopover extends React.Component { { icon: Expensicons.Send, text: this.props.translate('iou.sendMoney'), - onSelected: () => Navigation.navigate(ROUTES.IOU_SEND), + onSelected: () => Navigation.navigate(ROUTES.getMoneyRequestRoute(CONST.IOU.MONEY_REQUEST_TYPE.SEND)), }, ] : []), ...(Permissions.canUseIOU(this.props.betas) ? [ { icon: Expensicons.MoneyCircle, text: this.props.translate('iou.requestMoney'), - onSelected: () => Navigation.navigate(ROUTES.IOU_REQUEST), + onSelected: () => Navigation.navigate(ROUTES.getMoneyRequestRoute(CONST.IOU.MONEY_REQUEST_TYPE.REQUEST)), }, ] : []), ...(Permissions.canUseIOU(this.props.betas) ? [ { icon: Expensicons.Receipt, text: this.props.translate('iou.splitBill'), - onSelected: () => Navigation.navigate(ROUTES.IOU_BILL), + onSelected: () => Navigation.navigate(ROUTES.getMoneyRequestRoute(CONST.IOU.MONEY_REQUEST_TYPE.SPLIT)), }, ] : []), ...(Permissions.canUseTasks(this.props.betas) ? [ diff --git a/src/pages/iou/IOUBillPage.js b/src/pages/iou/IOUBillPage.js deleted file mode 100644 index 6ca3bc6ec916..000000000000 --- a/src/pages/iou/IOUBillPage.js +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; -import MoneyRequestModal from './MoneyRequestModal'; - -// eslint-disable-next-line react/jsx-props-no-spreading -const IOUBillPage = props => ; -IOUBillPage.displayName = 'IOUBillPage'; -export default IOUBillPage; diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index 1d4524be7d66..f29967dea61f 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -10,9 +10,9 @@ import ScreenWrapper from '../../components/ScreenWrapper'; import HeaderWithCloseButton from '../../components/HeaderWithCloseButton'; import compose from '../../libs/compose'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; -import * as IOU from '../../libs/actions/IOU'; import * as CurrencySymbolUtils from '../../libs/CurrencySymbolUtils'; import {withNetwork} from '../../components/OnyxProvider'; +import withMoneyRequest from './withMoneyRequest'; /** * IOU Currency selection for selecting currency @@ -105,7 +105,7 @@ class IOUCurrencySelection extends Component { * @param {String} option.currencyCode */ confirmCurrencySelection(option) { - IOU.setIOUSelectedCurrency(option.currencyCode); + this.props.setCurrency(option.currencyCode); Navigation.goBack(); } @@ -140,6 +140,7 @@ IOUCurrencySelection.defaultProps = defaultProps; export default compose( withLocalize, + withMoneyRequest, withOnyx({ currencyList: {key: ONYXKEYS.CURRENCY_LIST}, }), diff --git a/src/pages/iou/IOURequestPage.js b/src/pages/iou/IOURequestPage.js deleted file mode 100644 index 0d45ba531878..000000000000 --- a/src/pages/iou/IOURequestPage.js +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; -import MoneyRequestModal from './MoneyRequestModal'; - -// eslint-disable-next-line react/jsx-props-no-spreading -const IOURequestPage = props => ; -IOURequestPage.displayName = 'IOURequestPage'; -export default IOURequestPage; diff --git a/src/pages/iou/IOUSendPage.js b/src/pages/iou/IOUSendPage.js deleted file mode 100644 index b4bb94a6c0f9..000000000000 --- a/src/pages/iou/IOUSendPage.js +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react'; -import CONST from '../../CONST'; -import MoneyRequestModal from './MoneyRequestModal'; - -// eslint-disable-next-line react/jsx-props-no-spreading -const IOUSendPage = props => ; -IOUSendPage.displayName = 'IOUSendPage'; -export default IOUSendPage; diff --git a/src/pages/iou/MoneyRequestDescriptionPage.js b/src/pages/iou/MoneyRequestDescriptionPage.js index b03debb9bb86..16614b70a376 100644 --- a/src/pages/iou/MoneyRequestDescriptionPage.js +++ b/src/pages/iou/MoneyRequestDescriptionPage.js @@ -1,7 +1,6 @@ import React, {Component} from 'react'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import PropTypes from 'prop-types'; +import lodashGet from 'lodash/get'; import TextInput from '../../components/TextInput'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import ScreenWrapper from '../../components/ScreenWrapper'; @@ -11,22 +10,11 @@ import ONYXKEYS from '../../ONYXKEYS'; import styles from '../../styles/styles'; import Navigation from '../../libs/Navigation/Navigation'; import compose from '../../libs/compose'; -import * as IOU from '../../libs/actions/IOU'; +import withMoneyRequest, {moneyRequestPropTypes} from './withMoneyRequest'; const propTypes = { ...withLocalizePropTypes, - - /** Onyx Props */ - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: PropTypes.shape({ - comment: PropTypes.string, - }), -}; - -const defaultProps = { - iou: { - comment: '', - }, + ...moneyRequestPropTypes, }; class MoneyRequestDescriptionPage extends Component { @@ -36,12 +24,10 @@ class MoneyRequestDescriptionPage extends Component { this.updateComment = this.updateComment.bind(this); } - /** - * Closes the modal and clears the description from Onyx. - */ - onCloseButtonPress() { - IOU.setMoneyRequestDescription(''); - Navigation.dismissModal(); + componentDidMount() { + const iouType = lodashGet(this.props.route, 'params.iouType', ''); + const reportID = lodashGet(this.props.route, 'params.reportID', ''); + this.props.redirectIfEmpty([this.props.participants, this.props.amount], iouType, reportID); } /** @@ -51,7 +37,7 @@ class MoneyRequestDescriptionPage extends Component { * @param {String} value.moneyRequestComment */ updateComment(value) { - IOU.setMoneyRequestDescription(value.moneyRequestComment); + this.props.setComment(value.moneyRequestComment); Navigation.goBack(); } @@ -62,7 +48,7 @@ class MoneyRequestDescriptionPage extends Component { title={this.props.translate('common.description')} shouldShowBackButton onBackButtonPress={Navigation.goBack} - onCloseButtonPress={this.onCloseButtonPress} + onCloseButtonPress={Navigation.dismissModal} />
@@ -87,11 +73,8 @@ class MoneyRequestDescriptionPage extends Component { } MoneyRequestDescriptionPage.propTypes = propTypes; -MoneyRequestDescriptionPage.defaultProps = defaultProps; export default compose( withLocalize, - withOnyx({ - iou: {key: ONYXKEYS.IOU}, - }), + withMoneyRequest, )(MoneyRequestDescriptionPage); diff --git a/src/pages/iou/MoneyRequestModal.js b/src/pages/iou/MoneyRequestModal.js deleted file mode 100644 index fb727646a61b..000000000000 --- a/src/pages/iou/MoneyRequestModal.js +++ /dev/null @@ -1,477 +0,0 @@ -import _ from 'underscore'; -import React, { - useState, useEffect, useRef, useCallback, useMemo, -} from 'react'; -import {View} from 'react-native'; -import PropTypes from 'prop-types'; -import lodashGet from 'lodash/get'; -import {withOnyx} from 'react-native-onyx'; -import MoneyRequestAmountPage from './steps/MoneyRequestAmountPage'; -import MoneyRequestParticipantsPage from './steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage'; -import MoneyRequestConfirmPage from './steps/MoneyRequestConfirmPage'; -import ModalHeader from './ModalHeader'; -import styles from '../../styles/styles'; -import * as IOU from '../../libs/actions/IOU'; -import Navigation from '../../libs/Navigation/Navigation'; -import ONYXKEYS from '../../ONYXKEYS'; -import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; -import compose from '../../libs/compose'; -import * as OptionsListUtils from '../../libs/OptionsListUtils'; -import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator'; -import AnimatedStep from '../../components/AnimatedStep'; -import ScreenWrapper from '../../components/ScreenWrapper'; -import CONST from '../../CONST'; -import * as PersonalDetails from '../../libs/actions/PersonalDetails'; -import withCurrentUserPersonalDetails from '../../components/withCurrentUserPersonalDetails'; -import networkPropTypes from '../../components/networkPropTypes'; -import {withNetwork} from '../../components/OnyxProvider'; -import reportPropTypes from '../reportPropTypes'; -import * as ReportUtils from '../../libs/ReportUtils'; -import * as ReportScrollManager from '../../libs/ReportScrollManager'; -import * as DeviceCapabilities from '../../libs/DeviceCapabilities'; - -/** - * A modal used for requesting money, splitting bills or sending money. - */ -const propTypes = { - /** Whether the request is for a single request or a group bill split */ - hasMultipleParticipants: PropTypes.bool, - - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.string, - - /** The report passed via the route */ - // eslint-disable-next-line react/no-unused-prop-types - report: reportPropTypes, - - /** Information about the network */ - network: networkPropTypes.isRequired, - - // Holds data related to request view state, rather than the underlying request data. - iou: PropTypes.shape({ - /** Whether or not transaction creation has started */ - creatingIOUTransaction: PropTypes.bool, - - /** Whether or not transaction creation has resulted to error */ - error: PropTypes.bool, - - // Selected Currency Code of the current request - selectedCurrencyCode: PropTypes.string, - }), - - /** Personal details of all the users */ - personalDetails: PropTypes.shape({ - /** Primary login of participant */ - login: PropTypes.string, - - /** Display Name of participant */ - displayName: PropTypes.string, - - /** Avatar url of participant */ - avatar: PropTypes.string, - }), - - /** Personal details of the current user */ - currentUserPersonalDetails: PropTypes.shape({ - // Local Currency Code of the current user - localCurrencyCode: PropTypes.string, - }), - - ...withLocalizePropTypes, -}; - -const defaultProps = { - hasMultipleParticipants: false, - report: { - participants: [], - }, - iouType: CONST.IOU.MONEY_REQUEST_TYPE.REQUEST, - currentUserPersonalDetails: { - localCurrencyCode: CONST.CURRENCY.USD, - }, - personalDetails: {}, - iou: { - creatingIOUTransaction: false, - error: false, - selectedCurrencyCode: null, - }, -}; - -// Determines type of step to display within Modal, value provides the title for that page. -const Steps = { - MoneyRequestAmount: 'moneyRequest.amount', - MoneyRequestParticipants: 'moneyRequest.participants', - MoneyRequestConfirm: 'moneyRequest.confirm', -}; - -const MoneyRequestModal = (props) => { - // Skip MoneyRequestParticipants step if participants are passed in - const reportParticipants = lodashGet(props, 'report.participants', []); - const steps = useMemo(() => (reportParticipants.length - ? [Steps.MoneyRequestAmount, Steps.MoneyRequestConfirm] - : [Steps.MoneyRequestAmount, Steps.MoneyRequestParticipants, Steps.MoneyRequestConfirm]), - [reportParticipants.length]); - const prevCreatingIOUTransactionStatusRef = useRef(lodashGet(props.iou, 'creatingIOUTransaction')); - const prevNetworkStatusRef = useRef(props.network.isOffline); - - const [previousStepIndex, setPreviousStepIndex] = useState(-1); - const [currentStepIndex, setCurrentStepIndex] = useState(0); - const [selectedOptions, setSelectedOptions] = useState( - ReportUtils.isPolicyExpenseChat(props.report) - ? OptionsListUtils.getPolicyExpenseReportOptions(props.report) - : OptionsListUtils.getParticipantsOptions(props.report, props.personalDetails), - ); - const [amount, setAmount] = useState(''); - - useEffect(() => { - PersonalDetails.openMoneyRequestModalPage(); - IOU.setIOUSelectedCurrency(props.currentUserPersonalDetails.localCurrencyCode); - IOU.setMoneyRequestDescription(''); - // eslint-disable-next-line react-hooks/exhaustive-deps -- props.currentUserPersonalDetails will always exist from Onyx and we don't want this effect to run again - }, []); - - useEffect(() => { - // We only want to check if we just finished creating an IOU transaction - // We check it within this effect because we're sending the request optimistically but if an error occurs from the API, we will update the iou state with the error later - if (!prevCreatingIOUTransactionStatusRef.current || lodashGet(props.iou, 'creatingIOUTransaction')) { - return; - } - - if (lodashGet(props.iou, 'error') === true) { - setCurrentStepIndex(0); - } else { - Navigation.dismissModal(); - } - }, [props.iou]); - - useEffect(() => { - if (props.network.isOffline || !prevNetworkStatusRef.current) { - return; - } - - // User came back online, so let's refetch the currency details based on location - PersonalDetails.openMoneyRequestModalPage(); - }, [props.network.isOffline]); - - useEffect(() => { - // Used to store previous prop values to compare on next render - prevNetworkStatusRef.current = props.network.isOffline; - prevCreatingIOUTransactionStatusRef.current = lodashGet(props.iou, 'creatingIOUTransaction'); - }); - - /** - * Decides our animation type based on whether we're increasing or decreasing - * our step index. - * @returns {String|null} - */ - const direction = useMemo(() => { - // If we're going to the "amount" step from the "confirm" step, push it in and pop it out like we're moving - // forward instead of backwards. - const amountIndex = _.indexOf(steps, Steps.MoneyRequestAmount); - const confirmIndex = _.indexOf(steps, Steps.MoneyRequestConfirm); - if (previousStepIndex === confirmIndex && currentStepIndex === amountIndex) { - return 'in'; - } - if (previousStepIndex === amountIndex && currentStepIndex === confirmIndex) { - return 'out'; - } - - if (previousStepIndex < currentStepIndex) { - return 'in'; - } - if (previousStepIndex > currentStepIndex) { - return 'out'; - } - - // Doesn't animate the step when first opening the modal - if (previousStepIndex === currentStepIndex) { - return null; - } - }, [previousStepIndex, currentStepIndex, steps]); - - /** - * Retrieve title for current step, based upon current step and type of request - * - * @returns {String} - */ - const titleForStep = useMemo(() => { - if (currentStepIndex === 0) { - const confirmIndex = _.indexOf(steps, Steps.MoneyRequestConfirm); - if (previousStepIndex === confirmIndex) { - return props.translate('iou.amount'); - } - if (props.iouType === CONST.IOU.MONEY_REQUEST_TYPE.SEND) { - return props.translate('iou.sendMoney'); - } - return props.translate(props.hasMultipleParticipants ? 'iou.splitBill' : 'iou.requestMoney'); - } - return props.translate('iou.cash'); - // eslint-disable-next-line react-hooks/exhaustive-deps -- props does not need to be a dependency as it will always exist - }, [currentStepIndex, props.translate, steps]); - - /** - * Navigate to a provided step. - * - * @param {Number} stepIndex - * @type {(function(*): void)|*} - */ - const navigateToStep = useCallback((stepIndex) => { - if (stepIndex < 0 || stepIndex > steps.length) { - return; - } - - if (currentStepIndex === stepIndex) { - return; - } - - setPreviousStepIndex(currentStepIndex); - setCurrentStepIndex(stepIndex); - }, [currentStepIndex, steps.length]); - - /** - * Navigate to the previous request step if possible - */ - const navigateToPreviousStep = useCallback(() => { - if (currentStepIndex <= 0 && previousStepIndex < 0) { - return; - } - - setPreviousStepIndex(currentStepIndex); - setCurrentStepIndex(currentStepIndex - 1); - }, [currentStepIndex, previousStepIndex]); - - /** - * Navigate to the next request step if possible - */ - const navigateToNextStep = useCallback(() => { - if (currentStepIndex >= steps.length - 1) { - return; - } - - // If we're coming from the confirm step, it means we were editing something so go back to the confirm step. - const confirmIndex = _.indexOf(steps, Steps.MoneyRequestConfirm); - if (previousStepIndex === confirmIndex) { - navigateToStep(confirmIndex); - return; - } - - setPreviousStepIndex(currentStepIndex); - setCurrentStepIndex(currentStepIndex + 1); - }, [currentStepIndex, previousStepIndex, navigateToStep, steps]); - - /** - * Checks if user has a GOLD wallet then creates a paid IOU report on the fly - * - * @param {String} paymentMethodType - */ - const sendMoney = useCallback((paymentMethodType) => { - const amountInDollars = Math.round(amount * 100); - const currency = props.iou.selectedCurrencyCode; - const trimmedComment = props.iou.comment.trim(); - const participant = selectedOptions[0]; - - if (paymentMethodType === CONST.IOU.PAYMENT_TYPE.ELSEWHERE) { - IOU.sendMoneyElsewhere( - props.report, - amountInDollars, - currency, - trimmedComment, - props.currentUserPersonalDetails.login, - participant, - ); - return; - } - - if (paymentMethodType === CONST.IOU.PAYMENT_TYPE.PAYPAL_ME) { - IOU.sendMoneyViaPaypal( - props.report, - amountInDollars, - currency, - trimmedComment, - props.currentUserPersonalDetails.login, - participant, - ); - return; - } - - if (paymentMethodType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY) { - IOU.sendMoneyWithWallet( - props.report, - amountInDollars, - currency, - trimmedComment, - props.currentUserPersonalDetails.login, - participant, - ); - } - }, [amount, props.iou.comment, selectedOptions, props.currentUserPersonalDetails.login, props.iou.selectedCurrencyCode, props.report]); - - /** - * @param {Array} selectedParticipants - */ - const createTransaction = useCallback((selectedParticipants) => { - const reportID = lodashGet(props.route, 'params.reportID', ''); - const trimmedComment = props.iou.comment.trim(); - - // IOUs created from a group report will have a reportID param in the route. - // Since the user is already viewing the report, we don't need to navigate them to the report - if (props.hasMultipleParticipants && CONST.REGEX.NUMBER.test(reportID)) { - IOU.splitBill( - selectedParticipants, - props.currentUserPersonalDetails.login, - amount, - trimmedComment, - props.iou.selectedCurrencyCode, - props.preferredLocale, - reportID, - ); - return; - } - - // If the request is created from the global create menu, we also navigate the user to the group report - if (props.hasMultipleParticipants) { - IOU.splitBillAndOpenReport( - selectedParticipants, - props.currentUserPersonalDetails.login, - amount, - trimmedComment, - props.iou.selectedCurrencyCode, - props.preferredLocale, - ); - return; - } - if (!selectedParticipants[0].login) { - // TODO - request to the policy expense chat. Not implemented yet! - // Will be implemented here: https://github.com/Expensify/Expensify/issues/270581 - return; - } - IOU.requestMoney( - props.report, - Math.round(amount * 100), - props.iou.selectedCurrencyCode, - props.currentUserPersonalDetails.login, - selectedParticipants[0], - trimmedComment, - ); - }, [amount, props.iou.comment, props.currentUserPersonalDetails.login, props.hasMultipleParticipants, props.iou.selectedCurrencyCode, props.preferredLocale, props.report, props.route]); - - const currentStep = steps[currentStepIndex]; - const moneyRequestStepIndex = _.indexOf(steps, Steps.MoneyRequestConfirm); - const isEditingAmountAfterConfirm = currentStepIndex === 0 && previousStepIndex === _.indexOf(steps, Steps.MoneyRequestConfirm); - const reportID = lodashGet(props, 'route.params.reportID', ''); - const shouldShowBackButton = currentStepIndex > 0 || isEditingAmountAfterConfirm; - const modalHeader = ( - navigateToStep(moneyRequestStepIndex) : navigateToPreviousStep} - /> - ); - const amountButtonText = isEditingAmountAfterConfirm ? props.translate('common.save') : props.translate('common.next'); - const enableMaxHeight = DeviceCapabilities.canUseTouchScreen() && currentStep === Steps.MoneyRequestParticipants; - - return ( - - {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( - <> - - {!didScreenTransitionEnd && } - {didScreenTransitionEnd && ( - <> - {currentStep === Steps.MoneyRequestAmount && ( - - {modalHeader} - { - setAmount(value); - navigateToNextStep(); - }} - reportID={reportID} - hasMultipleParticipants={props.hasMultipleParticipants} - selectedAmount={amount} - navigation={props.navigation} - iouType={props.iouType} - buttonText={amountButtonText} - /> - - )} - {currentStep === Steps.MoneyRequestParticipants && ( - - {modalHeader} - - - )} - {currentStep === Steps.MoneyRequestConfirm && ( - - {modalHeader} - { - // TODO: ADD HANDLING TO DISABLE BUTTON FUNCTIONALITY WHILE REQUEST IS IN FLIGHT - createTransaction(selectedParticipants); - ReportScrollManager.scrollToBottom(); - }} - onSendMoney={(paymentMethodType) => { - // TODO: ADD HANDLING TO DISABLE BUTTON FUNCTIONALITY WHILE REQUEST IS IN FLIGHT - sendMoney(paymentMethodType); - ReportScrollManager.scrollToBottom(); - }} - hasMultipleParticipants={props.hasMultipleParticipants} - participants={_.filter(selectedOptions, email => props.currentUserPersonalDetails.login !== email.login)} - iouAmount={amount} - iouType={props.iouType} - - // The participants can only be modified when the action is initiated from directly within a group chat and not the floating-action-button. - // This is because when there is a group of people, say they are on a trip, and you have some shared expenses with some of the people, - // but not all of them (maybe someone skipped out on dinner). Then it's nice to be able to select/deselect people from the group chat bill - // split rather than forcing the user to create a new group, just for that expense. The reportID is empty, when the action was initiated from - // the floating-action-button (since it is something that exists outside the context of a report). - canModifyParticipants={!_.isEmpty(reportID)} - navigateToStep={navigateToStep} - /> - - )} - - )} - - - )} - - ); -}; - -MoneyRequestModal.displayName = 'MoneyRequestModal'; -MoneyRequestModal.propTypes = propTypes; -MoneyRequestModal.defaultProps = defaultProps; - -export default compose( - withLocalize, - withNetwork(), - withCurrentUserPersonalDetails, - withOnyx({ - report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID', '')}`, - }, - iou: { - key: ONYXKEYS.IOU, - }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, - }, - }), -)(MoneyRequestModal); diff --git a/src/pages/iou/steps/MoneyRequestAmountPage.js b/src/pages/iou/steps/MoneyRequestAmountPage.js index 7fb169fa3d84..810bab43f703 100755 --- a/src/pages/iou/steps/MoneyRequestAmountPage.js +++ b/src/pages/iou/steps/MoneyRequestAmountPage.js @@ -4,52 +4,58 @@ import { InteractionManager, } from 'react-native'; import PropTypes from 'prop-types'; -import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import _ from 'underscore'; -import ONYXKEYS from '../../../ONYXKEYS'; +import {withOnyx} from 'react-native-onyx'; import styles from '../../../styles/styles'; import BigNumberPad from '../../../components/BigNumberPad'; import Navigation from '../../../libs/Navigation/Navigation'; import ROUTES from '../../../ROUTES'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; import compose from '../../../libs/compose'; +import * as OptionsListUtils from '../../../libs/OptionsListUtils'; +import * as ReportUtils from '../../../libs/ReportUtils'; +import * as IOUUtils from '../../../libs/IOUUtils'; import Button from '../../../components/Button'; import CONST from '../../../CONST'; import * as DeviceCapabilities from '../../../libs/DeviceCapabilities'; import TextInputWithCurrencySymbol from '../../../components/TextInputWithCurrencySymbol'; +import ScreenWrapper from '../../../components/ScreenWrapper'; +import FullPageNotFoundView from '../../../components/BlockingViews/FullPageNotFoundView'; +import withMoneyRequest, {moneyRequestPropTypes} from '../withMoneyRequest'; +import withNavigation from '../../../components/withNavigation'; +import ModalHeader from '../ModalHeader'; +import ONYXKEYS from '../../../ONYXKEYS'; +import personalDetailsPropType from '../../personalDetailsPropType'; +import reportPropTypes from '../../reportPropTypes'; const propTypes = { - /** Whether or not this IOU has multiple participants */ - hasMultipleParticipants: PropTypes.bool.isRequired, - - /** The ID of the report this screen should display */ - reportID: PropTypes.string.isRequired, - - /** Callback to inform parent modal of success */ - onStepComplete: PropTypes.func.isRequired, - - /** Previously selected amount to show if the user comes back to this screen */ - selectedAmount: PropTypes.string.isRequired, - - /** Text to display on the button that "saves" the amount */ - buttonText: PropTypes.string.isRequired, + route: PropTypes.shape({ + params: PropTypes.shape({ + iouType: PropTypes.string, + reportID: PropTypes.string, + }), + }), - /* Onyx Props */ + /** The report on which the request is initiated on */ + report: reportPropTypes, - /** Holds data related to IOU view state, rather than the underlying IOU data. */ - iou: PropTypes.shape({ - /** Selected Currency Code of the current IOU */ - selectedCurrencyCode: PropTypes.string, - }), + /** Personal details of all users */ + personalDetails: personalDetailsPropType, ...withLocalizePropTypes, + ...moneyRequestPropTypes, }; const defaultProps = { - iou: { - selectedCurrencyCode: CONST.CURRENCY.USD, + route: { + params: { + iouType: '', + reportID: '', + }, }, + report: {}, + personalDetails: {}, }; class MoneyRequestAmountPage extends React.Component { constructor(props) { @@ -65,18 +71,31 @@ class MoneyRequestAmountPage extends React.Component { this.amountViewID = 'amountView'; this.numPadContainerViewID = 'numPadContainerView'; this.numPadViewID = 'numPadView'; + this.iouType = lodashGet(props.route, 'params.iouType', ''); + this.reportID = lodashGet(props.route, 'params.reportID', ''); + this.isEditing = lodashGet(props.route, 'path', '').includes('amount'); this.state = { - amount: props.selectedAmount, + amount: props.amount, shouldUpdateSelection: true, selection: { - start: props.selectedAmount.length, - end: props.selectedAmount.length, + start: props.amount.length, + end: props.amount.length, }, }; } componentDidMount() { + if (this.isEditing) { + this.props.redirectIfEmpty([this.props.participants, this.props.amount], this.iouType, this.reportID); + } else { + // Set the money request participants based on the report participants + const participants = ReportUtils.isPolicyExpenseChat(this.props.report) + ? OptionsListUtils.getPolicyExpenseReportOptions(this.props.report) + : OptionsListUtils.getParticipantsOptions(this.props.report, this.props.personalDetails); + this.props.setParticipants(_.map(participants, participant => ({...participant, selected: true}))); + } + this.focusTextInput(); // Focus automatically after navigating back from currency selector @@ -85,6 +104,19 @@ class MoneyRequestAmountPage extends React.Component { }); } + componentDidUpdate(prevProps) { + if (prevProps.amount === this.props.amount) { + return; + } + this.setState({ + amount: this.props.amount, + selection: { + start: this.props.amount.length, + end: this.props.amount.length, + }, + }); + } + componentWillUnmount() { this.unsubscribeNavFocus(); } @@ -139,6 +171,23 @@ class MoneyRequestAmountPage extends React.Component { return {amount: this.stripCommaFromAmount(newAmountWithoutSpaces), selection}; } + /** + * Get page title based on the iou type + * + * @returns {String} + */ + getTitleForStep() { + if (this.isEditing) { + return this.props.translate('iou.amount'); + } + const title = { + [CONST.IOU.MONEY_REQUEST_TYPE.REQUEST]: this.props.translate('iou.requestMoney'), + [CONST.IOU.MONEY_REQUEST_TYPE.SEND]: this.props.translate('iou.sendMoney'), + [CONST.IOU.MONEY_REQUEST_TYPE.SPLIT]: this.props.translate('iou.splitBill'), + }; + return title[this.iouType]; + } + /** * Focus text input */ @@ -291,72 +340,95 @@ class MoneyRequestAmountPage extends React.Component { } navigateToCurrencySelectionPage() { - if (this.props.hasMultipleParticipants) { - return Navigation.navigate(ROUTES.getIouBillCurrencyRoute(this.props.reportID)); + Navigation.navigate(ROUTES.getMoneyRequestCurrencyRoute(this.iouType, this.reportID)); + } + + navigateToNextPage() { + if (this.isEditing) { + Navigation.goBack(); + return; } - if (this.props.iouType === CONST.IOU.MONEY_REQUEST_TYPE.SEND) { - return Navigation.navigate(ROUTES.getIouSendCurrencyRoute(this.props.reportID)); + + // If a request is initiated on a report, skip the participants selection step and navigate to the confirmation page. + if (this.reportID) { + Navigation.navigate(ROUTES.getMoneyRequestConfirmationRoute(this.iouType, this.reportID)); + return; } - return Navigation.navigate(ROUTES.getIouRequestCurrencyRoute(this.props.reportID)); + Navigation.navigate(ROUTES.getMoneyRequestParticipantsRoute(this.iouType)); } render() { const formattedAmount = this.replaceAllDigits(this.state.amount, this.props.toLocaleDigit); + const buttonText = this.isEditing ? this.props.translate('common.save') : this.props.translate('common.next'); return ( - <> - this.onMouseDown(event, [this.amountViewID])} - style={[ - styles.flex1, - styles.flexRow, - styles.w100, - styles.alignItemsCenter, - styles.justifyContentCenter, - ]} - > - this.textInput = el} - selectedCurrencyCode={this.props.iou.selectedCurrencyCode} - selection={this.state.selection} - onSelectionChange={(e) => { - if (!this.state.shouldUpdateSelection) { - return; - } - this.setState({selection: e.nativeEvent.selection}); - }} - /> - - this.onMouseDown(event, [this.numPadContainerViewID, this.numPadViewID])} - style={[styles.w100, styles.justifyContentEnd, styles.pageWrapper]} - nativeID={this.numPadContainerViewID} - > - {DeviceCapabilities.canUseTouchScreen() - ? ( - + + {({safeAreaPaddingBottomStyle}) => ( + + - ) : } - -