diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index cbb6e0af27a3..0d1eceb4d711 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -8,7 +8,6 @@ import * as StyleUtils from '../../../styles/StyleUtils'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; import CONST from '../../../CONST'; import compose from '../../compose'; -import * as Report from '../../actions/Report'; import * as PersonalDetails from '../../actions/PersonalDetails'; import * as Pusher from '../../Pusher/pusher'; import PusherConnectionManager from '../../PusherConnectionManager'; @@ -110,7 +109,6 @@ class AuthScreens extends React.Component { cluster: CONFIG.PUSHER.CLUSTER, authEndpoint: `${CONFIG.EXPENSIFY.URL_API_ROOT}api?command=AuthenticatePusher`, }).then(() => { - Report.subscribeToUserEvents(); User.subscribeToUserEvents(); Policy.subscribeToPolicyEvents(); }); diff --git a/src/libs/Pusher/EventType.js b/src/libs/Pusher/EventType.js index 97fa8cc9246b..79e7fb0c5137 100644 --- a/src/libs/Pusher/EventType.js +++ b/src/libs/Pusher/EventType.js @@ -4,7 +4,6 @@ */ export default { REPORT_COMMENT: 'reportComment', - REPORT_COMMENT_EDIT: 'reportCommentEdit', PREFERRED_LOCALE: 'preferredLocale', EXPENSIFY_CARD_UPDATE: 'expensifyCardUpdate', SCREEN_SHARE_REQUEST: 'screenshareRequest', diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 5cef373dd1ad..5146eeb8e986 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -99,7 +99,8 @@ function canEditReportAction(reportAction) { return reportAction.actorEmail === sessionEmail && reportAction.reportActionID && reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT - && !isReportMessageAttachment(lodashGet(reportAction, ['message', 0], {})); + && !isReportMessageAttachment(lodashGet(reportAction, ['message', 0], {})) + && reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; } /** @@ -113,7 +114,8 @@ function canEditReportAction(reportAction) { function canDeleteReportAction(reportAction) { return reportAction.actorEmail === sessionEmail && reportAction.reportActionID - && reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT; + && reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT + && reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; } /** diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 541bb2a51a72..397a6893231c 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -24,7 +24,6 @@ import * as ReportUtils from '../ReportUtils'; import * as ReportActions from './ReportActions'; import Growl from '../Growl'; import * as Localize from '../Localize'; -import PusherUtils from '../PusherUtils'; import DateUtils from '../DateUtils'; import * as ReportActionsUtils from '../ReportActionsUtils'; import * as NumberUtils from '../NumberUtils'; @@ -436,48 +435,6 @@ function getReportChannelName(reportID) { return `${CONST.PUSHER.PRIVATE_REPORT_CHANNEL_PREFIX}${reportID}${CONFIG.PUSHER.SUFFIX}`; } -/** - * Initialize our pusher subscriptions to listen for new report comments and pin toggles - */ -function subscribeToUserEvents() { - // If we don't have the user's accountID yet we can't subscribe so return early - if (!currentUserAccountID) { - return; - } - - const pusherChannelName = `${CONST.PUSHER.PRIVATE_USER_CHANNEL_PREFIX}${currentUserAccountID}${CONFIG.PUSHER.SUFFIX}`; - if (Pusher.isSubscribed(pusherChannelName) || Pusher.isAlreadySubscribing(pusherChannelName)) { - return; - } - - // Live-update a report's actions when an 'edit comment' event is received. - PusherUtils.subscribeToPrivateUserChannelEvent(Pusher.TYPE.REPORT_COMMENT_EDIT, - currentUserAccountID, - ({reportID, sequenceNumber, message}) => { - // We only want the active client to process these events once otherwise multiple tabs would decrement the 'unreadActionCount' - if (!ActiveClientManager.isClientTheLeader()) { - return; - } - - const actionsToMerge = {}; - actionsToMerge[sequenceNumber] = {message: [message]}; - - // If someone besides the current user deleted an action and the sequenceNumber is greater than our last read we will decrement the unread count - // we skip this for the current user because we should already have decremented the count optimistically when they deleted the comment. - const isFromCurrentUser = ReportActions.isFromCurrentUser(reportID, sequenceNumber, currentUserAccountID, actionsToMerge); - if (!message.html && !isFromCurrentUser && sequenceNumber > getLastReadSequenceNumber(reportID)) { - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { - unreadActionCount: Math.max(getUnreadActionCount(reportID) - 1, 0), - }); - } - - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { - lastMessageText: ReportActions.getLastVisibleMessageText(reportID, actionsToMerge), - }); - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, actionsToMerge); - }); -} - /** * Setup reportComment push notification callbacks. */ @@ -880,73 +837,6 @@ function addComment(reportID, text) { addActions(reportID, text); } -/** - * Deletes a comment from the report, basically sets it as empty string - * - * @param {Number} reportID - * @param {Object} reportAction - */ -function deleteReportComment(reportID, reportAction) { - // Optimistic Response - const sequenceNumber = reportAction.sequenceNumber; - const reportActionsToMerge = {}; - const oldMessage = {...reportAction.message}; - reportActionsToMerge[sequenceNumber] = { - ...reportAction, - message: [ - { - type: CONST.REPORT.MESSAGE.TYPE.COMMENT, - html: '', - text: '', - }, - ], - }; - - // If the comment we are deleting is more recent than our last read comment we will update the unread count - if (sequenceNumber > getLastReadSequenceNumber(reportID)) { - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { - unreadActionCount: Math.max(getUnreadActionCount(reportID) - 1, 0), - }); - } - - // Optimistically update the report and reportActions - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, reportActionsToMerge); - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { - lastMessageText: ReportActions.getLastVisibleMessageText(reportID, reportActionsToMerge), - }); - - // Try to delete the comment by calling the API - DeprecatedAPI.Report_EditComment({ - reportID, - reportActionID: reportAction.reportActionID, - reportComment: '', - sequenceNumber, - }) - .then((response) => { - if (response.jsonCode === 200) { - return; - } - - // Reverse Optimistic Response - reportActionsToMerge[sequenceNumber] = { - ...reportAction, - message: oldMessage, - }; - - if (sequenceNumber > getLastReadSequenceNumber(reportID)) { - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { - unreadActionCount: getUnreadActionCount(reportID) + 1, - }); - } - - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { - lastMessageText: ReportActions.getLastVisibleMessageText(reportID, reportActionsToMerge), - }); - - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, reportActionsToMerge); - }); -} - /** * Gets the latest page of report actions and updates the last read message * @@ -1216,6 +1106,88 @@ Onyx.connect({ callback: handleReportChanged, }); +/** + * Deletes a comment from the report, basically sets it as empty string + * + * @param {Number} reportID + * @param {Object} reportAction + */ +function deleteReportComment(reportID, reportAction) { + const sequenceNumber = reportAction.sequenceNumber; + const optimisticReportActions = { + [sequenceNumber]: { + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + }, + }; + + // If we are deleting the last visible message, let's find the previous visible one + // and update the lastMessageText in the chat preview. + const optimisticReport = { + lastMessageText: ReportActions.getLastVisibleMessageText(reportID, { + [sequenceNumber]: { + message: [{ + html: '', + text: '', + }], + }, + }), + }; + + // If the API call fails we must show the original message again, so we revert the message content back to how it was + // and and remove the pendingAction so the strike-through clears + const failureData = [ + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [sequenceNumber]: { + message: reportAction.message, + pendingAction: null, + }, + }, + }, + ]; + + const successData = [ + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [sequenceNumber]: { + pendingAction: null, + }, + }, + }, + ]; + + // If we are deleting an unread message that is greater than our last read we decrease the unreadActionCount + // since the message we are deleting is an unread + if (sequenceNumber > getLastReadSequenceNumber(reportID)) { + const unreadActionCount = getUnreadActionCount(reportID); + optimisticReport.unreadActionCount = Math.max(unreadActionCount - 1, 0); + } + + const optimisticData = [ + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: optimisticReportActions, + }, + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: optimisticReport, + }, + ]; + + const parameters = { + reportID, + sequenceNumber, + reportActionID: reportAction.reportActionID, + }; + API.write('DeleteComment', parameters, {optimisticData, successData, failureData}); +} + /** * Saves a new message for a comment. Marks the comment as edited, which will be reflected in the UI. * @@ -1241,32 +1213,59 @@ function editReportComment(reportID, originalReportAction, textForNewComment) { return; } - // Optimistically update the report action with the new message + // Optimistically update the reportAction 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 = parser.htmlToText(htmlForNewComment); - actionToMerge[sequenceNumber] = newReportAction; - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, actionToMerge); - - // Persist the updated report comment - DeprecatedAPI.Report_EditComment({ + const optimisticReportActions = { + [sequenceNumber]: { + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + message: [{ + isEdited: true, + html: htmlForNewComment, + text: textForNewComment, + }], + }, + }; + + const optimisticData = [ + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: optimisticReportActions, + }, + ]; + + const failureData = [ + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [sequenceNumber]: { + ...originalReportAction, + pendingAction: null, + }, + }, + }, + ]; + + const successData = [ + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [sequenceNumber]: { + pendingAction: null, + }, + }, + }, + ]; + + const parameters = { reportID, - reportActionID: originalReportAction.reportActionID, - reportComment: htmlForNewComment, sequenceNumber, - }) - .then((response) => { - if (response.jsonCode === 200) { - return; - } - - // If it fails, reset Onyx - actionToMerge[sequenceNumber] = originalReportAction; - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, actionToMerge); - }); + reportComment: htmlForNewComment, + reportActionID: originalReportAction.reportActionID, + }; + API.write('UpdateComment', parameters, {optimisticData, successData, failureData}); } /** @@ -1582,7 +1581,6 @@ export { updateNotificationPreference, setNewMarkerPosition, subscribeToReportTypingEvents, - subscribeToUserEvents, subscribeToReportCommentPushNotifications, unsubscribeFromReportChannel, saveReportComment, diff --git a/src/libs/actions/ReportActions.js b/src/libs/actions/ReportActions.js index 4cbb0a4a00c6..504e915ef76b 100644 --- a/src/libs/actions/ReportActions.js +++ b/src/libs/actions/ReportActions.js @@ -109,9 +109,22 @@ function deleteOptimisticReportAction(reportID, sequenceNumber) { }); } +/** + * @param {Number} reportID + * @param {String} sequenceNumber + */ +function clearReportActionErrors(reportID, sequenceNumber) { + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, { + [sequenceNumber]: { + errors: null, + }, + }); +} + export { getDeletedCommentsCount, getLastVisibleMessageText, + clearReportActionErrors, isFromCurrentUser, deleteOptimisticReportAction, }; diff --git a/src/libs/deprecatedAPI.js b/src/libs/deprecatedAPI.js index b056f08a2890..6576cbb6abff 100644 --- a/src/libs/deprecatedAPI.js +++ b/src/libs/deprecatedAPI.js @@ -260,19 +260,6 @@ function Report_GetHistory(parameters) { return Network.post(commandName, parameters); } -/** - * @param {Object} parameters - * @param {Number} parameters.reportID - * @param {Number} parameters.reportActionID - * @param {String} parameters.reportComment - * @returns {Promise} - */ -function Report_EditComment(parameters) { - const commandName = 'Report_EditComment'; - requireParameters(['reportID', 'reportActionID', 'reportComment'], parameters, commandName); - return Network.post(commandName, parameters); -} - /** * @param {Object} parameters * @param {String} parameters.email @@ -650,7 +637,6 @@ export { Policy_Employees_Merge, RejectTransaction, Report_GetHistory, - Report_EditComment, ResendValidateCode, SetNameValuePair, SetPassword, diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index f6d0a2c3153a..59475e15d4b1 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -188,7 +188,13 @@ class ReportActionItem extends Component { )} > ReportActions.deleteOptimisticReportAction(this.props.report.reportID, this.props.action.sequenceNumber)} + onClose={() => { + if (this.props.action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { + ReportActions.deleteOptimisticReportAction(this.props.report.reportID, this.props.action.sequenceNumber); + } else { + ReportActions.clearReportActionErrors(this.props.report.reportID, this.props.action.sequenceNumber); + } + }} pendingAction={this.props.action.pendingAction} errors={this.props.action.errors} errorRowStyles={[styles.ml10, styles.mr2]} diff --git a/src/pages/home/report/ReportActionItemMessage.js b/src/pages/home/report/ReportActionItemMessage.js index 18d4b2306807..9cfa1e5eb537 100644 --- a/src/pages/home/report/ReportActionItemMessage.js +++ b/src/pages/home/report/ReportActionItemMessage.js @@ -18,15 +18,25 @@ const propTypes = { /** Information about the network */ network: networkPropTypes.isRequired, + /** Additional styles to add after local styles. */ + style: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.object), + PropTypes.object, + ]), + /** localization props */ ...withLocalizePropTypes, }; +const defaultProps = { + style: [], +}; + const ReportActionItemMessage = (props) => { const isUnsent = props.network.isOffline && props.action.isLoading; return ( - + {_.map(_.compact(props.action.message), (fragment, index) => ( { }; ReportActionItemMessage.propTypes = propTypes; +ReportActionItemMessage.defaultProps = defaultProps; ReportActionItemMessage.displayName = 'ReportActionItemMessage'; export default compose(