Skip to content

Commit

Permalink
Move the notification logic into its own method
Browse files Browse the repository at this point in the history
Use timestamp based solution to determine if we need to send a notification

set notificationPreference in report object

Use notification preference from Pusher for now

improve comment

Get rid of sendLocalNotification and just perform directly in Onyx.connect

change name to viewNewReportAction

Make a better diff

Fix tests
  • Loading branch information
marcaaron committed Jun 7, 2022
1 parent 284e0ba commit 0b0616d
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 87 deletions.
201 changes: 114 additions & 87 deletions src/libs/actions/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -591,42 +591,20 @@ function updateReportWithNewAction(
reportAction,
notificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS,
) {
const newMaxSequenceNumber = reportAction.sequenceNumber;
const isFromCurrentUser = reportAction.actorAccountID === currentUserAccountID;
const initialLastReadSequenceNumber = lastReadSequenceNumbers[reportID] || 0;
const messageText = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.RENAMED
? lodashGet(reportAction, 'originalMessage.html', '')
: lodashGet(reportAction, ['message', 0, 'text'], '');

// When handling an action from the current users we can assume that their
// last read actionID has been updated in the server but not necessarily reflected
// locally so we must first update it and then calculate the unread (which should be 0)
if (isFromCurrentUser) {
setLocalLastRead(reportID, newMaxSequenceNumber);
}

let messageText = lodashGet(reportAction, ['message', 0, 'text'], '');
if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.RENAMED) {
messageText = lodashGet(reportAction, 'originalMessage.html', '');
}

// 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
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,

// Use updated lastReadSequenceNumber, value may have been modified by setLocalLastRead
// Here deletedComments count does not include the new action being added. We can safely assume that newly received action is not deleted.
unreadActionCount: newMaxSequenceNumber - (lastReadSequenceNumbers[reportID] || 0) - ReportActions.getDeletedCommentsCount(reportID, lastReadSequenceNumbers[reportID] || 0),
maxSequenceNumber: reportAction.sequenceNumber,
notificationPreference,
lastMessageTimestamp: reportAction.timestamp,
lastMessageText: ReportUtils.formatReportLastMessageText(messageText),
lastActorEmail: reportAction.actorEmail,
};

// If the report action from pusher is a higher sequence number than we know about (meaning it has come from
// a chat participant in another application), then the last message text and author needs to be updated as well
if (newMaxSequenceNumber > initialLastReadSequenceNumber) {
updatedReportObject.lastMessageTimestamp = reportAction.timestamp;
updatedReportObject.lastMessageText = ReportUtils.formatReportLastMessageText(messageText);
updatedReportObject.lastActorEmail = reportAction.actorEmail;
}

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

const reportActionsToMerge = {};
Expand All @@ -644,63 +622,6 @@ function updateReportWithNewAction(
};

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

// If chat report receives an action with IOU and we have an IOUReportID, update IOU object
if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && reportAction.originalMessage.IOUReportID) {
const iouReportID = reportAction.originalMessage.IOUReportID;

// If the IOUDetails object exists we are in the Send Money flow, and we should not fetch and update the chatReport
// as this would overwrite any existing IOUs. For all other cases we must update the chatReport with the iouReportID as
// if we don't, new IOUs would not be displayed and paid IOUs would still show as unpaid.
if (reportAction.originalMessage.IOUDetails === undefined) {
fetchIOUReportByIDAndUpdateChatReport(iouReportID, reportID);
}
}

if (!ActiveClientManager.isClientTheLeader()) {
Log.info('[LOCAL_NOTIFICATION] Skipping notification because this client is not the leader');
return;
}

// We don't want to send a local notification if the user preference is daily or mute
if (notificationPreference === 'mute' || notificationPreference === 'daily') {
// eslint-disable-next-line max-len
Log.info(`[LOCAL_NOTIFICATION] No notification because user preference is to be notified: ${notificationPreference}`);
return;
}

// If this comment is from the current user we don't want to parrot whatever they wrote back to them.
if (isFromCurrentUser) {
Log.info('[LOCAL_NOTIFICATION] No notification because comment is from the currently logged in user');
return;
}

// If we are currently viewing this report do not show a notification.
if (reportID === lastViewedReportID && Visibility.isVisible()) {
Log.info('[LOCAL_NOTIFICATION] No notification because it was a comment for the current report');
return;
}

// If the comment came from Concierge let's not show a notification since we already show one for expensify.com
if (lodashGet(reportAction, 'actorEmail') === CONST.EMAIL.CONCIERGE) {
return;
}

// When a new message comes in, if the New marker is not already set (newMarkerSequenceNumber === 0), set the
// marker above the incoming message.
if (lodashGet(allReports, [reportID, 'newMarkerSequenceNumber'], 0) === 0
&& updatedReportObject.unreadActionCount > 0) {
const oldestUnreadSeq = (updatedReportObject.maxSequenceNumber - updatedReportObject.unreadActionCount) + 1;
setNewMarkerPosition(reportID, oldestUnreadSeq);
}
Log.info('[LOCAL_NOTIFICATION] Creating notification');
LocalNotification.showCommentNotification({
reportAction,
onClick: () => {
// Navigate to this report onClick
Navigation.navigate(ROUTES.getReportRoute(reportID));
},
});
}

/**
Expand Down Expand Up @@ -1543,6 +1464,112 @@ function renameReport(reportID, reportName) {
.finally(() => Onyx.set(ONYXKEYS.IS_LOADING_RENAME_POLICY_ROOM, false));
}

/**
* @param {Number} reportID
* @param {Object} action
*/
function viewNewReportAction(reportID, action) {
const newMaxSequenceNumber = action.sequenceNumber;
const isFromCurrentUser = action.actorAccountID === currentUserAccountID;

// When handling an action from the current users we can assume that their
// last read actionID has been updated in the server but not necessarily reflected
// locally so we must first update it and then calculate the unread (which should be 0)
if (isFromCurrentUser) {
setLocalLastRead(reportID, newMaxSequenceNumber);
}

const updatedReportObject = {
// Use updated lastReadSequenceNumber, value may have been modified by setLocalLastRead
// Here deletedComments count does not include the new action being added. We can safely assume that newly received action is not deleted.
unreadActionCount: newMaxSequenceNumber - (lastReadSequenceNumbers[reportID] || 0) - ReportActions.getDeletedCommentsCount(reportID, lastReadSequenceNumbers[reportID] || 0),
};

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

// If chat report receives an action with IOU and we have an IOUReportID, update IOU object
if (action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && action.originalMessage.IOUReportID) {
const iouReportID = action.originalMessage.IOUReportID;

// If the IOUDetails object exists we are in the Send Money flow, and we should not fetch and update the chatReport
// as this would overwrite any existing IOUs. For all other cases we must update the chatReport with the iouReportID as
// if we don't, new IOUs would not be displayed and paid IOUs would still show as unpaid.
if (action.originalMessage.IOUDetails === undefined) {
fetchIOUReportByIDAndUpdateChatReport(iouReportID, reportID);
}
}

const notificationPreference = lodashGet(allReports, [reportID, 'notificationPreference'], CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS);
if (!ActiveClientManager.isClientTheLeader()) {
Log.info('[LOCAL_NOTIFICATION] Skipping notification because this client is not the leader');
return;
}

// We don't want to send a local notification if the user preference is daily or mute
if (notificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS || notificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.DAILY) {
Log.info(`[LOCAL_NOTIFICATION] No notification because user preference is to be notified: ${notificationPreference}`);
return;
}

// If this comment is from the current user we don't want to parrot whatever they wrote back to them.
if (isFromCurrentUser) {
Log.info('[LOCAL_NOTIFICATION] No notification because comment is from the currently logged in user');
return;
}

// If we are currently viewing this report do not show a notification.
if (reportID === lastViewedReportID && Visibility.isVisible()) {
Log.info('[LOCAL_NOTIFICATION] No notification because it was a comment for the current report');
return;
}

// If the comment came from Concierge let's not show a notification since we already show one for expensify.com
if (lodashGet(action, 'actorEmail') === CONST.EMAIL.CONCIERGE) {
return;
}

// When a new message comes in, if the New marker is not already set (newMarkerSequenceNumber === 0), set the marker above the incoming message.
const report = lodashGet(allReports, 'reportID', {});
if (lodashGet(report, 'newMarkerSequenceNumber', 0) === 0 && report.unreadActionCount > 0) {
const oldestUnreadSeq = (report.maxSequenceNumber - report.unreadActionCount) + 1;
setNewMarkerPosition(reportID, oldestUnreadSeq);
}

Log.info('[LOCAL_NOTIFICATION] Creating notification');
LocalNotification.showCommentNotification({
reportAction: action,
onClick: () => {
// Navigate to this report onClick
Navigation.navigate(ROUTES.getReportRoute(reportID));
},
});
}

Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
initWithStoredValues: false,
callback: (actions, key) => {
// reportID can be derived from the Onyx key
const reportID = parseInt(key.split('_')[1], 10);
if (!reportID) {
return;
}

_.each(actions, (action) => {
if (!action.timestamp) {
return;
}

// If we are past the deadline to notify for this comment don't do it
if (moment.utc(action.timestamp * 1000).isBefore(moment.utc().subtract(10, 'seconds'))) {
return;
}

viewNewReportAction(reportID, action);
});
},
});

export {
fetchAllReports,
fetchActions,
Expand Down
2 changes: 2 additions & 0 deletions tests/actions/ReportTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ describe('actions/Report', () => {
});

it('should store a new report action in Onyx when reportComment event is handled via Pusher', () => {
global.fetch = TestHelper.getGlobalFetchMock();

const TEST_USER_ACCOUNT_ID = 1;
const TEST_USER_LOGIN = 'test@test.com';
const REPORT_ID = 1;
Expand Down

0 comments on commit 0b0616d

Please sign in to comment.