Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate lastMessageTimestamp to lastActionCreated #12761

Merged
merged 11 commits into from
Nov 25, 2022
10 changes: 7 additions & 3 deletions src/libs/DateUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,14 @@ function getMicroseconds() {

/**
* Returns the current time in milliseconds in the format expected by the database
*
* @param {String|Number} [timestamp]
*
* @returns {String}
*/
function currentDBTime() {
return new Date().toISOString()
function getDBTime(timestamp = '') {
const datetime = timestamp ? new Date(timestamp) : new Date();
return datetime.toISOString()
.replace('T', ' ')
.replace('Z', '');
}
Expand All @@ -183,7 +187,7 @@ const DateUtils = {
canUpdateTimezone,
setTimezoneUpdated,
getMicroseconds,
currentDBTime,
getDBTime,
};

export default DateUtils;
2 changes: 1 addition & 1 deletion src/libs/OptionsListUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ function getOptions(reports, personalDetails, {
return -Infinity;
}

return report.lastMessageTimestamp;
return report.lastActionCreated;
});
orderedReports.reverse();

Expand Down
8 changes: 4 additions & 4 deletions src/libs/ReportUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,7 @@ function buildOptimisticReportAction(sequenceNumber, text, file) {
sequenceNumber,
clientID: NumberUtils.generateReportActionClientID(),
avatar: lodashGet(allPersonalDetails, [currentUserEmail, 'avatar'], getDefaultAvatar(currentUserEmail)),
created: DateUtils.currentDBTime(),
created: DateUtils.getDBTime(),
message: [
{
type: CONST.REPORT.MESSAGE.TYPE.COMMENT,
Expand Down Expand Up @@ -795,7 +795,7 @@ function buildOptimisticIOUReportAction(sequenceNumber, type, amount, currency,
reportActionID: NumberUtils.rand64(),
sequenceNumber,
shouldShow: true,
created: DateUtils.currentDBTime(),
created: DateUtils.getDBTime(),
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
};
}
Expand Down Expand Up @@ -834,7 +834,7 @@ function buildOptimisticChatReport(
lastMessageHtml: '',
lastMessageText: null,
lastReadSequenceNumber: 0,
lastMessageTimestamp: 0,
lastActionCreated: '',
lastVisitedTimestamp: 0,
maxSequenceNumber: 0,
notificationPreference,
Expand Down Expand Up @@ -883,7 +883,7 @@ function buildOptimisticCreatedReportAction(ownerEmail) {
automatic: false,
sequenceNumber: 0,
avatar: lodashGet(allPersonalDetails, [currentUserEmail, 'avatar'], getDefaultAvatar(currentUserEmail)),
created: DateUtils.currentDBTime(),
created: DateUtils.getDBTime(),
shouldShow: true,
},
};
Expand Down
8 changes: 4 additions & 4 deletions src/libs/SidebarUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,10 @@ function getOrderedReportIDs(reportIDFromRoute) {
// 2. Outstanding IOUs - Always sorted by iouReportAmount with the largest amounts at the top of the group
// 3. Drafts - Always sorted by reportDisplayName
// 4. Non-archived reports
// - Sorted by lastMessageTimestamp in default (most recent) view mode
// - Sorted by lastActionCreated in default (most recent) view mode
// - Sorted by reportDisplayName in GSD (focus) view mode
// 5. Archived reports
// - Sorted by lastMessageTimestamp in default (most recent) view mode
// - Sorted by lastActionCreated in default (most recent) view mode
// - Sorted by reportDisplayName in GSD (focus) view mode
let pinnedReports = [];
let outstandingIOUReports = [];
Expand Down Expand Up @@ -153,8 +153,8 @@ function getOrderedReportIDs(reportIDFromRoute) {
pinnedReports = _.sortBy(pinnedReports, report => report.displayName.toLowerCase());
outstandingIOUReports = _.sortBy(outstandingIOUReports, 'iouReportAmount').reverse();
draftReports = _.sortBy(draftReports, report => report.displayName.toLowerCase());
nonArchivedReports = _.sortBy(nonArchivedReports, report => (isInDefaultMode ? report.lastMessageTimestamp : report.displayName.toLowerCase()));
archivedReports = _.sortBy(archivedReports, report => (isInDefaultMode ? report.lastMessageTimestamp : report.displayName.toLowerCase()));
nonArchivedReports = _.sortBy(nonArchivedReports, report => (isInDefaultMode ? report.lastActionCreated : report.displayName.toLowerCase()));
archivedReports = _.sortBy(archivedReports, report => (isInDefaultMode ? report.lastActionCreated : report.displayName.toLowerCase()));

// For archived and non-archived reports, ensure that most recent reports are at the top by reversing the order of the arrays because underscore will only sort them in ascending order
if (isInDefaultMode) {
Expand Down
7 changes: 3 additions & 4 deletions src/libs/actions/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,7 @@ function getParticipantEmailsFromReport({sharedReportList, reportNameValuePairs,
* @returns {Object}
*/
function getSimplifiedReportObject(report) {
const createTimestamp = lodashGet(report, 'lastActionCreated', 0);
const lastMessageTimestamp = moment.utc(createTimestamp).unix();
const lastActionCreated = lodashGet(report, 'lastActionCreated', 0);
const lastActionMessage = lodashGet(report, ['lastActionMessage', 'html'], '');
const isLastMessageAttachment = new RegExp(`<img|a\\s[^>]*${CONST.ATTACHMENT_SOURCE_ATTRIBUTE}\\s*=\\s*"[^"]*"[^>]*>`, 'gi').test(lastActionMessage);
const chatType = lodashGet(report, ['reportNameValuePairs', 'chatType'], '');
Expand Down Expand Up @@ -135,7 +134,7 @@ function getSimplifiedReportObject(report) {
'timestamp',
], 0),
lastReadSequenceNumber,
lastMessageTimestamp,
lastActionCreated,
lastMessageText: isLastMessageAttachment ? '[Attachment]' : lastMessageText,
lastActorEmail,
notificationPreference,
Expand Down Expand Up @@ -414,7 +413,7 @@ function addActions(reportID, text = '', file) {
// Update the report in Onyx to have the new sequence number
const optimisticReport = {
maxSequenceNumber: newSequenceNumber,
lastMessageTimestamp: Date.now(),
lastActionCreated: DateUtils.getDBTime(),
lastMessageText: ReportUtils.formatReportLastMessageText(lastAction.message[0].text),
lastActorEmail: currentUserEmail,
lastReadSequenceNumber: newSequenceNumber,
Expand Down
46 changes: 46 additions & 0 deletions src/libs/migrations/AddLastActionCreated.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import _ from 'underscore';
import Onyx from 'react-native-onyx';
import Log from '../Log';
import ONYXKEYS from '../../ONYXKEYS';

/**
* This migration adds lastActionCreated to all reports in Onyx, using the value of lastMessageTimestamp
*
* @returns {Promise}
*/
export default function () {
return new Promise((resolve) => {
const connectionID = Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallbacks: true,
callback: (allReports) => {
Onyx.disconnect(connectionID);
const reportsToUpdate = {};
_.each(allReports, (report, key) => {
if (_.has(report, 'lastActionCreated')) {
return;
}

if (!_.has(report, 'lastMessageTimestamp')) {
return;
}

reportsToUpdate[key] = report;
reportsToUpdate[key].lastActionCreated = new Date(report.lastMessageTimestamp)
.toISOString()
.replace('T', ' ')
.replace('Z', '');
});

if (_.isEmpty(reportsToUpdate)) {
Log.info('[Migrate Onyx] Skipped migration AddLastActionCreated');
} else {
Log.info(`[Migrate Onyx] Adding lastActionCreated field to ${_.keys(reportsToUpdate).length} reports`);
// eslint-disable-next-line rulesdir/prefer-actions-set-data
Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, reportsToUpdate);
}
},
});
return resolve();
});
}
2 changes: 1 addition & 1 deletion src/pages/home/sidebar/SidebarLinks.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ const reportSelector = report => report && ({
maxSequenceNumber: report.maxSequenceNumber,
lastReadSequenceNumber: report.lastReadSequenceNumber,
lastMessageText: report.lastMessageText,
lastMessageTimestamp: report.lastMessageTimestamp,
lastActionCreated: report.lastActionCreated,
iouReportID: report.iouReportID,
hasOutstandingIOU: report.hasOutstandingIOU,
statusNum: report.statusNum,
Expand Down
2 changes: 1 addition & 1 deletion src/pages/reportPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default PropTypes.shape({
lastMessageText: PropTypes.string,

/** The time of the last message on the report */
lastMessageTimestamp: PropTypes.number,
lastActionCreated: PropTypes.string,

/** The sequence number of the last action read by the user */
lastReadSequenceNumber: PropTypes.number,
Expand Down
10 changes: 5 additions & 5 deletions tests/actions/ReportTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ describe('actions/Report', () => {
reportID: REPORT_ID,
maxSequenceNumber: 1,
notificationPreference: 'always',
lastMessageTimestamp: 0,
lastActionCreated: '2022-11-22 03:48:27.267',
lastMessageText: 'Testing a comment',
lastActorEmail: TEST_USER_LOGIN,
},
Expand Down Expand Up @@ -240,7 +240,7 @@ describe('actions/Report', () => {
reportID: REPORT_ID,
maxSequenceNumber: 1,
notificationPreference: 'always',
lastMessageTimestamp: 0,
lastActionCreated: '2022-11-22 03:48:27.267',
lastMessageText: 'Comment 1',
lastActorEmail: USER_2_LOGIN,
lastReadSequenceNumber: 0,
Expand All @@ -260,7 +260,7 @@ describe('actions/Report', () => {
person: [{type: 'TEXT', style: 'strong', text: 'Test User'}],
sequenceNumber: 1,
shouldShow: true,
created: DateUtils.currentDBTime(),
created: DateUtils.getDBTime(),
},
},
},
Expand Down Expand Up @@ -325,7 +325,7 @@ describe('actions/Report', () => {
avatar: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_3.png',
person: [{type: 'TEXT', style: 'strong', text: 'Test User'}],
shouldShow: true,
created: DateUtils.currentDBTime(),
created: DateUtils.getDBTime(),
reportActionID: 'derp',
};

Expand All @@ -338,7 +338,7 @@ describe('actions/Report', () => {
reportID: REPORT_ID,
maxSequenceNumber: 4,
notificationPreference: 'always',
lastMessageTimestamp: 0,
lastActionCreated: '2022-11-22 03:48:27.267',
lastMessageText: 'Current User Comment 3',
lastActorEmail: 'test@test.com',
lastReadSequenceNumber: 4,
Expand Down
7 changes: 4 additions & 3 deletions tests/ui/UnreadIndicatorsTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import * as NumberUtils from '../../src/libs/NumberUtils';
import LocalNotification from '../../src/libs/Notification/LocalNotification';
import * as Report from '../../src/libs/actions/Report';
import * as CollectionUtils from '../../src/libs/CollectionUtils';
import DateUtils from '../../src/libs/DateUtils';

jest.mock('../../src/libs/Notification/LocalNotification');

Expand Down Expand Up @@ -133,7 +134,7 @@ function signInAndGetAppWithUnreadChat() {
reportName: CONST.REPORT.DEFAULT_REPORT_NAME,
maxSequenceNumber: 9,
lastReadSequenceNumber: 1,
lastMessageTimestamp: MOMENT_TEN_MINUTES_AGO.utc().valueOf(),
lastActionCreated: DateUtils.getDBTime(MOMENT_TEN_MINUTES_AGO.utc().valueOf()),
lastMessageText: 'Test',
participants: [USER_B_EMAIL],
});
Expand Down Expand Up @@ -268,7 +269,7 @@ describe('Unread Indicators', () => {
reportName: CONST.REPORT.DEFAULT_REPORT_NAME,
maxSequenceNumber: 1,
lastReadSequenceNumber: 0,
lastMessageTimestamp: NEW_REPORT_CREATED_MOMENT.utc().valueOf(),
lastActionCreated: DateUtils.getDBTime(NEW_REPORT_CREATED_MOMENT.utc().valueOf()),
lastMessageText: 'Comment 1',
participants: [USER_C_EMAIL],
});
Expand Down Expand Up @@ -497,7 +498,7 @@ describe('Unread Indicators', () => {
delete lastReportAction[lastReportAction.clientID];
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, {
lastMessageText: lastReportAction.message[0].text,
lastMessageTimestamp: lastReportAction.timestamp,
lastActionCreated: DateUtils.getDBTime(lastReportAction.timestamp),
lastActorEmail: lastReportAction.actorEmail,
maxSequenceNumber: lastReportAction.sequenceNumber,
reportID: REPORT_ID,
Expand Down
26 changes: 23 additions & 3 deletions tests/unit/DateUtilsTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,28 @@ describe('DateUtils', () => {
expect(DateUtils.datetimeToRelative(LOCALE, anHourAgo)).toBe('an hour ago');
});

it('should return the date in the format expected by the database when calling currentDBTime', () => {
const currentDBTime = DateUtils.currentDBTime();
expect(currentDBTime).toBe(moment(currentDBTime).format('YYYY-MM-DD HH:mm:ss.SSS'));
describe('getDBTime', () => {
it('should return the date in the format expected by the database', () => {
const getDBTime = DateUtils.getDBTime();
expect(getDBTime).toBe(moment(getDBTime).format('YYYY-MM-DD HH:mm:ss.SSS'));
});

it('should represent the correct moment in utc when used with a standard datetime string', () => {
const timestamp = 'Mon Nov 21 2022 19:04:14 GMT-0800 (Pacific Standard Time)';
const getDBTime = DateUtils.getDBTime(timestamp);
expect(getDBTime).toBe('2022-11-22 03:04:14.000');
});

it('should represent the correct moment in time when used with an ISO string', () => {
const timestamp = '2022-11-22T03:08:04.326Z';
const getDBTime = DateUtils.getDBTime(timestamp);
expect(getDBTime).toBe('2022-11-22 03:08:04.326');
});

it('should represent the correct moment in time when used with a unix timestamp', () => {
const timestamp = 1669086850792;
const getDBTime = DateUtils.getDBTime(timestamp);
expect(getDBTime).toBe('2022-11-22 03:14:10.792');
});
});
});
Loading