From 202122a111e28c9cbfafe3fe26c756ca70340c9e Mon Sep 17 00:00:00 2001 From: David Bondy Date: Wed, 10 May 2023 18:46:23 -0600 Subject: [PATCH 01/18] create method to find if a thread exists or not --- src/libs/ReportUtils.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 060e3f6bf1a0..9f1383a32305 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1847,6 +1847,24 @@ function getWhisperDisplayNames(participants) { return _.map(participants, (login) => getDisplayNameForParticipant(login, !isWhisperOnlyVisibleToCurrentUSer)).join(', '); } +/** + * Used to determine if a thread exists already or not for a given reportActionID + * + * @param {String} + * @returns {Boolean} + */ +function doesThreadExistForReportActionID(reportActionID) { + const thread = _.find(allReports, (report) => { + if (!report.parentReportActionID) { + return false; + } + + return report.parentReportActionID === reportActionID; + }); + + return !_.isEmpty(thread); +} + export { getReportParticipantsTitle, isReportMessageAttachment, @@ -1925,4 +1943,5 @@ export { canRequestMoney, getWhisperDisplayNames, getWorkspaceAvatar, + doesThreadExistForReportActionID, }; From fa1b3dc1509c6ebf5cd29662ee25d556950ca031 Mon Sep 17 00:00:00 2001 From: David Bondy Date: Wed, 10 May 2023 18:46:59 -0600 Subject: [PATCH 02/18] add rough sketch of what needs to happen to create a thread on demand --- src/components/ReportActionItem/IOUAction.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/ReportActionItem/IOUAction.js b/src/components/ReportActionItem/IOUAction.js index c304129517b6..f15f9b6505c6 100644 --- a/src/components/ReportActionItem/IOUAction.js +++ b/src/components/ReportActionItem/IOUAction.js @@ -15,6 +15,7 @@ import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; import styles from '../../styles/styles'; import * as IOUUtils from '../../libs/IOUUtils'; +import {doesThreadExistForReportActionID} from '../../libs/ReportUtils'; const propTypes = { /** All the data of the action */ @@ -71,6 +72,10 @@ const defaultProps = { const IOUAction = (props) => { const hasMultipleParticipants = props.chatReport.participants.length > 1; const onIOUPreviewPressed = () => { + if (!doesThreadExistForReportActionID(props.action.reportActionID)) { + // optimistically create reportID + // pass it along to OpenReport since that is build to create a thread when needed, not sure what to do about the navigation stuff below... + } if (hasMultipleParticipants) { Navigation.navigate(ROUTES.getReportParticipantsRoute(props.chatReportID)); } else { From a5e043eab511b6952366e19e6519f71b31d872f0 Mon Sep 17 00:00:00 2001 From: David Bondy Date: Thu, 11 May 2023 20:42:03 -0600 Subject: [PATCH 03/18] rename method and update to return the report object for a thread --- src/libs/ReportUtils.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index e1b482775c43..25762a1e041b 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1900,16 +1900,14 @@ function getWhisperDisplayNames(participants) { * @param {String} * @returns {Boolean} */ -function doesThreadExistForReportActionID(reportActionID) { - const thread = _.find(allReports, (report) => { +function getThreadForReportActionID(reportActionID) { + return _.find(allReports, (report) => { if (!report.parentReportActionID) { return false; } return report.parentReportActionID === reportActionID; }); - - return !_.isEmpty(thread); } /** @@ -2008,6 +2006,6 @@ export { canRequestMoney, getWhisperDisplayNames, getWorkspaceAvatar, - doesThreadExistForReportActionID, + getThreadForReportActionID, shouldReportShowSubscript, }; From 94c9125e3a3e7986010b4ce17a0dd9a13e79b4f2 Mon Sep 17 00:00:00 2001 From: David Bondy Date: Thu, 11 May 2023 23:09:30 -0600 Subject: [PATCH 04/18] allow the passing of a parentReportActionID so we can create threads --- src/libs/ReportUtils.js | 2 ++ src/libs/actions/Report.js | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 25762a1e041b..c13b15f30887 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1276,6 +1276,7 @@ function buildOptimisticChatReport( oldPolicyName = '', visibility = undefined, notificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, + parentReportActionID = '', ) { const currentTime = DateUtils.getDBTime(); return { @@ -1295,6 +1296,7 @@ function buildOptimisticChatReport( participants: participantList, policyID, reportID: generateReportID(), + parentReportActionID, reportName, stateNum: 0, statusNum: 0, diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 26d9b9da7357..a9e8b36b8e6c 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -399,6 +399,11 @@ function openReport(reportID, participantList = [], newReportObject = {}) { // Add the createdReportActionID parameter to the API call params.createdReportActionID = optimisticCreatedAction.reportActionID; + + // Only add the parentReportActionID if it's been added to the optimistic report + if (newReportObject.parentReportActionID) { + params.parentReportActionID = newReportObject.parentReportActionID; + } } API.write('OpenReport', params, onyxData); From 94c19d6adfe1c3a2547748ce16d9ea287a507f6e Mon Sep 17 00:00:00 2001 From: David Bondy Date: Thu, 11 May 2023 23:10:21 -0600 Subject: [PATCH 05/18] generate report optimistically and pass parentReportActionID to create a thread --- .../ReportActionItem/MoneyRequestAction.js | 48 +++++++++++++++++-- src/languages/en.js | 1 + 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestAction.js b/src/components/ReportActionItem/MoneyRequestAction.js index d07d76640745..46894288e2db 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.js +++ b/src/components/ReportActionItem/MoneyRequestAction.js @@ -15,7 +15,10 @@ import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; import styles from '../../styles/styles'; import * as IOUUtils from '../../libs/IOUUtils'; -import {doesThreadExistForReportActionID} from '../../libs/ReportUtils'; +import {getThreadForReportActionID, buildOptimisticChatReport} from '../../libs/ReportUtils'; +import * as Report from '../../libs/actions/Report'; +import withLocalize, {withLocalizePropTypes} from '../withLocalize'; +import lodashGet from 'lodash/get'; const propTypes = { /** All the data of the action */ @@ -56,6 +59,14 @@ const propTypes = { isHovered: PropTypes.bool, network: networkPropTypes.isRequired, + + /** Session info for the currently logged in user. */ + session: PropTypes.shape({ + /** Currently logged in user email */ + email: PropTypes.string, + }), + + ...withLocalizePropTypes, }; const defaultProps = { @@ -67,14 +78,39 @@ const defaultProps = { iouReport: {}, reportActions: {}, isHovered: false, + session: { + email: null, + }, }; const MoneyRequestAction = (props) => { const hasMultipleParticipants = props.chatReport.participants.length > 1; const onIOUPreviewPressed = () => { - if (!doesThreadExistForReportActionID(props.action.reportActionID)) { - // optimistically create reportID - // pass it along to OpenReport since that is build to create a thread when needed, not sure what to do about the navigation stuff below... + // This would ideally be passed as a prop or hooked up via withOnyx so that we are not be triggering a potentially intensive + // search in an onPress handler, I think this could lead to performance issues but it probably ok for now. + const thread = getThreadForReportActionID(props.action.reportActionID); + console.log({thread}); + if (_.isEmpty(thread)) { + // Since a thread does not exist yet then we need to create it now. This is done by creating the report object + // and passing the parentReportActionID of the reportAction. OpenReport will then automatically create the thread for us. + const optimisticThreadReport = buildOptimisticChatReport( + props.chatReport.participants, + props.translate('iou.threadReportName', {payee: props.action.actorEmail, comment: props.action.originalMessage.comment}), + '', + props.chatReport.policyID, + props.chatReport.owner, + props.chatReport.type === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, + props.chatReport.oldPolicyName, + undefined, + CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, + props.action.reportActionID, + ); + console.log({optimisticThreadReport}); + Report.openReport( + optimisticThreadReport.reportID, + [_.reject(optimisticThreadReport.participants, (login) => login === lodashGet(props.session, 'email', ''))], + optimisticThreadReport, + ); } if (hasMultipleParticipants) { Navigation.navigate(ROUTES.getReportParticipantsRoute(props.chatReportID)); @@ -131,6 +167,7 @@ MoneyRequestAction.defaultProps = defaultProps; MoneyRequestAction.displayName = 'MoneyRequestAction'; export default compose( + withLocalize, withOnyx({ chatReport: { key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, @@ -142,6 +179,9 @@ export default compose( key: ({chatReportID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReportID}`, canEvict: false, }, + session: { + key: ONYXKEYS.SESSION, + }, }), withNetwork(), )(MoneyRequestAction); diff --git a/src/languages/en.js b/src/languages/en.js index b9f49302a43a..7c08af03ce3f 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -328,6 +328,7 @@ export default { payerOwesAmount: ({payer, amount}) => `${payer} owes ${amount}`, noReimbursableExpenses: 'This report has an invalid amount', pendingConversionMessage: "Total will update when you're back online", + threadReportName: ({payee, comment}) => `${payee} request${comment ? ` for ${comment}` : ''}`, error: { invalidSplit: 'Split amounts do not equal total amount', other: 'Unexpected error, please try again later', From 6e3bf2c53730b7507f2d95a852b0b2a08d298ed9 Mon Sep 17 00:00:00 2001 From: David Bondy Date: Fri, 12 May 2023 12:32:54 -0600 Subject: [PATCH 06/18] appease linter --- src/components/ReportActionItem/MoneyRequestAction.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ReportActionItem/MoneyRequestAction.js b/src/components/ReportActionItem/MoneyRequestAction.js index f74fa806fd56..798393a78e46 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.js +++ b/src/components/ReportActionItem/MoneyRequestAction.js @@ -2,6 +2,7 @@ import _ from 'underscore'; import React from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; +import lodashGet from 'lodash/get'; import ONYXKEYS from '../../ONYXKEYS'; import CONST from '../../CONST'; import {withNetwork} from '../OnyxProvider'; From b4a991b92f1167687a9673863d58b88b1a53d207 Mon Sep 17 00:00:00 2001 From: David Bondy Date: Fri, 12 May 2023 12:33:13 -0600 Subject: [PATCH 07/18] appease linter and import full lib --- src/components/ReportActionItem/MoneyRequestAction.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestAction.js b/src/components/ReportActionItem/MoneyRequestAction.js index 798393a78e46..ec613e1e0cc5 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.js +++ b/src/components/ReportActionItem/MoneyRequestAction.js @@ -16,10 +16,9 @@ import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; import styles from '../../styles/styles'; import * as IOUUtils from '../../libs/IOUUtils'; -import {getThreadForReportActionID, buildOptimisticChatReport} from '../../libs/ReportUtils'; +import * as ReportUtils from '../../libs/ReportUtils'; import * as Report from '../../libs/actions/Report'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; -import lodashGet from 'lodash/get'; const propTypes = { /** All the data of the action */ @@ -89,12 +88,12 @@ const MoneyRequestAction = (props) => { const onIOUPreviewPressed = () => { // This would ideally be passed as a prop or hooked up via withOnyx so that we are not be triggering a potentially intensive // search in an onPress handler, I think this could lead to performance issues but it probably ok for now. - const thread = getThreadForReportActionID(props.action.reportActionID); + const thread = ReportUtils.getThreadForReportActionID(props.action.reportActionID); console.log({thread}); if (_.isEmpty(thread)) { // Since a thread does not exist yet then we need to create it now. This is done by creating the report object // and passing the parentReportActionID of the reportAction. OpenReport will then automatically create the thread for us. - const optimisticThreadReport = buildOptimisticChatReport( + const optimisticThreadReport = ReportUtils.buildOptimisticChatReport( props.chatReport.participants, props.translate('iou.threadReportName', {payee: props.action.actorEmail, comment: props.action.originalMessage.comment}), '', From c0e243046d8d035313781674e9ac1de7fe8f6424 Mon Sep 17 00:00:00 2001 From: David Bondy Date: Fri, 12 May 2023 12:35:34 -0600 Subject: [PATCH 08/18] we dont need to prefilter the user making the request out since auth will do this for us --- src/components/ReportActionItem/MoneyRequestAction.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestAction.js b/src/components/ReportActionItem/MoneyRequestAction.js index ec613e1e0cc5..df3afea34ab3 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.js +++ b/src/components/ReportActionItem/MoneyRequestAction.js @@ -106,11 +106,7 @@ const MoneyRequestAction = (props) => { props.action.reportActionID, ); console.log({optimisticThreadReport}); - Report.openReport( - optimisticThreadReport.reportID, - [_.reject(optimisticThreadReport.participants, (login) => login === lodashGet(props.session, 'email', ''))], - optimisticThreadReport, - ); + Report.openReport(optimisticThreadReport.reportID, optimisticThreadReport.participants, optimisticThreadReport); } if (hasMultipleParticipants) { Navigation.navigate(ROUTES.getReportParticipantsRoute(props.chatReportID)); From 4cfefbb9ae67f862b249d692dd0155eb4de2a745 Mon Sep 17 00:00:00 2001 From: David Bondy Date: Fri, 12 May 2023 12:56:56 -0600 Subject: [PATCH 09/18] use amount not payee in report name --- src/components/ReportActionItem/MoneyRequestAction.js | 6 +++++- src/languages/en.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestAction.js b/src/components/ReportActionItem/MoneyRequestAction.js index df3afea34ab3..80a9e8bd99a3 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.js +++ b/src/components/ReportActionItem/MoneyRequestAction.js @@ -19,6 +19,7 @@ import * as IOUUtils from '../../libs/IOUUtils'; import * as ReportUtils from '../../libs/ReportUtils'; import * as Report from '../../libs/actions/Report'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; +import * as CurrencyUtils from '../../libs/CurrencyUtils'; const propTypes = { /** All the data of the action */ @@ -95,7 +96,10 @@ const MoneyRequestAction = (props) => { // and passing the parentReportActionID of the reportAction. OpenReport will then automatically create the thread for us. const optimisticThreadReport = ReportUtils.buildOptimisticChatReport( props.chatReport.participants, - props.translate('iou.threadReportName', {payee: props.action.actorEmail, comment: props.action.originalMessage.comment}), + props.translate('iou.threadReportName', { + formattedAmount: CurrencyUtils.convertToDisplayString(props.iouReport.iouReportAmount, props.iouReport.currency), + comment: props.action.originalMessage.comment, + }), '', props.chatReport.policyID, props.chatReport.owner, diff --git a/src/languages/en.js b/src/languages/en.js index d078f55d042e..42bc7dd60e5b 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -329,7 +329,7 @@ export default { payerSettled: ({amount}) => `settled up ${amount}`, noReimbursableExpenses: 'This report has an invalid amount', pendingConversionMessage: "Total will update when you're back online", - threadReportName: ({payee, comment}) => `${payee} request${comment ? ` for ${comment}` : ''}`, + threadReportName: ({formattedAmount, comment}) => `${formattedAmount} request${comment ? ` for ${comment}` : ''}`, error: { invalidSplit: 'Split amounts do not equal total amount', other: 'Unexpected error, please try again later', From 9d518ba705f355e4ae512be8d312c144d470469f Mon Sep 17 00:00:00 2001 From: David Bondy Date: Fri, 12 May 2023 12:58:54 -0600 Subject: [PATCH 10/18] fixing jsdocs and comment --- src/libs/ReportUtils.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index b46887d4f89d..b02cf301f0e7 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1295,6 +1295,7 @@ function buildOptimisticIOUReportAction(type, amount, currency, comment, partici * @param {String} oldPolicyName * @param {String} visibility * @param {String} notificationPreference + * @param {String} parentReportActionID * @returns {Object} */ function buildOptimisticChatReport( @@ -1912,9 +1913,9 @@ function getWhisperDisplayNames(participants) { } /** - * Used to determine if a thread exists already or not for a given reportActionID + * Used to determine if a thread exists already for a given reportActionID * - * @param {String} + * @param {String} reportActionID * @returns {Boolean} */ function getThreadForReportActionID(reportActionID) { From f83e2404e8ede29eaa4512a94cfc4d26255df962 Mon Sep 17 00:00:00 2001 From: David Bondy Date: Fri, 12 May 2023 13:01:32 -0600 Subject: [PATCH 11/18] use proper prop --- src/components/ReportActionItem/MoneyRequestAction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestAction.js b/src/components/ReportActionItem/MoneyRequestAction.js index 80a9e8bd99a3..7341cace4434 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.js +++ b/src/components/ReportActionItem/MoneyRequestAction.js @@ -103,7 +103,7 @@ const MoneyRequestAction = (props) => { '', props.chatReport.policyID, props.chatReport.owner, - props.chatReport.type === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, + props.chatReport.isOwnPolicyExpenseReport, props.chatReport.oldPolicyName, undefined, CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, From dfc4e7974e3a1dbd1289e103cc5c6b34e28dea87 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Sun, 14 May 2023 00:26:15 +0200 Subject: [PATCH 12/18] Fix linter --- src/components/ReportActionItem/MoneyRequestAction.js | 7 +++---- src/libs/ReportUtils.js | 1 - src/libs/actions/Report.js | 5 +++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestAction.js b/src/components/ReportActionItem/MoneyRequestAction.js index 7341cace4434..bac44c5c0e8c 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.js +++ b/src/components/ReportActionItem/MoneyRequestAction.js @@ -2,7 +2,6 @@ import _ from 'underscore'; import React from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; -import lodashGet from 'lodash/get'; import ONYXKEYS from '../../ONYXKEYS'; import CONST from '../../CONST'; import {withNetwork} from '../OnyxProvider'; @@ -90,14 +89,13 @@ const MoneyRequestAction = (props) => { // This would ideally be passed as a prop or hooked up via withOnyx so that we are not be triggering a potentially intensive // search in an onPress handler, I think this could lead to performance issues but it probably ok for now. const thread = ReportUtils.getThreadForReportActionID(props.action.reportActionID); - console.log({thread}); if (_.isEmpty(thread)) { // Since a thread does not exist yet then we need to create it now. This is done by creating the report object // and passing the parentReportActionID of the reportAction. OpenReport will then automatically create the thread for us. const optimisticThreadReport = ReportUtils.buildOptimisticChatReport( props.chatReport.participants, props.translate('iou.threadReportName', { - formattedAmount: CurrencyUtils.convertToDisplayString(props.iouReport.iouReportAmount, props.iouReport.currency), + formattedAmount: CurrencyUtils.convertToDisplayString(props.iouReport.amount, props.iouReport.currency), comment: props.action.originalMessage.comment, }), '', @@ -109,8 +107,9 @@ const MoneyRequestAction = (props) => { CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, props.action.reportActionID, ); - console.log({optimisticThreadReport}); Report.openReport(optimisticThreadReport.reportID, optimisticThreadReport.participants, optimisticThreadReport); + } else { + Report.openReport(thread.reportID, thread.participants, thread); } if (hasMultipleParticipants) { Navigation.navigate(ROUTES.getReportParticipantsRoute(props.chatReportID)); diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index c3dd7d9d5691..a2b810fb0cf6 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1411,7 +1411,6 @@ function buildOptimisticChatReport( participants: participantList, policyID, reportID: generateReportID(), - parentReportActionID, reportName, stateNum: 0, statusNum: 0, diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 301d33651cdd..d71ab7d5b526 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -401,8 +401,9 @@ function openReport(reportID, participantList = [], newReportObject = {}, parent value: {[optimisticCreatedAction.reportActionID]: {pendingAction: null}}, }); - // Add the createdReportActionID parameter to the API call - params.createdReportActionID = optimisticCreatedAction.reportActionID; + // Add the createdReportActionID parameter to the API call + params.createdReportActionID = optimisticCreatedAction.reportActionID; + } // Only add the parentReportActionID if it's been added to the optimistic report if (newReportObject.parentReportActionID) { From 35c3c79599a0d3e9b424cec0907f54f277877fe1 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Sun, 14 May 2023 22:29:14 +0200 Subject: [PATCH 13/18] Ensure threads are created optimistically --- .../ReportActionItem/MoneyRequestAction.js | 44 ++++++++++--------- src/languages/es.js | 1 + src/libs/ReportActionsUtils.js | 12 +++++ src/libs/ReportUtils.js | 16 +++++++ .../report/ReportActionItemParentAction.js | 6 +++ 5 files changed, 59 insertions(+), 20 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestAction.js b/src/components/ReportActionItem/MoneyRequestAction.js index d19841c1dedb..443944264858 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.js +++ b/src/components/ReportActionItem/MoneyRequestAction.js @@ -16,6 +16,7 @@ import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; import styles from '../../styles/styles'; import * as IOUUtils from '../../libs/IOUUtils'; +import * as OptionsListUtils from '../../libs/OptionsListUtils'; import * as ReportUtils from '../../libs/ReportUtils'; import * as Report from '../../libs/actions/Report'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; @@ -87,35 +88,38 @@ const defaultProps = { const MoneyRequestAction = (props) => { const hasMultipleParticipants = lodashGet(props.chatReport, 'participants', []).length > 1; const onIOUPreviewPressed = () => { - // This would ideally be passed as a prop or hooked up via withOnyx so that we are not be triggering a potentially intensive - // search in an onPress handler, I think this could lead to performance issues but it probably ok for now. - const thread = ReportUtils.getThreadForReportActionID(props.action.reportActionID); - if (_.isEmpty(thread)) { - // Since a thread does not exist yet then we need to create it now. This is done by creating the report object - // and passing the parentReportActionID of the reportAction. OpenReport will then automatically create the thread for us. - const optimisticThreadReport = ReportUtils.buildOptimisticChatReport( - props.chatReport.participants, + if (lodashGet(props.action, 'originalMessage.type', '') === CONST.IOU.REPORT_ACTION_TYPE.SPLIT && hasMultipleParticipants) { + Navigation.navigate(ROUTES.getReportParticipantsRoute(props.chatReportID)); + return; + } + + // If the childReportID is not present, we need to create a new thread + const childReportID = lodashGet(props.action, 'childReportID', '0'); + if (childReportID === '0') { + const participants = _.uniq([props.session.email, props.action.actorEmail]); + const formattedUserLogins = _.map(participants, (login) => OptionsListUtils.addSMSDomainIfPhoneNumber(login).toLowerCase()); + const thread = ReportUtils.buildOptimisticChatReport( + formattedUserLogins, props.translate('iou.threadReportName', { - formattedAmount: CurrencyUtils.convertToDisplayString(props.iouReport.amount, props.iouReport.currency), + formattedAmount: CurrencyUtils.convertToDisplayString(lodashGet(props.action, 'originalMessage.amount', 0), lodashGet(props.action, 'originalMessage.currency', '')), comment: props.action.originalMessage.comment, }), '', - props.chatReport.policyID, - props.chatReport.owner, - props.chatReport.isOwnPolicyExpenseReport, - props.chatReport.oldPolicyName, + CONST.POLICY.OWNER_EMAIL_FAKE, + CONST.POLICY.OWNER_EMAIL_FAKE, + false, + '', undefined, CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, props.action.reportActionID, + props.chatReportID, // Needs to be changed to iouReportID ); - Report.openReport(optimisticThreadReport.reportID, optimisticThreadReport.participants, optimisticThreadReport); - } else { - Report.openReport(thread.reportID, thread.participants, thread); - } - if (hasMultipleParticipants) { - Navigation.navigate(ROUTES.getReportParticipantsRoute(props.chatReportID)); + + Report.openReport(thread.reportID, thread.participants, thread, props.action.reportActionID); + Navigation.navigate(ROUTES.getReportRoute(thread.reportID)); } else { - Navigation.navigate(ROUTES.getIouDetailsRoute(props.chatReportID, props.action.originalMessage.IOUReportID)); + Report.openReport(childReportID); + Navigation.navigate(ROUTES.getReportRoute(childReportID)); } }; diff --git a/src/languages/es.js b/src/languages/es.js index de00f658fb16..65d829af87dc 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -330,6 +330,7 @@ export default { payerSettled: ({amount}) => `pagado ${amount}`, noReimbursableExpenses: 'El monto de este informe es inválido', pendingConversionMessage: 'El total se actualizará cuando estés online', + threadReportName: ({formattedAmount, comment}) => `${formattedAmount} pedido${comment ? ` per ${comment}` : ''}`, error: { invalidSplit: 'La suma de las partes no equivale al monto total', other: 'Error inesperado, por favor inténtalo más tarde', diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index 6160e4ca1e19..c290ad7c3b4b 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -53,6 +53,17 @@ function getParentReportAction(report) { return lodashGet(allReportActions, [report.parentReportID, report.parentReportActionID], {}); } +/** + * Returns whether the thread is a transaction thread, which is any thread with IOU parent + * report action of type create. + * + * @param {Object} parentReportAction + * @returns {Boolean} + */ +function isTransactionThread(parentReportAction) { + return parentReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && lodashGet(parentReportAction, 'originalMessage.type') === CONST.IOU.REPORT_ACTION_TYPE.CREATE; +} + /** * Sort an array of reportActions by their created timestamp first, and reportActionID second * This gives us a stable order even in the case of multiple reportActions created on the same millisecond @@ -318,4 +329,5 @@ export { getLinkedTransactionID, isCreatedTaskReportAction, getParentReportAction, + isTransactionThread, }; diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 1812ed9496fc..cf51c25b659b 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1001,6 +1001,19 @@ function getMoneyRequestReportName(report) { return Localize.translateLocal('iou.payerOwesAmount', {payer: payerName, amount: formattedAmount}); } +/** + * Given a parent IOU report action get report name for the LHN. + * + * @param {Object} reportAction + * @returns {String} + */ +function getTransactionReportName(reportAction) { + return Localize.translateLocal('iou.threadReportName', { + formattedAmount: CurrencyUtils.convertToDisplayString(lodashGet(reportAction, 'originalMessage.amount', 0), lodashGet(reportAction, 'originalMessage.currency', '')), + comment: lodashGet(reportAction, 'originalMessage.comment'), + }); +} + /** * Get the title for a report. * @@ -1011,6 +1024,9 @@ function getReportName(report) { let formattedName; if (isThread(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); + if (ReportActionsUtils.isTransactionThread(parentReportAction)) { + return getTransactionReportName(parentReportAction); + } const parentReportActionMessage = lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); return parentReportActionMessage || Localize.translateLocal('parentReportAction.deletedMessage'); } diff --git a/src/pages/home/report/ReportActionItemParentAction.js b/src/pages/home/report/ReportActionItemParentAction.js index 0714265a7899..cc50b62a1774 100644 --- a/src/pages/home/report/ReportActionItemParentAction.js +++ b/src/pages/home/report/ReportActionItemParentAction.js @@ -14,6 +14,7 @@ import compose from '../../../libs/compose'; import withLocalize from '../../../components/withLocalize'; import ReportActionItem from './ReportActionItem'; import reportActionPropTypes from './reportActionPropTypes'; +import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; const propTypes = { /** The id of the report */ @@ -41,6 +42,11 @@ const defaultProps = { const ReportActionItemParentAction = (props) => { const parentReportAction = props.parentReportActions[`${props.report.parentReportActionID}`]; + + // In case of transaction threads, we do not want to render the parent report action. + if (ReportActionsUtils.isTransactionThread(parentReportAction)) { + return null; + } return ( Date: Mon, 15 May 2023 00:13:11 +0200 Subject: [PATCH 14/18] Add the childReportID to the report action so we do not create multiple reports --- src/libs/actions/Report.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index d71ab7d5b526..cc965e19871c 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -405,9 +405,18 @@ function openReport(reportID, participantList = [], newReportObject = {}, parent params.createdReportActionID = optimisticCreatedAction.reportActionID; } - // Only add the parentReportActionID if it's been added to the optimistic report - if (newReportObject.parentReportActionID) { - params.parentReportActionID = newReportObject.parentReportActionID; + // If we are creating a thread, ensure the report action has childReportID property added + if (newReportObject.parentReportID && parentReportActionID) { + onyxData.optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${newReportObject.parentReportID}`, + value: {[parentReportActionID]: {childReportID: reportID}}, + }); + onyxData.failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${newReportObject.parentReportID}`, + value: {[parentReportActionID]: {childReportID: '0'}}, + }); } } From 3ec51b59dac3750e8f9ce35e640dd7e6251d9a5f Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Mon, 15 May 2023 00:27:09 +0200 Subject: [PATCH 15/18] Remove unsused method --- src/libs/ReportUtils.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 46c17bf9abf8..8fa24c8fb75c 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -2051,22 +2051,6 @@ function getWhisperDisplayNames(participants) { return _.map(participants, (login) => getDisplayNameForParticipant(login, !isWhisperOnlyVisibleToCurrentUSer)).join(', '); } -/** - * Used to determine if a thread exists already for a given reportActionID - * - * @param {String} reportActionID - * @returns {Boolean} - */ -function getThreadForReportActionID(reportActionID) { - return _.find(allReports, (report) => { - if (!report.parentReportActionID) { - return false; - } - - return report.parentReportActionID === reportActionID; - }); -} - /** * Show subscript on IOU or expense report * @param {Object} report @@ -2173,7 +2157,6 @@ export { canRequestMoney, getWhisperDisplayNames, getWorkspaceAvatar, - getThreadForReportActionID, isThread, isThreadParent, isThreadFirstChat, From e9d4c4afb7f89d554f228a5ae140b0e81416a149 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Mon, 15 May 2023 18:15:33 +0200 Subject: [PATCH 16/18] Access property safely --- src/libs/ReportActionsUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index 108e6eee427b..f6b4d7ba7ab7 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -61,7 +61,7 @@ function getParentReportAction(report) { * @returns {Boolean} */ function isTransactionThread(parentReportAction) { - return parentReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && lodashGet(parentReportAction, 'originalMessage.type') === CONST.IOU.REPORT_ACTION_TYPE.CREATE; + return parentReportAction && parentReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && lodashGet(parentReportAction, 'originalMessage.type') === CONST.IOU.REPORT_ACTION_TYPE.CREATE; } /** From df1462a91f9ef3b101b7c031c02cd15c2c50645b Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Mon, 15 May 2023 18:30:53 +0200 Subject: [PATCH 17/18] Update spanish translation --- src/languages/es.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.js b/src/languages/es.js index e7b012fbeee3..23114b5bc428 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -332,7 +332,7 @@ export default { payerSettled: ({amount}) => `pagado ${amount}`, noReimbursableExpenses: 'El monto de este informe es inválido', pendingConversionMessage: 'El total se actualizará cuando estés online', - threadReportName: ({formattedAmount, comment}) => `${formattedAmount} pedido${comment ? ` per ${comment}` : ''}`, + threadReportName: ({formattedAmount, comment}) => `Solicitud de ${formattedAmount}${comment ? ` para ${comment}` : ''}`, error: { invalidSplit: 'La suma de las partes no equivale al monto total', other: 'Error inesperado, por favor inténtalo más tarde', From 42db583d22ff671f73422b60215b9b6df9e88c79 Mon Sep 17 00:00:00 2001 From: Vit Horacek Date: Mon, 15 May 2023 18:38:13 +0200 Subject: [PATCH 18/18] Fix lint --- src/components/MagicCodeInput.js | 6 +----- src/libs/ReportActionsUtils.js | 4 +++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/MagicCodeInput.js b/src/components/MagicCodeInput.js index a9505e829c1e..7248e069a7d7 100644 --- a/src/components/MagicCodeInput.js +++ b/src/components/MagicCodeInput.js @@ -267,11 +267,7 @@ function MagicCodeInput(props) { key={index} style={[styles.w15]} > - + {decomposeString(props.value)[index] || ''} diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index f6b4d7ba7ab7..558afca9904e 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -61,7 +61,9 @@ function getParentReportAction(report) { * @returns {Boolean} */ function isTransactionThread(parentReportAction) { - return parentReportAction && parentReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && lodashGet(parentReportAction, 'originalMessage.type') === CONST.IOU.REPORT_ACTION_TYPE.CREATE; + return ( + parentReportAction && parentReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && lodashGet(parentReportAction, 'originalMessage.type') === CONST.IOU.REPORT_ACTION_TYPE.CREATE + ); } /**