From 15cf4c5e61fead83081f9eeaa8eb66493b8a6408 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Fri, 28 Apr 2023 13:23:52 +0100 Subject: [PATCH 01/38] New report action type in CONST file. --- src/CONST.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CONST.js b/src/CONST.js index 20e293011f1c..76c296deb0a3 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -400,6 +400,7 @@ const CONST = { IOU: 'IOU', RENAMED: 'RENAMED', CHRONOSOOOLIST: 'CHRONOSOOOLIST', + REPORTPREVIEW: 'REPORTPREVIEW', POLICYCHANGELOG: { ADD_APPROVER_RULE: 'POLICYCHANGELOG_ADD_APPROVER_RULE', ADD_CATEGORY: 'POLICYCHANGELOG_ADD_CATEGORY', From 972480e22019b9766e13c8dda3dcbe9f5bc7eba0 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Fri, 28 Apr 2023 13:36:55 +0100 Subject: [PATCH 02/38] Create ReportPreview component. --- src/components/ReportActionItem/ReportPreview.js | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/components/ReportActionItem/ReportPreview.js diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js new file mode 100644 index 000000000000..669a227ed19a --- /dev/null +++ b/src/components/ReportActionItem/ReportPreview.js @@ -0,0 +1,7 @@ +import React from 'react'; + +const ReportPreview = (props) => { + return <>; +}; + +export default ReportPreview; \ No newline at end of file From 7b86c61b783dd713f981bcf27fedcaefea3b929b Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Fri, 28 Apr 2023 15:41:34 +0100 Subject: [PATCH 03/38] Utility method to get report preview action. --- src/libs/ReportActionsUtils.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index 2e9ea66f7fd4..0eea842480d1 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -272,6 +272,14 @@ function getLatestReportActionFromOnyxData(onyxData) { return _.last(sortedReportActions); } +/** + * @param {*} reportID + * @returns {Object} The report preview action or `null` if one couldn't be found + */ +function getReportPreviewAction(reportID) { + return _.findWhere(allReportActions[reportID], {actionName: CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW}); +} + export { getSortedReportActions, getLastVisibleAction, @@ -284,4 +292,5 @@ export { getSortedReportActionsForDisplay, getLastClosedReportAction, getLatestReportActionFromOnyxData, + getReportPreviewAction, }; From ff09d17029ae7eee6784154f2b15e1ba521f8c0c Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Fri, 28 Apr 2023 16:06:25 +0100 Subject: [PATCH 04/38] Utility function which builds optimistic report preview. --- src/libs/ReportActionsUtils.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index 0eea842480d1..b55ca18d104f 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -280,6 +280,18 @@ function getReportPreviewAction(reportID) { return _.findWhere(allReportActions[reportID], {actionName: CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW}); } +function buildOptimisticReportPreview(reportID, payeeAccountID, amount) { + return ({ + reportActionID: NumberUtils.rand64(), + reportID: reportID, + created: DateUtils.getDBTime(), + action: CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + accountID: payeeAccountID, + message: {amount: amount}, + }); +} + export { getSortedReportActions, getLastVisibleAction, @@ -293,4 +305,5 @@ export { getLastClosedReportAction, getLatestReportActionFromOnyxData, getReportPreviewAction, + buildOptimisticReportPreview, }; From fb9429963dab2fe476dbf83d8070ae771a4c7638 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Mon, 1 May 2023 14:15:36 +0300 Subject: [PATCH 05/38] Add report preview in optimistic and success data --- src/components/ReportActionItem/ReportPreview.js | 3 ++- src/libs/ReportActionsUtils.js | 2 ++ src/libs/actions/IOU.js | 8 ++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index 669a227ed19a..9b2208d46540 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -1,7 +1,8 @@ import React from 'react'; +import Text from '../Text'; const ReportPreview = (props) => { - return <>; + return Report Preview; }; export default ReportPreview; \ No newline at end of file diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index b55ca18d104f..c4a564fa4b95 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -10,6 +10,8 @@ import CONST from '../CONST'; import ONYXKEYS from '../ONYXKEYS'; import Log from './Log'; import isReportMessageAttachment from './isReportMessageAttachment'; +import * as NumberUtils from './NumberUtils'; +import DateUtils from './DateUtils'; const allReportActions = {}; Onyx.connect({ diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 050c1b0e7f6b..50ebdb61b69a 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -10,6 +10,7 @@ import * as Localize from '../Localize'; import asyncOpenURL from '../asyncOpenURL'; import * as API from '../API'; import * as ReportUtils from '../ReportUtils'; +import * as ReportActionsUtils from '../ReportActionsUtils'; import * as IOUUtils from '../IOUUtils'; import * as OptionsListUtils from '../OptionsListUtils'; import DateUtils from '../DateUtils'; @@ -87,6 +88,11 @@ function requestMoney(report, amount, currency, recipientEmail, participant, com '', iouReport.reportID, ); + + let reportPreview = ReportActionsUtils.getReportPreviewAction(chatReport.reportID); + if (!reportPreview) { + reportPreview = ReportActionsUtils.buildOptimisticReportPreview(chatReport.reportID); + } // First, add data that will be used in all cases const optimisticChatReportData = { @@ -113,6 +119,7 @@ function requestMoney(report, amount, currency, recipientEmail, participant, com key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, value: { [optimisticReportAction.reportActionID]: optimisticReportAction, + [reportPreview.reportActionID]: reportPreview, }, }; @@ -124,6 +131,7 @@ function requestMoney(report, amount, currency, recipientEmail, participant, com [optimisticReportAction.reportActionID]: { pendingAction: null, }, + [reportPreview.reportActionID]: reportPreview, }, }; From 4f72a9f0b99a4ff713a2e6a0369ec651f1f4bbe8 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Mon, 1 May 2023 22:36:41 +0300 Subject: [PATCH 06/38] WIP. --- src/libs/ReportActionsUtils.js | 7 +++++-- src/libs/actions/IOU.js | 1 + src/pages/home/report/ReportActionItem.js | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index c4a564fa4b95..5be1b4844d70 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -287,10 +287,13 @@ function buildOptimisticReportPreview(reportID, payeeAccountID, amount) { reportActionID: NumberUtils.rand64(), reportID: reportID, created: DateUtils.getDBTime(), - action: CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW, + actionName: CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, accountID: payeeAccountID, - message: {amount: amount}, + + // TODO - the followings are hardcodings for whatever ReportActionItemSingle/ReportActionItemGrouped expect + message: [{text: 'This is a report preview.', type: 'COMMENT'}], + actorEmail: 'cristi+dev5@expensify.com', }); } diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 50ebdb61b69a..d717e77cc88f 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -90,6 +90,7 @@ function requestMoney(report, amount, currency, recipientEmail, participant, com ); let reportPreview = ReportActionsUtils.getReportPreviewAction(chatReport.reportID); + console.log(reportPreview); if (!reportPreview) { reportPreview = ReportActionsUtils.buildOptimisticReportPreview(chatReport.reportID); } diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index a47ff6d4de4e..8f0583e90f7b 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -50,6 +50,7 @@ import * as Expensicons from '../../../components/Icon/Expensicons'; import Text from '../../../components/Text'; import DisplayNames from '../../../components/DisplayNames'; import personalDetailsPropType from '../../personalDetailsPropType'; +import ReportPreview from '../../../components/ReportActionItem/ReportPreview'; const propTypes = { /** Report for this action */ @@ -184,6 +185,8 @@ class ReportActionItem extends Component { checkIfContextMenuActive={this.checkIfContextMenuActive} /> ); + } else if (this.props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW) { + children = (); } else { const message = _.last(lodashGet(this.props.action, 'message', [{}])); const isAttachment = _.has(this.props.action, 'isAttachment') From 8b463a0cd120c406763727d2a3aa076ca261a2e2 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 2 May 2023 14:32:20 +0300 Subject: [PATCH 07/38] Connect to Onyx in ReportActionsUtils to get the current user email. --- src/libs/ReportActionsUtils.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index 5be1b4844d70..e44f3d7110dd 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -32,6 +32,19 @@ Onyx.connect({ callback: val => isNetworkOffline = lodashGet(val, 'isOffline', false), }); +let currentUserEmail; +Onyx.connect({ + key: ONYXKEYS.SESSION, + callback: (val) => { + // When signed out, val is undefined + if (!val) { + return; + } + + currentUserEmail = val.email; + }, +}); + /** * @param {Object} reportAction * @returns {Boolean} @@ -293,7 +306,7 @@ function buildOptimisticReportPreview(reportID, payeeAccountID, amount) { // TODO - the followings are hardcodings for whatever ReportActionItemSingle/ReportActionItemGrouped expect message: [{text: 'This is a report preview.', type: 'COMMENT'}], - actorEmail: 'cristi+dev5@expensify.com', + actorEmail: currentUserEmail, }); } From 87f0c6b99f49ab7b516f778db52a6392c31d24e4 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 2 May 2023 15:08:48 +0300 Subject: [PATCH 08/38] Improve message property in optimistic report preview action. --- src/libs/ReportActionsUtils.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index e44f3d7110dd..07bf6abc24b2 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -296,6 +296,7 @@ function getReportPreviewAction(reportID) { } function buildOptimisticReportPreview(reportID, payeeAccountID, amount) { + const textComment = `Duraflame owes you ${amount}`; return ({ reportActionID: NumberUtils.rand64(), reportID: reportID, @@ -303,9 +304,12 @@ function buildOptimisticReportPreview(reportID, payeeAccountID, amount) { actionName: CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, accountID: payeeAccountID, - - // TODO - the followings are hardcodings for whatever ReportActionItemSingle/ReportActionItemGrouped expect - message: [{text: 'This is a report preview.', type: 'COMMENT'}], + message: [{ + html: textComment, + text: textComment, + isEdited: false, + type: CONST.REPORT.MESSAGE.TYPE.COMMENT + }], actorEmail: currentUserEmail, }); } From 650389d4687529be4c6258ad6a3997c766500a3d Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 2 May 2023 15:46:42 +0300 Subject: [PATCH 09/38] Build the preview component starting from IOUAction --- .../ReportActionItem/ReportPreview.js | 140 +++++++++++++++++- src/libs/ReportActionsUtils.js | 5 +- src/libs/actions/IOU.js | 2 +- src/pages/home/report/ReportActionItem.js | 11 +- 4 files changed, 152 insertions(+), 6 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index 9b2208d46540..2677baf0532f 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -1,8 +1,142 @@ +import _ from 'underscore'; import React from 'react'; -import Text from '../Text'; +import PropTypes from 'prop-types'; +import {withOnyx} from 'react-native-onyx'; +import ONYXKEYS from '../../ONYXKEYS'; +import CONST from '../../CONST'; +import {withNetwork} from '../OnyxProvider'; +import compose from '../../libs/compose'; +import IOUQuote from './IOUQuote'; +import reportActionPropTypes from '../../pages/home/report/reportActionPropTypes'; +import networkPropTypes from '../networkPropTypes'; +import iouReportPropTypes from '../../pages/iouReportPropTypes'; +import IOUPreview from './IOUPreview'; +import Navigation from '../../libs/Navigation/Navigation'; +import ROUTES from '../../ROUTES'; +import styles from '../../styles/styles'; +import * as IOUUtils from '../../libs/IOUUtils'; + +const propTypes = { + /** All the data of the action */ + action: PropTypes.shape(reportActionPropTypes).isRequired, + + /** The ID of the associated chatReport */ + chatReportID: PropTypes.string.isRequired, + + /** Is this IOUACTION the most recent? */ + isMostRecentIOUReportAction: PropTypes.bool.isRequired, + + /** Popover context menu anchor, used for showing context menu */ + contextMenuAnchor: PropTypes.shape({current: PropTypes.elementType}), + + /** Callback for updating context menu active state, used for showing context menu */ + checkIfContextMenuActive: PropTypes.func, + + /* Onyx Props */ + /** chatReport associated with iouReport */ + chatReport: PropTypes.shape({ + /** The participants of this report */ + participants: PropTypes.arrayOf(PropTypes.string), + + /** Whether the chat report has an outstanding IOU */ + hasOutstandingIOU: PropTypes.bool.isRequired, + }), + + /** IOU report data object */ + iouReport: iouReportPropTypes, + + /** Array of report actions for this report */ + reportActions: PropTypes.objectOf(PropTypes.shape(reportActionPropTypes)), + + /** Whether the IOU is hovered so we can modify its style */ + isHovered: PropTypes.bool, + + network: networkPropTypes.isRequired, +}; + +const defaultProps = { + contextMenuAnchor: undefined, + checkIfContextMenuActive: () => {}, + chatReport: { + participants: [], + }, + iouReport: {}, + reportActions: {}, + isHovered: false, +}; const ReportPreview = (props) => { - return Report Preview; + const launchDetailsModal = () => { + Navigation.navigate(ROUTES.getIouDetailsRoute(props.chatReportID, props.action.originalMessage.IOUReportID)); + }; + + const shouldShowIOUPreview = ( + props.isMostRecentIOUReportAction + && Boolean(props.action.originalMessage.IOUReportID) + && props.chatReport.hasOutstandingIOU) || props.action.originalMessage.type === 'pay'; + + let shouldShowPendingConversionMessage = false; + if ( + !_.isEmpty(props.iouReport) + && !_.isEmpty(props.reportActions) + && props.chatReport.hasOutstandingIOU + && props.isMostRecentIOUReportAction + && props.action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD + && props.network.isOffline + ) { + shouldShowPendingConversionMessage = IOUUtils.isIOUReportPendingCurrencyConversion(props.reportActions, props.iouReport); + } + + return ( + <> + + {shouldShowIOUPreview && ( + + )} + + ); }; -export default ReportPreview; \ No newline at end of file +ReportPreview.propTypes = propTypes; +ReportPreview.defaultProps = defaultProps; +ReportPreview.displayName = 'ReportPreview'; + +export default compose( + withOnyx({ + chatReport: { + key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, + }, + iouReport: { + key: ({action}) => `${ONYXKEYS.COLLECTION.REPORT}${action.originalMessage.IOUReportID}`, + }, + reportActions: { + key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReportID}`, + canEvict: false, + }, + }), + withNetwork(), +)(ReportPreview); diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index 07bf6abc24b2..fe6d6cea2349 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -295,7 +295,7 @@ function getReportPreviewAction(reportID) { return _.findWhere(allReportActions[reportID], {actionName: CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW}); } -function buildOptimisticReportPreview(reportID, payeeAccountID, amount) { +function buildOptimisticReportPreview(reportID, iouReportID, payeeAccountID, amount) { const textComment = `Duraflame owes you ${amount}`; return ({ reportActionID: NumberUtils.rand64(), @@ -310,6 +310,9 @@ function buildOptimisticReportPreview(reportID, payeeAccountID, amount) { isEdited: false, type: CONST.REPORT.MESSAGE.TYPE.COMMENT }], + originalMessage: { + IOUReportID: iouReportID, + }, actorEmail: currentUserEmail, }); } diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index d717e77cc88f..c17b6ee5c94a 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -92,7 +92,7 @@ function requestMoney(report, amount, currency, recipientEmail, participant, com let reportPreview = ReportActionsUtils.getReportPreviewAction(chatReport.reportID); console.log(reportPreview); if (!reportPreview) { - reportPreview = ReportActionsUtils.buildOptimisticReportPreview(chatReport.reportID); + reportPreview = ReportActionsUtils.buildOptimisticReportPreview(chatReport.reportID, iouReport.reportID); } // First, add data that will be used in all cases diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 8f0583e90f7b..6d0fb6334aeb 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -186,7 +186,16 @@ class ReportActionItem extends Component { /> ); } else if (this.props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW) { - children = (); + children = ( + + ); } else { const message = _.last(lodashGet(this.props.action, 'message', [{}])); const isAttachment = _.has(this.props.action, 'isAttachment') From 4615b3f4634b00dacb3459db392b4ed0943527c0 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 2 May 2023 16:16:34 +0300 Subject: [PATCH 10/38] Remove the iou preview. --- .../ReportActionItem/ReportPreview.js | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index 2677baf0532f..e279d16d7e11 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -70,22 +70,22 @@ const ReportPreview = (props) => { Navigation.navigate(ROUTES.getIouDetailsRoute(props.chatReportID, props.action.originalMessage.IOUReportID)); }; - const shouldShowIOUPreview = ( - props.isMostRecentIOUReportAction - && Boolean(props.action.originalMessage.IOUReportID) - && props.chatReport.hasOutstandingIOU) || props.action.originalMessage.type === 'pay'; - - let shouldShowPendingConversionMessage = false; - if ( - !_.isEmpty(props.iouReport) - && !_.isEmpty(props.reportActions) - && props.chatReport.hasOutstandingIOU - && props.isMostRecentIOUReportAction - && props.action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD - && props.network.isOffline - ) { - shouldShowPendingConversionMessage = IOUUtils.isIOUReportPendingCurrencyConversion(props.reportActions, props.iouReport); - } + // const shouldShowIOUPreview = ( + // props.isMostRecentIOUReportAction + // && Boolean(props.action.originalMessage.IOUReportID) + // && props.chatReport.hasOutstandingIOU) || props.action.originalMessage.type === 'pay'; + + // let shouldShowPendingConversionMessage = false; + // if ( + // !_.isEmpty(props.iouReport) + // && !_.isEmpty(props.reportActions) + // && props.chatReport.hasOutstandingIOU + // && props.isMostRecentIOUReportAction + // && props.action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD + // && props.network.isOffline + // ) { + // shouldShowPendingConversionMessage = IOUUtils.isIOUReportPendingCurrencyConversion(props.reportActions, props.iouReport); + // } return ( <> @@ -98,25 +98,25 @@ const ReportPreview = (props) => { checkIfContextMenuActive={props.checkIfContextMenuActive} isHovered={props.isHovered} /> - {shouldShowIOUPreview && ( - - )} + {/* {shouldShowIOUPreview && ( + + )} */} ); }; From e36b159187bcbcabeec0e3da811c7699ac2bc07a Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 2 May 2023 16:31:03 +0300 Subject: [PATCH 11/38] Remove unnecessary props and logic. from the component. --- .../ReportActionItem/ReportPreview.js | 83 +------------------ src/pages/home/report/ReportActionItem.js | 1 - 2 files changed, 1 insertion(+), 83 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index e279d16d7e11..f7a5d6c27f36 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -1,20 +1,10 @@ import _ from 'underscore'; import React from 'react'; import PropTypes from 'prop-types'; -import {withOnyx} from 'react-native-onyx'; -import ONYXKEYS from '../../ONYXKEYS'; -import CONST from '../../CONST'; -import {withNetwork} from '../OnyxProvider'; -import compose from '../../libs/compose'; import IOUQuote from './IOUQuote'; import reportActionPropTypes from '../../pages/home/report/reportActionPropTypes'; -import networkPropTypes from '../networkPropTypes'; -import iouReportPropTypes from '../../pages/iouReportPropTypes'; -import IOUPreview from './IOUPreview'; import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; -import styles from '../../styles/styles'; -import * as IOUUtils from '../../libs/IOUUtils'; const propTypes = { /** All the data of the action */ @@ -23,35 +13,14 @@ const propTypes = { /** The ID of the associated chatReport */ chatReportID: PropTypes.string.isRequired, - /** Is this IOUACTION the most recent? */ - isMostRecentIOUReportAction: PropTypes.bool.isRequired, - /** Popover context menu anchor, used for showing context menu */ contextMenuAnchor: PropTypes.shape({current: PropTypes.elementType}), /** Callback for updating context menu active state, used for showing context menu */ checkIfContextMenuActive: PropTypes.func, - /* Onyx Props */ - /** chatReport associated with iouReport */ - chatReport: PropTypes.shape({ - /** The participants of this report */ - participants: PropTypes.arrayOf(PropTypes.string), - - /** Whether the chat report has an outstanding IOU */ - hasOutstandingIOU: PropTypes.bool.isRequired, - }), - - /** IOU report data object */ - iouReport: iouReportPropTypes, - - /** Array of report actions for this report */ - reportActions: PropTypes.objectOf(PropTypes.shape(reportActionPropTypes)), - /** Whether the IOU is hovered so we can modify its style */ isHovered: PropTypes.bool, - - network: networkPropTypes.isRequired, }; const defaultProps = { @@ -70,23 +39,6 @@ const ReportPreview = (props) => { Navigation.navigate(ROUTES.getIouDetailsRoute(props.chatReportID, props.action.originalMessage.IOUReportID)); }; - // const shouldShowIOUPreview = ( - // props.isMostRecentIOUReportAction - // && Boolean(props.action.originalMessage.IOUReportID) - // && props.chatReport.hasOutstandingIOU) || props.action.originalMessage.type === 'pay'; - - // let shouldShowPendingConversionMessage = false; - // if ( - // !_.isEmpty(props.iouReport) - // && !_.isEmpty(props.reportActions) - // && props.chatReport.hasOutstandingIOU - // && props.isMostRecentIOUReportAction - // && props.action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD - // && props.network.isOffline - // ) { - // shouldShowPendingConversionMessage = IOUUtils.isIOUReportPendingCurrencyConversion(props.reportActions, props.iouReport); - // } - return ( <> { checkIfContextMenuActive={props.checkIfContextMenuActive} isHovered={props.isHovered} /> - {/* {shouldShowIOUPreview && ( - - )} */} ); }; @@ -125,18 +58,4 @@ ReportPreview.propTypes = propTypes; ReportPreview.defaultProps = defaultProps; ReportPreview.displayName = 'ReportPreview'; -export default compose( - withOnyx({ - chatReport: { - key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, - }, - iouReport: { - key: ({action}) => `${ONYXKEYS.COLLECTION.REPORT}${action.originalMessage.IOUReportID}`, - }, - reportActions: { - key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReportID}`, - canEvict: false, - }, - }), - withNetwork(), -)(ReportPreview); +export default ReportPreview; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 6d0fb6334aeb..77722ace5f63 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -190,7 +190,6 @@ class ReportActionItem extends Component { Date: Tue, 2 May 2023 16:32:03 +0300 Subject: [PATCH 12/38] Remove unused default props. --- src/components/ReportActionItem/ReportPreview.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index f7a5d6c27f36..ac0df99c495f 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -26,11 +26,6 @@ const propTypes = { const defaultProps = { contextMenuAnchor: undefined, checkIfContextMenuActive: () => {}, - chatReport: { - participants: [], - }, - iouReport: {}, - reportActions: {}, isHovered: false, }; From 538eb6ecb8b6b551f811f5374e540410aa0f8f12 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 2 May 2023 22:23:11 +0300 Subject: [PATCH 13/38] Pull iou report from onyx to get the cached iou total --- .../ReportActionItem/ReportPreview.js | 49 ++++++++++++++++++- src/pages/home/report/ReportActionItem.js | 1 + 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index ac0df99c495f..bf8138e4eeac 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -1,10 +1,13 @@ -import _ from 'underscore'; +import _, { compose } from 'underscore'; import React from 'react'; import PropTypes from 'prop-types'; import IOUQuote from './IOUQuote'; import reportActionPropTypes from '../../pages/home/report/reportActionPropTypes'; import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; +import withLocalize from '../withLocalize'; +import { withOnyx } from 'react-native-onyx'; +import ONYXKEYS from '../../ONYXKEYS'; const propTypes = { /** All the data of the action */ @@ -21,6 +24,29 @@ const propTypes = { /** Whether the IOU is hovered so we can modify its style */ isHovered: PropTypes.bool, + + /** The active IOUReport, used for Onyx subscription */ + // eslint-disable-next-line react/no-unused-prop-types + iouReportID: PropTypes.string.isRequired, + + /* Onyx Props */ + /** Active IOU Report for current report */ + iouReport: 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 in cents of this transaction */ + total: PropTypes.number, + + /** Currency of outstanding amount of this transaction */ + currency: PropTypes.string, + + /** Does the iouReport have an outstanding IOU? */ + hasOutstandingIOU: PropTypes.bool, + }), }; const defaultProps = { @@ -30,14 +56,26 @@ const defaultProps = { }; const ReportPreview = (props) => { + + if (props.iouReport.total === 0) { + return null; + } + const launchDetailsModal = () => { Navigation.navigate(ROUTES.getIouDetailsRoute(props.chatReportID, props.action.originalMessage.IOUReportID)); }; + const cachedTotal = props.iouReport.total && props.iouReport.currency + ? props.numberFormat( + Math.abs(props.iouReport.total) / 100, + {style: 'currency', currency: props.iouReport.currency}, + ) : ''; + return ( <> `${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, + }, + }), +)(ReportPreview); diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 77722ace5f63..a9659d74bef5 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -188,6 +188,7 @@ class ReportActionItem extends Component { } else if (this.props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW) { children = ( Date: Tue, 2 May 2023 22:46:17 +0300 Subject: [PATCH 14/38] No more IOUQUote reusability in ReportPreview --- .../ReportActionItem/ReportPreview.js | 72 ++++++++++++++----- 1 file changed, 55 insertions(+), 17 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index bf8138e4eeac..66c321075e56 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -1,12 +1,22 @@ -import _, { compose } from 'underscore'; +import _, {compose} from 'underscore'; import React from 'react'; +import {View, Pressable} from 'react-native'; import PropTypes from 'prop-types'; -import IOUQuote from './IOUQuote'; +import Text from '../Text'; +import Icon from '../Icon'; +import * as Expensicons from '../Icon/Expensicons'; +import styles from '../../styles/styles'; +import themeColors from '../../styles/themes/default'; import reportActionPropTypes from '../../pages/home/report/reportActionPropTypes'; +import withLocalize, {withLocalizePropTypes} from '../withLocalize'; +import ControlSelection from '../../libs/ControlSelection'; +import * as DeviceCapabilities from '../../libs/DeviceCapabilities'; +import {showContextMenuForReport} from '../ShowContextMenuContext'; +import * as StyleUtils from '../../styles/StyleUtils'; +import getButtonState from '../../libs/getButtonState'; import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; -import withLocalize from '../withLocalize'; -import { withOnyx } from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import ONYXKEYS from '../../ONYXKEYS'; const propTypes = { @@ -47,6 +57,8 @@ const propTypes = { /** Does the iouReport have an outstanding IOU? */ hasOutstandingIOU: PropTypes.bool, }), + + ...withLocalizePropTypes, }; const defaultProps = { @@ -60,7 +72,7 @@ const ReportPreview = (props) => { if (props.iouReport.total === 0) { return null; } - + const launchDetailsModal = () => { Navigation.navigate(ROUTES.getIouDetailsRoute(props.chatReportID, props.action.originalMessage.IOUReportID)); }; @@ -72,18 +84,44 @@ const ReportPreview = (props) => { ) : ''; return ( - <> - - + + {_.map(props.action.message, (fragment, index) => ( + DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} + onPressOut={() => ControlSelection.unblock()} + onLongPress={event => showContextMenuForReport( + event, + props.contextMenuAnchor, + props.chatReportID, + props.action, + props.checkIfContextMenuActive, + )} + style={[styles.flexRow, styles.justifyContentBetween, + props.shouldAllowViewDetails + ? undefined + : styles.cursorDefault, + ]} + focusable={props.shouldAllowViewDetails} + > + + + {/* Get first word of IOU message */} + {fragment.text.split(' ')[0]} + + + {/* Get remainder of IOU message */} + {fragment.text.substring(fragment.text.indexOf(' '))} + + + + + ))} + ); }; From dd2664965651781fb8c18cf36943eaab9c48befb Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Tue, 2 May 2023 23:43:33 +0300 Subject: [PATCH 15/38] Hardcode translations. --- .../ReportActionItem/ReportPreview.js | 61 ++++++++----------- src/libs/ReportActionsUtils.js | 5 +- 2 files changed, 26 insertions(+), 40 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index 66c321075e56..7a52eb30e4ae 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -6,7 +6,6 @@ import Text from '../Text'; import Icon from '../Icon'; import * as Expensicons from '../Icon/Expensicons'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/default'; import reportActionPropTypes from '../../pages/home/report/reportActionPropTypes'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import ControlSelection from '../../libs/ControlSelection'; @@ -82,45 +81,30 @@ const ReportPreview = (props) => { Math.abs(props.iouReport.total) / 100, {style: 'currency', currency: props.iouReport.currency}, ) : ''; + + const text = props.iouReport.hasOutstandingIOU ? `${props.chatReport.displayName} owes ${cachedTotal}` : `Settled up ${cachedTotal}`; return ( - {_.map(props.action.message, (fragment, index) => ( - DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} - onPressOut={() => ControlSelection.unblock()} - onLongPress={event => showContextMenuForReport( - event, - props.contextMenuAnchor, - props.chatReportID, - props.action, - props.checkIfContextMenuActive, - )} - style={[styles.flexRow, styles.justifyContentBetween, - props.shouldAllowViewDetails - ? undefined - : styles.cursorDefault, - ]} - focusable={props.shouldAllowViewDetails} - > - - - {/* Get first word of IOU message */} - {fragment.text.split(' ')[0]} - - - {/* Get remainder of IOU message */} - {fragment.text.substring(fragment.text.indexOf(' '))} - - - - - ))} + DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} + onPressOut={() => ControlSelection.unblock()} + onLongPress={event => showContextMenuForReport( + event, + props.contextMenuAnchor, + props.chatReportID, + props.action, + props.checkIfContextMenuActive, + )} + style={[styles.flexRow, styles.justifyContentBetween]} + focusable + > + + {text} + + + ); }; @@ -132,6 +116,9 @@ ReportPreview.displayName = 'ReportPreview'; export default compose( withLocalize, withOnyx({ + chatReport: { + key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, + }, iouReport: { key: ({iouReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, }, diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index fe6d6cea2349..a72614729f1e 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -296,7 +296,6 @@ function getReportPreviewAction(reportID) { } function buildOptimisticReportPreview(reportID, iouReportID, payeeAccountID, amount) { - const textComment = `Duraflame owes you ${amount}`; return ({ reportActionID: NumberUtils.rand64(), reportID: reportID, @@ -305,8 +304,8 @@ function buildOptimisticReportPreview(reportID, iouReportID, payeeAccountID, amo pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, accountID: payeeAccountID, message: [{ - html: textComment, - text: textComment, + html: '', + text: '', isEdited: false, type: CONST.REPORT.MESSAGE.TYPE.COMMENT }], From 031ffbf8756f7fa950bbc18f399d4890ecd4e66d Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Thu, 4 May 2023 19:34:10 +0300 Subject: [PATCH 16/38] Add reportPreviewReportActionID as a param to RequestMoney command --- 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 c17b6ee5c94a..83ed6361b96f 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -213,6 +213,7 @@ function requestMoney(report, amount, currency, recipientEmail, participant, com transactionID: optimisticReportAction.originalMessage.IOUTransactionID, reportActionID: optimisticReportAction.reportActionID, createdReportActionID: isNewChat ? optimisticCreatedAction.reportActionID : 0, + reportPreviewReportActionID: reportPreview.reportActionID, }, {optimisticData, successData, failureData}); Navigation.navigate(ROUTES.getReportRoute(chatReport.reportID)); } From 595cb4163402d280bca2ad28b09f7a63471b41e8 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Fri, 5 May 2023 00:02:11 +0300 Subject: [PATCH 17/38] Improvements. --- src/components/ReportActionItem/ReportPreview.js | 4 ++-- src/libs/ReportActionsUtils.js | 6 +++++- src/libs/actions/IOU.js | 1 - 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index 7a52eb30e4ae..7766c679df10 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -82,7 +82,7 @@ const ReportPreview = (props) => { {style: 'currency', currency: props.iouReport.currency}, ) : ''; - const text = props.iouReport.hasOutstandingIOU ? `${props.chatReport.displayName} owes ${cachedTotal}` : `Settled up ${cachedTotal}`; + const text = props.iouReport.hasOutstandingIOU ? `${props.report.displayName} owes ${cachedTotal}` : `Settled up ${cachedTotal}`; return ( @@ -116,7 +116,7 @@ ReportPreview.displayName = 'ReportPreview'; export default compose( withLocalize, withOnyx({ - chatReport: { + report: { key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, }, iouReport: { diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index a72614729f1e..0179fcc400cf 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -81,6 +81,10 @@ function getSortedReportActions(reportActions, shouldSortInDescendingOrder = fal if ((first.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED || second.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED) && first.actionName !== second.actionName) { return ((first.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED) ? -1 : 1) * invertedMultiplier; } + // Ensure that `REPORTPREVIEW` actions always come after if they have the same timestamp as another action type + if ((first.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW || second.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW) && first.actionName !== second.actionName) { + return ((first.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW) ? 1 : -1) * invertedMultiplier; + } // Then fallback on reportActionID as the final sorting criteria. It is a random number, // but using this will ensure that the order of reportActions with the same created time and action type @@ -224,7 +228,7 @@ function shouldReportActionBeVisible(reportAction, key) { } // All other actions are displayed except deleted, non-pending actions - const isDeleted = isDeletedAction(reportAction); + const isDeleted = isDeletedAction(reportAction); // && !reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW; const isPending = !_.isEmpty(reportAction.pendingAction); return !isDeleted || isPending; } diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 883096e0fcb0..2948fd17dc77 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -102,7 +102,6 @@ function requestMoney(report, amount, currency, recipientEmail, participant, com ); let reportPreview = ReportActionsUtils.getReportPreviewAction(chatReport.reportID); - console.log(reportPreview); if (!reportPreview) { reportPreview = ReportActionsUtils.buildOptimisticReportPreview(chatReport.reportID, iouReport.reportID); } From a77f5c69edbfbea3ad4eaaf85657c96295800ec4 Mon Sep 17 00:00:00 2001 From: Cristi Paval Date: Fri, 5 May 2023 00:58:53 +0300 Subject: [PATCH 18/38] Pay button. --- .../ReportActionItem/ReportPreview.js | 41 ++++++++++++++++--- src/libs/ReportActionsUtils.js | 2 +- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index 7766c679df10..f71c6526a01b 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -1,4 +1,5 @@ import _, {compose} from 'underscore'; +import lodashGet from 'lodash/get'; import React from 'react'; import {View, Pressable} from 'react-native'; import PropTypes from 'prop-types'; @@ -17,6 +18,8 @@ import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; import {withOnyx} from 'react-native-onyx'; import ONYXKEYS from '../../ONYXKEYS'; +import Button from '../Button'; +import CONST from '../../CONST'; const propTypes = { /** All the data of the action */ @@ -72,6 +75,11 @@ const ReportPreview = (props) => { return null; } + const sessionEmail = lodashGet(props.session, 'email', null); + const managerEmail = props.iouReport.managerEmail || ''; + // Pay button should only be visible to the manager of the report. + const isCurrentUserManager = managerEmail === sessionEmail; + const launchDetailsModal = () => { Navigation.navigate(ROUTES.getIouDetailsRoute(props.chatReportID, props.action.originalMessage.IOUReportID)); }; @@ -82,8 +90,8 @@ const ReportPreview = (props) => { {style: 'currency', currency: props.iouReport.currency}, ) : ''; - const text = props.iouReport.hasOutstandingIOU ? `${props.report.displayName} owes ${cachedTotal}` : `Settled up ${cachedTotal}`; - + const text = props.iouReport.hasOutstandingIOU ? `${props.chatReport.displayName} owes ${cachedTotal}` : `Settled up ${cachedTotal}`; + return ( { style={[styles.flexRow, styles.justifyContentBetween]} focusable > - - {text} - + + + {text} + + {(isCurrentUserManager && props.iouReport.stateNum === CONST.REPORT.STATE_NUM.PROCESSING && ( +