From d0190cdc932727fc23b072bb11caaddafae80d42 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Wed, 7 Apr 2021 17:51:33 +0100 Subject: [PATCH 001/141] create dynamic IOU Details route and empty Modal --- src/ROUTES.js | 5 ++ .../Navigation/AppNavigator/AuthScreens.js | 6 ++ .../AppNavigator/ModalStackNavigators.js | 20 ++++++ src/libs/Navigation/linkingConfig.js | 5 ++ src/pages/iou/IOUDetailsPage.js | 68 +++++++++++++++++++ 5 files changed, 104 insertions(+) create mode 100644 src/pages/iou/IOUDetailsPage.js diff --git a/src/ROUTES.js b/src/ROUTES.js index 404b8c4cfddb..340921f2e438 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -21,10 +21,15 @@ export default { REPORT, REPORT_WITH_ID: 'r/:reportID', getReportRoute: reportID => `r/${reportID}`, + IOU_REQUEST: 'iou/request', IOU_REQUEST: 'iou/request/:reportID', getIouRequestRoute: reportID => `iou/request/${reportID}`, + IOU_BILL: 'iou/split', IOU_BILL: 'iou/split/:reportID', getIouSplitRoute: reportID => `iou/split/${reportID}`, + IOU_DETAILS: 'iou/details', + IOU_DETAILS_WITH_IOU_REPORT_ID: 'iou/details/:iouReportID', + getIouDetailsRoute: iouReportID => `iou/details/${iouReportID}`, SEARCH: 'search', SIGNIN: 'signin', SET_PASSWORD_WITH_VALIDATE_CODE: 'setpassword/:accountID/:validateCode', diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index d136fdf2cdf6..f14eb90ccec3 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -40,6 +40,7 @@ import ValidateLoginPage from '../../../pages/ValidateLoginPage'; import { IOUBillStackNavigator, IOURequestModalStackNavigator, + IOUDetailsModalStackNavigator, DetailsModalStackNavigator, ReportParticipantsModalStackNavigator, SearchModalStackNavigator, @@ -265,6 +266,11 @@ class AuthScreens extends React.Component { component={IOUBillStackNavigator} listeners={modalScreenListeners} /> + ); } diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 532b95e7a68e..14fe7ab40139 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -15,6 +15,7 @@ import SettingsPasswordPage from '../../../pages/settings/PasswordPage'; import SettingsPaymentsPage from '../../../pages/settings/PaymentsPage'; import SettingsAddSecondaryLoginPage from '../../../pages/settings/AddSecondaryLoginPage'; import ReportParticipantsPage from '../../../pages/ReportParticipantsPage'; +import IOUDetailsPage from '../../../pages/iou/IOUDetailsPage'; // Setup the modal stack navigators so we only have to create them once const SettingsModalStack = createStackNavigator(); @@ -25,6 +26,7 @@ const DetailsModalStack = createStackNavigator(); const ReportParticipantsModalStack = createStackNavigator(); const IOURequestModalStack = createStackNavigator(); const IOUBillModalStack = createStackNavigator(); +const IOUDetailsModalStack = createStackNavigator(); const defaultSubRouteOptions = { cardStyle: styles.navigationScreenCardStyle, @@ -66,6 +68,23 @@ const IOURequestModalStackNavigator = () => ( ); +const IOUDetailsModalStackNavigator = () => ( + + + +); + const DetailsModalStackNavigator = () => ( ( export { IOUBillStackNavigator, IOURequestModalStackNavigator, + IOUDetailsModalStackNavigator, DetailsModalStackNavigator, ReportParticipantsModalStackNavigator, SearchModalStackNavigator, diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index e6df75231b6c..6611bcbbbe6f 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -89,6 +89,11 @@ export default { IOU_Bill_Root: ROUTES.IOU_BILL, }, }, + IOU_Details: { + screens: { + IOU_Details_Route: ROUTES.IOU_DETAILS_WITH_IOU_REPORT_ID, + }, + }, }, }, }; diff --git a/src/pages/iou/IOUDetailsPage.js b/src/pages/iou/IOUDetailsPage.js new file mode 100644 index 000000000000..25c4dae6a064 --- /dev/null +++ b/src/pages/iou/IOUDetailsPage.js @@ -0,0 +1,68 @@ +import React, {Component} from 'react'; +import { + View, +} from 'react-native'; +import PropTypes from 'prop-types'; +import {withOnyx} from 'react-native-onyx'; +import styles from '../../styles/styles'; +import Text from '../../components/Text'; +import ONYXKEYS from '../../ONYXKEYS'; +import HeaderWithCloseButton from '../../components/HeaderWithCloseButton'; +import Navigation from '../../libs/Navigation/Navigation'; +import ScreenWrapper from '../../components/ScreenWrapper'; + +const matchType = PropTypes.shape({ + params: PropTypes.shape({ + // iouReportID passed via route /iou/details/:iouReportID + iouReportID: PropTypes.string, + }), +}); + +const propTypes = { + /* Onyx Props */ + // ID for the iouReport that is displayed + iouReportID: PropTypes.number, + + // Route params + route: matchType.isRequired, +}; + +class IOUDetailsPage extends Component { + constructor(props) { + super(props); + + console.debug('jules: init ', this.props); + } + + render() { + console.debug('jules: route: ', this.props.route); + console.debug('jules: iouReportID: ', this.props.route.params.iouReportID); + return ( + + + + + {`IOU Details: ${this.props.route.params.iouReportID}`} + + + + ); + } +} + +IOUDetailsPage.propTypes = propTypes; +IOUDetailsPage.displayName = 'IOUDetailsPage'; + +export default withOnyx({ + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS, + }, +})(IOUDetailsPage); From 113825f53d8b4a1b51ea4f86c6e5445d0e38385d Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 8 Apr 2021 15:24:00 +0100 Subject: [PATCH 002/141] replace preview HTML with a React View, to simplify 'view details' callback --- src/components/ReportActionItemIOUQuote.js | 19 ++++++++++++++++--- src/styles/styles.js | 7 +++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/components/ReportActionItemIOUQuote.js b/src/components/ReportActionItemIOUQuote.js index bf56ed9d243e..5cdc747f60d2 100644 --- a/src/components/ReportActionItemIOUQuote.js +++ b/src/components/ReportActionItemIOUQuote.js @@ -1,8 +1,10 @@ import React from 'react'; -import {View} from 'react-native'; +import {View, Text} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; -import styles from '../styles/styles'; +import Navigation from '../libs/Navigation/Navigation'; +import ROUTES from '../ROUTES'; +import styles, {webViewStyles} from '../styles/styles'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; import RenderHTML from './RenderHTML'; @@ -17,7 +19,18 @@ const ReportActionItemIOUQuote = ({action}) => ( const viewDetails = '
View Details'; const html = `
${fragment.text}${viewDetails}
`; return ( - + + + + {fragment.text} + + { + Navigation.navigate(ROUTES.getIouDetailsRoute('707')); + }}> + View Details + + + ); })} diff --git a/src/styles/styles.js b/src/styles/styles.js index baf5dc750413..fd17e3959a70 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -762,6 +762,13 @@ const styles = { opacity: 0.6, }, + chatItemMessageLink: { + color: colors.blue, + fontSize: variables.fontSizeNormal, + fontFamily: fontFamily.GTA, + lineHeight: 20, + }, + chatItemCompose: { minHeight: 65, marginBottom: 5, From 73640e392ad05519f9c5799771af89a404fa6f9b Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 9 Apr 2021 18:10:59 +0100 Subject: [PATCH 003/141] retrieve IOU report dynamically --- src/components/ReportActionItemIOUQuote.js | 1 - src/pages/iou/IOUDetailsPage.js | 23 +++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/components/ReportActionItemIOUQuote.js b/src/components/ReportActionItemIOUQuote.js index 5cdc747f60d2..3143dc50810c 100644 --- a/src/components/ReportActionItemIOUQuote.js +++ b/src/components/ReportActionItemIOUQuote.js @@ -6,7 +6,6 @@ import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; import styles, {webViewStyles} from '../styles/styles'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; -import RenderHTML from './RenderHTML'; const propTypes = { // All the data of the action diff --git a/src/pages/iou/IOUDetailsPage.js b/src/pages/iou/IOUDetailsPage.js index 25c4dae6a064..b2563e29af0c 100644 --- a/src/pages/iou/IOUDetailsPage.js +++ b/src/pages/iou/IOUDetailsPage.js @@ -18,25 +18,29 @@ const matchType = PropTypes.shape({ }), }); +const defaultProps = { + iouReport: {}, +}; + const propTypes = { /* Onyx Props */ - // ID for the iouReport that is displayed - iouReportID: PropTypes.number, - // Route params route: matchType.isRequired, + + // IOU Report data object + iouReport: PropTypes.shape({ + // The total amount in cents + total: PropTypes.number, + }), }; class IOUDetailsPage extends Component { constructor(props) { super(props); - - console.debug('jules: init ', this.props); } render() { - console.debug('jules: route: ', this.props.route); - console.debug('jules: iouReportID: ', this.props.route.params.iouReportID); + console.debug('jules: props: ', this.props); return ( `${ONYXKEYS.COLLECTION.REPORT_IOUS}${route.params.iouReportID}`, }, })(IOUDetailsPage); From bbd260f62039e5b7b58c9fcc62f4d1e934126eb1 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 9 Apr 2021 18:24:02 +0100 Subject: [PATCH 004/141] add basic button --- src/pages/iou/IOUDetailsPage.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/pages/iou/IOUDetailsPage.js b/src/pages/iou/IOUDetailsPage.js index b2563e29af0c..6eea946a7fb2 100644 --- a/src/pages/iou/IOUDetailsPage.js +++ b/src/pages/iou/IOUDetailsPage.js @@ -9,6 +9,7 @@ import Text from '../../components/Text'; import ONYXKEYS from '../../ONYXKEYS'; import HeaderWithCloseButton from '../../components/HeaderWithCloseButton'; import Navigation from '../../libs/Navigation/Navigation'; +import ButtonWithLoader from '../../components/ButtonWithLoader'; import ScreenWrapper from '../../components/ScreenWrapper'; const matchType = PropTypes.shape({ @@ -20,6 +21,7 @@ const matchType = PropTypes.shape({ const defaultProps = { iouReport: {}, + loading: false, }; const propTypes = { @@ -32,11 +34,21 @@ const propTypes = { // The total amount in cents total: PropTypes.number, }), + + loading: PropTypes.bool }; class IOUDetailsPage extends Component { constructor(props) { super(props); + + this.performIOUSettlement = this.performIOUSettlement.bind(this); + } + + performIOUSettlement() { + this.setState({ + loading: true, + }); } render() { @@ -55,7 +67,15 @@ class IOUDetailsPage extends Component { > {`IOU Details: ${this.props.route.params.iouReportID}`} + {`\n\n${JSON.stringify(this.props.iouReport)}`} + + {/* Reuse Preview Component here! */} + ); From fdb133805332408574343aad62f5c18dcae1eea5 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 12 Apr 2021 18:33:58 +0100 Subject: [PATCH 005/141] start using originalMessage json data, now that API returns it --- src/components/ReportActionItemIOUQuote.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItemIOUQuote.js b/src/components/ReportActionItemIOUQuote.js index 3143dc50810c..7b89e2855cb6 100644 --- a/src/components/ReportActionItemIOUQuote.js +++ b/src/components/ReportActionItemIOUQuote.js @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import _ from 'underscore'; import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; +import {getIOUReportDetailFromTransactionID} from '../libs/actions/IOU'; import styles, {webViewStyles} from '../styles/styles'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; @@ -24,7 +25,14 @@ const ReportActionItemIOUQuote = ({action}) => ( {fragment.text} { - Navigation.navigate(ROUTES.getIouDetailsRoute('707')); + if (!action.originalMessage) { + console.error('reportAction `originalMessage` data not provided.'); + } + if (action.originalMessage.IOUTransactionID) { + getIOUReportDetailFromTransactionID(action.originalMessage.IOUTransactionID); + } else if (action.originalMessage.IOUReportID) { + Navigation.navigate(ROUTES.getIouDetailsRoute(action.originalMessage.IOUReportID)); + } }}> View Details From 6707a315b729cb9eef68ee2bbb444126e3f4a000 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Wed, 14 Apr 2021 16:48:44 +0100 Subject: [PATCH 006/141] refactor transaction Views to own component --- src/components/TransactionItem.js | 39 +++++++++++++++++++++++++++++++ src/pages/iou/IOUDetailsPage.js | 14 +++++------ 2 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 src/components/TransactionItem.js diff --git a/src/components/TransactionItem.js b/src/components/TransactionItem.js new file mode 100644 index 000000000000..5bb3f880ffb2 --- /dev/null +++ b/src/components/TransactionItem.js @@ -0,0 +1,39 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {Text, View} from 'react-native'; +import styles, {webViewStyles} from '../styles/styles'; + +const propTypes = { + // Transaction to display + transaction: PropTypes.shape({ + + // The transaction currency + currency: PropTypes.string, + + // The transaction amount + total: PropTypes.number, + + // The transaction comment + comment: PropTypes.string, + }), +}; + +const defaultProps = { + iouReport: { + total: 0, + }, +}; + +const TransactionItem = props => ( + + + {`Requested : ${props.transaction.currency} ${props.transaction.amount} -- ${props.transaction.comment}`} + + +); + +TransactionItem.displayName = 'TransactionItem'; +TransactionItem.propTypes = propTypes; +TransactionItem.defaultProps = defaultProps; +export default TransactionItem; + diff --git a/src/pages/iou/IOUDetailsPage.js b/src/pages/iou/IOUDetailsPage.js index 6eea946a7fb2..49ec79c83db5 100644 --- a/src/pages/iou/IOUDetailsPage.js +++ b/src/pages/iou/IOUDetailsPage.js @@ -1,12 +1,13 @@ import React, {Component} from 'react'; +import _ from 'underscore'; import { View, } from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import styles from '../../styles/styles'; -import Text from '../../components/Text'; import ONYXKEYS from '../../ONYXKEYS'; +import TransactionItem from '../../components/TransactionItem'; import HeaderWithCloseButton from '../../components/HeaderWithCloseButton'; import Navigation from '../../libs/Navigation/Navigation'; import ButtonWithLoader from '../../components/ButtonWithLoader'; @@ -52,7 +53,6 @@ class IOUDetailsPage extends Component { } render() { - console.debug('jules: props: ', this.props); return ( - - {`IOU Details: ${this.props.route.params.iouReportID}`} - {`\n\n${JSON.stringify(this.props.iouReport)}`} - + {_.map(this.props.iouReport.transactions, (transaction) => ( + + ))} - {/* Reuse Preview Component here! */} + {/* Reuse Preview Component here? */} + Date: Wed, 14 Apr 2021 16:50:11 +0100 Subject: [PATCH 007/141] rename IOUDetailPage to IOUDetailsModal --- .../Navigation/AppNavigator/ModalStackNavigators.js | 4 ++-- .../iou/{IOUDetailsPage.js => IOUDetailsModal.js} | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) rename src/pages/iou/{IOUDetailsPage.js => IOUDetailsModal.js} (92%) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 14fe7ab40139..4213a32779e6 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -15,7 +15,7 @@ import SettingsPasswordPage from '../../../pages/settings/PasswordPage'; import SettingsPaymentsPage from '../../../pages/settings/PaymentsPage'; import SettingsAddSecondaryLoginPage from '../../../pages/settings/AddSecondaryLoginPage'; import ReportParticipantsPage from '../../../pages/ReportParticipantsPage'; -import IOUDetailsPage from '../../../pages/iou/IOUDetailsPage'; +import IOUDetailsModal from '../../../pages/iou/IOUDetailsModal'; // Setup the modal stack navigators so we only have to create them once const SettingsModalStack = createStackNavigator(); @@ -77,7 +77,7 @@ const IOUDetailsModalStackNavigator = () => ( > `${ONYXKEYS.COLLECTION.REPORT_IOUS}${route.params.iouReportID}`, }, -})(IOUDetailsPage); +})(IOUDetailsModal); From e7084b17085748c375d3a5bca6bdbac1f74270ab Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 15 Apr 2021 15:46:54 +0100 Subject: [PATCH 008/141] attempt to depend on two onyx subscriptions --- src/pages/iou/IOUDetailsModal.js | 36 +++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index fe983c19c18d..12fc7f9d0e57 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -12,6 +12,8 @@ import HeaderWithCloseButton from '../../components/HeaderWithCloseButton'; import Navigation from '../../libs/Navigation/Navigation'; import ButtonWithLoader from '../../components/ButtonWithLoader'; import ScreenWrapper from '../../components/ScreenWrapper'; +import compose from '../../libscompose'; +import ReportActionPropTypes from '../../pages/home/report/ReportActionPropTypes'; const matchType = PropTypes.shape({ params: PropTypes.shape({ @@ -21,7 +23,10 @@ const matchType = PropTypes.shape({ }); const defaultProps = { - iouReport: {}, + iouReport: { + chatReportID: 0, + }, + reportActions: {}, loading: false, }; @@ -32,10 +37,12 @@ const propTypes = { // IOU Report data object iouReport: PropTypes.shape({ - // The total amount in cents - total: PropTypes.number, + // TODODODODODODODOODODODODODODODODOOD + chatReportID: PropTypes.number, }), + reportActions: PropTypes.objectOf(PropTypes.shape(ReportActionPropTypes)), + loading: PropTypes.bool }; @@ -46,6 +53,12 @@ class IOUDetailsModal extends Component { this.performIOUSettlement = this.performIOUSettlement.bind(this); } + componentDidUpdate(prevProps) { + if (prevProps.reportActions !== this.props.reportActions) { + console.debug('juless: reportActions: ', this.props.reportActions); + } + } + performIOUSettlement() { this.setState({ loading: true, @@ -86,8 +99,15 @@ IOUDetailsModal.propTypes = propTypes; IOUDetailsModal.displayName = 'IOUDetailsModal'; IOUDetailsModal.defaultProps = defaultProps; -export default withOnyx({ - iouReport: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_IOUS}${route.params.iouReportID}`, - }, -})(IOUDetailsModal); +export default compose( + withOnyx({ + iouReport: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_IOUS}${route.params.iouReportID}`, + }, + }), + withOnyx({ + reportActions: { + key: ({iouReport}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.iouReportID}`, + }, + }), +)(IOUDetailsModal); \ No newline at end of file From 3e5dc119bdcadfcbc6328f099e1d35791d569617 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 15 Apr 2021 15:55:52 +0100 Subject: [PATCH 009/141] formatting --- src/pages/iou/IOUDetailsModal.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 12fc7f9d0e57..b095bc6a8132 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -12,7 +12,7 @@ import HeaderWithCloseButton from '../../components/HeaderWithCloseButton'; import Navigation from '../../libs/Navigation/Navigation'; import ButtonWithLoader from '../../components/ButtonWithLoader'; import ScreenWrapper from '../../components/ScreenWrapper'; -import compose from '../../libscompose'; +import compose from '../../libs/compose'; import ReportActionPropTypes from '../../pages/home/report/ReportActionPropTypes'; const matchType = PropTypes.shape({ @@ -43,7 +43,7 @@ const propTypes = { reportActions: PropTypes.objectOf(PropTypes.shape(ReportActionPropTypes)), - loading: PropTypes.bool + loading: PropTypes.bool, }; class IOUDetailsModal extends Component { @@ -81,7 +81,7 @@ class IOUDetailsModal extends Component { {_.map(this.props.iouReport.transactions, (transaction) => ( ))} - + {/* Reuse Preview Component here? */} `${ONYXKEYS.COLLECTION.REPORT_IOUS}${route.params.iouReportID}`, }, - }), - withOnyx({ + }), + withOnyx({ reportActions: { - key: ({iouReport}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.iouReportID}`, + key: ({iouReport}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.chatReportID}`, }, - }), -)(IOUDetailsModal); \ No newline at end of file + }), +)(IOUDetailsModal); From 8e2dc077a7012de52c6ececca6b8abeec5b6b9ce Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 15 Apr 2021 16:41:16 +0100 Subject: [PATCH 010/141] use user email to determine whether payment is possible --- src/pages/iou/IOUDetailsModal.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index b095bc6a8132..9ff32545ced1 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -5,6 +5,7 @@ import { } from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; +import lodashGet from 'lodash/get'; import styles from '../../styles/styles'; import ONYXKEYS from '../../ONYXKEYS'; import TransactionItem from '../../components/TransactionItem'; @@ -44,6 +45,11 @@ const propTypes = { reportActions: PropTypes.objectOf(PropTypes.shape(ReportActionPropTypes)), loading: PropTypes.bool, + // Session info for the currently logged in user. + session: PropTypes.shape({ + // Currently logged in user email + email: PropTypes.string, + }).isRequired, }; class IOUDetailsModal extends Component { @@ -84,11 +90,12 @@ class IOUDetailsModal extends Component { {/* Reuse Preview Component here? */} + {(this.props.iouReport.managerEmail === sessionEmail && + />)} ); @@ -104,10 +111,13 @@ export default compose( iouReport: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_IOUS}${route.params.iouReportID}`, }, - }), - withOnyx({ - reportActions: { - key: ({iouReport}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.chatReportID}`, + session: { + key: ONYXKEYS.SESSION, }, }), + // withOnyx({ + // reportActions: { + // key: ({iouReport}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.chatReportID}`, + // }, + // }), )(IOUDetailsModal); From 7f0790944c7a9bcbf5f5ac6dd0245d16b9f4ce72 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 15 Apr 2021 16:41:50 +0100 Subject: [PATCH 011/141] add isLoading to state --- src/pages/iou/IOUDetailsModal.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 9ff32545ced1..313829dd6334 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -28,7 +28,6 @@ const defaultProps = { chatReportID: 0, }, reportActions: {}, - loading: false, }; const propTypes = { @@ -44,7 +43,6 @@ const propTypes = { reportActions: PropTypes.objectOf(PropTypes.shape(ReportActionPropTypes)), - loading: PropTypes.bool, // Session info for the currently logged in user. session: PropTypes.shape({ // Currently logged in user email @@ -56,10 +54,15 @@ class IOUDetailsModal extends Component { constructor(props) { super(props); + this.state = { + loading: false, + } + this.performIOUSettlement = this.performIOUSettlement.bind(this); } componentDidUpdate(prevProps) { + console.debug('juless props: ', this.props); if (prevProps.reportActions !== this.props.reportActions) { console.debug('juless: reportActions: ', this.props.reportActions); } @@ -93,7 +96,7 @@ class IOUDetailsModal extends Component { {(this.props.iouReport.managerEmail === sessionEmail && )} From 32e00f4b40a5601d213de438427aa2b605bca50b Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 15 Apr 2021 16:42:52 +0100 Subject: [PATCH 012/141] add padding to IOU Detail Modal --- src/pages/iou/IOUDetailsModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 313829dd6334..83d33838f24e 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -84,7 +84,7 @@ class IOUDetailsModal extends Component { {_.map(this.props.iouReport.transactions, (transaction) => ( From 867bd47683156dd94614b35448ea698885b276b5 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 15 Apr 2021 17:34:48 +0100 Subject: [PATCH 013/141] use email const --- src/pages/iou/IOUDetailsModal.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 83d33838f24e..f03085a55aa4 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -75,6 +75,7 @@ class IOUDetailsModal extends Component { } render() { + const sessionEmail = lodashGet(this.props.session, 'email', null); return ( Date: Thu, 15 Apr 2021 17:37:27 +0100 Subject: [PATCH 014/141] add PayIOU request/action --- src/libs/API.js | 14 ++++++++++++++ src/libs/actions/IOU.js | 22 +++++++++++++++++++++- src/pages/iou/IOUDetailsModal.js | 6 ++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/libs/API.js b/src/libs/API.js index 633c6420b24e..d4b94b4d6428 100644 --- a/src/libs/API.js +++ b/src/libs/API.js @@ -466,6 +466,19 @@ function Graphite_Timer(parameters) { return Network.post(commandName, parameters); } +/** + * + * @param {Object} parameters + * @param {String} parameters.reportID + * @param {String} parameters.paymentMethodType + * @returns {Promise} + */ + function PayIOU(parameters) { + const commandName = 'PayIOU'; + requireParameters(['reportID', 'paymentMethodType'], parameters, commandName); + return Network.post(commandName, parameters); +} + /** * @param {Object} parameters * @param {String} parameters.emailList @@ -703,6 +716,7 @@ export { Graphite_Timer, Log, Mobile_GetConstants, + PayIOU, PersonalDetails_GetForEmails, PersonalDetails_Update, Push_Authenticate, diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index f56871642da9..ac780265ec0f 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -107,8 +107,28 @@ function createIOUSplit({ .then(reportIDList => getIOUReportsForNewTransaction(reportIDList)); } +/** + * Settles an IOU Report + */ +function settleIOUReport({ + reportID, paymentMethodType, +}) { + //Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: false}); + console.debug('juless: settleIOUReport', {'reportID': reportID, 'paymentType': paymentMethodType}); + return; + + API.PayIOU({ + reportID, + paymentMethodType, + }) + .then(data => { + console.debug('juless: IOU Settled: ', data); + }); +} + export { getPreferredCurrency, createIOUTransaction, createIOUSplit, -}; + settleIOUReport, +} \ No newline at end of file diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index f03085a55aa4..5df8e8aaae5d 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -14,6 +14,7 @@ import Navigation from '../../libs/Navigation/Navigation'; import ButtonWithLoader from '../../components/ButtonWithLoader'; import ScreenWrapper from '../../components/ScreenWrapper'; import compose from '../../libs/compose'; +import {settleIOUReport} from '../../libs/actions/IOU'; import ReportActionPropTypes from '../../pages/home/report/ReportActionPropTypes'; const matchType = PropTypes.shape({ @@ -56,6 +57,7 @@ class IOUDetailsModal extends Component { this.state = { loading: false, + settlementType: 'Elsewhere' } this.performIOUSettlement = this.performIOUSettlement.bind(this); @@ -69,6 +71,10 @@ class IOUDetailsModal extends Component { } performIOUSettlement() { + settleIOUReport({ + reportID: this.props.route.params.iouReportID, + paymentMethodType: this.state.settlementType, + }); this.setState({ loading: true, }); From a03e4740bfb0a64c3c5e55bee8c7a4c1fdc59702 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 15 Apr 2021 18:37:06 +0100 Subject: [PATCH 015/141] open IOUReport dynamically, by retrieving ID from transaction --- src/libs/actions/IOU.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index ac780265ec0f..4a868315d864 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -126,9 +126,30 @@ function settleIOUReport({ }); } +/** + * Retrieve an IOU report using a transactionID, then navigate to the page. + */ + function getIOUReportDetailFromTransactionID(transactionID) { + API.Get({ + returnValueList: 'transactionList', + transactionID: transactionID, + }) + .then(data => { + const chatReportID = data.transactionList[0].reportID; + if (!chatReportID) { + return; + } + Navigation.navigate(ROUTES.getIouDetailsRoute(chatReportID)); + }) + .catch((error) => { + console.error('Error retrieving Transaction: ', error); + }); +} + export { getPreferredCurrency, createIOUTransaction, createIOUSplit, - settleIOUReport, -} \ No newline at end of file + getIOUReportDetailFromTransactionID, + settleIOUReport +} From d755943d0cba85ddb728abb65d7ffaff62468282 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 16 Apr 2021 18:09:26 +0100 Subject: [PATCH 016/141] fix double Onyx subscription --- src/pages/iou/IOUDetailsModal.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 5df8e8aaae5d..1a7de6259fa8 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -15,7 +15,7 @@ import ButtonWithLoader from '../../components/ButtonWithLoader'; import ScreenWrapper from '../../components/ScreenWrapper'; import compose from '../../libs/compose'; import {settleIOUReport} from '../../libs/actions/IOU'; -import ReportActionPropTypes from '../../pages/home/report/ReportActionPropTypes'; +import ReportActionPropTypes from '../home/report/ReportActionPropTypes'; const matchType = PropTypes.shape({ params: PropTypes.shape({ @@ -28,7 +28,7 @@ const defaultProps = { iouReport: { chatReportID: 0, }, - reportActions: {}, + reportActions: [], }; const propTypes = { @@ -42,7 +42,7 @@ const propTypes = { chatReportID: PropTypes.number, }), - reportActions: PropTypes.objectOf(PropTypes.shape(ReportActionPropTypes)), + reportActions: PropTypes.arrayOf(PropTypes.shape(ReportActionPropTypes)), // Session info for the currently logged in user. session: PropTypes.shape({ @@ -57,8 +57,8 @@ class IOUDetailsModal extends Component { this.state = { loading: false, - settlementType: 'Elsewhere' - } + settlementType: 'Elsewhere', + }; this.performIOUSettlement = this.performIOUSettlement.bind(this); } @@ -125,9 +125,10 @@ export default compose( key: ONYXKEYS.SESSION, }, }), - // withOnyx({ - // reportActions: { - // key: ({iouReport}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.chatReportID}`, - // }, - // }), + withOnyx({ + reportActions: { + key: ({iouReport}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.chatReportID}`, + canEvict: false, + }, + }), )(IOUDetailsModal); From 0c76ef1352d2175bf66f2d7def08cf3e0d5173f1 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 16 Apr 2021 18:09:53 +0100 Subject: [PATCH 017/141] update TransactionItem component to take action --- src/components/TransactionItem.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/TransactionItem.js b/src/components/TransactionItem.js index 5bb3f880ffb2..ebdfb3baaaf3 100644 --- a/src/components/TransactionItem.js +++ b/src/components/TransactionItem.js @@ -2,8 +2,11 @@ import React from 'react'; import PropTypes from 'prop-types'; import {Text, View} from 'react-native'; import styles, {webViewStyles} from '../styles/styles'; +import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; const propTypes = { + action: PropTypes.shape(ReportActionPropTypes), + // Transaction to display transaction: PropTypes.shape({ @@ -27,6 +30,7 @@ const defaultProps = { const TransactionItem = props => ( + {/* TODO: display preview component! */} {`Requested : ${props.transaction.currency} ${props.transaction.amount} -- ${props.transaction.comment}`} From 57c4789f911611ee09e42d743777d640b07d9376 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 16 Apr 2021 18:10:35 +0100 Subject: [PATCH 018/141] find action and pass to TransactionItem --- src/pages/iou/IOUDetailsModal.js | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 1a7de6259fa8..4a6cfca3434c 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -94,18 +94,28 @@ class IOUDetailsModal extends Component { styles.detailsPageContainer, styles.p5 ]} > - {_.map(this.props.iouReport.transactions, (transaction) => ( - - ))} - - {/* Reuse Preview Component here? */} - + {_.map(this.props.iouReport.transactions, (transaction) => { + const actionForTransaction = _.find(this.props.reportActions, (action) => { + if (action && action.originalMessage) { + return action.originalMessage.IOUTransactionID == transaction.transactionID; + } + return false; + }); + return ( + + ); + })} {(this.props.iouReport.managerEmail === sessionEmail && - )} + ( + ) + )} ); From 80d373f078ad4e04f9da40e9fa53feb040850b41 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 20 Apr 2021 14:39:21 +0100 Subject: [PATCH 019/141] post rebase formatting fixes --- src/components/TransactionItem.js | 14 +++++------ src/libs/API.js | 3 +-- src/libs/Navigation/linkingConfig.js | 2 +- src/libs/actions/IOU.js | 35 +++++++++++++++------------- src/pages/iou/IOUDetailsModal.js | 27 ++++++++++++++------- 5 files changed, 47 insertions(+), 34 deletions(-) diff --git a/src/components/TransactionItem.js b/src/components/TransactionItem.js index ebdfb3baaaf3..ce780e575c9a 100644 --- a/src/components/TransactionItem.js +++ b/src/components/TransactionItem.js @@ -5,20 +5,20 @@ import styles, {webViewStyles} from '../styles/styles'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; const propTypes = { - action: PropTypes.shape(ReportActionPropTypes), + action: PropTypes.shape(ReportActionPropTypes).isRequired, // Transaction to display transaction: PropTypes.shape({ - // The transaction currency + // The transaction currency currency: PropTypes.string, - // The transaction amount - total: PropTypes.number, - - // The transaction comment + // The transaction comment comment: PropTypes.string, - }), + + // The transaction amount + amount: PropTypes.string, + }).isRequired, }; const defaultProps = { diff --git a/src/libs/API.js b/src/libs/API.js index d4b94b4d6428..7f47d29844d4 100644 --- a/src/libs/API.js +++ b/src/libs/API.js @@ -467,13 +467,12 @@ function Graphite_Timer(parameters) { } /** - * * @param {Object} parameters * @param {String} parameters.reportID * @param {String} parameters.paymentMethodType * @returns {Promise} */ - function PayIOU(parameters) { +function PayIOU(parameters) { const commandName = 'PayIOU'; requireParameters(['reportID', 'paymentMethodType'], parameters, commandName); return Network.post(commandName, parameters); diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 6611bcbbbe6f..d6d400f1d395 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -91,7 +91,7 @@ export default { }, IOU_Details: { screens: { - IOU_Details_Route: ROUTES.IOU_DETAILS_WITH_IOU_REPORT_ID, + IOU_Details_Route: ROUTES.IOU_DETAILS_WITH_IOU_REPORT_ID, }, }, }, diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 4a868315d864..fe4cb26c449d 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -3,6 +3,8 @@ import _ from 'underscore'; import ONYXKEYS from '../../ONYXKEYS'; import * as API from '../API'; import {getSimplifiedIOUReport} from './Report'; +import Navigation from '../Navigation/Navigation'; +import ROUTES from '../../ROUTES'; /** * Retrieve the users preferred currency @@ -111,30 +113,31 @@ function createIOUSplit({ * Settles an IOU Report */ function settleIOUReport({ - reportID, paymentMethodType, + reportID, paymentMethodType, }) { - //Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: false}); - console.debug('juless: settleIOUReport', {'reportID': reportID, 'paymentType': paymentMethodType}); - return; + // Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: false}); + console.debug('juless: settleIOUReport', {reportID, paymentMethodType}); + return; - API.PayIOU({ - reportID, - paymentMethodType, - }) - .then(data => { - console.debug('juless: IOU Settled: ', data); - }); + API.PayIOU({ + reportID, + paymentMethodType, + }) + .then((data) => { + console.debug('juless: IOU Settled: ', data); + }); } /** * Retrieve an IOU report using a transactionID, then navigate to the page. + * @param {Int} transactionID */ - function getIOUReportDetailFromTransactionID(transactionID) { +function getIOUReportDetailFromTransactionID(transactionID) { API.Get({ returnValueList: 'transactionList', - transactionID: transactionID, + transactionID, }) - .then(data => { + .then((data) => { const chatReportID = data.transactionList[0].reportID; if (!chatReportID) { return; @@ -151,5 +154,5 @@ export { createIOUTransaction, createIOUSplit, getIOUReportDetailFromTransactionID, - settleIOUReport -} + settleIOUReport, +}; diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 4a6cfca3434c..1da9ab4ba2c7 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -40,6 +40,20 @@ const propTypes = { iouReport: PropTypes.shape({ // TODODODODODODODOODODODODODODODODOOD chatReportID: PropTypes.number, + + // Manager is the person who currently owes money + managerEmail: PropTypes.string, + + transactions: PropTypes.arrayOf(PropTypes.shape({ + // The transaction currency + currency: PropTypes.string, + + // The transaction amount + total: PropTypes.number, + + // The transaction comment + comment: PropTypes.string, + })), }), reportActions: PropTypes.arrayOf(PropTypes.shape(ReportActionPropTypes)), @@ -90,14 +104,12 @@ class IOUDetailsModal extends Component { /> {_.map(this.props.iouReport.transactions, (transaction) => { const actionForTransaction = _.find(this.props.reportActions, (action) => { if (action && action.originalMessage) { - return action.originalMessage.IOUTransactionID == transaction.transactionID; + return action.originalMessage.IOUTransactionID == transaction.transactionID; // TODO } return false; }); @@ -108,14 +120,13 @@ class IOUDetailsModal extends Component { /> ); })} - {(this.props.iouReport.managerEmail === sessionEmail && - ( + {(this.props.iouReport.managerEmail === sessionEmail && ( ) - )} + /> + ))} ); From d5bec436c5826ed1dc5def2e899008f062155194 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 20 Apr 2021 15:17:27 +0100 Subject: [PATCH 020/141] prep and comments for post Preview UI work --- src/components/TransactionItem.js | 16 ++++++-------- src/pages/home/report/ReportActionItem.js | 2 +- src/pages/iou/IOUDetailsModal.js | 26 +++++++++++++++++++++++ src/styles/styles.js | 2 +- 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/components/TransactionItem.js b/src/components/TransactionItem.js index ce780e575c9a..cd5e524dbb28 100644 --- a/src/components/TransactionItem.js +++ b/src/components/TransactionItem.js @@ -1,8 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {Text, View} from 'react-native'; -import styles, {webViewStyles} from '../styles/styles'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; +import ReportActionItemIOUPreview from '../components/ReportActionItemIOUPreview'; const propTypes = { action: PropTypes.shape(ReportActionPropTypes).isRequired, @@ -17,7 +16,7 @@ const propTypes = { comment: PropTypes.string, // The transaction amount - amount: PropTypes.string, + amount: PropTypes.number, }).isRequired, }; @@ -28,12 +27,11 @@ const defaultProps = { }; const TransactionItem = props => ( - - - {/* TODO: display preview component! */} - {`Requested : ${props.transaction.currency} ${props.transaction.amount} -- ${props.transaction.comment}`} - - + + ); TransactionItem.displayName = 'TransactionItem'; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index b6f9e4ec3f09..78ec38cc2cd0 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -107,7 +107,7 @@ class ReportActionItem extends Component { const children = this.props.action.actionName === 'IOU' ? ( + + + + {`${this.props.iouReport.currencySymbol}${this.props.iouReport.total}`} + + {'Cat A'} + {' owes '} + {'Cat B'} + + + + + + + {_.map(this.props.iouReport.transactions, (transaction) => { const actionForTransaction = _.find(this.props.reportActions, (action) => { if (action && action.originalMessage) { diff --git a/src/styles/styles.js b/src/styles/styles.js index fd17e3959a70..28989fe6e6af 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -1327,7 +1327,7 @@ const styles = { color: themeColors.heading, }, 0), - iouPreviewBox: { + iouPreviewBox: { // TODO: Add margin bottom, remove margin top-- extract outside borderColor: themeColors.border, borderWidth: 1, borderRadius: variables.componentBorderRadiusCard, From b063b9c89df8f43069d65761c89152cbf15cf4a1 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 20 Apr 2021 15:56:52 +0100 Subject: [PATCH 021/141] display temp button to unblock removeTransaction functionality --- src/components/TransactionItem.js | 42 +++++++++++++++++++++++++------ src/styles/utilities/flex.js | 4 +++ 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/components/TransactionItem.js b/src/components/TransactionItem.js index cd5e524dbb28..6e218d6530ae 100644 --- a/src/components/TransactionItem.js +++ b/src/components/TransactionItem.js @@ -1,7 +1,9 @@ -import React from 'react'; +import React, {Component} from 'react'; import PropTypes from 'prop-types'; +import { View, Text, Pressable } from 'react-native-web'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; import ReportActionItemIOUPreview from '../components/ReportActionItemIOUPreview'; +import styles from '../styles/styles'; const propTypes = { action: PropTypes.shape(ReportActionPropTypes).isRequired, @@ -17,6 +19,9 @@ const propTypes = { // The transaction amount amount: PropTypes.number, + + // Was this transaction created by the current user + createdByUser: PropTypes.bool, }).isRequired, }; @@ -26,13 +31,36 @@ const defaultProps = { }, }; -const TransactionItem = props => ( - +class TransactionItem extends Component { + constructor(props) { + super(props); + + this.removeTransaction = this.removeTransaction.bind(this); + } -); + removeTransaction() { + console.debug('removeTransaction'); + } + + render() { + return ( + + + this.removeTransaction()} + > + + {this.props.transaction.createdByUser ? 'Cancel' : 'Decline'} + + + + ); + } +}; TransactionItem.displayName = 'TransactionItem'; TransactionItem.propTypes = propTypes; diff --git a/src/styles/utilities/flex.js b/src/styles/utilities/flex.js index d8d115bf5c38..e51d5cd66ff8 100644 --- a/src/styles/utilities/flex.js +++ b/src/styles/utilities/flex.js @@ -52,6 +52,10 @@ export default { alignItems: 'flex-end', }, + alignItemsStart: { + alignItems: 'start', + }, + flexWrap: { flexWrap: 'wrap', }, From f15262ee26fabf04646ee71ff5a8fa65dd3e48e8 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 20 Apr 2021 17:08:02 +0100 Subject: [PATCH 022/141] add rejectTransaction API call and IOU Action function --- src/components/TransactionItem.js | 7 ++++ src/libs/API.js | 7 ++++ src/libs/actions/IOU.js | 53 ++++++++++++++++++++----------- src/pages/iou/IOUDetailsModal.js | 2 ++ 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/src/components/TransactionItem.js b/src/components/TransactionItem.js index 6e218d6530ae..622074a615d0 100644 --- a/src/components/TransactionItem.js +++ b/src/components/TransactionItem.js @@ -4,6 +4,7 @@ import { View, Text, Pressable } from 'react-native-web'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; import ReportActionItemIOUPreview from '../components/ReportActionItemIOUPreview'; import styles from '../styles/styles'; +import {rejectTransaction} from '../libs/actions/IOU'; const propTypes = { action: PropTypes.shape(ReportActionPropTypes).isRequired, @@ -39,7 +40,13 @@ class TransactionItem extends Component { } removeTransaction() { + // TODO: delegate to parent console.debug('removeTransaction'); + rejectTransaction({ + reportID: 999, + transactionID: 999999, + comment: 'NO!' + }); } render() { diff --git a/src/libs/API.js b/src/libs/API.js index 7f47d29844d4..ff21f10d2387 100644 --- a/src/libs/API.js +++ b/src/libs/API.js @@ -515,6 +515,12 @@ function Push_Authenticate(parameters) { return Network.post(commandName, parameters); } +function RejectTransaction(parameters) { + const commandName = 'RejectTransaction'; + requireParameters(['reportID', 'transactionID'], parameters, commandName); + return Network.post(commandName, parameters); +} + /** * @param {Object} parameters * @param {String} parameters.reportComment @@ -719,6 +725,7 @@ export { PersonalDetails_GetForEmails, PersonalDetails_Update, Push_Authenticate, + RejectTransaction, Report_AddComment, Report_GetHistory, Report_TogglePinned, diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index fe4cb26c449d..0d3262a0853f 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -109,15 +109,35 @@ function createIOUSplit({ .then(reportIDList => getIOUReportsForNewTransaction(reportIDList)); } +/** + * Retrieve an IOU report using a transactionID, then navigate to the page. + * @param {Int} transactionID + */ +function getIOUReportDetailFromTransactionID(transactionID) { + API.Get({ + returnValueList: 'transactionList', + transactionID, + }) + .then((data) => { + const chatReportID = data.transactionList[0].reportID; + if (!chatReportID) { + return; + } + Navigation.navigate(ROUTES.getIouDetailsRoute(chatReportID)); + }) + .catch((error) => { + console.error('Error retrieving Transaction: ', error); + }); +} + /** * Settles an IOU Report */ -function settleIOUReport({ + function settleIOUReport({ reportID, paymentMethodType, }) { // Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: false}); console.debug('juless: settleIOUReport', {reportID, paymentMethodType}); - return; API.PayIOU({ reportID, @@ -129,24 +149,20 @@ function settleIOUReport({ } /** - * Retrieve an IOU report using a transactionID, then navigate to the page. - * @param {Int} transactionID + * Decline or cancel a transaction */ -function getIOUReportDetailFromTransactionID(transactionID) { - API.Get({ - returnValueList: 'transactionList', + function rejectTransaction({ + reportID, transactionID, comment +}) { + console.debug('juless: rejectTransaction', {reportID, transactionID, comment}); + + API.RejectTransaction({ + reportID, transactionID, - }) - .then((data) => { - const chatReportID = data.transactionList[0].reportID; - if (!chatReportID) { - return; - } - Navigation.navigate(ROUTES.getIouDetailsRoute(chatReportID)); - }) - .catch((error) => { - console.error('Error retrieving Transaction: ', error); - }); + comment, + }).then((data) => { + console.debug('juless: rejectedTransaction response: ', data); + }); } export { @@ -154,5 +170,6 @@ export { createIOUTransaction, createIOUSplit, getIOUReportDetailFromTransactionID, + rejectTransaction, settleIOUReport, }; diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index a43800668dc3..e7a8672e3dcf 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -143,6 +143,8 @@ class IOUDetailsModal extends Component { ); })} From 5de6dc98f094f828d6d052aabdd7dd2df6e4a830 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 26 Apr 2021 15:58:01 +0100 Subject: [PATCH 023/141] rename ...IOUPreview Component to ...IOUAction --- ...temIOUPreview.js => ReportActionItemIOUAction.js} | 12 ++++++------ src/components/TransactionItem.js | 4 ++-- src/pages/home/report/ReportActionItem.js | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) rename src/components/{ReportActionItemIOUPreview.js => ReportActionItemIOUAction.js} (93%) diff --git a/src/components/ReportActionItemIOUPreview.js b/src/components/ReportActionItemIOUAction.js similarity index 93% rename from src/components/ReportActionItemIOUPreview.js rename to src/components/ReportActionItemIOUAction.js index aa3d3a0778ae..997bd447957c 100644 --- a/src/components/ReportActionItemIOUPreview.js +++ b/src/components/ReportActionItemIOUAction.js @@ -53,7 +53,7 @@ const defaultProps = { iou: {}, }; -const ReportActionItemIOUPreview = ({ +const ReportActionItemIOUAction = ({ action, isMostRecentIOUReportAction, hasOutstandingIOU, @@ -84,7 +84,7 @@ const ReportActionItemIOUPreview = ({ {isMostRecentIOUReportAction - && hasOutstandingIOU + && hasOutstandingIOU // rename to should show? no longer required && !_.isEmpty(iou) && ( @@ -123,9 +123,9 @@ const ReportActionItemIOUPreview = ({ ); }; -ReportActionItemIOUPreview.propTypes = propTypes; -ReportActionItemIOUPreview.defaultProps = defaultProps; -ReportActionItemIOUPreview.displayName = 'ReportActionItemIOUPreview'; +ReportActionItemIOUAction.propTypes = propTypes; +ReportActionItemIOUAction.defaultProps = defaultProps; +ReportActionItemIOUAction.displayName = 'ReportActionItemIOUAction'; export default withOnyx({ iou: { @@ -137,4 +137,4 @@ export default withOnyx({ session: { key: ONYXKEYS.SESSION, }, -})(ReportActionItemIOUPreview); +})(ReportActionItemIOUAction); diff --git a/src/components/TransactionItem.js b/src/components/TransactionItem.js index 622074a615d0..95d00cb2c385 100644 --- a/src/components/TransactionItem.js +++ b/src/components/TransactionItem.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import { View, Text, Pressable } from 'react-native-web'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; -import ReportActionItemIOUPreview from '../components/ReportActionItemIOUPreview'; +import ReportActionItemIOUAction from './ReportActionItemIOUAction'; import styles from '../styles/styles'; import {rejectTransaction} from '../libs/actions/IOU'; @@ -52,7 +52,7 @@ class TransactionItem extends Component { render() { return ( - diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 78ec38cc2cd0..0d08561d1e5a 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -13,7 +13,7 @@ import PopoverWithMeasuredContent from '../../../components/PopoverWithMeasuredC import ReportActionItemSingle from './ReportActionItemSingle'; import ReportActionItemGrouped from './ReportActionItemGrouped'; import ReportActionContextMenu from './ReportActionContextMenu'; -import ReportActionItemIOUPreview from '../../../components/ReportActionItemIOUPreview'; +import ReportActionItemIOUAction from '../../../components/ReportActionItemIOUAction'; import ReportActionItemMessage from './ReportActionItemMessage'; import UnreadActionIndicator from '../../../components/UnreadActionIndicator'; @@ -106,8 +106,8 @@ class ReportActionItem extends Component { render() { const children = this.props.action.actionName === 'IOU' ? ( - Date: Mon, 26 Apr 2021 17:14:59 +0100 Subject: [PATCH 024/141] refactor IOUAction, splitting out IOUPreview to reusable Component --- src/components/ReportActionItemIOUAction.js | 104 ++++-------------- src/components/ReportActionItemIOUPreview.js | 109 +++++++++++++++++++ src/components/ReportActionItemIOUQuote.js | 31 +++--- src/components/TransactionItem.js | 20 +--- src/pages/home/report/ReportActionItem.js | 5 +- src/pages/iou/IOUDetailsModal.js | 46 ++------ 6 files changed, 163 insertions(+), 152 deletions(-) create mode 100644 src/components/ReportActionItemIOUPreview.js diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index 997bd447957c..3fe8db34c135 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -1,26 +1,23 @@ import React from 'react'; -import {View, TouchableOpacity} from 'react-native'; +import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import lodashGet from 'lodash/get'; -import Str from 'expensify-common/lib/str'; import ONYXKEYS from '../ONYXKEYS'; import ReportActionItemIOUQuote from './ReportActionItemIOUQuote'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; -import Text from './Text'; -import MultipleAvatars from './MultipleAvatars'; -import styles from '../styles/styles'; +import ReportActionItemIOUPreview from './ReportActionItemIOUPreview'; const propTypes = { // All the data of the action action: PropTypes.shape(ReportActionPropTypes).isRequired, - // Is this the most recent IOU Action? - isMostRecentIOUReportAction: PropTypes.bool.isRequired, + // The linked iouReportID + // eslint-disable-next-line react/no-unused-prop-types + iouReportID: PropTypes.number.isRequired, - // Whether there is an outstanding amount in IOU - hasOutstandingIOU: PropTypes.bool.isRequired, + // Should render the preview Component? + shouldDisplayPreviewComp: PropTypes.bool.isRequired, /* --- Onyx Props --- */ // Active IOU Report for current report @@ -35,13 +32,6 @@ const propTypes = { cachedTotal: PropTypes.string, }), - // All of the personal details for everyone - personalDetails: PropTypes.objectOf(PropTypes.shape({ - - // This is either the user's full name, or their login if full name is an empty string - displayName: PropTypes.string.isRequired, - })).isRequired, - // Session info for the currently logged in user. session: PropTypes.shape({ // Currently logged in user email @@ -55,73 +45,20 @@ const defaultProps = { const ReportActionItemIOUAction = ({ action, - isMostRecentIOUReportAction, - hasOutstandingIOU, + shouldDisplayPreviewComp, iou, - personalDetails, session, -}) => { - const managerName = lodashGet( - personalDetails, - [iou.managerEmail, 'displayName'], - iou.managerEmail ? Str.removeSMSDomain(iou.managerEmail) : '', - ); - const ownerName = lodashGet( - personalDetails, - [iou.ownerEmail, 'displayName'], - iou.ownerEmail ? Str.removeSMSDomain(iou.ownerEmail) : '', - ); - const managerAvatar = lodashGet(personalDetails, [iou.managerEmail, 'avatar'], ''); - const ownerAvatar = lodashGet(personalDetails, [iou.ownerEmail, 'avatar'], ''); - const sessionEmail = lodashGet(session, 'email', null); - const cachedTotal = iou.cachedTotal ? iou.cachedTotal.replace(/[()]/g, '') : ''; - - // Pay button should be visible to manager person in the report - // Check if the currently logged in user is the manager. - const isCurrentUserManager = iou.managerEmail === sessionEmail; - - return ( - - - {isMostRecentIOUReportAction - && hasOutstandingIOU // rename to should show? no longer required - && !_.isEmpty(iou) && ( - - - - {cachedTotal} - - {managerName} - {' owes '} - {ownerName} - - - - - - - {isCurrentUserManager && ( - - - Pay - - - )} - - )} - - ); -}; +}) => ( + + + {shouldDisplayPreviewComp && !_.isEmpty(iou) && ( + + )} + +); ReportActionItemIOUAction.propTypes = propTypes; ReportActionItemIOUAction.defaultProps = defaultProps; @@ -131,9 +68,6 @@ export default withOnyx({ iou: { key: ({iouReportID}) => `${ONYXKEYS.COLLECTION.REPORT_IOUS}${iouReportID}`, }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, - }, session: { key: ONYXKEYS.SESSION, }, diff --git a/src/components/ReportActionItemIOUPreview.js b/src/components/ReportActionItemIOUPreview.js new file mode 100644 index 000000000000..0f3bf02d4541 --- /dev/null +++ b/src/components/ReportActionItemIOUPreview.js @@ -0,0 +1,109 @@ +import React from 'react'; +import {View, TouchableOpacity, Text} from 'react-native'; +import PropTypes from 'prop-types'; +import Str from 'expensify-common/lib/str'; +import {withOnyx} from 'react-native-onyx'; +import lodashGet from 'lodash/get'; +import styles from '../styles/styles'; +import ONYXKEYS from '../ONYXKEYS'; +import MultipleAvatars from './MultipleAvatars'; + +const propTypes = { + // Active IOU Report for current report + iou: PropTypes.shape({ + // Email address of the manager in this iou report + managerEmail: PropTypes.string, + + // Email address of the creator of this iou report + ownerEmail: PropTypes.string, + + // Outstanding amount of this transaction + cachedTotal: PropTypes.string, + }).isRequired, + + // Session info for the currently logged in user. + session: PropTypes.shape({ + // Currently logged in user email + email: PropTypes.string, + }).isRequired, + + /* --- Onyx Props --- */ + // All of the personal details for everyone + personalDetails: PropTypes.objectOf(PropTypes.shape({ + + // This is either the user's full name, or their login if full name is an empty string + displayName: PropTypes.string.isRequired, + })).isRequired, +}; + +const ReportActionItemIOUPreview = ({ + iou, + personalDetails, + session, +}) => { + const sessionEmail = lodashGet(session, 'email', null); + + // Pay button should be visible to manager person in the report + // Check if the currently logged in user is the manager. + const isCurrentUserManager = iou.managerEmail === sessionEmail; + + const managerName = lodashGet( + personalDetails, + [iou.managerEmail, 'displayName'], + iou.managerEmail ? Str.removeSMSDomain(iou.managerEmail) : '', + ); + const ownerName = lodashGet( + personalDetails, + [iou.ownerEmail, 'displayName'], + iou.ownerEmail ? Str.removeSMSDomain(iou.ownerEmail) : '', + ); + const managerAvatar = lodashGet(personalDetails, [iou.managerEmail, 'avatar'], ''); + const ownerAvatar = lodashGet(personalDetails, [iou.ownerEmail, 'avatar'], ''); + const cachedTotal = iou.cachedTotal ? iou.cachedTotal.replace(/[()]/g, '') : ''; + + return ( + + + + + {cachedTotal} + + {managerName} + {' owes '} + {ownerName} + + + + + + + {isCurrentUserManager && ( + + + Pay + + + )} + + + ); +}; + +ReportActionItemIOUPreview.propTypes = propTypes; +ReportActionItemIOUPreview.displayName = 'ReportActionItemIOUPreview'; + +export default withOnyx({ + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS, + }, +})(ReportActionItemIOUPreview); diff --git a/src/components/ReportActionItemIOUQuote.js b/src/components/ReportActionItemIOUQuote.js index 7b89e2855cb6..4a2a1542f274 100644 --- a/src/components/ReportActionItemIOUQuote.js +++ b/src/components/ReportActionItemIOUQuote.js @@ -15,16 +15,15 @@ const propTypes = { const ReportActionItemIOUQuote = ({action}) => ( - {_.map(action.message, (fragment, index) => { - const viewDetails = '
View Details'; - const html = `
${fragment.text}${viewDetails}
`; - return ( - - - - {fragment.text} - - { + {_.map(action.message, (fragment, index) => ( + + + + {fragment.text} + + { if (!action.originalMessage) { console.error('reportAction `originalMessage` data not provided.'); } @@ -33,13 +32,13 @@ const ReportActionItemIOUQuote = ({action}) => ( } else if (action.originalMessage.IOUReportID) { Navigation.navigate(ROUTES.getIouDetailsRoute(action.originalMessage.IOUReportID)); } - }}> - View Details - - + }} + > + View Details + - ); - })} + + ))}
); diff --git a/src/components/TransactionItem.js b/src/components/TransactionItem.js index 95d00cb2c385..f778d6321ed2 100644 --- a/src/components/TransactionItem.js +++ b/src/components/TransactionItem.js @@ -1,6 +1,6 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; -import { View, Text, Pressable } from 'react-native-web'; +import {View, Text, Pressable} from 'react-native-web'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; import ReportActionItemIOUAction from './ReportActionItemIOUAction'; import styles from '../styles/styles'; @@ -26,12 +26,6 @@ const propTypes = { }).isRequired, }; -const defaultProps = { - iouReport: { - total: 0, - }, -}; - class TransactionItem extends Component { constructor(props) { super(props); @@ -45,16 +39,16 @@ class TransactionItem extends Component { rejectTransaction({ reportID: 999, transactionID: 999999, - comment: 'NO!' + comment: 'NO!', }); } render() { return ( - ); } -}; +} TransactionItem.displayName = 'TransactionItem'; TransactionItem.propTypes = propTypes; -TransactionItem.defaultProps = defaultProps; export default TransactionItem; - diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 0d08561d1e5a..bd8d9fc7554d 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -107,10 +107,9 @@ class ReportActionItem extends Component { const children = this.props.action.actionName === 'IOU' ? ( ) : ; diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index e7a8672e3dcf..92d53861980b 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -1,9 +1,6 @@ import React, {Component} from 'react'; import _ from 'underscore'; -import { - View, - Text, -} from 'react-native'; +import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; @@ -17,7 +14,7 @@ import ScreenWrapper from '../../components/ScreenWrapper'; import compose from '../../libs/compose'; import {settleIOUReport} from '../../libs/actions/IOU'; import ReportActionPropTypes from '../home/report/ReportActionPropTypes'; -import MultipleAvatars from '../../components/MultipleAvatars'; +import ReportActionItemIOUPreview from '../../components/ReportActionItemIOUPreview'; const matchType = PropTypes.shape({ params: PropTypes.shape({ @@ -40,12 +37,15 @@ const propTypes = { // IOU Report data object iouReport: PropTypes.shape({ - // TODODODODODODODOODODODODODODODODOOD + // ID for the chatReport that this IOU is linked to chatReportID: PropTypes.number, // Manager is the person who currently owes money managerEmail: PropTypes.string, + // Owner is the person who is owed money + ownerEmail: PropTypes.string, + transactions: PropTypes.arrayOf(PropTypes.shape({ // The transaction currency currency: PropTypes.string, @@ -56,15 +56,9 @@ const propTypes = { // The transaction comment comment: PropTypes.string, })), - - // The total report amount - total: PropTypes.number, - - // The total report amount - currencySymbol: PropTypes.string, }), - reportActions: PropTypes.arrayOf(PropTypes.shape(ReportActionPropTypes)), + reportActions: PropTypes.arrayOf(PropTypes.shape(ReportActionPropTypes)), // should this be array/object? // Session info for the currently logged in user. session: PropTypes.shape({ @@ -114,28 +108,14 @@ class IOUDetailsModal extends Component { pointerEvents="box-none" style={[styles.detailsPageContainer, styles.p5]} > - - - - {`${this.props.iouReport.currencySymbol}${this.props.iouReport.total}`} - - {'Cat A'} - {' owes '} - {'Cat B'} - - - - - - - + {_.map(this.props.iouReport.transactions, (transaction) => { const actionForTransaction = _.find(this.props.reportActions, (action) => { if (action && action.originalMessage) { - return action.originalMessage.IOUTransactionID == transaction.transactionID; // TODO + return action.originalMessage.IOUTransactionID == transaction.transactionID; } return false; }); @@ -143,8 +123,6 @@ class IOUDetailsModal extends Component { ); })} From 68c0041ec5dd202480b19ede27eb6ac9c4debcc7 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 26 Apr 2021 17:15:16 +0100 Subject: [PATCH 025/141] format IOU Action --- src/libs/actions/IOU.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 0d3262a0853f..6aff3d1e1c20 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -133,7 +133,7 @@ function getIOUReportDetailFromTransactionID(transactionID) { /** * Settles an IOU Report */ - function settleIOUReport({ +function settleIOUReport({ reportID, paymentMethodType, }) { // Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: false}); @@ -151,8 +151,8 @@ function getIOUReportDetailFromTransactionID(transactionID) { /** * Decline or cancel a transaction */ - function rejectTransaction({ - reportID, transactionID, comment +function rejectTransaction({ + reportID, transactionID, comment, }) { console.debug('juless: rejectTransaction', {reportID, transactionID, comment}); From 4021260e4cd77a5af0dfbb1d8b5d956b355ce7cb Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 27 Apr 2021 14:16:54 +0100 Subject: [PATCH 026/141] add shouldHidePayButton prop to PreviewComponent --- src/components/ReportActionItemIOUPreview.js | 11 ++++++++++- src/pages/iou/IOUDetailsModal.js | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItemIOUPreview.js b/src/components/ReportActionItemIOUPreview.js index 0f3bf02d4541..4e1d5eeedc9b 100644 --- a/src/components/ReportActionItemIOUPreview.js +++ b/src/components/ReportActionItemIOUPreview.js @@ -9,6 +9,9 @@ import ONYXKEYS from '../ONYXKEYS'; import MultipleAvatars from './MultipleAvatars'; const propTypes = { + // Additional logic for displaying the pay button + shouldHidePayButton: PropTypes.bool, + // Active IOU Report for current report iou: PropTypes.shape({ // Email address of the manager in this iou report @@ -36,10 +39,15 @@ const propTypes = { })).isRequired, }; +const defaultProps = { + shouldHidePayButton: false, +} + const ReportActionItemIOUPreview = ({ iou, personalDetails, session, + shouldHidePayButton, }) => { const sessionEmail = lodashGet(session, 'email', null); @@ -80,7 +88,7 @@ const ReportActionItemIOUPreview = ({ />
- {isCurrentUserManager && ( + {isCurrentUserManager && !shouldHidePayButton && ( @@ -100,6 +108,7 @@ const ReportActionItemIOUPreview = ({ }; ReportActionItemIOUPreview.propTypes = propTypes; +ReportActionItemIOUPreview.defaultProps = defaultProps; ReportActionItemIOUPreview.displayName = 'ReportActionItemIOUPreview'; export default withOnyx({ diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 92d53861980b..66c6a9e1f34b 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -111,6 +111,7 @@ class IOUDetailsModal extends Component { {_.map(this.props.iouReport.transactions, (transaction) => { const actionForTransaction = _.find(this.props.reportActions, (action) => { From ee830c373321b0084b662b50fb6e48efab4eeeea Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 27 Apr 2021 14:26:56 +0100 Subject: [PATCH 027/141] order IOU transactions by creation date --- src/pages/iou/IOUDetailsModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 66c6a9e1f34b..3b6e055a594c 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -113,7 +113,7 @@ class IOUDetailsModal extends Component { session={this.props.session} shouldHidePayButton={true} /> - {_.map(this.props.iouReport.transactions, (transaction) => { + {_.map(this.props.iouReport.transactions.reverse(), (transaction) => { const actionForTransaction = _.find(this.props.reportActions, (action) => { if (action && action.originalMessage) { return action.originalMessage.IOUTransactionID == transaction.transactionID; From 151f45b8196e3394b741e4d5149a569ad0d13c61 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 27 Apr 2021 15:14:26 +0100 Subject: [PATCH 028/141] refactor launch IOUModal func, call when IOU Preview pay button is pressed --- src/components/ReportActionItemIOUAction.js | 48 ++++++++++++-------- src/components/ReportActionItemIOUPreview.js | 8 +++- src/components/ReportActionItemIOUQuote.js | 13 +----- src/libs/actions/IOU.js | 20 +++++++- src/pages/iou/IOUDetailsModal.js | 3 +- 5 files changed, 60 insertions(+), 32 deletions(-) diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index 3fe8db34c135..4df2fdbe8498 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {Component} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; @@ -7,6 +7,7 @@ import ONYXKEYS from '../ONYXKEYS'; import ReportActionItemIOUQuote from './ReportActionItemIOUQuote'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; import ReportActionItemIOUPreview from './ReportActionItemIOUPreview'; +import {launchDetailsFromIOUAction} from '../libs/actions/IOU'; const propTypes = { // All the data of the action @@ -14,7 +15,7 @@ const propTypes = { // The linked iouReportID // eslint-disable-next-line react/no-unused-prop-types - iouReportID: PropTypes.number.isRequired, + iouReportID: PropTypes.number, // Should render the preview Component? shouldDisplayPreviewComp: PropTypes.bool.isRequired, @@ -41,24 +42,35 @@ const propTypes = { const defaultProps = { iou: {}, + iouReportID: null, }; -const ReportActionItemIOUAction = ({ - action, - shouldDisplayPreviewComp, - iou, - session, -}) => ( - - - {shouldDisplayPreviewComp && !_.isEmpty(iou) && ( - - )} - -); +class ReportActionItemIOUAction extends Component { + constructor(props) { + super(props); + + this.launchIOUDetailsModal = this.launchIOUDetailsModal.bind(this); + } + + launchIOUDetailsModal() { + launchDetailsFromIOUAction(this.props.action); + } + + render() { + return ( + + + {this.props.shouldDisplayPreviewComp && !_.isEmpty(this.props.iou) && ( + + )} + + ); + } +} ReportActionItemIOUAction.propTypes = propTypes; ReportActionItemIOUAction.defaultProps = defaultProps; diff --git a/src/components/ReportActionItemIOUPreview.js b/src/components/ReportActionItemIOUPreview.js index 4e1d5eeedc9b..9e701fd8d6c6 100644 --- a/src/components/ReportActionItemIOUPreview.js +++ b/src/components/ReportActionItemIOUPreview.js @@ -12,6 +12,9 @@ const propTypes = { // Additional logic for displaying the pay button shouldHidePayButton: PropTypes.bool, + // Callback for the Pay/Settle button + onPayButtonPressed: PropTypes.func, + // Active IOU Report for current report iou: PropTypes.shape({ // Email address of the manager in this iou report @@ -41,13 +44,15 @@ const propTypes = { const defaultProps = { shouldHidePayButton: false, -} + onPayButtonPressed: null, +}; const ReportActionItemIOUPreview = ({ iou, personalDetails, session, shouldHidePayButton, + onPayButtonPressed, }) => { const sessionEmail = lodashGet(session, 'email', null); @@ -91,6 +96,7 @@ const ReportActionItemIOUPreview = ({ {isCurrentUserManager && !shouldHidePayButton && ( ( { - if (!action.originalMessage) { - console.error('reportAction `originalMessage` data not provided.'); - } - if (action.originalMessage.IOUTransactionID) { - getIOUReportDetailFromTransactionID(action.originalMessage.IOUTransactionID); - } else if (action.originalMessage.IOUReportID) { - Navigation.navigate(ROUTES.getIouDetailsRoute(action.originalMessage.IOUReportID)); - } + launchDetailsFromIOUAction(action); }} > View Details diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 6aff3d1e1c20..3b238fe4a5f6 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -165,11 +165,29 @@ function rejectTransaction({ }); } +/** + * @param {Object} action + * @param {Object} originalMessage + * @param {number} action.originalMessage.IOUReportID + * @param {number} action.originalMessage.IOUTransactionID + */ +function launchDetailsFromIOUAction(action) { + if (!action.originalMessage) { + console.error('Error launching IOUDetailModal: reportAction `originalMessage` data not provided.'); + return; + } + if (action.originalMessage.IOUReportID) { + Navigation.navigate(ROUTES.getIouDetailsRoute(action.originalMessage.IOUReportID)); + } else if (action.originalMessage.IOUTransactionID) { + getIOUReportDetailFromTransactionID(action.originalMessage.IOUTransactionID); + } +} + export { getPreferredCurrency, createIOUTransaction, createIOUSplit, - getIOUReportDetailFromTransactionID, + launchDetailsFromIOUAction, rejectTransaction, settleIOUReport, }; diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 3b6e055a594c..3a37a5d9230d 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -111,7 +111,8 @@ class IOUDetailsModal extends Component { {_.map(this.props.iouReport.transactions.reverse(), (transaction) => { const actionForTransaction = _.find(this.props.reportActions, (action) => { From dd08992ee29f5fc807428c25052759a9c57669e0 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 27 Apr 2021 18:13:42 +0100 Subject: [PATCH 029/141] display loading icon in button during IOU settlement --- src/libs/actions/IOU.js | 13 ++++++++----- src/pages/iou/IOUDetailsModal.js | 21 +++++++++++++++------ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 3b238fe4a5f6..f7c8fd99b4c3 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -136,16 +136,19 @@ function getIOUReportDetailFromTransactionID(transactionID) { function settleIOUReport({ reportID, paymentMethodType, }) { - // Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: false}); - console.debug('juless: settleIOUReport', {reportID, paymentMethodType}); - + Onyx.merge(ONYXKEYS.IOU, {loading: true, error: false}); API.PayIOU({ reportID, paymentMethodType, }) .then((data) => { - console.debug('juless: IOU Settled: ', data); - }); + if (data.jsonCode != 200) { + console.error(data.message); + return; + } + }) + .catch(() => Onyx.merge(ONYXKEYS.IOU, {error: true})) + .finally(() => Onyx.merge(ONYXKEYS.IOU, {loading: false})); } /** diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 3a37a5d9230d..6a1969b192ff 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -35,6 +35,15 @@ const propTypes = { // Route params route: matchType.isRequired, + // Holds data related to IOU view state, rather than the underlying IOU data. + iou: PropTypes.shape({ + // Is the IOU Report currently being settled + loading: PropTypes.bool, + + // Whether or not transaction creation has resulted to error + error: PropTypes.bool, + }).isRequired, + // IOU Report data object iouReport: PropTypes.shape({ // ID for the chatReport that this IOU is linked to @@ -72,7 +81,6 @@ class IOUDetailsModal extends Component { super(props); this.state = { - loading: false, settlementType: 'Elsewhere', }; @@ -91,13 +99,11 @@ class IOUDetailsModal extends Component { reportID: this.props.route.params.iouReportID, paymentMethodType: this.state.settlementType, }); - this.setState({ - loading: true, - }); } render() { const sessionEmail = lodashGet(this.props.session, 'email', null); + const transactionsByCreationDate = this.props.iouReport.transactions ? this.props.iouReport.transactions.reverse() : []; return ( - {_.map(this.props.iouReport.transactions.reverse(), (transaction) => { + {_.map(transactionsByCreationDate, (transaction) => { const actionForTransaction = _.find(this.props.reportActions, (action) => { if (action && action.originalMessage) { return action.originalMessage.IOUTransactionID == transaction.transactionID; @@ -131,7 +137,7 @@ class IOUDetailsModal extends Component { {(this.props.iouReport.managerEmail === sessionEmail && ( ))} @@ -147,6 +153,9 @@ IOUDetailsModal.defaultProps = defaultProps; export default compose( withOnyx({ + iou: { + key: ONYXKEYS.IOU, + }, iouReport: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_IOUS}${route.params.iouReportID}`, }, From 178bb001b73dc214a34221f9d7c9974968422b02 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Wed, 28 Apr 2021 17:05:16 +0100 Subject: [PATCH 030/141] display transaction in same style as chat comment --- src/components/TransactionItem.js | 64 +++++++++++++++++++++++++++---- src/styles/styles.js | 7 ++++ 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/src/components/TransactionItem.js b/src/components/TransactionItem.js index f778d6321ed2..61c9ce800f30 100644 --- a/src/components/TransactionItem.js +++ b/src/components/TransactionItem.js @@ -1,10 +1,17 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; +import {withOnyx} from 'react-native-onyx'; import {View, Text, Pressable} from 'react-native-web'; -import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; -import ReportActionItemIOUAction from './ReportActionItemIOUAction'; +import _ from 'underscore'; import styles from '../styles/styles'; +import Avatar from './Avatar'; +import CONST from '../CONST'; +import ONYXKEYS from '../ONYXKEYS'; import {rejectTransaction} from '../libs/actions/IOU'; +import ReportActionItemFragment from '../pages/home/report/ReportActionItemFragment'; +import ReportActionItemDate from '../pages/home/report/ReportActionItemDate'; +import personalDetailsPropType from '../pages/personalDetailsPropType'; +import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; const propTypes = { action: PropTypes.shape(ReportActionPropTypes).isRequired, @@ -24,6 +31,13 @@ const propTypes = { // Was this transaction created by the current user createdByUser: PropTypes.bool, }).isRequired, + + // All of the personalDetails + personalDetails: PropTypes.objectOf(personalDetailsPropType), +}; + +const defaultProps = { + personalDetails: {}, }; class TransactionItem extends Component { @@ -44,12 +58,43 @@ class TransactionItem extends Component { } render() { + const {avatar, displayName} = this.props.personalDetails[this.props.action.actorEmail] || {}; + const avatarUrl = this.props.action.automatic + ? `${CONST.CLOUDFRONT_URL}/images/icons/concierge_2019.svg` + + // Use avatar in personalDetails if we have one then fallback to avatar provided by the action + : (avatar || this.props.action.avatar); + + // Since the display name for a report action message is delivered with the report history as an array of fragments + // we'll need to take the displayName from personal details and have it be in the same format for now. Eventually, + // we should stop referring to the report history items entirely for this information. + const personArray = displayName ? [{type: 'TEXT', text: displayName}] : this.props.action.person; + console.debug('juless: ', this.props.action.message); return ( - + + + + + {_.map(personArray, (fragment, index) => ( + + ))} + + + + {this.props.action.message[0].text} + + + this.removeTransaction()} @@ -64,5 +109,10 @@ class TransactionItem extends Component { } TransactionItem.displayName = 'TransactionItem'; +TransactionItem.defaultProps = defaultProps; TransactionItem.propTypes = propTypes; -export default TransactionItem; +export default withOnyx({ + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS, + }, +})(TransactionItem); \ No newline at end of file diff --git a/src/styles/styles.js b/src/styles/styles.js index baee8323c28f..f2296135c214 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -1144,6 +1144,13 @@ const styles = { ...{borderRadius: variables.componentBorderRadiusSmall}, }, + reportTransaction: { + paddingTop: 8, + paddingBottom: 8, + display: 'flex', + flexDirection: 'row', + }, + settingsPageBackground: { flexDirection: 'column', width: '100%', From c5448e9417a0ed3ba61f6abb6e7bf69ae9252b52 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 29 Apr 2021 15:04:52 +0100 Subject: [PATCH 031/141] reuse ReportActionItemSingle Component --- src/components/TransactionItem.js | 47 ++++--------------- .../home/report/ReportActionItemSingle.js | 7 ++- 2 files changed, 15 insertions(+), 39 deletions(-) diff --git a/src/components/TransactionItem.js b/src/components/TransactionItem.js index 61c9ce800f30..14236dae7cdd 100644 --- a/src/components/TransactionItem.js +++ b/src/components/TransactionItem.js @@ -4,14 +4,11 @@ import {withOnyx} from 'react-native-onyx'; import {View, Text, Pressable} from 'react-native-web'; import _ from 'underscore'; import styles from '../styles/styles'; -import Avatar from './Avatar'; -import CONST from '../CONST'; import ONYXKEYS from '../ONYXKEYS'; import {rejectTransaction} from '../libs/actions/IOU'; -import ReportActionItemFragment from '../pages/home/report/ReportActionItemFragment'; -import ReportActionItemDate from '../pages/home/report/ReportActionItemDate'; import personalDetailsPropType from '../pages/personalDetailsPropType'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; +import ReportActionItemSingle from '../pages/home/report/ReportActionItemSingle'; const propTypes = { action: PropTypes.shape(ReportActionPropTypes).isRequired, @@ -58,43 +55,17 @@ class TransactionItem extends Component { } render() { - const {avatar, displayName} = this.props.personalDetails[this.props.action.actorEmail] || {}; - const avatarUrl = this.props.action.automatic - ? `${CONST.CLOUDFRONT_URL}/images/icons/concierge_2019.svg` - - // Use avatar in personalDetails if we have one then fallback to avatar provided by the action - : (avatar || this.props.action.avatar); - - // Since the display name for a report action message is delivered with the report history as an array of fragments - // we'll need to take the displayName from personal details and have it be in the same format for now. Eventually, - // we should stop referring to the report history items entirely for this information. - const personArray = displayName ? [{type: 'TEXT', text: displayName}] : this.props.action.person; console.debug('juless: ', this.props.action.message); return ( - - - - - {_.map(personArray, (fragment, index) => ( - - ))} - - - - {this.props.action.message[0].text} - - - + + + {this.props.action.message[0].text} + + this.removeTransaction()} diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index 84e08efdc84c..c98ae804b9b0 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -19,18 +19,23 @@ const propTypes = { // All of the personalDetails personalDetails: PropTypes.objectOf(personalDetailsPropType), + // Styles for the outermost View + outerViewStyles: PropTypes.arrayOf(PropTypes.object), + // Children view component for this action item children: PropTypes.node.isRequired, }; const defaultProps = { personalDetails: {}, + outerViewStyles: [styles.chatItem], }; const ReportActionItemSingle = ({ action, personalDetails, children, + outerViewStyles, }) => { const {avatar, displayName} = personalDetails[action.actorEmail] || {}; const avatarUrl = action.automatic @@ -44,7 +49,7 @@ const ReportActionItemSingle = ({ // we should stop referring to the report history items entirely for this information. const personArray = displayName ? [{type: 'TEXT', text: displayName}] : action.person; return ( - + Date: Thu, 29 Apr 2021 16:38:01 +0100 Subject: [PATCH 032/141] rename TransactionItem to ReportTransaction --- .../{TransactionItem.js => ReportTransaction.js} | 12 +++++------- src/pages/iou/IOUDetailsModal.js | 4 ++-- 2 files changed, 7 insertions(+), 9 deletions(-) rename src/components/{TransactionItem.js => ReportTransaction.js} (90%) diff --git a/src/components/TransactionItem.js b/src/components/ReportTransaction.js similarity index 90% rename from src/components/TransactionItem.js rename to src/components/ReportTransaction.js index 14236dae7cdd..b0eca4c5d842 100644 --- a/src/components/TransactionItem.js +++ b/src/components/ReportTransaction.js @@ -37,7 +37,7 @@ const defaultProps = { personalDetails: {}, }; -class TransactionItem extends Component { +class ReportTransaction extends Component { constructor(props) { super(props); @@ -45,8 +45,6 @@ class TransactionItem extends Component { } removeTransaction() { - // TODO: delegate to parent - console.debug('removeTransaction'); rejectTransaction({ reportID: 999, transactionID: 999999, @@ -79,11 +77,11 @@ class TransactionItem extends Component { } } -TransactionItem.displayName = 'TransactionItem'; -TransactionItem.defaultProps = defaultProps; -TransactionItem.propTypes = propTypes; +ReportTransaction.displayName = 'ReportTransaction'; +ReportTransaction.defaultProps = defaultProps; +ReportTransaction.propTypes = propTypes; export default withOnyx({ personalDetails: { key: ONYXKEYS.PERSONAL_DETAILS, }, -})(TransactionItem); \ No newline at end of file +})(ReportTransaction); \ No newline at end of file diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 6a1969b192ff..4da97f34ff8b 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -6,7 +6,7 @@ import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import styles from '../../styles/styles'; import ONYXKEYS from '../../ONYXKEYS'; -import TransactionItem from '../../components/TransactionItem'; +import ReportTransaction from '../../components/ReportTransaction'; import HeaderWithCloseButton from '../../components/HeaderWithCloseButton'; import Navigation from '../../libs/Navigation/Navigation'; import ButtonWithLoader from '../../components/ButtonWithLoader'; @@ -128,7 +128,7 @@ class IOUDetailsModal extends Component { return false; }); return ( - From 3398b205ffd20afbe5470f3bf50f3a3d8dec0e5d Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 29 Apr 2021 17:54:43 +0100 Subject: [PATCH 033/141] reject specific transaction when button is pressed --- src/components/ReportTransaction.js | 12 +++++++++--- src/pages/iou/IOUDetailsModal.js | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/ReportTransaction.js b/src/components/ReportTransaction.js index b0eca4c5d842..e523bb629861 100644 --- a/src/components/ReportTransaction.js +++ b/src/components/ReportTransaction.js @@ -11,6 +11,10 @@ import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; import ReportActionItemSingle from '../pages/home/report/ReportActionItemSingle'; const propTypes = { + // The chatReport which the transaction is associated with + chatReportID: PropTypes.number, + + // The report action which we are displaying action: PropTypes.shape(ReportActionPropTypes).isRequired, // Transaction to display @@ -25,6 +29,8 @@ const propTypes = { // The transaction amount amount: PropTypes.number, + transactionID: PropTypes.number, + // Was this transaction created by the current user createdByUser: PropTypes.bool, }).isRequired, @@ -46,9 +52,9 @@ class ReportTransaction extends Component { removeTransaction() { rejectTransaction({ - reportID: 999, - transactionID: 999999, - comment: 'NO!', + reportID: this.props.chatReportID, + transactionID: this.props.transaction.transactionID, + comment: 'no comment', }); } diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 4da97f34ff8b..bc0183359eb2 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -129,6 +129,7 @@ class IOUDetailsModal extends Component { }); return ( From 967db30e36b97800b8949ce1c6d360972a8314ce Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 29 Apr 2021 17:55:15 +0100 Subject: [PATCH 034/141] fix issue where IOU was undefined when accessed --- src/pages/iou/IOUDetailsModal.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index bc0183359eb2..48d4b882c743 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -28,6 +28,7 @@ const defaultProps = { chatReportID: 0, }, reportActions: [], + iou: {}, }; const propTypes = { From 2f712c81c1fb8cbc945c8d4fe0b3f9a3d27ba209 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 30 Apr 2021 18:50:41 +0100 Subject: [PATCH 035/141] ensure the report is updated after local settlement --- src/libs/actions/IOU.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index f7c8fd99b4c3..55d8940fd6b2 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -2,7 +2,7 @@ import Onyx from 'react-native-onyx'; import _ from 'underscore'; import ONYXKEYS from '../../ONYXKEYS'; import * as API from '../API'; -import {getSimplifiedIOUReport} from './Report'; +import {getSimplifiedIOUReport, fetchChatReportsByIDs} from './Report'; import Navigation from '../Navigation/Navigation'; import ROUTES from '../../ROUTES'; @@ -134,7 +134,7 @@ function getIOUReportDetailFromTransactionID(transactionID) { * Settles an IOU Report */ function settleIOUReport({ - reportID, paymentMethodType, + chatReportID, reportID, paymentMethodType, }) { Onyx.merge(ONYXKEYS.IOU, {loading: true, error: false}); API.PayIOU({ @@ -147,6 +147,7 @@ function settleIOUReport({ return; } }) + .then(fetchChatReportsByIDs(chatReportID)) .catch(() => Onyx.merge(ONYXKEYS.IOU, {error: true})) .finally(() => Onyx.merge(ONYXKEYS.IOU, {loading: false})); } From ea30ecce1ef850552b5d74cc03727c6de6634711 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 30 Apr 2021 18:57:11 +0100 Subject: [PATCH 036/141] separate IOU Detail transactions to Component, to fix compose bug --- src/components/IOUDetailsTransactions.js | 66 ++++++++++++++++++++++++ src/pages/iou/IOUDetailsModal.js | 53 ++++--------------- 2 files changed, 77 insertions(+), 42 deletions(-) create mode 100644 src/components/IOUDetailsTransactions.js diff --git a/src/components/IOUDetailsTransactions.js b/src/components/IOUDetailsTransactions.js new file mode 100644 index 000000000000..fc10c652c7d7 --- /dev/null +++ b/src/components/IOUDetailsTransactions.js @@ -0,0 +1,66 @@ +import React, {PureComponent} from 'react'; +import {withOnyx} from 'react-native-onyx'; +import _ from 'underscore'; +import ONYXKEYS from '../ONYXKEYS'; +import PropTypes from 'prop-types'; +import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; +import ReportTransaction from './ReportTransaction'; +import { View } from 'react-native-web'; + +const propTypes = { + reportActions: PropTypes.arrayOf(PropTypes.shape(ReportActionPropTypes)), // should this be array/object? + + // ReportID for the associated chat report + chatReportID: PropTypes.number, + + // ReportID for the associated IOU report + iouReportID: PropTypes.number, + + transactions: PropTypes.arrayOf(PropTypes.shape({ + // The transaction currency + currency: PropTypes.string, + + // The transaction amount + total: PropTypes.number, + + // The transaction comment + comment: PropTypes.string, + })), +}; + +const defaultProps = { + reportActions: [], +}; + +class IOUDetailsTransactions extends PureComponent { + render() { + const transactionsByCreationDate = this.props.transactions ? this.props.transactions.reverse() : []; + return + {_.map(transactionsByCreationDate, (transaction) => { + const actionForTransaction = _.find(this.props.reportActions, (action) => { + if (action && action.originalMessage) { + return action.originalMessage.IOUTransactionID == transaction.transactionID; + // TODO: make sure type is equal + } + return false; + }); + return ( + + ); + })} + + } +} + +IOUDetailsTransactions.defaultProps = defaultProps; +IOUDetailsTransactions.propTypes = propTypes; +export default withOnyx({ + reportActions: { + key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReportID}`, + canEvict: false, + }, +})(IOUDetailsTransactions); \ No newline at end of file diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 48d4b882c743..eae1f3af31c6 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -6,15 +6,13 @@ import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import styles from '../../styles/styles'; import ONYXKEYS from '../../ONYXKEYS'; -import ReportTransaction from '../../components/ReportTransaction'; import HeaderWithCloseButton from '../../components/HeaderWithCloseButton'; import Navigation from '../../libs/Navigation/Navigation'; import ButtonWithLoader from '../../components/ButtonWithLoader'; import ScreenWrapper from '../../components/ScreenWrapper'; -import compose from '../../libs/compose'; import {settleIOUReport} from '../../libs/actions/IOU'; -import ReportActionPropTypes from '../home/report/ReportActionPropTypes'; import ReportActionItemIOUPreview from '../../components/ReportActionItemIOUPreview'; +import IOUDetailsTransactions from '../../components/IOUDetailsTransactions'; const matchType = PropTypes.shape({ params: PropTypes.shape({ @@ -24,10 +22,7 @@ const matchType = PropTypes.shape({ }); const defaultProps = { - iouReport: { - chatReportID: 0, - }, - reportActions: [], + iouReport: {}, iou: {}, }; @@ -43,7 +38,7 @@ const propTypes = { // Whether or not transaction creation has resulted to error error: PropTypes.bool, - }).isRequired, + }), // IOU Report data object iouReport: PropTypes.shape({ @@ -68,8 +63,6 @@ const propTypes = { })), }), - reportActions: PropTypes.arrayOf(PropTypes.shape(ReportActionPropTypes)), // should this be array/object? - // Session info for the currently logged in user. session: PropTypes.shape({ // Currently logged in user email @@ -88,15 +81,9 @@ class IOUDetailsModal extends Component { this.performIOUSettlement = this.performIOUSettlement.bind(this); } - componentDidUpdate(prevProps) { - console.debug('juless props: ', this.props); - if (prevProps.reportActions !== this.props.reportActions) { - console.debug('juless: reportActions: ', this.props.reportActions); - } - } - performIOUSettlement() { settleIOUReport({ + chatReportID: this.props.iouReport.chatReportID, reportID: this.props.route.params.iouReportID, paymentMethodType: this.state.settlementType, }); @@ -104,7 +91,6 @@ class IOUDetailsModal extends Component { render() { const sessionEmail = lodashGet(this.props.session, 'email', null); - const transactionsByCreationDate = this.props.iouReport.transactions ? this.props.iouReport.transactions.reverse() : []; return ( - {_.map(transactionsByCreationDate, (transaction) => { - const actionForTransaction = _.find(this.props.reportActions, (action) => { - if (action && action.originalMessage) { - return action.originalMessage.IOUTransactionID == transaction.transactionID; - } - return false; - }); - return ( - - ); - })} + {(this.props.iouReport.managerEmail === sessionEmail && ( `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.chatReportID}`, - canEvict: false, - }, - }), + } )(IOUDetailsModal); From 6d5c1e4f8b1f5dedaad7e848b309abeb91b42e56 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 30 Apr 2021 19:19:56 +0100 Subject: [PATCH 037/141] format and fix lint errors --- src/components/IOUDetailsTransactions.js | 50 +++++++++++++----------- src/components/ReportTransaction.js | 28 +++---------- src/libs/actions/IOU.js | 1 - src/pages/iou/IOUDetailsModal.js | 22 +++++------ 4 files changed, 43 insertions(+), 58 deletions(-) diff --git a/src/components/IOUDetailsTransactions.js b/src/components/IOUDetailsTransactions.js index fc10c652c7d7..1416b726b01d 100644 --- a/src/components/IOUDetailsTransactions.js +++ b/src/components/IOUDetailsTransactions.js @@ -1,20 +1,20 @@ import React, {PureComponent} from 'react'; +import View from 'react-native-web'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import ONYXKEYS from '../ONYXKEYS'; import PropTypes from 'prop-types'; +import ONYXKEYS from '../ONYXKEYS'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; import ReportTransaction from './ReportTransaction'; -import { View } from 'react-native-web'; const propTypes = { reportActions: PropTypes.arrayOf(PropTypes.shape(ReportActionPropTypes)), // should this be array/object? // ReportID for the associated chat report - chatReportID: PropTypes.number, + chatReportID: PropTypes.number.isRequired, // ReportID for the associated IOU report - iouReportID: PropTypes.number, + iouReportID: PropTypes.number.isRequired, transactions: PropTypes.arrayOf(PropTypes.shape({ // The transaction currency @@ -30,29 +30,33 @@ const propTypes = { const defaultProps = { reportActions: [], + transactions: [], }; class IOUDetailsTransactions extends PureComponent { render() { const transactionsByCreationDate = this.props.transactions ? this.props.transactions.reverse() : []; - return - {_.map(transactionsByCreationDate, (transaction) => { - const actionForTransaction = _.find(this.props.reportActions, (action) => { - if (action && action.originalMessage) { - return action.originalMessage.IOUTransactionID == transaction.transactionID; - // TODO: make sure type is equal - } - return false; - }); - return ( - - ); - })} - + return ( + + {_.map(transactionsByCreationDate, (transaction) => { + const actionForTransaction = _.find(this.props.reportActions, (action) => { + if (action && action.originalMessage) { + return action.originalMessage.IOUTransactionID == transaction.transactionID; + + // TODO: make sure type is equal + } + return false; + }); + return ( + + ); + })} + + ); } } @@ -63,4 +67,4 @@ export default withOnyx({ key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReportID}`, canEvict: false, }, -})(IOUDetailsTransactions); \ No newline at end of file +})(IOUDetailsTransactions); diff --git a/src/components/ReportTransaction.js b/src/components/ReportTransaction.js index e523bb629861..cbe383b01bff 100644 --- a/src/components/ReportTransaction.js +++ b/src/components/ReportTransaction.js @@ -1,18 +1,14 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; -import {withOnyx} from 'react-native-onyx'; import {View, Text, Pressable} from 'react-native-web'; -import _ from 'underscore'; import styles from '../styles/styles'; -import ONYXKEYS from '../ONYXKEYS'; import {rejectTransaction} from '../libs/actions/IOU'; -import personalDetailsPropType from '../pages/personalDetailsPropType'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; import ReportActionItemSingle from '../pages/home/report/ReportActionItemSingle'; const propTypes = { // The chatReport which the transaction is associated with - chatReportID: PropTypes.number, + chatReportID: PropTypes.number.isRequired, // todo: string is passed! // The report action which we are displaying action: PropTypes.shape(ReportActionPropTypes).isRequired, @@ -29,18 +25,11 @@ const propTypes = { // The transaction amount amount: PropTypes.number, - transactionID: PropTypes.number, + transactionID: PropTypes.number, // todo: string is passed! // Was this transaction created by the current user createdByUser: PropTypes.bool, }).isRequired, - - // All of the personalDetails - personalDetails: PropTypes.objectOf(personalDetailsPropType), -}; - -const defaultProps = { - personalDetails: {}, }; class ReportTransaction extends Component { @@ -66,9 +55,9 @@ class ReportTransaction extends Component { action={this.props.action} outerViewStyles={[styles.reportTransaction]} > - - {this.props.action.message[0].text} - + + {this.props.action.message[0].text} + { if (data.jsonCode != 200) { console.error(data.message); - return; } }) .then(fetchChatReportsByIDs(chatReportID)) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index eae1f3af31c6..2fb534070a9a 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -1,5 +1,4 @@ import React, {Component} from 'react'; -import _ from 'underscore'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; @@ -130,14 +129,13 @@ IOUDetailsModal.displayName = 'IOUDetailsModal'; IOUDetailsModal.defaultProps = defaultProps; export default withOnyx({ - iou: { - key: ONYXKEYS.IOU, - }, - iouReport: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_IOUS}${route.params.iouReportID}`, - }, - session: { - key: ONYXKEYS.SESSION, - }, - } -)(IOUDetailsModal); + iou: { + key: ONYXKEYS.IOU, + }, + iouReport: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_IOUS}${route.params.iouReportID}`, + }, + session: { + key: ONYXKEYS.SESSION, + }, +})(IOUDetailsModal); From 7fd307d52621a6825038d044f4fcb0e5262c967b Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 4 May 2021 14:39:41 +0100 Subject: [PATCH 038/141] refactor IOUTransactions name and file location --- src/pages/iou/IOUDetailsModal.js | 4 ++-- .../iou/IOUTransactions.js} | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) rename src/{components/IOUDetailsTransactions.js => pages/iou/IOUTransactions.js} (85%) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 2fb534070a9a..758f96fa8c1c 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -11,7 +11,7 @@ import ButtonWithLoader from '../../components/ButtonWithLoader'; import ScreenWrapper from '../../components/ScreenWrapper'; import {settleIOUReport} from '../../libs/actions/IOU'; import ReportActionItemIOUPreview from '../../components/ReportActionItemIOUPreview'; -import IOUDetailsTransactions from '../../components/IOUDetailsTransactions'; +import IOUTransactions from './IOUTransactions'; const matchType = PropTypes.shape({ params: PropTypes.shape({ @@ -106,7 +106,7 @@ class IOUDetailsModal extends Component { onPayButtonPressed={null} shouldHidePayButton /> - `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReportID}`, canEvict: false, }, -})(IOUDetailsTransactions); +})(IOUTransactions); From e43f51c4b5de62822b05620690bea118b839cabf Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 4 May 2021 16:47:11 +0100 Subject: [PATCH 039/141] fix bad View import --- src/pages/iou/IOUTransactions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/IOUTransactions.js b/src/pages/iou/IOUTransactions.js index 3e0d067f2699..c86ca3a6bcff 100644 --- a/src/pages/iou/IOUTransactions.js +++ b/src/pages/iou/IOUTransactions.js @@ -1,5 +1,5 @@ import React, {PureComponent} from 'react'; -import View from 'react-native-web'; +import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import PropTypes from 'prop-types'; From 5de2094be4a17db20bb28afedf4571cd4d177a66 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Wed, 5 May 2021 15:18:51 +0100 Subject: [PATCH 040/141] use the updated IOUAction data --- src/libs/actions/IOU.js | 36 ++++++------------------------------ 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 1d7c347932f4..238e2bfd6042 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -113,27 +113,6 @@ function createIOUSplit(params) { }); } -/** - * Retrieve an IOU report using a transactionID, then navigate to the page. - * @param {Int} transactionID - */ -function getIOUReportDetailFromTransactionID(transactionID) { - API.Get({ - returnValueList: 'transactionList', - transactionID, - }) - .then((data) => { - const chatReportID = data.transactionList[0].reportID; - if (!chatReportID) { - return; - } - Navigation.navigate(ROUTES.getIouDetailsRoute(chatReportID)); - }) - .catch((error) => { - console.error('Error retrieving Transaction: ', error); - }); -} - /** * Settles an IOU Report */ @@ -174,20 +153,17 @@ function rejectTransaction({ /** * @param {Object} action - * @param {Object} originalMessage + * @param {Object} action.originalMessage * @param {number} action.originalMessage.IOUReportID - * @param {number} action.originalMessage.IOUTransactionID + * + * Launch the IOU Details Modal, using data from the report action */ function launchDetailsFromIOUAction(action) { - if (!action.originalMessage) { - console.error('Error launching IOUDetailModal: reportAction `originalMessage` data not provided.'); + if (!action.originalMessage || !action.originalMessage.IOUReportID) { + console.error('Error launching IOUDetailModal: reportAction `IOUReportID` not provided.'); return; } - if (action.originalMessage.IOUReportID) { - Navigation.navigate(ROUTES.getIouDetailsRoute(action.originalMessage.IOUReportID)); - } else if (action.originalMessage.IOUTransactionID) { - getIOUReportDetailFromTransactionID(action.originalMessage.IOUTransactionID); - } + Navigation.navigate(ROUTES.getIouDetailsRoute(action.originalMessage.IOUReportID)); } export { From 718c7127192df60ab115b4a59dca82b46febc30e Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Wed, 5 May 2021 16:21:47 +0100 Subject: [PATCH 041/141] display loading indicator while IOU Report is fetched --- src/pages/iou/IOUDetailsModal.js | 56 ++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 758f96fa8c1c..08e8e3fa1de8 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -1,10 +1,11 @@ import React, {Component} from 'react'; -import {View} from 'react-native'; +import {View, ActivityIndicator} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import styles from '../../styles/styles'; import ONYXKEYS from '../../ONYXKEYS'; +import themeColors from '../../styles/themes/default'; import HeaderWithCloseButton from '../../components/HeaderWithCloseButton'; import Navigation from '../../libs/Navigation/Navigation'; import ButtonWithLoader from '../../components/ButtonWithLoader'; @@ -21,7 +22,7 @@ const matchType = PropTypes.shape({ }); const defaultProps = { - iouReport: {}, + iouReport: undefined, iou: {}, }; @@ -80,6 +81,10 @@ class IOUDetailsModal extends Component { this.performIOUSettlement = this.performIOUSettlement.bind(this); } + componentDidMount() { + //fetchIOUReportByID(this.props.route.params.iouReportID, this.props.iouReport.chatReportID); + } + performIOUSettlement() { settleIOUReport({ chatReportID: this.props.iouReport.chatReportID, @@ -90,35 +95,38 @@ class IOUDetailsModal extends Component { render() { const sessionEmail = lodashGet(this.props.session, 'email', null); + const reportIsLoading = this.props.iouReport === undefined; return ( - - - - {(this.props.iouReport.managerEmail === sessionEmail && ( - : ( + + + - ))} - + {(this.props.iouReport.managerEmail === sessionEmail && ( + + ))} + + )} ); } From baabe70ad2920bdf31acf68ff1d5c77a96af3220 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 6 May 2021 15:09:02 +0100 Subject: [PATCH 042/141] hide View Details link for group splits --- src/components/ReportActionItemIOUAction.js | 23 ++++++++++++++--- src/components/ReportActionItemIOUQuote.js | 28 ++++++++++++++------- src/pages/home/report/ReportActionItem.js | 1 + 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index 4df2fdbe8498..702f087155cf 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -14,8 +14,10 @@ const propTypes = { action: PropTypes.shape(ReportActionPropTypes).isRequired, // The linked iouReportID - // eslint-disable-next-line react/no-unused-prop-types - iouReportID: PropTypes.number, + iouReportID: PropTypes.number.isRequired, + + // The linked iouReportID + chatReportID: PropTypes.number.isRequired, // Should render the preview Component? shouldDisplayPreviewComp: PropTypes.bool.isRequired, @@ -33,6 +35,12 @@ const propTypes = { cachedTotal: PropTypes.string, }), + // ChatReport associated with iouReport + chatReport: PropTypes.shape({ + // The participants of this report + participants: PropTypes.arrayOf(PropTypes.string), + }), + // Session info for the currently logged in user. session: PropTypes.shape({ // Currently logged in user email @@ -42,7 +50,7 @@ const propTypes = { const defaultProps = { iou: {}, - iouReportID: null, + chatReport: {}, }; class ReportActionItemIOUAction extends Component { @@ -57,9 +65,13 @@ class ReportActionItemIOUAction extends Component { } render() { + const hasMultipleParticipants = this.props.chatReport.participants.length > 1; return ( - + {this.props.shouldDisplayPreviewComp && !_.isEmpty(this.props.iou) && ( `${ONYXKEYS.COLLECTION.REPORT_IOUS}${iouReportID}`, }, + chatReport: { + key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, + }, session: { key: ONYXKEYS.SESSION, }, diff --git a/src/components/ReportActionItemIOUQuote.js b/src/components/ReportActionItemIOUQuote.js index 4c114f02dca8..f827c109af0f 100644 --- a/src/components/ReportActionItemIOUQuote.js +++ b/src/components/ReportActionItemIOUQuote.js @@ -9,9 +9,16 @@ import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; const propTypes = { // All the data of the action action: PropTypes.shape(ReportActionPropTypes).isRequired, + + // Should the View Details link be displayed? + showViewDetailsLink: PropTypes.bool, }; -const ReportActionItemIOUQuote = ({action}) => ( +const defaultProps = { + showViewDetailsLink: false, +} + +const ReportActionItemIOUQuote = ({action, showViewDetailsLink}) => ( {_.map(action.message, (fragment, index) => ( @@ -19,14 +26,16 @@ const ReportActionItemIOUQuote = ({action}) => ( {fragment.text} - { - launchDetailsFromIOUAction(action); - }} - > - View Details - + {showViewDetailsLink && ( + { + launchDetailsFromIOUAction(action); + }} + > + View Details + + )} ))} @@ -34,6 +43,7 @@ const ReportActionItemIOUQuote = ({action}) => ( ); ReportActionItemIOUQuote.propTypes = propTypes; +ReportActionItemIOUQuote.defaultProps = defaultProps; ReportActionItemIOUQuote.displayName = 'ReportActionItemIOUQuote'; export default ReportActionItemIOUQuote; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 43d0d691d60d..dfaf80adacb7 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -108,6 +108,7 @@ class ReportActionItem extends Component { ? ( From 59be4bef7b850458f3aed291603e6206bf7f6461 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 6 May 2021 15:38:16 +0100 Subject: [PATCH 043/141] modify IOU Details route to take chatReportID and IOUReportID --- src/ROUTES.js | 4 ++-- src/libs/actions/IOU.js | 2 +- src/pages/iou/IOUDetailsModal.js | 11 +++++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/ROUTES.js b/src/ROUTES.js index 111bb2f54dfd..0d44726e9c6f 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -28,8 +28,8 @@ export default { IOU_BILL: 'iou/split/:reportID', getIouSplitRoute: reportID => `iou/split/${reportID}`, IOU_DETAILS: 'iou/details', - IOU_DETAILS_WITH_IOU_REPORT_ID: 'iou/details/:iouReportID', - getIouDetailsRoute: iouReportID => `iou/details/${iouReportID}`, + IOU_DETAILS_WITH_IOU_REPORT_ID: 'iou/details/:chatReportID/:iouReportID/', + getIouDetailsRoute: (chatReportID, iouReportID) => `iou/details/${chatReportID}/${iouReportID}`, SEARCH: 'search', SET_PASSWORD_WITH_VALIDATE_CODE: 'setpassword/:accountID/:validateCode', DETAILS: 'details', diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 238e2bfd6042..174ca6f86485 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -163,7 +163,7 @@ function launchDetailsFromIOUAction(action) { console.error('Error launching IOUDetailModal: reportAction `IOUReportID` not provided.'); return; } - Navigation.navigate(ROUTES.getIouDetailsRoute(action.originalMessage.IOUReportID)); + Navigation.navigate(ROUTES.getIouDetailsRoute(1042, action.originalMessage.IOUReportID)); } export { diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 08e8e3fa1de8..c3dcaabd02b3 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -16,7 +16,10 @@ import IOUTransactions from './IOUTransactions'; const matchType = PropTypes.shape({ params: PropTypes.shape({ - // iouReportID passed via route /iou/details/:iouReportID + // chatReportID passed via route /iou/:chatReportID // todo: drop /iou/ + chatReportID: PropTypes.string, + + // iouReportID passed via route /iou/:iouReportID iouReportID: PropTypes.string, }), }); @@ -82,12 +85,12 @@ class IOUDetailsModal extends Component { } componentDidMount() { - //fetchIOUReportByID(this.props.route.params.iouReportID, this.props.iouReport.chatReportID); + // update IOU report with IOU action } performIOUSettlement() { settleIOUReport({ - chatReportID: this.props.iouReport.chatReportID, + chatReportID: this.props.route.params.chatReportID, reportID: this.props.route.params.iouReportID, paymentMethodType: this.state.settlementType, }); @@ -114,7 +117,7 @@ class IOUDetailsModal extends Component { shouldHidePayButton /> From 470b8e2d28e02bfa1b0acdd61f4051332c92eafb Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 6 May 2021 16:02:19 +0100 Subject: [PATCH 044/141] pass dynamic chatReportID to IOU Details Modal route --- src/components/ReportActionItemIOUAction.js | 3 ++- src/components/ReportActionItemIOUQuote.js | 11 ++++++----- src/libs/actions/IOU.js | 4 ++-- src/pages/iou/IOUDetailsModal.js | 1 - 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index 702f087155cf..12c70110e6fb 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -61,7 +61,7 @@ class ReportActionItemIOUAction extends Component { } launchIOUDetailsModal() { - launchDetailsFromIOUAction(this.props.action); + launchDetailsFromIOUAction(this.props.chatReportID, this.props.action); } render() { @@ -71,6 +71,7 @@ class ReportActionItemIOUAction extends Component { {this.props.shouldDisplayPreviewComp && !_.isEmpty(this.props.iou) && ( ( +const ReportActionItemIOUQuote = ({action, showViewDetailsLink, onViewDetailsPressed}) => ( {_.map(action.message, (fragment, index) => ( @@ -29,9 +32,7 @@ const ReportActionItemIOUQuote = ({action, showViewDetailsLink}) => ( {showViewDetailsLink && ( { - launchDetailsFromIOUAction(action); - }} + onPress={onViewDetailsPressed} > View Details diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 174ca6f86485..60cff4538a03 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -158,12 +158,12 @@ function rejectTransaction({ * * Launch the IOU Details Modal, using data from the report action */ -function launchDetailsFromIOUAction(action) { +function launchDetailsFromIOUAction(chatReportID, action) { if (!action.originalMessage || !action.originalMessage.IOUReportID) { console.error('Error launching IOUDetailModal: reportAction `IOUReportID` not provided.'); return; } - Navigation.navigate(ROUTES.getIouDetailsRoute(1042, action.originalMessage.IOUReportID)); + Navigation.navigate(ROUTES.getIouDetailsRoute(chatReportID, action.originalMessage.IOUReportID)); } export { diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index c3dcaabd02b3..ccb88644b1cd 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -113,7 +113,6 @@ class IOUDetailsModal extends Component { Date: Thu, 6 May 2021 16:11:15 +0100 Subject: [PATCH 045/141] simplify launching of IOU Details Modal, thanks to new prop availability --- src/components/ReportActionItemIOUAction.js | 11 +++++++---- src/libs/actions/IOU.js | 16 ---------------- src/pages/home/report/ReportActionItem.js | 6 +++--- src/pages/iou/IOUDetailsModal.js | 3 ++- 4 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index 12c70110e6fb..c06e577522de 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -7,16 +7,15 @@ import ONYXKEYS from '../ONYXKEYS'; import ReportActionItemIOUQuote from './ReportActionItemIOUQuote'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; import ReportActionItemIOUPreview from './ReportActionItemIOUPreview'; -import {launchDetailsFromIOUAction} from '../libs/actions/IOU'; const propTypes = { // All the data of the action action: PropTypes.shape(ReportActionPropTypes).isRequired, - // The linked iouReportID + // The linked IOUReport iouReportID: PropTypes.number.isRequired, - // The linked iouReportID + // The associated chatReport chatReportID: PropTypes.number.isRequired, // Should render the preview Component? @@ -60,8 +59,12 @@ class ReportActionItemIOUAction extends Component { this.launchIOUDetailsModal = this.launchIOUDetailsModal.bind(this); } + /** + * + * Launch the IOU Details Modal, using data from the report action + */ launchIOUDetailsModal() { - launchDetailsFromIOUAction(this.props.chatReportID, this.props.action); + Navigation.navigate(ROUTES.getIouDetailsRoute(this.props.chatReportID, this.props.IOUReportID)); } render() { diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 60cff4538a03..43c62916d464 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -151,26 +151,10 @@ function rejectTransaction({ }); } -/** - * @param {Object} action - * @param {Object} action.originalMessage - * @param {number} action.originalMessage.IOUReportID - * - * Launch the IOU Details Modal, using data from the report action - */ -function launchDetailsFromIOUAction(chatReportID, action) { - if (!action.originalMessage || !action.originalMessage.IOUReportID) { - console.error('Error launching IOUDetailModal: reportAction `IOUReportID` not provided.'); - return; - } - Navigation.navigate(ROUTES.getIouDetailsRoute(chatReportID, action.originalMessage.IOUReportID)); -} - export { getPreferredCurrency, createIOUTransaction, createIOUSplit, - launchDetailsFromIOUAction, rejectTransaction, settleIOUReport, }; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index dfaf80adacb7..79d3ff1d6205 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -21,6 +21,9 @@ const propTypes = { // The ID of the report this action is on. reportID: PropTypes.number.isRequired, + // IOU report ID associated with current report + iouReportID: PropTypes.number, + // All the data of the action item action: PropTypes.shape(ReportActionPropTypes).isRequired, @@ -33,9 +36,6 @@ const propTypes = { // Whether there is an outstanding amount in IOU hasOutstandingIOU: PropTypes.bool, - // IOU report ID associated with current report - iouReportID: PropTypes.number, - // Should we display the new indicator on top of the comment? shouldDisplayNewIndicator: PropTypes.bool.isRequired, }; diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index ccb88644b1cd..e7048a9b5abc 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -11,6 +11,7 @@ import Navigation from '../../libs/Navigation/Navigation'; import ButtonWithLoader from '../../components/ButtonWithLoader'; import ScreenWrapper from '../../components/ScreenWrapper'; import {settleIOUReport} from '../../libs/actions/IOU'; +import {fetchIOUReportByID} from '../../libs/actions/Report'; import ReportActionItemIOUPreview from '../../components/ReportActionItemIOUPreview'; import IOUTransactions from './IOUTransactions'; @@ -85,7 +86,7 @@ class IOUDetailsModal extends Component { } componentDidMount() { - // update IOU report with IOU action + fetchIOUReportByID(this.props.route.params.chatReportID, this.props.route.params.iouReportID); } performIOUSettlement() { From d7ec83b91442b4f9fae69fce76f46b81f31cbda8 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 6 May 2021 16:12:54 +0100 Subject: [PATCH 046/141] retrieve and persist settled IOU reports to Onyx when viewed --- src/components/ReportTransaction.js | 1 - src/libs/actions/Report.js | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/ReportTransaction.js b/src/components/ReportTransaction.js index cbe383b01bff..d183796f5557 100644 --- a/src/components/ReportTransaction.js +++ b/src/components/ReportTransaction.js @@ -48,7 +48,6 @@ class ReportTransaction extends Component { } render() { - console.debug('juless: ', this.props.action.message); return ( setLocalIOUReportData(iouReportObject, chatReportID)) + .catch((error) => console.error(`Error fetching IOU Report ${iouReportID}: ${error}`)); +} + /** * Updates a report in Onyx with a new pinned state. * @@ -998,6 +1011,7 @@ export { fetchAllReports, fetchActions, fetchOrCreateChatReport, + fetchIOUReportByID, addAction, updateLastReadActionID, subscribeToReportTypingEvents, From f1499b845125bd0583f9ee273fa58cc7ca80900c Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 6 May 2021 18:46:38 +0100 Subject: [PATCH 047/141] get IOUReportID from action to launch the details Modal --- src/components/ReportActionItemIOUAction.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index c06e577522de..acb6b2b4c48a 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -7,6 +7,8 @@ import ONYXKEYS from '../ONYXKEYS'; import ReportActionItemIOUQuote from './ReportActionItemIOUQuote'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; import ReportActionItemIOUPreview from './ReportActionItemIOUPreview'; +import Navigation from '../libs/Navigation/Navigation'; +import ROUTES from '../ROUTES'; const propTypes = { // All the data of the action @@ -64,7 +66,7 @@ class ReportActionItemIOUAction extends Component { * Launch the IOU Details Modal, using data from the report action */ launchIOUDetailsModal() { - Navigation.navigate(ROUTES.getIouDetailsRoute(this.props.chatReportID, this.props.IOUReportID)); + Navigation.navigate(ROUTES.getIouDetailsRoute(this.props.chatReportID, this.props.action.originalMessage.IOUReportID)); } render() { From 885212d024d0915a1a203851e755d47dcc030de8 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 6 May 2021 18:54:31 +0100 Subject: [PATCH 048/141] refactor and vastly simplify Report logic for updating IOU Reports --- src/libs/actions/Report.js | 43 ++++++++++++++--------- src/pages/home/report/ReportActionItem.js | 3 +- src/pages/iou/IOUDetailsModal.js | 2 +- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index a06b61118492..becb1d9f8092 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -354,18 +354,30 @@ function fetchChatReportsByIDs(chatList) { * @param {Number} iouReportObject.total * @param {Number} iouReportObject.reportID * @param {Number} chatReportID + * @param {Boolean} shouldUpdateChatReport - should the local chatReport be updated too? */ -function setLocalIOUReportData(iouReportObject, chatReportID) { +function setLocalIOUReportData(iouReportObject, chatReportID, shouldUpdateChatReport) { + // Persist IOU Report data to Onyx + const iouReportKey = `${ONYXKEYS.COLLECTION.REPORT_IOUS}${iouReportObject.reportID}`; + Onyx.merge(iouReportKey, iouReportObject); + console.debug('merge IOU: ', iouReportObject); + + // We don't always want to update the chatReport, as the IOU could be an old settled report + if (!shouldUpdateChatReport) { + console.log('No need to update the local chat report.'); + return; + } + const chatReportObject = { hasOutstandingIOU: iouReportObject.stateNum === 1 && iouReportObject.total !== 0, iouReportID: iouReportObject.reportID, }; + if (!chatReportObject.hasOutstandingIOU) { chatReportObject.iouReportID = null; } - const iouReportKey = `${ONYXKEYS.COLLECTION.REPORT_IOUS}${iouReportObject.reportID}`; + const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`; - Onyx.merge(iouReportKey, iouReportObject); Onyx.merge(reportKey, chatReportObject); } @@ -464,16 +476,13 @@ function updateReportWithNewAction(reportID, reportAction) { // If chat report receives an action with IOU, update IOU object if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) { - const chatReport = lodashGet(allReports, reportID); - const iouReportID = lodashGet(chatReport, 'iouReportID'); - if (iouReportID) { - fetchIOUReport(iouReportID, reportID) - .then(iouReportObject => setLocalIOUReportData(iouReportObject, reportID)); - } else if (!chatReport || chatReport.participants.length === 1) { - fetchIOUReportID(chatReport ? chatReport.participants[0] : reportAction.actorEmail) - .then(iouID => fetchIOUReport(iouID, reportID)) - .then(iouReportObject => setLocalIOUReportData(iouReportObject, reportID)); + const iouReportID = reportAction.originalMessage.IOUReportID; + if (!iouReportID) { + console.error('IOUReportID not found in report action data, this should not happen!'); } + + // As this flow is for a new Action, we definately need to update the local chat Report + fetchIOUReportByID(iouReportID, reportID, true); } if (!ActiveClientManager.isClientTheLeader()) { @@ -510,13 +519,13 @@ function updateReportWithNewAction(reportID, reportAction) { /** * @param {Number} iouReportID - ID of the report we are fetching * @param {Number} chatReportID - associated chat report ID, this is required in order to link the reports in Onyx + * @param {Boolean} shouldUpdateChatReport - Only necessary if triggered by a notification, or the chatReport lists the IOU report as outstanding * - * Fetch a single IOU Report and persist to Onyx, associating the IOUReport with a chatReport. + * Fetch a single IOU Report and persist to Onyx, associating the IOUReport with a chatReport if it is active. */ -function fetchIOUReportByID(iouReportID, chatReportID) { - console.debug(`fetchIOUReportByID ${iouReportID}, ${chatReportID}`); - fetchIOUReport(chatReportID, iouReportID) - .then(iouReportObject => setLocalIOUReportData(iouReportObject, chatReportID)) +function fetchIOUReportByID(iouReportID, chatReportID, shouldUpdateChatReport) { + fetchIOUReport(iouReportID, chatReportID) + .then(iouReportObject => setLocalIOUReportData(iouReportObject, chatReportID, shouldUpdateChatReport)) .catch((error) => console.error(`Error fetching IOU Report ${iouReportID}: ${error}`)); } diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 79d3ff1d6205..e0557d57083a 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -22,7 +22,7 @@ const propTypes = { reportID: PropTypes.number.isRequired, // IOU report ID associated with current report - iouReportID: PropTypes.number, + iouReportID: PropTypes.number.isRequired, // All the data of the action item action: PropTypes.shape(ReportActionPropTypes).isRequired, @@ -41,7 +41,6 @@ const propTypes = { }; const defaultProps = { - iouReportID: undefined, hasOutstandingIOU: false, }; diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index e7048a9b5abc..611a09dc94c2 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -86,7 +86,7 @@ class IOUDetailsModal extends Component { } componentDidMount() { - fetchIOUReportByID(this.props.route.params.chatReportID, this.props.route.params.iouReportID); + fetchIOUReportByID(this.props.route.params.iouReportID, this.props.route.params.chatReportID, false); } performIOUSettlement() { From 827eb35662c12498e2f0e566601b79ad54339c74 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 7 May 2021 14:47:43 +0100 Subject: [PATCH 049/141] hide settlement button for settled IOUs --- src/pages/iou/IOUDetailsModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 611a09dc94c2..09de97efaebb 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -121,7 +121,7 @@ class IOUDetailsModal extends Component { iouReportID={this.props.route.params.iouReportID} transactions={this.props.iouReport.transactions} /> - {(this.props.iouReport.managerEmail === sessionEmail && ( + {(this.props.iouReport.hasOutstandingIOU && this.props.iouReport.managerEmail === sessionEmail && ( Date: Fri, 7 May 2021 15:10:49 +0100 Subject: [PATCH 050/141] show 'userA paid userB' in Preview if IOU is settled --- src/components/ReportActionItemIOUPreview.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/ReportActionItemIOUPreview.js b/src/components/ReportActionItemIOUPreview.js index 9e701fd8d6c6..1824ee64cfe3 100644 --- a/src/components/ReportActionItemIOUPreview.js +++ b/src/components/ReportActionItemIOUPreview.js @@ -81,9 +81,10 @@ const ReportActionItemIOUPreview = ({ {cachedTotal} - {managerName} - {' owes '} - {ownerName} + {iou.hasOutstandingIOU + ? `${managerName} owes ${ownerName}` + : `${ownerName} paid ${managerName}` + } From 959d3e7a9f5da184468dde2190aa99f16d317128 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 7 May 2021 15:24:10 +0100 Subject: [PATCH 051/141] remove divider from IOUModal --- src/pages/iou/IOUModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/IOUModal.js b/src/pages/iou/IOUModal.js index f3fe5f198afc..9da9ae2025ca 100644 --- a/src/pages/iou/IOUModal.js +++ b/src/pages/iou/IOUModal.js @@ -210,7 +210,7 @@ class IOUModal extends Component { const currentStep = this.steps[this.state.currentStepIndex]; return ( <> - + Date: Fri, 7 May 2021 15:24:33 +0100 Subject: [PATCH 052/141] IOUPreview formatting --- src/components/ReportActionItemIOUPreview.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItemIOUPreview.js b/src/components/ReportActionItemIOUPreview.js index 1824ee64cfe3..6d4bff6c4c10 100644 --- a/src/components/ReportActionItemIOUPreview.js +++ b/src/components/ReportActionItemIOUPreview.js @@ -25,6 +25,9 @@ const propTypes = { // Outstanding amount of this transaction cachedTotal: PropTypes.string, + + // Is the IOU report settled? + hasOutstandingIOU: PropTypes.bool, }).isRequired, // Session info for the currently logged in user. @@ -83,8 +86,7 @@ const ReportActionItemIOUPreview = ({ {iou.hasOutstandingIOU ? `${managerName} owes ${ownerName}` - : `${ownerName} paid ${managerName}` - } + : `${ownerName} paid ${managerName}`} From c33a2bd3591d16a1f71e03777522834356cb590c Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 7 May 2021 16:08:23 +0100 Subject: [PATCH 053/141] update the IOU report after settling it --- src/libs/actions/IOU.js | 5 ++--- src/libs/actions/Report.js | 5 +++-- src/pages/iou/IOUDetailsModal.js | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 43c62916d464..f16a87144f3f 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -2,9 +2,7 @@ import Onyx from 'react-native-onyx'; import _ from 'underscore'; import ONYXKEYS from '../../ONYXKEYS'; import * as API from '../API'; -import {getSimplifiedIOUReport, fetchChatReportsByIDs} from './Report'; -import Navigation from '../Navigation/Navigation'; -import ROUTES from '../../ROUTES'; +import {getSimplifiedIOUReport, fetchChatReportsByIDs, fetchIOUReportByID} from './Report'; /** * Retrieve the users preferred currency @@ -130,6 +128,7 @@ function settleIOUReport({ } }) .then(fetchChatReportsByIDs(chatReportID)) + .then(fetchIOUReportByID(reportID, chatReportID, true)) // As we can garuntee the settled report was previously active, we should update the chatReport data too. .catch(() => Onyx.merge(ONYXKEYS.IOU, {error: true})) .finally(() => Onyx.merge(ONYXKEYS.IOU, {loading: false})); } diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index becb1d9f8092..3cb760949f0e 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -519,9 +519,10 @@ function updateReportWithNewAction(reportID, reportAction) { /** * @param {Number} iouReportID - ID of the report we are fetching * @param {Number} chatReportID - associated chat report ID, this is required in order to link the reports in Onyx - * @param {Boolean} shouldUpdateChatReport - Only necessary if triggered by a notification, or the chatReport lists the IOU report as outstanding + * @param {Boolean} shouldUpdateChatReport - We should only update and link the chatReport if the IOU report is currently active! * - * Fetch a single IOU Report and persist to Onyx, associating the IOUReport with a chatReport if it is active. + * Fetch a single IOU Report and persist to Onyx, associating the IOUReport with a chatReport only if it is the active IOU report. + * Else we would break the link between the real active IOU for that chatReport (breaking IOUBadge and IOUPreview Components). */ function fetchIOUReportByID(iouReportID, chatReportID, shouldUpdateChatReport) { fetchIOUReport(iouReportID, chatReportID) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 09de97efaebb..d645020746e8 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -86,6 +86,7 @@ class IOUDetailsModal extends Component { } componentDidMount() { + // We should not update the chatReport data here, as there is no guarantte this is the active IOU fetchIOUReportByID(this.props.route.params.iouReportID, this.props.route.params.chatReportID, false); } From 297f9054f23c8d1c33942339f3da2049858d498f Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 7 May 2021 16:26:18 +0100 Subject: [PATCH 054/141] implement basic logic for preventing rejection of settled report transactions --- src/components/ReportTransaction.js | 21 +++++++++++++-------- src/pages/iou/IOUDetailsModal.js | 4 ++++ src/pages/iou/IOUTransactions.js | 4 ++++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/components/ReportTransaction.js b/src/components/ReportTransaction.js index d183796f5557..803ddf7ddb89 100644 --- a/src/components/ReportTransaction.js +++ b/src/components/ReportTransaction.js @@ -30,6 +30,9 @@ const propTypes = { // Was this transaction created by the current user createdByUser: PropTypes.bool, }).isRequired, + + // Can this transaction be rejected? + canReject: PropTypes.bool.isRequired, }; class ReportTransaction extends Component { @@ -58,14 +61,16 @@ class ReportTransaction extends Component { {this.props.action.message[0].text} - this.removeTransaction()} - > - - {this.props.transaction.createdByUser ? 'Cancel' : 'Decline'} - - + {this.props.canReject && ( + this.removeTransaction()} + > + + {this.props.transaction.createdByUser ? 'Cancel' : 'Decline'} + + + )}
); } diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index d645020746e8..2eb708160940 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -65,6 +65,9 @@ const propTypes = { // The transaction comment comment: PropTypes.string, })), + + // Is the IOU report settled? + hasOutstandingIOU: PropTypes.bool, }), // Session info for the currently logged in user. @@ -121,6 +124,7 @@ class IOUDetailsModal extends Component { chatReportID={this.props.route.params.chatReportID} iouReportID={this.props.route.params.iouReportID} transactions={this.props.iouReport.transactions} + hasOutstandingIOU={this.props.iouReport.hasOutstandingIOU} /> {(this.props.iouReport.hasOutstandingIOU && this.props.iouReport.managerEmail === sessionEmail && ( ); })} From 7796caea50b8d5c0086f7bc6b443a41d44b31a95 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 7 May 2021 18:28:27 +0100 Subject: [PATCH 055/141] simplify IOU Detail transactions --- src/components/ReportTransaction.js | 14 ++++++++++---- src/libs/actions/Report.js | 3 +-- src/pages/iou/IOUTransactions.js | 8 +++----- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/components/ReportTransaction.js b/src/components/ReportTransaction.js index 803ddf7ddb89..114e41d9d1a6 100644 --- a/src/components/ReportTransaction.js +++ b/src/components/ReportTransaction.js @@ -8,7 +8,10 @@ import ReportActionItemSingle from '../pages/home/report/ReportActionItemSingle' const propTypes = { // The chatReport which the transaction is associated with - chatReportID: PropTypes.number.isRequired, // todo: string is passed! + chatReportID: PropTypes.number.isRequired, + + // ID for the IOU report + iouReportID: PropTypes.number.isRequired, // The report action which we are displaying action: PropTypes.shape(ReportActionPropTypes).isRequired, @@ -25,7 +28,8 @@ const propTypes = { // The transaction amount amount: PropTypes.number, - transactionID: PropTypes.number, // todo: string is passed! + // The transaction ID + transactionID: PropTypes.number, // Was this transaction created by the current user createdByUser: PropTypes.bool, @@ -44,7 +48,8 @@ class ReportTransaction extends Component { removeTransaction() { rejectTransaction({ - reportID: this.props.chatReportID, + reportID: this.props.iouReportID, + chatReportID: this.props.chatReportID, transactionID: this.props.transaction.transactionID, comment: 'no comment', }); @@ -61,7 +66,8 @@ class ReportTransaction extends Component { {this.props.action.message[0].text} - {this.props.canReject && ( + {/* this.props.canReject && ( // TODO: reject transaction will be implemented by the followup issue */} + {false && ( this.removeTransaction()} diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 3cb760949f0e..95faef138369 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -187,7 +187,7 @@ function getSimplifiedIOUReport(reportData, chatReportID) { currency: transaction.currency, created: transaction.created, comment: transaction.comment, - })); + })).reverse(); return { reportID: reportData.reportID, @@ -360,7 +360,6 @@ function setLocalIOUReportData(iouReportObject, chatReportID, shouldUpdateChatRe // Persist IOU Report data to Onyx const iouReportKey = `${ONYXKEYS.COLLECTION.REPORT_IOUS}${iouReportObject.reportID}`; Onyx.merge(iouReportKey, iouReportObject); - console.debug('merge IOU: ', iouReportObject); // We don't always want to update the chatReport, as the IOU could be an old settled report if (!shouldUpdateChatReport) { diff --git a/src/pages/iou/IOUTransactions.js b/src/pages/iou/IOUTransactions.js index a39b60d3bb23..d9f013076924 100644 --- a/src/pages/iou/IOUTransactions.js +++ b/src/pages/iou/IOUTransactions.js @@ -38,21 +38,19 @@ const defaultProps = { class IOUTransactions extends PureComponent { render() { - const transactionsByCreationDate = this.props.transactions ? this.props.transactions.reverse() : []; return ( - {_.map(transactionsByCreationDate, (transaction) => { + {_.map(this.props.transactions, (transaction) => { const actionForTransaction = _.find(this.props.reportActions, (action) => { if (action && action.originalMessage) { - return action.originalMessage.IOUTransactionID == transaction.transactionID; - - // TODO: make sure type is equal + return action.originalMessage.IOUTransactionID === transaction.transactionID; } return false; }); return ( Date: Fri, 7 May 2021 18:29:42 +0100 Subject: [PATCH 056/141] ensure reportIDs props are passed as Number --- src/libs/actions/IOU.js | 9 +++------ src/pages/iou/IOUDetailsModal.js | 4 ++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index f16a87144f3f..3cc5f1406396 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -137,17 +137,14 @@ function settleIOUReport({ * Decline or cancel a transaction */ function rejectTransaction({ - reportID, transactionID, comment, + reportID, chatReportID, transactionID, comment, }) { - console.debug('juless: rejectTransaction', {reportID, transactionID, comment}); - API.RejectTransaction({ reportID, transactionID, comment, - }).then((data) => { - console.debug('juless: rejectedTransaction response: ', data); - }); + }) + .then(fetchIOUReportByID(reportID, chatReportID, true)); } export { diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 2eb708160940..45c66c20f142 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -121,8 +121,8 @@ class IOUDetailsModal extends Component { shouldHidePayButton /> From 005c34f88f162d4041c4d66406c786a5fad4476f Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 7 May 2021 18:31:09 +0100 Subject: [PATCH 057/141] clear IOU state on app restart --- src/Expensify.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Expensify.js b/src/Expensify.js index 1588c6c60f5c..9041fa68fb4c 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -26,7 +26,7 @@ Onyx.init({ [ONYXKEYS.SESSION]: {loading: false}, [ONYXKEYS.ACCOUNT]: CONST.DEFAULT_ACCOUNT_DATA, [ONYXKEYS.NETWORK]: {isOffline: false}, - [ONYXKEYS.IOU]: {loading: false}, + [ONYXKEYS.IOU]: {loading: false, error: false, creatingIOUTransaction: false}, }, registerStorageEventListener: (onStorageEvent) => { listenToStorageEvents(onStorageEvent); From 5efb7d45dbdd249982afdfd4b654b392d31bf92c Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 7 May 2021 18:48:29 +0100 Subject: [PATCH 058/141] formatting improvements and fixes --- src/ROUTES.js | 2 - src/components/ReportActionItemIOUAction.js | 9 +++-- src/components/ReportActionItemIOUQuote.js | 2 +- src/components/ReportTransaction.js | 8 +++- src/libs/actions/IOU.js | 9 +++-- src/libs/actions/Report.js | 42 +++++++++++---------- src/pages/iou/IOUDetailsModal.js | 3 +- src/pages/iou/IOUTransactions.js | 1 + 8 files changed, 45 insertions(+), 31 deletions(-) diff --git a/src/ROUTES.js b/src/ROUTES.js index 0d44726e9c6f..f7b5745087dc 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -21,10 +21,8 @@ export default { REPORT, REPORT_WITH_ID: 'r/:reportID', getReportRoute: reportID => `r/${reportID}`, - IOU_REQUEST: 'iou/request', IOU_REQUEST: 'iou/request/:reportID', getIouRequestRoute: reportID => `iou/request/${reportID}`, - IOU_BILL: 'iou/split', IOU_BILL: 'iou/split/:reportID', getIouSplitRoute: reportID => `iou/split/${reportID}`, IOU_DETAILS: 'iou/details', diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index acb6b2b4c48a..4924247e92c4 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -14,7 +14,8 @@ const propTypes = { // All the data of the action action: PropTypes.shape(ReportActionPropTypes).isRequired, - // The linked IOUReport + // The linked IOUReport, used for Onyx subscription + // eslint-disable-next-line react/no-unused-prop-types iouReportID: PropTypes.number.isRequired, // The associated chatReport @@ -62,11 +63,13 @@ class ReportActionItemIOUAction extends Component { } /** - * + * * Launch the IOU Details Modal, using data from the report action */ launchIOUDetailsModal() { - Navigation.navigate(ROUTES.getIouDetailsRoute(this.props.chatReportID, this.props.action.originalMessage.IOUReportID)); + Navigation.navigate(ROUTES.getIouDetailsRoute( + this.props.chatReportID, this.props.action.originalMessage.IOUReportID, + )); } render() { diff --git a/src/components/ReportActionItemIOUQuote.js b/src/components/ReportActionItemIOUQuote.js index 25f26824ac03..3d03a2dcdddc 100644 --- a/src/components/ReportActionItemIOUQuote.js +++ b/src/components/ReportActionItemIOUQuote.js @@ -19,7 +19,7 @@ const propTypes = { const defaultProps = { showViewDetailsLink: false, onViewDetailsPressed: null, -} +}; const ReportActionItemIOUQuote = ({action, showViewDetailsLink, onViewDetailsPressed}) => ( diff --git a/src/components/ReportTransaction.js b/src/components/ReportTransaction.js index 114e41d9d1a6..66ea6096a1fd 100644 --- a/src/components/ReportTransaction.js +++ b/src/components/ReportTransaction.js @@ -36,7 +36,12 @@ const propTypes = { }).isRequired, // Can this transaction be rejected? - canReject: PropTypes.bool.isRequired, + // eslint-disable-next-line react/no-unused-prop-types + canReject: PropTypes.bool, +}; + +const defaultProps = { + canReject: false, }; class ReportTransaction extends Component { @@ -83,5 +88,6 @@ class ReportTransaction extends Component { } ReportTransaction.displayName = 'ReportTransaction'; +ReportTransaction.defaultProps = defaultProps; ReportTransaction.propTypes = propTypes; export default ReportTransaction; diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 3cc5f1406396..657bf1898d8a 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -112,7 +112,8 @@ function createIOUSplit(params) { } /** - * Settles an IOU Report + * Settles an IOU Report. As we can guarantee that the settled report was previously active, + * we should update the chatReport data too when calling `fetchIOUReportByID`. */ function settleIOUReport({ chatReportID, reportID, paymentMethodType, @@ -123,12 +124,12 @@ function settleIOUReport({ paymentMethodType, }) .then((data) => { - if (data.jsonCode != 200) { + if (data.jsonCode !== 200) { console.error(data.message); } }) .then(fetchChatReportsByIDs(chatReportID)) - .then(fetchIOUReportByID(reportID, chatReportID, true)) // As we can garuntee the settled report was previously active, we should update the chatReport data too. + .then(fetchIOUReportByID(reportID, chatReportID, true)) .catch(() => Onyx.merge(ONYXKEYS.IOU, {error: true})) .finally(() => Onyx.merge(ONYXKEYS.IOU, {loading: false})); } @@ -144,7 +145,7 @@ function rejectTransaction({ transactionID, comment, }) - .then(fetchIOUReportByID(reportID, chatReportID, true)); + .then(fetchIOUReportByID(reportID, chatReportID, true)); } export { diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 95faef138369..a55e925e37ab 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -182,7 +182,7 @@ function getSimplifiedReportObject(report) { */ function getSimplifiedIOUReport(reportData, chatReportID) { const transactions = _.map(reportData.transactionList, transaction => ({ - transactionID: transaction.transactionID, + transactionID: Number(transaction.transactionID), amount: transaction.amount, currency: transaction.currency, created: transaction.created, @@ -228,6 +228,8 @@ function fetchIOUReport(iouReportID, chatReportID) { } const iouReportData = response.reports[iouReportID]; if (!iouReportData) { + // TODO: why is this being met after settling report!>?!?!? + // the report is probably settled, this is expected! console.error(`No iouReportData found for reportID ${iouReportID}`); return; } @@ -239,11 +241,13 @@ function fetchIOUReport(iouReportID, chatReportID) { /** * Given debtorEmail finds active IOU report ID via GetIOUReport API call + * .. todo: should deprecate!!!! * * @param {String} debtorEmail * @returns {Promise} */ function fetchIOUReportID(debtorEmail) { + // deprecate, use id in action return API.GetIOUReport({ debtorEmail, }).then((response) => { @@ -288,7 +292,7 @@ function fetchChatReportsByIDs(chatList) { reportAction => reportAction.action === CONST.REPORT.ACTIONS.TYPE.IOU); // If there aren't any IOU actions, we don't need to fetch any additional data - if (!containsIOUAction) { + if (!containsIOUAction) { // todo: all IOUActions should now contain IOUReportID return; } @@ -354,7 +358,7 @@ function fetchChatReportsByIDs(chatList) { * @param {Number} iouReportObject.total * @param {Number} iouReportObject.reportID * @param {Number} chatReportID - * @param {Boolean} shouldUpdateChatReport - should the local chatReport be updated too? + * @param {Boolean} shouldUpdateChatReport - should the local chatReport be updated too? */ function setLocalIOUReportData(iouReportObject, chatReportID, shouldUpdateChatReport) { // Persist IOU Report data to Onyx @@ -363,7 +367,6 @@ function setLocalIOUReportData(iouReportObject, chatReportID, shouldUpdateChatRe // We don't always want to update the chatReport, as the IOU could be an old settled report if (!shouldUpdateChatReport) { - console.log('No need to update the local chat report.'); return; } @@ -371,7 +374,7 @@ function setLocalIOUReportData(iouReportObject, chatReportID, shouldUpdateChatRe hasOutstandingIOU: iouReportObject.stateNum === 1 && iouReportObject.total !== 0, iouReportID: iouReportObject.reportID, }; - + if (!chatReportObject.hasOutstandingIOU) { chatReportObject.iouReportID = null; } @@ -416,6 +419,20 @@ function removeOptimisticActions(reportID) { }); } +/** + * @param {Number} iouReportID - ID of the report we are fetching + * @param {Number} chatReportID - associated chat report ID, required in order to link the reports in Onyx + * @param {Boolean} shouldUpdateChatReport - should we update and link the chat report to this IOU report? + * + * Fetch an IOU Report and persist to Onyx, associating the IOUReport with a chatReport only if it is the active IOU + * report. Else we would break the link to the active IOU for that chatReport (breaking badge and preview Components). + */ +function fetchIOUReportByID(iouReportID, chatReportID, shouldUpdateChatReport) { + fetchIOUReport(iouReportID, chatReportID) + .then(iouReportObject => setLocalIOUReportData(iouReportObject, chatReportID, shouldUpdateChatReport)) + .catch(error => console.error(`Error fetching IOU Report ${iouReportID}: ${error}`)); +} + /** * Updates a report in the store with a new report action * @@ -515,20 +532,6 @@ function updateReportWithNewAction(reportID, reportAction) { }); } -/** - * @param {Number} iouReportID - ID of the report we are fetching - * @param {Number} chatReportID - associated chat report ID, this is required in order to link the reports in Onyx - * @param {Boolean} shouldUpdateChatReport - We should only update and link the chatReport if the IOU report is currently active! - * - * Fetch a single IOU Report and persist to Onyx, associating the IOUReport with a chatReport only if it is the active IOU report. - * Else we would break the link between the real active IOU for that chatReport (breaking IOUBadge and IOUPreview Components). - */ -function fetchIOUReportByID(iouReportID, chatReportID, shouldUpdateChatReport) { - fetchIOUReport(iouReportID, chatReportID) - .then(iouReportObject => setLocalIOUReportData(iouReportObject, chatReportID, shouldUpdateChatReport)) - .catch((error) => console.error(`Error fetching IOU Report ${iouReportID}: ${error}`)); -} - /** * Updates a report in Onyx with a new pinned state. * @@ -1020,6 +1023,7 @@ export { fetchAllReports, fetchActions, fetchOrCreateChatReport, + fetchChatReportsByIDs, fetchIOUReportByID, addAction, updateLastReadActionID, diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 45c66c20f142..098272656f92 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -126,7 +126,8 @@ class IOUDetailsModal extends Component { transactions={this.props.iouReport.transactions} hasOutstandingIOU={this.props.iouReport.hasOutstandingIOU} /> - {(this.props.iouReport.hasOutstandingIOU && this.props.iouReport.managerEmail === sessionEmail && ( + {(this.props.iouReport.hasOutstandingIOU + && this.props.iouReport.managerEmail === sessionEmail && ( Date: Fri, 7 May 2021 18:49:48 +0100 Subject: [PATCH 059/141] remove comments --- src/libs/actions/Report.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index a55e925e37ab..77a6516f6880 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -228,8 +228,6 @@ function fetchIOUReport(iouReportID, chatReportID) { } const iouReportData = response.reports[iouReportID]; if (!iouReportData) { - // TODO: why is this being met after settling report!>?!?!? - // the report is probably settled, this is expected! console.error(`No iouReportData found for reportID ${iouReportID}`); return; } @@ -241,13 +239,11 @@ function fetchIOUReport(iouReportID, chatReportID) { /** * Given debtorEmail finds active IOU report ID via GetIOUReport API call - * .. todo: should deprecate!!!! * * @param {String} debtorEmail * @returns {Promise} */ function fetchIOUReportID(debtorEmail) { - // deprecate, use id in action return API.GetIOUReport({ debtorEmail, }).then((response) => { @@ -292,7 +288,7 @@ function fetchChatReportsByIDs(chatList) { reportAction => reportAction.action === CONST.REPORT.ACTIONS.TYPE.IOU); // If there aren't any IOU actions, we don't need to fetch any additional data - if (!containsIOUAction) { // todo: all IOUActions should now contain IOUReportID + if (!containsIOUAction) { return; } From ae72d4452c17ad3fb285081a80eff7e49f6a45fa Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 11:21:28 +0100 Subject: [PATCH 060/141] replace error with debug log --- src/libs/actions/Report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 77a6516f6880..3edf3bb21b35 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -228,7 +228,7 @@ function fetchIOUReport(iouReportID, chatReportID) { } const iouReportData = response.reports[iouReportID]; if (!iouReportData) { - console.error(`No iouReportData found for reportID ${iouReportID}`); + console.error(`No iouReportData found for reportID ${iouReportID}, report it most likely settled.`); return; } return getSimplifiedIOUReport(iouReportData, chatReportID); From 6f9cb9a0951b7f872430230f8926bbb6e303a536 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 11:39:20 +0100 Subject: [PATCH 061/141] active IOuReportID was passed, should instead be retrieved from the IOUACTION --- src/components/ReportActionItemIOUAction.js | 23 ++------------------ src/components/ReportActionItemIOUPreview.js | 21 ++++++++++++------ src/pages/home/report/ReportActionItem.js | 5 ----- src/pages/home/report/ReportActionsView.js | 7 +----- 4 files changed, 17 insertions(+), 39 deletions(-) diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index 4924247e92c4..5d7ee14c399f 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -14,10 +14,6 @@ const propTypes = { // All the data of the action action: PropTypes.shape(ReportActionPropTypes).isRequired, - // The linked IOUReport, used for Onyx subscription - // eslint-disable-next-line react/no-unused-prop-types - iouReportID: PropTypes.number.isRequired, - // The associated chatReport chatReportID: PropTypes.number.isRequired, @@ -25,18 +21,6 @@ const propTypes = { shouldDisplayPreviewComp: PropTypes.bool.isRequired, /* --- Onyx Props --- */ - // Active IOU Report for current report - iou: PropTypes.shape({ - // Email address of the manager in this iou report - managerEmail: PropTypes.string, - - // Email address of the creator of this iou report - ownerEmail: PropTypes.string, - - // Outstanding amount of this transaction - cachedTotal: PropTypes.string, - }), - // ChatReport associated with iouReport chatReport: PropTypes.shape({ // The participants of this report @@ -81,9 +65,9 @@ class ReportActionItemIOUAction extends Component { showViewDetailsLink={!hasMultipleParticipants} onViewDetailsPressed={this.launchIOUDetailsModal} /> - {this.props.shouldDisplayPreviewComp && !_.isEmpty(this.props.iou) && ( + {this.props.shouldDisplayPreviewComp && ( @@ -98,9 +82,6 @@ ReportActionItemIOUAction.defaultProps = defaultProps; ReportActionItemIOUAction.displayName = 'ReportActionItemIOUAction'; export default withOnyx({ - iou: { - key: ({iouReportID}) => `${ONYXKEYS.COLLECTION.REPORT_IOUS}${iouReportID}`, - }, chatReport: { key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, }, diff --git a/src/components/ReportActionItemIOUPreview.js b/src/components/ReportActionItemIOUPreview.js index 6d4bff6c4c10..34a7d1360110 100644 --- a/src/components/ReportActionItemIOUPreview.js +++ b/src/components/ReportActionItemIOUPreview.js @@ -15,6 +15,17 @@ const propTypes = { // Callback for the Pay/Settle button onPayButtonPressed: PropTypes.func, + // The active IOUReport, used for Onyx subscription + // eslint-disable-next-line react/no-unused-prop-types + iouReportID: PropTypes.number.isRequired, + + // Session info for the currently logged in user. + session: PropTypes.shape({ + // Currently logged in user email + email: PropTypes.string, + }).isRequired, + + /* --- Onyx Props --- */ // Active IOU Report for current report iou: PropTypes.shape({ // Email address of the manager in this iou report @@ -30,13 +41,6 @@ const propTypes = { hasOutstandingIOU: PropTypes.bool, }).isRequired, - // Session info for the currently logged in user. - session: PropTypes.shape({ - // Currently logged in user email - email: PropTypes.string, - }).isRequired, - - /* --- Onyx Props --- */ // All of the personal details for everyone personalDetails: PropTypes.objectOf(PropTypes.shape({ @@ -124,4 +128,7 @@ export default withOnyx({ personalDetails: { key: ONYXKEYS.PERSONAL_DETAILS, }, + iou: { + key: ({iouReportID}) => `${ONYXKEYS.COLLECTION.REPORT_IOUS}${iouReportID}`, + }, })(ReportActionItemIOUPreview); diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index e0557d57083a..4600a352ea89 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -21,9 +21,6 @@ const propTypes = { // The ID of the report this action is on. reportID: PropTypes.number.isRequired, - // IOU report ID associated with current report - iouReportID: PropTypes.number.isRequired, - // All the data of the action item action: PropTypes.shape(ReportActionPropTypes).isRequired, @@ -67,7 +64,6 @@ class ReportActionItem extends Component { || this.props.displayAsGroup !== nextProps.displayAsGroup || this.props.isMostRecentIOUReportAction !== nextProps.isMostRecentIOUReportAction || this.props.hasOutstandingIOU !== nextProps.hasOutstandingIOU - || this.props.iouReportID !== nextProps.iouReportID || (this.props.shouldDisplayNewIndicator !== nextProps.shouldDisplayNewIndicator) || !_.isEqual(this.props.action, nextProps.action); } @@ -106,7 +102,6 @@ class ReportActionItem extends Component { const children = this.props.action.actionName === 'IOU' ? ( 0 && item.action.sequenceNumber === this.initialNewMarkerPosition} isMostRecentIOUReportAction={item.action.sequenceNumber === this.mostRecentIOUReportSequenceNumber} - iouReportID={this.props.report.iouReportID} hasOutstandingIOU={this.props.report.hasOutstandingIOU} /> ); From d2a4c24e3910825e2339d1489461c7e7c37e5054 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 12:12:02 +0100 Subject: [PATCH 062/141] simplify isDMChat logic --- src/components/ReportActionItemIOUAction.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index 5d7ee14c399f..1ba2cfdcff12 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -57,12 +57,12 @@ class ReportActionItemIOUAction extends Component { } render() { - const hasMultipleParticipants = this.props.chatReport.participants.length > 1; + const isDMChat = this.props.chatReport.participants.length === 1; return ( {this.props.shouldDisplayPreviewComp && ( From 93dc2e5f1365ede155d714a5b1f2bbaa0d684d89 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 12:34:44 +0100 Subject: [PATCH 063/141] remove rejectTransaction logic for next PR --- src/components/ReportTransaction.js | 91 ++++++----------------------- src/libs/API.js | 7 --- src/libs/actions/IOU.js | 15 ----- src/pages/iou/IOUTransactions.js | 6 -- src/styles/utilities/flex.js | 4 -- 5 files changed, 18 insertions(+), 105 deletions(-) diff --git a/src/components/ReportTransaction.js b/src/components/ReportTransaction.js index 66ea6096a1fd..dd2665bf2fb0 100644 --- a/src/components/ReportTransaction.js +++ b/src/components/ReportTransaction.js @@ -1,93 +1,38 @@ -import React, {Component} from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; -import {View, Text, Pressable} from 'react-native-web'; +import {View, Text} from 'react-native-web'; import styles from '../styles/styles'; -import {rejectTransaction} from '../libs/actions/IOU'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; import ReportActionItemSingle from '../pages/home/report/ReportActionItemSingle'; const propTypes = { // The chatReport which the transaction is associated with + // eslint-disable-next-line react/no-unused-prop-types chatReportID: PropTypes.number.isRequired, // ID for the IOU report + // eslint-disable-next-line react/no-unused-prop-types iouReportID: PropTypes.number.isRequired, // The report action which we are displaying action: PropTypes.shape(ReportActionPropTypes).isRequired, - - // Transaction to display - transaction: PropTypes.shape({ - - // The transaction currency - currency: PropTypes.string, - - // The transaction comment - comment: PropTypes.string, - - // The transaction amount - amount: PropTypes.number, - - // The transaction ID - transactionID: PropTypes.number, - - // Was this transaction created by the current user - createdByUser: PropTypes.bool, - }).isRequired, - - // Can this transaction be rejected? - // eslint-disable-next-line react/no-unused-prop-types - canReject: PropTypes.bool, -}; - -const defaultProps = { - canReject: false, }; -class ReportTransaction extends Component { - constructor(props) { - super(props); - - this.removeTransaction = this.removeTransaction.bind(this); - } - - removeTransaction() { - rejectTransaction({ - reportID: this.props.iouReportID, - chatReportID: this.props.chatReportID, - transactionID: this.props.transaction.transactionID, - comment: 'no comment', - }); - } - - render() { - return ( - - - - {this.props.action.message[0].text} - - - {/* this.props.canReject && ( // TODO: reject transaction will be implemented by the followup issue */} - {false && ( - this.removeTransaction()} - > - - {this.props.transaction.createdByUser ? 'Cancel' : 'Decline'} - - - )} - - ); - } -} +const ReportTransaction = ({ + action, +}) => ( + + + + {action.message[0].text} + + + +); ReportTransaction.displayName = 'ReportTransaction'; -ReportTransaction.defaultProps = defaultProps; ReportTransaction.propTypes = propTypes; export default ReportTransaction; diff --git a/src/libs/API.js b/src/libs/API.js index ff21f10d2387..7f47d29844d4 100644 --- a/src/libs/API.js +++ b/src/libs/API.js @@ -515,12 +515,6 @@ function Push_Authenticate(parameters) { return Network.post(commandName, parameters); } -function RejectTransaction(parameters) { - const commandName = 'RejectTransaction'; - requireParameters(['reportID', 'transactionID'], parameters, commandName); - return Network.post(commandName, parameters); -} - /** * @param {Object} parameters * @param {String} parameters.reportComment @@ -725,7 +719,6 @@ export { PersonalDetails_GetForEmails, PersonalDetails_Update, Push_Authenticate, - RejectTransaction, Report_AddComment, Report_GetHistory, Report_TogglePinned, diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 657bf1898d8a..b5e3c6a7bf31 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -134,24 +134,9 @@ function settleIOUReport({ .finally(() => Onyx.merge(ONYXKEYS.IOU, {loading: false})); } -/** - * Decline or cancel a transaction - */ -function rejectTransaction({ - reportID, chatReportID, transactionID, comment, -}) { - API.RejectTransaction({ - reportID, - transactionID, - comment, - }) - .then(fetchIOUReportByID(reportID, chatReportID, true)); -} - export { getPreferredCurrency, createIOUTransaction, createIOUSplit, - rejectTransaction, settleIOUReport, }; diff --git a/src/pages/iou/IOUTransactions.js b/src/pages/iou/IOUTransactions.js index d16df5b76cb0..fb4ce9389810 100644 --- a/src/pages/iou/IOUTransactions.js +++ b/src/pages/iou/IOUTransactions.js @@ -26,15 +26,11 @@ const propTypes = { // The transaction comment comment: PropTypes.string, })), - - // Is the IOU report settled? - hasOutstandingIOU: PropTypes.bool, }; const defaultProps = { reportActions: [], transactions: [], - hasOutstandingIOU: false, }; class IOUTransactions extends PureComponent { @@ -52,9 +48,7 @@ class IOUTransactions extends PureComponent { ); })} diff --git a/src/styles/utilities/flex.js b/src/styles/utilities/flex.js index e51d5cd66ff8..d8d115bf5c38 100644 --- a/src/styles/utilities/flex.js +++ b/src/styles/utilities/flex.js @@ -52,10 +52,6 @@ export default { alignItems: 'flex-end', }, - alignItemsStart: { - alignItems: 'start', - }, - flexWrap: { flexWrap: 'wrap', }, From f523ba5c9008682ae2d2385b51115704d7d83c2a Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 12:43:50 +0100 Subject: [PATCH 064/141] fix IOU prop default --- src/components/ReportActionItemIOUAction.js | 2 -- src/components/ReportActionItemIOUPreview.js | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index 1ba2cfdcff12..d81ba4b19d9f 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -2,7 +2,6 @@ import React, {Component} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; import ONYXKEYS from '../ONYXKEYS'; import ReportActionItemIOUQuote from './ReportActionItemIOUQuote'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; @@ -35,7 +34,6 @@ const propTypes = { }; const defaultProps = { - iou: {}, chatReport: {}, }; diff --git a/src/components/ReportActionItemIOUPreview.js b/src/components/ReportActionItemIOUPreview.js index 34a7d1360110..f0efc9ec88ae 100644 --- a/src/components/ReportActionItemIOUPreview.js +++ b/src/components/ReportActionItemIOUPreview.js @@ -39,7 +39,7 @@ const propTypes = { // Is the IOU report settled? hasOutstandingIOU: PropTypes.bool, - }).isRequired, + }), // All of the personal details for everyone personalDetails: PropTypes.objectOf(PropTypes.shape({ @@ -50,6 +50,7 @@ const propTypes = { }; const defaultProps = { + iou: {}, shouldHidePayButton: false, onPayButtonPressed: null, }; From 5d2827276a56f82222bc097de6babae5da4f105d Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 15:12:36 +0100 Subject: [PATCH 065/141] allow IOU transactions to scroll, fix padding --- src/pages/iou/IOUDetailsModal.js | 43 ++++++++++++++++---------------- src/pages/iou/IOUTransactions.js | 3 ++- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 098272656f92..a6d3a69bed11 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -1,5 +1,5 @@ import React, {Component} from 'react'; -import {View, ActivityIndicator} from 'react-native'; +import {View, ActivityIndicator, ScrollView} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; @@ -111,28 +111,29 @@ class IOUDetailsModal extends Component { onCloseButtonPress={Navigation.dismissModal} /> {reportIsLoading ? : ( - - - + + + + + {(this.props.iouReport.hasOutstandingIOU && this.props.iouReport.managerEmail === sessionEmail && ( - + + + ))} )} diff --git a/src/pages/iou/IOUTransactions.js b/src/pages/iou/IOUTransactions.js index fb4ce9389810..49baa476df90 100644 --- a/src/pages/iou/IOUTransactions.js +++ b/src/pages/iou/IOUTransactions.js @@ -3,6 +3,7 @@ import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import PropTypes from 'prop-types'; +import styles from '../../styles/styles'; import ONYXKEYS from '../../ONYXKEYS'; import ReportActionPropTypes from '../home/report/ReportActionPropTypes'; import ReportTransaction from '../../components/ReportTransaction'; @@ -36,7 +37,7 @@ const defaultProps = { class IOUTransactions extends PureComponent { render() { return ( - + {_.map(this.props.transactions, (transaction) => { const actionForTransaction = _.find(this.props.reportActions, (action) => { if (action && action.originalMessage) { From 6b674adc8cdcdb0a6a735b223456c229ab2891f9 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 15:50:39 +0100 Subject: [PATCH 066/141] replace error with log --- src/libs/actions/Report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index d6e7057b2dae..896cbf15c7e4 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -242,7 +242,7 @@ function fetchIOUReport(iouReportID, chatReportID) { } const iouReportData = response.reports[iouReportID]; if (!iouReportData) { - console.error(`No iouReportData found for reportID ${iouReportID}, report it most likely settled.`); + console.debug(`No iouReportData found for reportID ${iouReportID}, report is most likely settled.`); return; } return getSimplifiedIOUReport(iouReportData, chatReportID); From 544246d77262de955c0e40489aeda1781dceffe8 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 16:01:22 +0100 Subject: [PATCH 067/141] proptype fixes --- src/pages/iou/IOUDetailsModal.js | 17 +++++------------ src/pages/iou/IOUTansactionPropTypes.js | 12 ++++++++++++ src/pages/iou/IOUTransactions.js | 15 ++++----------- 3 files changed, 21 insertions(+), 23 deletions(-) create mode 100644 src/pages/iou/IOUTansactionPropTypes.js diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index a6d3a69bed11..1ae8fa1c2958 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -13,6 +13,7 @@ import ScreenWrapper from '../../components/ScreenWrapper'; import {settleIOUReport} from '../../libs/actions/IOU'; import {fetchIOUReportByID} from '../../libs/actions/Report'; import ReportActionItemIOUPreview from '../../components/ReportActionItemIOUPreview'; +import IOUTansactionPropTypes from './IOUTansactionPropTypes'; import IOUTransactions from './IOUTransactions'; const matchType = PropTypes.shape({ @@ -26,8 +27,8 @@ const matchType = PropTypes.shape({ }); const defaultProps = { - iouReport: undefined, iou: {}, + iouReport: {}, }; const propTypes = { @@ -55,16 +56,8 @@ const propTypes = { // Owner is the person who is owed money ownerEmail: PropTypes.string, - transactions: PropTypes.arrayOf(PropTypes.shape({ - // The transaction currency - currency: PropTypes.string, - - // The transaction amount - total: PropTypes.number, - - // The transaction comment - comment: PropTypes.string, - })), + // The IOU transactions + transactions: PropTypes.arrayOf(PropTypes.shape(IOUTansactionPropTypes)), // Is the IOU report settled? hasOutstandingIOU: PropTypes.bool, @@ -112,7 +105,7 @@ class IOUDetailsModal extends Component { /> {reportIsLoading ? : ( - + Date: Mon, 10 May 2021 16:21:44 +0100 Subject: [PATCH 068/141] update podfile --- ios/ExpensifyCash.xcodeproj/project.pbxproj | 10 +- ios/Podfile | 13 --- ios/Podfile.lock | 100 +------------------- 3 files changed, 8 insertions(+), 115 deletions(-) diff --git a/ios/ExpensifyCash.xcodeproj/project.pbxproj b/ios/ExpensifyCash.xcodeproj/project.pbxproj index b5d3c915a5d0..0e92606294d2 100644 --- a/ios/ExpensifyCash.xcodeproj/project.pbxproj +++ b/ios/ExpensifyCash.xcodeproj/project.pbxproj @@ -19,16 +19,16 @@ 1E76D5242522316A005A268F /* GTAmericaExp-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */; }; 1E76D5252522316A005A268F /* GTAmericaExp-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */; }; 425866037F4C482AAB46CB8B /* GTAmericaExp-BdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */; }; + 52477A09739546F4814EA25F /* GTAmericaExpMono-Bd.otf in Resources */ = {isa = PBXBuildFile; fileRef = 0DE5D096095C41EE96746C9E /* GTAmericaExpMono-Bd.otf */; }; 6856B78873B64C44A92E51DB /* GTAmericaExp-MdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */; }; 70CF6E82262E297300711ADC /* BootSplash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 70CF6E81262E297300711ADC /* BootSplash.storyboard */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 8821A238A081483FA947BC4E /* GTAmericaExp-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */; }; 8C86654500DCC843A74147B5 /* libPods-ExpensifyCash-ExpensifyCashTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ED2AB27DDDFCCE3CD100EA0C /* libPods-ExpensifyCash-ExpensifyCashTests.a */; }; BB6CECBDA023256B6B955321 /* libPods-ExpensifyCash.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F2C8BDCC1FF0B64AE2DFC9B /* libPods-ExpensifyCash.a */; }; + DB77016704074197AB6633BB /* GTAmericaExpMono-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 5150E5D0D7F74DBA8D7C1914 /* GTAmericaExpMono-RgIt.otf */; }; E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; }; - 52477A09739546F4814EA25F /* GTAmericaExpMono-Bd.otf in Resources */ = {isa = PBXBuildFile; fileRef = 0DE5D096095C41EE96746C9E /* GTAmericaExpMono-Bd.otf */; }; ED814D34526B415CAFA0451E /* GTAmericaExpMono-BdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3981452A2C7340EBBA2B9BD1 /* GTAmericaExpMono-BdIt.otf */; }; - DB77016704074197AB6633BB /* GTAmericaExpMono-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 5150E5D0D7F74DBA8D7C1914 /* GTAmericaExpMono-RgIt.otf */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -46,6 +46,7 @@ 00E356EE1AD99517003FC87E /* ExpensifyCashTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExpensifyCashTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* ExpensifyCashTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExpensifyCashTests.m; sourceTree = ""; }; + 0DE5D096095C41EE96746C9E /* GTAmericaExpMono-Bd.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-Bd.otf"; path = "../assets/fonts/GTAmericaExpMono-Bd.otf"; sourceTree = ""; }; 0F5BE0CD252686320097D869 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* Expensify.cash.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Expensify.cash.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ExpensifyCash/AppDelegate.h; sourceTree = ""; }; @@ -53,6 +54,8 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ExpensifyCash/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ExpensifyCash/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ExpensifyCash/main.m; sourceTree = ""; }; + 3981452A2C7340EBBA2B9BD1 /* GTAmericaExpMono-BdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-BdIt.otf"; path = "../assets/fonts/GTAmericaExpMono-BdIt.otf"; sourceTree = ""; }; + 5150E5D0D7F74DBA8D7C1914 /* GTAmericaExpMono-RgIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-RgIt.otf"; path = "../assets/fonts/GTAmericaExpMono-RgIt.otf"; sourceTree = ""; }; 67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = ""; }; 6F2C8BDCC1FF0B64AE2DFC9B /* libPods-ExpensifyCash.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ExpensifyCash.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 70CF6E81262E297300711ADC /* BootSplash.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = BootSplash.storyboard; path = ExpensifyCash/BootSplash.storyboard; sourceTree = ""; }; @@ -73,9 +76,6 @@ ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; ED2AB27DDDFCCE3CD100EA0C /* libPods-ExpensifyCash-ExpensifyCashTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ExpensifyCash-ExpensifyCashTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; F4BC0E78FF1E9BD8B2D38C66 /* Pods-ExpensifyCash.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash.debug.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash.debug.xcconfig"; sourceTree = ""; }; - 0DE5D096095C41EE96746C9E /* GTAmericaExpMono-Bd.otf */ = {isa = PBXFileReference; name = "GTAmericaExpMono-Bd.otf"; path = "../assets/fonts/GTAmericaExpMono-Bd.otf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 3981452A2C7340EBBA2B9BD1 /* GTAmericaExpMono-BdIt.otf */ = {isa = PBXFileReference; name = "GTAmericaExpMono-BdIt.otf"; path = "../assets/fonts/GTAmericaExpMono-BdIt.otf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 5150E5D0D7F74DBA8D7C1914 /* GTAmericaExpMono-RgIt.otf */ = {isa = PBXFileReference; name = "GTAmericaExpMono-RgIt.otf"; path = "../assets/fonts/GTAmericaExpMono-RgIt.otf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ diff --git a/ios/Podfile b/ios/Podfile index b8f5255f0274..6d7141422f7f 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -12,17 +12,4 @@ target 'ExpensifyCash' do inherit! :complete # Pods for testing end - - # Enables Flipper. - # - # Note that if you have use_frameworks! enabled, Flipper will not work and - # you should disable these next few lines. - use_flipper! - post_install do |installer| - flipper_post_install(installer) - - installer.pods_project.build_configurations.each do |config| - config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" - end - end end diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b1af438affe9..547460f5877e 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -12,8 +12,6 @@ PODS: - Airship/MessageCenter (14.2.0): - Airship/Core - boost-for-react-native (1.63.0) - - CocoaAsyncSocket (7.6.5) - - CocoaLibEvent (1.0.0) - DoubleConversion (1.1.6) - FBLazyVector (0.63.3) - FBReactNativeSpec (0.63.3): @@ -62,52 +60,6 @@ PODS: - GoogleUtilities/Environment (~> 6.7) - GoogleUtilities/UserDefaults (~> 6.7) - PromisesObjC (~> 1.2) - - Flipper (0.54.0): - - Flipper-Folly (~> 2.2) - - Flipper-RSocket (~> 1.1) - - Flipper-DoubleConversion (1.1.7) - - Flipper-Folly (2.3.0): - - boost-for-react-native - - CocoaLibEvent (~> 1.0) - - Flipper-DoubleConversion - - Flipper-Glog - - OpenSSL-Universal (= 1.0.2.20) - - Flipper-Glog (0.3.6) - - Flipper-PeerTalk (0.0.4) - - Flipper-RSocket (1.1.0): - - Flipper-Folly (~> 2.2) - - FlipperKit (0.54.0): - - FlipperKit/Core (= 0.54.0) - - FlipperKit/Core (0.54.0): - - Flipper (~> 0.54.0) - - FlipperKit/CppBridge - - FlipperKit/FBCxxFollyDynamicConvert - - FlipperKit/FBDefines - - FlipperKit/FKPortForwarding - - FlipperKit/CppBridge (0.54.0): - - Flipper (~> 0.54.0) - - FlipperKit/FBCxxFollyDynamicConvert (0.54.0): - - Flipper-Folly (~> 2.2) - - FlipperKit/FBDefines (0.54.0) - - FlipperKit/FKPortForwarding (0.54.0): - - CocoaAsyncSocket (~> 7.6) - - Flipper-PeerTalk (~> 0.0.4) - - FlipperKit/FlipperKitHighlightOverlay (0.54.0) - - FlipperKit/FlipperKitLayoutPlugin (0.54.0): - - FlipperKit/Core - - FlipperKit/FlipperKitHighlightOverlay - - FlipperKit/FlipperKitLayoutTextSearchable - - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutTextSearchable (0.54.0) - - FlipperKit/FlipperKitNetworkPlugin (0.54.0): - - FlipperKit/Core - - FlipperKit/FlipperKitReactPlugin (0.54.0): - - FlipperKit/Core - - FlipperKit/FlipperKitUserDefaultsPlugin (0.54.0): - - FlipperKit/Core - - FlipperKit/SKIOSNetworkPlugin (0.54.0): - - FlipperKit/Core - - FlipperKit/FlipperKitNetworkPlugin - Folly (2020.01.13.00): - boost-for-react-native - DoubleConversion @@ -150,10 +102,7 @@ PODS: - nanopb/encode (= 1.30906.0) - nanopb/decode (1.30906.0) - nanopb/encode (1.30906.0) - - OpenSSL-Universal (1.0.2.20): - - OpenSSL-Universal/Static (= 1.0.2.20) - - OpenSSL-Universal/Static (1.0.2.20) - - PromisesObjC (1.2.11) + - PromisesObjC (1.2.12) - RCTRequired (0.63.3) - RCTTypeSafety (0.63.3): - FBLazyVector (= 0.63.3) @@ -433,32 +382,11 @@ PODS: - Airship (= 14.2.0) - React-Core - Yoga (1.14.0) - - YogaKit (1.18.1): - - Yoga (~> 1.14) DEPENDENCIES: - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`) - - Flipper (~> 0.54.0) - - Flipper-DoubleConversion (= 1.1.7) - - Flipper-Folly (~> 2.2) - - Flipper-Glog (= 0.3.6) - - Flipper-PeerTalk (~> 0.0.4) - - Flipper-RSocket (~> 1.1) - - FlipperKit (~> 0.54.0) - - FlipperKit/Core (~> 0.54.0) - - FlipperKit/CppBridge (~> 0.54.0) - - FlipperKit/FBCxxFollyDynamicConvert (~> 0.54.0) - - FlipperKit/FBDefines (~> 0.54.0) - - FlipperKit/FKPortForwarding (~> 0.54.0) - - FlipperKit/FlipperKitHighlightOverlay (~> 0.54.0) - - FlipperKit/FlipperKitLayoutPlugin (~> 0.54.0) - - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.54.0) - - FlipperKit/FlipperKitNetworkPlugin (~> 0.54.0) - - FlipperKit/FlipperKitReactPlugin (~> 0.54.0) - - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.54.0) - - FlipperKit/SKIOSNetworkPlugin (~> 0.54.0) - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) @@ -511,28 +439,17 @@ SPEC REPOS: trunk: - Airship - boost-for-react-native - - CocoaAsyncSocket - - CocoaLibEvent - Firebase - FirebaseAnalytics - FirebaseCore - FirebaseCoreDiagnostics - FirebaseCrashlytics - FirebaseInstallations - - Flipper - - Flipper-DoubleConversion - - Flipper-Folly - - Flipper-Glog - - Flipper-PeerTalk - - Flipper-RSocket - - FlipperKit - GoogleAppMeasurement - GoogleDataTransport - GoogleUtilities - nanopb - - OpenSSL-Universal - PromisesObjC - - YogaKit EXTERNAL SOURCES: DoubleConversion: @@ -635,8 +552,6 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Airship: 02ad73780f9eed21870e36b0aaab327acda6a102 boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c - CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 - CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f DoubleConversion: cde416483dac037923206447da6e1454df403714 FBLazyVector: 878b59e31113e289e275165efbe4b54fa614d43d FBReactNativeSpec: 7da9338acfb98d4ef9e5536805a0704572d33c2f @@ -646,21 +561,13 @@ SPEC CHECKSUMS: FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1 FirebaseCrashlytics: 1a747c9cc084a24dc6d9511c991db1cd078154eb FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2 - Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365 - Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41 - Flipper-Folly: e4493b013c02d9347d5e0cb4d128680239f6c78a - Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6 - Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 - Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7 - FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d Folly: b73c3869541e86821df3c387eb0af5f65addfab4 glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3 GoogleAppMeasurement: a6a3a066369828db64eda428cb2856dc1cdc7c4e GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc - OpenSSL-Universal: ff34003318d5e1163e9529b08470708e389ffcdd - PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f + PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 RCTRequired: 48884c74035a0b5b76dbb7a998bd93bcfc5f2047 RCTTypeSafety: edf4b618033c2f1c5b7bc3d90d8e085ed95ba2ab React: f36e90f3ceb976546e97df3403e37d226f79d0e3 @@ -704,8 +611,7 @@ SPEC CHECKSUMS: RNSVG: ce9d996113475209013317e48b05c21ee988d42e urbanairship-react-native: dfb6dc22b2f41ccaadd636b73d51b448cd1b2bbc Yoga: 7d13633d129fd179e01b8953d38d47be90db185a - YogaKit: f782866e155069a2cca2517aafea43200b01fd5a -PODFILE CHECKSUM: 41b806c7f131f87b716be1f1f9377532d6c9e43a +PODFILE CHECKSUM: d0a108dc40e53de929a9cbaaf377f87a7b9125fc COCOAPODS: 1.10.1 From 55501d671847245d6c013c10407545f7c124a19c Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 16:37:58 +0100 Subject: [PATCH 069/141] pass unique key to transaction Components --- src/pages/iou/IOUTransactions.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/iou/IOUTransactions.js b/src/pages/iou/IOUTransactions.js index b76bd20ac2d3..a52eb85a0cae 100644 --- a/src/pages/iou/IOUTransactions.js +++ b/src/pages/iou/IOUTransactions.js @@ -43,6 +43,7 @@ class IOUTransactions extends PureComponent { chatReportID={this.props.chatReportID} iouReportID={this.props.iouReportID} action={actionForTransaction} + key={actionForTransaction.originalMessage.IOUTransactionID} /> ); })} From 5a5a69f6ce28e9462afac82e28fc123919877822 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 16:38:23 +0100 Subject: [PATCH 070/141] fix issue where reportID was passed outside of array --- src/libs/actions/IOU.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index b5e3c6a7bf31..fe080093e8c0 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -128,7 +128,7 @@ function settleIOUReport({ console.error(data.message); } }) - .then(fetchChatReportsByIDs(chatReportID)) + .then(fetchChatReportsByIDs([chatReportID])) .then(fetchIOUReportByID(reportID, chatReportID, true)) .catch(() => Onyx.merge(ONYXKEYS.IOU, {error: true})) .finally(() => Onyx.merge(ONYXKEYS.IOU, {loading: false})); From 900ff6c5c84b0973a75867e7eb06aac1e76c274d Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 16:39:37 +0100 Subject: [PATCH 071/141] remove useless comment --- src/pages/iou/IOUTransactions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/IOUTransactions.js b/src/pages/iou/IOUTransactions.js index a52eb85a0cae..2a54e5163f83 100644 --- a/src/pages/iou/IOUTransactions.js +++ b/src/pages/iou/IOUTransactions.js @@ -10,7 +10,7 @@ import IOUTansactionPropTypes from './IOUTansactionPropTypes'; import ReportTransaction from '../../components/ReportTransaction'; const propTypes = { - reportActions: PropTypes.arrayOf(PropTypes.shape(ReportActionPropTypes)), // should this be array/object? + reportActions: PropTypes.shape(ReportActionPropTypes), // ReportID for the associated chat report chatReportID: PropTypes.number.isRequired, From ec0e19c889f4abefc6774c553ea9453c0705f91a Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 16:43:28 +0100 Subject: [PATCH 072/141] ensure a null IOU report will not be rendered --- src/pages/iou/IOUDetailsModal.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 1ae8fa1c2958..d6e336593c2d 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -28,7 +28,7 @@ const matchType = PropTypes.shape({ const defaultProps = { iou: {}, - iouReport: {}, + iouReport: null, }; const propTypes = { @@ -96,7 +96,7 @@ class IOUDetailsModal extends Component { render() { const sessionEmail = lodashGet(this.props.session, 'email', null); - const reportIsLoading = this.props.iouReport === undefined; + const reportIsLoading = this.props.iouReport === null; return ( Date: Mon, 10 May 2021 16:44:33 +0100 Subject: [PATCH 073/141] remove useless style --- src/components/ReportTransaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportTransaction.js b/src/components/ReportTransaction.js index dd2665bf2fb0..851e6f9d51f2 100644 --- a/src/components/ReportTransaction.js +++ b/src/components/ReportTransaction.js @@ -21,7 +21,7 @@ const propTypes = { const ReportTransaction = ({ action, }) => ( - + Date: Mon, 10 May 2021 18:05:31 +0100 Subject: [PATCH 074/141] re-enables flipper, I'll leave a note in the PR instead --- ios/Podfile | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ios/Podfile b/ios/Podfile index 6d7141422f7f..622ae6fc9433 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -12,4 +12,16 @@ target 'ExpensifyCash' do inherit! :complete # Pods for testing end + # Enables Flipper. + # + # Note that if you have use_frameworks! enabled, Flipper will not work and + # you should disable these next few lines. + use_flipper! + post_install do |installer| + flipper_post_install(installer) + + installer.pods_project.build_configurations.each do |config| + config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" + end + end end From 4e92ed04f24f08edd03428a45942a9dcd850fc07 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 18:05:44 +0100 Subject: [PATCH 075/141] remove useless console log --- src/libs/actions/Report.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 896cbf15c7e4..2ae62b46f1f8 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -242,7 +242,6 @@ function fetchIOUReport(iouReportID, chatReportID) { } const iouReportData = response.reports[iouReportID]; if (!iouReportData) { - console.debug(`No iouReportData found for reportID ${iouReportID}, report is most likely settled.`); return; } return getSimplifiedIOUReport(iouReportData, chatReportID); From fdec47e8672b98240a1834bcf24726dc375af489 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 18:05:55 +0100 Subject: [PATCH 076/141] fix bug where incorrect import is used --- src/components/ReportTransaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportTransaction.js b/src/components/ReportTransaction.js index 851e6f9d51f2..008d375a3a79 100644 --- a/src/components/ReportTransaction.js +++ b/src/components/ReportTransaction.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {View, Text} from 'react-native-web'; +import {View, Text} from 'react-native'; import styles from '../styles/styles'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; import ReportActionItemSingle from '../pages/home/report/ReportActionItemSingle'; From e8e9f4e42b236cd74cb5254ca117cfc6e71f527a Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 18:06:31 +0100 Subject: [PATCH 077/141] change iouReportID default --- src/components/ReportActionItemIOUPreview.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItemIOUPreview.js b/src/components/ReportActionItemIOUPreview.js index f0efc9ec88ae..149315079aaa 100644 --- a/src/components/ReportActionItemIOUPreview.js +++ b/src/components/ReportActionItemIOUPreview.js @@ -17,7 +17,7 @@ const propTypes = { // The active IOUReport, used for Onyx subscription // eslint-disable-next-line react/no-unused-prop-types - iouReportID: PropTypes.number.isRequired, + iouReportID: PropTypes.number, // Session info for the currently logged in user. session: PropTypes.shape({ @@ -51,6 +51,7 @@ const propTypes = { const defaultProps = { iou: {}, + iouReportID: undefined, shouldHidePayButton: false, onPayButtonPressed: null, }; From d5057de108708dd09db126fa96f3eb5c0c57d4db Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 18:07:10 +0100 Subject: [PATCH 078/141] format podfile, to avoid any change --- ios/Podfile | 1 + 1 file changed, 1 insertion(+) diff --git a/ios/Podfile b/ios/Podfile index 622ae6fc9433..b8f5255f0274 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -12,6 +12,7 @@ target 'ExpensifyCash' do inherit! :complete # Pods for testing end + # Enables Flipper. # # Note that if you have use_frameworks! enabled, Flipper will not work and From 643e76822f8cddc065b22ed6931e255884cb48ff Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 18:15:05 +0100 Subject: [PATCH 079/141] reverse podfile commit --- ios/ExpensifyCash.xcodeproj/project.pbxproj | 170 ++++++++++++-------- ios/Podfile.lock | 101 +++++++++++- 2 files changed, 204 insertions(+), 67 deletions(-) diff --git a/ios/ExpensifyCash.xcodeproj/project.pbxproj b/ios/ExpensifyCash.xcodeproj/project.pbxproj index 0e92606294d2..699e69b48758 100644 --- a/ios/ExpensifyCash.xcodeproj/project.pbxproj +++ b/ios/ExpensifyCash.xcodeproj/project.pbxproj @@ -18,14 +18,14 @@ 1E76D5232522316A005A268F /* GTAmericaExp-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */; }; 1E76D5242522316A005A268F /* GTAmericaExp-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */; }; 1E76D5252522316A005A268F /* GTAmericaExp-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */; }; + 2F85A9164BEF50A30645E9A2 /* libPods-ExpensifyCash-ExpensifyCashTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27A358A9ACB556CEF9524883 /* libPods-ExpensifyCash-ExpensifyCashTests.a */; }; 425866037F4C482AAB46CB8B /* GTAmericaExp-BdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */; }; + 5216989E3468C96D498B1014 /* libPods-ExpensifyCash.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8573A353D45E3505A720E5E8 /* libPods-ExpensifyCash.a */; }; 52477A09739546F4814EA25F /* GTAmericaExpMono-Bd.otf in Resources */ = {isa = PBXBuildFile; fileRef = 0DE5D096095C41EE96746C9E /* GTAmericaExpMono-Bd.otf */; }; 6856B78873B64C44A92E51DB /* GTAmericaExp-MdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */; }; 70CF6E82262E297300711ADC /* BootSplash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 70CF6E81262E297300711ADC /* BootSplash.storyboard */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 8821A238A081483FA947BC4E /* GTAmericaExp-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */; }; - 8C86654500DCC843A74147B5 /* libPods-ExpensifyCash-ExpensifyCashTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ED2AB27DDDFCCE3CD100EA0C /* libPods-ExpensifyCash-ExpensifyCashTests.a */; }; - BB6CECBDA023256B6B955321 /* libPods-ExpensifyCash.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F2C8BDCC1FF0B64AE2DFC9B /* libPods-ExpensifyCash.a */; }; DB77016704074197AB6633BB /* GTAmericaExpMono-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 5150E5D0D7F74DBA8D7C1914 /* GTAmericaExpMono-RgIt.otf */; }; E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; }; ED814D34526B415CAFA0451E /* GTAmericaExpMono-BdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3981452A2C7340EBBA2B9BD1 /* GTAmericaExpMono-BdIt.otf */; }; @@ -54,28 +54,28 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ExpensifyCash/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ExpensifyCash/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ExpensifyCash/main.m; sourceTree = ""; }; + 27A358A9ACB556CEF9524883 /* libPods-ExpensifyCash-ExpensifyCashTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ExpensifyCash-ExpensifyCashTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3981452A2C7340EBBA2B9BD1 /* GTAmericaExpMono-BdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-BdIt.otf"; path = "../assets/fonts/GTAmericaExpMono-BdIt.otf"; sourceTree = ""; }; 5150E5D0D7F74DBA8D7C1914 /* GTAmericaExpMono-RgIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-RgIt.otf"; path = "../assets/fonts/GTAmericaExpMono-RgIt.otf"; sourceTree = ""; }; 67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = ""; }; - 6F2C8BDCC1FF0B64AE2DFC9B /* libPods-ExpensifyCash.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ExpensifyCash.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 70CF6E81262E297300711ADC /* BootSplash.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = BootSplash.storyboard; path = ExpensifyCash/BootSplash.storyboard; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ExpensifyCash/LaunchScreen.storyboard; sourceTree = ""; }; 8437A5A38F2047E0BCCD7C2F /* GTAmericaExpMono-Rg.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-Rg.otf"; path = "../assets/fonts/GTAmericaExpMono-Rg.otf"; sourceTree = ""; }; + 8573A353D45E3505A720E5E8 /* libPods-ExpensifyCash.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ExpensifyCash.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Regular.otf"; path = "../assets/fonts/GTAmericaExp-Regular.otf"; sourceTree = ""; }; 918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-RgIt.otf"; path = "../assets/fonts/GTAmericaExp-RgIt.otf"; sourceTree = ""; }; - A13EE2CFAF952F935D201D2F /* Pods-ExpensifyCash.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash.release.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash.release.xcconfig"; sourceTree = ""; }; A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Thin.otf"; path = "../assets/fonts/GTAmericaExp-Thin.otf"; sourceTree = ""; }; A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Bold.otf"; path = "../assets/fonts/GTAmericaExp-Bold.otf"; sourceTree = ""; }; A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-BdIt.otf"; path = "../assets/fonts/GTAmericaExp-BdIt.otf"; sourceTree = ""; }; AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Medium.otf"; path = "../assets/fonts/GTAmericaExp-Medium.otf"; sourceTree = ""; }; - AF728B3FCBC8C2731C4DA7B4 /* Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig"; sourceTree = ""; }; + C2E22C1F168EC5E8ADE1C6D4 /* Pods-ExpensifyCash.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash.debug.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash.debug.xcconfig"; sourceTree = ""; }; + C61F74A950D4102529F6C079 /* Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig"; sourceTree = ""; }; DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-MdIt.otf"; path = "../assets/fonts/GTAmericaExp-MdIt.otf"; sourceTree = ""; }; - E7967C67752432EA2031954F /* Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig"; sourceTree = ""; }; + E5FF58CC2F62D48FB7F83D5C /* Pods-ExpensifyCash.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash.release.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash.release.xcconfig"; sourceTree = ""; }; E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; - ED2AB27DDDFCCE3CD100EA0C /* libPods-ExpensifyCash-ExpensifyCashTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ExpensifyCash-ExpensifyCashTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - F4BC0E78FF1E9BD8B2D38C66 /* Pods-ExpensifyCash.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash.debug.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash.debug.xcconfig"; sourceTree = ""; }; + FD0A867BA09B4414553E906B /* Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -83,7 +83,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 8C86654500DCC843A74147B5 /* libPods-ExpensifyCash-ExpensifyCashTests.a in Frameworks */, + 2F85A9164BEF50A30645E9A2 /* libPods-ExpensifyCash-ExpensifyCashTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -91,7 +91,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BB6CECBDA023256B6B955321 /* libPods-ExpensifyCash.a in Frameworks */, + 5216989E3468C96D498B1014 /* libPods-ExpensifyCash.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -137,8 +137,8 @@ children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED2971642150620600B7C4FE /* JavaScriptCore.framework */, - 6F2C8BDCC1FF0B64AE2DFC9B /* libPods-ExpensifyCash.a */, - ED2AB27DDDFCCE3CD100EA0C /* libPods-ExpensifyCash-ExpensifyCashTests.a */, + 8573A353D45E3505A720E5E8 /* libPods-ExpensifyCash.a */, + 27A358A9ACB556CEF9524883 /* libPods-ExpensifyCash-ExpensifyCashTests.a */, ); name = Frameworks; sourceTree = ""; @@ -197,10 +197,10 @@ EC29677F0A49C2946A495A33 /* Pods */ = { isa = PBXGroup; children = ( - F4BC0E78FF1E9BD8B2D38C66 /* Pods-ExpensifyCash.debug.xcconfig */, - A13EE2CFAF952F935D201D2F /* Pods-ExpensifyCash.release.xcconfig */, - AF728B3FCBC8C2731C4DA7B4 /* Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig */, - E7967C67752432EA2031954F /* Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig */, + C2E22C1F168EC5E8ADE1C6D4 /* Pods-ExpensifyCash.debug.xcconfig */, + E5FF58CC2F62D48FB7F83D5C /* Pods-ExpensifyCash.release.xcconfig */, + C61F74A950D4102529F6C079 /* Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig */, + FD0A867BA09B4414553E906B /* Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -212,11 +212,12 @@ isa = PBXNativeTarget; buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ExpensifyCashTests" */; buildPhases = ( - AEFD4743761AD0E2373D0494 /* [CP] Check Pods Manifest.lock */, + 7781CC04809D1929FDB2E270 /* [CP] Check Pods Manifest.lock */, 00E356EA1AD99517003FC87E /* Sources */, 00E356EB1AD99517003FC87E /* Frameworks */, 00E356EC1AD99517003FC87E /* Resources */, - F2B8013B9E9ECAB2B509E702 /* [CP] Copy Pods Resources */, + F408EFDBF122C3697C07606B /* [CP] Embed Pods Frameworks */, + 33A58FF39EE65B2A02886129 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -232,15 +233,16 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ExpensifyCash" */; buildPhases = ( - 6A9F4436BADC311F14A4349F /* [CP] Check Pods Manifest.lock */, + 00FE8D3EC623CA84C643DB2F /* [CP] Check Pods Manifest.lock */, FD10A7F022414F080027D42C /* Start Packager */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - C9071365B664DE0D85DC5195 /* [CP] Copy Pods Resources */, - CF6259710B5A341870372EA2 /* [CP-User] [RNFB] Core Configuration */, - ED5B8E90A3384FC6A128FB95 /* [CP-User] [RNFB] Crashlytics Configuration */, + A189F039C75BB41E6A26A120 /* [CP] Embed Pods Frameworks */, + 66E8A3C39CA306DBE7CA927B /* [CP] Copy Pods Resources */, + 60932B5F0B132E279E05A998 /* [CP-User] [RNFB] Core Configuration */, + FE17A0EBF379D2384E521938 /* [CP-User] [RNFB] Crashlytics Configuration */, ); buildRules = ( ); @@ -339,7 +341,7 @@ shellPath = /bin/sh; shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; }; - 6A9F4436BADC311F14A4349F /* [CP] Check Pods Manifest.lock */ = { + 00FE8D3EC623CA84C643DB2F /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -361,35 +363,13 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - AEFD4743761AD0E2373D0494 /* [CP] Check Pods Manifest.lock */ = { + 33A58FF39EE65B2A02886129 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-ExpensifyCash-ExpensifyCashTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - C9071365B664DE0D85DC5195 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash-resources.sh", + "${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests-resources.sh", "${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist", "${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib", "${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib", @@ -508,10 +488,10 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; - CF6259710B5A341870372EA2 /* [CP-User] [RNFB] Core Configuration */ = { + 60932B5F0B132E279E05A998 /* [CP-User] [RNFB] Core Configuration */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -521,23 +501,13 @@ shellPath = /bin/sh; shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n _JSON_OUTPUT_BASE64=$(python -c 'import json,sys,base64;print(base64.b64encode(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"').read())['${_JSON_ROOT}'])))' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\n\n # config.admob_delay_app_measurement_init\n _ADMOB_DELAY_APP_MEASUREMENT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"admob_delay_app_measurement_init\")\n if [[ $_ADMOB_DELAY_APP_MEASUREMENT == \"true\" ]]; then\n _PLIST_ENTRY_KEYS+=(\"GADDelayAppMeasurementInit\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"YES\")\n fi\n\n # config.admob_ios_app_id\n _ADMOB_IOS_APP_ID=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"admob_ios_app_id\")\n if [[ $_ADMOB_IOS_APP_ID ]]; then\n _PLIST_ENTRY_KEYS+=(\"GADApplicationIdentifier\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_ADMOB_IOS_APP_ID\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; }; - ED5B8E90A3384FC6A128FB95 /* [CP-User] [RNFB] Crashlytics Configuration */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - name = "[CP-User] [RNFB] Crashlytics Configuration"; - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\nif [[ ${PODS_ROOT} ]]; then\n echo \"info: Exec FirebaseCrashlytics Run from Pods\"\n \"${PODS_ROOT}/FirebaseCrashlytics/run\"\nelse\n echo \"info: Exec FirebaseCrashlytics Run from framework\"\n \"${PROJECT_DIR}/FirebaseCrashlytics.framework/run\"\nfi\n"; - }; - F2B8013B9E9ECAB2B509E702 /* [CP] Copy Pods Resources */ = { + 66E8A3C39CA306DBE7CA927B /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests-resources.sh", + "${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash-resources.sh", "${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist", "${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib", "${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib", @@ -656,7 +626,65 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 7781CC04809D1929FDB2E270 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-ExpensifyCash-ExpensifyCashTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + A189F039C75BB41E6A26A120 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash-frameworks.sh", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL/OpenSSL.framework/OpenSSL", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + F408EFDBF122C3697C07606B /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests-frameworks.sh", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL/OpenSSL.framework/OpenSSL", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; FD10A7F022414F080027D42C /* Start Packager */ = { @@ -678,6 +706,16 @@ shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n"; showEnvVarsInLog = 0; }; + FE17A0EBF379D2384E521938 /* [CP-User] [RNFB] Crashlytics Configuration */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + name = "[CP-User] [RNFB] Crashlytics Configuration"; + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\nif [[ ${PODS_ROOT} ]]; then\n echo \"info: Exec FirebaseCrashlytics Run from Pods\"\n \"${PODS_ROOT}/FirebaseCrashlytics/run\"\nelse\n echo \"info: Exec FirebaseCrashlytics Run from framework\"\n \"${PROJECT_DIR}/FirebaseCrashlytics.framework/run\"\nfi\n"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -711,7 +749,7 @@ /* Begin XCBuildConfiguration section */ 00E356F61AD99517003FC87E /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = AF728B3FCBC8C2731C4DA7B4 /* Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig */; + baseConfigurationReference = C61F74A950D4102529F6C079 /* Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -735,7 +773,7 @@ }; 00E356F71AD99517003FC87E /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E7967C67752432EA2031954F /* Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig */; + baseConfigurationReference = FD0A867BA09B4414553E906B /* Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -757,7 +795,7 @@ }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F4BC0E78FF1E9BD8B2D38C66 /* Pods-ExpensifyCash.debug.xcconfig */; + baseConfigurationReference = C2E22C1F168EC5E8ADE1C6D4 /* Pods-ExpensifyCash.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -789,7 +827,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A13EE2CFAF952F935D201D2F /* Pods-ExpensifyCash.release.xcconfig */; + baseConfigurationReference = E5FF58CC2F62D48FB7F83D5C /* Pods-ExpensifyCash.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 547460f5877e..fd481f36f362 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -12,6 +12,7 @@ PODS: - Airship/MessageCenter (14.2.0): - Airship/Core - boost-for-react-native (1.63.0) + - CocoaAsyncSocket (7.6.5) - DoubleConversion (1.1.6) - FBLazyVector (0.63.3) - FBReactNativeSpec (0.63.3): @@ -60,6 +61,55 @@ PODS: - GoogleUtilities/Environment (~> 6.7) - GoogleUtilities/UserDefaults (~> 6.7) - PromisesObjC (~> 1.2) + - Flipper (0.54.0): + - Flipper-Folly (~> 2.2) + - Flipper-RSocket (~> 1.1) + - Flipper-Boost-iOSX (1.76.0.1.10) + - Flipper-DoubleConversion (1.1.7) + - Flipper-Fmt (7.1.7) + - Flipper-Folly (2.6.7): + - Flipper-Boost-iOSX + - Flipper-DoubleConversion + - Flipper-Fmt (= 7.1.7) + - Flipper-Glog + - libevent (~> 2.1.12) + - OpenSSL-Universal (= 1.1.180) + - Flipper-Glog (0.3.6) + - Flipper-PeerTalk (0.0.4) + - Flipper-RSocket (1.4.3): + - Flipper-Folly (~> 2.6) + - FlipperKit (0.54.0): + - FlipperKit/Core (= 0.54.0) + - FlipperKit/Core (0.54.0): + - Flipper (~> 0.54.0) + - FlipperKit/CppBridge + - FlipperKit/FBCxxFollyDynamicConvert + - FlipperKit/FBDefines + - FlipperKit/FKPortForwarding + - FlipperKit/CppBridge (0.54.0): + - Flipper (~> 0.54.0) + - FlipperKit/FBCxxFollyDynamicConvert (0.54.0): + - Flipper-Folly (~> 2.2) + - FlipperKit/FBDefines (0.54.0) + - FlipperKit/FKPortForwarding (0.54.0): + - CocoaAsyncSocket (~> 7.6) + - Flipper-PeerTalk (~> 0.0.4) + - FlipperKit/FlipperKitHighlightOverlay (0.54.0) + - FlipperKit/FlipperKitLayoutPlugin (0.54.0): + - FlipperKit/Core + - FlipperKit/FlipperKitHighlightOverlay + - FlipperKit/FlipperKitLayoutTextSearchable + - YogaKit (~> 1.18) + - FlipperKit/FlipperKitLayoutTextSearchable (0.54.0) + - FlipperKit/FlipperKitNetworkPlugin (0.54.0): + - FlipperKit/Core + - FlipperKit/FlipperKitReactPlugin (0.54.0): + - FlipperKit/Core + - FlipperKit/FlipperKitUserDefaultsPlugin (0.54.0): + - FlipperKit/Core + - FlipperKit/SKIOSNetworkPlugin (0.54.0): + - FlipperKit/Core + - FlipperKit/FlipperKitNetworkPlugin - Folly (2020.01.13.00): - boost-for-react-native - DoubleConversion @@ -97,11 +147,13 @@ PODS: - GoogleUtilities/Logger - GoogleUtilities/UserDefaults (6.7.2): - GoogleUtilities/Logger + - libevent (2.1.12) - nanopb (1.30906.0): - nanopb/decode (= 1.30906.0) - nanopb/encode (= 1.30906.0) - nanopb/decode (1.30906.0) - nanopb/encode (1.30906.0) + - OpenSSL-Universal (1.1.180) - PromisesObjC (1.2.12) - RCTRequired (0.63.3) - RCTTypeSafety (0.63.3): @@ -382,11 +434,32 @@ PODS: - Airship (= 14.2.0) - React-Core - Yoga (1.14.0) + - YogaKit (1.18.1): + - Yoga (~> 1.14) DEPENDENCIES: - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`) + - Flipper (~> 0.54.0) + - Flipper-DoubleConversion (= 1.1.7) + - Flipper-Folly (~> 2.2) + - Flipper-Glog (= 0.3.6) + - Flipper-PeerTalk (~> 0.0.4) + - Flipper-RSocket (~> 1.1) + - FlipperKit (~> 0.54.0) + - FlipperKit/Core (~> 0.54.0) + - FlipperKit/CppBridge (~> 0.54.0) + - FlipperKit/FBCxxFollyDynamicConvert (~> 0.54.0) + - FlipperKit/FBDefines (~> 0.54.0) + - FlipperKit/FKPortForwarding (~> 0.54.0) + - FlipperKit/FlipperKitHighlightOverlay (~> 0.54.0) + - FlipperKit/FlipperKitLayoutPlugin (~> 0.54.0) + - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.54.0) + - FlipperKit/FlipperKitNetworkPlugin (~> 0.54.0) + - FlipperKit/FlipperKitReactPlugin (~> 0.54.0) + - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.54.0) + - FlipperKit/SKIOSNetworkPlugin (~> 0.54.0) - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) @@ -439,17 +512,30 @@ SPEC REPOS: trunk: - Airship - boost-for-react-native + - CocoaAsyncSocket - Firebase - FirebaseAnalytics - FirebaseCore - FirebaseCoreDiagnostics - FirebaseCrashlytics - FirebaseInstallations + - Flipper + - Flipper-Boost-iOSX + - Flipper-DoubleConversion + - Flipper-Fmt + - Flipper-Folly + - Flipper-Glog + - Flipper-PeerTalk + - Flipper-RSocket + - FlipperKit - GoogleAppMeasurement - GoogleDataTransport - GoogleUtilities + - libevent - nanopb + - OpenSSL-Universal - PromisesObjC + - YogaKit EXTERNAL SOURCES: DoubleConversion: @@ -552,6 +638,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Airship: 02ad73780f9eed21870e36b0aaab327acda6a102 boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c + CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: cde416483dac037923206447da6e1454df403714 FBLazyVector: 878b59e31113e289e275165efbe4b54fa614d43d FBReactNativeSpec: 7da9338acfb98d4ef9e5536805a0704572d33c2f @@ -561,12 +648,23 @@ SPEC CHECKSUMS: FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1 FirebaseCrashlytics: 1a747c9cc084a24dc6d9511c991db1cd078154eb FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2 + Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365 + Flipper-Boost-iOSX: df3765e7e26b44bc3e68a250ef1fa0afa15963bc + Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41 + Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b + Flipper-Folly: 83af37379faa69497529e414bd43fbfc7cae259a + Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6 + Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 + Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541 + FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d Folly: b73c3869541e86821df3c387eb0af5f65addfab4 glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3 GoogleAppMeasurement: a6a3a066369828db64eda428cb2856dc1cdc7c4e GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 + libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc + OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 RCTRequired: 48884c74035a0b5b76dbb7a998bd93bcfc5f2047 RCTTypeSafety: edf4b618033c2f1c5b7bc3d90d8e085ed95ba2ab @@ -611,7 +709,8 @@ SPEC CHECKSUMS: RNSVG: ce9d996113475209013317e48b05c21ee988d42e urbanairship-react-native: dfb6dc22b2f41ccaadd636b73d51b448cd1b2bbc Yoga: 7d13633d129fd179e01b8953d38d47be90db185a + YogaKit: f782866e155069a2cca2517aafea43200b01fd5a -PODFILE CHECKSUM: d0a108dc40e53de929a9cbaaf377f87a7b9125fc +PODFILE CHECKSUM: 41b806c7f131f87b716be1f1f9377532d6c9e43a COCOAPODS: 1.10.1 From d0a5329e74fbbf8783191f6feca61475c21e56a3 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 18:38:16 +0100 Subject: [PATCH 080/141] add additional comments --- src/pages/iou/IOUDetailsModal.js | 25 ++++++++++++------------- src/pages/iou/IOUTransactions.js | 1 + src/styles/styles.js | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index d6e336593c2d..34d2b07d8aca 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -16,26 +16,25 @@ import ReportActionItemIOUPreview from '../../components/ReportActionItemIOUPrev import IOUTansactionPropTypes from './IOUTansactionPropTypes'; import IOUTransactions from './IOUTransactions'; -const matchType = PropTypes.shape({ - params: PropTypes.shape({ - // chatReportID passed via route /iou/:chatReportID // todo: drop /iou/ - chatReportID: PropTypes.string, - - // iouReportID passed via route /iou/:iouReportID - iouReportID: PropTypes.string, - }), -}); - const defaultProps = { iou: {}, iouReport: null, }; const propTypes = { - /* Onyx Props */ - // Route params - route: matchType.isRequired, + // URL Route params + route: PropTypes.shape({ + // Params from the URL path + params: PropTypes.shape({ + // chatReportID passed via route /iou/:chatReportID + chatReportID: PropTypes.string, + + // iouReportID passed via route /iou/:iouReportID + iouReportID: PropTypes.string, + }), + }).isRequired, + /* Onyx Props */ // Holds data related to IOU view state, rather than the underlying IOU data. iou: PropTypes.shape({ // Is the IOU Report currently being settled diff --git a/src/pages/iou/IOUTransactions.js b/src/pages/iou/IOUTransactions.js index 2a54e5163f83..e380c16f5c91 100644 --- a/src/pages/iou/IOUTransactions.js +++ b/src/pages/iou/IOUTransactions.js @@ -10,6 +10,7 @@ import IOUTansactionPropTypes from './IOUTansactionPropTypes'; import ReportTransaction from '../../components/ReportTransaction'; const propTypes = { + // Actions from the ChatReport reportActions: PropTypes.shape(ReportActionPropTypes), // ReportID for the associated chat report diff --git a/src/styles/styles.js b/src/styles/styles.js index 1461e5ffe17e..58518a5f250b 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -1346,7 +1346,7 @@ const styles = { color: themeColors.heading, }, 0), - iouPreviewBox: { // TODO: Add margin bottom, remove margin top-- extract outside + iouPreviewBox: { borderColor: themeColors.border, borderWidth: 1, borderRadius: variables.componentBorderRadiusCard, From 8634f3de9b4c93e390ad8c71e1bf04e00d59eadf Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 10 May 2021 18:42:45 +0100 Subject: [PATCH 081/141] revert pod changes, to exclude Flipper workaround --- ios/ExpensifyCash.xcodeproj/project.pbxproj | 180 ++++++++------------ ios/Podfile.lock | 39 ++--- 2 files changed, 88 insertions(+), 131 deletions(-) diff --git a/ios/ExpensifyCash.xcodeproj/project.pbxproj b/ios/ExpensifyCash.xcodeproj/project.pbxproj index 699e69b48758..b5d3c915a5d0 100644 --- a/ios/ExpensifyCash.xcodeproj/project.pbxproj +++ b/ios/ExpensifyCash.xcodeproj/project.pbxproj @@ -18,17 +18,17 @@ 1E76D5232522316A005A268F /* GTAmericaExp-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */; }; 1E76D5242522316A005A268F /* GTAmericaExp-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */; }; 1E76D5252522316A005A268F /* GTAmericaExp-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */; }; - 2F85A9164BEF50A30645E9A2 /* libPods-ExpensifyCash-ExpensifyCashTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27A358A9ACB556CEF9524883 /* libPods-ExpensifyCash-ExpensifyCashTests.a */; }; 425866037F4C482AAB46CB8B /* GTAmericaExp-BdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */; }; - 5216989E3468C96D498B1014 /* libPods-ExpensifyCash.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8573A353D45E3505A720E5E8 /* libPods-ExpensifyCash.a */; }; - 52477A09739546F4814EA25F /* GTAmericaExpMono-Bd.otf in Resources */ = {isa = PBXBuildFile; fileRef = 0DE5D096095C41EE96746C9E /* GTAmericaExpMono-Bd.otf */; }; 6856B78873B64C44A92E51DB /* GTAmericaExp-MdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */; }; 70CF6E82262E297300711ADC /* BootSplash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 70CF6E81262E297300711ADC /* BootSplash.storyboard */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 8821A238A081483FA947BC4E /* GTAmericaExp-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */; }; - DB77016704074197AB6633BB /* GTAmericaExpMono-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 5150E5D0D7F74DBA8D7C1914 /* GTAmericaExpMono-RgIt.otf */; }; + 8C86654500DCC843A74147B5 /* libPods-ExpensifyCash-ExpensifyCashTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ED2AB27DDDFCCE3CD100EA0C /* libPods-ExpensifyCash-ExpensifyCashTests.a */; }; + BB6CECBDA023256B6B955321 /* libPods-ExpensifyCash.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F2C8BDCC1FF0B64AE2DFC9B /* libPods-ExpensifyCash.a */; }; E9DF872D2525201700607FDC /* AirshipConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9DF872C2525201700607FDC /* AirshipConfig.plist */; }; + 52477A09739546F4814EA25F /* GTAmericaExpMono-Bd.otf in Resources */ = {isa = PBXBuildFile; fileRef = 0DE5D096095C41EE96746C9E /* GTAmericaExpMono-Bd.otf */; }; ED814D34526B415CAFA0451E /* GTAmericaExpMono-BdIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3981452A2C7340EBBA2B9BD1 /* GTAmericaExpMono-BdIt.otf */; }; + DB77016704074197AB6633BB /* GTAmericaExpMono-RgIt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 5150E5D0D7F74DBA8D7C1914 /* GTAmericaExpMono-RgIt.otf */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -46,7 +46,6 @@ 00E356EE1AD99517003FC87E /* ExpensifyCashTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExpensifyCashTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* ExpensifyCashTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExpensifyCashTests.m; sourceTree = ""; }; - 0DE5D096095C41EE96746C9E /* GTAmericaExpMono-Bd.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-Bd.otf"; path = "../assets/fonts/GTAmericaExpMono-Bd.otf"; sourceTree = ""; }; 0F5BE0CD252686320097D869 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* Expensify.cash.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Expensify.cash.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ExpensifyCash/AppDelegate.h; sourceTree = ""; }; @@ -54,28 +53,29 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ExpensifyCash/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ExpensifyCash/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ExpensifyCash/main.m; sourceTree = ""; }; - 27A358A9ACB556CEF9524883 /* libPods-ExpensifyCash-ExpensifyCashTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ExpensifyCash-ExpensifyCashTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 3981452A2C7340EBBA2B9BD1 /* GTAmericaExpMono-BdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-BdIt.otf"; path = "../assets/fonts/GTAmericaExpMono-BdIt.otf"; sourceTree = ""; }; - 5150E5D0D7F74DBA8D7C1914 /* GTAmericaExpMono-RgIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-RgIt.otf"; path = "../assets/fonts/GTAmericaExpMono-RgIt.otf"; sourceTree = ""; }; 67D5C3A6A7FA417C8A853FC1 /* GTAmericaExp-Light.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Light.otf"; path = "../assets/fonts/GTAmericaExp-Light.otf"; sourceTree = ""; }; + 6F2C8BDCC1FF0B64AE2DFC9B /* libPods-ExpensifyCash.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ExpensifyCash.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 70CF6E81262E297300711ADC /* BootSplash.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = BootSplash.storyboard; path = ExpensifyCash/BootSplash.storyboard; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ExpensifyCash/LaunchScreen.storyboard; sourceTree = ""; }; 8437A5A38F2047E0BCCD7C2F /* GTAmericaExpMono-Rg.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExpMono-Rg.otf"; path = "../assets/fonts/GTAmericaExpMono-Rg.otf"; sourceTree = ""; }; - 8573A353D45E3505A720E5E8 /* libPods-ExpensifyCash.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ExpensifyCash.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 8C7003903C1E4957824899BB /* GTAmericaExp-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Regular.otf"; path = "../assets/fonts/GTAmericaExp-Regular.otf"; sourceTree = ""; }; 918D7FEFF96242E6B5F5E14D /* GTAmericaExp-RgIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-RgIt.otf"; path = "../assets/fonts/GTAmericaExp-RgIt.otf"; sourceTree = ""; }; + A13EE2CFAF952F935D201D2F /* Pods-ExpensifyCash.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash.release.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash.release.xcconfig"; sourceTree = ""; }; A292718541C841859D97DF2F /* GTAmericaExp-Thin.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Thin.otf"; path = "../assets/fonts/GTAmericaExp-Thin.otf"; sourceTree = ""; }; A5AAD008CBD84A6CAEB9AC97 /* GTAmericaExp-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Bold.otf"; path = "../assets/fonts/GTAmericaExp-Bold.otf"; sourceTree = ""; }; A8D6F2F722FD4E66A38EBBB6 /* GTAmericaExp-BdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-BdIt.otf"; path = "../assets/fonts/GTAmericaExp-BdIt.otf"; sourceTree = ""; }; AE65058949E14DA5A2D5435D /* GTAmericaExp-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-Medium.otf"; path = "../assets/fonts/GTAmericaExp-Medium.otf"; sourceTree = ""; }; - C2E22C1F168EC5E8ADE1C6D4 /* Pods-ExpensifyCash.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash.debug.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash.debug.xcconfig"; sourceTree = ""; }; - C61F74A950D4102529F6C079 /* Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig"; sourceTree = ""; }; + AF728B3FCBC8C2731C4DA7B4 /* Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig"; sourceTree = ""; }; DB5A1365442D4419AF6F08E5 /* GTAmericaExp-MdIt.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "GTAmericaExp-MdIt.otf"; path = "../assets/fonts/GTAmericaExp-MdIt.otf"; sourceTree = ""; }; - E5FF58CC2F62D48FB7F83D5C /* Pods-ExpensifyCash.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash.release.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash.release.xcconfig"; sourceTree = ""; }; + E7967C67752432EA2031954F /* Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig"; sourceTree = ""; }; E9DF872C2525201700607FDC /* AirshipConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AirshipConfig.plist; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; - FD0A867BA09B4414553E906B /* Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig"; sourceTree = ""; }; + ED2AB27DDDFCCE3CD100EA0C /* libPods-ExpensifyCash-ExpensifyCashTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ExpensifyCash-ExpensifyCashTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + F4BC0E78FF1E9BD8B2D38C66 /* Pods-ExpensifyCash.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpensifyCash.debug.xcconfig"; path = "Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash.debug.xcconfig"; sourceTree = ""; }; + 0DE5D096095C41EE96746C9E /* GTAmericaExpMono-Bd.otf */ = {isa = PBXFileReference; name = "GTAmericaExpMono-Bd.otf"; path = "../assets/fonts/GTAmericaExpMono-Bd.otf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; + 3981452A2C7340EBBA2B9BD1 /* GTAmericaExpMono-BdIt.otf */ = {isa = PBXFileReference; name = "GTAmericaExpMono-BdIt.otf"; path = "../assets/fonts/GTAmericaExpMono-BdIt.otf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; + 5150E5D0D7F74DBA8D7C1914 /* GTAmericaExpMono-RgIt.otf */ = {isa = PBXFileReference; name = "GTAmericaExpMono-RgIt.otf"; path = "../assets/fonts/GTAmericaExpMono-RgIt.otf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -83,7 +83,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 2F85A9164BEF50A30645E9A2 /* libPods-ExpensifyCash-ExpensifyCashTests.a in Frameworks */, + 8C86654500DCC843A74147B5 /* libPods-ExpensifyCash-ExpensifyCashTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -91,7 +91,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5216989E3468C96D498B1014 /* libPods-ExpensifyCash.a in Frameworks */, + BB6CECBDA023256B6B955321 /* libPods-ExpensifyCash.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -137,8 +137,8 @@ children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED2971642150620600B7C4FE /* JavaScriptCore.framework */, - 8573A353D45E3505A720E5E8 /* libPods-ExpensifyCash.a */, - 27A358A9ACB556CEF9524883 /* libPods-ExpensifyCash-ExpensifyCashTests.a */, + 6F2C8BDCC1FF0B64AE2DFC9B /* libPods-ExpensifyCash.a */, + ED2AB27DDDFCCE3CD100EA0C /* libPods-ExpensifyCash-ExpensifyCashTests.a */, ); name = Frameworks; sourceTree = ""; @@ -197,10 +197,10 @@ EC29677F0A49C2946A495A33 /* Pods */ = { isa = PBXGroup; children = ( - C2E22C1F168EC5E8ADE1C6D4 /* Pods-ExpensifyCash.debug.xcconfig */, - E5FF58CC2F62D48FB7F83D5C /* Pods-ExpensifyCash.release.xcconfig */, - C61F74A950D4102529F6C079 /* Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig */, - FD0A867BA09B4414553E906B /* Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig */, + F4BC0E78FF1E9BD8B2D38C66 /* Pods-ExpensifyCash.debug.xcconfig */, + A13EE2CFAF952F935D201D2F /* Pods-ExpensifyCash.release.xcconfig */, + AF728B3FCBC8C2731C4DA7B4 /* Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig */, + E7967C67752432EA2031954F /* Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -212,12 +212,11 @@ isa = PBXNativeTarget; buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ExpensifyCashTests" */; buildPhases = ( - 7781CC04809D1929FDB2E270 /* [CP] Check Pods Manifest.lock */, + AEFD4743761AD0E2373D0494 /* [CP] Check Pods Manifest.lock */, 00E356EA1AD99517003FC87E /* Sources */, 00E356EB1AD99517003FC87E /* Frameworks */, 00E356EC1AD99517003FC87E /* Resources */, - F408EFDBF122C3697C07606B /* [CP] Embed Pods Frameworks */, - 33A58FF39EE65B2A02886129 /* [CP] Copy Pods Resources */, + F2B8013B9E9ECAB2B509E702 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -233,16 +232,15 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ExpensifyCash" */; buildPhases = ( - 00FE8D3EC623CA84C643DB2F /* [CP] Check Pods Manifest.lock */, + 6A9F4436BADC311F14A4349F /* [CP] Check Pods Manifest.lock */, FD10A7F022414F080027D42C /* Start Packager */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - A189F039C75BB41E6A26A120 /* [CP] Embed Pods Frameworks */, - 66E8A3C39CA306DBE7CA927B /* [CP] Copy Pods Resources */, - 60932B5F0B132E279E05A998 /* [CP-User] [RNFB] Core Configuration */, - FE17A0EBF379D2384E521938 /* [CP-User] [RNFB] Crashlytics Configuration */, + C9071365B664DE0D85DC5195 /* [CP] Copy Pods Resources */, + CF6259710B5A341870372EA2 /* [CP-User] [RNFB] Core Configuration */, + ED5B8E90A3384FC6A128FB95 /* [CP-User] [RNFB] Crashlytics Configuration */, ); buildRules = ( ); @@ -341,7 +339,7 @@ shellPath = /bin/sh; shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; }; - 00FE8D3EC623CA84C643DB2F /* [CP] Check Pods Manifest.lock */ = { + 6A9F4436BADC311F14A4349F /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -363,13 +361,35 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 33A58FF39EE65B2A02886129 /* [CP] Copy Pods Resources */ = { + AEFD4743761AD0E2373D0494 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests-resources.sh", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-ExpensifyCash-ExpensifyCashTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + C9071365B664DE0D85DC5195 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash-resources.sh", "${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist", "${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib", "${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib", @@ -488,10 +508,10 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 60932B5F0B132E279E05A998 /* [CP-User] [RNFB] Core Configuration */ = { + CF6259710B5A341870372EA2 /* [CP-User] [RNFB] Core Configuration */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -501,13 +521,23 @@ shellPath = /bin/sh; shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n _JSON_OUTPUT_BASE64=$(python -c 'import json,sys,base64;print(base64.b64encode(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"').read())['${_JSON_ROOT}'])))' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\n\n # config.admob_delay_app_measurement_init\n _ADMOB_DELAY_APP_MEASUREMENT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"admob_delay_app_measurement_init\")\n if [[ $_ADMOB_DELAY_APP_MEASUREMENT == \"true\" ]]; then\n _PLIST_ENTRY_KEYS+=(\"GADDelayAppMeasurementInit\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"YES\")\n fi\n\n # config.admob_ios_app_id\n _ADMOB_IOS_APP_ID=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"admob_ios_app_id\")\n if [[ $_ADMOB_IOS_APP_ID ]]; then\n _PLIST_ENTRY_KEYS+=(\"GADApplicationIdentifier\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_ADMOB_IOS_APP_ID\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; }; - 66E8A3C39CA306DBE7CA927B /* [CP] Copy Pods Resources */ = { + ED5B8E90A3384FC6A128FB95 /* [CP-User] [RNFB] Crashlytics Configuration */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + name = "[CP-User] [RNFB] Crashlytics Configuration"; + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\nif [[ ${PODS_ROOT} ]]; then\n echo \"info: Exec FirebaseCrashlytics Run from Pods\"\n \"${PODS_ROOT}/FirebaseCrashlytics/run\"\nelse\n echo \"info: Exec FirebaseCrashlytics Run from framework\"\n \"${PROJECT_DIR}/FirebaseCrashlytics.framework/run\"\nfi\n"; + }; + F2B8013B9E9ECAB2B509E702 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash-resources.sh", + "${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests-resources.sh", "${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAAutomationActions.plist", "${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerContentView.xib", "${PODS_ROOT}/Airship/Airship/AirshipAutomation/Resources/UAInAppMessageBannerView.xib", @@ -626,65 +656,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 7781CC04809D1929FDB2E270 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-ExpensifyCash-ExpensifyCashTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - A189F039C75BB41E6A26A120 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL/OpenSSL.framework/OpenSSL", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash/Pods-ExpensifyCash-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - F408EFDBF122C3697C07606B /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL/OpenSSL.framework/OpenSSL", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpensifyCash-ExpensifyCashTests/Pods-ExpensifyCash-ExpensifyCashTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; FD10A7F022414F080027D42C /* Start Packager */ = { @@ -706,16 +678,6 @@ shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n"; showEnvVarsInLog = 0; }; - FE17A0EBF379D2384E521938 /* [CP-User] [RNFB] Crashlytics Configuration */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - name = "[CP-User] [RNFB] Crashlytics Configuration"; - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\nif [[ ${PODS_ROOT} ]]; then\n echo \"info: Exec FirebaseCrashlytics Run from Pods\"\n \"${PODS_ROOT}/FirebaseCrashlytics/run\"\nelse\n echo \"info: Exec FirebaseCrashlytics Run from framework\"\n \"${PROJECT_DIR}/FirebaseCrashlytics.framework/run\"\nfi\n"; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -749,7 +711,7 @@ /* Begin XCBuildConfiguration section */ 00E356F61AD99517003FC87E /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C61F74A950D4102529F6C079 /* Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig */; + baseConfigurationReference = AF728B3FCBC8C2731C4DA7B4 /* Pods-ExpensifyCash-ExpensifyCashTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -773,7 +735,7 @@ }; 00E356F71AD99517003FC87E /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FD0A867BA09B4414553E906B /* Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig */; + baseConfigurationReference = E7967C67752432EA2031954F /* Pods-ExpensifyCash-ExpensifyCashTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -795,7 +757,7 @@ }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C2E22C1F168EC5E8ADE1C6D4 /* Pods-ExpensifyCash.debug.xcconfig */; + baseConfigurationReference = F4BC0E78FF1E9BD8B2D38C66 /* Pods-ExpensifyCash.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -827,7 +789,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E5FF58CC2F62D48FB7F83D5C /* Pods-ExpensifyCash.release.xcconfig */; + baseConfigurationReference = A13EE2CFAF952F935D201D2F /* Pods-ExpensifyCash.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index fd481f36f362..b1af438affe9 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -13,6 +13,7 @@ PODS: - Airship/Core - boost-for-react-native (1.63.0) - CocoaAsyncSocket (7.6.5) + - CocoaLibEvent (1.0.0) - DoubleConversion (1.1.6) - FBLazyVector (0.63.3) - FBReactNativeSpec (0.63.3): @@ -64,20 +65,17 @@ PODS: - Flipper (0.54.0): - Flipper-Folly (~> 2.2) - Flipper-RSocket (~> 1.1) - - Flipper-Boost-iOSX (1.76.0.1.10) - Flipper-DoubleConversion (1.1.7) - - Flipper-Fmt (7.1.7) - - Flipper-Folly (2.6.7): - - Flipper-Boost-iOSX + - Flipper-Folly (2.3.0): + - boost-for-react-native + - CocoaLibEvent (~> 1.0) - Flipper-DoubleConversion - - Flipper-Fmt (= 7.1.7) - Flipper-Glog - - libevent (~> 2.1.12) - - OpenSSL-Universal (= 1.1.180) + - OpenSSL-Universal (= 1.0.2.20) - Flipper-Glog (0.3.6) - Flipper-PeerTalk (0.0.4) - - Flipper-RSocket (1.4.3): - - Flipper-Folly (~> 2.6) + - Flipper-RSocket (1.1.0): + - Flipper-Folly (~> 2.2) - FlipperKit (0.54.0): - FlipperKit/Core (= 0.54.0) - FlipperKit/Core (0.54.0): @@ -147,14 +145,15 @@ PODS: - GoogleUtilities/Logger - GoogleUtilities/UserDefaults (6.7.2): - GoogleUtilities/Logger - - libevent (2.1.12) - nanopb (1.30906.0): - nanopb/decode (= 1.30906.0) - nanopb/encode (= 1.30906.0) - nanopb/decode (1.30906.0) - nanopb/encode (1.30906.0) - - OpenSSL-Universal (1.1.180) - - PromisesObjC (1.2.12) + - OpenSSL-Universal (1.0.2.20): + - OpenSSL-Universal/Static (= 1.0.2.20) + - OpenSSL-Universal/Static (1.0.2.20) + - PromisesObjC (1.2.11) - RCTRequired (0.63.3) - RCTTypeSafety (0.63.3): - FBLazyVector (= 0.63.3) @@ -513,6 +512,7 @@ SPEC REPOS: - Airship - boost-for-react-native - CocoaAsyncSocket + - CocoaLibEvent - Firebase - FirebaseAnalytics - FirebaseCore @@ -520,9 +520,7 @@ SPEC REPOS: - FirebaseCrashlytics - FirebaseInstallations - Flipper - - Flipper-Boost-iOSX - Flipper-DoubleConversion - - Flipper-Fmt - Flipper-Folly - Flipper-Glog - Flipper-PeerTalk @@ -531,7 +529,6 @@ SPEC REPOS: - GoogleAppMeasurement - GoogleDataTransport - GoogleUtilities - - libevent - nanopb - OpenSSL-Universal - PromisesObjC @@ -639,6 +636,7 @@ SPEC CHECKSUMS: Airship: 02ad73780f9eed21870e36b0aaab327acda6a102 boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 + CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f DoubleConversion: cde416483dac037923206447da6e1454df403714 FBLazyVector: 878b59e31113e289e275165efbe4b54fa614d43d FBReactNativeSpec: 7da9338acfb98d4ef9e5536805a0704572d33c2f @@ -649,23 +647,20 @@ SPEC CHECKSUMS: FirebaseCrashlytics: 1a747c9cc084a24dc6d9511c991db1cd078154eb FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2 Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365 - Flipper-Boost-iOSX: df3765e7e26b44bc3e68a250ef1fa0afa15963bc Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41 - Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b - Flipper-Folly: 83af37379faa69497529e414bd43fbfc7cae259a + Flipper-Folly: e4493b013c02d9347d5e0cb4d128680239f6c78a Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6 Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 - Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541 + Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7 FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d Folly: b73c3869541e86821df3c387eb0af5f65addfab4 glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3 GoogleAppMeasurement: a6a3a066369828db64eda428cb2856dc1cdc7c4e GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 - libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc - OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b - PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 + OpenSSL-Universal: ff34003318d5e1163e9529b08470708e389ffcdd + PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f RCTRequired: 48884c74035a0b5b76dbb7a998bd93bcfc5f2047 RCTTypeSafety: edf4b618033c2f1c5b7bc3d90d8e085ed95ba2ab React: f36e90f3ceb976546e97df3403e37d226f79d0e3 From d0ce923de75f2db3fcf38bbbf3be5ed5c4b293b4 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 14:25:40 +0100 Subject: [PATCH 082/141] Merge branch 'main' into jules-iouDetailsModal # Conflicts: # src/libs/Navigation/AppNavigator/ModalStackNavigators.js # src/libs/actions/Report.js # src/pages/home/report/ReportActionItem.js --- .github/actions/checkDeployBlockers/index.js | 8 +- .../createOrUpdateStagingDeploy/index.js | 8 +- .github/actions/getReleaseBody/index.js | 8 +- .../actions/isPullRequestMergeable/index.js | 8 +- .../actions/isStagingDeployLocked/index.js | 8 +- .../markPullRequestsAsDeployed/index.js | 8 +- .../actions/reopenIssueWithComment/index.js | 8 +- .github/libs/GithubUtils.js | 8 +- android/app/build.gradle | 4 +- ios/ExpensifyCash/Info.plist | 2 +- ios/ExpensifyCashTests/Info.plist | 2 +- package-lock.json | 6 +- package.json | 4 +- src/CONST.js | 13 - src/Expensify.js | 2 +- src/ONYXKEYS.js | 4 + src/components/AttachmentModal.js | 6 +- .../AttachmentPicker/index.native.js | 168 +++++------ src/components/AttachmentView.js | 14 +- src/components/ConfirmModal.js | 17 +- src/components/HeaderWithCloseButton.js | 7 +- src/components/IOUConfirmationList.js | 34 ++- src/components/InvertedFlatList/index.js | 13 +- src/components/OptionsSelector.js | 10 +- src/components/RenderHTML.js | 7 +- src/components/TextInputFocusable/index.js | 16 +- .../TextInputFocusable/index.native.js | 3 +- src/components/UnreadActionIndicator.js | 9 +- .../UpdateAppModal/BaseUpdateAppModal.js | 12 +- src/components/VideoChatButtonAndMenu.js | 29 +- src/components/WelcomeText.js | 11 +- src/components/withLocalize.js | 47 +++- src/languages/en.js | 229 +++++++++++++++ src/libs/API.js | 14 + .../AppNavigator/ModalStackNavigators.js | 260 ++++++------------ src/libs/OptionsListUtils.js | 11 +- src/libs/Pusher/EventType.js | 1 + src/libs/ReportScrollManager/index.js | 25 ++ src/libs/ReportScrollManager/index.native.js | 19 ++ src/libs/actions/Report.js | 88 +++++- src/libs/toggleReportActionComposeView.js | 8 + src/pages/DetailsPage.js | 30 +- src/pages/NewChatPage.js | 34 ++- src/pages/NewGroupPage.js | 38 ++- src/pages/NotFound.js | 11 +- src/pages/ReportParticipantsPage.js | 32 ++- src/pages/SearchPage.js | 34 ++- src/pages/SetPasswordPage.js | 23 +- .../home/report/EmojiPickerMenu/index.js | 11 +- src/pages/home/report/ReportActionCompose.js | 53 +++- .../home/report/ReportActionContextMenu.js | 70 ++++- .../report/ReportActionContextMenuItem.js | 1 - src/pages/home/report/ReportActionItem.js | 50 +++- .../home/report/ReportActionItemFragment.js | 22 +- .../report/ReportActionItemMessageEdit.js | 129 +++++++++ src/pages/home/report/ReportActionsView.js | 16 +- .../home/report/ReportTypingIndicator.js | 16 +- src/pages/home/report/ReportView.js | 38 ++- src/pages/home/sidebar/SidebarScreen.js | 17 +- src/pages/iou/IOUDetailsPage.js | 12 + src/pages/iou/IOUModal.js | 50 ++-- src/pages/iou/steps/IOUAmountPage.js | 16 +- .../IOUParticipantsRequest.js | 25 +- .../IOUParticipantsSplit.js | 31 ++- src/pages/settings/AddSecondaryLoginPage.js | 37 ++- src/pages/settings/InitialPage.js | 54 ++-- src/pages/settings/PasswordPage.js | 39 ++- src/pages/settings/PaymentsPage.js | 27 +- src/pages/settings/PreferencesPage.js | 130 +++++---- src/pages/settings/Profile/LoginField.js | 20 +- src/pages/settings/Profile/ProfilePage.js | 83 ++++-- src/pages/signin/ChangeExpensifyLoginLink.js | 18 +- src/pages/signin/LoginForm.js | 12 +- src/pages/signin/PasswordForm.js | 25 +- src/pages/signin/ResendValidationForm.js | 23 +- .../SignInPageLayoutNarrow.js | 18 +- .../SignInPageLayout/SignInPageLayoutWide.js | 18 +- .../signin/TermsAndLicenses/TermsOnly.js | 15 +- .../TermsAndLicenses/TermsWithLicenses.js | 19 +- tests/unit/GithubUtilsTest.js | 25 +- 80 files changed, 1730 insertions(+), 751 deletions(-) mode change 100644 => 100755 src/CONST.js mode change 100644 => 100755 src/ONYXKEYS.js mode change 100644 => 100755 src/components/AttachmentModal.js mode change 100644 => 100755 src/components/AttachmentPicker/index.native.js mode change 100644 => 100755 src/components/AttachmentView.js mode change 100644 => 100755 src/components/ConfirmModal.js mode change 100644 => 100755 src/components/HeaderWithCloseButton.js mode change 100644 => 100755 src/components/IOUConfirmationList.js mode change 100644 => 100755 src/components/OptionsSelector.js mode change 100644 => 100755 src/components/RenderHTML.js mode change 100644 => 100755 src/components/TextInputFocusable/index.js mode change 100644 => 100755 src/components/UnreadActionIndicator.js mode change 100644 => 100755 src/components/UpdateAppModal/BaseUpdateAppModal.js mode change 100644 => 100755 src/components/VideoChatButtonAndMenu.js mode change 100644 => 100755 src/components/WelcomeText.js mode change 100644 => 100755 src/components/withLocalize.js mode change 100644 => 100755 src/languages/en.js create mode 100644 src/libs/ReportScrollManager/index.js create mode 100644 src/libs/ReportScrollManager/index.native.js create mode 100644 src/libs/toggleReportActionComposeView.js mode change 100644 => 100755 src/pages/DetailsPage.js mode change 100644 => 100755 src/pages/NewChatPage.js mode change 100644 => 100755 src/pages/NewGroupPage.js mode change 100644 => 100755 src/pages/NotFound.js mode change 100644 => 100755 src/pages/ReportParticipantsPage.js mode change 100644 => 100755 src/pages/SearchPage.js mode change 100644 => 100755 src/pages/SetPasswordPage.js mode change 100644 => 100755 src/pages/home/report/EmojiPickerMenu/index.js mode change 100644 => 100755 src/pages/home/report/ReportActionCompose.js mode change 100644 => 100755 src/pages/home/report/ReportActionContextMenu.js create mode 100644 src/pages/home/report/ReportActionItemMessageEdit.js mode change 100644 => 100755 src/pages/home/report/ReportActionsView.js mode change 100644 => 100755 src/pages/home/report/ReportTypingIndicator.js mode change 100644 => 100755 src/pages/home/sidebar/SidebarScreen.js create mode 100644 src/pages/iou/IOUDetailsPage.js mode change 100644 => 100755 src/pages/iou/IOUModal.js mode change 100644 => 100755 src/pages/iou/steps/IOUAmountPage.js mode change 100644 => 100755 src/pages/iou/steps/IOUParticipantsPage/IOUParticipantsRequest.js mode change 100644 => 100755 src/pages/iou/steps/IOUParticipantsPage/IOUParticipantsSplit.js mode change 100644 => 100755 src/pages/settings/AddSecondaryLoginPage.js mode change 100644 => 100755 src/pages/settings/InitialPage.js mode change 100644 => 100755 src/pages/settings/PasswordPage.js mode change 100644 => 100755 src/pages/settings/PaymentsPage.js mode change 100644 => 100755 src/pages/settings/PreferencesPage.js mode change 100644 => 100755 src/pages/settings/Profile/LoginField.js mode change 100644 => 100755 src/pages/settings/Profile/ProfilePage.js mode change 100644 => 100755 src/pages/signin/ChangeExpensifyLoginLink.js mode change 100644 => 100755 src/pages/signin/LoginForm.js mode change 100644 => 100755 src/pages/signin/PasswordForm.js mode change 100644 => 100755 src/pages/signin/ResendValidationForm.js mode change 100644 => 100755 src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js mode change 100644 => 100755 src/pages/signin/SignInPageLayout/SignInPageLayoutWide.js mode change 100644 => 100755 src/pages/signin/TermsAndLicenses/TermsOnly.js mode change 100644 => 100755 src/pages/signin/TermsAndLicenses/TermsWithLicenses.js diff --git a/.github/actions/checkDeployBlockers/index.js b/.github/actions/checkDeployBlockers/index.js index 0687cbef4598..f34a1d6aefc2 100644 --- a/.github/actions/checkDeployBlockers/index.js +++ b/.github/actions/checkDeployBlockers/index.js @@ -172,7 +172,13 @@ class GithubUtils { * @returns {Array} - [{url: String, number: Number, isVerified: Boolean}] */ getStagingDeployCashPRList(issue) { - const PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/)[1]; + let PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/) || []; + if (PRListSection.length !== 2) { + // No PRs, return an empty array + console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); + return []; + } + PRListSection = PRListSection[1]; const unverifiedPRs = _.map( [...PRListSection.matchAll(new RegExp(`- \\[ ] (${PULL_REQUEST_REGEX.source})`, 'g'))], match => ({ diff --git a/.github/actions/createOrUpdateStagingDeploy/index.js b/.github/actions/createOrUpdateStagingDeploy/index.js index 5fc7ffa6c9bc..3fd6c4c9386b 100644 --- a/.github/actions/createOrUpdateStagingDeploy/index.js +++ b/.github/actions/createOrUpdateStagingDeploy/index.js @@ -198,7 +198,13 @@ class GithubUtils { * @returns {Array} - [{url: String, number: Number, isVerified: Boolean}] */ getStagingDeployCashPRList(issue) { - const PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/)[1]; + let PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/) || []; + if (PRListSection.length !== 2) { + // No PRs, return an empty array + console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); + return []; + } + PRListSection = PRListSection[1]; const unverifiedPRs = _.map( [...PRListSection.matchAll(new RegExp(`- \\[ ] (${PULL_REQUEST_REGEX.source})`, 'g'))], match => ({ diff --git a/.github/actions/getReleaseBody/index.js b/.github/actions/getReleaseBody/index.js index 0a3cb15cd7e8..f1601e48a833 100644 --- a/.github/actions/getReleaseBody/index.js +++ b/.github/actions/getReleaseBody/index.js @@ -111,7 +111,13 @@ class GithubUtils { * @returns {Array} - [{url: String, number: Number, isVerified: Boolean}] */ getStagingDeployCashPRList(issue) { - const PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/)[1]; + let PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/) || []; + if (PRListSection.length !== 2) { + // No PRs, return an empty array + console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); + return []; + } + PRListSection = PRListSection[1]; const unverifiedPRs = _.map( [...PRListSection.matchAll(new RegExp(`- \\[ ] (${PULL_REQUEST_REGEX.source})`, 'g'))], match => ({ diff --git a/.github/actions/isPullRequestMergeable/index.js b/.github/actions/isPullRequestMergeable/index.js index 58042983cfde..e105a86a30a3 100644 --- a/.github/actions/isPullRequestMergeable/index.js +++ b/.github/actions/isPullRequestMergeable/index.js @@ -146,7 +146,13 @@ class GithubUtils { * @returns {Array} - [{url: String, number: Number, isVerified: Boolean}] */ getStagingDeployCashPRList(issue) { - const PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/)[1]; + let PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/) || []; + if (PRListSection.length !== 2) { + // No PRs, return an empty array + console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); + return []; + } + PRListSection = PRListSection[1]; const unverifiedPRs = _.map( [...PRListSection.matchAll(new RegExp(`- \\[ ] (${PULL_REQUEST_REGEX.source})`, 'g'))], match => ({ diff --git a/.github/actions/isStagingDeployLocked/index.js b/.github/actions/isStagingDeployLocked/index.js index d3e79e56dacf..d96388a77dc0 100644 --- a/.github/actions/isStagingDeployLocked/index.js +++ b/.github/actions/isStagingDeployLocked/index.js @@ -124,7 +124,13 @@ class GithubUtils { * @returns {Array} - [{url: String, number: Number, isVerified: Boolean}] */ getStagingDeployCashPRList(issue) { - const PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/)[1]; + let PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/) || []; + if (PRListSection.length !== 2) { + // No PRs, return an empty array + console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); + return []; + } + PRListSection = PRListSection[1]; const unverifiedPRs = _.map( [...PRListSection.matchAll(new RegExp(`- \\[ ] (${PULL_REQUEST_REGEX.source})`, 'g'))], match => ({ diff --git a/.github/actions/markPullRequestsAsDeployed/index.js b/.github/actions/markPullRequestsAsDeployed/index.js index bd86817652cf..99248d97bdc7 100644 --- a/.github/actions/markPullRequestsAsDeployed/index.js +++ b/.github/actions/markPullRequestsAsDeployed/index.js @@ -157,7 +157,13 @@ class GithubUtils { * @returns {Array} - [{url: String, number: Number, isVerified: Boolean}] */ getStagingDeployCashPRList(issue) { - const PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/)[1]; + let PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/) || []; + if (PRListSection.length !== 2) { + // No PRs, return an empty array + console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); + return []; + } + PRListSection = PRListSection[1]; const unverifiedPRs = _.map( [...PRListSection.matchAll(new RegExp(`- \\[ ] (${PULL_REQUEST_REGEX.source})`, 'g'))], match => ({ diff --git a/.github/actions/reopenIssueWithComment/index.js b/.github/actions/reopenIssueWithComment/index.js index 37ac365ae566..557f18c17291 100644 --- a/.github/actions/reopenIssueWithComment/index.js +++ b/.github/actions/reopenIssueWithComment/index.js @@ -135,7 +135,13 @@ class GithubUtils { * @returns {Array} - [{url: String, number: Number, isVerified: Boolean}] */ getStagingDeployCashPRList(issue) { - const PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/)[1]; + let PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/) || []; + if (PRListSection.length !== 2) { + // No PRs, return an empty array + console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); + return []; + } + PRListSection = PRListSection[1]; const unverifiedPRs = _.map( [...PRListSection.matchAll(new RegExp(`- \\[ ] (${PULL_REQUEST_REGEX.source})`, 'g'))], match => ({ diff --git a/.github/libs/GithubUtils.js b/.github/libs/GithubUtils.js index 8930cfa00f5b..59b76c2cb9bc 100644 --- a/.github/libs/GithubUtils.js +++ b/.github/libs/GithubUtils.js @@ -82,7 +82,13 @@ class GithubUtils { * @returns {Array} - [{url: String, number: Number, isVerified: Boolean}] */ getStagingDeployCashPRList(issue) { - const PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/)[1]; + let PRListSection = issue.body.match(/pull requests:\*\*\r\n((?:.*\r\n)+)\r\n/) || []; + if (PRListSection.length !== 2) { + // No PRs, return an empty array + console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); + return []; + } + PRListSection = PRListSection[1]; const unverifiedPRs = _.map( [...PRListSection.matchAll(new RegExp(`- \\[ ] (${PULL_REQUEST_REGEX.source})`, 'g'))], match => ({ diff --git a/android/app/build.gradle b/android/app/build.gradle index 07baf4168866..ab60ce42d386 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -148,8 +148,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001004102 - versionName "1.0.41-2" + versionCode 1001004109 + versionName "1.0.41-9" } splits { abi { diff --git a/ios/ExpensifyCash/Info.plist b/ios/ExpensifyCash/Info.plist index ddabc915d1b6..ba39baeb2927 100644 --- a/ios/ExpensifyCash/Info.plist +++ b/ios/ExpensifyCash/Info.plist @@ -30,7 +30,7 @@ CFBundleVersion - 1.0.41.2 + 1.0.41.9 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/ExpensifyCashTests/Info.plist b/ios/ExpensifyCashTests/Info.plist index 48aa1bc12c9e..733005455f2d 100644 --- a/ios/ExpensifyCashTests/Info.plist +++ b/ios/ExpensifyCashTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.0.41.2 + 1.0.41.9 diff --git a/package-lock.json b/package-lock.json index 3c2e0efa4736..ea60b195b44a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "expensify.cash", - "version": "1.0.41-2", + "version": "1.0.41-9", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -11562,8 +11562,8 @@ } }, "expensify-common": { - "version": "git+https://github.com/Expensify/expensify-common.git#05b8a007e9faa8e3b7a1ddd7348503acca6f4e95", - "from": "git+https://github.com/Expensify/expensify-common.git#05b8a007e9faa8e3b7a1ddd7348503acca6f4e95", + "version": "git+https://github.com/Expensify/expensify-common.git#2e5cff552cf132da90a3fb9756e6b4fb6ae7b40c", + "from": "git+https://github.com/Expensify/expensify-common.git#2e5cff552cf132da90a3fb9756e6b4fb6ae7b40c", "requires": { "classnames": "2.2.5", "clipboard": "2.0.4", diff --git a/package.json b/package.json index 6f5f11131d53..676c2a5dbbfe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "expensify.cash", - "version": "1.0.41-2", + "version": "1.0.41-9", "author": "Expensify, Inc.", "homepage": "https://expensify.cash", "description": "Expensify.cash is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", @@ -57,7 +57,7 @@ "electron-log": "^4.2.4", "electron-serve": "^1.0.0", "electron-updater": "^4.3.4", - "expensify-common": "git+https://github.com/Expensify/expensify-common.git#05b8a007e9faa8e3b7a1ddd7348503acca6f4e95", + "expensify-common": "git+https://github.com/Expensify/expensify-common.git#2e5cff552cf132da90a3fb9756e6b4fb6ae7b40c", "file-loader": "^6.0.0", "html-entities": "^1.3.1", "lodash": "4.17.21", diff --git a/src/CONST.js b/src/CONST.js old mode 100644 new mode 100755 index d3f0ede5df0b..97ec927d51e0 --- a/src/CONST.js +++ b/src/CONST.js @@ -66,11 +66,6 @@ const CONST = { COLD: 'cold', REPORT_ACTION_ITEM_LAYOUT_DEBOUNCE_TIME: 1500, }, - MESSAGES: { - // eslint-disable-next-line max-len - NO_PHONE_NUMBER: 'Please enter a phone number including the country code e.g +447814266907', - MAXIMUM_PARTICIPANTS_REACHED: 'You\'ve reached the maximum number of participants for a group chat.', - }, PRIORITY_MODE: { GSD: 'gsd', DEFAULT: 'default', @@ -90,14 +85,6 @@ const CONST = { }, DEFAULT_TIME_ZONE: {automatic: true, selected: 'America/Los_Angeles'}, DEFAULT_ACCOUNT_DATA: {error: '', success: '', loading: false}, - PRONOUNS: { - HE_HIM_HIS: 'He/him', - SHE_HER_HERS: 'She/her', - THEY_THEM_THEIRS: 'They/them', - ZE_HIR_HIRS: 'Ze/hir', - SELF_SELECT: 'Self-select', - CALL_ME_BY_MY_NAME: 'Call me by my name', - }, APP_STATE: { ACTIVE: 'active', BACKGROUND: 'background', diff --git a/src/Expensify.js b/src/Expensify.js index 9041fa68fb4c..bd29c9486139 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -23,7 +23,7 @@ Onyx.init({ initialKeyStates: { // Clear any loading and error messages so they do not appear on app startup - [ONYXKEYS.SESSION]: {loading: false}, + [ONYXKEYS.SESSION]: {loading: false, shouldShowComposeInput: true}, [ONYXKEYS.ACCOUNT]: CONST.DEFAULT_ACCOUNT_DATA, [ONYXKEYS.NETWORK]: {isOffline: false}, [ONYXKEYS.IOU]: {loading: false, error: false, creatingIOUTransaction: false}, diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js old mode 100644 new mode 100755 index bbd2263fca1e..e2efceeadaf8 --- a/src/ONYXKEYS.js +++ b/src/ONYXKEYS.js @@ -68,7 +68,11 @@ export default { REPORT: 'report_', REPORT_ACTIONS: 'reportActions_', REPORT_DRAFT_COMMENT: 'reportDraftComment_', + REPORT_ACTIONS_DRAFTS: 'reportActionsDrafts_', REPORT_USER_IS_TYPING: 'reportUserIsTyping_', REPORT_IOUS: 'reportIOUs_', }, + + // Indicates which locale should be used + PREFERRED_LOCALE: 'preferredLocale', }; diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js old mode 100644 new mode 100755 index 4f8a00915671..f7a83a1146f9 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -15,6 +15,7 @@ import compose from '../libs/compose'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import HeaderWithCloseButton from './HeaderWithCloseButton'; import fileDownload from '../libs/fileDownload'; +import withLocalize, {withLocalizePropTypes} from './withLocalize'; /** * Modal render prop component that exposes modal launching triggers that can be used @@ -46,6 +47,8 @@ const propTypes = { authToken: PropTypes.string.isRequired, }).isRequired, + ...withLocalizePropTypes, + ...windowDimensionsPropTypes, }; @@ -133,7 +136,7 @@ class AttachmentModal extends PureComponent { styles.buttonConfirmText, ]} > - Upload + {this.props.translate('common.upload')} )} @@ -165,4 +168,5 @@ export default compose( key: ONYXKEYS.SESSION, }, }), + withLocalize, )(AttachmentModal); diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js old mode 100644 new mode 100755 index 085a4061ce29..e7a2da327afd --- a/src/components/AttachmentPicker/index.native.js +++ b/src/components/AttachmentPicker/index.native.js @@ -11,10 +11,13 @@ import Popover from '../Popover'; import MenuItem from '../MenuItem'; import {Camera, Gallery, Paperclip} from '../Icon/Expensicons'; import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; +import withLocalize, {withLocalizePropTypes} from '../withLocalize'; +import compose from '../../libs/compose'; const propTypes = { ...basePropTypes, ...windowDimensionsPropTypes, + ...withLocalizePropTypes, }; /** @@ -34,80 +37,6 @@ const documentPickerOptions = { type: [RNDocumentPicker.types.allFiles], }; -/** - * Inform the users when they need to grant camera access and guide them to settings - */ -function showPermissionsAlert() { - Alert.alert( - 'Camera Permission Required', - 'Expensify.cash does not have access to your camera, please enable the permission and try again.', - [ - { - text: 'Cancel', - style: 'cancel', - }, - { - text: 'Settings', - onPress: () => Linking.openSettings(), - }, - ], - {cancelable: false}, - ); -} - -/** - * A generic handling when we don't know the exact reason for an error - * - */ -function showGeneralAlert() { - Alert.alert( - 'Attachment Error', - 'An error occurred while selecting an attachment, please try again', - ); -} - -/** - * Launch the DocumentPicker. Results are in the same format as ImagePicker - * - * @returns {Promise} - */ -function showDocumentPicker() { - return RNDocumentPicker.pick(documentPickerOptions).catch((error) => { - if (!RNDocumentPicker.isCancel(error)) { - showGeneralAlert(error.message); - throw error; - } - }); -} - -/** - * Common image picker handling - * - * @param {function} imagePickerFunc - RNImagePicker.launchCamera or RNImagePicker.launchImageLibrary - * @returns {Promise} - */ -function showImagePicker(imagePickerFunc) { - return new Promise((resolve, reject) => { - imagePickerFunc(imagePickerOptions, (response) => { - if (response.error) { - switch (response.error) { - case 'Camera permissions not granted': - case 'Permissions weren\'t granted': - showPermissionsAlert(); - break; - default: - showGeneralAlert(response.error); - break; - } - - reject(new Error(`Error during attachment selection: ${response.error}`)); - } - - resolve(response); - }); - }); -} - /** * The data returned from `show` is different on web and mobile, so use this function to ensure the data we * send to the xhr will be handled properly. @@ -142,18 +71,18 @@ class AttachmentPicker extends Component { this.menuItemData = [ { icon: Camera, - text: 'Take Photo', - pickAttachment: () => showImagePicker(RNImagePicker.launchCamera), + text: this.props.translate('attachmentPicker.takePhoto'), + pickAttachment: () => this.showImagePicker(RNImagePicker.launchCamera), }, { icon: Gallery, - text: 'Choose from Gallery', - pickAttachment: () => showImagePicker(RNImagePicker.launchImageLibrary), + text: this.props.translate('attachmentPicker.chooseFromGallery'), + pickAttachment: () => this.showImagePicker(RNImagePicker.launchImageLibrary), }, { icon: Paperclip, - text: 'Choose Document', - pickAttachment: showDocumentPicker, + text: this.props.translate('attachmentPicker.chooseDocument'), + pickAttachment: this.showDocumentPicker, }, ]; @@ -174,6 +103,80 @@ class AttachmentPicker extends Component { } } + /** + * Inform the users when they need to grant camera access and guide them to settings + */ + showPermissionsAlert() { + Alert.alert( + this.props.translate('attachmentPicker.cameraPermissionRequired'), + this.props.translate('attachmentPicker.expensifyDoesntHaveAccessToCamera'), + [ + { + text: this.props.translate('common.cancel'), + style: 'cancel', + }, + { + text: this.props.translate('common.settings'), + onPress: () => Linking.openSettings(), + }, + ], + {cancelable: false}, + ); + } + + /** + * Common image picker handling + * + * @param {function} imagePickerFunc - RNImagePicker.launchCamera or RNImagePicker.launchImageLibrary + * @returns {Promise} + */ + showImagePicker(imagePickerFunc) { + return new Promise((resolve, reject) => { + imagePickerFunc(imagePickerOptions, (response) => { + if (response.error) { + switch (response.error) { + case 'Camera permissions not granted': + case 'Permissions weren\'t granted': + this.showPermissionsAlert(); + break; + default: + this.showGeneralAlert(response.error); + break; + } + const errorDescription = this.props.translate('attachmentPicker.errorDuringAttachmentSelection'); + reject(new Error(`${errorDescription}: ${response.error}`)); + } + + resolve(response); + }); + }); + } + + /** + * A generic handling when we don't know the exact reason for an error + * + */ + showGeneralAlert() { + Alert.alert( + this.props.translate('attachmentPicker.attachmentError'), + this.props.translate('attachmentPicker.errorWhileSelectingAttachment'), + ); + } + + /** + * Launch the DocumentPicker. Results are in the same format as ImagePicker + * + * @returns {Promise} + */ + showDocumentPicker() { + return RNDocumentPicker.pick(documentPickerOptions).catch((error) => { + if (!RNDocumentPicker.isCancel(error)) { + this.showGeneralAlert(error.message); + throw error; + } + }); + } + /** * Triggers the `onPicked` callback with the selected attachment */ @@ -247,4 +250,7 @@ class AttachmentPicker extends Component { AttachmentPicker.propTypes = propTypes; AttachmentPicker.displayName = 'AttachmentPicker'; -export default withWindowDimensions(AttachmentPicker); +export default compose( + withWindowDimensions, + withLocalize, +)(AttachmentPicker); diff --git a/src/components/AttachmentView.js b/src/components/AttachmentView.js old mode 100644 new mode 100755 index d1b37391e973..1c316d4199c1 --- a/src/components/AttachmentView.js +++ b/src/components/AttachmentView.js @@ -7,6 +7,8 @@ import PDFView from './PDFView'; import ImageView from './ImageView'; import Icon from './Icon'; import {Paperclip} from './Icon/Expensicons'; +import withLocalize, {withLocalizePropTypes} from './withLocalize'; +import compose from '../libs/compose'; const propTypes = { // URL to full-sized attachment @@ -15,18 +17,21 @@ const propTypes = { file: PropTypes.shape({ name: PropTypes.string, }), + + ...withLocalizePropTypes, }; const defaultProps = { file: { - name: 'Unknown Filename', + name: '', }, }; const AttachmentView = (props) => { // Check both sourceURL and file.name since PDFs dragged into the the text field // will appear with a sourceURL that is a blob - if (Str.isPDF(props.sourceURL) || (props.file && Str.isPDF(props.file.name))) { + if (Str.isPDF(props.sourceURL) + || (props.file && Str.isPDF(props.file.name || props.translate('attachmentView.unknownFilename')))) { return ( ( styles.buttonSuccessText, ]} > - {props.confirmText} + {props.confirmText || props.translate('common.yes')} @@ -77,7 +81,7 @@ const ConfirmModal = props => ( onPress={props.onCancel} > - {props.cancelText} + {props.cancelText || props.translate('common.no')} @@ -87,4 +91,7 @@ const ConfirmModal = props => ( ConfirmModal.propTypes = propTypes; ConfirmModal.defaultProps = defaultProps; ConfirmModal.displayName = 'ConfirmModal'; -export default withWindowDimensions(ConfirmModal); +export default compose( + withWindowDimensions, + withLocalize, +)(ConfirmModal); diff --git a/src/components/HeaderWithCloseButton.js b/src/components/HeaderWithCloseButton.js old mode 100644 new mode 100755 index 6a281565a1b0..5848ebf814d1 --- a/src/components/HeaderWithCloseButton.js +++ b/src/components/HeaderWithCloseButton.js @@ -7,6 +7,7 @@ import styles from '../styles/styles'; import Header from './Header'; import Icon from './Icon'; import {Close, Download, BackArrow} from './Icon/Expensicons'; +import withLocalize, {withLocalizePropTypes} from './withLocalize'; const propTypes = { /** Title of the Header */ @@ -29,6 +30,8 @@ const propTypes = { /** Whether we should show a border on the bottom of the Header */ shouldShowBorderBottom: PropTypes.bool, + + ...withLocalizePropTypes, }; const defaultProps = { @@ -63,7 +66,7 @@ const HeaderWithCloseButton = props => (
{ - props.title === 'Attachment' && ( + props.title === props.translate('common.attachment') && ( - WHAT'S IT FOR? + {this.props.translate('iOUConfirmationList.whatsItFor')} @@ -249,7 +252,7 @@ class IOUConfirmationList extends Component { style={[styles.textInput]} value={this.props.comment} onChangeText={this.props.onUpdateComment} - placeholder="Optional" + placeholder={this.props.translate('common.optional')} placeholderTextColor={themeColors.placeholderText} /> @@ -257,7 +260,9 @@ class IOUConfirmationList extends Component { this.props.onConfirm(this.getSplits())} /> @@ -270,9 +275,14 @@ IOUConfirmationList.displayName = 'IOUConfirmPage'; IOUConfirmationList.propTypes = propTypes; IOUConfirmationList.defaultProps = defaultProps; -export default compose(withOnyx({ - iou: {key: ONYXKEYS.IOU}, - myPersonalDetails: { - key: ONYXKEYS.MY_PERSONAL_DETAILS, - }, -}), withSafeAreaInsets, withWindowDimensions)(IOUConfirmationList); +export default compose( + withLocalize, + withSafeAreaInsets, + withWindowDimensions, + withOnyx({ + iou: {key: ONYXKEYS.IOU}, + myPersonalDetails: { + key: ONYXKEYS.MY_PERSONAL_DETAILS, + }, + }), +)(IOUConfirmationList); diff --git a/src/components/InvertedFlatList/index.js b/src/components/InvertedFlatList/index.js index 6f9a861cb8a1..2bf5a3844f9a 100644 --- a/src/components/InvertedFlatList/index.js +++ b/src/components/InvertedFlatList/index.js @@ -5,11 +5,15 @@ import React, { forwardRef, } from 'react'; import PropTypes from 'prop-types'; +import {FlatList} from 'react-native'; +import _ from 'underscore'; import BaseInvertedFlatList from './BaseInvertedFlatList'; const propTypes = { // Passed via forwardRef so we can access the FlatList ref - innerRef: PropTypes.func.isRequired, + innerRef: PropTypes.shape({ + current: PropTypes.instanceOf(FlatList), + }).isRequired, }; // This is copied from https://codesandbox.io/s/react-native-dsyse @@ -23,7 +27,12 @@ const InvertedFlatList = (props) => { }, []); useEffect(() => { - props.innerRef(ref.current); + if (!_.isFunction(props.innerRef)) { + // eslint-disable-next-line no-param-reassign + props.innerRef.current = ref.current; + } else { + props.innerRef(ref.current); + } }, []); useEffect(() => { diff --git a/src/components/OptionsSelector.js b/src/components/OptionsSelector.js old mode 100644 new mode 100755 index 422b7d72855d..597af214dfc3 --- a/src/components/OptionsSelector.js +++ b/src/components/OptionsSelector.js @@ -7,6 +7,7 @@ import OptionsList from './OptionsList'; import styles from '../styles/styles'; import themeColors from '../styles/themes/default'; import optionPropTypes from './optionPropTypes'; +import withLocalize, {withLocalizePropTypes} from './withLocalize'; const propTypes = { // Callback to fire when a row is tapped @@ -62,11 +63,13 @@ const propTypes = { // Whether to focus the textinput after an option is selected shouldFocusOnSelectRow: PropTypes.bool, + + ...withLocalizePropTypes, }; const defaultProps = { onSelectRow: () => {}, - placeholderText: 'Name, email, or phone number', + placeholderText: '', selectedOptions: [], headerMessage: '', canSelectMultipleOptions: false, @@ -195,7 +198,8 @@ class OptionsSelector extends Component { value={this.props.value} onChangeText={this.props.onChangeText} onKeyPress={this.handleKeyPress} - placeholder={this.props.placeholderText} + placeholder={this.props.placeholderText + || this.props.translate('optionsSelector.nameEmailOrPhoneNumber')} placeholderTextColor={themeColors.placeholderText} /> @@ -221,4 +225,4 @@ class OptionsSelector extends Component { OptionsSelector.defaultProps = defaultProps; OptionsSelector.propTypes = propTypes; -export default OptionsSelector; +export default withLocalize(OptionsSelector); diff --git a/src/components/RenderHTML.js b/src/components/RenderHTML.js old mode 100644 new mode 100755 index d2c7fa5bbe55..abeb396f6e3a --- a/src/components/RenderHTML.js +++ b/src/components/RenderHTML.js @@ -14,6 +14,7 @@ import AnchorForCommentsOnly from './AnchorForCommentsOnly'; import InlineCodeBlock from './InlineCodeBlock'; import AttachmentModal from './AttachmentModal'; import ThumbnailImage from './ThumbnailImage'; +import withLocalize from './withLocalize'; const MAX_IMG_DIMENSIONS = 512; @@ -96,7 +97,7 @@ function CodeRenderer({ ); } -function ImgRenderer({tnode}) { +function ImgRenderer({tnode, translate}) { const htmlAttribs = tnode.attributes; // There are two kinds of images that need to be displayed: @@ -134,7 +135,7 @@ function ImgRenderer({tnode}) { return ( @@ -163,7 +164,7 @@ ImgRenderer.model = defaultHTMLElementModels.img; const renderers = { a: AnchorRenderer, code: CodeRenderer, - img: ImgRenderer, + img: withLocalize(ImgRenderer), }; const propTypes = { diff --git a/src/components/TextInputFocusable/index.js b/src/components/TextInputFocusable/index.js old mode 100644 new mode 100755 index 89b5928742f9..a99e23c8ab78 --- a/src/components/TextInputFocusable/index.js +++ b/src/components/TextInputFocusable/index.js @@ -2,6 +2,7 @@ import React from 'react'; import {TextInput, StyleSheet} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; +import withLocalize, {withLocalizePropTypes} from '../withLocalize'; const propTypes = { // Maximum number of lines in the text input @@ -17,7 +18,7 @@ const propTypes = { onPasteFile: PropTypes.func, // A ref to forward to the text input - forwardedRef: PropTypes.func.isRequired, + forwardedRef: PropTypes.func, // General styles to apply to the text input // eslint-disable-next-line react/forbid-prop-types @@ -44,6 +45,8 @@ const propTypes = { /* Set focus to this component the first time it renders. Override this in case you need to set focus on one * field out of many, or when you want to disable autoFocus */ autoFocus: PropTypes.bool, + + ...withLocalizePropTypes, }; const defaultProps = { @@ -59,6 +62,7 @@ const defaultProps = { onDrop: () => {}, isDisabled: false, autoFocus: false, + forwardedRef: null, }; const IMAGE_EXTENSIONS = { @@ -178,15 +182,15 @@ class TextInputFocusable extends React.Component { .then((x) => { const extension = IMAGE_EXTENSIONS[x.type]; if (!extension) { - throw new Error('No extension found for mime type'); + throw new Error(this.props.translate('textInputFocusable.noExtentionFoundForMimeType')); } return new File([x], `pasted_image.${extension}`, {}); }) .then(this.props.onPasteFile) .catch((error) => { - console.debug(error); - alert(`There was a problem getting the image you pasted. \n${error.message}`); + const errorDesc = this.props.translate('textInputFocusable.problemGettingImageYouPasted'); + alert(`${errorDesc}. \n${error.message}`); }); } } @@ -235,7 +239,7 @@ class TextInputFocusable extends React.Component { TextInputFocusable.propTypes = propTypes; TextInputFocusable.defaultProps = defaultProps; -export default React.forwardRef((props, ref) => ( +export default withLocalize(React.forwardRef((props, ref) => ( /* eslint-disable-next-line react/jsx-props-no-spreading */ -)); +))); diff --git a/src/components/TextInputFocusable/index.native.js b/src/components/TextInputFocusable/index.native.js index f54704f2669b..fab67ddb7308 100644 --- a/src/components/TextInputFocusable/index.native.js +++ b/src/components/TextInputFocusable/index.native.js @@ -13,7 +13,7 @@ const propTypes = { shouldClear: PropTypes.bool, // A ref to forward to the text input - forwardedRef: PropTypes.func.isRequired, + forwardedRef: PropTypes.func, // When the input has cleared whoever owns this input should know about it onClear: PropTypes.func, @@ -31,6 +31,7 @@ const defaultProps = { onClear: () => {}, autoFocus: false, isDisabled: false, + forwardedRef: null, }; class TextInputFocusable extends React.Component { diff --git a/src/components/UnreadActionIndicator.js b/src/components/UnreadActionIndicator.js old mode 100644 new mode 100755 index 7c6789c0a66a..389413e4f38e --- a/src/components/UnreadActionIndicator.js +++ b/src/components/UnreadActionIndicator.js @@ -2,15 +2,18 @@ import React from 'react'; import {View} from 'react-native'; import styles from '../styles/styles'; import Text from './Text'; +import withLocalize, {withLocalizePropTypes} from './withLocalize'; -const UnreadActionIndicator = () => ( +const UnreadActionIndicator = props => ( - NEW + {props.translate('common.new')} ); +UnreadActionIndicator.propTypes = {...withLocalizePropTypes}; + UnreadActionIndicator.displayName = 'UnreadActionIndicator'; -export default UnreadActionIndicator; +export default withLocalize(UnreadActionIndicator); diff --git a/src/components/UpdateAppModal/BaseUpdateAppModal.js b/src/components/UpdateAppModal/BaseUpdateAppModal.js old mode 100644 new mode 100755 index e547c460e95f..f3c6280a16b4 --- a/src/components/UpdateAppModal/BaseUpdateAppModal.js +++ b/src/components/UpdateAppModal/BaseUpdateAppModal.js @@ -1,6 +1,7 @@ import React, {PureComponent} from 'react'; import {propTypes, defaultProps} from './UpdateAppModalPropTypes'; import ConfirmModal from '../ConfirmModal'; +import withLocalize from '../withLocalize'; class BaseUpdateAppModal extends PureComponent { constructor(props) { @@ -25,14 +26,13 @@ class BaseUpdateAppModal extends PureComponent { return ( <> this.setState({isModalOpen: false})} - prompt="A new version of Expensify.cash is available. - Update now or restart the app at a later time to download the latest changes." - confirmText="Update App" - cancelText="Cancel" + prompt={this.props.translate('baseUpdateAppModal.updatePrompt')} + confirmText={this.props.translate('baseUpdateAppModal.updateApp')} + cancelText={this.props.translate('common.cancel')} /> ); @@ -41,4 +41,4 @@ class BaseUpdateAppModal extends PureComponent { BaseUpdateAppModal.propTypes = propTypes; BaseUpdateAppModal.defaultProps = defaultProps; -export default BaseUpdateAppModal; +export default withLocalize(BaseUpdateAppModal); diff --git a/src/components/VideoChatButtonAndMenu.js b/src/components/VideoChatButtonAndMenu.js old mode 100644 new mode 100755 index e5419ce8acae..cf9069483904 --- a/src/components/VideoChatButtonAndMenu.js +++ b/src/components/VideoChatButtonAndMenu.js @@ -1,5 +1,5 @@ import React, {Component} from 'react'; -import {View, Pressable} from 'react-native'; +import {View, Pressable, Dimensions} from 'react-native'; import Icon from './Icon'; import {Phone} from './Icon/Expensicons'; import Popover from './Popover'; @@ -10,7 +10,14 @@ import GoogleMeetIcon from '../../assets/images/google-meet.svg'; import CONST from '../CONST'; import styles from '../styles/styles'; import themeColors from '../styles/themes/default'; -import withWindowDimensions from './withWindowDimensions'; +import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; +import withLocalize, {withLocalizePropTypes} from './withLocalize'; +import compose from '../libs/compose'; + +const propTypes = { + ...withLocalizePropTypes, + ...windowDimensionsPropTypes, +}; class VideoChatButtonAndMenu extends Component { constructor(props) { @@ -22,12 +29,12 @@ class VideoChatButtonAndMenu extends Component { this.menuItemData = [ { icon: ZoomIcon, - text: 'Zoom', + text: props.translate('videoChatButtonAndMenu.zoom'), onPress: () => openURLInNewTab(CONST.NEW_ZOOM_MEETING_URL), }, { icon: GoogleMeetIcon, - text: 'Google Meet', + text: props.translate('videoChatButtonAndMenu.googleMeet'), onPress: () => openURLInNewTab(CONST.NEW_GOOGLE_MEET_MEETING_URL), }, ].map(item => ({ @@ -44,6 +51,14 @@ class VideoChatButtonAndMenu extends Component { }; } + componentDidMount() { + Dimensions.addEventListener('change', this.measureVideoChatIconPosition); + } + + componentWillUnmount() { + Dimensions.removeEventListener('change', this.measureVideoChatIconPosition); + } + /** * Toggles the state variable isVideoChatMenuActive */ @@ -110,5 +125,9 @@ class VideoChatButtonAndMenu extends Component { } } +VideoChatButtonAndMenu.propTypes = propTypes; VideoChatButtonAndMenu.displayName = 'VideoChatButtonAndMenu'; -export default withWindowDimensions(VideoChatButtonAndMenu); +export default compose( + withWindowDimensions, + withLocalize, +)(VideoChatButtonAndMenu); diff --git a/src/components/WelcomeText.js b/src/components/WelcomeText.js old mode 100644 new mode 100755 index 2ccee7b079b5..8a192d13cb65 --- a/src/components/WelcomeText.js +++ b/src/components/WelcomeText.js @@ -2,11 +2,14 @@ import React from 'react'; import {Text} from 'react-native'; import PropTypes from 'prop-types'; import styles from '../styles/styles'; +import withLocalize, {withLocalizePropTypes} from './withLocalize'; const propTypes = { // Fontsize textSize: PropTypes.oneOf(['default', 'large']), + + ...withLocalizePropTypes, }; const defaultProps = { @@ -16,12 +19,12 @@ const defaultProps = { const WelcomeText = props => ( <> - With Expensify.cash, chat and payments are the same thing. + {props.translate('welcomeText.phrase1')} - Money talks. And now that chat and payments are in one place, it's also easy. + {props.translate('welcomeText.phrase2')} {' '} - Your payments get to you as fast as you can get your point across. + {props.translate('welcomeText.phrase3')} ); @@ -30,4 +33,4 @@ WelcomeText.displayName = 'WelcomeText'; WelcomeText.propTypes = propTypes; WelcomeText.defaultProps = defaultProps; -export default WelcomeText; +export default withLocalize(WelcomeText); diff --git a/src/components/withLocalize.js b/src/components/withLocalize.js old mode 100644 new mode 100755 index b18c99b37a39..9b10a7bc4d51 --- a/src/components/withLocalize.js +++ b/src/components/withLocalize.js @@ -10,19 +10,27 @@ import {toLocalPhone, fromLocalPhone} from '../libs/LocalePhoneNumber'; import numberFormat from '../libs/numberFormat'; const withLocalizePropTypes = { - // Translations functions using current User's preferred locale - translations: PropTypes.shape({ - translate: PropTypes.func.isRequired, - numberFormat: PropTypes.func.isRequired, - timestampToRelative: PropTypes.func.isRequired, - timestampToDateTime: PropTypes.func.isRequired, - toLocalPhone: PropTypes.func.isRequired, - fromLocalPhone: PropTypes.func.isRequired, - }), + // Returns translated string for given locale and phrase + translate: PropTypes.func.isRequired, + + // Formats number formatted according to locale and options + numberFormat: PropTypes.func.isRequired, + + // Converts a timestamp into a localized string representation that's relative to current moment in time + timestampToRelative: PropTypes.func.isRequired, + + // Formats a timestamp to local date and time string + timestampToDateTime: PropTypes.func.isRequired, + + // Returns a locally converted phone number without the country code + toLocalPhone: PropTypes.func.isRequired, + + // Returns an internationally converted phone number with the country code + fromLocalPhone: PropTypes.func.isRequired, }; function withLocalizeHOC(WrappedComponent) { - const withLocalize = (props) => { + const WithLocalize = (props) => { const translations = { translate: (phrase, variables) => translate(props.preferredLocale, phrase, variables), numberFormat: (number, options) => numberFormat(props.preferredLocale, number, options), @@ -39,15 +47,26 @@ function withLocalizeHOC(WrappedComponent) { ); }; - withLocalize.displayName = `withLocalize(${getComponentDisplayName(WrappedComponent)})`; - withLocalize.defaultProps = { + WithLocalize.displayName = `WithLocalize(${getComponentDisplayName(WrappedComponent)})`; + WithLocalize.propTypes = { + preferredLocale: PropTypes.string, + }; + WithLocalize.defaultProps = { preferredLocale: 'en', }; - return withLocalize; + return React.forwardRef((props, ref) => ( + // eslint-disable-next-line react/jsx-props-no-spreading + + )); } export default compose( withOnyx({ diff --git a/src/languages/en.js b/src/languages/en.js old mode 100644 new mode 100755 index ff1ccd6837b6..b6ea0f7f1801 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -1,4 +1,233 @@ +/* eslint-disable max-len */ export default { + common: { + cancel: 'Cancel', + upload: 'Upload', + yes: 'Yes', + no: 'No', + attachment: 'Attachment', + to: 'To', + optional: 'Optional', + split: 'Split', + new: 'NEW', + search: 'Search', + next: 'Next', + add: 'Add', + resend: 'Resend', + save: 'Save', + password: 'Password', + profile: 'Profile', + payments: 'Payments', + preferences: 'Preferences', + view: 'View', + not: 'Not', + signIn: 'Sign In', + continue: 'Continue', + phoneNumber: 'Phone Number', + email: 'Email', + and: 'and', + }, + attachmentPicker: { + cameraPermissionRequired: 'Camera Permission Required', + expensifyDoesntHaveAccessToCamera: 'Expensify.cash does not have access to your camera, please enable the permission and try again.', + attachmentError: 'Attachment Error', + errorWhileSelectingAttachment: 'An error occurred while selecting an attachment, please try again', + errorDuringAttachmentSelection: 'Error during attachment selection', + takePhoto: 'Take Photo', + chooseFromGallery: 'Choose from Gallery', + chooseDocument: 'Choose Document', + }, + textInputFocusable: { + noExtentionFoundForMimeType: 'No extension found for mime type', + problemGettingImageYouPasted: 'There was a problem getting the image you pasted', + }, + baseUpdateAppModal: { + updateApp: 'Update App', + updatePrompt: 'A new version of Expensify.cash is available.\nUpdate now or restart the app at a later time to download the latest changes.', + }, + iOUConfirmationList: { + whoPaid: 'WHO PAID?', + whoWasThere: 'WHO WAS THERE?', + whatsItFor: 'WHAT\'S IT FOR?', + }, + optionsSelector: { + nameEmailOrPhoneNumber: 'Name, email, or phone number', + }, + videoChatButtonAndMenu: { + zoom: 'Zoom', + googleMeet: 'Google Meet', + }, hello: 'Hello', phoneCountryCode: '1', + welcomeText: { + phrase1: 'With Expensify.cash, chat and payments are the same thing.', + phrase2: 'Money talks. And now that chat and payments are in one place, it\'s also easy.', + phrase3: 'Your payments get to you as fast as you can get your point across.', + }, + reportActionCompose: { + uploadAttachment: 'Upload Attachment', + addAttachment: 'Add Attachment', + writeSomething: 'Write something...', + youAppearToBeOffline: 'You appear to be offline.', + }, + reportActionContextMenu: { + copyToClipboard: 'Copy to Clipboard', + copied: 'Copied!', + copyLink: 'Copy Link', + markAsUnread: 'Mark as Unread', + editComment: 'Edit Comment', + deleteComment: 'Delete Comment', + }, + reportActionsView: { + beFirstPersonToComment: 'Be the first person to comment', + }, + reportTypingIndicator: { + isTyping: 'is typing...', + areTyping: 'are typing...', + multipleUsers: 'Multiple users', + }, + sidebarScreen: { + newChat: 'New Chat', + newGroup: 'New Group', + }, + iou: { + contacts: 'CONTACTS', + recents: 'RECENTS', + amount: 'Amount', + participants: 'Participants', + confirm: 'Confirm', + splitBill: 'Split Bill', + requestMoney: 'Request Money', + request: ({amount}) => `Request ${amount}`, + }, + loginField: { + addYourPhoneToSettleViaVenmo: 'Add your phone number to settle up via Venmo.', + numberHasNotBeenValidated: 'The number has not yet been validated. Click the button to resend the validation link via text.', + useYourPhoneToSettleViaVenmo: 'Use your phone number to settle up via Venmo.', + emailHasNotBeenValidated: 'The email has not yet been validated. Click the button to resend the validation link via text.', + }, + profilePage: { + uploadPhoto: 'Upload Photo', + removePhoto: 'Remove Photo', + profile: 'Profile', + editPhoto: 'Edit Photo', + tellUsAboutYourself: 'Tell us about yourself, we would love to get to know you!', + firstName: 'First Name', + john: 'John', + lastName: 'Last Name', + doe: 'Doe', + preferredPronouns: 'Preferred Pronouns', + selectYourPronouns: 'Select your pronouns', + selfSelectYourPronoun: 'Self-select your pronoun', + emailAddress: 'Email Address', + setMyTimezoneAutomatically: 'Set my timezone automatically', + timezone: 'Timezone', + }, + addSecondaryLoginPage: { + addPhoneNumber: 'Add Phone Number', + addEmailAddress: 'Add Email Address', + enterPreferredPhoneNumberToSendValidationLink: 'Enter your preferred phone number and password to send a validation link.', + enterPreferredEmailToSendValidationLink: 'Enter your preferred email address and password to send a validation link.', + sendValidation: 'Send Validation', + + }, + initialSettingsPage: { + settings: 'Settings', + signOut: 'Sign Out', + versionLetter: 'v', + changePassword: 'Change Password', + readTheTermsAndPrivacyPolicy: { + phrase1: 'Read the', + phrase2: 'terms of service', + phrase3: 'and', + phrase4: 'privacy policy', + }, + }, + passwordPage: { + changePassword: 'Change Password', + changingYourPasswordPrompt: 'Changing your password will update your password for both your Expensify.com\nand Expensify.cash accounts.', + currentPassword: 'Current Password', + newPassword: 'New Password', + newPasswordPrompt: 'New password must be different than your old password, have at least 8 characters,\n1 capital letter, 1 lowercase letter, 1 number.', + confirmNewPassword: 'Confirm New Password', + }, + paymentsPage: { + enterYourUsernameToGetPaidViaPayPal: 'Enter your username to get paid back via PayPal.', + payPalMe: 'PayPal.me/', + yourPayPalUsername: 'Your PayPal username', + addPayPalAccount: 'Add PayPal Account', + }, + preferencesPage: { + mostRecent: 'Most Recent', + mostRecentModeDescription: 'This will display all chats by default, sorted by most recent, with pinned items at the top', + focus: '#focus', + focusModeDescription: '#focus – This will only display unread and pinned chats, all sorted alphabetically.', + notifications: 'Notifications', + receiveRelevantFeatureUpdatesAndExpensifyNews: 'Receive relevant feature updates and Expensify news', + priorityMode: 'Priority Mode', + }, + signInPage: { + expensifyDotCash: 'Expensify.cash', + expensifyIsOpenSource: 'Expensify.cash is open source', + theCode: 'the code', + openJobs: 'open jobs', + }, + termsOfUse: { + phrase1: 'By logging in, you agree to the', + phrase2: 'terms of service', + phrase3: 'and', + phrase4: 'privacy policy', + phrase5: '. Money transmission is provided by Expensify Payments LLC (NMLS ID:2017010) pursuant to its', + phrase6: 'licenses', + }, + passwordForm: { + pleaseFillOutAllFields: 'Please fill out all fields', + forgot: 'Forgot?', + twoFactorCode: 'Two Factor Code', + requiredWhen2FAEnabled: 'Required when 2FA is enabled', + }, + loginForm: { + pleaseEnterEmailOrPhoneNumber: 'Please enter an email or phone number', + phoneOrEmail: 'Phone or Email', + enterYourPhoneOrEmail: 'Enter your phone or email:', + }, + resendValidationForm: { + linkHasBeenResent: 'Link has been re-sent', + accountUnvalidated: 'Account is unvalidated', + accountForgotPassword: 'Account forgot password: Sending reset password link.', + weSentYouMagicSignInLink: 'We\'ve sent you a magic sign in link – just click on it to log in!', + resendLink: 'Resend Link', + }, + detailsPage: { + details: 'Details', + localTime: 'Local Time', + }, + newGroupPage: { + createGroup: 'Create Group', + }, + notFound: { + chatYouLookingForCannotBeFound: 'The chat you are looking for cannot be found.', + getMeOutOfHere: 'Get me out of here', + }, + setPasswordPage: { + passwordCannotBeBlank: 'Password cannot be blank', + enterPassword: 'Enter a password', + setPassword: 'Set Password', + }, + attachmentView: { + unknownFilename: 'Unknown Filename', + }, + pronouns: { + heHimHis: 'He/him', + sheHerHers: 'She/her', + theyThemTheirs: 'They/them', + zeHirHirs: 'Ze/hir', + selfSelect: 'Self-select', + callMeByMyName: 'Call me by my name', + }, + cameraPermissionsNotGranted: 'Camera permissions not granted', + messages: { + noPhoneNumber: 'Please enter a phone number including the country code e.g +447814266907', + maxParticipantsReached: 'You\'ve reached the maximum number of participants for a group chat.', + }, }; diff --git a/src/libs/API.js b/src/libs/API.js index 2e9dbd0ae091..7c59a6908a31 100644 --- a/src/libs/API.js +++ b/src/libs/API.js @@ -556,6 +556,19 @@ function Report_TogglePinned(parameters) { return Network.post(commandName, parameters); } +/** + * @param {Object} parameters + * @param {Number} parameters.reportID + * @param {String} parameters.reportActionID + * @param {String} parameters.reportComment + * @return {Promise} + */ +function Report_EditComment(parameters) { + const commandName = 'Report_EditComment'; + requireParameters(['reportID', 'reportActionID', 'reportComment'], parameters, commandName); + return Network.post(commandName, parameters); +} + /** * @param {Object} parameters * @param {Number} parameters.accountID @@ -738,6 +751,7 @@ export { Report_AddComment, Report_GetHistory, Report_TogglePinned, + Report_EditComment, Report_UpdateLastRead, ResendValidateCode, ResetPassword, diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 4213a32779e6..5d2d795f5c48 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -1,13 +1,14 @@ +import _ from 'underscore'; import React from 'react'; import {createStackNavigator, CardStyleInterpolators} from '@react-navigation/stack'; import styles from '../../../styles/styles'; -import ROUTES from '../../../ROUTES'; import NewChatPage from '../../../pages/NewChatPage'; import NewGroupPage from '../../../pages/NewGroupPage'; import SearchPage from '../../../pages/SearchPage'; import DetailsPage from '../../../pages/DetailsPage'; import IOURequestPage from '../../../pages/iou/IOURequestPage'; import IOUBillPage from '../../../pages/iou/IOUBillPage'; +import IOUDetailsPage from '../../../pages/iou/IOUDetailsModal'; import SettingsInitialPage from '../../../pages/settings/InitialPage'; import SettingsProfilePage from '../../../pages/settings/Profile/ProfilePage'; import SettingsPreferencesPage from '../../../pages/settings/PreferencesPage'; @@ -15,18 +16,6 @@ import SettingsPasswordPage from '../../../pages/settings/PasswordPage'; import SettingsPaymentsPage from '../../../pages/settings/PaymentsPage'; import SettingsAddSecondaryLoginPage from '../../../pages/settings/AddSecondaryLoginPage'; import ReportParticipantsPage from '../../../pages/ReportParticipantsPage'; -import IOUDetailsModal from '../../../pages/iou/IOUDetailsModal'; - -// Setup the modal stack navigators so we only have to create them once -const SettingsModalStack = createStackNavigator(); -const NewChatModalStack = createStackNavigator(); -const NewGroupModalStack = createStackNavigator(); -const SearchModalStack = createStackNavigator(); -const DetailsModalStack = createStackNavigator(); -const ReportParticipantsModalStack = createStackNavigator(); -const IOURequestModalStack = createStackNavigator(); -const IOUBillModalStack = createStackNavigator(); -const IOUDetailsModalStack = createStackNavigator(); const defaultSubRouteOptions = { cardStyle: styles.navigationScreenCardStyle, @@ -34,172 +23,103 @@ const defaultSubRouteOptions = { cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS, }; -const IOUBillStackNavigator = () => ( - - ( + - -); + > + {_.map(screens, screen => ( + + ))} + + ); +} -const IOURequestModalStackNavigator = () => ( - - - -); +const IOUBillStackNavigator = createModalStackNavigator([{ + Component: IOUBillPage, + name: 'IOU_Bill_Root', +}]); -const IOUDetailsModalStackNavigator = () => ( - - - -); +const IOURequestModalStackNavigator = createModalStackNavigator([{ + Component: IOURequestPage, + name: 'IOU_Request_Root', +}]); -const DetailsModalStackNavigator = () => ( - - - -); +const IOUDetailsModalStackNavigator = createModalStackNavigator([{ + Component: IOUDetailsPage, + name: 'IOU_Details_Route', +}]); -const ReportParticipantsModalStackNavigator = () => ( - - - - -); +const DetailsModalStackNavigator = createModalStackNavigator([{ + Component: DetailsPage, + name: 'Details_Root', +}]); -const SearchModalStackNavigator = () => ( - - - -); +const ReportParticipantsModalStackNavigator = createModalStackNavigator([ + { + Component: ReportParticipantsPage, + name: 'ReportParticipants_Root', + }, + { + Component: DetailsPage, + name: 'ReportParticipants_Details', + }, +]); -const NewGroupModalStackNavigator = () => ( - - - -); +const SearchModalStackNavigator = createModalStackNavigator([{ + Component: SearchPage, + name: 'Search_Root', +}]); -const NewChatModalStackNavigator = () => ( - - - -); +const NewGroupModalStackNavigator = createModalStackNavigator([{ + Component: NewGroupPage, + name: 'NewGroup_Root', +}]); + +const NewChatModalStackNavigator = createModalStackNavigator([{ + Component: NewChatPage, + name: 'NewChat_Root', +}]); -const SettingsModalStackNavigator = () => ( - - - - - - - - -); +const SettingsModalStackNavigator = createModalStackNavigator([ + { + Component: SettingsInitialPage, + name: 'Settings_Root', + }, + { + Component: SettingsProfilePage, + name: 'Settings_Profile', + }, + { + Component: SettingsAddSecondaryLoginPage, + name: 'Settings_Add_Secondary_Login', + }, + { + Component: SettingsPreferencesPage, + name: 'Settings_Preferences', + }, + { + Component: SettingsPasswordPage, + name: 'Settings_Password', + }, + { + Component: SettingsPaymentsPage, + name: 'Settings_Payments', + }, +]); export { IOUBillStackNavigator, diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index d38acd01036c..567ddb9992ae 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -8,6 +8,7 @@ import {getDefaultAvatar} from './actions/PersonalDetails'; import ONYXKEYS from '../ONYXKEYS'; import CONST from '../CONST'; import {getReportParticipantsTitle} from './reportUtils'; +import {translate} from './translate'; import Permissions from './Permissions'; /** @@ -18,6 +19,7 @@ import Permissions from './Permissions'; let currentUserLogin; let countryCodeByIP; +let preferredLocale; // We are initializing a default avatar here so that we use the same default color for each user we are inviting. This // will update when the OptionsListUtils re-loads. But will stay the same color for the life of the JS session. @@ -130,6 +132,11 @@ Onyx.connect({ callback: val => countryCodeByIP = val || 1, }); +Onyx.connect({ + key: ONYXKEYS.PREFERRED_LOCALE, + callback: val => preferredLocale = val || 'en', +}); + /** * Searches for a match when provided with a value * @@ -451,12 +458,12 @@ function getSidebarOptions(reports, personalDetails, draftComments, activeReport */ function getHeaderMessage(hasSelectableOptions, hasUserToInvite, searchValue, maxParticipantsReached = false) { if (maxParticipantsReached) { - return CONST.MESSAGES.MAXIMUM_PARTICIPANTS_REACHED; + return translate(preferredLocale, 'messages.maxParticipantsReached'); } if (!hasSelectableOptions && !hasUserToInvite) { if (/^\d+$/.test(searchValue)) { - return CONST.MESSAGES.NO_PHONE_NUMBER; + return translate(preferredLocale, 'messages.noPhoneNumber'); } return searchValue; diff --git a/src/libs/Pusher/EventType.js b/src/libs/Pusher/EventType.js index 866c96110388..0810261af524 100644 --- a/src/libs/Pusher/EventType.js +++ b/src/libs/Pusher/EventType.js @@ -4,5 +4,6 @@ */ export default { REPORT_COMMENT: 'reportComment', + REPORT_COMMENT_EDIT: 'reportCommentEdit', REPORT_TOGGLE_PINNED: 'reportTogglePinned', }; diff --git a/src/libs/ReportScrollManager/index.js b/src/libs/ReportScrollManager/index.js new file mode 100644 index 000000000000..ddd34b1bf6e6 --- /dev/null +++ b/src/libs/ReportScrollManager/index.js @@ -0,0 +1,25 @@ +import React from 'react'; + +// This ref is created using React.createRef here because this function is used by a component that doesn't have access +// to the original ref. +const flatListRef = React.createRef(); + +/** + * Scroll to the provided index. On non-native implementations we do not want to scroll when we are scrolling because + * we are editing a comment. + * + * @param {Object} index + * @param {Boolean} isEditing + */ +function scrollToIndex(index, isEditing) { + if (isEditing) { + return; + } + + flatListRef.current.scrollToIndex(index); +} + +export { + flatListRef, + scrollToIndex, +}; diff --git a/src/libs/ReportScrollManager/index.native.js b/src/libs/ReportScrollManager/index.native.js new file mode 100644 index 000000000000..a52e78c5a9b5 --- /dev/null +++ b/src/libs/ReportScrollManager/index.native.js @@ -0,0 +1,19 @@ +import React from 'react'; + +// This ref is created using React.createRef here because this function is used by a component that doesn't have access +// to the original ref. +const flatListRef = React.createRef(); + +/** + * Scroll to the provided index. + * + * @param {Object} index + */ +function scrollToIndex(index) { + flatListRef.current.scrollToIndex(index); +} + +export { + flatListRef, + scrollToIndex, +}; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 2ae62b46f1f8..262e5e0a46c5 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -2,6 +2,7 @@ import moment from 'moment'; import _ from 'underscore'; import lodashGet from 'lodash/get'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; +import Str from 'expensify-common/lib/str'; import Onyx from 'react-native-onyx'; import ONYXKEYS from '../../ONYXKEYS'; import * as Pusher from '../Pusher/pusher'; @@ -447,6 +448,19 @@ function fetchIOUReportByID(iouReportID, chatReportID, shouldUpdateChatReport) { .catch(error => console.error(`Error fetching IOU Report ${iouReportID}: ${error}`)); } +/** + * Updates a report action's message to be a new value. + * + * @param {Number} reportID + * @param {Number} sequenceNumber + * @param {Object} message + */ +function updateReportActionMessage(reportID, sequenceNumber, message) { + const actionToMerge = {}; + actionToMerge[sequenceNumber] = {message: [message]}; + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, actionToMerge); +} + /** * Updates a report in the store with a new report action * @@ -598,6 +612,26 @@ function subscribeToUserEvents() { ); }); + // Live-update a report's actions when an 'edit comment' event is received. + Pusher.subscribe(pusherChannelName, Pusher.TYPE.REPORT_COMMENT_EDIT, (pushJSON) => { + Log.info( + `[Report] Handled ${Pusher.TYPE.REPORT_COMMENT_EDIT} event sent by Pusher`, true, { + reportActionID: pushJSON.reportActionID, + }, + ); + updateReportActionMessage(pushJSON.reportID, pushJSON.sequenceNumber, pushJSON.message); + }, false, + () => { + NetworkConnection.triggerReconnectionCallbacks('pusher re-subscribed to private user channel'); + }) + .catch((error) => { + Log.info( + '[Report] Failed to subscribe to Pusher channel', + true, + {error, pusherChannelName, eventName: Pusher.TYPE.REPORT_COMMENT_EDIT}, + ); + }); + // Live-update a report's pinned state when a 'report toggle pinned' event is received. Pusher.subscribe(pusherChannelName, Pusher.TYPE.REPORT_TOGGLE_PINNED, (pushJSON) => { Log.info( @@ -960,10 +994,11 @@ function addAction(reportID, text, file) { * is last read (meaning that the entire report history has been read) */ function updateLastReadActionID(reportID, sequenceNumber) { - // If we aren't specifying a sequenceNumber and have no maxSequenceNumber for this report then we should not update - // the last read. Most likely, we have just created the report and it has no comments. But we should err on the side - // of caution and do nothing in this case. - if (_.isUndefined(sequenceNumber) && _.isUndefined(reportMaxSequenceNumbers[reportID])) { + // If we aren't specifying a sequenceNumber and have no valid maxSequenceNumber for this report then we should not + // update the last read. Most likely, we have just created the report and it has no comments. But we should err on + // the side of caution and do nothing in this case. + if (_.isUndefined(sequenceNumber) + && (!reportMaxSequenceNumbers[reportID] && reportMaxSequenceNumbers[reportID] !== 0)) { return; } @@ -1067,6 +1102,49 @@ Onyx.connect({ // When the app reconnects from being offline, fetch all of the reports and their actions NetworkConnection.onReconnect(fetchAllReports); +/** + * Saves a new message for a comment. Marks the comment as edited, which will be reflected in the UI. + * + * @param {Number} reportID + * @param {Object} originalReportAction + * @param {String} htmlForNewComment + */ +function editReportComment(reportID, originalReportAction, htmlForNewComment) { + // Optimistically update the report action with the new message + const sequenceNumber = originalReportAction.sequenceNumber; + const newReportAction = {...originalReportAction}; + const actionToMerge = {}; + newReportAction.message[0].isEdited = true; + newReportAction.message[0].html = htmlForNewComment; + newReportAction.message[0].text = Str.stripHTML(htmlForNewComment); + actionToMerge[sequenceNumber] = newReportAction; + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, actionToMerge); + + // Persist the updated report comment + API.Report_EditComment({ + reportID, + reportActionID: originalReportAction.reportActionID, + reportComment: htmlForNewComment, + sequenceNumber, + }) + .catch(() => { + // If it fails, reset Onyx + actionToMerge[sequenceNumber] = originalReportAction; + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, actionToMerge); + }); +} + +/** + * Saves the draft for a comment report action. This will put the comment into "edit mode" + * + * @param {Number} reportID + * @param {Number} reportActionID + * @param {String} draftMessage + */ +function saveReportActionDraft(reportID, reportActionID, draftMessage) { + Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}_${reportActionID}`, draftMessage); +} + export { fetchAllReports, fetchActions, @@ -1083,6 +1161,8 @@ export { broadcastUserIsTyping, togglePinnedState, updateCurrentlyViewedReportID, + editReportComment, + saveReportActionDraft, getSimplifiedIOUReport, getSimplifiedReportObject, }; diff --git a/src/libs/toggleReportActionComposeView.js b/src/libs/toggleReportActionComposeView.js new file mode 100644 index 000000000000..3b43305ebafb --- /dev/null +++ b/src/libs/toggleReportActionComposeView.js @@ -0,0 +1,8 @@ +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '../ONYXKEYS'; + +export default (shouldShowComposeInput, isSmallScreenWidth) => { + if (isSmallScreenWidth) { + Onyx.merge(ONYXKEYS.SESSION, {shouldShowComposeInput}); + } +}; diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js old mode 100644 new mode 100755 index 7a99bbdd61cf..3fa3d0ba3bc6 --- a/src/pages/DetailsPage.js +++ b/src/pages/DetailsPage.js @@ -14,6 +14,8 @@ import HeaderWithCloseButton from '../components/HeaderWithCloseButton'; import Navigation from '../libs/Navigation/Navigation'; import ScreenWrapper from '../components/ScreenWrapper'; import personalDetailsPropType from './personalDetailsPropType'; +import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; +import compose from '../libs/compose'; const matchType = PropTypes.shape({ params: PropTypes.shape({ @@ -32,19 +34,20 @@ const propTypes = { // Route params route: matchType.isRequired, + + ...withLocalizePropTypes, }; -const DetailsPage = ({personalDetails, route}) => { +const DetailsPage = ({personalDetails, route, translate}) => { const details = personalDetails[route.params.login]; // If we have a reportID param this means that we // arrived here via the ParticipantsPage and should be allowed to navigate back to it const shouldShowBackButton = Boolean(route.params.reportID); - return ( Navigation.dismissModal()} @@ -74,7 +77,9 @@ const DetailsPage = ({personalDetails, route}) => { {details.login ? ( - {Str.isSMSLogin(details.login) ? 'Phone Number' : 'Email'} + {translate(Str.isSMSLogin(details.login) + ? 'common.phoneNumber' + : 'common.email')} {Str.isSMSLogin(details.login) @@ -86,7 +91,7 @@ const DetailsPage = ({personalDetails, route}) => { {details.pronouns ? ( - Preferred Pronouns + {translate('profilePage.preferredPronouns')} {details.pronouns} @@ -96,7 +101,7 @@ const DetailsPage = ({personalDetails, route}) => { {details.timezone ? ( - Local Time + {translate('detailsPage.localTime')} {moment().tz(details.timezone.selected).format('LT')} @@ -116,8 +121,11 @@ const DetailsPage = ({personalDetails, route}) => { DetailsPage.propTypes = propTypes; DetailsPage.displayName = 'DetailsPage'; -export default withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, - }, -})(DetailsPage); +export default compose( + withLocalize, + withOnyx({ + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS, + }, + }), +)(DetailsPage); diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js old mode 100644 new mode 100755 index f0aa0160c889..2eae67d9f2e6 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -12,6 +12,8 @@ import withWindowDimensions, {windowDimensionsPropTypes} from '../components/wit import HeaderWithCloseButton from '../components/HeaderWithCloseButton'; import Navigation from '../libs/Navigation/Navigation'; import ScreenWrapper from '../components/ScreenWrapper'; +import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; +import compose from '../libs/compose'; const personalDetailsPropTypes = PropTypes.shape({ // The login of the person (either email or phone number) @@ -23,6 +25,8 @@ const personalDetailsPropTypes = PropTypes.shape({ // This is either the user's full name, or their login if full name is an empty string displayName: PropTypes.string.isRequired, + + ...withLocalizePropTypes, }); const propTypes = { @@ -74,7 +78,7 @@ class NewChatPage extends Component { const sections = []; sections.push({ - title: 'CONTACTS', + title: this.props.translate('iou.contacts'), data: this.state.personalDetails, shouldShow: this.state.personalDetails.length > 0, indexOffset: sections.reduce((prev, {data}) => prev + data.length, 0), @@ -114,7 +118,7 @@ class NewChatPage extends Component { return ( Navigation.dismissModal(true)} /> @@ -152,14 +156,18 @@ class NewChatPage extends Component { NewChatPage.propTypes = propTypes; -export default withWindowDimensions(withOnyx({ - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, - }, - session: { - key: ONYXKEYS.SESSION, - }, -})(NewChatPage)); +export default compose( + withLocalize, + withWindowDimensions, + withOnyx({ + reports: { + key: ONYXKEYS.COLLECTION.REPORT, + }, + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS, + }, + session: { + key: ONYXKEYS.SESSION, + }, + }), +)(NewChatPage); diff --git a/src/pages/NewGroupPage.js b/src/pages/NewGroupPage.js old mode 100644 new mode 100755 index 3b3d6cad19ae..f1e83de73ae5 --- a/src/pages/NewGroupPage.js +++ b/src/pages/NewGroupPage.js @@ -14,6 +14,8 @@ import withWindowDimensions, {windowDimensionsPropTypes} from '../components/wit import HeaderWithCloseButton from '../components/HeaderWithCloseButton'; import ScreenWrapper from '../components/ScreenWrapper'; import Navigation from '../libs/Navigation/Navigation'; +import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; +import compose from '../libs/compose'; const personalDetailsPropTypes = PropTypes.shape({ // The login of the person (either email or phone number) @@ -43,6 +45,8 @@ const propTypes = { }).isRequired, ...windowDimensionsPropTypes, + + ...withLocalizePropTypes, }; class NewGroupPage extends Component { @@ -92,14 +96,14 @@ class NewGroupPage extends Component { } sections.push({ - title: 'RECENTS', + title: this.props.translate('iou.recents'), data: this.state.recentReports, shouldShow: this.state.recentReports.length > 0, indexOffset: sections.reduce((prev, {data}) => prev + data.length, 0), }); sections.push({ - title: 'CONTACTS', + title: this.props.translate('iou.contacts'), data: this.state.personalDetails, shouldShow: this.state.personalDetails.length > 0, indexOffset: sections.reduce((prev, {data}) => prev + data.length, 0), @@ -182,7 +186,7 @@ class NewGroupPage extends Component { return ( Navigation.dismissModal(true)} /> @@ -228,7 +232,7 @@ class NewGroupPage extends Component { ]} > - Create Group + {this.props.translate('newGroupPage.createGroup')} @@ -242,14 +246,18 @@ class NewGroupPage extends Component { NewGroupPage.propTypes = propTypes; -export default withWindowDimensions(withOnyx({ - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, - }, - session: { - key: ONYXKEYS.SESSION, - }, -})(NewGroupPage)); +export default compose( + withLocalize, + withWindowDimensions, + withOnyx({ + reports: { + key: ONYXKEYS.COLLECTION.REPORT, + }, + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS, + }, + session: { + key: ONYXKEYS.SESSION, + }, + }), +)(NewGroupPage); diff --git a/src/pages/NotFound.js b/src/pages/NotFound.js old mode 100644 new mode 100755 index fc73cd44a302..9372506327c6 --- a/src/pages/NotFound.js +++ b/src/pages/NotFound.js @@ -10,8 +10,9 @@ import styles from '../styles/styles'; import logo from '../../assets/images/expensify-logo_reversed.png'; import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; +import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; -const NotFound = () => ( +const NotFound = ({translations: {translate}}) => ( <> ( /> 404 - The chat you are looking for cannot be found. + {translate('notFound.chatYouLookingForCannotBeFound')} Navigation.navigate(ROUTES.HOME)} > - Get me out of here + {translate('notFound.getMeOutOfHere')} ); -export default NotFound; +NotFound.propTypes = {...withLocalizePropTypes}; + +export default withLocalize(NotFound); diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js old mode 100644 new mode 100755 index 697914b452ae..b1975196f760 --- a/src/pages/ReportParticipantsPage.js +++ b/src/pages/ReportParticipantsPage.js @@ -14,6 +14,8 @@ import ScreenWrapper from '../components/ScreenWrapper'; import OptionsList from '../components/OptionsList'; import ROUTES from '../ROUTES'; import personalDetailsPropType from './personalDetailsPropType'; +import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; +import compose from '../libs/compose'; const propTypes = { /* Onyx Props */ @@ -39,6 +41,8 @@ const propTypes = { reportID: PropTypes.string, }), }).isRequired, + + ...withLocalizePropTypes, }; /** @@ -68,13 +72,18 @@ const getAllParticipants = (report, personalDetails) => { }); }; -const ReportParticipantsPage = ({personalDetails, report, route}) => { +const ReportParticipantsPage = ({ + personalDetails, + report, + route, + translate, +}) => { const participants = getAllParticipants(report, personalDetails); return ( { ReportParticipantsPage.propTypes = propTypes; ReportParticipantsPage.displayName = 'ParticipantsPage'; -export default withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, - }, - report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, - }, -})(ReportParticipantsPage); +export default compose( + withLocalize, + withOnyx({ + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS, + }, + report: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, + }, + }), +)(ReportParticipantsPage); diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js old mode 100644 new mode 100755 index 339b4cfe28bf..2ba3ebc84e4a --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -15,6 +15,8 @@ import HeaderWithCloseButton from '../components/HeaderWithCloseButton'; import ScreenWrapper from '../components/ScreenWrapper'; import Timing from '../libs/actions/Timing'; import CONST from '../CONST'; +import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; +import compose from '../libs/compose'; const personalDetailsPropTypes = PropTypes.shape({ // The login of the person (either email or phone number) @@ -47,6 +49,8 @@ const propTypes = { /* Window Dimensions Props */ ...windowDimensionsPropTypes, + + ...withLocalizePropTypes, }; class SearchPage extends Component { @@ -86,7 +90,7 @@ class SearchPage extends Component { */ getSections() { const sections = [{ - title: 'RECENT', + title: this.props.translate('iou.recents'), data: this.state.recentReports.concat(this.state.personalDetails), shouldShow: true, indexOffset: 0, @@ -138,7 +142,7 @@ class SearchPage extends Component { return ( Navigation.dismissModal(true)} /> @@ -178,14 +182,18 @@ class SearchPage extends Component { SearchPage.propTypes = propTypes; SearchPage.displayName = 'SearchPage'; -export default withWindowDimensions(withOnyx({ - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, - }, - session: { - key: ONYXKEYS.SESSION, - }, -})(SearchPage)); +export default compose( + withLocalize, + withWindowDimensions, + withOnyx({ + reports: { + key: ONYXKEYS.COLLECTION.REPORT, + }, + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS, + }, + session: { + key: ONYXKEYS.SESSION, + }, + }), +)(SearchPage); diff --git a/src/pages/SetPasswordPage.js b/src/pages/SetPasswordPage.js old mode 100644 new mode 100755 index 8309ad5bd728..6e45d9ea353a --- a/src/pages/SetPasswordPage.js +++ b/src/pages/SetPasswordPage.js @@ -17,6 +17,8 @@ import ButtonWithLoader from '../components/ButtonWithLoader'; import themeColors from '../styles/themes/default'; import SignInPageLayout from './signin/SignInPageLayout'; import canFocusInputOnScreenFocus from '../libs/canFocusInputOnScreenFocus'; +import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; +import compose from '../libs/compose'; const propTypes = { /* Onyx Props */ @@ -41,6 +43,8 @@ const propTypes = { // The accountID and validateCode are passed via the URL route: validateLinkPropTypes, + + ...withLocalizePropTypes, }; const defaultProps = { @@ -69,7 +73,7 @@ class SetPasswordPage extends Component { validateAndSubmitForm() { if (!this.state.password.trim()) { this.setState({ - formError: 'Password cannot be blank', + formError: this.props.translate('setPasswordPage.passwordCannotBeBlank'), }); return; } @@ -89,7 +93,9 @@ class SetPasswordPage extends Component { - Enter a password: + + {this.props.translate('setPasswordPage.enterPassword')} + @@ -131,7 +137,10 @@ class SetPasswordPage extends Component { SetPasswordPage.propTypes = propTypes; SetPasswordPage.defaultProps = defaultProps; -export default withOnyx({ - credentials: {key: ONYXKEYS.CREDENTIALS}, - account: {key: ONYXKEYS.ACCOUNT}, -})(SetPasswordPage); +export default compose( + withLocalize, + withOnyx({ + credentials: {key: ONYXKEYS.CREDENTIALS}, + account: {key: ONYXKEYS.ACCOUNT}, + }), +)(SetPasswordPage); diff --git a/src/pages/home/report/EmojiPickerMenu/index.js b/src/pages/home/report/EmojiPickerMenu/index.js old mode 100644 new mode 100755 index c6dbedb14c6f..06d453009695 --- a/src/pages/home/report/EmojiPickerMenu/index.js +++ b/src/pages/home/report/EmojiPickerMenu/index.js @@ -9,6 +9,8 @@ import emojis from '../../../../../assets/emojis'; import EmojiPickerMenuItem from '../EmojiPickerMenuItem'; import TextInputFocusable from '../../../../components/TextInputFocusable'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; +import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; +import compose from '../../../../libs/compose'; const propTypes = { // Function to add the selected emoji to the main compose text input @@ -18,6 +20,8 @@ const propTypes = { forwardedRef: PropTypes.func, ...windowDimensionsPropTypes, + + ...withLocalizePropTypes, }; const defaultProps = { @@ -305,7 +309,7 @@ class EmojiPickerMenu extends Component { ( +export default compose( + withWindowDimensions, + withLocalize, +)(React.forwardRef((props, ref) => ( // eslint-disable-next-line react/jsx-props-no-spreading ))); diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js old mode 100644 new mode 100755 index dd28bcb37b4f..c590bc509d98 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -6,6 +6,7 @@ import { Pressable, InteractionManager, Text, + Dimensions, } from 'react-native'; import {withNavigationFocus} from '@react-navigation/compat'; import _ from 'underscore'; @@ -33,12 +34,13 @@ import compose from '../../../libs/compose'; import CreateMenu from '../../../components/CreateMenu'; import Popover from '../../../components/Popover'; import EmojiPickerMenu from './EmojiPickerMenu'; -import withWindowDimensions from '../../../components/withWindowDimensions'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; import withDrawerState from '../../../components/withDrawerState'; import getButtonState from '../../../libs/getButtonState'; import CONST from '../../../CONST'; import canFocusInputOnScreenFocus from '../../../libs/canFocusInputOnScreenFocus'; import variables from '../../../styles/variables'; +import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; import Permissions from '../../../libs/Permissions'; import Navigation from '../../../libs/Navigation/Navigation'; import ROUTES from '../../../ROUTES'; @@ -81,6 +83,8 @@ const propTypes = { isOffline: PropTypes.bool, }), + ...windowDimensionsPropTypes, + ...withLocalizePropTypes, }; const defaultProps = { @@ -108,7 +112,8 @@ class ReportActionCompose extends React.Component { this.comment = props.comment; this.shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus(); this.focusEmojiSearchInput = this.focusEmojiSearchInput.bind(this); - + this.measureEmojiPopoverAnchorPosition = this.measureEmojiPopoverAnchorPosition.bind(this); + this.emojiPopoverAnchor = null; this.emojiSearchInput = null; this.state = { @@ -126,6 +131,10 @@ class ReportActionCompose extends React.Component { }; } + componentDidMount() { + Dimensions.addEventListener('change', this.measureEmojiPopoverAnchorPosition); + } + componentDidUpdate(prevProps) { // We want to focus or refocus the input when a modal has been closed and the underlying screen is focused. // We avoid doing this on native platforms since the software keyboard popping @@ -137,6 +146,10 @@ class ReportActionCompose extends React.Component { } } + componentWillUnmount() { + Dimensions.removeEventListener('change', this.measureEmojiPopoverAnchorPosition); + } + /** * Updates the Highlight state of the composer * @@ -168,7 +181,7 @@ class ReportActionCompose extends React.Component { * Focus the composer text input */ focus() { - if (this.textInput) { + if (this.textInput && this.textInput.focus) { // There could be other animations running while we trigger manual focus. // This prevents focus from making those animations janky. InteractionManager.runAfterInteractions(() => { @@ -225,17 +238,24 @@ class ReportActionCompose extends React.Component { /** * Show the ReportActionContextMenu modal popover. * - * @param {Object} [event] - A press event. */ - showEmojiPicker(event) { + showEmojiPicker() { this.textInput.blur(); - this.state.emojiPopoverAnchorPosition = { - horizontal: event.nativeEvent.pageX, - vertical: event.nativeEvent.pageY, - }; this.setState({isEmojiPickerVisible: true}); } + /** + * This gets called onLayout to find the cooridnates of the Anchor for the Emoji Picker. + */ + measureEmojiPopoverAnchorPosition() { + if (this.emojiPopoverAnchor) { + this.emojiPopoverAnchor.measureInWindow((x, y) => this.setState({ + emojiPopoverAnchorPosition: {horizontal: x, vertical: y}, + })); + } + } + + /** * Hide the ReportActionContextMenu modal popover. */ @@ -304,7 +324,7 @@ class ReportActionCompose extends React.Component { ]} > { addAction(this.props.reportID, '', file); this.setTextInputShouldClear(false); @@ -337,7 +357,7 @@ class ReportActionCompose extends React.Component { hasMultipleParticipants ? { icon: Receipt, - text: 'Split Bill', + text: this.props.translate('iou.splitBill'), onSelected: () => { Navigation.navigate( ROUTES.getIouSplitRoute(this.props.reportID), @@ -346,7 +366,7 @@ class ReportActionCompose extends React.Component { } : { icon: MoneyCircle, - text: 'Request Money', + text: this.props.translate('iou.requestMoney'), onSelected: () => { Navigation.navigate( ROUTES.getIouRequestRoute(this.props.reportID), @@ -356,7 +376,7 @@ class ReportActionCompose extends React.Component { ] : []), { icon: Paperclip, - text: 'Add Attachment', + text: this.props.translate('reportActionCompose.addAttachment'), onSelected: () => { openPicker({ onPicked: (file) => { @@ -375,7 +395,7 @@ class ReportActionCompose extends React.Component { multiline ref={el => this.textInput = el} textAlignVertical="top" - placeholder="Write something..." + placeholder={this.props.translate('reportActionCompose.writeSomething')} placeholderTextColor={themeColors.placeholderText} onChangeText={this.updateComment} onKeyPress={this.triggerSubmitShortcut} @@ -433,6 +453,8 @@ class ReportActionCompose extends React.Component { styles.chatItemEmojiButton, getButtonBackgroundColorStyle(getButtonState(hovered, pressed)), ])} + ref={el => this.emojiPopoverAnchor = el} + onLayout={this.measureEmojiPopoverAnchorPosition} onPress={this.showEmojiPicker} > {({hovered, pressed}) => ( @@ -466,7 +488,7 @@ class ReportActionCompose extends React.Component { height={variables.iconSizeExtraSmall} /> - You appear to be offline. + {this.props.translate('reportActionCompose.youAppearToBeOffline')} @@ -483,6 +505,7 @@ export default compose( withWindowDimensions, withDrawerState, withNavigationFocus, + withLocalize, withOnyx({ comment: { key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, diff --git a/src/pages/home/report/ReportActionContextMenu.js b/src/pages/home/report/ReportActionContextMenu.js old mode 100644 new mode 100755 index 5edbd19eff68..92cbbd7df18c --- a/src/pages/home/report/ReportActionContextMenu.js +++ b/src/pages/home/report/ReportActionContextMenu.js @@ -3,15 +3,19 @@ import React from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; +import {withOnyx} from 'react-native-onyx'; import { Clipboard as ClipboardIcon, LinkCopy, Mail, Pencil, Trashcan, Checkmark, } from '../../../components/Icon/Expensicons'; import getReportActionContextMenuStyles from '../../../styles/getReportActionContextMenuStyles'; -import {setNewMarkerPosition, updateLastReadActionID} from '../../../libs/actions/Report'; +import {setNewMarkerPosition, updateLastReadActionID, saveReportActionDraft} from '../../../libs/actions/Report'; import ReportActionContextMenuItem from './ReportActionContextMenuItem'; import ReportActionPropTypes from './ReportActionPropTypes'; import Clipboard from '../../../libs/Clipboard'; +import compose from '../../../libs/compose'; import {isReportMessageAttachment} from '../../../libs/reportUtils'; +import ONYXKEYS from '../../../ONYXKEYS'; +import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; const propTypes = { // The ID of the report this report action is attached to. @@ -27,11 +31,27 @@ const propTypes = { // Controls the visibility of this component. isVisible: PropTypes.bool, + + // Draft message - if this is set the comment is in 'edit' mode + draftMessage: PropTypes.string, + + // Function to dismiss the popover containing this menu + hidePopover: PropTypes.func.isRequired, + + /* Onyx Props */ + // The session of the logged in person + session: PropTypes.shape({ + // Email of the logged in person + email: PropTypes.string, + }), + ...withLocalizePropTypes, }; const defaultProps = { isMini: false, isVisible: false, + session: {}, + draftMessage: '', }; class ReportActionContextMenu extends React.Component { @@ -42,9 +62,9 @@ class ReportActionContextMenu extends React.Component { this.CONTEXT_ACTIONS = [ // Copy to clipboard { - text: 'Copy to Clipboard', + text: this.props.translate('reportActionContextMenu.copyToClipboard'), icon: ClipboardIcon, - successText: 'Copied!', + successText: this.props.translate('reportActionContextMenu.copied'), successIcon: Checkmark, shouldShow: true, @@ -67,14 +87,14 @@ class ReportActionContextMenu extends React.Component { }, { - text: 'Copy Link', + text: this.props.translate('reportActionContextMenu.copyLink'), icon: LinkCopy, shouldShow: false, onPress: () => {}, }, { - text: 'Mark as Unread', + text: this.props.translate('reportActionContextMenu.markAsUnread'), icon: Mail, successIcon: Checkmark, shouldShow: true, @@ -85,14 +105,23 @@ class ReportActionContextMenu extends React.Component { }, { - text: 'Edit Comment', + text: this.props.translate('reportActionContextMenu.editComment'), icon: Pencil, - shouldShow: false, - onPress: () => {}, + shouldShow: this.props.reportAction.actorEmail === this.props.session.email + && !isReportMessageAttachment(this.getActionText()) + && this.props.reportAction.reportActionID, + onPress: () => { + this.props.hidePopover(); + saveReportActionDraft( + this.props.reportID, + this.props.reportAction.reportActionID, + _.isEmpty(this.props.draftMessage) ? this.getActionText() : '', + ); + }, }, { - text: 'Delete Comment', + text: this.props.translate('reportActionContextMenu.deleteComment'), icon: Trashcan, shouldShow: false, onPress: () => {}, @@ -100,6 +129,18 @@ class ReportActionContextMenu extends React.Component { ]; this.wrapperStyle = getReportActionContextMenuStyles(this.props.isMini); + + this.getActionText = this.getActionText.bind(this); + } + + /** + * Gets the text (not HTML) portion of the message in an action. + * + * @return {String} + */ + getActionText() { + const message = _.last(lodashGet(this.props.reportAction, 'message', null)); + return lodashGet(message, 'text', ''); } render() { @@ -113,7 +154,7 @@ class ReportActionContextMenu extends React.Component { successText={contextAction.successText} isMini={this.props.isMini} key={contextAction.text} - onPress={contextAction.onPress} + onPress={() => contextAction.onPress(this.props.reportAction)} /> ))} @@ -124,4 +165,11 @@ class ReportActionContextMenu extends React.Component { ReportActionContextMenu.propTypes = propTypes; ReportActionContextMenu.defaultProps = defaultProps; -export default ReportActionContextMenu; +export default compose( + withLocalize, + withOnyx({ + session: { + key: ONYXKEYS.SESSION, + }, + }), +)(ReportActionContextMenu); diff --git a/src/pages/home/report/ReportActionContextMenuItem.js b/src/pages/home/report/ReportActionContextMenuItem.js index 590510650b14..77406ef4d041 100644 --- a/src/pages/home/report/ReportActionContextMenuItem.js +++ b/src/pages/home/report/ReportActionContextMenuItem.js @@ -86,7 +86,6 @@ class ReportActionContextMenuItem extends Component { } } - ReportActionContextMenuItem.propTypes = propTypes; ReportActionContextMenuItem.defaultProps = defaultProps; ReportActionContextMenuItem.displayName = 'ReportActionContextMenuItem'; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index cda621eda20f..aa7498b05632 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -2,6 +2,8 @@ import _ from 'underscore'; import React, {Component} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; +import {withOnyx} from 'react-native-onyx'; +import ONYXKEYS from '../../../ONYXKEYS'; import ReportActionPropTypes from './ReportActionPropTypes'; import { getReportActionItemStyle, @@ -16,6 +18,7 @@ import ReportActionContextMenu from './ReportActionContextMenu'; import ReportActionItemIOUAction from '../../../components/ReportActionItemIOUAction'; import ReportActionItemMessage from './ReportActionItemMessage'; import UnreadActionIndicator from '../../../components/UnreadActionIndicator'; +import ReportActionItemMessageEdit from './ReportActionItemMessageEdit'; const propTypes = { // The ID of the report this action is on. @@ -36,11 +39,19 @@ const propTypes = { // Should we display the new indicator on top of the comment? shouldDisplayNewIndicator: PropTypes.bool.isRequired, + // Position index of the report action in the overall report FlatList view + index: PropTypes.number.isRequired, + + /* --- Onyx Props --- */ + // Draft message - if this is set the comment is in 'edit' mode + draftMessage: PropTypes.string, + // Runs when the view enclosing the chat message lays out indicating it has rendered onLayout: PropTypes.func.isRequired, }; const defaultProps = { + draftMessage: '', hasOutstandingIOU: false, }; @@ -65,9 +76,11 @@ class ReportActionItem extends Component { shouldComponentUpdate(nextProps, nextState) { return this.state.isPopoverVisible !== nextState.isPopoverVisible || this.props.displayAsGroup !== nextProps.displayAsGroup + || this.props.draftMessage !== nextProps.draftMessage || this.props.isMostRecentIOUReportAction !== nextProps.isMostRecentIOUReportAction || this.props.hasOutstandingIOU !== nextProps.hasOutstandingIOU || (this.props.shouldDisplayNewIndicator !== nextProps.shouldDisplayNewIndicator) + || this.props.shouldDisplayNewIndicator !== nextProps.shouldDisplayNewIndicator || !_.isEqual(this.props.action, nextProps.action); } @@ -102,15 +115,27 @@ class ReportActionItem extends Component { } render() { - const children = this.props.action.actionName === 'IOU' - ? ( + let children; + if (this.props.action.actionName === 'IOU') { + children = ( - ) - : ; + ); + } else { + children = !this.props.draftMessage + ? + : ( + + ); + } return ( @@ -119,7 +144,10 @@ class ReportActionItem extends Component { {this.props.shouldDisplayNewIndicator && ( )} - + {!this.props.displayAsGroup ? ( @@ -140,6 +168,8 @@ class ReportActionItem extends Component { hovered && !this.state.isPopoverVisible } + draftMessage={this.props.draftMessage} + hidePopover={this.hidePopover} isMini /> @@ -154,6 +184,7 @@ class ReportActionItem extends Component { isVisible reportID={this.props.reportID} reportAction={this.props.action} + hidePopover={this.hidePopover} /> )} > @@ -161,6 +192,8 @@ class ReportActionItem extends Component { isVisible reportID={this.props.reportID} reportAction={this.props.action} + draftMessage={this.props.draftMessage} + hidePopover={this.hidePopover} /> @@ -173,4 +206,9 @@ class ReportActionItem extends Component { ReportActionItem.propTypes = propTypes; ReportActionItem.defaultProps = defaultProps; -export default ReportActionItem; + +export default withOnyx({ + draftMessage: { + key: ({reportID, action}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}_${action.reportActionID}`, + }, +})(ReportActionItem); diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index a94cfebdb49c..d512f04b33f3 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import Str from 'expensify-common/lib/str'; import ReportActionFragmentPropTypes from './ReportActionFragmentPropTypes'; import styles from '../../../styles/styles'; +import variables from '../../../styles/variables'; import themeColors from '../../../styles/themes/default'; import RenderHTML from '../../../components/RenderHTML'; import Text from '../../../components/Text'; @@ -48,10 +49,23 @@ class ReportActionItemFragment extends React.PureComponent { } // Only render HTML if we have html in the fragment - return fragment.html !== fragment.text ? ( - - ) : ( - {Str.htmlDecode(fragment.text)} + return ( + + {fragment.html !== fragment.text ? ( + + ) : ( + {Str.htmlDecode(fragment.text)} + )} + {fragment.isEdited && ( + + (edited) + + )} + ); case 'TEXT': return ( diff --git a/src/pages/home/report/ReportActionItemMessageEdit.js b/src/pages/home/report/ReportActionItemMessageEdit.js new file mode 100644 index 000000000000..3ad2abd25ce9 --- /dev/null +++ b/src/pages/home/report/ReportActionItemMessageEdit.js @@ -0,0 +1,129 @@ +import React from 'react'; +import {View, Pressable, Text} from 'react-native'; +import PropTypes from 'prop-types'; +import _ from 'underscore'; +import ReportActionPropTypes from './ReportActionPropTypes'; +import styles from '../../../styles/styles'; +import TextInputFocusable from '../../../components/TextInputFocusable'; +import {editReportComment, saveReportActionDraft} from '../../../libs/actions/Report'; +import {scrollToIndex} from '../../../libs/ReportScrollManager'; +import toggleReportActionComposeView from '../../../libs/toggleReportActionComposeView'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; + +const propTypes = { + // All the data of the action + action: PropTypes.shape(ReportActionPropTypes).isRequired, + + // Draft message + draftMessage: PropTypes.string.isRequired, + + // ReportID that holds the comment we're editing + reportID: PropTypes.number.isRequired, + + // Position index of the report action in the overall report FlatList view + index: PropTypes.number.isRequired, + + /* Window Dimensions Props */ + ...windowDimensionsPropTypes, +}; + +class ReportActionItemMessageEdit extends React.Component { + constructor(props) { + super(props); + this.updateDraft = this.updateDraft.bind(this); + this.deleteDraft = this.deleteDraft.bind(this); + this.debouncedSaveDraft = _.debounce(this.debouncedSaveDraft.bind(this), 1000, true); + this.publishDraft = this.publishDraft.bind(this); + this.triggerSaveOrCancel = this.triggerSaveOrCancel.bind(this); + + this.state = { + draft: this.props.draftMessage, + }; + } + + /** + * Update the value of the draft in Onyx + * + * @param {String} newDraft + */ + updateDraft(newDraft) { + const trimmedNewDraft = newDraft.trim(); + this.setState({draft: trimmedNewDraft}); + this.debouncedSaveDraft(trimmedNewDraft); + } + + /** + * Delete the draft of the comment being edited. This will take the comment out of "edit mode" with the old content. + */ + deleteDraft() { + saveReportActionDraft(this.props.reportID, this.props.action.reportActionID, ''); + toggleReportActionComposeView(true, this.props.isSmallScreenWidth); + } + + /** + * Save the draft of the comment. This debounced so that we're not ceaselessly saving your edit. Saving the draft + * allows one to navigate somewhere else and come back to the comment and still have it in edit mode. + */ + debouncedSaveDraft() { + saveReportActionDraft(this.props.reportID, this.props.action.reportActionID, this.state.draft); + } + + /** + * Save the draft of the comment to be the new comment message. This will take the comment out of "edit mode" with + * the new content. + */ + publishDraft() { + editReportComment(this.props.reportID, this.props.action, this.state.draft); + this.deleteDraft(); + } + + /** + * Key event handlers that short cut to saving/canceling. + * + * @param {Event} e + */ + triggerSaveOrCancel(e) { + if (e && e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + this.publishDraft(); + } else if (e && e.key === 'Escape') { + e.preventDefault(); + this.deleteDraft(); + } + } + + render() { + return ( + + { + scrollToIndex({animated: true, index: this.props.index}, true); + toggleReportActionComposeView(false); + }} + autoFocus + /> + + + + Cancel + + + + + Save Changes + + + + + ); + } +} + +ReportActionItemMessageEdit.propTypes = propTypes; +export default withWindowDimensions(ReportActionItemMessageEdit); diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js old mode 100644 new mode 100755 index f35c06f833f0..c95594c7a9bc --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -30,6 +30,8 @@ import themeColors from '../../../styles/themes/default'; import compose from '../../../libs/compose'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; import withDrawerState, {withDrawerPropTypes} from '../../../components/withDrawerState'; +import {flatListRef, scrollToIndex} from '../../../libs/ReportScrollManager'; +import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; const propTypes = { // The ID of the report actions will be created for @@ -63,6 +65,7 @@ const propTypes = { ...windowDimensionsPropTypes, ...withDrawerPropTypes, + ...withLocalizePropTypes, }; const defaultProps = { @@ -297,9 +300,7 @@ class ReportActionsView extends React.Component { * scroll the list to the end. As a report can contain non-message actions, we should confirm that list data exists. */ scrollToListBottom() { - if (this.actionListElement) { - this.actionListElement.scrollToIndex({animated: false, index: 0}); - } + scrollToIndex({animated: false, index: 0}); updateLastReadActionID(this.props.reportID); } @@ -358,6 +359,7 @@ class ReportActionsView extends React.Component { shouldDisplayNewIndicator={shouldDisplayNewIndicator} isMostRecentIOUReportAction={item.action.sequenceNumber === this.mostRecentIOUReportSequenceNumber} hasOutstandingIOU={this.props.report.hasOutstandingIOU} + index={index} onLayout={this.recordTimeToMeasureItemLayout} /> ); @@ -373,14 +375,16 @@ class ReportActionsView extends React.Component { if (_.size(this.props.reportActions) === 1) { return ( - Be the first person to comment! + + {this.props.translate('reportActionsView.beFirstPersonToComment')} + ); } return ( this.actionListElement = el} + ref={flatListRef} data={this.sortedReportActions} renderItem={this.renderItem} CellRendererComponent={this.renderCell} @@ -392,6 +396,7 @@ class ReportActionsView extends React.Component { ListFooterComponent={this.state.isLoadingMoreChats ? : null} + keyboardShouldPersistTaps="handled" /> ); } @@ -403,6 +408,7 @@ ReportActionsView.defaultProps = defaultProps; export default compose( withWindowDimensions, withDrawerState, + withLocalize, withOnyx({ report: { key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, diff --git a/src/pages/home/report/ReportTypingIndicator.js b/src/pages/home/report/ReportTypingIndicator.js old mode 100644 new mode 100755 index 7936d7dfc7b8..c0a70b824064 --- a/src/pages/home/report/ReportTypingIndicator.js +++ b/src/pages/home/report/ReportTypingIndicator.js @@ -7,10 +7,13 @@ import compose from '../../../libs/compose'; import ONYXKEYS from '../../../ONYXKEYS'; import styles from '../../../styles/styles'; import {getDisplayName} from '../../../libs/actions/PersonalDetails'; +import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; const propTypes = { // Key-value pairs of user logins and whether or not they are typing. Keys are logins. userTypingStatuses: PropTypes.objectOf(PropTypes.bool), + + ...withLocalizePropTypes, }; const defaultProps = { @@ -56,7 +59,7 @@ class ReportTypingIndicator extends React.Component { ]} > {getDisplayName(this.state.usersTyping[0])} - {' is typing...'} + {` ${this.props.translate('reportTypingIndicator.isTyping')}`} ); @@ -69,9 +72,9 @@ class ReportTypingIndicator extends React.Component { ]} > {getDisplayName(this.state.usersTyping[0])} - {' and '} + {` ${this.props.translate('common.and')} `} {getDisplayName(this.state.usersTyping[1])} - {' are typing...'} + {` ${this.props.translate('reportTypingIndicator.areTyping')}`} ); @@ -83,8 +86,10 @@ class ReportTypingIndicator extends React.Component { styles.chatItemComposeSecondaryRowOffset, ]} > - Multiple users - {' are typing...'} + + {this.props.translate('reportTypingIndicator.multipleUsers')} + + {` ${this.props.translate('reportTypingIndicator.areTyping')}`} ); @@ -97,6 +102,7 @@ ReportTypingIndicator.defaultProps = defaultProps; ReportTypingIndicator.displayName = 'ReportTypingIndicator'; export default compose( + withLocalize, withOnyx({ userTypingStatuses: { key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING}${reportID}`, diff --git a/src/pages/home/report/ReportView.js b/src/pages/home/report/ReportView.js index b00a8fb50628..f6d132a451ae 100644 --- a/src/pages/home/report/ReportView.js +++ b/src/pages/home/report/ReportView.js @@ -1,31 +1,53 @@ import React from 'react'; import {Keyboard, View} from 'react-native'; import PropTypes from 'prop-types'; +import {withOnyx} from 'react-native-onyx'; import ReportActionsView from './ReportActionsView'; import ReportActionCompose from './ReportActionCompose'; import {addAction} from '../../../libs/actions/Report'; import KeyboardSpacer from '../../../components/KeyboardSpacer'; import styles from '../../../styles/styles'; import SwipeableView from '../../../components/SwipeableView'; +import ONYXKEYS from '../../../ONYXKEYS'; const propTypes = { /* The ID of the report the selected report */ reportID: PropTypes.number.isRequired, + + /* Onyx Keys */ + // Whether or not to show the Compose Input + session: PropTypes.shape({ + shouldShowComposeInput: PropTypes.bool, + }), +}; + +const defaultProps = { + session: { + shouldShowComposeInput: true, + }, }; -const ReportView = ({reportID}) => ( +const ReportView = ({reportID, session}) => ( - Keyboard.dismiss()}> - addAction(reportID, text)} - reportID={reportID} - /> - + {session.shouldShowComposeInput && ( + Keyboard.dismiss()}> + addAction(reportID, text)} + reportID={reportID} + /> + + )} ); ReportView.propTypes = propTypes; -export default ReportView; +ReportView.defaultProps = defaultProps; + +export default withOnyx({ + session: { + key: ONYXKEYS.SESSION, + }, +})(ReportView); diff --git a/src/pages/home/sidebar/SidebarScreen.js b/src/pages/home/sidebar/SidebarScreen.js old mode 100644 new mode 100755 index ff4ef2ee630c..1e90b7aa98a4 --- a/src/pages/home/sidebar/SidebarScreen.js +++ b/src/pages/home/sidebar/SidebarScreen.js @@ -10,6 +10,8 @@ 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 { ChatBubble, Users, @@ -21,6 +23,8 @@ import Permissions from '../../../libs/Permissions'; const propTypes = { // propTypes for withWindowDimensions ...windowDimensionsPropTypes, + + ...withLocalizePropTypes, }; class SidebarScreen extends Component { @@ -101,25 +105,25 @@ class SidebarScreen extends Component { menuItems={[ { icon: ChatBubble, - text: 'New Chat', + text: this.props.translate('sidebarScreen.newChat'), onSelected: () => Navigation.navigate(ROUTES.NEW_CHAT), }, ...(Permissions.canUseIOU() ? [ { icon: MoneyCircle, - text: 'Request Money', + text: this.props.translate('iou.requestMoney'), onSelected: () => Navigation.navigate(ROUTES.IOU_REQUEST), }, ] : []), { icon: Users, - text: 'New Group', + text: this.props.translate('sidebarScreen.newGroup'), onSelected: () => Navigation.navigate(ROUTES.NEW_GROUP), }, ...(Permissions.canUseIOU() ? [ { icon: Receipt, - text: 'Split Bill', + text: this.props.translate('iou.splitBill'), onSelected: () => Navigation.navigate(ROUTES.IOU_BILL), }, ] : []), @@ -133,4 +137,7 @@ class SidebarScreen extends Component { } SidebarScreen.propTypes = propTypes; -export default withWindowDimensions(SidebarScreen); +export default compose( + withLocalize, + withWindowDimensions, +)(SidebarScreen); diff --git a/src/pages/iou/IOUDetailsPage.js b/src/pages/iou/IOUDetailsPage.js new file mode 100644 index 000000000000..083be379e774 --- /dev/null +++ b/src/pages/iou/IOUDetailsPage.js @@ -0,0 +1,12 @@ +import React from 'react'; +import IOUDetailsModal from './IOUDetailsModal'; +import ScreenWrapper from '../../components/ScreenWrapper'; + +export default props => ( + + {() => ( + // eslint-disable-next-line react/jsx-props-no-spreading + + )} + +); diff --git a/src/pages/iou/IOUModal.js b/src/pages/iou/IOUModal.js old mode 100644 new mode 100755 index 9da9ae2025ca..2095c84e8172 --- a/src/pages/iou/IOUModal.js +++ b/src/pages/iou/IOUModal.js @@ -13,6 +13,8 @@ import {createIOUSplit, createIOUTransaction, getPreferredCurrency} from '../../ import {Close, BackArrow} from '../../components/Icon/Expensicons'; import Navigation from '../../libs/Navigation/Navigation'; import ONYXKEYS from '../../ONYXKEYS'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import compose from '../../libs/compose'; import {getPersonalDetailsForLogins} from '../../libs/OptionsListUtils'; /** @@ -48,6 +50,8 @@ const propTypes = { // Avatar url of participant avatar: PropTypes.string, }).isRequired, + + ...withLocalizePropTypes, }; const defaultProps = { @@ -59,9 +63,9 @@ const defaultProps = { // Determines type of step to display within Modal, value provides the title for that page. const Steps = { - IOUAmount: 'Amount', - IOUParticipants: 'Participants', - IOUConfirm: 'Confirm', + IOUAmount: 'iou.amount', + IOUParticipants: 'iou.participants', + IOUConfirm: 'iou.confirm', }; class IOUModal extends Component { @@ -122,12 +126,15 @@ class IOUModal extends Component { getTitleForStep() { const currentStepIndex = this.state.currentStepIndex; if (currentStepIndex === 1 || currentStepIndex === 2) { - return `${this.props.hasMultipleParticipants ? 'Split' : 'Request'} $${this.state.amount}`; + return `${this.props.hasMultipleParticipants + ? this.props.translate('common.split') + : this.props.translate('iou.request', {amount: this.state.amount})}`; } if (currentStepIndex === 0) { - return this.props.hasMultipleParticipants ? 'Split Bill' : 'Request Money'; + return this.props.translate(this.props.hasMultipleParticipants ? 'iou.splitBill' : 'iou.requestMoney'); } - return this.steps[currentStepIndex] || ''; + + return this.props.translate(this.steps[currentStepIndex]) || ''; } addParticipants(participants) { @@ -278,17 +285,20 @@ IOUModal.propTypes = propTypes; IOUModal.defaultProps = defaultProps; IOUModal.displayName = 'IOUModal'; -export default withOnyx({ - report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, - }, - iousReport: { - key: ONYXKEYS.COLLECTION.REPORT_IOUS, - }, - iou: { - key: ONYXKEYS.IOU, - }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, - }, -})(IOUModal); +export default compose( + withLocalize, + withOnyx({ + report: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, + }, + iousReport: { + key: ONYXKEYS.COLLECTION.REPORT_IOUS, + }, + iou: { + key: ONYXKEYS.IOU, + }, + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS, + }, + }), +)(IOUModal); diff --git a/src/pages/iou/steps/IOUAmountPage.js b/src/pages/iou/steps/IOUAmountPage.js old mode 100644 new mode 100755 index e266b6359451..507a9f9624f6 --- a/src/pages/iou/steps/IOUAmountPage.js +++ b/src/pages/iou/steps/IOUAmountPage.js @@ -13,6 +13,8 @@ import themeColors from '../../../styles/themes/default'; import BigNumberPad from '../../../components/BigNumberPad'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; import TextInputAutoWidth from '../../../components/TextInputAutoWidth'; +import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; +import compose from '../../../libs/compose'; const propTypes = { // Callback to inform parent modal of success @@ -36,6 +38,8 @@ const propTypes = { // Whether or not the IOU step is loading (retrieving users preferred currency) loading: PropTypes.bool, }), + + ...withLocalizePropTypes, }; const defaultProps = { @@ -129,7 +133,7 @@ class IOUAmountPage extends React.Component { disabled={this.state.amount.length === 0} > - Next + {this.props.translate('common.next')} @@ -141,6 +145,10 @@ IOUAmountPage.displayName = 'IOUAmountPage'; IOUAmountPage.propTypes = propTypes; IOUAmountPage.defaultProps = defaultProps; -export default withWindowDimensions(withOnyx({ - iou: {key: ONYXKEYS.IOU}, -})(IOUAmountPage)); +export default compose( + withWindowDimensions, + withLocalize, + withOnyx({ + iou: {key: ONYXKEYS.IOU}, + }), +)(IOUAmountPage); diff --git a/src/pages/iou/steps/IOUParticipantsPage/IOUParticipantsRequest.js b/src/pages/iou/steps/IOUParticipantsPage/IOUParticipantsRequest.js old mode 100644 new mode 100755 index 2ade0725a778..bfb83d80fcf2 --- a/src/pages/iou/steps/IOUParticipantsPage/IOUParticipantsRequest.js +++ b/src/pages/iou/steps/IOUParticipantsPage/IOUParticipantsRequest.js @@ -4,6 +4,8 @@ import {withOnyx} from 'react-native-onyx'; import {getNewChatOptions} from '../../../../libs/OptionsListUtils'; import OptionsSelector from '../../../../components/OptionsSelector'; import ONYXKEYS from '../../../../ONYXKEYS'; +import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; +import compose from '../../../../libs/compose'; const personalDetailsPropTypes = PropTypes.shape({ // The login of the person (either email or phone number) @@ -32,6 +34,8 @@ const propTypes = { reportID: PropTypes.number, reportName: PropTypes.string, }).isRequired, + + ...withLocalizePropTypes, }; class IOUParticipantsRequest extends Component { @@ -61,7 +65,7 @@ class IOUParticipantsRequest extends Component { getSections() { const sections = []; sections.push({ - title: 'CONTACTS', + title: this.props.translate('iou.contacts'), data: this.state.personalDetails, shouldShow: this.state.personalDetails.length > 0, indexOffset: 0, @@ -120,11 +124,14 @@ class IOUParticipantsRequest extends Component { IOUParticipantsRequest.displayName = 'IOUParticipantsRequest'; IOUParticipantsRequest.propTypes = propTypes; -export default withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, - }, - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, -})(IOUParticipantsRequest); +export default compose( + withLocalize, + withOnyx({ + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS, + }, + reports: { + key: ONYXKEYS.COLLECTION.REPORT, + }, + }), +)(IOUParticipantsRequest); diff --git a/src/pages/iou/steps/IOUParticipantsPage/IOUParticipantsSplit.js b/src/pages/iou/steps/IOUParticipantsPage/IOUParticipantsSplit.js old mode 100644 new mode 100755 index 0aa0cafe7ed5..564539b4177d --- a/src/pages/iou/steps/IOUParticipantsPage/IOUParticipantsSplit.js +++ b/src/pages/iou/steps/IOUParticipantsPage/IOUParticipantsSplit.js @@ -12,6 +12,8 @@ import styles from '../../../../styles/styles'; import OptionsSelector from '../../../../components/OptionsSelector'; import {getNewGroupOptions} from '../../../../libs/OptionsListUtils'; import CONST from '../../../../CONST'; +import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; +import compose from '../../../../libs/compose'; const personalDetailsPropTypes = PropTypes.shape({ // The login of the person (either email or phone number) @@ -54,6 +56,8 @@ const propTypes = { reportID: PropTypes.number, reportName: PropTypes.string, }).isRequired, + + ...withLocalizePropTypes, }; const defaultProps = { @@ -106,7 +110,7 @@ class IOUParticipantsSplit extends Component { } sections.push({ - title: 'RECENTS', + title: this.props.translate('iou.recents'), data: this.state.recentReports, shouldShow: this.state.recentReports.length > 0, @@ -116,7 +120,7 @@ class IOUParticipantsSplit extends Component { }); sections.push({ - title: 'CONTACTS', + title: this.props.translate('iou.contacts'), data: this.state.personalDetails, shouldShow: this.state.personalDetails.length > 0, @@ -191,7 +195,7 @@ class IOUParticipantsSplit extends Component { return ( - To + {this.props.translate('common.to')} - Next + {this.props.translate('common.next')} @@ -247,11 +251,14 @@ IOUParticipantsSplit.displayName = 'IOUParticipantsSplit'; IOUParticipantsSplit.propTypes = propTypes; IOUParticipantsSplit.defaultProps = defaultProps; -export default withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, - }, - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, -})(IOUParticipantsSplit); +export default compose( + withLocalize, + withOnyx({ + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS, + }, + reports: { + key: ONYXKEYS.COLLECTION.REPORT, + }, + }), +)(IOUParticipantsSplit); diff --git a/src/pages/settings/AddSecondaryLoginPage.js b/src/pages/settings/AddSecondaryLoginPage.js old mode 100644 new mode 100755 index 4527654d97f3..56ee6ef0e9f8 --- a/src/pages/settings/AddSecondaryLoginPage.js +++ b/src/pages/settings/AddSecondaryLoginPage.js @@ -15,6 +15,8 @@ import ButtonWithLoader from '../../components/ButtonWithLoader'; import ROUTES from '../../ROUTES'; import CONST from '../../CONST'; import KeyboardAvoidingView from '../../libs/KeyboardAvoidingView'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import compose from '../../libs/compose'; const propTypes = { /* Onyx Props */ @@ -48,6 +50,8 @@ const propTypes = { type: PropTypes.string, }), }), + + ...withLocalizePropTypes, }; const defaultProps = { @@ -99,7 +103,9 @@ class AddSecondaryLoginPage extends Component { Navigation.navigate(ROUTES.SETTINGS_PROFILE)} onCloseButtonPress={() => Navigation.dismissModal()} @@ -107,13 +113,15 @@ class AddSecondaryLoginPage extends Component { - {this.formType === CONST.LOGIN_TYPE.PHONE - ? 'Enter your preferred phone number and password to send a validation link.' - : 'Enter your preferred email address and password to send a validation link.'} + {this.props.translate(this.formType === CONST.LOGIN_TYPE.PHONE + ? 'addSecondaryLoginPage.enterPreferredPhoneNumberToSendValidationLink' + : 'addSecondaryLoginPage.enterPreferredEmailToSendValidationLink')} - {this.formType === CONST.LOGIN_TYPE.PHONE ? 'Phone Number' : 'Email Address'} + {this.props.translate(this.formType === CONST.LOGIN_TYPE.PHONE + ? 'common.phoneNumber' + : 'profilePage.emailAddress')} - Password + + {this.props.translate('addSecondaryLoginPage.password')} + @@ -162,8 +172,11 @@ AddSecondaryLoginPage.propTypes = propTypes; AddSecondaryLoginPage.defaultProps = defaultProps; AddSecondaryLoginPage.displayName = 'AddSecondaryLoginPage'; -export default withOnyx({ - user: { - key: ONYXKEYS.USER, - }, -})(AddSecondaryLoginPage); +export default compose( + withLocalize, + withOnyx({ + user: { + key: ONYXKEYS.USER, + }, + }), +)(AddSecondaryLoginPage); diff --git a/src/pages/settings/InitialPage.js b/src/pages/settings/InitialPage.js old mode 100644 new mode 100755 index 9ae4ce9ab941..f8976accd8df --- a/src/pages/settings/InitialPage.js +++ b/src/pages/settings/InitialPage.js @@ -20,6 +20,8 @@ import MenuItem from '../../components/MenuItem'; import ROUTES from '../../ROUTES'; import openURLInNewTab from '../../libs/openURLInNewTab'; import CONST from '../../CONST'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import compose from '../../libs/compose'; const propTypes = { /* Onyx Props */ @@ -43,6 +45,8 @@ const propTypes = { // Email of the logged in person email: PropTypes.string, }), + + ...withLocalizePropTypes, }; const defaultProps = { @@ -53,28 +57,28 @@ const defaultProps = { const menuItems = [ { - title: 'Profile', + translationKey: 'common.profile', icon: Profile, action: () => { Navigation.navigate(ROUTES.SETTINGS_PROFILE); }, }, { - title: 'Preferences', + translationKey: 'common.preferences', icon: Gear, action: () => { Navigation.navigate(ROUTES.SETTINGS_PREFERENCES); }, }, { - title: 'Change Password', + translationKey: 'initialSettingsPage.changePassword', icon: Lock, action: () => { Navigation.navigate(ROUTES.SETTINGS_PASSWORD); }, }, { - title: 'Payments', + translationKey: 'common.payments', icon: Wallet, action: () => { Navigation.navigate(ROUTES.SETTINGS_PAYMENTS); }, }, { - title: 'Sign Out', + translationKey: 'initialSettingsPage.signOut', icon: SignOut, action: signOut, }, @@ -84,6 +88,7 @@ const InitialSettingsPage = ({ myPersonalDetails, network, session, + translate, }) => { // On the very first sign in or after clearing storage these // details will not be present on the first render so we'll just @@ -94,7 +99,7 @@ const InitialSettingsPage = ({ return ( Navigation.dismissModal(true)} /> ( item.action()} shouldShowRightArrow @@ -136,26 +141,26 @@ const InitialSettingsPage = ({ - v + {translate('initialSettingsPage.versionLetter')} {version} - Read the + {translate('initialSettingsPage.readTheTermsAndPrivacyPolicy.phrase1')} {' '} openURLInNewTab(CONST.TERMS_URL)} > - terms of service + {translate('initialSettingsPage.readTheTermsAndPrivacyPolicy.phrase2')} {' '} - and + {translate('initialSettingsPage.readTheTermsAndPrivacyPolicy.phrase3')} {' '} openURLInNewTab(CONST.PRIVACY_URL)} > - privacy policy + {translate('initialSettingsPage.readTheTermsAndPrivacyPolicy.phrase4')} . @@ -169,14 +174,17 @@ InitialSettingsPage.propTypes = propTypes; InitialSettingsPage.defaultProps = defaultProps; InitialSettingsPage.displayName = 'InitialSettingsPage'; -export default withOnyx({ - myPersonalDetails: { - key: ONYXKEYS.MY_PERSONAL_DETAILS, - }, - network: { - key: ONYXKEYS.NETWORK, - }, - session: { - key: ONYXKEYS.SESSION, - }, -})(InitialSettingsPage); +export default compose( + withLocalize, + withOnyx({ + myPersonalDetails: { + key: ONYXKEYS.MY_PERSONAL_DETAILS, + }, + network: { + key: ONYXKEYS.NETWORK, + }, + session: { + key: ONYXKEYS.SESSION, + }, + }), +)(InitialSettingsPage); diff --git a/src/pages/settings/PasswordPage.js b/src/pages/settings/PasswordPage.js old mode 100644 new mode 100755 index e01689d4d69d..d43d50e4911d --- a/src/pages/settings/PasswordPage.js +++ b/src/pages/settings/PasswordPage.js @@ -14,6 +14,8 @@ import ONYXKEYS from '../../ONYXKEYS'; import CONST from '../../CONST'; import ButtonWithLoader from '../../components/ButtonWithLoader'; import {changePassword} from '../../libs/actions/User'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import compose from '../../libs/compose'; const propTypes = { /* Onyx Props */ @@ -28,6 +30,8 @@ const propTypes = { // Whether or not a sign on form is loading (being submitted) loading: PropTypes.bool, }), + + ...withLocalizePropTypes, }; const defaultProps = { @@ -64,7 +68,7 @@ class PasswordPage extends Component { return ( Navigation.navigate(ROUTES.SETTINGS)} onCloseButtonPress={() => Navigation.dismissModal(true)} @@ -72,11 +76,12 @@ class PasswordPage extends Component { - Changing your password will update your password for both your Expensify.com - and Expensify.cash accounts. + {this.props.translate('passwordPage.changingYourPasswordPrompt')} - Current Password* + + {`${this.props.translate('passwordPage.currentPassword')}*`} + - New Password* + + {`${this.props.translate('passwordPage.newPassword')}*`} + {this.state.isPasswordRequirementsVisible && ( - New password must be different than your old password, have at least 8 characters, - 1 capital letter, 1 lowercase letter, 1 number. + {this.props.translate('passwordPage.newPasswordPrompt')} )} - Confirm New Password* + + {`${this.props.translate('passwordPage.confirmNewPassword')}*`} + @@ -145,8 +153,11 @@ PasswordPage.displayName = 'PasswordPage'; PasswordPage.propTypes = propTypes; PasswordPage.defaultProps = defaultProps; -export default withOnyx({ - account: { - key: ONYXKEYS.ACCOUNT, - }, -})(PasswordPage); +export default compose( + withLocalize, + withOnyx({ + account: { + key: ONYXKEYS.ACCOUNT, + }, + }), +)(PasswordPage); diff --git a/src/pages/settings/PaymentsPage.js b/src/pages/settings/PaymentsPage.js old mode 100644 new mode 100755 index 37db4ba2d203..e62f6cc31282 --- a/src/pages/settings/PaymentsPage.js +++ b/src/pages/settings/PaymentsPage.js @@ -12,9 +12,13 @@ import NameValuePair from '../../libs/actions/NameValuePair'; import {getUserDetails} from '../../libs/actions/User'; import Navigation from '../../libs/Navigation/Navigation'; import styles from '../../styles/styles'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import compose from '../../libs/compose'; const propTypes = { payPalMeUsername: PropTypes.string, + + ...withLocalizePropTypes, }; const defaultProps = { @@ -54,7 +58,7 @@ class PaymentsPage extends React.Component { return ( Navigation.navigate(ROUTES.SETTINGS)} onCloseButtonPress={() => Navigation.dismissModal(true)} @@ -62,15 +66,15 @@ class PaymentsPage extends React.Component { - Enter your username to get paid back via PayPal. + {this.props.translate('paymentsPage.enterYourUsernameToGetPaidViaPayPal')} - PayPal.me/ + {this.props.translate('paymentsPage.payPalMe')} this.setState({payPalMeUsername: text})} /> @@ -84,7 +88,7 @@ class PaymentsPage extends React.Component { ]} > - Add PayPal Account + {this.props.translate('paymentsPage.addPayPalAccount')} @@ -97,8 +101,11 @@ PaymentsPage.propTypes = propTypes; PaymentsPage.defaultProps = defaultProps; PaymentsPage.displayName = 'PaymentsPage'; -export default withOnyx({ - payPalMeUsername: { - key: ONYXKEYS.NVP_PAYPAL_ME_ADDRESS, - }, -})(PaymentsPage); +export default compose( + withLocalize, + withOnyx({ + payPalMeUsername: { + key: ONYXKEYS.NVP_PAYPAL_ME_ADDRESS, + }, + }), +)(PaymentsPage); diff --git a/src/pages/settings/PreferencesPage.js b/src/pages/settings/PreferencesPage.js old mode 100644 new mode 100755 index aaa9b480d7ce..6881c8a40a52 --- a/src/pages/settings/PreferencesPage.js +++ b/src/pages/settings/PreferencesPage.js @@ -17,6 +17,8 @@ import {setExpensifyNewsStatus} from '../../libs/actions/User'; import ScreenWrapper from '../../components/ScreenWrapper'; import Switch from '../../components/Switch'; import Picker from '../../components/Picker'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import compose from '../../libs/compose'; const propTypes = { // The chat priority mode @@ -27,6 +29,8 @@ const propTypes = { // Whether or not the user is subscribed to news updates expensifyNewsStatus: PropTypes.bool, }), + + ...withLocalizePropTypes, }; const defaultProps = { @@ -34,74 +38,80 @@ const defaultProps = { user: {}, }; -const priorityModes = { - default: { - value: CONST.PRIORITY_MODE.DEFAULT, - label: 'Most Recent', - description: 'This will display all chats by default, sorted by most recent, with pinned items at the top', - }, - gsd: { - value: CONST.PRIORITY_MODE.GSD, - label: '#focus', - description: '#focus – This will only display unread and pinned chats, all sorted alphabetically.', - }, -}; - +const PreferencesPage = ({priorityMode, user, translate}) => { + const priorityModes = { + default: { + value: CONST.PRIORITY_MODE.DEFAULT, + label: translate('preferencesPage.mostRecent'), + description: translate('preferencesPage.mostRecentModeDescription'), + }, + gsd: { + value: CONST.PRIORITY_MODE.GSD, + label: translate('preferencesPage.focus'), + description: translate('preferencesPage.focusModeDescription'), + }, + }; -const PreferencesPage = ({priorityMode, user}) => ( - - Navigation.navigate(ROUTES.SETTINGS)} - onCloseButtonPress={() => Navigation.dismissModal(true)} - /> - - - Notifications - - - - Receive relevant feature updates and Expensify news - + return ( + + Navigation.navigate(ROUTES.SETTINGS)} + onCloseButtonPress={() => Navigation.dismissModal(true)} + /> + + + + {translate('preferencesPage.notifications')} + + + + + {translate('preferencesPage.receiveRelevantFeatureUpdatesAndExpensifyNews')} + + + + + - - + {translate('preferencesPage.priorityMode')} + + + NameValuePair.set(CONST.NVP.PRIORITY_MODE, mode, ONYXKEYS.NVP_PRIORITY_MODE) + } + items={Object.values(priorityModes)} + value={priorityMode} + icon={() => } /> + + {priorityModes[priorityMode].description} + - - Priority Mode - - - NameValuePair.set(CONST.NVP.PRIORITY_MODE, mode, ONYXKEYS.NVP_PRIORITY_MODE) - } - items={Object.values(priorityModes)} - value={priorityMode} - icon={() => } - /> - - - {priorityModes[priorityMode].description} - - - -); + + ); +}; PreferencesPage.propTypes = propTypes; PreferencesPage.defaultProps = defaultProps; PreferencesPage.displayName = 'PreferencesPage'; -export default withOnyx({ - priorityMode: { - key: ONYXKEYS.NVP_PRIORITY_MODE, - }, - user: { - key: ONYXKEYS.USER, - }, -})(PreferencesPage); +export default compose( + withLocalize, + withOnyx({ + priorityMode: { + key: ONYXKEYS.NVP_PRIORITY_MODE, + }, + user: { + key: ONYXKEYS.USER, + }, + }), +)(PreferencesPage); diff --git a/src/pages/settings/Profile/LoginField.js b/src/pages/settings/Profile/LoginField.js old mode 100644 new mode 100755 index 9342f4e7b059..43d1829471e5 --- a/src/pages/settings/Profile/LoginField.js +++ b/src/pages/settings/Profile/LoginField.js @@ -10,6 +10,7 @@ import ROUTES from '../../../ROUTES'; import CONST from '../../../CONST'; import Navigation from '../../../libs/Navigation/Navigation'; import {resendValidateCode} from '../../../libs/actions/User'; +import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; const propTypes = { // Label to display on login form @@ -26,9 +27,11 @@ const propTypes = { // Date of when login was validated validatedDate: PropTypes.string, }).isRequired, + + ...withLocalizePropTypes, }; -export default class LoginField extends Component { +class LoginField extends Component { constructor(props) { super(props); this.state = { @@ -61,21 +64,20 @@ export default class LoginField extends Component { if (this.props.type === CONST.LOGIN_TYPE.PHONE) { // No phone number if (!this.props.login.partnerUserID) { - note = 'Add your phone number to settle up via Venmo.'; + note = this.props.translate('loginField.addYourPhoneToSettleViaVenmo'); // Has unvalidated phone number } else if (!this.props.login.validatedDate) { - // eslint-disable-next-line max-len - note = 'The number has not yet been validated. Click the button to resend the validation link via text.'; + note = this.props.translate('loginField.numberHasNotBeenValidated'); // Has verified phone number } else { - note = 'Use your phone number to settle up via Venmo.'; + note = this.props.translate('loginField.useYourPhoneToSettleViaVenmo'); } // Has unvalidated email } else if (this.props.login.partnerUserID && !this.props.login.validatedDate) { - note = 'The email has not yet been validated. Click the button to resend the validation link via text.'; + note = this.props.translate('loginField.emailHasNotBeenValidated'); } return ( @@ -92,7 +94,7 @@ export default class LoginField extends Component { - {`Add ${this.props.label}`} + {`${this.props.translate('common.add')} ${this.props.label}`} @@ -111,7 +113,7 @@ export default class LoginField extends Component { ) : ( - Resend + {this.props.translate('common.resend')} )} @@ -130,3 +132,5 @@ export default class LoginField extends Component { LoginField.propTypes = propTypes; LoginField.displayName = 'LoginField'; + +export default withLocalize(LoginField); diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js old mode 100644 new mode 100755 index 9844cfb4396d..294ca6c081cd --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -28,6 +28,8 @@ import {DownArrow, Upload, Trashcan} from '../../../components/Icon/Expensicons' import AttachmentPicker from '../../../components/AttachmentPicker'; import CreateMenu from '../../../components/CreateMenu'; import Picker from '../../../components/Picker'; +import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; +import compose from '../../../libs/compose'; const propTypes = { /* Onyx Props */ @@ -74,6 +76,8 @@ const propTypes = { validatedDate: PropTypes.string, })), }), + + ...withLocalizePropTypes, }; const defaultProps = { @@ -98,14 +102,14 @@ class ProfilePage extends Component { pronouns, timezone = {}, } = props.myPersonalDetails; - const pronounsList = Object.values(CONST.PRONOUNS); + const pronounsList = Object.values(this.props.translate('pronouns')); let currentUserPronouns = pronouns; let initialSelfSelectedPronouns = ''; // This handles populating the self-selected pronouns in the form if (pronouns && !pronounsList.includes(pronouns)) { - currentUserPronouns = CONST.PRONOUNS.SELF_SELECT; + currentUserPronouns = this.props.translate('pronouns.selfSelect'); initialSelfSelectedPronouns = pronouns; } @@ -195,7 +199,9 @@ class ProfilePage extends Component { setPersonalDetails({ firstName, lastName, - pronouns: pronouns === CONST.PRONOUNS.SELF_SELECT ? selfSelectedPronouns : pronouns, + pronouns: pronouns === this.props.translate('pronouns.selfSelect') + ? selfSelectedPronouns + : pronouns, timezone: { automatic: isAutomaticTimezone, selected: selectedTimezone, @@ -213,7 +219,7 @@ class ProfilePage extends Component { const menuItems = [ { icon: Upload, - text: 'Upload Photo', + text: this.props.translate('profilePage.uploadPhoto'), onSelected: () => { openPicker({ onPicked: setAvatar, @@ -226,7 +232,7 @@ class ProfilePage extends Component { if (!this.props.myPersonalDetails.avatar.includes('/images/avatars/avatar')) { menuItems.push({ icon: Trashcan, - text: 'Remove Photo', + text: this.props.translate('profilePage.removePhoto'), onSelected: () => { deleteAvatar(this.props.myPersonalDetails.login); }, @@ -251,7 +257,7 @@ class ProfilePage extends Component { return ( Navigation.navigate(ROUTES.SETTINGS)} onCloseButtonPress={() => Navigation.dismissModal(true)} @@ -272,7 +278,7 @@ class ProfilePage extends Component { - Edit Photo + {this.props.translate('profilePage.editPhoto')} @@ -290,58 +296,74 @@ class ProfilePage extends Component { )} - Tell us about yourself, we would love to get to know you! + {this.props.translate('profilePage.tellUsAboutYourself')} - First Name + + {this.props.translate('profilePage.firstName')} + this.setState({firstName})} - placeholder="John" + placeholder={this.props.translate('profilePage.john')} placeholderTextColor={themeColors.placeholderText} /> - Last Name + + {this.props.translate('profilePage.lastName')} + this.setState({lastName})} - placeholder="Doe" + placeholder={this.props.translate('profilePage.doe')} placeholderTextColor={themeColors.placeholderText} /> - Preferred Pronouns + + {this.props.translate('profilePage.preferredPronouns')} + this.setState({pronouns, selfSelectedPronouns: ''})} items={this.pronounDropdownValues} placeholder={{ value: '', - label: 'Select your pronouns', + label: this.props.translate('profilePage.selectYourPronouns'), }} value={this.state.pronouns} icon={() => } /> - {this.state.pronouns === CONST.PRONOUNS.SELF_SELECT && ( + {this.state.pronouns === this.props.translate('pronouns.selfSelect') && ( this.setState({selfSelectedPronouns})} - placeholder="Self-select your pronoun" + placeholder={this.props.translate('profilePage.selfSelectYourPronoun')} placeholderTextColor={themeColors.placeholderText} /> )} - - + + - Timezone + + {this.props.translate('profilePage.timezone')} + this.setState({selectedTimezone})} items={timezones} @@ -352,7 +374,7 @@ class ProfilePage extends Component { /> @@ -370,7 +392,7 @@ class ProfilePage extends Component { ]} > - Save + {this.props.translate('common.save')} @@ -383,11 +405,14 @@ ProfilePage.propTypes = propTypes; ProfilePage.defaultProps = defaultProps; ProfilePage.displayName = 'ProfilePage'; -export default withOnyx({ - myPersonalDetails: { - key: ONYXKEYS.MY_PERSONAL_DETAILS, - }, - user: { - key: ONYXKEYS.USER, - }, -})(ProfilePage); +export default compose( + withLocalize, + withOnyx({ + myPersonalDetails: { + key: ONYXKEYS.MY_PERSONAL_DETAILS, + }, + user: { + key: ONYXKEYS.USER, + }, + }), +)(ProfilePage); diff --git a/src/pages/signin/ChangeExpensifyLoginLink.js b/src/pages/signin/ChangeExpensifyLoginLink.js old mode 100644 new mode 100755 index a973d81da0f5..598dd35e10e4 --- a/src/pages/signin/ChangeExpensifyLoginLink.js +++ b/src/pages/signin/ChangeExpensifyLoginLink.js @@ -7,6 +7,8 @@ import styles from '../../styles/styles'; import {restartSignin} from '../../libs/actions/Session'; import themeColors from '../../styles/themes/default'; import ONYXKEYS from '../../ONYXKEYS'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import compose from '../../libs/compose'; const propTypes = { // The credentials of the logged in person @@ -14,9 +16,11 @@ const propTypes = { // The email the user logged in with login: PropTypes.string, }).isRequired, + + ...withLocalizePropTypes, }; -const ChangeExpensifyLoginLink = ({credentials}) => ( +const ChangeExpensifyLoginLink = ({credentials, translate}) => ( ( underlayColor={themeColors.componentBG} > - Not  + {translate('common.not')} +   {Str.removeSMSDomain(credentials.login)} ? @@ -35,6 +40,9 @@ const ChangeExpensifyLoginLink = ({credentials}) => ( ChangeExpensifyLoginLink.propTypes = propTypes; ChangeExpensifyLoginLink.displayName = 'ChangeExpensifyLoginLink'; -export default withOnyx({ - credentials: {key: ONYXKEYS.CREDENTIALS}, -})(ChangeExpensifyLoginLink); +export default compose( + withLocalize, + withOnyx({ + credentials: {key: ONYXKEYS.CREDENTIALS}, + }), +)(ChangeExpensifyLoginLink); diff --git a/src/pages/signin/LoginForm.js b/src/pages/signin/LoginForm.js old mode 100644 new mode 100755 index d37e22431193..95511fad652b --- a/src/pages/signin/LoginForm.js +++ b/src/pages/signin/LoginForm.js @@ -15,6 +15,7 @@ import ONYXKEYS from '../../ONYXKEYS'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../components/withWindowDimensions'; import compose from '../../libs/compose'; import canFocusInputOnScreenFocus from '../../libs/canFocusInputOnScreenFocus'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; const propTypes = { /* Onyx Props */ @@ -32,6 +33,8 @@ const propTypes = { }), ...windowDimensionsPropTypes, + + ...withLocalizePropTypes, }; const defaultProps = { @@ -55,7 +58,7 @@ class LoginForm extends React.Component { */ validateAndSubmitForm() { if (!this.state.login.trim()) { - this.setState({formError: 'Please enter an email or phone number'}); + this.setState({formError: this.props.translate('loginForm.pleaseEnterEmailOrPhoneNumber')}); return; } @@ -71,7 +74,7 @@ class LoginForm extends React.Component { return ( <> - Enter your phone or email: + {this.props.translate('loginForm.enterYourPhoneOrEmail')} this.setState({login: text})} onSubmitEditing={this.validateAndSubmitForm} autoCapitalize="none" - placeholder="Phone or Email" + placeholder={this.props.translate('loginForm.phoneOrEmail')} placeholderTextColor={themeColors.placeholderText} autoFocus={canFocusInputOnScreenFocus()} /> @@ -122,4 +125,5 @@ export default compose( account: {key: ONYXKEYS.ACCOUNT}, }), withWindowDimensions, + withLocalize, )(LoginForm); diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js old mode 100644 new mode 100755 index 173d294586ff..9faddcf9a492 --- a/src/pages/signin/PasswordForm.js +++ b/src/pages/signin/PasswordForm.js @@ -11,6 +11,8 @@ import {signIn, resetPassword} from '../../libs/actions/Session'; import ONYXKEYS from '../../ONYXKEYS'; import CONST from '../../CONST'; import ChangeExpensifyLoginLink from './ChangeExpensifyLoginLink'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import compose from '../../libs/compose'; const propTypes = { /* Onyx Props */ @@ -26,6 +28,8 @@ const propTypes = { // Whether or not a sign on form is loading (being submitted) loading: PropTypes.bool, }), + + ...withLocalizePropTypes, }; const defaultProps = { @@ -52,7 +56,7 @@ class PasswordForm extends React.Component { if (!this.state.password.trim() || (this.props.account.requiresTwoFactorAuth && !this.state.twoFactorAuthCode.trim()) ) { - this.setState({formError: 'Please fill out all fields'}); + this.setState({formError: this.props.translate('passwordForm.pleaseFillOutAllFields')}); return; } @@ -67,7 +71,7 @@ class PasswordForm extends React.Component { return ( <> - Password + {this.props.translate('common.password')} - Forgot? + {this.props.translate('passwordForm.forgot')} {this.props.account.requiresTwoFactorAuth && ( - Two Factor Code + {this.props.translate('passwordForm.twoFactorCode')} this.setState({twoFactorAuthCode: text})} onSubmitEditing={this.validateAndSubmitForm} @@ -104,7 +108,7 @@ class PasswordForm extends React.Component { )} @@ -123,6 +127,9 @@ class PasswordForm extends React.Component { PasswordForm.propTypes = propTypes; PasswordForm.defaultProps = defaultProps; -export default withOnyx({ - account: {key: ONYXKEYS.ACCOUNT}, -})(PasswordForm); +export default compose( + withLocalize, + withOnyx({ + account: {key: ONYXKEYS.ACCOUNT}, + }), +)(PasswordForm); diff --git a/src/pages/signin/ResendValidationForm.js b/src/pages/signin/ResendValidationForm.js old mode 100644 new mode 100755 index 504b5601d817..0f5fffcd47ef --- a/src/pages/signin/ResendValidationForm.js +++ b/src/pages/signin/ResendValidationForm.js @@ -8,6 +8,8 @@ import ButtonWithLoader from '../../components/ButtonWithLoader'; import {resendValidationLink, resetPassword} from '../../libs/actions/Session'; import ONYXKEYS from '../../ONYXKEYS'; import ChangeExpensifyLoginLink from './ChangeExpensifyLoginLink'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import compose from '../../libs/compose'; const propTypes = { /* Onyx Props */ @@ -20,6 +22,8 @@ const propTypes = { // Weather or not the account is validated validated: PropTypes.bool, }), + + ...withLocalizePropTypes, }; const defaultProps = { @@ -48,15 +52,15 @@ class ResendValidationForm extends React.Component { */ validateAndSubmitForm() { this.setState({ - formSuccess: 'Link has been re-sent', + formSuccess: this.props.translate('resendValidationForm.linkHasBeenResent'), }); if (!this.props.account.validated) { resendValidationLink(); - console.debug('Account is unvalidated: Sending validation link.'); + console.debug(this.props.translate('resendValidationForm.accountUnvalidated')); } else { resetPassword(); - console.debug('Account forgot password: Sending reset password link.'); + console.debug(this.props.translate('resendValidationForm.accountForgotPassword')); } this.successMessageTimer = setTimeout(() => { @@ -69,12 +73,12 @@ class ResendValidationForm extends React.Component { <> - We've sent you a magic sign in link – just click on it to log in! + {this.props.translate('resendValidationForm.weSentYouMagicSignInLink')} @@ -94,6 +98,9 @@ class ResendValidationForm extends React.Component { ResendValidationForm.propTypes = propTypes; ResendValidationForm.defaultProps = defaultProps; -export default withOnyx({ - account: {key: ONYXKEYS.ACCOUNT}, -})(ResendValidationForm); +export default compose( + withLocalize, + withOnyx({ + account: {key: ONYXKEYS.ACCOUNT}, + }), +)(ResendValidationForm); diff --git a/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js b/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js old mode 100644 new mode 100755 index 8e3f533a20b0..88062b5ef15f --- a/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js +++ b/src/pages/signin/SignInPageLayout/SignInPageLayoutNarrow.js @@ -6,12 +6,13 @@ import { import PropTypes from 'prop-types'; import styles from '../../../styles/styles'; import variables from '../../../styles/variables'; -import ExpensifyCashLogo from '../../../components/ExpensifyCashLogo'; +import ExpensifyCashLogo from '../../../../assets/images/expensify-cash.svg'; import welcomeScreenshot from '../../../../assets/images/welcome-screenshot.png'; import TermsAndLicenses from '../TermsAndLicenses'; import WelcomeText from '../../../components/WelcomeText'; import openURLInNewTab from '../../../libs/openURLInNewTab/index.native'; import CONST from '../../../CONST'; +import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; const propTypes = { @@ -21,6 +22,8 @@ const propTypes = { // Whether we should show the welcome elements shouldShowWelcomeText: PropTypes.bool, shouldShowWelcomeScreenshot: PropTypes.bool, + + ...withLocalizePropTypes, }; const defaultProps = { @@ -38,7 +41,7 @@ const SignInPageLayoutNarrow = props => ( - Expensify.cash + {props.translate('signInPage.expensifyDotCash')} @@ -59,21 +62,22 @@ const SignInPageLayoutNarrow = props => ( {props.shouldShowWelcomeText && } - Expensify.cash is open source. View + {`${props.translate('signInPage.expensifyIsOpenSource')}. ${ + props.translate('common.view')}`} {' '} openURLInNewTab(CONST.GITHUB_URL)} > - the code + {props.translate('signInPage.theCode')} - . View + {`. ${props.translate('common.view')}`} {' '} openURLInNewTab(CONST.UPWORK_URL)} > - open jobs + {props.translate('signInPage.openJobs')} . @@ -90,4 +94,4 @@ SignInPageLayoutNarrow.defaultProps = defaultProps; SignInPageLayoutNarrow.displayName = 'SignInPageLayoutNarrow'; -export default SignInPageLayoutNarrow; +export default withLocalize(SignInPageLayoutNarrow); diff --git a/src/pages/signin/SignInPageLayout/SignInPageLayoutWide.js b/src/pages/signin/SignInPageLayout/SignInPageLayoutWide.js old mode 100644 new mode 100755 index 99289a7f7e3a..37fcc000aee2 --- a/src/pages/signin/SignInPageLayout/SignInPageLayoutWide.js +++ b/src/pages/signin/SignInPageLayout/SignInPageLayoutWide.js @@ -4,13 +4,14 @@ import { } from 'react-native'; import PropTypes from 'prop-types'; import styles from '../../../styles/styles'; -import ExpensifyCashLogo from '../../../components/ExpensifyCashLogo'; +import ExpensifyCashLogo from '../../../../assets/images/expensify-cash.svg'; import welcomeScreenshot from '../../../../assets/images/welcome-screenshot-wide.png'; import variables from '../../../styles/variables'; import TermsAndLicenses from '../TermsAndLicenses'; import WelcomeText from '../../../components/WelcomeText'; import openURLInNewTab from '../../../libs/openURLInNewTab'; import CONST from '../../../CONST'; +import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; const propTypes = { // The children to show inside the layout @@ -19,6 +20,8 @@ const propTypes = { // Whether we should show the welcome text // (the welcome screenshot always displays on wide views) shouldShowWelcomeText: PropTypes.bool, + + ...withLocalizePropTypes, }; const defaultProps = { @@ -44,7 +47,7 @@ const SignInPageLayoutWide = props => ( - Expensify.cash + {props.translate('signInPage.expensifyDotCash')} @@ -58,21 +61,22 @@ const SignInPageLayoutWide = props => ( )} - Expensify.cash is open source. View + {`${props.translate('signInPage.expensifyIsOpenSource')}. ${ + props.translate('common.view')}`} {' '} openURLInNewTab(CONST.GITHUB_URL)} > - the code + {props.translate('signInPage.theCode')} - . View + {`. ${props.translate('common.view')}`} {' '} openURLInNewTab(CONST.UPWORK_URL)} > - open jobs + {props.translate('signInPage.openJobs')} . @@ -88,4 +92,4 @@ SignInPageLayoutWide.defaultProps = defaultProps; SignInPageLayoutWide.displayName = 'SignInPageLayoutWide'; -export default SignInPageLayoutWide; +export default withLocalize(SignInPageLayoutWide); diff --git a/src/pages/signin/TermsAndLicenses/TermsOnly.js b/src/pages/signin/TermsAndLicenses/TermsOnly.js old mode 100644 new mode 100755 index c1d4ca2ce59e..a208104f2d37 --- a/src/pages/signin/TermsAndLicenses/TermsOnly.js +++ b/src/pages/signin/TermsAndLicenses/TermsOnly.js @@ -3,30 +3,33 @@ import {Text, View} from 'react-native'; import styles from '../../../styles/styles'; import CONST from '../../../CONST'; import openURLInNewTab from '../../../libs/openURLInNewTab'; +import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; -const TermsOnly = () => ( +const TermsOnly = ({translate}) => ( - By logging in, you agree to the + {translate('termsOfUse.phrase1')} {' '} openURLInNewTab(CONST.TERMS_URL)} > - terms of service + {translate('termsOfUse.phrase2')} {' '} - and + {translate('termsOfUse.phrase3')} {' '} openURLInNewTab(CONST.PRIVACY_URL)} > - privacy policy + {translate('termsOfUse.phrase4')} . ); -export default TermsOnly; +TermsOnly.propTypes = {...withLocalizePropTypes}; + +export default withLocalize(TermsOnly); diff --git a/src/pages/signin/TermsAndLicenses/TermsWithLicenses.js b/src/pages/signin/TermsAndLicenses/TermsWithLicenses.js old mode 100644 new mode 100755 index 94af1d8c4307..1b70e1827a25 --- a/src/pages/signin/TermsAndLicenses/TermsWithLicenses.js +++ b/src/pages/signin/TermsAndLicenses/TermsWithLicenses.js @@ -3,38 +3,41 @@ import {Text, View} from 'react-native'; import styles from '../../../styles/styles'; import CONST from '../../../CONST'; import openURLInNewTab from '../../../libs/openURLInNewTab'; +import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; -const TermsWithLicenses = () => ( +const TermsWithLicenses = ({translate}) => ( - By logging in, you agree to the + {translate('termsOfUse.phrase1')} {' '} openURLInNewTab(CONST.TERMS_URL)} > - terms of service + {translate('termsOfUse.phrase2')} {' '} - and + {translate('termsOfUse.phrase3')} {' '} openURLInNewTab(CONST.PRIVACY_URL)} > - privacy policy + {translate('termsOfUse.phrase4')} - . Money transmission is provided by Expensify Payments LLC (NMLS ID:2017010) pursuant to its + {translate('termsOfUse.phrase5')} {' '} openURLInNewTab(CONST.LICENSES_URL)} > - licenses + {translate('termsOfUse.phrase6')} . ); -export default TermsWithLicenses; +TermsWithLicenses.propTypes = {...withLocalizePropTypes}; + +export default withLocalize(TermsWithLicenses); diff --git a/tests/unit/GithubUtilsTest.js b/tests/unit/GithubUtilsTest.js index 1f73a65cdbd4..8c14182b9556 100644 --- a/tests/unit/GithubUtilsTest.js +++ b/tests/unit/GithubUtilsTest.js @@ -21,9 +21,9 @@ describe('GithubUtils', () => { }, ], // eslint-disable-next-line max-len - body: '**Release Version:** `1.0.1-47`\r\n**Compare Changes:** https://github.com/Expensify/Expensify.cash/compare/production...staging\r\n\r\n**This release contains changes from the following pull requests:**\r\n- [ ] https://github.com/Expensify/Expensify.cash/pull/21\r\n- [x] https://github.com/Expensify/Expensify.cash/pull/22\r\n- [ ] https://github.com/Expensify/Expensify.cash/pull/23\r\n', + body: '**Release Version:** `1.0.1-47`\r\n**Compare Changes:** https://github.com/Expensify/Expensify.cash/compare/production...staging\r\n\r\n**This release contains changes from the following pull requests:**\r\n- [ ] https://github.com/Expensify/Expensify.cash/pull/21\r\n- [x] https://github.com/Expensify/Expensify.cash/pull/22\r\n- [ ] https://github.com/Expensify/Expensify.cash/pull/23\r\n\r\n', }; - const issueWithDeployBlockers = baseIssue; + const issueWithDeployBlockers = {...baseIssue}; // eslint-disable-next-line max-len issueWithDeployBlockers.body += '\r\n**Deploy Blockers:**\r\n- [ ] https://github.com/Expensify/Expensify.cash/issues/1\r\n- [x] https://github.com/Expensify/Expensify.cash/issues/2\r\n- [ ] https://github.com/Expensify/Expensify.cash/pull/1234\r\n'; @@ -59,8 +59,9 @@ describe('GithubUtils', () => { tag: '1.0.1-47', title: 'Andrew Test Issue', url: 'https://api.github.com/repos/Andrew-Test-Org/Public-Test-Repo/issues/29', + deployBlockers: [], }; - const expectedResponseWithDeployBlockers = baseExpectedResponse; + const expectedResponseWithDeployBlockers = {...baseExpectedResponse}; expectedResponseWithDeployBlockers.deployBlockers = [ { url: 'https://github.com/Expensify/Expensify.cash/issues/1', @@ -79,6 +80,24 @@ describe('GithubUtils', () => { }, ]; + test('Test finding an open issue with no PRs successfully', () => { + const octokit = new Octokit(); + const github = new GithubUtils(octokit); + const bareIssue = { + ...baseIssue, + // eslint-disable-next-line max-len + body: '**Release Version:** `1.0.1-47`\r\n**Compare Changes:** https://github.com/Expensify/Expensify.cash/compare/production...staging\r\n\r\ncc @Expensify/applauseleads\n', + }; + + const bareExpectedResponse = { + ...baseExpectedResponse, + PRList: [], + }; + + octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [bareIssue]}); + return github.getStagingDeployCash().then(data => expect(data).toStrictEqual(bareExpectedResponse)); + }); + test('Test finding an open issue successfully', () => { const octokit = new Octokit(); const github = new GithubUtils(octokit); From c74cf3f885b13876f17d56be63204d4f853288f1 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 14:42:34 +0100 Subject: [PATCH 083/141] iou error is now of type string --- src/Expensify.js | 2 +- src/libs/actions/IOU.js | 10 +++++----- src/pages/iou/IOUDetailsModal.js | 4 ++-- src/pages/iou/IOUModal.js | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Expensify.js b/src/Expensify.js index bd29c9486139..a07cf9e199b7 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -26,7 +26,7 @@ Onyx.init({ [ONYXKEYS.SESSION]: {loading: false, shouldShowComposeInput: true}, [ONYXKEYS.ACCOUNT]: CONST.DEFAULT_ACCOUNT_DATA, [ONYXKEYS.NETWORK]: {isOffline: false}, - [ONYXKEYS.IOU]: {loading: false, error: false, creatingIOUTransaction: false}, + [ONYXKEYS.IOU]: {loading: false, error: '', creatingIOUTransaction: false}, }, registerStorageEventListener: (onStorageEvent) => { listenToStorageEvents(onStorageEvent); diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index fe080093e8c0..ac8876e7905a 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -56,7 +56,7 @@ function getIOUReportsForNewTransaction(requestParams) { Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, chatReportsToUpdate); return Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT_IOUS, iouReportsToUpdate); }) - .catch(() => Onyx.merge(ONYXKEYS.IOU, {loading: false, creatingIOUTransaction: false, error: true})) + .catch(() => Onyx.merge(ONYXKEYS.IOU, {loading: false, creatingIOUTransaction: false, error: ''})) .finally(() => Onyx.merge(ONYXKEYS.IOU, {loading: false, creatingIOUTransaction: false})); } @@ -69,7 +69,7 @@ function getIOUReportsForNewTransaction(requestParams) { * @param {String} params.debtorEmail */ function createIOUTransaction(params) { - Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: false}); + Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: ''}); API.CreateIOUTransaction(params) .then(data => getIOUReportsForNewTransaction([data])); } @@ -83,7 +83,7 @@ function createIOUTransaction(params) { * @param {String} params.currency */ function createIOUSplit(params) { - Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: false}); + Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: ''}); API.CreateChatReport({ emailList: params.splits.map(participant => participant.email).join(','), @@ -118,7 +118,7 @@ function createIOUSplit(params) { function settleIOUReport({ chatReportID, reportID, paymentMethodType, }) { - Onyx.merge(ONYXKEYS.IOU, {loading: true, error: false}); + Onyx.merge(ONYXKEYS.IOU, {loading: true, error: ''}); API.PayIOU({ reportID, paymentMethodType, @@ -130,7 +130,7 @@ function settleIOUReport({ }) .then(fetchChatReportsByIDs([chatReportID])) .then(fetchIOUReportByID(reportID, chatReportID, true)) - .catch(() => Onyx.merge(ONYXKEYS.IOU, {error: true})) + .catch((error) => Onyx.merge(ONYXKEYS.IOU, {error: error})) .finally(() => Onyx.merge(ONYXKEYS.IOU, {loading: false})); } diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 34d2b07d8aca..c805298d0bdf 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -40,8 +40,8 @@ const propTypes = { // Is the IOU Report currently being settled loading: PropTypes.bool, - // Whether or not transaction creation has resulted to error - error: PropTypes.bool, + // Error message, empty represents no error + error: PropTypes.string, }), // IOU Report data object diff --git a/src/pages/iou/IOUModal.js b/src/pages/iou/IOUModal.js index 2095c84e8172..12a75a7e3968 100755 --- a/src/pages/iou/IOUModal.js +++ b/src/pages/iou/IOUModal.js @@ -35,8 +35,8 @@ const propTypes = { // Whether or not transaction creation has started creatingIOUTransaction: PropTypes.bool, - // Whether or not transaction creation has resulted to error - error: PropTypes.bool, + // Error message, empty represents no error + error: PropTypes.string, }).isRequired, // Personal details of all the users From 73d7e8707b83b64e944a72dbeaf07a4d00d7a0df Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 14:44:35 +0100 Subject: [PATCH 084/141] improve prop name --- src/components/ReportActionItemIOUAction.js | 4 ++-- src/pages/home/report/ReportActionItem.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index d81ba4b19d9f..da74f9eaa6be 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -17,7 +17,7 @@ const propTypes = { chatReportID: PropTypes.number.isRequired, // Should render the preview Component? - shouldDisplayPreviewComp: PropTypes.bool.isRequired, + shouldDisplayPreview: PropTypes.bool.isRequired, /* --- Onyx Props --- */ // ChatReport associated with iouReport @@ -63,7 +63,7 @@ class ReportActionItemIOUAction extends Component { showViewDetailsLink={isDMChat} onViewDetailsPressed={this.launchIOUDetailsModal} /> - {this.props.shouldDisplayPreviewComp && ( + {this.props.shouldDisplayPreview && ( ); } else { From 01403b8b4aadc5c5dce2d41bd15ceee6d3616ebb Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 14:52:43 +0100 Subject: [PATCH 085/141] invert hasMultipleParticipants logic to simplify --- src/components/ReportActionItemIOUAction.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index da74f9eaa6be..4223ec240b59 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -55,12 +55,12 @@ class ReportActionItemIOUAction extends Component { } render() { - const isDMChat = this.props.chatReport.participants.length === 1; + const hasMultipleParticipants = this.props.chatReport.participants.length >= 2; return ( {this.props.shouldDisplayPreview && ( From e1920faeed52bed13c0fc4adeafd7f29f3dbf772 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 15:07:56 +0100 Subject: [PATCH 086/141] move IOUPreview session subscription out of parent --- src/components/ReportActionItemIOUAction.js | 10 ---------- src/components/ReportActionItemIOUPreview.js | 15 +++++++++------ src/pages/iou/IOUDetailsModal.js | 1 - 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index 4223ec240b59..583b9b1dbd1d 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -25,12 +25,6 @@ const propTypes = { // The participants of this report participants: PropTypes.arrayOf(PropTypes.string), }), - - // Session info for the currently logged in user. - session: PropTypes.shape({ - // Currently logged in user email - email: PropTypes.string, - }).isRequired, }; const defaultProps = { @@ -66,7 +60,6 @@ class ReportActionItemIOUAction extends Component { {this.props.shouldDisplayPreview && ( )} @@ -83,7 +76,4 @@ export default withOnyx({ chatReport: { key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, }, - session: { - key: ONYXKEYS.SESSION, - }, })(ReportActionItemIOUAction); diff --git a/src/components/ReportActionItemIOUPreview.js b/src/components/ReportActionItemIOUPreview.js index 149315079aaa..409a58f9e7db 100644 --- a/src/components/ReportActionItemIOUPreview.js +++ b/src/components/ReportActionItemIOUPreview.js @@ -19,12 +19,6 @@ const propTypes = { // eslint-disable-next-line react/no-unused-prop-types iouReportID: PropTypes.number, - // Session info for the currently logged in user. - session: PropTypes.shape({ - // Currently logged in user email - email: PropTypes.string, - }).isRequired, - /* --- Onyx Props --- */ // Active IOU Report for current report iou: PropTypes.shape({ @@ -47,6 +41,12 @@ const propTypes = { // This is either the user's full name, or their login if full name is an empty string displayName: PropTypes.string.isRequired, })).isRequired, + + // Session info for the currently logged in user. + session: PropTypes.shape({ + // Currently logged in user email + email: PropTypes.string, + }).isRequired, }; const defaultProps = { @@ -133,4 +133,7 @@ export default withOnyx({ iou: { key: ({iouReportID}) => `${ONYXKEYS.COLLECTION.REPORT_IOUS}${iouReportID}`, }, + session: { + key: ONYXKEYS.SESSION, + }, })(ReportActionItemIOUPreview); diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index c805298d0bdf..e0453c49e3bb 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -107,7 +107,6 @@ class IOUDetailsModal extends Component { Date: Tue, 11 May 2021 15:24:00 +0100 Subject: [PATCH 087/141] remove useless outer style for Preview comp --- src/components/ReportActionItemIOUPreview.js | 62 ++++++++++---------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/src/components/ReportActionItemIOUPreview.js b/src/components/ReportActionItemIOUPreview.js index 409a58f9e7db..a3083dd877fa 100644 --- a/src/components/ReportActionItemIOUPreview.js +++ b/src/components/ReportActionItemIOUPreview.js @@ -84,40 +84,38 @@ const ReportActionItemIOUPreview = ({ const cachedTotal = iou.cachedTotal ? iou.cachedTotal.replace(/[()]/g, '') : ''; return ( - - - - - {cachedTotal} - - {iou.hasOutstandingIOU - ? `${managerName} owes ${ownerName}` - : `${ownerName} paid ${managerName}`} - - - - - + + + + {cachedTotal} + + {iou.hasOutstandingIOU + ? `${managerName} owes ${ownerName}` + : `${ownerName} paid ${managerName}`} + + + + - {isCurrentUserManager && !shouldHidePayButton && ( - - - Pay - - - )} + {isCurrentUserManager && !shouldHidePayButton && ( + + + Pay + + + )} ); }; From 2841e9a769603117f7456be4d8874bc7002aaed8 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 15:32:08 +0100 Subject: [PATCH 088/141] various prop improvements --- src/components/ReportActionItemIOUAction.js | 2 +- src/components/ReportActionItemIOUQuote.js | 8 ++++---- src/components/ReportTransaction.js | 2 +- src/libs/API.js | 2 +- src/pages/home/report/ReportActionItemSingle.js | 8 ++++---- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index 583b9b1dbd1d..672c4a7e2837 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -54,7 +54,7 @@ class ReportActionItemIOUAction extends Component { {this.props.shouldDisplayPreview && ( diff --git a/src/components/ReportActionItemIOUQuote.js b/src/components/ReportActionItemIOUQuote.js index 3d03a2dcdddc..1c65d523b746 100644 --- a/src/components/ReportActionItemIOUQuote.js +++ b/src/components/ReportActionItemIOUQuote.js @@ -10,18 +10,18 @@ const propTypes = { action: PropTypes.shape(ReportActionPropTypes).isRequired, // Should the View Details link be displayed? - showViewDetailsLink: PropTypes.bool, + shouldShowViewDetailsLink: PropTypes.bool, // Callback invoked when View Details is pressed onViewDetailsPressed: PropTypes.func, }; const defaultProps = { - showViewDetailsLink: false, + shouldShowViewDetailsLink: false, onViewDetailsPressed: null, }; -const ReportActionItemIOUQuote = ({action, showViewDetailsLink, onViewDetailsPressed}) => ( +const ReportActionItemIOUQuote = ({action, shouldShowViewDetailsLink, onViewDetailsPressed}) => ( {_.map(action.message, (fragment, index) => ( @@ -29,7 +29,7 @@ const ReportActionItemIOUQuote = ({action, showViewDetailsLink, onViewDetailsPre {fragment.text} - {showViewDetailsLink && ( + {shouldShowViewDetailsLink && ( {action.message[0].text} diff --git a/src/libs/API.js b/src/libs/API.js index 7c59a6908a31..336ebc5fa843 100644 --- a/src/libs/API.js +++ b/src/libs/API.js @@ -469,7 +469,7 @@ function Graphite_Timer(parameters) { /** * @param {Object} parameters - * @param {String} parameters.reportID + * @param {Number} parameters.reportID * @param {String} parameters.paymentMethodType * @returns {Promise} */ diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index c98ae804b9b0..293c384108de 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -20,7 +20,7 @@ const propTypes = { personalDetails: PropTypes.objectOf(personalDetailsPropType), // Styles for the outermost View - outerViewStyles: PropTypes.arrayOf(PropTypes.object), + wrapperStyles: PropTypes.arrayOf(PropTypes.object), // Children view component for this action item children: PropTypes.node.isRequired, @@ -28,14 +28,14 @@ const propTypes = { const defaultProps = { personalDetails: {}, - outerViewStyles: [styles.chatItem], + wrapperStyles: [styles.chatItem], }; const ReportActionItemSingle = ({ action, personalDetails, children, - outerViewStyles, + wrapperStyles, }) => { const {avatar, displayName} = personalDetails[action.actorEmail] || {}; const avatarUrl = action.automatic @@ -49,7 +49,7 @@ const ReportActionItemSingle = ({ // we should stop referring to the report history items entirely for this information. const personArray = displayName ? [{type: 'TEXT', text: displayName}] : action.person; return ( - + Date: Tue, 11 May 2021 15:36:24 +0100 Subject: [PATCH 089/141] improvements to IOUDetailModal --- src/pages/iou/IOUDetailsModal.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index e0453c49e3bb..383bbfb7272e 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -3,6 +3,7 @@ import {View, ActivityIndicator, ScrollView} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; +import _ from 'underscore'; import styles from '../../styles/styles'; import ONYXKEYS from '../../ONYXKEYS'; import themeColors from '../../styles/themes/default'; @@ -74,6 +75,7 @@ class IOUDetailsModal extends Component { super(props); this.state = { + // Temporarily placeholder, as the Venmo/Paypal issue will introduce settlement types. settlementType: 'Elsewhere', }; @@ -81,7 +83,7 @@ class IOUDetailsModal extends Component { } componentDidMount() { - // We should not update the chatReport data here, as there is no guarantte this is the active IOU + // We should not update the chatReport data here, as there is no guarantee this is the active IOU fetchIOUReportByID(this.props.route.params.iouReportID, this.props.route.params.chatReportID, false); } @@ -95,7 +97,7 @@ class IOUDetailsModal extends Component { render() { const sessionEmail = lodashGet(this.props.session, 'email', null); - const reportIsLoading = this.props.iouReport === null; + const reportIsLoading = _.isNull(this.props.iouReport); return ( Date: Tue, 11 May 2021 15:50:58 +0100 Subject: [PATCH 090/141] replace IOUTransaction PureComponent with Component --- src/pages/iou/IOUTransactions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/IOUTransactions.js b/src/pages/iou/IOUTransactions.js index e380c16f5c91..ed53b8310eb1 100644 --- a/src/pages/iou/IOUTransactions.js +++ b/src/pages/iou/IOUTransactions.js @@ -1,4 +1,4 @@ -import React, {PureComponent} from 'react'; +import React, {Component} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -28,7 +28,7 @@ const defaultProps = { transactions: [], }; -class IOUTransactions extends PureComponent { +class IOUTransactions extends Component { render() { return ( From 96471cd2e60de6c8654a149b9e9c84745e2f4607 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 16:10:32 +0100 Subject: [PATCH 091/141] refactor style name --- src/components/ReportActionItemIOUQuote.js | 4 ++-- src/styles/styles.js | 19 ++++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/components/ReportActionItemIOUQuote.js b/src/components/ReportActionItemIOUQuote.js index 1c65d523b746..85a2b7c6c856 100644 --- a/src/components/ReportActionItemIOUQuote.js +++ b/src/components/ReportActionItemIOUQuote.js @@ -2,7 +2,7 @@ import React from 'react'; import {View, Text} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; -import styles, {webViewStyles} from '../styles/styles'; +import styles from '../styles/styles'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; const propTypes = { @@ -25,7 +25,7 @@ const ReportActionItemIOUQuote = ({action, shouldShowViewDetailsLink, onViewDeta {_.map(action.message, (fragment, index) => ( - + {fragment.text} diff --git a/src/styles/styles.js b/src/styles/styles.js index 58518a5f250b..2122bbae26b5 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -1410,6 +1410,14 @@ const styles = { opacity: 0, transform: 'translateX(-100%)', }, + + blockquote: { + borderLeftColor: themeColors.border, + borderLeftWidth: 4, + paddingLeft: 12, + marginTop: 4, + marginBottom: 4, + }, }; const baseCodeTagStyles = { @@ -1447,17 +1455,6 @@ const webViewStyles = { flexShrink: 1, }, - blockquote: { - borderLeftColor: themeColors.border, - borderLeftWidth: 4, - paddingLeft: 12, - marginTop: 4, - marginBottom: 4, - - // Overwrite default HTML margin for blockquotes - marginLeft: 0, - }, - pre: { ...baseCodeTagStyles, paddingTop: 4, From 6368d7f8d9e254893c556143b191264cccdab600 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 16:27:45 +0100 Subject: [PATCH 092/141] document getSimplifiedIOUReport function --- src/libs/actions/Report.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 262e5e0a46c5..17ad60a5f655 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -197,12 +197,13 @@ function getSimplifiedReportObject(report) { */ function getSimplifiedIOUReport(reportData, chatReportID) { const transactions = _.map(reportData.transactionList, transaction => ({ + // TransactionID is returned from API as a String transactionID: Number(transaction.transactionID), amount: transaction.amount, currency: transaction.currency, created: transaction.created, comment: transaction.comment, - })).reverse(); + })).reverse(); // transactionList is returned in desc order, but we desire asc order return { reportID: reportData.reportID, From aead72e11394b1430792c877dd173b566d7580ae Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 16:54:36 +0100 Subject: [PATCH 093/141] add explanation for removing the IOU Report empty error message --- src/libs/actions/Report.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 17ad60a5f655..4eb3e22d09dc 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -244,6 +244,8 @@ function fetchIOUReport(iouReportID, chatReportID) { } const iouReportData = response.reports[iouReportID]; if (!iouReportData) { + // This is occuring due to 'fetchChatReportsByIDs' calling this function with a IOUReportID that has + // already been settled. In this case it is expected that no data is returned from 'getIOU'. return; } return getSimplifiedIOUReport(iouReportData, chatReportID); From 7d33d18f6a1052506cc22d247247fd565df0ceed Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 17:06:26 +0100 Subject: [PATCH 094/141] improve comments --- src/libs/actions/Report.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 4eb3e22d09dc..fff229707cc0 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -378,7 +378,8 @@ function setLocalIOUReportData(iouReportObject, chatReportID, shouldUpdateChatRe const iouReportKey = `${ONYXKEYS.COLLECTION.REPORT_IOUS}${iouReportObject.reportID}`; Onyx.merge(iouReportKey, iouReportObject); - // We don't always want to update the chatReport, as the IOU could be an old settled report + // We don't always want to update the chatReport. If the IOU Report we have retrieved is a settled report, then + // we must not update the chatReport - as the chatReports association with the active IOUreport would be overidden. if (!shouldUpdateChatReport) { return; } @@ -524,11 +525,8 @@ function updateReportWithNewAction(reportID, reportAction) { // If chat report receives an action with IOU, update IOU object if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) { const iouReportID = reportAction.originalMessage.IOUReportID; - if (!iouReportID) { - console.error('IOUReportID not found in report action data, this should not happen!'); - } - // As this flow is for a new Action, we definately need to update the local chat Report + // As this flow is for a new Action, we definitely need to update the local chat Report fetchIOUReportByID(iouReportID, reportID, true); } From a0d49a4bd3ec6db8b9c6f65db3f9e751294384d4 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 18:31:20 +0100 Subject: [PATCH 095/141] Merge branch 'main' into jules-iouDetailsModal # Conflicts: # .github/actions/checkDeployBlockers/index.js # .github/actions/createOrUpdateStagingDeploy/index.js # .github/actions/getReleaseBody/index.js # .github/actions/isPullRequestMergeable/index.js # .github/actions/isStagingDeployLocked/index.js # .github/actions/markPullRequestsAsDeployed/index.js # .github/actions/reopenIssueWithComment/index.js # .github/libs/GithubUtils.js # android/app/build.gradle # ios/ExpensifyCash/Info.plist # ios/ExpensifyCashTests/Info.plist # package-lock.json # package.json # src/libs/Navigation/AppNavigator/ModalStackNavigators.js # src/libs/actions/Report.js # src/pages/home/report/ReportActionItem.js --- .github/actions/checkDeployBlockers/index.js | 2 +- .github/actions/createOrUpdateStagingDeploy/index.js | 2 +- .github/actions/getReleaseBody/index.js | 2 +- .github/actions/isPullRequestMergeable/index.js | 2 +- .github/actions/isStagingDeployLocked/index.js | 2 +- .github/actions/markPullRequestsAsDeployed/index.js | 2 +- .github/actions/reopenIssueWithComment/index.js | 2 +- .github/libs/GithubUtils.js | 2 +- CONTRIBUTING.md | 5 +++-- android/app/build.gradle | 4 ++-- ios/ExpensifyCash/Info.plist | 2 +- ios/ExpensifyCashTests/Info.plist | 2 +- package-lock.json | 2 +- package.json | 2 +- tests/unit/GithubUtilsTest.js | 12 ++++++++++++ 15 files changed, 29 insertions(+), 16 deletions(-) diff --git a/.github/actions/checkDeployBlockers/index.js b/.github/actions/checkDeployBlockers/index.js index f34a1d6aefc2..0fc0c28ae73b 100644 --- a/.github/actions/checkDeployBlockers/index.js +++ b/.github/actions/checkDeployBlockers/index.js @@ -210,7 +210,7 @@ class GithubUtils { * @returns {Array} - [{URL: String, number: Number, isResolved: Boolean}] */ getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r\n((?:.*\r\n)+)/) || []; + let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r?\n((?:.*\r?\n)+)/) || []; if (deployBlockerSection.length !== 2) { return []; } diff --git a/.github/actions/createOrUpdateStagingDeploy/index.js b/.github/actions/createOrUpdateStagingDeploy/index.js index 3fd6c4c9386b..8443ffc8f707 100644 --- a/.github/actions/createOrUpdateStagingDeploy/index.js +++ b/.github/actions/createOrUpdateStagingDeploy/index.js @@ -236,7 +236,7 @@ class GithubUtils { * @returns {Array} - [{URL: String, number: Number, isResolved: Boolean}] */ getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r\n((?:.*\r\n)+)/) || []; + let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r?\n((?:.*\r?\n)+)/) || []; if (deployBlockerSection.length !== 2) { return []; } diff --git a/.github/actions/getReleaseBody/index.js b/.github/actions/getReleaseBody/index.js index f1601e48a833..689abbb6e97f 100644 --- a/.github/actions/getReleaseBody/index.js +++ b/.github/actions/getReleaseBody/index.js @@ -149,7 +149,7 @@ class GithubUtils { * @returns {Array} - [{URL: String, number: Number, isResolved: Boolean}] */ getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r\n((?:.*\r\n)+)/) || []; + let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r?\n((?:.*\r?\n)+)/) || []; if (deployBlockerSection.length !== 2) { return []; } diff --git a/.github/actions/isPullRequestMergeable/index.js b/.github/actions/isPullRequestMergeable/index.js index e105a86a30a3..bbb6388e0f3a 100644 --- a/.github/actions/isPullRequestMergeable/index.js +++ b/.github/actions/isPullRequestMergeable/index.js @@ -184,7 +184,7 @@ class GithubUtils { * @returns {Array} - [{URL: String, number: Number, isResolved: Boolean}] */ getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r\n((?:.*\r\n)+)/) || []; + let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r?\n((?:.*\r?\n)+)/) || []; if (deployBlockerSection.length !== 2) { return []; } diff --git a/.github/actions/isStagingDeployLocked/index.js b/.github/actions/isStagingDeployLocked/index.js index d96388a77dc0..6610948ead22 100644 --- a/.github/actions/isStagingDeployLocked/index.js +++ b/.github/actions/isStagingDeployLocked/index.js @@ -162,7 +162,7 @@ class GithubUtils { * @returns {Array} - [{URL: String, number: Number, isResolved: Boolean}] */ getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r\n((?:.*\r\n)+)/) || []; + let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r?\n((?:.*\r?\n)+)/) || []; if (deployBlockerSection.length !== 2) { return []; } diff --git a/.github/actions/markPullRequestsAsDeployed/index.js b/.github/actions/markPullRequestsAsDeployed/index.js index 99248d97bdc7..c1910c4d2995 100644 --- a/.github/actions/markPullRequestsAsDeployed/index.js +++ b/.github/actions/markPullRequestsAsDeployed/index.js @@ -195,7 +195,7 @@ class GithubUtils { * @returns {Array} - [{URL: String, number: Number, isResolved: Boolean}] */ getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r\n((?:.*\r\n)+)/) || []; + let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r?\n((?:.*\r?\n)+)/) || []; if (deployBlockerSection.length !== 2) { return []; } diff --git a/.github/actions/reopenIssueWithComment/index.js b/.github/actions/reopenIssueWithComment/index.js index 557f18c17291..b313aec0e780 100644 --- a/.github/actions/reopenIssueWithComment/index.js +++ b/.github/actions/reopenIssueWithComment/index.js @@ -173,7 +173,7 @@ class GithubUtils { * @returns {Array} - [{URL: String, number: Number, isResolved: Boolean}] */ getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r\n((?:.*\r\n)+)/) || []; + let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r?\n((?:.*\r?\n)+)/) || []; if (deployBlockerSection.length !== 2) { return []; } diff --git a/.github/libs/GithubUtils.js b/.github/libs/GithubUtils.js index 59b76c2cb9bc..9d97dd2dc0d7 100644 --- a/.github/libs/GithubUtils.js +++ b/.github/libs/GithubUtils.js @@ -120,7 +120,7 @@ class GithubUtils { * @returns {Array} - [{URL: String, number: Number, isResolved: Boolean}] */ getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r\n((?:.*\r\n)+)/) || []; + let deployBlockerSection = issue.body.match(/Deploy Blockers:\*\*\r?\n((?:.*\r?\n)+)/) || []; if (deployBlockerSection.length !== 2) { return []; } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d394f9d48cc3..3019cf9cc149 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,8 +35,9 @@ In this scenario, it’s possible that you found a bug or enhancement that we ha 2. If your bug or enhancement matches an existing issue, please feel free to comment on that GitHub issue with your findings if you think it’ll help solve a problem. 3. If there is no existing issue or Upwork job, create a new GitHub issue in the Expensify.cash repo. 4. Make sure to fill out all the required information fields in the issue template. - 5. Optional: If you would like to solve the bug or enhancement that you are proposing, please add a comment on your issue with a solution proposal. - 6. Pause on this step until a member of the Expensify team responds on your issue with next steps. + 5. Add the `AutoAssignerTriage` label to your issue. + 6. Optional: If you would like to solve the bug or enhancement that you are proposing, please add a comment on your issue with a solution proposal. + 7. Pause on this step until a member of the Expensify team responds on your issue with next steps. >**Note:** Our problem solving approach at Expensify is to focus on high value problems and avoid small optimizations with results that are difficult to measure. We also prefer to identify and solve problems at their root. Given that, please ensure all proposed jobs fix a specific problem in a measurable way with evidence so they are easy to evaluate. Here's an example of a good problem/solution: > diff --git a/android/app/build.gradle b/android/app/build.gradle index ab60ce42d386..273bacd68441 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -148,8 +148,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001004109 - versionName "1.0.41-9" + versionCode 1001004111 + versionName "1.0.41-11" } splits { abi { diff --git a/ios/ExpensifyCash/Info.plist b/ios/ExpensifyCash/Info.plist index ba39baeb2927..f61635aa8fc1 100644 --- a/ios/ExpensifyCash/Info.plist +++ b/ios/ExpensifyCash/Info.plist @@ -30,7 +30,7 @@ CFBundleVersion - 1.0.41.9 + 1.0.41.11 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/ExpensifyCashTests/Info.plist b/ios/ExpensifyCashTests/Info.plist index 733005455f2d..007524a42ee0 100644 --- a/ios/ExpensifyCashTests/Info.plist +++ b/ios/ExpensifyCashTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.0.41.9 + 1.0.41.11 diff --git a/package-lock.json b/package-lock.json index ea60b195b44a..b3b76e4acc43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "expensify.cash", - "version": "1.0.41-9", + "version": "1.0.41-11", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 676c2a5dbbfe..39530e4dc38c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "expensify.cash", - "version": "1.0.41-9", + "version": "1.0.41-11", "author": "Expensify, Inc.", "homepage": "https://expensify.cash", "description": "Expensify.cash is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", diff --git a/tests/unit/GithubUtilsTest.js b/tests/unit/GithubUtilsTest.js index 8c14182b9556..0c914b3f42bf 100644 --- a/tests/unit/GithubUtilsTest.js +++ b/tests/unit/GithubUtilsTest.js @@ -113,6 +113,18 @@ describe('GithubUtils', () => { .then(data => expect(data).toStrictEqual(expectedResponseWithDeployBlockers)); }); + test('Test finding an open issue successfully and parsing with blockers w/o carriage returns', () => { + const octokit = new Octokit(); + const github = new GithubUtils(octokit); + + const modifiedIssueWithDeployBlockers = {...issueWithDeployBlockers}; + modifiedIssueWithDeployBlockers.body = modifiedIssueWithDeployBlockers.body.replace(/\r/g, ''); + + octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [modifiedIssueWithDeployBlockers]}); + return github.getStagingDeployCash() + .then(data => expect(data).toStrictEqual(expectedResponseWithDeployBlockers)); + }); + test('Test finding an open issue without a body', () => { const octokit = new Octokit(); const github = new GithubUtils(octokit); From 6973227ab158c438a6bef4b9204c84a2f8d1b192 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 18:43:10 +0100 Subject: [PATCH 096/141] further formatting and code review adjustments --- src/components/ReportActionItemIOUAction.js | 18 ++++++------------ .../AppNavigator/ModalStackNavigators.js | 6 +++--- src/libs/Navigation/linkingConfig.js | 2 +- src/libs/actions/IOU.js | 2 +- src/libs/actions/Report.js | 2 +- src/pages/iou/IOUDetailsPage.js | 12 ------------ 6 files changed, 12 insertions(+), 30 deletions(-) delete mode 100644 src/pages/iou/IOUDetailsPage.js diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index 672c4a7e2837..7b7c4cd2cede 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -38,16 +38,6 @@ class ReportActionItemIOUAction extends Component { this.launchIOUDetailsModal = this.launchIOUDetailsModal.bind(this); } - /** - * - * Launch the IOU Details Modal, using data from the report action - */ - launchIOUDetailsModal() { - Navigation.navigate(ROUTES.getIouDetailsRoute( - this.props.chatReportID, this.props.action.originalMessage.IOUReportID, - )); - } - render() { const hasMultipleParticipants = this.props.chatReport.participants.length >= 2; return ( @@ -55,12 +45,16 @@ class ReportActionItemIOUAction extends Component { {this.props.shouldDisplayPreview && ( )} diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 5d2d795f5c48..d93cbdcb4cb7 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -8,7 +8,7 @@ import SearchPage from '../../../pages/SearchPage'; import DetailsPage from '../../../pages/DetailsPage'; import IOURequestPage from '../../../pages/iou/IOURequestPage'; import IOUBillPage from '../../../pages/iou/IOUBillPage'; -import IOUDetailsPage from '../../../pages/iou/IOUDetailsModal'; +import IOUDetailsModal from '../../../pages/iou/IOUDetailsModal'; import SettingsInitialPage from '../../../pages/settings/InitialPage'; import SettingsProfilePage from '../../../pages/settings/Profile/ProfilePage'; import SettingsPreferencesPage from '../../../pages/settings/PreferencesPage'; @@ -59,8 +59,8 @@ const IOURequestModalStackNavigator = createModalStackNavigator([{ }]); const IOUDetailsModalStackNavigator = createModalStackNavigator([{ - Component: IOUDetailsPage, - name: 'IOU_Details_Route', + Component: IOUDetailsModal, + name: 'IOU_Details_Root', }]); const DetailsModalStackNavigator = createModalStackNavigator([{ diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index f1f090eec8fc..d258bcc80ac3 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -91,7 +91,7 @@ export default { }, IOU_Details: { screens: { - IOU_Details_Route: ROUTES.IOU_DETAILS_WITH_IOU_REPORT_ID, + IOU_Details_Root: ROUTES.IOU_DETAILS_WITH_IOU_REPORT_ID, }, }, }, diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index ac8876e7905a..e0ca93d3567b 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -130,7 +130,7 @@ function settleIOUReport({ }) .then(fetchChatReportsByIDs([chatReportID])) .then(fetchIOUReportByID(reportID, chatReportID, true)) - .catch((error) => Onyx.merge(ONYXKEYS.IOU, {error: error})) + .catch(error => Onyx.merge(ONYXKEYS.IOU, {error})) .finally(() => Onyx.merge(ONYXKEYS.IOU, {loading: false})); } diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index fff229707cc0..1258ff04d95f 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -245,7 +245,7 @@ function fetchIOUReport(iouReportID, chatReportID) { const iouReportData = response.reports[iouReportID]; if (!iouReportData) { // This is occuring due to 'fetchChatReportsByIDs' calling this function with a IOUReportID that has - // already been settled. In this case it is expected that no data is returned from 'getIOU'. + // already been settled. In this case it is expected that no data is returned from 'getIOU'. return; } return getSimplifiedIOUReport(iouReportData, chatReportID); diff --git a/src/pages/iou/IOUDetailsPage.js b/src/pages/iou/IOUDetailsPage.js deleted file mode 100644 index 083be379e774..000000000000 --- a/src/pages/iou/IOUDetailsPage.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import IOUDetailsModal from './IOUDetailsModal'; -import ScreenWrapper from '../../components/ScreenWrapper'; - -export default props => ( - - {() => ( - // eslint-disable-next-line react/jsx-props-no-spreading - - )} - -); From e3575d80ea4f2269e10a6547be68d1e4fc9cf4fc Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 19:16:48 +0100 Subject: [PATCH 097/141] throw error instead of just printing an error message --- src/libs/actions/IOU.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index e0ca93d3567b..8d5286aaaea9 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -123,9 +123,9 @@ function settleIOUReport({ reportID, paymentMethodType, }) - .then((data) => { - if (data.jsonCode !== 200) { - console.error(data.message); + .then((response) => { + if (response.jsonCode !== 200) { + throw new Error(`Error while settling IOU: ${response.message}`); } }) .then(fetchChatReportsByIDs([chatReportID])) From 2e57b601e5845d1214ec9cf2bb980040841bd5ce Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 19:18:09 +0100 Subject: [PATCH 098/141] improve and simplify logic for updating reports, with better documentation --- src/libs/actions/IOU.js | 4 +-- src/libs/actions/Report.js | 51 +++++++++++++++++++++----------- src/pages/iou/IOUDetailsModal.js | 4 +-- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 8d5286aaaea9..643bda0f3d04 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -2,7 +2,7 @@ import Onyx from 'react-native-onyx'; import _ from 'underscore'; import ONYXKEYS from '../../ONYXKEYS'; import * as API from '../API'; -import {getSimplifiedIOUReport, fetchChatReportsByIDs, fetchIOUReportByID} from './Report'; +import {getSimplifiedIOUReport, fetchChatReportsByIDs, fetchIOUReportByIDAndUpdateChatReport} from './Report'; /** * Retrieve the users preferred currency @@ -129,7 +129,7 @@ function settleIOUReport({ } }) .then(fetchChatReportsByIDs([chatReportID])) - .then(fetchIOUReportByID(reportID, chatReportID, true)) + .then(fetchIOUReportByIDAndUpdateChatReport(reportID, chatReportID)) .catch(error => Onyx.merge(ONYXKEYS.IOU, {error})) .finally(() => Onyx.merge(ONYXKEYS.IOU, {loading: false})); } diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 1258ff04d95f..97b720428ab4 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -364,26 +364,32 @@ function fetchChatReportsByIDs(chatList) { } /** - * Given IOU object and chat report ID save the data to Onyx. + * Given IOU object, save the data to Onyx. * * @param {Object} iouReportObject * @param {Number} iouReportObject.stateNum * @param {Number} iouReportObject.total * @param {Number} iouReportObject.reportID - * @param {Number} chatReportID - * @param {Boolean} shouldUpdateChatReport - should the local chatReport be updated too? */ -function setLocalIOUReportData(iouReportObject, chatReportID, shouldUpdateChatReport) { - // Persist IOU Report data to Onyx + function setLocalIOUReportData(iouReportObject) { const iouReportKey = `${ONYXKEYS.COLLECTION.REPORT_IOUS}${iouReportObject.reportID}`; Onyx.merge(iouReportKey, iouReportObject); +} - // We don't always want to update the chatReport. If the IOU Report we have retrieved is a settled report, then - // we must not update the chatReport - as the chatReports association with the active IOUreport would be overidden. - if (!shouldUpdateChatReport) { - return; - } +/** + * Given IOU object and associated chatReportID, save the data to Onyx. + * + * @param {Object} iouReportObject + * @param {Number} iouReportObject.stateNum + * @param {Number} iouReportObject.total + * @param {Number} iouReportObject.reportID + * @param {Number} chatReportID + */ +function setLocalIOUReportAndChatData(iouReportObject, chatReportID) { + // First persist the IOU Report data to Onyx + setLocalIOUReportData(iouReportData); + // Now update the associated chatReport data to ensure it has reference to updated report const chatReportObject = { hasOutstandingIOU: iouReportObject.stateNum === 1 && iouReportObject.total !== 0, iouReportID: iouReportObject.reportID, @@ -440,15 +446,27 @@ function removeOptimisticActions(reportID) { /** * @param {Number} iouReportID - ID of the report we are fetching - * @param {Number} chatReportID - associated chat report ID, required in order to link the reports in Onyx - * @param {Boolean} shouldUpdateChatReport - should we update and link the chat report to this IOU report? + * @param {Number} chatReportID - associated chatReportI which should be added to IOU report data + * + * Retrieve an IOU report, but do not associate it with the chatReportID. As an example, the user might be viewing an + * old settled report that does not have outstanding IOUs. + */ +function fetchIOUReportByID(iouReportID, chatReportID) { + fetchIOUReport(iouReportID, chatReportID) + .then(iouReportObject => setLocalIOUReportData(iouReportObject)) + .catch(error => console.error(`Error fetching IOU Report ${iouReportID}: ${error}`)); +} + +/** + * @param {Number} iouReportID - ID of the report we are fetching + * @param {Number} chatReportID - associated chatReportID which should be updated and linked * * Fetch an IOU Report and persist to Onyx, associating the IOUReport with a chatReport only if it is the active IOU * report. Else we would break the link to the active IOU for that chatReport (breaking badge and preview Components). */ -function fetchIOUReportByID(iouReportID, chatReportID, shouldUpdateChatReport) { +function fetchIOUReportByIDAndUpdateChatReport(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) - .then(iouReportObject => setLocalIOUReportData(iouReportObject, chatReportID, shouldUpdateChatReport)) + .then(iouReportObject => setLocalIOUReportAndChatData(iouReportObject, chatReportID)) .catch(error => console.error(`Error fetching IOU Report ${iouReportID}: ${error}`)); } @@ -525,9 +543,7 @@ function updateReportWithNewAction(reportID, reportAction) { // If chat report receives an action with IOU, update IOU object if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) { const iouReportID = reportAction.originalMessage.IOUReportID; - - // As this flow is for a new Action, we definitely need to update the local chat Report - fetchIOUReportByID(iouReportID, reportID, true); + fetchIOUReportByIDAndUpdateChatReport(iouReportID, reportID); } if (!ActiveClientManager.isClientTheLeader()) { @@ -1152,6 +1168,7 @@ export { fetchOrCreateChatReport, fetchChatReportsByIDs, fetchIOUReportByID, + fetchIOUReportByIDAndUpdateChatReport, addAction, updateLastReadActionID, setNewMarkerPosition, diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 383bbfb7272e..d44e2181a0ef 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -83,8 +83,8 @@ class IOUDetailsModal extends Component { } componentDidMount() { - // We should not update the chatReport data here, as there is no guarantee this is the active IOU - fetchIOUReportByID(this.props.route.params.iouReportID, this.props.route.params.chatReportID, false); + // As there is no guarantee this is the active IOU, we should avoid updating the chatReport here. + fetchIOUReportByID(this.props.route.params.iouReportID, this.props.route.params.chatReportID); } performIOUSettlement() { From bf76b0139a936c53654dde7f15617d9e4df5d2a9 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 19:23:13 +0100 Subject: [PATCH 099/141] additional formatting --- src/libs/actions/Report.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 97b720428ab4..43c05ff8d13f 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -371,7 +371,7 @@ function fetchChatReportsByIDs(chatList) { * @param {Number} iouReportObject.total * @param {Number} iouReportObject.reportID */ - function setLocalIOUReportData(iouReportObject) { +function setLocalIOUReportData(iouReportObject) { const iouReportKey = `${ONYXKEYS.COLLECTION.REPORT_IOUS}${iouReportObject.reportID}`; Onyx.merge(iouReportKey, iouReportObject); } @@ -387,7 +387,7 @@ function fetchChatReportsByIDs(chatList) { */ function setLocalIOUReportAndChatData(iouReportObject, chatReportID) { // First persist the IOU Report data to Onyx - setLocalIOUReportData(iouReportData); + setLocalIOUReportData(iouReportObject); // Now update the associated chatReport data to ensure it has reference to updated report const chatReportObject = { @@ -450,7 +450,7 @@ function removeOptimisticActions(reportID) { * * Retrieve an IOU report, but do not associate it with the chatReportID. As an example, the user might be viewing an * old settled report that does not have outstanding IOUs. - */ + */ function fetchIOUReportByID(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) .then(iouReportObject => setLocalIOUReportData(iouReportObject)) @@ -459,7 +459,7 @@ function fetchIOUReportByID(iouReportID, chatReportID) { /** * @param {Number} iouReportID - ID of the report we are fetching - * @param {Number} chatReportID - associated chatReportID which should be updated and linked + * @param {Number} chatReportID - associated chatReportID which should be updated and linked * * Fetch an IOU Report and persist to Onyx, associating the IOUReport with a chatReport only if it is the active IOU * report. Else we would break the link to the active IOU for that chatReport (breaking badge and preview Components). From 762d4527084317ab28a172453489a58510cec874 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 11 May 2021 19:44:27 +0100 Subject: [PATCH 100/141] minor formatting fix --- src/libs/actions/Report.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index a9cd8039a24d..d94137c30c76 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -469,6 +469,7 @@ function fetchIOUReportByIDAndUpdateChatReport(iouReportID, chatReportID) { .then(iouReportObject => setLocalIOUReportAndChatData(iouReportObject, chatReportID)) .catch(error => console.error(`Error fetching IOU Report ${iouReportID}: ${error}`)); } + /** * @param {Number} reportID * @param {Number} sequenceNumber From a3906dca55e4fc52770969e2a3519c6c534634ae Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Wed, 12 May 2021 11:00:22 +0100 Subject: [PATCH 101/141] rename iouTransactionProp to camelCase --- src/pages/iou/IOUDetailsModal.js | 4 ++-- src/pages/iou/IOUTransactions.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index d44e2181a0ef..cbca5e10e2c2 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -14,7 +14,7 @@ import ScreenWrapper from '../../components/ScreenWrapper'; import {settleIOUReport} from '../../libs/actions/IOU'; import {fetchIOUReportByID} from '../../libs/actions/Report'; import ReportActionItemIOUPreview from '../../components/ReportActionItemIOUPreview'; -import IOUTansactionPropTypes from './IOUTansactionPropTypes'; +import iouTansactionPropTypes from './iouTansactionPropTypes'; import IOUTransactions from './IOUTransactions'; const defaultProps = { @@ -57,7 +57,7 @@ const propTypes = { ownerEmail: PropTypes.string, // The IOU transactions - transactions: PropTypes.arrayOf(PropTypes.shape(IOUTansactionPropTypes)), + transactions: PropTypes.arrayOf(PropTypes.shape(iouTansactionPropTypes)), // Is the IOU report settled? hasOutstandingIOU: PropTypes.bool, diff --git a/src/pages/iou/IOUTransactions.js b/src/pages/iou/IOUTransactions.js index ed53b8310eb1..36b0c07ebd96 100644 --- a/src/pages/iou/IOUTransactions.js +++ b/src/pages/iou/IOUTransactions.js @@ -6,7 +6,7 @@ import PropTypes from 'prop-types'; import styles from '../../styles/styles'; import ONYXKEYS from '../../ONYXKEYS'; import ReportActionPropTypes from '../home/report/ReportActionPropTypes'; -import IOUTansactionPropTypes from './IOUTansactionPropTypes'; +import iouTansactionPropTypes from './iouTansactionPropTypes'; import ReportTransaction from '../../components/ReportTransaction'; const propTypes = { @@ -20,7 +20,7 @@ const propTypes = { iouReportID: PropTypes.number.isRequired, // Transactions for this IOU report - transactions: PropTypes.arrayOf(PropTypes.shape(IOUTansactionPropTypes)), + transactions: PropTypes.arrayOf(PropTypes.shape(iouTansactionPropTypes)), }; const defaultProps = { From b12022ac04b0ac8049d4da47d7c69eb885a499d2 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Wed, 12 May 2021 11:15:43 +0100 Subject: [PATCH 102/141] make ReportActionItemIOUAction a stateless function --- src/components/ReportActionItemIOUAction.js | 56 +++++++++------------ 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index 7b7c4cd2cede..5c2285901e32 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -1,4 +1,4 @@ -import React, {Component} from 'react'; +import React from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; @@ -31,36 +31,30 @@ const defaultProps = { chatReport: {}, }; -class ReportActionItemIOUAction extends Component { - constructor(props) { - super(props); - - this.launchIOUDetailsModal = this.launchIOUDetailsModal.bind(this); - } - - render() { - const hasMultipleParticipants = this.props.chatReport.participants.length >= 2; - return ( - - - {this.props.shouldDisplayPreview && ( - - )} - - ); - } -} +const ReportActionItemIOUAction = ({ + action, + chatReportID, + shouldDisplayPreview, + chatReport, +}) => ( + + Navigation.navigate(ROUTES.getIouDetailsRoute( + chatReportID, action.originalMessage.IOUReportID, + ))} + /> + {shouldDisplayPreview && ( + Navigation.navigate(ROUTES.getIouDetailsRoute( + chatReportID, action.originalMessage.IOUReportID, + ))} + /> + )} + +); ReportActionItemIOUAction.propTypes = propTypes; ReportActionItemIOUAction.defaultProps = defaultProps; From 4ae8b8528dd436207cd7baf2a7bd81b92511ded7 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Wed, 12 May 2021 16:28:03 +0100 Subject: [PATCH 103/141] clarify transaction/action matching logic --- src/pages/iou/IOUTransactions.js | 40 +++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/pages/iou/IOUTransactions.js b/src/pages/iou/IOUTransactions.js index 36b0c07ebd96..0980db459d91 100644 --- a/src/pages/iou/IOUTransactions.js +++ b/src/pages/iou/IOUTransactions.js @@ -29,22 +29,46 @@ const defaultProps = { }; class IOUTransactions extends Component { + constructor(props) { + super(props); + + this.getActionForTransaction = this.getActionForTransaction.bind(this); + } + + /** + * Given a transaction from an IOU Report, returns the chatReport action with a matching transactionID. Unless + * something has gone wrong with our storing logic, there should always exist an action for each transaction. + * + * @param {Object} transaction + * @returns {Object} action + */ + getActionForTransaction(transaction) { + const matchedAction = _.find(this.props.reportActions, (action) => { + if (action && action.originalMessage + && action.originalMessage.IOUTransactionID === transaction.transactionID) { + return action; + } + return false; + }); + if (!matchedAction) { + throw new Error(`Unable to locate a matching report action for transaction ${transaction.transactionID}!`); + } + + return matchedAction; + } + render() { return ( + {/* For each IOU transaction, get the matching report action */} {_.map(this.props.transactions, (transaction) => { - const actionForTransaction = _.find(this.props.reportActions, (action) => { - if (action && action.originalMessage) { - return action.originalMessage.IOUTransactionID === transaction.transactionID; - } - return false; - }); + const action = this.getActionForTransaction(transaction); return ( ); })} From ed2a8382318bc529848e80228a7f9ae89c33f81e Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Wed, 12 May 2021 16:29:01 +0100 Subject: [PATCH 104/141] replace null check with undefined check --- src/pages/iou/IOUDetailsModal.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index cbca5e10e2c2..fe7da9e7f0d5 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -19,7 +19,6 @@ import IOUTransactions from './IOUTransactions'; const defaultProps = { iou: {}, - iouReport: null, }; const propTypes = { @@ -61,7 +60,7 @@ const propTypes = { // Is the IOU report settled? hasOutstandingIOU: PropTypes.bool, - }), + }).isRequired, // Session info for the currently logged in user. session: PropTypes.shape({ @@ -97,7 +96,7 @@ class IOUDetailsModal extends Component { render() { const sessionEmail = lodashGet(this.props.session, 'email', null); - const reportIsLoading = _.isNull(this.props.iouReport); + const reportIsLoading = _.isUndefined(this.props.iouReport); return ( Date: Wed, 12 May 2021 16:37:05 +0100 Subject: [PATCH 105/141] remove iouTransaction props --- src/pages/iou/IOUTansactionPropTypes.js | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 src/pages/iou/IOUTansactionPropTypes.js diff --git a/src/pages/iou/IOUTansactionPropTypes.js b/src/pages/iou/IOUTansactionPropTypes.js deleted file mode 100644 index c87522bab590..000000000000 --- a/src/pages/iou/IOUTansactionPropTypes.js +++ /dev/null @@ -1,12 +0,0 @@ -import PropTypes from 'prop-types'; - -export default { - // The transaction currency - currency: PropTypes.string, - - // The transaction amount - total: PropTypes.number, - - // The transaction comment - comment: PropTypes.string, -}; From 95d175857f86d08bef36e4a7aeee897654e359b6 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Wed, 12 May 2021 16:38:09 +0100 Subject: [PATCH 106/141] replace prop-type file with camelCase (case change wasn't recognised by git) --- src/pages/iou/iouTansactionPropTypes.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/pages/iou/iouTansactionPropTypes.js diff --git a/src/pages/iou/iouTansactionPropTypes.js b/src/pages/iou/iouTansactionPropTypes.js new file mode 100644 index 000000000000..c87522bab590 --- /dev/null +++ b/src/pages/iou/iouTansactionPropTypes.js @@ -0,0 +1,12 @@ +import PropTypes from 'prop-types'; + +export default { + // The transaction currency + currency: PropTypes.string, + + // The transaction amount + total: PropTypes.number, + + // The transaction comment + comment: PropTypes.string, +}; From 124a547336e6acc0490a1e1da5c83c9f284cafdf Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Wed, 12 May 2021 19:09:45 +0100 Subject: [PATCH 107/141] modify pPromise chain to take function --- src/libs/actions/IOU.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 643bda0f3d04..7f80f77d35b2 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -128,8 +128,8 @@ function settleIOUReport({ throw new Error(`Error while settling IOU: ${response.message}`); } }) - .then(fetchChatReportsByIDs([chatReportID])) - .then(fetchIOUReportByIDAndUpdateChatReport(reportID, chatReportID)) + .then(() => fetchChatReportsByIDs([chatReportID])) + .then(() => fetchIOUReportByIDAndUpdateChatReport(reportID, chatReportID)) .catch(error => Onyx.merge(ONYXKEYS.IOU, {error})) .finally(() => Onyx.merge(ONYXKEYS.IOU, {loading: false})); } From fc637b8ed968a3d29da79278fa8a51488ec048e5 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 13 May 2021 12:05:39 +0100 Subject: [PATCH 108/141] locallize hardcoded strings --- src/components/ReportActionItemIOUPreview.js | 36 ++++++++++++-------- src/components/ReportActionItemIOUQuote.js | 14 ++++++-- src/languages/en.js | 5 +++ src/pages/iou/IOUDetailsModal.js | 31 ++++++++++------- 4 files changed, 57 insertions(+), 29 deletions(-) diff --git a/src/components/ReportActionItemIOUPreview.js b/src/components/ReportActionItemIOUPreview.js index a3083dd877fa..c53952a1f706 100644 --- a/src/components/ReportActionItemIOUPreview.js +++ b/src/components/ReportActionItemIOUPreview.js @@ -4,9 +4,11 @@ import PropTypes from 'prop-types'; import Str from 'expensify-common/lib/str'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; +import compose from '../libs/compose'; import styles from '../styles/styles'; import ONYXKEYS from '../ONYXKEYS'; import MultipleAvatars from './MultipleAvatars'; +import withLocalize, {withLocalizePropTypes} from './withLocalize'; const propTypes = { // Additional logic for displaying the pay button @@ -47,6 +49,8 @@ const propTypes = { // Currently logged in user email email: PropTypes.string, }).isRequired, + + ...withLocalizePropTypes, }; const defaultProps = { @@ -62,6 +66,7 @@ const ReportActionItemIOUPreview = ({ session, shouldHidePayButton, onPayButtonPressed, + translate, }) => { const sessionEmail = lodashGet(session, 'email', null); @@ -90,8 +95,8 @@ const ReportActionItemIOUPreview = ({ {cachedTotal} {iou.hasOutstandingIOU - ? `${managerName} owes ${ownerName}` - : `${ownerName} paid ${managerName}`} + ? translate('iou.owes', {manager: managerName, owner: ownerName}) + : translate('iou.paid', {manager: managerName, owner: ownerName})} @@ -112,7 +117,7 @@ const ReportActionItemIOUPreview = ({ styles.buttonSuccessText, ]} > - Pay + {translate('iou.pay')} )} @@ -124,14 +129,17 @@ ReportActionItemIOUPreview.propTypes = propTypes; ReportActionItemIOUPreview.defaultProps = defaultProps; ReportActionItemIOUPreview.displayName = 'ReportActionItemIOUPreview'; -export default withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, - }, - iou: { - key: ({iouReportID}) => `${ONYXKEYS.COLLECTION.REPORT_IOUS}${iouReportID}`, - }, - session: { - key: ONYXKEYS.SESSION, - }, -})(ReportActionItemIOUPreview); +export default compose( + withLocalize, + withOnyx({ + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS, + }, + iou: { + key: ({iouReportID}) => `${ONYXKEYS.COLLECTION.REPORT_IOUS}${iouReportID}`, + }, + session: { + key: ONYXKEYS.SESSION, + }, + }), +)(ReportActionItemIOUPreview); diff --git a/src/components/ReportActionItemIOUQuote.js b/src/components/ReportActionItemIOUQuote.js index 85a2b7c6c856..3759a3003cb3 100644 --- a/src/components/ReportActionItemIOUQuote.js +++ b/src/components/ReportActionItemIOUQuote.js @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import _ from 'underscore'; import styles from '../styles/styles'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; +import withLocalize, {withLocalizePropTypes} from './withLocalize'; const propTypes = { // All the data of the action @@ -14,6 +15,8 @@ const propTypes = { // Callback invoked when View Details is pressed onViewDetailsPressed: PropTypes.func, + + ...withLocalizePropTypes, }; const defaultProps = { @@ -21,7 +24,12 @@ const defaultProps = { onViewDetailsPressed: null, }; -const ReportActionItemIOUQuote = ({action, shouldShowViewDetailsLink, onViewDetailsPressed}) => ( +const ReportActionItemIOUQuote = ({ + action, + shouldShowViewDetailsLink, + onViewDetailsPressed, + translate, +}) => ( {_.map(action.message, (fragment, index) => ( @@ -34,7 +42,7 @@ const ReportActionItemIOUQuote = ({action, shouldShowViewDetailsLink, onViewDeta style={[styles.chatItemMessageLink]} onPress={onViewDetailsPressed} > - View Details + {translate('iou.viewDetails')} )} @@ -47,4 +55,4 @@ ReportActionItemIOUQuote.propTypes = propTypes; ReportActionItemIOUQuote.defaultProps = defaultProps; ReportActionItemIOUQuote.displayName = 'ReportActionItemIOUQuote'; -export default ReportActionItemIOUQuote; +export default withLocalize(ReportActionItemIOUQuote); diff --git a/src/languages/en.js b/src/languages/en.js index b6ea0f7f1801..0a781a6fdbbd 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -98,7 +98,12 @@ export default { confirm: 'Confirm', splitBill: 'Split Bill', requestMoney: 'Request Money', + pay: 'Pay', + viewDetails: 'View Details', + settleElsewhere: 'I\'ll settle up elsewhere', request: ({amount}) => `Request ${amount}`, + owes: ({manager, owner}) => `${manager} owes ${owner}`, + paid: ({owner, manager}) => `${owner} paid ${manager}`, }, loginField: { addYourPhoneToSettleViaVenmo: 'Add your phone number to settle up via Venmo.', diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index fe7da9e7f0d5..41a65b9fbcf3 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -16,6 +16,8 @@ import {fetchIOUReportByID} from '../../libs/actions/Report'; import ReportActionItemIOUPreview from '../../components/ReportActionItemIOUPreview'; import iouTansactionPropTypes from './iouTansactionPropTypes'; import IOUTransactions from './IOUTransactions'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import compose from '../../libs/compose'; const defaultProps = { iou: {}, @@ -67,6 +69,8 @@ const propTypes = { // Currently logged in user email email: PropTypes.string, }).isRequired, + + ...withLocalizePropTypes, }; class IOUDetailsModal extends Component { @@ -121,7 +125,7 @@ class IOUDetailsModal extends Component { && this.props.iouReport.managerEmail === sessionEmail && ( @@ -138,14 +142,17 @@ IOUDetailsModal.propTypes = propTypes; IOUDetailsModal.displayName = 'IOUDetailsModal'; IOUDetailsModal.defaultProps = defaultProps; -export default withOnyx({ - iou: { - key: ONYXKEYS.IOU, - }, - iouReport: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_IOUS}${route.params.iouReportID}`, - }, - session: { - key: ONYXKEYS.SESSION, - }, -})(IOUDetailsModal); +export default compose( + withLocalize, + withOnyx({ + iou: { + key: ONYXKEYS.IOU, + }, + iouReport: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_IOUS}${route.params.iouReportID}`, + }, + session: { + key: ONYXKEYS.SESSION, + }, + }), +)(IOUDetailsModal); From 50f8dbbd041982d0dfbe1449a4aa9d1489a8788b Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 13 May 2021 12:45:49 +0100 Subject: [PATCH 109/141] further document the logic behind linking chat and iou reports --- src/libs/actions/Report.js | 11 +++++++---- src/pages/iou/IOUDetailsModal.js | 3 ++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 8fbc929cdb05..6931a0ac70a8 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -448,8 +448,8 @@ function removeOptimisticActions(reportID) { * @param {Number} iouReportID - ID of the report we are fetching * @param {Number} chatReportID - associated chatReportI which should be added to IOU report data * - * Retrieve an IOU report, but do not associate it with the chatReportID. As an example, the user might be viewing an - * old settled report that does not have outstanding IOUs. + * Retrieve an iouReport, but do not associate it with the chatReport! As an example, the user might be viewing an old + * settled report that does not have outstanding IOUs, thus we must not override the chatReports `iouReportID` value. */ function fetchIOUReportByID(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) @@ -461,8 +461,8 @@ function fetchIOUReportByID(iouReportID, chatReportID) { * @param {Number} iouReportID - ID of the report we are fetching * @param {Number} chatReportID - associated chatReportID which should be updated and linked * - * Fetch an IOU Report and persist to Onyx, associating the IOUReport with a chatReport only if it is the active IOU - * report. Else we would break the link to the active IOU for that chatReport (breaking badge and preview Components). + * Retrieve an iouReport, associating it with a chatReport. This should be called in place of `fetchIOUReportByID` + * when we believe that the iouReport is open and currently linked to the chatReport. */ function fetchIOUReportByIDAndUpdateChatReport(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) @@ -553,6 +553,9 @@ function updateReportWithNewAction(reportID, reportAction) { // If chat report receives an action with IOU, update IOU object if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) { const iouReportID = reportAction.originalMessage.IOUReportID; + + // We have been notified of an update to this iouReport, therefore it must be the currently open iouReport. + // Therefore we should make sure the chatReport points to this iouReportID after fetching it. fetchIOUReportByIDAndUpdateChatReport(iouReportID, reportID); } diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 41a65b9fbcf3..23181110d713 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -86,7 +86,8 @@ class IOUDetailsModal extends Component { } componentDidMount() { - // As there is no guarantee this is the active IOU, we should avoid updating the chatReport here. + // Fetch the iouReport without linking it to the chatReport. Else, the chatReport's iouReportID field would + // wrongly be pointed to an old settled iouReport, overwriting the iouReportID of the open iouReport. fetchIOUReportByID(this.props.route.params.iouReportID, this.props.route.params.chatReportID); } From d780c1b8b50f5cbfbb28acf0b9c426e2f9fbdbaf Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 13 May 2021 13:04:15 +0100 Subject: [PATCH 110/141] further function documentation --- src/libs/actions/IOU.js | 5 +++-- src/libs/actions/Report.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 7f80f77d35b2..d7e938cb1d37 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -112,8 +112,7 @@ function createIOUSplit(params) { } /** - * Settles an IOU Report. As we can guarantee that the settled report was previously active, - * we should update the chatReport data too when calling `fetchIOUReportByID`. + * Settles an IOU Report and then retrieves the iou and chat reports to trigger updates to the UI. */ function settleIOUReport({ chatReportID, reportID, paymentMethodType, @@ -129,6 +128,8 @@ function settleIOUReport({ } }) .then(() => fetchChatReportsByIDs([chatReportID])) + // Any report that is being settled must be open, and must be currently set as open iouReport within the + // chatReport object. Therefore, we must also update the chatReport to break this existing link. .then(() => fetchIOUReportByIDAndUpdateChatReport(reportID, chatReportID)) .catch(error => Onyx.merge(ONYXKEYS.IOU, {error})) .finally(() => Onyx.merge(ONYXKEYS.IOU, {loading: false})); diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 6931a0ac70a8..d118535535e6 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -462,7 +462,7 @@ function fetchIOUReportByID(iouReportID, chatReportID) { * @param {Number} chatReportID - associated chatReportID which should be updated and linked * * Retrieve an iouReport, associating it with a chatReport. This should be called in place of `fetchIOUReportByID` - * when we believe that the iouReport is open and currently linked to the chatReport. + * when we believe that the iouReport is open and currently linked to the chatReport. */ function fetchIOUReportByIDAndUpdateChatReport(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) From cc6a450445dec307fd4c194569686dc4809b60c0 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 13 May 2021 13:19:11 +0100 Subject: [PATCH 111/141] add back blockquote for web override --- src/styles/styles.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/styles/styles.js b/src/styles/styles.js index 479395f270db..0e7db4ce679d 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -1455,6 +1455,17 @@ const webViewStyles = { flexShrink: 1, }, + blockquote: { + borderLeftColor: themeColors.border, + borderLeftWidth: 4, + paddingLeft: 12, + marginTop: 4, + marginBottom: 4, + + // Overwrite default HTML margin for blockquotes + marginLeft: 0, + }, + pre: { ...baseCodeTagStyles, paddingTop: 4, From 918b7b612c9c51af8aa8b6ede42741b9974cf8a1 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 13 May 2021 13:23:04 +0100 Subject: [PATCH 112/141] fix lint warning --- src/libs/actions/IOU.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index d7e938cb1d37..20663df27e97 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -128,6 +128,7 @@ function settleIOUReport({ } }) .then(() => fetchChatReportsByIDs([chatReportID])) + // Any report that is being settled must be open, and must be currently set as open iouReport within the // chatReport object. Therefore, we must also update the chatReport to break this existing link. .then(() => fetchIOUReportByIDAndUpdateChatReport(reportID, chatReportID)) From db86f85d04026ba4fd664fa0284f41931cb108a3 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 13 May 2021 18:00:12 +0100 Subject: [PATCH 113/141] refactor ReportActionItem IOU Components and rename --- .../IOUPreview.js} | 10 +++++----- .../IOUQuote.js} | 6 +++--- .../IOUReportAction.js} | 12 ++++++------ src/pages/home/report/ReportActionItem.js | 2 +- src/pages/iou/IOUDetailsModal.js | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) rename src/components/{ReportActionItemIOUPreview.js => ReportActionItem/IOUPreview.js} (95%) rename src/components/{ReportActionItemIOUQuote.js => ReportActionItem/IOUQuote.js} (89%) rename src/components/{ReportActionItemIOUAction.js => ReportActionItem/IOUReportAction.js} (84%) diff --git a/src/components/ReportActionItemIOUPreview.js b/src/components/ReportActionItem/IOUPreview.js similarity index 95% rename from src/components/ReportActionItemIOUPreview.js rename to src/components/ReportActionItem/IOUPreview.js index c53952a1f706..c6c4f108b66d 100644 --- a/src/components/ReportActionItemIOUPreview.js +++ b/src/components/ReportActionItem/IOUPreview.js @@ -4,11 +4,11 @@ import PropTypes from 'prop-types'; import Str from 'expensify-common/lib/str'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; -import compose from '../libs/compose'; -import styles from '../styles/styles'; -import ONYXKEYS from '../ONYXKEYS'; -import MultipleAvatars from './MultipleAvatars'; -import withLocalize, {withLocalizePropTypes} from './withLocalize'; +import compose from '../../libs/compose'; +import styles from '../../styles/styles'; +import ONYXKEYS from '../../ONYXKEYS'; +import MultipleAvatars from '../MultipleAvatars'; +import withLocalize, {withLocalizePropTypes} from '../withLocalize'; const propTypes = { // Additional logic for displaying the pay button diff --git a/src/components/ReportActionItemIOUQuote.js b/src/components/ReportActionItem/IOUQuote.js similarity index 89% rename from src/components/ReportActionItemIOUQuote.js rename to src/components/ReportActionItem/IOUQuote.js index 3759a3003cb3..7b5aab5beca9 100644 --- a/src/components/ReportActionItemIOUQuote.js +++ b/src/components/ReportActionItem/IOUQuote.js @@ -2,9 +2,9 @@ import React from 'react'; import {View, Text} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; -import styles from '../styles/styles'; -import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; -import withLocalize, {withLocalizePropTypes} from './withLocalize'; +import styles from '../../styles/styles'; +import ReportActionPropTypes from '../../pages/home/report/ReportActionPropTypes'; +import withLocalize, {withLocalizePropTypes} from '../withLocalize'; const propTypes = { // All the data of the action diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItem/IOUReportAction.js similarity index 84% rename from src/components/ReportActionItemIOUAction.js rename to src/components/ReportActionItem/IOUReportAction.js index 5c2285901e32..91574620ed08 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItem/IOUReportAction.js @@ -2,12 +2,12 @@ import React from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; -import ONYXKEYS from '../ONYXKEYS'; -import ReportActionItemIOUQuote from './ReportActionItemIOUQuote'; -import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; -import ReportActionItemIOUPreview from './ReportActionItemIOUPreview'; -import Navigation from '../libs/Navigation/Navigation'; -import ROUTES from '../ROUTES'; +import ONYXKEYS from '../../ONYXKEYS'; +import ReportActionItemIOUQuote from './IOUQuote'; +import ReportActionPropTypes from '../../pages/home/report/ReportActionPropTypes'; +import ReportActionItemIOUPreview from './IOUPreview'; +import Navigation from '../../libs/Navigation/Navigation'; +import ROUTES from '../../ROUTES'; const propTypes = { // All the data of the action diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index b144e96f86a9..4618e32e31bf 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -15,7 +15,7 @@ import PopoverWithMeasuredContent from '../../../components/PopoverWithMeasuredC import ReportActionItemSingle from './ReportActionItemSingle'; import ReportActionItemGrouped from './ReportActionItemGrouped'; import ReportActionContextMenu from './ReportActionContextMenu'; -import ReportActionItemIOUAction from '../../../components/ReportActionItemIOUAction'; +import ReportActionItemIOUAction from '../../../components/ReportActionItem/IOUReportAction'; import ReportActionItemMessage from './ReportActionItemMessage'; import UnreadActionIndicator from '../../../components/UnreadActionIndicator'; import ReportActionItemMessageEdit from './ReportActionItemMessageEdit'; diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 23181110d713..5a19a9068785 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -13,7 +13,7 @@ import ButtonWithLoader from '../../components/ButtonWithLoader'; import ScreenWrapper from '../../components/ScreenWrapper'; import {settleIOUReport} from '../../libs/actions/IOU'; import {fetchIOUReportByID} from '../../libs/actions/Report'; -import ReportActionItemIOUPreview from '../../components/ReportActionItemIOUPreview'; +import ReportActionItemIOUPreview from '../../components/ReportActionItem/IOUPreview'; import iouTansactionPropTypes from './iouTansactionPropTypes'; import IOUTransactions from './IOUTransactions'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; From bdbc65e468ee0856ddebba20c641482fcb2fd7a5 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 13 May 2021 18:20:57 +0100 Subject: [PATCH 114/141] define const for launch modal callback --- .../ReportActionItem/IOUReportAction.js | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/components/ReportActionItem/IOUReportAction.js b/src/components/ReportActionItem/IOUReportAction.js index 91574620ed08..823e27ef132a 100644 --- a/src/components/ReportActionItem/IOUReportAction.js +++ b/src/components/ReportActionItem/IOUReportAction.js @@ -1,5 +1,4 @@ import React from 'react'; -import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import ONYXKEYS from '../../ONYXKEYS'; @@ -36,25 +35,26 @@ const ReportActionItemIOUAction = ({ chatReportID, shouldDisplayPreview, chatReport, -}) => ( - - Navigation.navigate(ROUTES.getIouDetailsRoute( - chatReportID, action.originalMessage.IOUReportID, - ))} - /> - {shouldDisplayPreview && ( - Navigation.navigate(ROUTES.getIouDetailsRoute( - chatReportID, action.originalMessage.IOUReportID, - ))} +}) => { + const launchDetailsModal = () => { + Navigation.navigate(ROUTES.getIouDetailsRoute(chatReportID, action.originalMessage.IOUReportID)); + }; + return ( + <> + - )} - -); + {shouldDisplayPreview && ( + + )} + + ); +}; ReportActionItemIOUAction.propTypes = propTypes; ReportActionItemIOUAction.defaultProps = defaultProps; From 4c3ccf92f79e5932f9275a6e4c75c5e569261336 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 13 May 2021 18:37:06 +0100 Subject: [PATCH 115/141] replace reference to settled with paid, improve other comments --- src/components/ReportActionItem/IOUPreview.js | 5 ++--- src/libs/actions/IOU.js | 8 ++++---- src/libs/actions/Report.js | 6 +++--- src/pages/iou/IOUDetailsModal.js | 10 +++++----- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/components/ReportActionItem/IOUPreview.js b/src/components/ReportActionItem/IOUPreview.js index c6c4f108b66d..c3a80912b11e 100644 --- a/src/components/ReportActionItem/IOUPreview.js +++ b/src/components/ReportActionItem/IOUPreview.js @@ -33,7 +33,7 @@ const propTypes = { // Outstanding amount of this transaction cachedTotal: PropTypes.string, - // Is the IOU report settled? + // Does the report have an outstanding IOU that needs to be paid? hasOutstandingIOU: PropTypes.bool, }), @@ -70,8 +70,7 @@ const ReportActionItemIOUPreview = ({ }) => { const sessionEmail = lodashGet(session, 'email', null); - // Pay button should be visible to manager person in the report - // Check if the currently logged in user is the manager. + // Pay button should only be visible to the manager of the report. const isCurrentUserManager = iou.managerEmail === sessionEmail; const managerName = lodashGet( diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 20663df27e97..d8d7722b33c7 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -112,9 +112,9 @@ function createIOUSplit(params) { } /** - * Settles an IOU Report and then retrieves the iou and chat reports to trigger updates to the UI. + * Pays an IOU Report and then retrieves the iou and chat reports to trigger updates to the UI. */ -function settleIOUReport({ +function payIOUReport({ chatReportID, reportID, paymentMethodType, }) { Onyx.merge(ONYXKEYS.IOU, {loading: true, error: ''}); @@ -129,7 +129,7 @@ function settleIOUReport({ }) .then(() => fetchChatReportsByIDs([chatReportID])) - // Any report that is being settled must be open, and must be currently set as open iouReport within the + // Any report that is being paid must be open, and must be currently set as open iouReport within the // chatReport object. Therefore, we must also update the chatReport to break this existing link. .then(() => fetchIOUReportByIDAndUpdateChatReport(reportID, chatReportID)) .catch(error => Onyx.merge(ONYXKEYS.IOU, {error})) @@ -140,5 +140,5 @@ export { getPreferredCurrency, createIOUTransaction, createIOUSplit, - settleIOUReport, + payIOUReport, }; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index d118535535e6..6c35abbb4728 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -203,7 +203,7 @@ function getSimplifiedIOUReport(reportData, chatReportID) { currency: transaction.currency, created: transaction.created, comment: transaction.comment, - })).reverse(); // transactionList is returned in desc order, but we desire asc order + })).reverse(); // transactionList returns in desc order, we desire asc order (they must be sorted by creation date) return { reportID: reportData.reportID, @@ -245,7 +245,7 @@ function fetchIOUReport(iouReportID, chatReportID) { const iouReportData = response.reports[iouReportID]; if (!iouReportData) { // This is occuring due to 'fetchChatReportsByIDs' calling this function with a IOUReportID that has - // already been settled. In this case it is expected that no data is returned from 'getIOU'. + // already been paid. In this case it is expected that no data is returned from 'getIOU'. return; } return getSimplifiedIOUReport(iouReportData, chatReportID); @@ -449,7 +449,7 @@ function removeOptimisticActions(reportID) { * @param {Number} chatReportID - associated chatReportI which should be added to IOU report data * * Retrieve an iouReport, but do not associate it with the chatReport! As an example, the user might be viewing an old - * settled report that does not have outstanding IOUs, thus we must not override the chatReports `iouReportID` value. + * paid report that does not have outstanding IOUs, thus we must not override the chatReports `iouReportID` value. */ function fetchIOUReportByID(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 5a19a9068785..3e58b33b91dc 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -11,7 +11,7 @@ import HeaderWithCloseButton from '../../components/HeaderWithCloseButton'; import Navigation from '../../libs/Navigation/Navigation'; import ButtonWithLoader from '../../components/ButtonWithLoader'; import ScreenWrapper from '../../components/ScreenWrapper'; -import {settleIOUReport} from '../../libs/actions/IOU'; +import {payIOUReport} from '../../libs/actions/IOU'; import {fetchIOUReportByID} from '../../libs/actions/Report'; import ReportActionItemIOUPreview from '../../components/ReportActionItem/IOUPreview'; import iouTansactionPropTypes from './iouTansactionPropTypes'; @@ -39,7 +39,7 @@ const propTypes = { /* Onyx Props */ // Holds data related to IOU view state, rather than the underlying IOU data. iou: PropTypes.shape({ - // Is the IOU Report currently being settled + // Is the IOU Report currently being paid? loading: PropTypes.bool, // Error message, empty represents no error @@ -60,7 +60,7 @@ const propTypes = { // The IOU transactions transactions: PropTypes.arrayOf(PropTypes.shape(iouTansactionPropTypes)), - // Is the IOU report settled? + // Does the report have an outstanding IOU that needs to be paid? hasOutstandingIOU: PropTypes.bool, }).isRequired, @@ -87,12 +87,12 @@ class IOUDetailsModal extends Component { componentDidMount() { // Fetch the iouReport without linking it to the chatReport. Else, the chatReport's iouReportID field would - // wrongly be pointed to an old settled iouReport, overwriting the iouReportID of the open iouReport. + // wrongly be pointed to an old paid iouReport, overwriting the iouReportID of the open iouReport. fetchIOUReportByID(this.props.route.params.iouReportID, this.props.route.params.chatReportID); } performIOUSettlement() { - settleIOUReport({ + payIOUReport({ chatReportID: this.props.route.params.chatReportID, reportID: this.props.route.params.iouReportID, paymentMethodType: this.state.settlementType, From 1984da13da7cbc2fe300d5366b0d5d08358be104 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 13 May 2021 19:01:29 +0100 Subject: [PATCH 116/141] remove unnecessary use of View, reorder defaultProps, remove function --- src/components/ReportTransaction.js | 6 ++-- src/libs/actions/Report.js | 45 +++++++++++------------------ src/pages/iou/IOUDetailsModal.js | 13 ++++----- 3 files changed, 26 insertions(+), 38 deletions(-) diff --git a/src/components/ReportTransaction.js b/src/components/ReportTransaction.js index c913a0bfb3f6..2e9eaf9f82a6 100644 --- a/src/components/ReportTransaction.js +++ b/src/components/ReportTransaction.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {View, Text} from 'react-native'; +import Text from 'react-native'; import styles from '../styles/styles'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; import ReportActionItemSingle from '../pages/home/report/ReportActionItemSingle'; @@ -21,7 +21,7 @@ const propTypes = { const ReportTransaction = ({ action, }) => ( - + <> - + ); ReportTransaction.displayName = 'ReportTransaction'; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 6c35abbb4728..8a5967fb04f8 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -376,33 +376,6 @@ function setLocalIOUReportData(iouReportObject) { Onyx.merge(iouReportKey, iouReportObject); } -/** - * Given IOU object and associated chatReportID, save the data to Onyx. - * - * @param {Object} iouReportObject - * @param {Number} iouReportObject.stateNum - * @param {Number} iouReportObject.total - * @param {Number} iouReportObject.reportID - * @param {Number} chatReportID - */ -function setLocalIOUReportAndChatData(iouReportObject, chatReportID) { - // First persist the IOU Report data to Onyx - setLocalIOUReportData(iouReportObject); - - // Now update the associated chatReport data to ensure it has reference to updated report - const chatReportObject = { - hasOutstandingIOU: iouReportObject.stateNum === 1 && iouReportObject.total !== 0, - iouReportID: iouReportObject.reportID, - }; - - if (!chatReportObject.hasOutstandingIOU) { - chatReportObject.iouReportID = null; - } - - const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`; - Onyx.merge(reportKey, chatReportObject); -} - /** * Update the lastRead actionID and timestamp in local memory and Onyx * @@ -466,7 +439,23 @@ function fetchIOUReportByID(iouReportID, chatReportID) { */ function fetchIOUReportByIDAndUpdateChatReport(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) - .then(iouReportObject => setLocalIOUReportAndChatData(iouReportObject, chatReportID)) + .then((iouReportObject) => { + // First persist the IOU Report data to Onyx + setLocalIOUReportData(iouReportObject); + + // Now update the associated chatReport data to ensure it has reference to updated report + const chatReportObject = { + hasOutstandingIOU: iouReportObject.stateNum === 1 && iouReportObject.total !== 0, + iouReportID: iouReportObject.reportID, + }; + + if (!chatReportObject.hasOutstandingIOU) { + chatReportObject.iouReportID = null; + } + + const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`; + Onyx.merge(reportKey, chatReportObject); + }) .catch(error => console.error(`Error fetching IOU Report ${iouReportID}: ${error}`)); } diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 3e58b33b91dc..cf1de8c2cfb7 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -19,19 +19,15 @@ import IOUTransactions from './IOUTransactions'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import compose from '../../libs/compose'; -const defaultProps = { - iou: {}, -}; - const propTypes = { // URL Route params route: PropTypes.shape({ // Params from the URL path params: PropTypes.shape({ - // chatReportID passed via route /iou/:chatReportID + // chatReportID passed via route: /iou/details/:chatReportID/:iouReportID chatReportID: PropTypes.string, - // iouReportID passed via route /iou/:iouReportID + // iouReportID passed via route: /iou/details/:chatReportID/:iouReportID iouReportID: PropTypes.string, }), }).isRequired, @@ -73,6 +69,10 @@ const propTypes = { ...withLocalizePropTypes, }; +const defaultProps = { + iou: {}, +}; + class IOUDetailsModal extends Component { constructor(props) { super(props); @@ -140,7 +140,6 @@ class IOUDetailsModal extends Component { } IOUDetailsModal.propTypes = propTypes; -IOUDetailsModal.displayName = 'IOUDetailsModal'; IOUDetailsModal.defaultProps = defaultProps; export default compose( From 61e044660a931e7352500ed09e26585cfb0ae5db Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Thu, 13 May 2021 19:06:35 +0100 Subject: [PATCH 117/141] style improvements, based on review feedback --- src/pages/iou/IOUDetailsModal.js | 2 +- src/styles/styles.js | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index cf1de8c2cfb7..21781c299a77 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -110,7 +110,7 @@ class IOUDetailsModal extends Component { /> {reportIsLoading ? : ( - + Date: Thu, 13 May 2021 19:48:45 +0100 Subject: [PATCH 118/141] further review feedback applied --- .../ReportActionItem/IOUReportAction.js | 3 ++- src/components/ReportTransaction.js | 2 +- src/libs/actions/Report.js | 19 ++++++++++--------- src/pages/home/report/ReportActionItem.js | 1 - 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/components/ReportActionItem/IOUReportAction.js b/src/components/ReportActionItem/IOUReportAction.js index 823e27ef132a..32dc42d08695 100644 --- a/src/components/ReportActionItem/IOUReportAction.js +++ b/src/components/ReportActionItem/IOUReportAction.js @@ -39,11 +39,12 @@ const ReportActionItemIOUAction = ({ const launchDetailsModal = () => { Navigation.navigate(ROUTES.getIouDetailsRoute(chatReportID, action.originalMessage.IOUReportID)); }; + const hasMultipleParticipants = chatReport.participants.length >= 2; return ( <> {shouldDisplayPreview && ( diff --git a/src/components/ReportTransaction.js b/src/components/ReportTransaction.js index 2e9eaf9f82a6..0b36e5115de6 100644 --- a/src/components/ReportTransaction.js +++ b/src/components/ReportTransaction.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Text from 'react-native'; +import {Text} from 'react-native'; import styles from '../styles/styles'; import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; import ReportActionItemSingle from '../pages/home/report/ReportActionItemSingle'; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 8a5967fb04f8..4f6abdba2989 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -244,8 +244,8 @@ function fetchIOUReport(iouReportID, chatReportID) { } const iouReportData = response.reports[iouReportID]; if (!iouReportData) { - // This is occuring due to 'fetchChatReportsByIDs' calling this function with a IOUReportID that has - // already been paid. In this case it is expected that no data is returned from 'getIOU'. + // IOU data for a report will be missing when the IOU report has already been settled. + // This is expected and we return early as no further processing can be done return; } return getSimplifiedIOUReport(iouReportData, chatReportID); @@ -418,11 +418,12 @@ function removeOptimisticActions(reportID) { } /** - * @param {Number} iouReportID - ID of the report we are fetching - * @param {Number} chatReportID - associated chatReportI which should be added to IOU report data + * Retrieve an iouReport, but do not associate it with the chatReport. For example, the user could be viewing a paid + * report, and we must never override the chatReport's 'iouReportID' value with a paid report ID. Doing so would cause + * 'hasOutstandingIOU' to be set to false, preventing the IOUPreview and IOUBadge Components from displaying. * - * Retrieve an iouReport, but do not associate it with the chatReport! As an example, the user might be viewing an old - * paid report that does not have outstanding IOUs, thus we must not override the chatReports `iouReportID` value. + * @param {Number} iouReportID - ID of the report we are fetching + * @param {Number} chatReportID - associated chatReportID which should be added to IOU report data */ function fetchIOUReportByID(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) @@ -431,11 +432,11 @@ function fetchIOUReportByID(iouReportID, chatReportID) { } /** - * @param {Number} iouReportID - ID of the report we are fetching - * @param {Number} chatReportID - associated chatReportID which should be updated and linked - * * Retrieve an iouReport, associating it with a chatReport. This should be called in place of `fetchIOUReportByID` * when we believe that the iouReport is open and currently linked to the chatReport. + * + * @param {Number} iouReportID - ID of the report we are fetching + * @param {Number} chatReportID - associated chatReportID which should be updated and linked */ function fetchIOUReportByIDAndUpdateChatReport(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 4618e32e31bf..f9bead064a02 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -80,7 +80,6 @@ class ReportActionItem extends Component { || this.props.draftMessage !== nextProps.draftMessage || this.props.isMostRecentIOUReportAction !== nextProps.isMostRecentIOUReportAction || this.props.hasOutstandingIOU !== nextProps.hasOutstandingIOU - || (this.props.shouldDisplayNewIndicator !== nextProps.shouldDisplayNewIndicator) || this.props.shouldDisplayNewIndicator !== nextProps.shouldDisplayNewIndicator || !_.isEqual(this.props.action, nextProps.action); } From 5840b667cd485d1a63cdc02fb14cd7899d4f0df6 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 14 May 2021 15:27:12 +0100 Subject: [PATCH 119/141] move follow up functions out of the Promise chain --- src/libs/actions/IOU.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index d8d7722b33c7..8b9ce709fe21 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -126,12 +126,12 @@ function payIOUReport({ if (response.jsonCode !== 200) { throw new Error(`Error while settling IOU: ${response.message}`); } - }) - .then(() => fetchChatReportsByIDs([chatReportID])) + fetchChatReportsByIDs([chatReportID]); - // Any report that is being paid must be open, and must be currently set as open iouReport within the - // chatReport object. Therefore, we must also update the chatReport to break this existing link. - .then(() => fetchIOUReportByIDAndUpdateChatReport(reportID, chatReportID)) + // Any report that is being paid must be open, and must be currently set as open iouReport within the + // chatReport object. Therefore, we must also update the chatReport to break this existing link. + fetchIOUReportByIDAndUpdateChatReport(reportID, chatReportID); + }) .catch(error => Onyx.merge(ONYXKEYS.IOU, {error})) .finally(() => Onyx.merge(ONYXKEYS.IOU, {loading: false})); } From cd0bf1a29bf7437ffa13854fec4e52e105e47d2e Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 14 May 2021 16:00:19 +0100 Subject: [PATCH 120/141] revert IOU error back to boolean, remove unnecessary updates --- src/Expensify.js | 2 +- src/libs/actions/IOU.js | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Expensify.js b/src/Expensify.js index a07cf9e199b7..bd29c9486139 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -26,7 +26,7 @@ Onyx.init({ [ONYXKEYS.SESSION]: {loading: false, shouldShowComposeInput: true}, [ONYXKEYS.ACCOUNT]: CONST.DEFAULT_ACCOUNT_DATA, [ONYXKEYS.NETWORK]: {isOffline: false}, - [ONYXKEYS.IOU]: {loading: false, error: '', creatingIOUTransaction: false}, + [ONYXKEYS.IOU]: {loading: false, error: false, creatingIOUTransaction: false}, }, registerStorageEventListener: (onStorageEvent) => { listenToStorageEvents(onStorageEvent); diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 8b9ce709fe21..b40d4b48c9b8 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -56,7 +56,7 @@ function getIOUReportsForNewTransaction(requestParams) { Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, chatReportsToUpdate); return Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT_IOUS, iouReportsToUpdate); }) - .catch(() => Onyx.merge(ONYXKEYS.IOU, {loading: false, creatingIOUTransaction: false, error: ''})) + .catch(() => Onyx.merge(ONYXKEYS.IOU, {error: true})) .finally(() => Onyx.merge(ONYXKEYS.IOU, {loading: false, creatingIOUTransaction: false})); } @@ -69,7 +69,7 @@ function getIOUReportsForNewTransaction(requestParams) { * @param {String} params.debtorEmail */ function createIOUTransaction(params) { - Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: ''}); + Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: false}); API.CreateIOUTransaction(params) .then(data => getIOUReportsForNewTransaction([data])); } @@ -83,7 +83,7 @@ function createIOUTransaction(params) { * @param {String} params.currency */ function createIOUSplit(params) { - Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: ''}); + Onyx.merge(ONYXKEYS.IOU, {loading: true, creatingIOUTransaction: true, error: false}); API.CreateChatReport({ emailList: params.splits.map(participant => participant.email).join(','), @@ -117,7 +117,7 @@ function createIOUSplit(params) { function payIOUReport({ chatReportID, reportID, paymentMethodType, }) { - Onyx.merge(ONYXKEYS.IOU, {loading: true, error: ''}); + Onyx.merge(ONYXKEYS.IOU, {loading: true, error: false}); API.PayIOU({ reportID, paymentMethodType, @@ -132,7 +132,10 @@ function payIOUReport({ // chatReport object. Therefore, we must also update the chatReport to break this existing link. fetchIOUReportByIDAndUpdateChatReport(reportID, chatReportID); }) - .catch(error => Onyx.merge(ONYXKEYS.IOU, {error})) + .catch(error => { + console.error(`Error Paying iouReport: ${error}`); + Onyx.merge(ONYXKEYS.IOU, {error: true}); + }) .finally(() => Onyx.merge(ONYXKEYS.IOU, {loading: false})); } From 8719dd392702276ebdaf494f27f6bd8f81aabd39 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 14 May 2021 17:16:35 +0100 Subject: [PATCH 121/141] improve error handling --- src/libs/actions/IOU.js | 4 ++-- src/libs/actions/Report.js | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index b40d4b48c9b8..bbd1a0c2adbd 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -124,7 +124,7 @@ function payIOUReport({ }) .then((response) => { if (response.jsonCode !== 200) { - throw new Error(`Error while settling IOU: ${response.message}`); + throw new Error(response.message); } fetchChatReportsByIDs([chatReportID]); @@ -132,7 +132,7 @@ function payIOUReport({ // chatReport object. Therefore, we must also update the chatReport to break this existing link. fetchIOUReportByIDAndUpdateChatReport(reportID, chatReportID); }) - .catch(error => { + .catch((error) => { console.error(`Error Paying iouReport: ${error}`); Onyx.merge(ONYXKEYS.IOU, {error: true}); }) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 4f6abdba2989..a7dbf320699c 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -203,7 +203,8 @@ function getSimplifiedIOUReport(reportData, chatReportID) { currency: transaction.currency, created: transaction.created, comment: transaction.comment, - })).reverse(); // transactionList returns in desc order, we desire asc order (they must be sorted by creation date) + })).reverse(); // `transactionList` data is returned ordered by desc creation date, they are changed to asc order + // because we must instead display them in the order that they were created (asc). return { reportID: reportData.reportID, @@ -427,8 +428,7 @@ function removeOptimisticActions(reportID) { */ function fetchIOUReportByID(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) - .then(iouReportObject => setLocalIOUReportData(iouReportObject)) - .catch(error => console.error(`Error fetching IOU Report ${iouReportID}: ${error}`)); + .then(iouReportObject => setLocalIOUReportData(iouReportObject)); } /** @@ -456,8 +456,7 @@ function fetchIOUReportByIDAndUpdateChatReport(iouReportID, chatReportID) { const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`; Onyx.merge(reportKey, chatReportObject); - }) - .catch(error => console.error(`Error fetching IOU Report ${iouReportID}: ${error}`)); + }); } /** From b120209795453dbf47515b4c4be60d76cb12017a Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Fri, 14 May 2021 18:00:26 +0100 Subject: [PATCH 122/141] improve fetchIOUReportByIDAndUpdateChatReport usage comment --- src/libs/actions/IOU.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index bbd1a0c2adbd..01b192e48aa3 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -128,8 +128,11 @@ function payIOUReport({ } fetchChatReportsByIDs([chatReportID]); - // Any report that is being paid must be open, and must be currently set as open iouReport within the - // chatReport object. Therefore, we must also update the chatReport to break this existing link. + // Each chatReport cannot have more than one open iouReport at any time. While an iouReport is open, the + // chatReport stored in Onyx must have a reference to it via the 'iouReportID' field, preventing the need + // to retrieve linked reports via the API when rendering Components. All iouReport's being paid are open + // and will be currently set as the chatReports 'iouReportID' field. Therefore when paying an iou we must + // also update the chatReport, clearing the 'iouReportID' field and breaking the existing link fetchIOUReportByIDAndUpdateChatReport(reportID, chatReportID); }) .catch((error) => { From a990a5479553de550354b98da9be264cf4a9fea9 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 17 May 2021 11:19:41 +0100 Subject: [PATCH 123/141] refactor IOU components back to original location and name for now --- ...UReportAction.js => ReportActionItemIOUAction.js} | 12 ++++++------ .../IOUPreview.js => ReportActionItemIOUPreview.js} | 10 +++++----- .../IOUQuote.js => ReportActionItemIOUQuote.js} | 6 +++--- src/pages/home/report/ReportActionItem.js | 2 +- src/pages/iou/IOUDetailsModal.js | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) rename src/components/{ReportActionItem/IOUReportAction.js => ReportActionItemIOUAction.js} (84%) rename src/components/{ReportActionItem/IOUPreview.js => ReportActionItemIOUPreview.js} (95%) rename src/components/{ReportActionItem/IOUQuote.js => ReportActionItemIOUQuote.js} (89%) diff --git a/src/components/ReportActionItem/IOUReportAction.js b/src/components/ReportActionItemIOUAction.js similarity index 84% rename from src/components/ReportActionItem/IOUReportAction.js rename to src/components/ReportActionItemIOUAction.js index 32dc42d08695..6cb189fe5d75 100644 --- a/src/components/ReportActionItem/IOUReportAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -1,12 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; -import ONYXKEYS from '../../ONYXKEYS'; -import ReportActionItemIOUQuote from './IOUQuote'; -import ReportActionPropTypes from '../../pages/home/report/ReportActionPropTypes'; -import ReportActionItemIOUPreview from './IOUPreview'; -import Navigation from '../../libs/Navigation/Navigation'; -import ROUTES from '../../ROUTES'; +import ONYXKEYS from '../ONYXKEYS'; +import ReportActionItemIOUQuote from './ReportActionItemIOUQuote'; +import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; +import ReportActionItemIOUPreview from './ReportActionItemIOUPreview'; +import Navigation from '../libs/Navigation/Navigation'; +import ROUTES from '../ROUTES'; const propTypes = { // All the data of the action diff --git a/src/components/ReportActionItem/IOUPreview.js b/src/components/ReportActionItemIOUPreview.js similarity index 95% rename from src/components/ReportActionItem/IOUPreview.js rename to src/components/ReportActionItemIOUPreview.js index c3a80912b11e..11a0b49e540d 100644 --- a/src/components/ReportActionItem/IOUPreview.js +++ b/src/components/ReportActionItemIOUPreview.js @@ -4,11 +4,11 @@ import PropTypes from 'prop-types'; import Str from 'expensify-common/lib/str'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; -import compose from '../../libs/compose'; -import styles from '../../styles/styles'; -import ONYXKEYS from '../../ONYXKEYS'; -import MultipleAvatars from '../MultipleAvatars'; -import withLocalize, {withLocalizePropTypes} from '../withLocalize'; +import compose from '../libs/compose'; +import styles from '../styles/styles'; +import ONYXKEYS from '../ONYXKEYS'; +import MultipleAvatars from './MultipleAvatars'; +import withLocalize, {withLocalizePropTypes} from './withLocalize'; const propTypes = { // Additional logic for displaying the pay button diff --git a/src/components/ReportActionItem/IOUQuote.js b/src/components/ReportActionItemIOUQuote.js similarity index 89% rename from src/components/ReportActionItem/IOUQuote.js rename to src/components/ReportActionItemIOUQuote.js index 7b5aab5beca9..3759a3003cb3 100644 --- a/src/components/ReportActionItem/IOUQuote.js +++ b/src/components/ReportActionItemIOUQuote.js @@ -2,9 +2,9 @@ import React from 'react'; import {View, Text} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; -import styles from '../../styles/styles'; -import ReportActionPropTypes from '../../pages/home/report/ReportActionPropTypes'; -import withLocalize, {withLocalizePropTypes} from '../withLocalize'; +import styles from '../styles/styles'; +import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; +import withLocalize, {withLocalizePropTypes} from './withLocalize'; const propTypes = { // All the data of the action diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index f9bead064a02..5a10a134ebea 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -15,7 +15,7 @@ import PopoverWithMeasuredContent from '../../../components/PopoverWithMeasuredC import ReportActionItemSingle from './ReportActionItemSingle'; import ReportActionItemGrouped from './ReportActionItemGrouped'; import ReportActionContextMenu from './ReportActionContextMenu'; -import ReportActionItemIOUAction from '../../../components/ReportActionItem/IOUReportAction'; +import ReportActionItemIOUAction from '../../../components/ReportActionItemIOUAction'; import ReportActionItemMessage from './ReportActionItemMessage'; import UnreadActionIndicator from '../../../components/UnreadActionIndicator'; import ReportActionItemMessageEdit from './ReportActionItemMessageEdit'; diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 21781c299a77..d21465948787 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -13,7 +13,7 @@ import ButtonWithLoader from '../../components/ButtonWithLoader'; import ScreenWrapper from '../../components/ScreenWrapper'; import {payIOUReport} from '../../libs/actions/IOU'; import {fetchIOUReportByID} from '../../libs/actions/Report'; -import ReportActionItemIOUPreview from '../../components/ReportActionItem/IOUPreview'; +import ReportActionItemIOUPreview from '../../components/ReportActionItemIOUPreview'; import iouTansactionPropTypes from './iouTansactionPropTypes'; import IOUTransactions from './IOUTransactions'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; From 9f85df18b5f8d75c023ea314704b8e203fe5e2ea Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 17 May 2021 11:20:13 +0100 Subject: [PATCH 124/141] update prop type to fix type warning --- src/pages/iou/IOUDetailsModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index d21465948787..940fa00c97ac 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -39,7 +39,7 @@ const propTypes = { loading: PropTypes.bool, // Error message, empty represents no error - error: PropTypes.string, + error: PropTypes.bool, }), // IOU Report data object From 260aca972d3786758bc2de875f35d61fab3fb7e1 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 17 May 2021 13:45:33 +0100 Subject: [PATCH 125/141] reuse the translate function --- src/languages/en.js | 2 +- src/pages/DetailsPage.js | 2 +- src/pages/ReportParticipantsPage.js | 2 +- src/pages/iou/IOUDetailsModal.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index ff94569687e9..6441c2415003 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -26,6 +26,7 @@ export default { phoneNumber: 'Phone Number', email: 'Email', and: 'and', + details: 'Details', }, attachmentPicker: { cameraPermissionRequired: 'Camera Permission Required', @@ -202,7 +203,6 @@ export default { resendLink: 'Resend Link', }, detailsPage: { - details: 'Details', localTime: 'Local Time', }, newGroupPage: { diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js index 0193bca74df2..ae13e9271c6f 100755 --- a/src/pages/DetailsPage.js +++ b/src/pages/DetailsPage.js @@ -48,7 +48,7 @@ const DetailsPage = ({personalDetails, route, translate}) => { return ( Navigation.dismissModal()} diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js index f211ea82a1d6..08785faeeb04 100755 --- a/src/pages/ReportParticipantsPage.js +++ b/src/pages/ReportParticipantsPage.js @@ -84,7 +84,7 @@ const ReportParticipantsPage = ({ return ( {reportIsLoading ? : ( From 8fbe49d7b6e4ed15ae449358a48c022f92f89054 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 17 May 2021 16:37:29 +0100 Subject: [PATCH 126/141] rewrite the fetchIOUReportByID... function docs to make clearer --- src/libs/actions/IOU.js | 12 ++++------ src/libs/actions/Report.js | 41 ++++++++++++++++++++++---------- src/pages/iou/IOUDetailsModal.js | 4 +--- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 01b192e48aa3..87c0b6fe2f9f 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -2,7 +2,7 @@ import Onyx from 'react-native-onyx'; import _ from 'underscore'; import ONYXKEYS from '../../ONYXKEYS'; import * as API from '../API'; -import {getSimplifiedIOUReport, fetchChatReportsByIDs, fetchIOUReportByIDAndUpdateChatReport} from './Report'; +import {getSimplifiedIOUReport, fetchChatReportsByIDs, fetchIOUReportByIDAndUpdateChatReportLink} from './Report'; /** * Retrieve the users preferred currency @@ -128,12 +128,10 @@ function payIOUReport({ } fetchChatReportsByIDs([chatReportID]); - // Each chatReport cannot have more than one open iouReport at any time. While an iouReport is open, the - // chatReport stored in Onyx must have a reference to it via the 'iouReportID' field, preventing the need - // to retrieve linked reports via the API when rendering Components. All iouReport's being paid are open - // and will be currently set as the chatReports 'iouReportID' field. Therefore when paying an iou we must - // also update the chatReport, clearing the 'iouReportID' field and breaking the existing link - fetchIOUReportByIDAndUpdateChatReport(reportID, chatReportID); + // An iouReport that is being paid must be open, and open iouReports are always linked to the associated + // chatReport via its Onyx data. As this link must be kept updated, after fetching the report we must also + // update the chatReport link - see function for more info. + fetchIOUReportByIDAndUpdateChatReportLink(reportID, chatReportID); }) .catch((error) => { console.error(`Error Paying iouReport: ${error}`); diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index eec1dc47b53c..1f41921cf644 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -419,12 +419,19 @@ function removeOptimisticActions(reportID) { } /** - * Retrieve an iouReport, but do not associate it with the chatReport. For example, the user could be viewing a paid - * report, and we must never override the chatReport's 'iouReportID' value with a paid report ID. Doing so would cause - * 'hasOutstandingIOU' to be set to false, preventing the IOUPreview and IOUBadge Components from displaying. + * If an iouReport is open (has an IOU, but is not yet paid) then we maintain a link between it and the associated + * chatReport in Onyx, simplifying IOU data retrieval and reducing necessary API calls when displaying IOU components: + * - chatReport: {id: 123, iouReportID: 987, ...} + * - iouReport: {id: 987, chatReportID: 123, ...} + * + * This function allows us to fetch an iouReport without updating the report link data, preventing the chatReport's + * 'iouReportID' value from being updated. As an example, this is desired when fetching historical reports, to avoid overwritting an + * open iouReportID with a closed iouReportID. If this was to occur, unpaid IOUs would not be highlighted to the user. + * + * If updating the report link data is desired, use `fetchIOUReportByIDAndUpdateChatReportLink` instead. * * @param {Number} iouReportID - ID of the report we are fetching - * @param {Number} chatReportID - associated chatReportID which should be added to IOU report data + * @param {Number} chatReportID - associated chatReportID which should be updated and linked */ function fetchIOUReportByID(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) @@ -432,19 +439,28 @@ function fetchIOUReportByID(iouReportID, chatReportID) { } /** - * Retrieve an iouReport, associating it with a chatReport. This should be called in place of `fetchIOUReportByID` - * when we believe that the iouReport is open and currently linked to the chatReport. + * If an iouReport is open (has an IOU, but is not yet paid) then we maintain a link between it and the associated + * chatReport in Onyx, simplifying IOU data retrieval and reducing necessary API calls when displaying IOU components: + * - chatReport: {id: 123, iouReportID: 987, ...} + * - iouReport: {id: 987, chatReportID: 123, ...} + * + * This link must remain in sync when the iouReport is modified. This function forces a link update after fetching the + * iouReport and therefore should only be called if we are certain that the fetched iouReport is currently open or about + * to be made open - else we would overwrite the existing open report link with a closed report. + * + * Examples of usage include 'receieving a push notification', or 'paying an IOU', because both of these cases can only + * occur for a report that is currently open (notifications are not sent for closed reports, and you cannot pay a closed IOU). * * @param {Number} iouReportID - ID of the report we are fetching * @param {Number} chatReportID - associated chatReportID which should be updated and linked */ -function fetchIOUReportByIDAndUpdateChatReport(iouReportID, chatReportID) { +function fetchIOUReportByIDAndUpdateChatReportLink(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) .then((iouReportObject) => { // First persist the IOU Report data to Onyx setLocalIOUReportData(iouReportObject); - // Now update the associated chatReport data to ensure it has reference to updated report + // Now update the linked chatReport data to ensure it has reference to updated report const chatReportObject = { hasOutstandingIOU: iouReportObject.stateNum === 1 && iouReportObject.total !== 0, iouReportID: iouReportObject.reportID, @@ -543,9 +559,10 @@ function updateReportWithNewAction(reportID, reportAction) { if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) { const iouReportID = reportAction.originalMessage.IOUReportID; - // We have been notified of an update to this iouReport, therefore it must be the currently open iouReport. - // Therefore we should make sure the chatReport points to this iouReportID after fetching it. - fetchIOUReportByIDAndUpdateChatReport(iouReportID, reportID); + // This iouReport is open, so after fetching the report we must update the chatReport link. We can be sure that + // the iouReport is open, because reportActions of type CONST.REPORT.ACTIONS.TYPE.IOU can only be triggered for + // open reports (or open reports that are about to be closed) -- see function for more info. + fetchIOUReportByIDAndUpdateChatReportLink(iouReportID, reportID); } if (!ActiveClientManager.isClientTheLeader()) { @@ -1169,7 +1186,7 @@ export { fetchOrCreateChatReport, fetchChatReportsByIDs, fetchIOUReportByID, - fetchIOUReportByIDAndUpdateChatReport, + fetchIOUReportByIDAndUpdateChatReportLink, addAction, updateLastReadActionID, setNewMarkerPosition, diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 73d3342ad844..fab4e7bbaf21 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -85,9 +85,7 @@ class IOUDetailsModal extends Component { this.performIOUSettlement = this.performIOUSettlement.bind(this); } - componentDidMount() { - // Fetch the iouReport without linking it to the chatReport. Else, the chatReport's iouReportID field would - // wrongly be pointed to an old paid iouReport, overwriting the iouReportID of the open iouReport. + componentDidMount() { fetchIOUReportByID(this.props.route.params.iouReportID, this.props.route.params.chatReportID); } From c10d6ac85f0d05e0e189b13accbc2493d25a646e Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 17 May 2021 16:42:34 +0100 Subject: [PATCH 127/141] improved comment formatting --- src/libs/actions/Report.js | 12 +++++++----- src/pages/iou/IOUDetailsModal.js | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 1f41921cf644..329ca0dc7bc8 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -425,9 +425,10 @@ function removeOptimisticActions(reportID) { * - iouReport: {id: 987, chatReportID: 123, ...} * * This function allows us to fetch an iouReport without updating the report link data, preventing the chatReport's - * 'iouReportID' value from being updated. As an example, this is desired when fetching historical reports, to avoid overwritting an - * open iouReportID with a closed iouReportID. If this was to occur, unpaid IOUs would not be highlighted to the user. - * + * 'iouReportID' value from being updated. As an example, this is desired when fetching historical reports, to avoid + * overwritting an open iouReportID with a closed iouReportID. If this was to occur, unpaid IOUs would not be + * highlighted to the user. + * * If updating the report link data is desired, use `fetchIOUReportByIDAndUpdateChatReportLink` instead. * * @param {Number} iouReportID - ID of the report we are fetching @@ -446,10 +447,11 @@ function fetchIOUReportByID(iouReportID, chatReportID) { * * This link must remain in sync when the iouReport is modified. This function forces a link update after fetching the * iouReport and therefore should only be called if we are certain that the fetched iouReport is currently open or about - * to be made open - else we would overwrite the existing open report link with a closed report. + * to be made open - else we would overwrite the existing open report link with a closed report. * * Examples of usage include 'receieving a push notification', or 'paying an IOU', because both of these cases can only - * occur for a report that is currently open (notifications are not sent for closed reports, and you cannot pay a closed IOU). + * occur for a report that is currently open (notifications are not sent for closed reports, and you cannot pay a + * closed IOU). * * @param {Number} iouReportID - ID of the report we are fetching * @param {Number} chatReportID - associated chatReportID which should be updated and linked diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index fab4e7bbaf21..11dc8c362195 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -85,7 +85,7 @@ class IOUDetailsModal extends Component { this.performIOUSettlement = this.performIOUSettlement.bind(this); } - componentDidMount() { + componentDidMount() { fetchIOUReportByID(this.props.route.params.iouReportID, this.props.route.params.chatReportID); } From d7f213c70d3a816e8514b00fd9c335aabc0a1bdd Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 17 May 2021 17:09:19 +0100 Subject: [PATCH 128/141] update fetchIOUReportID... param docs too --- src/libs/actions/Report.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 329ca0dc7bc8..755734da5d10 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -432,7 +432,7 @@ function removeOptimisticActions(reportID) { * If updating the report link data is desired, use `fetchIOUReportByIDAndUpdateChatReportLink` instead. * * @param {Number} iouReportID - ID of the report we are fetching - * @param {Number} chatReportID - associated chatReportID which should be updated and linked + * @param {Number} chatReportID - associated chatReportID, set as an iouReport field, but not used to maintain the link */ function fetchIOUReportByID(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) @@ -454,7 +454,7 @@ function fetchIOUReportByID(iouReportID, chatReportID) { * closed IOU). * * @param {Number} iouReportID - ID of the report we are fetching - * @param {Number} chatReportID - associated chatReportID which should be updated and linked + * @param {Number} chatReportID - associated chatReportID, used to maintain the link between reports */ function fetchIOUReportByIDAndUpdateChatReportLink(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) From c1c5c2e528ed28363f9922e684b51db415073ed6 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 17 May 2021 17:22:21 +0100 Subject: [PATCH 129/141] migrate to new latest propType style --- src/components/ReportActionItemIOUAction.js | 10 +++---- src/components/ReportActionItemIOUPreview.js | 4 +-- src/components/ReportActionItemIOUQuote.js | 4 +-- src/components/ReportTransaction.js | 10 +++---- src/pages/home/report/ReportActionItem.js | 2 +- .../home/report/ReportActionItemSingle.js | 2 +- src/pages/iou/IOUDetailsModal.js | 30 +++++++++---------- src/pages/iou/IOUTransactions.js | 8 ++--- src/pages/iou/iouTansactionPropTypes.js | 6 ++-- 9 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index 6cb189fe5d75..6be9e5085add 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -9,19 +9,19 @@ import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; const propTypes = { - // All the data of the action + /** All the data of the action */ action: PropTypes.shape(ReportActionPropTypes).isRequired, - // The associated chatReport + /** The associated chatReport */ chatReportID: PropTypes.number.isRequired, - // Should render the preview Component? + /** Should render the preview Component? */ shouldDisplayPreview: PropTypes.bool.isRequired, /* --- Onyx Props --- */ - // ChatReport associated with iouReport + /** ChatReport associated with iouReport */ chatReport: PropTypes.shape({ - // The participants of this report + /** The participants of this report */ participants: PropTypes.arrayOf(PropTypes.string), }), }; diff --git a/src/components/ReportActionItemIOUPreview.js b/src/components/ReportActionItemIOUPreview.js index 63226efead77..fb89bf51dadd 100644 --- a/src/components/ReportActionItemIOUPreview.js +++ b/src/components/ReportActionItemIOUPreview.js @@ -17,7 +17,7 @@ const propTypes = { /** Callback for the Pay/Settle button */ onPayButtonPressed: PropTypes.func, - /* The active IOUReport, used for Onyx subscription */ + /** The active IOUReport, used for Onyx subscription */ /* eslint-disable-next-line react/no-unused-prop-types */ iouReportID: PropTypes.number, @@ -34,7 +34,7 @@ const propTypes = { /** Outstanding amount of this transaction */ cachedTotal: PropTypes.string, - // Does the report have an outstanding IOU that needs to be paid? + /** Does the report have an outstanding IOU that needs to be paid? */ hasOutstandingIOU: PropTypes.bool, }), diff --git a/src/components/ReportActionItemIOUQuote.js b/src/components/ReportActionItemIOUQuote.js index c0182b726f67..885a22edf68b 100644 --- a/src/components/ReportActionItemIOUQuote.js +++ b/src/components/ReportActionItemIOUQuote.js @@ -10,10 +10,10 @@ const propTypes = { /** All the data of the action */ action: PropTypes.shape(ReportActionPropTypes).isRequired, - // Should the View Details link be displayed? + /** Should the View Details link be displayed? */ shouldShowViewDetailsLink: PropTypes.bool, - // Callback invoked when View Details is pressed + /** Callback invoked when View Details is pressed */ onViewDetailsPressed: PropTypes.func, ...withLocalizePropTypes, diff --git a/src/components/ReportTransaction.js b/src/components/ReportTransaction.js index 0b36e5115de6..90d2bbeb5b8e 100644 --- a/src/components/ReportTransaction.js +++ b/src/components/ReportTransaction.js @@ -6,15 +6,15 @@ import ReportActionPropTypes from '../pages/home/report/ReportActionPropTypes'; import ReportActionItemSingle from '../pages/home/report/ReportActionItemSingle'; const propTypes = { - // The chatReport which the transaction is associated with - // eslint-disable-next-line react/no-unused-prop-types + /** The chatReport which the transaction is associated with */ + /* eslint-disable-next-line react/no-unused-prop-types */ chatReportID: PropTypes.number.isRequired, - // ID for the IOU report - // eslint-disable-next-line react/no-unused-prop-types + /** ID for the IOU report */ + /* eslint-disable-next-line react/no-unused-prop-types */ iouReportID: PropTypes.number.isRequired, - // The report action which we are displaying + /** The report action which we are displaying */ action: PropTypes.shape(ReportActionPropTypes).isRequired, }; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 55da48e7cdc1..f28b1472d579 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -36,7 +36,7 @@ const propTypes = { /** Whether there is an outstanding amount in IOU */ hasOutstandingIOU: PropTypes.bool, - /* Should we display the new indicator on top of the comment? */ + /** Should we display the new indicator on top of the comment? */ shouldDisplayNewIndicator: PropTypes.bool.isRequired, /** Position index of the report action in the overall report FlatList view */ diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index 1c1083fe02cc..5d60f327a811 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -19,7 +19,7 @@ const propTypes = { /** All of the personalDetails */ personalDetails: PropTypes.objectOf(personalDetailsPropType), - /* Styles for the outermost View */ + /** Styles for the outermost View */ wrapperStyles: PropTypes.arrayOf(PropTypes.object), /** Children view component for this action item */ diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 11dc8c362195..7c3f92eb49a8 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -20,49 +20,49 @@ import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize import compose from '../../libs/compose'; const propTypes = { - // URL Route params + /** URL Route params */ route: PropTypes.shape({ - // Params from the URL path + /** Params from the URL path */ params: PropTypes.shape({ - // chatReportID passed via route: /iou/details/:chatReportID/:iouReportID + /** chatReportID passed via route: /iou/details/:chatReportID/:iouReportID */ chatReportID: PropTypes.string, - // iouReportID passed via route: /iou/details/:chatReportID/:iouReportID + /** iouReportID passed via route: /iou/details/:chatReportID/:iouReportID */ iouReportID: PropTypes.string, }), }).isRequired, /* Onyx Props */ - // Holds data related to IOU view state, rather than the underlying IOU data. + /** Holds data related to IOU view state, rather than the underlying IOU data. */ iou: PropTypes.shape({ - // Is the IOU Report currently being paid? + /** Is the IOU Report currently being paid? */ loading: PropTypes.bool, - // Error message, empty represents no error + /** Error message, empty represents no error */ error: PropTypes.bool, }), - // IOU Report data object + /** IOU Report data object */ iouReport: PropTypes.shape({ - // ID for the chatReport that this IOU is linked to + /** ID for the chatReport that this IOU is linked to */ chatReportID: PropTypes.number, - // Manager is the person who currently owes money + /** Manager is the person who currently owes money */ managerEmail: PropTypes.string, - // Owner is the person who is owed money + /** Owner is the person who is owed money */ ownerEmail: PropTypes.string, - // The IOU transactions + /** The IOU transactions */ transactions: PropTypes.arrayOf(PropTypes.shape(iouTansactionPropTypes)), - // Does the report have an outstanding IOU that needs to be paid? + /** Does the report have an outstanding IOU that needs to be paid? */ hasOutstandingIOU: PropTypes.bool, }).isRequired, - // Session info for the currently logged in user. + /** Session info for the currently logged in user. */ session: PropTypes.shape({ - // Currently logged in user email + /** Currently logged in user email */ email: PropTypes.string, }).isRequired, diff --git a/src/pages/iou/IOUTransactions.js b/src/pages/iou/IOUTransactions.js index 0980db459d91..c1f8dc381a33 100644 --- a/src/pages/iou/IOUTransactions.js +++ b/src/pages/iou/IOUTransactions.js @@ -10,16 +10,16 @@ import iouTansactionPropTypes from './iouTansactionPropTypes'; import ReportTransaction from '../../components/ReportTransaction'; const propTypes = { - // Actions from the ChatReport + /** Actions from the ChatReport */ reportActions: PropTypes.shape(ReportActionPropTypes), - // ReportID for the associated chat report + /** ReportID for the associated chat report */ chatReportID: PropTypes.number.isRequired, - // ReportID for the associated IOU report + /** ReportID for the associated IOU report */ iouReportID: PropTypes.number.isRequired, - // Transactions for this IOU report + /** Transactions for this IOU report */ transactions: PropTypes.arrayOf(PropTypes.shape(iouTansactionPropTypes)), }; diff --git a/src/pages/iou/iouTansactionPropTypes.js b/src/pages/iou/iouTansactionPropTypes.js index c87522bab590..18ee3ffe6b4a 100644 --- a/src/pages/iou/iouTansactionPropTypes.js +++ b/src/pages/iou/iouTansactionPropTypes.js @@ -1,12 +1,12 @@ import PropTypes from 'prop-types'; export default { - // The transaction currency + /** The transaction currency */ currency: PropTypes.string, - // The transaction amount + /** The transaction amount */ total: PropTypes.number, - // The transaction comment + /** The transaction comment */ comment: PropTypes.string, }; From bfaef8e503e84dd1c0d856d14dd948efa2c8d090 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 17 May 2021 18:27:04 +0100 Subject: [PATCH 130/141] apply code review feedback fixes and improvements --- src/components/ReportActionItemIOUQuote.js | 2 +- src/components/ReportTransaction.js | 18 ++++++++---------- src/libs/actions/Report.js | 14 +++++++------- src/pages/iou/IOUDetailsModal.js | 13 +++++++------ src/pages/iou/IOUTransactions.js | 4 ++-- src/pages/iou/iouTansactionPropTypes.js | 12 ------------ src/pages/iou/iouTransactionPropTypes.js | 18 ++++++++++++++++++ 7 files changed, 43 insertions(+), 38 deletions(-) delete mode 100644 src/pages/iou/iouTansactionPropTypes.js create mode 100644 src/pages/iou/iouTransactionPropTypes.js diff --git a/src/components/ReportActionItemIOUQuote.js b/src/components/ReportActionItemIOUQuote.js index 885a22edf68b..02ab47b860bb 100644 --- a/src/components/ReportActionItemIOUQuote.js +++ b/src/components/ReportActionItemIOUQuote.js @@ -21,7 +21,7 @@ const propTypes = { const defaultProps = { shouldShowViewDetailsLink: false, - onViewDetailsPressed: null, + onViewDetailsPressed: () => {}, }; const ReportActionItemIOUQuote = ({ diff --git a/src/components/ReportTransaction.js b/src/components/ReportTransaction.js index 90d2bbeb5b8e..747982060c5d 100644 --- a/src/components/ReportTransaction.js +++ b/src/components/ReportTransaction.js @@ -21,16 +21,14 @@ const propTypes = { const ReportTransaction = ({ action, }) => ( - <> - - - {action.message[0].text} - - - + + + {action.message[0].text} + + ); ReportTransaction.displayName = 'ReportTransaction'; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 755734da5d10..c3aa5cc80d25 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -245,8 +245,8 @@ function fetchIOUReport(iouReportID, chatReportID) { } const iouReportData = response.reports[iouReportID]; if (!iouReportData) { - // IOU data for a report will be missing when the IOU report has already been settled. - // This is expected and we return early as no further processing can be done + // IOU data for a report will be missing when the IOU report has already been paid. + // This is expected and we return early as no further processing can be done. return; } return getSimplifiedIOUReport(iouReportData, chatReportID); @@ -424,12 +424,12 @@ function removeOptimisticActions(reportID) { * - chatReport: {id: 123, iouReportID: 987, ...} * - iouReport: {id: 987, chatReportID: 123, ...} * - * This function allows us to fetch an iouReport without updating the report link data, preventing the chatReport's + * This function allows us to fetch an iouReport without updating the linked chatReport, preventing the chatReport's * 'iouReportID' value from being updated. As an example, this is desired when fetching historical reports, to avoid * overwritting an open iouReportID with a closed iouReportID. If this was to occur, unpaid IOUs would not be * highlighted to the user. * - * If updating the report link data is desired, use `fetchIOUReportByIDAndUpdateChatReportLink` instead. + * If updating the chatReport's data is desired, use `fetchIOUReportByIDAndUpdateChatReportLink` instead. * * @param {Number} iouReportID - ID of the report we are fetching * @param {Number} chatReportID - associated chatReportID, set as an iouReport field, but not used to maintain the link @@ -447,10 +447,10 @@ function fetchIOUReportByID(iouReportID, chatReportID) { * * This link must remain in sync when the iouReport is modified. This function forces a link update after fetching the * iouReport and therefore should only be called if we are certain that the fetched iouReport is currently open or about - * to be made open - else we would overwrite the existing open report link with a closed report. + * to be made open - else we would overwrite the existing open iouReport link with a closed iouReport. * * Examples of usage include 'receieving a push notification', or 'paying an IOU', because both of these cases can only - * occur for a report that is currently open (notifications are not sent for closed reports, and you cannot pay a + * occur for an iouReport that is currently open (notifications are not sent for closed iouReports, and you cannot pay a * closed IOU). * * @param {Number} iouReportID - ID of the report we are fetching @@ -563,7 +563,7 @@ function updateReportWithNewAction(reportID, reportAction) { // This iouReport is open, so after fetching the report we must update the chatReport link. We can be sure that // the iouReport is open, because reportActions of type CONST.REPORT.ACTIONS.TYPE.IOU can only be triggered for - // open reports (or open reports that are about to be closed) -- see function for more info. + // open iouReports -- see function for more info. fetchIOUReportByIDAndUpdateChatReportLink(iouReportID, reportID); } diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 7c3f92eb49a8..9bdeb36c601b 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -14,7 +14,7 @@ import ScreenWrapper from '../../components/ScreenWrapper'; import {payIOUReport} from '../../libs/actions/IOU'; import {fetchIOUReportByID} from '../../libs/actions/Report'; import ReportActionItemIOUPreview from '../../components/ReportActionItemIOUPreview'; -import iouTansactionPropTypes from './iouTansactionPropTypes'; +import iouTransactionPropTypes from './iouTransactionPropTypes'; import IOUTransactions from './IOUTransactions'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import compose from '../../libs/compose'; @@ -54,11 +54,11 @@ const propTypes = { ownerEmail: PropTypes.string, /** The IOU transactions */ - transactions: PropTypes.arrayOf(PropTypes.shape(iouTansactionPropTypes)), + transactions: PropTypes.arrayOf(PropTypes.shape(iouTransactionPropTypes)), /** Does the report have an outstanding IOU that needs to be paid? */ hasOutstandingIOU: PropTypes.bool, - }).isRequired, + }), /** Session info for the currently logged in user. */ session: PropTypes.shape({ @@ -71,6 +71,7 @@ const propTypes = { const defaultProps = { iou: {}, + iouReport: {}, }; class IOUDetailsModal extends Component { @@ -82,14 +83,14 @@ class IOUDetailsModal extends Component { settlementType: 'Elsewhere', }; - this.performIOUSettlement = this.performIOUSettlement.bind(this); + this.performIOUPayment = this.performIOUPayment.bind(this); } componentDidMount() { fetchIOUReportByID(this.props.route.params.iouReportID, this.props.route.params.chatReportID); } - performIOUSettlement() { + performIOUPayment() { payIOUReport({ chatReportID: this.props.route.params.chatReportID, reportID: this.props.route.params.iouReportID, @@ -126,7 +127,7 @@ class IOUDetailsModal extends Component { ))} diff --git a/src/pages/iou/IOUTransactions.js b/src/pages/iou/IOUTransactions.js index c1f8dc381a33..d382b3b5ddcc 100644 --- a/src/pages/iou/IOUTransactions.js +++ b/src/pages/iou/IOUTransactions.js @@ -6,7 +6,7 @@ import PropTypes from 'prop-types'; import styles from '../../styles/styles'; import ONYXKEYS from '../../ONYXKEYS'; import ReportActionPropTypes from '../home/report/ReportActionPropTypes'; -import iouTansactionPropTypes from './iouTansactionPropTypes'; +import iouTransactionPropTypes from './iouTransactionPropTypes'; import ReportTransaction from '../../components/ReportTransaction'; const propTypes = { @@ -20,7 +20,7 @@ const propTypes = { iouReportID: PropTypes.number.isRequired, /** Transactions for this IOU report */ - transactions: PropTypes.arrayOf(PropTypes.shape(iouTansactionPropTypes)), + transactions: PropTypes.arrayOf(PropTypes.shape(iouTransactionPropTypes)), }; const defaultProps = { diff --git a/src/pages/iou/iouTansactionPropTypes.js b/src/pages/iou/iouTansactionPropTypes.js deleted file mode 100644 index 18ee3ffe6b4a..000000000000 --- a/src/pages/iou/iouTansactionPropTypes.js +++ /dev/null @@ -1,12 +0,0 @@ -import PropTypes from 'prop-types'; - -export default { - /** The transaction currency */ - currency: PropTypes.string, - - /** The transaction amount */ - total: PropTypes.number, - - /** The transaction comment */ - comment: PropTypes.string, -}; diff --git a/src/pages/iou/iouTransactionPropTypes.js b/src/pages/iou/iouTransactionPropTypes.js new file mode 100644 index 000000000000..7bc31f6cbdea --- /dev/null +++ b/src/pages/iou/iouTransactionPropTypes.js @@ -0,0 +1,18 @@ +import PropTypes from 'prop-types'; + +export default { + /** The transaction currency code */ + currency: PropTypes.string, + + /** The transaction amount */ + amount: PropTypes.number, + + /** The transaction comment */ + comment: PropTypes.string, + + /** Date that the transaction was created */ + created: PropTypes.string, + + /** The ID of this report transaction */ + transactionID: PropTypes.number, +}; From e548db28a076bf5a2895471f0b46a54cfe4b5f96 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 17 May 2021 20:33:18 +0100 Subject: [PATCH 131/141] switch transactionID type to String, to prevent conversion bug --- src/libs/actions/Report.js | 5 ++--- src/pages/iou/IOUTransactions.js | 3 ++- src/pages/iou/iouTransactionPropTypes.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index c3aa5cc80d25..1a8913fc8a43 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -183,7 +183,7 @@ function getSimplifiedReportObject(report) { * Get a simplified version of an IOU report * * @param {Object} reportData - * @param {Number} reportData.transactionID + * @param {String} reportData.transactionID * @param {Number} reportData.amount * @param {String} reportData.currency * @param {String} reportData.created @@ -197,8 +197,7 @@ function getSimplifiedReportObject(report) { */ function getSimplifiedIOUReport(reportData, chatReportID) { const transactions = _.map(reportData.transactionList, transaction => ({ - // TransactionID is returned from API as a String - transactionID: Number(transaction.transactionID), + transactionID: transaction.transactionID, amount: transaction.amount, currency: transaction.currency, created: transaction.created, diff --git a/src/pages/iou/IOUTransactions.js b/src/pages/iou/IOUTransactions.js index d382b3b5ddcc..c0c73b7ab7c1 100644 --- a/src/pages/iou/IOUTransactions.js +++ b/src/pages/iou/IOUTransactions.js @@ -44,8 +44,9 @@ class IOUTransactions extends Component { */ getActionForTransaction(transaction) { const matchedAction = _.find(this.props.reportActions, (action) => { + // iouReport.transaction.transactionID is returned as a String, but the originalMessage value is Number if (action && action.originalMessage - && action.originalMessage.IOUTransactionID === transaction.transactionID) { + && action.originalMessage.IOUTransactionID.toString() === transaction.transactionID) { return action; } return false; diff --git a/src/pages/iou/iouTransactionPropTypes.js b/src/pages/iou/iouTransactionPropTypes.js index 7bc31f6cbdea..51664bfd8559 100644 --- a/src/pages/iou/iouTransactionPropTypes.js +++ b/src/pages/iou/iouTransactionPropTypes.js @@ -14,5 +14,5 @@ export default { created: PropTypes.string, /** The ID of this report transaction */ - transactionID: PropTypes.number, + transactionID: PropTypes.string, }; From 129ddedad3333485fc943044c123c03d2056637d Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 17 May 2021 23:42:50 +0100 Subject: [PATCH 132/141] further improve comments based upon review feedback --- assets/images/link-copy.svg | 1 - src/components/ReportActionItemIOUAction.js | 2 +- src/libs/actions/IOU.js | 8 +++-- src/libs/actions/Report.js | 35 ++++++++------------- 4 files changed, 19 insertions(+), 27 deletions(-) delete mode 100644 assets/images/link-copy.svg diff --git a/assets/images/link-copy.svg b/assets/images/link-copy.svg deleted file mode 100644 index 9b883ade5067..000000000000 --- a/assets/images/link-copy.svg +++ /dev/null @@ -1 +0,0 @@ -link-copy \ No newline at end of file diff --git a/src/components/ReportActionItemIOUAction.js b/src/components/ReportActionItemIOUAction.js index 6be9e5085add..ca6564e68ae5 100644 --- a/src/components/ReportActionItemIOUAction.js +++ b/src/components/ReportActionItemIOUAction.js @@ -18,7 +18,7 @@ const propTypes = { /** Should render the preview Component? */ shouldDisplayPreview: PropTypes.bool.isRequired, - /* --- Onyx Props --- */ + /* Onyx Props */ /** ChatReport associated with iouReport */ chatReport: PropTypes.shape({ /** The participants of this report */ diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 87c0b6fe2f9f..88016fd938d8 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -128,9 +128,11 @@ function payIOUReport({ } fetchChatReportsByIDs([chatReportID]); - // An iouReport that is being paid must be open, and open iouReports are always linked to the associated - // chatReport via its Onyx data. As this link must be kept updated, after fetching the report we must also - // update the chatReport link - see function for more info. + // If an iouReport is open (has an IOU, but is not yet paid) then we maintain a link between it and the + // associated chatReport in Onyx, simplifying IOU data retrieval and reducing necessary API calls when + // displaying IOU components. If the link isn't updated in this case, the paid IOU would still be shown + // to users as unpaid. The iouReport being fetched here must be open, because only an open iouReoport can + // be paid. Therefore, we should also update the chatReport link after fetching the iouReport. fetchIOUReportByIDAndUpdateChatReportLink(reportID, chatReportID); }) .catch((error) => { diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 1a8913fc8a43..686b82a96024 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -418,20 +418,10 @@ function removeOptimisticActions(reportID) { } /** - * If an iouReport is open (has an IOU, but is not yet paid) then we maintain a link between it and the associated - * chatReport in Onyx, simplifying IOU data retrieval and reducing necessary API calls when displaying IOU components: - * - chatReport: {id: 123, iouReportID: 987, ...} - * - iouReport: {id: 987, chatReportID: 123, ...} - * - * This function allows us to fetch an iouReport without updating the linked chatReport, preventing the chatReport's - * 'iouReportID' value from being updated. As an example, this is desired when fetching historical reports, to avoid - * overwritting an open iouReportID with a closed iouReportID. If this was to occur, unpaid IOUs would not be - * highlighted to the user. - * - * If updating the chatReport's data is desired, use `fetchIOUReportByIDAndUpdateChatReportLink` instead. + * Fetch the iouReport and persist the data to Onyx. * * @param {Number} iouReportID - ID of the report we are fetching - * @param {Number} chatReportID - associated chatReportID, set as an iouReport field, but not used to maintain the link + * @param {Number} chatReportID - associated chatReportID, set as an iouReport field */ function fetchIOUReportByID(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) @@ -439,21 +429,21 @@ function fetchIOUReportByID(iouReportID, chatReportID) { } /** - * If an iouReport is open (has an IOU, but is not yet paid) then we maintain a link between it and the associated - * chatReport in Onyx, simplifying IOU data retrieval and reducing necessary API calls when displaying IOU components: + * If an iouReport is open (has an IOU, but is not yet paid) then we sync the reportIDs of both chatReport and + * iouReport in Onyx, simplifying IOU data retrieval and reducing necessary API calls when displaying IOU components: * - chatReport: {id: 123, iouReportID: 987, ...} * - iouReport: {id: 987, chatReportID: 123, ...} * - * This link must remain in sync when the iouReport is modified. This function forces a link update after fetching the - * iouReport and therefore should only be called if we are certain that the fetched iouReport is currently open or about - * to be made open - else we would overwrite the existing open iouReport link with a closed iouReport. + * The reports must remain in sync when the iouReport is modified. This function ensures that we sync reportIds after + * fetching the iouReport and therefore should only be called if we are certain that the fetched iouReport is currently + * open - else we would overwrite the existing open iouReportID with a closed iouReportID. * * Examples of usage include 'receieving a push notification', or 'paying an IOU', because both of these cases can only * occur for an iouReport that is currently open (notifications are not sent for closed iouReports, and you cannot pay a * closed IOU). * * @param {Number} iouReportID - ID of the report we are fetching - * @param {Number} chatReportID - associated chatReportID, used to maintain the link between reports + * @param {Number} chatReportID - associated chatReportID, used to sync the reports */ function fetchIOUReportByIDAndUpdateChatReportLink(iouReportID, chatReportID) { fetchIOUReport(iouReportID, chatReportID) @@ -461,7 +451,7 @@ function fetchIOUReportByIDAndUpdateChatReportLink(iouReportID, chatReportID) { // First persist the IOU Report data to Onyx setLocalIOUReportData(iouReportObject); - // Now update the linked chatReport data to ensure it has reference to updated report + // Now update the linked chatReport data to ensure it has a reference to the updated reportiouReportID const chatReportObject = { hasOutstandingIOU: iouReportObject.stateNum === 1 && iouReportObject.total !== 0, iouReportID: iouReportObject.reportID, @@ -560,9 +550,10 @@ function updateReportWithNewAction(reportID, reportAction) { if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) { const iouReportID = reportAction.originalMessage.IOUReportID; - // This iouReport is open, so after fetching the report we must update the chatReport link. We can be sure that - // the iouReport is open, because reportActions of type CONST.REPORT.ACTIONS.TYPE.IOU can only be triggered for - // open iouReports -- see function for more info. + // We know this iouReport is open because reportActions of type CONST.REPORT.ACTIONS.TYPE.IOU can only be + // triggered for open iouReports (an open iouReport has an IOU, but is not yet paid). After fetching the + // iouReport we must update the chatReport link, ensuring that it points to the correct iouReportID. If this + // link update didn't occur, then new IOUs would not be displayed and paid IOUs would show as unpaid. fetchIOUReportByIDAndUpdateChatReportLink(iouReportID, reportID); } From f43bbf027e2dc85abe0991883c9c4056cc5106eb Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 17 May 2021 23:47:23 +0100 Subject: [PATCH 133/141] reverse accidental removal of image --- assets/images/link-copy.svg | 1 + 1 file changed, 1 insertion(+) create mode 100644 assets/images/link-copy.svg diff --git a/assets/images/link-copy.svg b/assets/images/link-copy.svg new file mode 100644 index 000000000000..9b883ade5067 --- /dev/null +++ b/assets/images/link-copy.svg @@ -0,0 +1 @@ +link-copy \ No newline at end of file From 9054afccd5e613bfdf8a93b637f41e5f03948dab Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Mon, 17 May 2021 23:50:45 +0100 Subject: [PATCH 134/141] additional usage of sync --- src/libs/actions/IOU.js | 6 +++--- src/libs/actions/Report.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 88016fd938d8..fe7046ed5707 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -130,9 +130,9 @@ function payIOUReport({ // If an iouReport is open (has an IOU, but is not yet paid) then we maintain a link between it and the // associated chatReport in Onyx, simplifying IOU data retrieval and reducing necessary API calls when - // displaying IOU components. If the link isn't updated in this case, the paid IOU would still be shown - // to users as unpaid. The iouReport being fetched here must be open, because only an open iouReoport can - // be paid. Therefore, we should also update the chatReport link after fetching the iouReport. + // displaying IOU components. If we didn't sync the reportIDs, the paid IOU would still be shown to users + // as unpaid. The iouReport being fetched here must be open, because only an open iouReoport can be paid. + // Therefore, we should also update the chatReport link after fetching the iouReport. fetchIOUReportByIDAndUpdateChatReportLink(reportID, chatReportID); }) .catch((error) => { diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 686b82a96024..930204f51f63 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -552,8 +552,8 @@ function updateReportWithNewAction(reportID, reportAction) { // We know this iouReport is open because reportActions of type CONST.REPORT.ACTIONS.TYPE.IOU can only be // triggered for open iouReports (an open iouReport has an IOU, but is not yet paid). After fetching the - // iouReport we must update the chatReport link, ensuring that it points to the correct iouReportID. If this - // link update didn't occur, then new IOUs would not be displayed and paid IOUs would show as unpaid. + // iouReport we must update the chatReport, ensuring that it points to the correct iouReportID. If this + // sync didn't occur, then new IOUs would not be displayed and paid IOUs would show as unpaid. fetchIOUReportByIDAndUpdateChatReportLink(iouReportID, reportID); } From b403daa34f0949212616668bc7cc30c529a5bdbc Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 18 May 2021 00:01:04 +0100 Subject: [PATCH 135/141] prevents .toString from being called on undefined --- src/pages/iou/IOUTransactions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/IOUTransactions.js b/src/pages/iou/IOUTransactions.js index c0c73b7ab7c1..b01323d87e6e 100644 --- a/src/pages/iou/IOUTransactions.js +++ b/src/pages/iou/IOUTransactions.js @@ -45,7 +45,7 @@ class IOUTransactions extends Component { getActionForTransaction(transaction) { const matchedAction = _.find(this.props.reportActions, (action) => { // iouReport.transaction.transactionID is returned as a String, but the originalMessage value is Number - if (action && action.originalMessage + if (action && action.originalMessage && action.originalMessage.IOUTransactionID && action.originalMessage.IOUTransactionID.toString() === transaction.transactionID) { return action; } From d173c818ed5b7ed75c9c930dd8634c824c9d85f8 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 18 May 2021 00:44:41 +0100 Subject: [PATCH 136/141] simplify fetchIOUReportByIDAndUpdateChatReport function name, reuse 'fetchIOUReportByID' --- src/libs/actions/IOU.js | 4 ++-- src/libs/actions/Report.js | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index fe7046ed5707..29c59d5c59a0 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -2,7 +2,7 @@ import Onyx from 'react-native-onyx'; import _ from 'underscore'; import ONYXKEYS from '../../ONYXKEYS'; import * as API from '../API'; -import {getSimplifiedIOUReport, fetchChatReportsByIDs, fetchIOUReportByIDAndUpdateChatReportLink} from './Report'; +import {getSimplifiedIOUReport, fetchChatReportsByIDs, fetchIOUReportByIDAndUpdateChatReport} from './Report'; /** * Retrieve the users preferred currency @@ -133,7 +133,7 @@ function payIOUReport({ // displaying IOU components. If we didn't sync the reportIDs, the paid IOU would still be shown to users // as unpaid. The iouReport being fetched here must be open, because only an open iouReoport can be paid. // Therefore, we should also update the chatReport link after fetching the iouReport. - fetchIOUReportByIDAndUpdateChatReportLink(reportID, chatReportID); + fetchIOUReportByIDAndUpdateChatReport(reportID, chatReportID); }) .catch((error) => { console.error(`Error Paying iouReport: ${error}`); diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 930204f51f63..138018076bf3 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -422,10 +422,14 @@ function removeOptimisticActions(reportID) { * * @param {Number} iouReportID - ID of the report we are fetching * @param {Number} chatReportID - associated chatReportID, set as an iouReport field + * @returns {Object} */ function fetchIOUReportByID(iouReportID, chatReportID) { - fetchIOUReport(iouReportID, chatReportID) - .then(iouReportObject => setLocalIOUReportData(iouReportObject)); + return fetchIOUReport(iouReportID, chatReportID) + .then((iouReportObject) => { + setLocalIOUReportData(iouReportObject); + return iouReportObject; + }); } /** @@ -445,12 +449,9 @@ function fetchIOUReportByID(iouReportID, chatReportID) { * @param {Number} iouReportID - ID of the report we are fetching * @param {Number} chatReportID - associated chatReportID, used to sync the reports */ -function fetchIOUReportByIDAndUpdateChatReportLink(iouReportID, chatReportID) { - fetchIOUReport(iouReportID, chatReportID) +function fetchIOUReportByIDAndUpdateChatReport(iouReportID, chatReportID) { + fetchIOUReportByID(iouReportID, chatReportID) .then((iouReportObject) => { - // First persist the IOU Report data to Onyx - setLocalIOUReportData(iouReportObject); - // Now update the linked chatReport data to ensure it has a reference to the updated reportiouReportID const chatReportObject = { hasOutstandingIOU: iouReportObject.stateNum === 1 && iouReportObject.total !== 0, @@ -554,7 +555,7 @@ function updateReportWithNewAction(reportID, reportAction) { // triggered for open iouReports (an open iouReport has an IOU, but is not yet paid). After fetching the // iouReport we must update the chatReport, ensuring that it points to the correct iouReportID. If this // sync didn't occur, then new IOUs would not be displayed and paid IOUs would show as unpaid. - fetchIOUReportByIDAndUpdateChatReportLink(iouReportID, reportID); + fetchIOUReportByIDAndUpdateChatReport(iouReportID, reportID); } if (!ActiveClientManager.isClientTheLeader()) { @@ -1178,7 +1179,7 @@ export { fetchOrCreateChatReport, fetchChatReportsByIDs, fetchIOUReportByID, - fetchIOUReportByIDAndUpdateChatReportLink, + fetchIOUReportByIDAndUpdateChatReport, addAction, updateLastReadActionID, setNewMarkerPosition, From 92e22631419d509d3bbab2abd047df9d70ceb1b6 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 18 May 2021 00:45:30 +0100 Subject: [PATCH 137/141] fixed exception that was recently introduced, to ensure that the PR is testable --- src/pages/home/sidebar/OptionRow.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/sidebar/OptionRow.js b/src/pages/home/sidebar/OptionRow.js index d15bd013e49d..49d6407eaa13 100644 --- a/src/pages/home/sidebar/OptionRow.js +++ b/src/pages/home/sidebar/OptionRow.js @@ -115,7 +115,7 @@ const OptionRow = ({ ? hoverStyle.backgroundColor : backgroundColor; const focusedBackgroundColor = styles.sidebarLinkActive.backgroundColor; - const isMultipleParticipant = option.participantsList.length > 1; + const isMultipleParticipant = option.participantsList && option.participantsList.length > 1; const displayNamesWithTooltips = _.map( option.participantsList, ({displayName, firstName, login}) => ( From 5a7a25ae499ed3b811c981b15a2d437e0aff8947 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 18 May 2021 13:30:50 +0100 Subject: [PATCH 138/141] additional comment improvements --- src/libs/actions/IOU.js | 10 +++++----- src/libs/actions/Report.js | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 29c59d5c59a0..631f432cb598 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -128,11 +128,11 @@ function payIOUReport({ } fetchChatReportsByIDs([chatReportID]); - // If an iouReport is open (has an IOU, but is not yet paid) then we maintain a link between it and the - // associated chatReport in Onyx, simplifying IOU data retrieval and reducing necessary API calls when - // displaying IOU components. If we didn't sync the reportIDs, the paid IOU would still be shown to users - // as unpaid. The iouReport being fetched here must be open, because only an open iouReoport can be paid. - // Therefore, we should also update the chatReport link after fetching the iouReport. + // If an iouReport is open (has an IOU, but is not yet paid) then we sync the chatReport's 'iouReportID' + // field in Onyx, simplifying IOU data retrieval and reducing necessary API calls when displaying IOU + // components. If we didn't sync the reportIDs, the paid IOU would still be shown to users as unpaid. The + // iouReport being fetched here must be open, because only an open iouReoport can be paid. + // Therefore, we should also sync the chatReport after fetching the iouReport. fetchIOUReportByIDAndUpdateChatReport(reportID, chatReportID); }) .catch((error) => { diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 138018076bf3..b3175177cfd1 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -422,7 +422,7 @@ function removeOptimisticActions(reportID) { * * @param {Number} iouReportID - ID of the report we are fetching * @param {Number} chatReportID - associated chatReportID, set as an iouReport field - * @returns {Object} + * @returns {Promise} */ function fetchIOUReportByID(iouReportID, chatReportID) { return fetchIOUReport(iouReportID, chatReportID) @@ -452,7 +452,7 @@ function fetchIOUReportByID(iouReportID, chatReportID) { function fetchIOUReportByIDAndUpdateChatReport(iouReportID, chatReportID) { fetchIOUReportByID(iouReportID, chatReportID) .then((iouReportObject) => { - // Now update the linked chatReport data to ensure it has a reference to the updated reportiouReportID + // Now sync the chatReport data to ensure it has a reference to the updated iouReportID const chatReportObject = { hasOutstandingIOU: iouReportObject.stateNum === 1 && iouReportObject.total !== 0, iouReportID: iouReportObject.reportID, @@ -552,9 +552,9 @@ function updateReportWithNewAction(reportID, reportAction) { const iouReportID = reportAction.originalMessage.IOUReportID; // We know this iouReport is open because reportActions of type CONST.REPORT.ACTIONS.TYPE.IOU can only be - // triggered for open iouReports (an open iouReport has an IOU, but is not yet paid). After fetching the - // iouReport we must update the chatReport, ensuring that it points to the correct iouReportID. If this - // sync didn't occur, then new IOUs would not be displayed and paid IOUs would show as unpaid. + // triggered for an open iouReport (an open iouReport has an IOU, but is not yet paid). After fetching the + // iouReport we must update the chatReport with the correct iouReportID. If we don't, then new IOUs would not + // be displayed and paid IOUs would show as unpaid. fetchIOUReportByIDAndUpdateChatReport(iouReportID, reportID); } From a1ef2b9381134dcbef3d7821e24e34d778dfb453 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 18 May 2021 18:34:47 +0100 Subject: [PATCH 139/141] ensure that activityIndicator displays when loading paid reports --- src/pages/iou/IOUDetailsModal.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index 9bdeb36c601b..d02ffd573869 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -71,7 +71,6 @@ const propTypes = { const defaultProps = { iou: {}, - iouReport: {}, }; class IOUDetailsModal extends Component { From 3f6fafdfcdac13ccad8cc5e5ab4c3052c2d47319 Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 18 May 2021 18:37:52 +0100 Subject: [PATCH 140/141] remove iouReport isRequired, as this is no longer true --- src/pages/iou/IOUDetailsModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/IOUDetailsModal.js b/src/pages/iou/IOUDetailsModal.js index d02ffd573869..7cb125bb3e67 100644 --- a/src/pages/iou/IOUDetailsModal.js +++ b/src/pages/iou/IOUDetailsModal.js @@ -58,7 +58,7 @@ const propTypes = { /** Does the report have an outstanding IOU that needs to be paid? */ hasOutstandingIOU: PropTypes.bool, - }), + }).isRequired, /** Session info for the currently logged in user. */ session: PropTypes.shape({ From 2edf67d31762d5c2a17038d21f0f4e47aea2220b Mon Sep 17 00:00:00 2001 From: Jules Rosser Date: Tue, 18 May 2021 22:44:37 +0100 Subject: [PATCH 141/141] reverse user 'paid' user message --- src/languages/en.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/en.js b/src/languages/en.js index 79326a78f1c9..8a84e7440af6 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -105,7 +105,7 @@ export default { settleElsewhere: 'I\'ll settle up elsewhere', request: ({amount}) => `Request ${amount}`, owes: ({manager, owner}) => `${manager} owes ${owner}`, - paid: ({owner, manager}) => `${owner} paid ${manager}`, + paid: ({owner, manager}) => `${manager} paid ${owner}`, }, loginField: { addYourPhoneToSettleViaVenmo: 'Add your phone number to settle up via Venmo.',