Skip to content

Commit

Permalink
Merge pull request #9350 from Expensify/marcaaron-updateReportWithNew…
Browse files Browse the repository at this point in the history
…Action2

Replace Report_AddComment with AddComment (Part 2)
  • Loading branch information
PauloGasparSv authored Jul 5, 2022
2 parents 2005ab5 + 8761aa6 commit 258c268
Show file tree
Hide file tree
Showing 15 changed files with 161 additions and 135 deletions.
6 changes: 6 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,12 @@ const CONST = {

// There's a limit of 60k characters in Auth - https://github.com/Expensify/Auth/blob/198d59547f71fdee8121325e8bc9241fc9c3236a/auth/lib/Request.h#L28
MAX_COMMENT_LENGTH: 60000,

ONYX: {
METHOD: {
MERGE: 'merge',
},
},
};

export default CONST;
7 changes: 6 additions & 1 deletion src/components/InlineSystemMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import Icon from './Icon';

const propTypes = {
/** Error to display */
message: PropTypes.string.isRequired,
message: PropTypes.string,
};

const defaultProps = {
message: '',
};

const InlineSystemMessage = (props) => {
Expand All @@ -25,5 +29,6 @@ const InlineSystemMessage = (props) => {
};

InlineSystemMessage.propTypes = propTypes;
InlineSystemMessage.defaultProps = defaultProps;
InlineSystemMessage.displayName = 'InlineSystemMessage';
export default InlineSystemMessage;
35 changes: 27 additions & 8 deletions src/libs/DateUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,21 +115,38 @@ function startCurrentDateUpdater() {
});
}

/*
* Updates user's timezone, if their timezone is set to automatic and
* is different from current timezone
/**
* @returns {Object}
*/
function updateTimezone() {
function getCurrentTimezone() {
const currentTimezone = moment.tz.guess(true);
if (timezone.automatic && timezone.selected !== currentTimezone) {
PersonalDetails.setPersonalDetails({timezone: {...timezone, selected: currentTimezone}});
return {...timezone, selected: currentTimezone};
}
return timezone;
}

/*
* Returns a version of updateTimezone function throttled by 5 minutes
* Updates user's timezone, if their timezone is set to automatic and
* is different from current timezone
*/
function updateTimezone() {
PersonalDetails.setPersonalDetails({timezone: getCurrentTimezone()});
}

// Used to throttle updates to the timezone when necessary
let lastUpdatedTimezoneTime = moment();

/**
* @returns {Boolean}
*/
const throttledUpdateTimezone = _.throttle(() => updateTimezone(), 1000 * 60 * 5);
function canUpdateTimezone() {
return lastUpdatedTimezoneTime.isBefore(moment().subtract(5, 'minutes'));
}

function setTimezoneUpdated() {
lastUpdatedTimezoneTime = moment();
}

/**
* @namespace DateUtils
Expand All @@ -139,8 +156,10 @@ const DateUtils = {
timestampToDateTime,
startCurrentDateUpdater,
updateTimezone,
throttledUpdateTimezone,
getLocalMomentFromTimestamp,
getCurrentTimezone,
canUpdateTimezone,
setTimezoneUpdated,
};

export default DateUtils;
155 changes: 64 additions & 91 deletions src/libs/actions/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import * as Pusher from '../Pusher/pusher';
import LocalNotification from '../Notification/LocalNotification';
import PushNotification from '../Notification/PushNotification';
import * as PersonalDetails from './PersonalDetails';
import * as User from './User';
import Navigation from '../Navigation/Navigation';
import * as ActiveClientManager from '../ActiveClientManager';
import Visibility from '../Visibility';
Expand All @@ -27,6 +26,7 @@ import * as ReportActions from './ReportActions';
import Growl from '../Growl';
import * as Localize from '../Localize';
import PusherUtils from '../PusherUtils';
import DateUtils from '../DateUtils';

let currentUserEmail;
let currentUserAccountID;
Expand Down Expand Up @@ -532,54 +532,6 @@ function updateReportActionMessage(reportID, sequenceNumber, message) {
});
}

/**
* Updates a report in the store with a new report action
*
* @param {Number} reportID
* @param {Object} reportAction
* @param {String} [notificationPreference] On what cadence the user would like to be notified
*/
function updateReportWithNewAction(
reportID,
reportAction,
notificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS,
) {
const messageHtml = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.RENAMED
? lodashGet(reportAction, 'originalMessage.html', '')
: lodashGet(reportAction, ['message', 0, 'html'], '');

const parser = new ExpensiMark();
const messageText = parser.htmlToText(messageHtml);

const updatedReportObject = {
// Always merge the reportID into Onyx. If the report doesn't exist in Onyx yet, then all the rest of the data will be filled out by handleReportChanged
reportID,
maxSequenceNumber: reportAction.sequenceNumber,
notificationPreference,
lastMessageTimestamp: reportAction.timestamp,
lastMessageText: ReportUtils.formatReportLastMessageText(messageText),
lastActorEmail: reportAction.actorEmail,
};

Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, updatedReportObject);

const reportActionsToMerge = {};
if (reportAction.clientID) {
// Remove the optimistic action from the report since we are about to replace it
// with the real one (which has the true sequenceNumber)
reportActionsToMerge[reportAction.clientID] = null;
}

// Add the action into Onyx
reportActionsToMerge[reportAction.sequenceNumber] = {
...reportAction,
isAttachment: ReportUtils.isReportMessageAttachment(lodashGet(reportAction, ['message', 0], {})),
loading: false,
};

Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, reportActionsToMerge);
}

/**
* Get the private pusher channel name for a Report.
*
Expand All @@ -604,13 +556,6 @@ function subscribeToUserEvents() {
return;
}

// Live-update a report's actions when a 'report comment' event is received.
PusherUtils.subscribeToPrivateUserChannelEvent(
Pusher.TYPE.REPORT_COMMENT,
currentUserAccountID,
pushJSON => updateReportWithNewAction(pushJSON.reportID, pushJSON.reportAction, pushJSON.notificationPreference),
);

// Live-update a report's actions when an 'edit comment' event is received.
PusherUtils.subscribeToPrivateUserChannelEvent(Pusher.TYPE.REPORT_COMMENT_EDIT,
currentUserAccountID,
Expand All @@ -621,9 +566,9 @@ function subscribeToUserEvents() {
* Setup reportComment push notification callbacks.
*/
function subscribeToReportCommentPushNotifications() {
PushNotification.onReceived(PushNotification.TYPE.REPORT_COMMENT, ({reportID, reportAction}) => {
PushNotification.onReceived(PushNotification.TYPE.REPORT_COMMENT, ({reportID, onyxData}) => {
Log.info('[Report] Handled event sent by Airship', false, {reportID});
updateReportWithNewAction(reportID, reportAction);
Onyx.update(onyxData);
});

// Open correct report when push notification is clicked
Expand Down Expand Up @@ -904,7 +849,7 @@ function fetchAllReports(
* @param {String} text
* @param {File} [file]
*/
function addAction(reportID, text, file) {
function addComment(reportID, text, file) {
// For comments shorter than 10k chars, convert the comment from MD into HTML because that's how it is stored in the database
// For longer comments, skip parsing and display plaintext for performance reasons. It takes over 40s to parse a 100k long string!!
const parser = new ExpensiMark();
Expand All @@ -922,12 +867,12 @@ function addAction(reportID, text, file) {
: parser.htmlToText(htmlForNewComment);

// Update the report in Onyx to have the new sequence number
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {
const optimisticReport = {
maxSequenceNumber: newSequenceNumber,
lastMessageTimestamp: moment().unix(),
lastMessageText: ReportUtils.formatReportLastMessageText(textForNewComment),
lastActorEmail: currentUserEmail,
});
};

// Generate a clientID so we can save the optimistic action to storage with the clientID as key. Later, we will
// remove the optimistic action when we add the real action created in the server. We do this because it's not
Expand All @@ -943,12 +888,10 @@ function addAction(reportID, text, file) {
// Store the optimistic action ID on the report the comment was added to. It will be removed later when refetching
// report actions in order to clear out any stuck actions (i.e. actions where the client never received a Pusher
// event, for whatever reason, from the server with the new action data
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {
optimisticReportActionIDs: [...(optimisticReportActionIDs[reportID] || []), optimisticReportActionID],
});
optimisticReport.optimisticReportActionIDs = [...(optimisticReportActionIDs[reportID] || []), optimisticReportActionID];

// Optimistically add the new comment to the store before waiting to save it to the server
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, {
const optimisticReportActions = {
[optimisticReportActionID]: {
actionName: CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT,
actorEmail: currentUserEmail,
Expand Down Expand Up @@ -977,41 +920,70 @@ function addAction(reportID, text, file) {
isFirstItem: false,
isAttachment,
attachmentInfo,
loading: true,
isLoading: true,
shouldShow: true,
},
});
};

DeprecatedAPI.Report_AddComment({
const parameters = {
reportID,
file,
reportComment: commentText,
clientID: optimisticReportActionID,
persist: true,
})
.then((response) => {
if (response.jsonCode === 408) {
Growl.error(Localize.translateLocal('reportActionCompose.fileUploadFailed'));
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, {
[optimisticReportActionID]: null,
});
console.error(response.message);
return;
}

if (response.jsonCode === 666 && reportID === conciergeChatReportID) {
Growl.error(Localize.translateLocal('reportActionCompose.blockedFromConcierge'));
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, {
[optimisticReportActionID]: null,
});
};

// The fact that the API is returning this error means the BLOCKED_FROM_CONCIERGE nvp in the user details has changed since the last time we checked, so let's update
User.getUserDetails();
return;
}
const optimisticData = [
{
onyxMethod: CONST.ONYX.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
value: optimisticReport,
},
{
onyxMethod: CONST.ONYX.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
value: optimisticReportActions,
},
];

updateReportWithNewAction(reportID, response.reportAction);
// Update the timezone if it's been 5 minutes from the last time the user added a comment
if (DateUtils.canUpdateTimezone()) {
const timezone = DateUtils.getCurrentTimezone();
parameters.timezone = JSON.stringify(timezone);
optimisticData.push({
onyxMethod: CONST.ONYX.METHOD.MERGE,
key: ONYXKEYS.MY_PERSONAL_DETAILS,
value: {timezone},
});
optimisticData.push({
onyxMethod: CONST.ONYX.METHOD.MERGE,
key: ONYXKEYS.PERSONAL_DETAILS,
value: {[currentUserEmail]: timezone},
});
DateUtils.setTimezoneUpdated();
}

API.write(isAttachment ? 'AddAttachment' : 'AddComment', parameters, {
optimisticData,
failureData: [
{
onyxMethod: CONST.ONYX.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
value: {
[optimisticReportActionID]: {
isLoading: false,
},
},
},
],
});
}

/**
* @param {Number} reportID
* @param {Object} file
*/
function addAttachment(reportID, file) {
addComment(reportID, '', file);
}

/**
Expand Down Expand Up @@ -1525,7 +1497,8 @@ export {
fetchChatReportsByIDs,
fetchIOUReportByID,
fetchIOUReportByIDAndUpdateChatReport,
addAction,
addComment,
addAttachment,
updateLastReadActionID,
updateNotificationPreference,
setNewMarkerPosition,
Expand Down
2 changes: 1 addition & 1 deletion src/libs/actions/ReportActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Onyx.connect({
const reportID = CollectionUtils.extractCollectionItemID(key);
const actionsArray = _.toArray(actions);
reportActions[reportID] = actionsArray;
const mostRecentNonLoadingActionIndex = _.findLastIndex(actionsArray, action => !action.loading);
const mostRecentNonLoadingActionIndex = _.findLastIndex(actionsArray, action => !action.isLoading);
const mostRecentAction = actionsArray[mostRecentNonLoadingActionIndex];
if (!mostRecentAction || _.isUndefined(mostRecentAction.sequenceNumber)) {
return;
Expand Down
2 changes: 1 addition & 1 deletion src/pages/home/ReportScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class ReportScreen extends React.Component {
* @param {String} text
*/
onSubmitComment(text) {
Report.addAction(getReportID(this.props.route), text);
Report.addComment(getReportID(this.props.route), text);
}

/**
Expand Down
5 changes: 1 addition & 4 deletions src/pages/home/report/ReportActionCompose.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import ReportActionComposeFocusManager from '../../../libs/ReportActionComposeFo
import participantPropTypes from '../../../components/participantPropTypes';
import ParticipantLocalTime from './ParticipantLocalTime';
import {withPersonalDetails} from '../../../components/OnyxProvider';
import DateUtils from '../../../libs/DateUtils';
import * as User from '../../../libs/actions/User';
import Tooltip from '../../../components/Tooltip';
import EmojiPickerButton from '../../../components/EmojiPicker/EmojiPickerButton';
Expand Down Expand Up @@ -446,8 +445,6 @@ class ReportActionCompose extends React.Component {
return;
}

DateUtils.throttledUpdateTimezone();

this.props.onSubmit(trimmedComment);
this.updateComment('');
this.setTextInputShouldClear(true);
Expand Down Expand Up @@ -496,7 +493,7 @@ class ReportActionCompose extends React.Component {
headerTitle={this.props.translate('reportActionCompose.sendAttachment')}
onConfirm={(file) => {
this.submitForm();
Report.addAction(this.props.reportID, '', file);
Report.addAttachment(this.props.reportID, file);
this.setTextInputShouldClear(false);
}}
>
Expand Down
Loading

0 comments on commit 258c268

Please sign in to comment.