From 968de5a987de09c6d6c34d1b4fe3fe079e94c2a3 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Mon, 9 Oct 2023 19:34:57 -0300 Subject: [PATCH 1/8] Fix read receipts not being created on the first time a user reads a room --- .../app/message-read-receipt/server/hooks/afterReadMessages.ts | 2 +- apps/meteor/ee/server/lib/message-read-receipt/ReadReceipt.js | 2 +- apps/meteor/server/models/raw/Messages.ts | 2 +- packages/model-typings/src/models/IMessagesModel.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/meteor/ee/app/message-read-receipt/server/hooks/afterReadMessages.ts b/apps/meteor/ee/app/message-read-receipt/server/hooks/afterReadMessages.ts index f96a9a773fec..293ab0a17373 100644 --- a/apps/meteor/ee/app/message-read-receipt/server/hooks/afterReadMessages.ts +++ b/apps/meteor/ee/app/message-read-receipt/server/hooks/afterReadMessages.ts @@ -15,7 +15,7 @@ callbacks.add( if (tmid) { await MessageReads.readThread(uid, tmid); - } else if (lastSeen) { + } else { await ReadReceipt.markMessagesAsRead(rid, uid, lastSeen); } }, diff --git a/apps/meteor/ee/server/lib/message-read-receipt/ReadReceipt.js b/apps/meteor/ee/server/lib/message-read-receipt/ReadReceipt.js index bb957b8f2b14..710c1a10d451 100644 --- a/apps/meteor/ee/server/lib/message-read-receipt/ReadReceipt.js +++ b/apps/meteor/ee/server/lib/message-read-receipt/ReadReceipt.js @@ -40,7 +40,7 @@ export const ReadReceipt = { const room = await Rooms.findOneById(roomId, { projection: { lm: 1 } }); // if users last seen is greater than room's last message, it means the user already have this room marked as read - if (!room || userLastSeen > room.lm) { + if (!room || (userLastSeen && userLastSeen > room.lm)) { return; } diff --git a/apps/meteor/server/models/raw/Messages.ts b/apps/meteor/server/models/raw/Messages.ts index 49930a8c92ea..8f35e5efe020 100644 --- a/apps/meteor/server/models/raw/Messages.ts +++ b/apps/meteor/server/models/raw/Messages.ts @@ -1536,7 +1536,7 @@ export class MessagesRaw extends BaseRaw implements IMessagesModel { ); } - findVisibleUnreadMessagesByRoomAndDate(rid: string, after: Date): FindCursor> { + findVisibleUnreadMessagesByRoomAndDate(rid: string, after?: Date): FindCursor> { const query = { unread: true, rid, diff --git a/packages/model-typings/src/models/IMessagesModel.ts b/packages/model-typings/src/models/IMessagesModel.ts index d681405850fa..99d0a9a0e380 100644 --- a/packages/model-typings/src/models/IMessagesModel.ts +++ b/packages/model-typings/src/models/IMessagesModel.ts @@ -258,7 +258,7 @@ export interface IMessagesModel extends IBaseModel { userId: string, after: Date, ): FindCursor>; - findVisibleUnreadMessagesByRoomAndDate(rid: string, after: Date): FindCursor>; + findVisibleUnreadMessagesByRoomAndDate(rid: string, after?: Date): FindCursor>; setAsReadById(_id: string): Promise; countThreads(): Promise; addThreadFollowerByThreadId(tmid: string, userId: string): Promise; From 56c9ca695109f6deb2847f4e6ee5b7d3cd5ef439 Mon Sep 17 00:00:00 2001 From: Heitor Tanoue Date: Mon, 9 Oct 2023 20:56:49 -0300 Subject: [PATCH 2/8] add tests --- .../meteor/tests/end-to-end/api/24-methods.js | 282 +++++++++++++++++- 1 file changed, 281 insertions(+), 1 deletion(-) diff --git a/apps/meteor/tests/end-to-end/api/24-methods.js b/apps/meteor/tests/end-to-end/api/24-methods.js index 899eb0db7d5f..9c39ce367764 100644 --- a/apps/meteor/tests/end-to-end/api/24-methods.js +++ b/apps/meteor/tests/end-to-end/api/24-methods.js @@ -1,10 +1,11 @@ import { expect } from 'chai'; import { after, before, beforeEach, describe, it } from 'mocha'; -import { getCredentials, request, methodCall, api, credentials } from '../../data/api-data.js'; +import { api, credentials, getCredentials, methodCall, request } from '../../data/api-data.js'; import { CI_MAX_ROOMS_PER_GUEST as maxRoomsPerGuest } from '../../data/constants'; import { updatePermission, updateSetting } from '../../data/permissions.helper'; import { createRoom } from '../../data/rooms.helper'; +import { password } from '../../data/user'; import { createUser, deleteUser, login } from '../../data/users.helper.js'; describe('Meteor.methods', function () { @@ -123,6 +124,285 @@ describe('Meteor.methods', function () { }); }); + describe('[@getReadReceipts]', () => { + let firstMessage = false; + let firstThreadMessage = false; + + let channelName = false; + let user, userCredentials, room; + + const roomName = `methods-test-channel-${ Date.now() }`; + before(async () => { + user = await createUser(); + userCredentials = await login(user.username, password); + room = (await createRoom({ type: 'p', name: roomName, members: [user.username] })).body.group; + }); + + before(async () => { + await updateSetting('Message_Read_Receipt_Enabled', true); + await updateSetting('Message_Read_Receipt_Store_Users', true); + }); + + after(async () => { + deleteUser(user); + }); + + before('send sample message', (done) => { + request + .post(api('chat.sendMessage')) + .set(credentials) + .send({ + message: { + text: 'Sample message', + rid: room._id, + }, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + firstMessage = res.body.message; + }) + .end(done); + }); + + before('send sample message into thread', (done) => { + request + .post(api('chat.sendMessage')) + .set(credentials) + .send({ + message: { + text: 'Second Sample message', + rid: room._id, + tmid: firstMessage._id, + }, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + firstThreadMessage = res.body.message; + }) + .end(done); + }); + + it('should fail if not logged in', (done) => { + request + .post(methodCall('getReadReceipts')) + .send({ + message: JSON.stringify({ + method: 'getReadReceipts', + params: [], + id: 'id', + msg: 'method', + }), + }) + .expect('Content-Type', 'application/json') + .expect(401) + .expect((res) => { + expect(res.body).to.have.property('status', 'error'); + expect(res.body).to.have.property('message'); + }) + .end(done); + }); + + it("should return the sender's read receipt for a message sent in the main room", (done) => { + request + .post(methodCall('getReadReceipts')) + .set(credentials) + .send({ + message: JSON.stringify({ + method: 'getReadReceipts', + params: [{ messageId: firstMessage._id }], + id: 'id', + msg: 'method', + }), + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.a.property('success', true); + expect(res.body).to.have.a.property('message').that.is.a('string'); + + const data = JSON.parse(res.body.message); + expect(data).to.have.a.property('result').that.is.an('array'); + expect(data.result.length).to.equal(1); + expect(data.result[0]).to.have.property('userId', credentials['X-User-Id']); + }) + .end(done); + }); + + it("should return the sender's read receipt for a message sent in a thread", (done) => { + request + .post(methodCall('getReadReceipts')) + .set(credentials) + .send({ + message: JSON.stringify({ + method: 'getReadReceipts', + params: [{ messageId: firstThreadMessage._id }], + id: 'id', + msg: 'method', + }), + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.a.property('success', true); + expect(res.body).to.have.a.property('message').that.is.a('string'); + + const data = JSON.parse(res.body.message); + expect(data).to.have.a.property('result').that.is.an('array'); + expect(data.result.length).to.equal(1); + expect(data.result[0]).to.have.property('userId', credentials['X-User-Id']); + }) + .end(done); + }); + + it("should read all main room's messages with the invited user", (done) => { + request + .post(api('subscriptions.read')) + .set(credentials) + .send({ + rid: room._id, + readThreads: true, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.a.property('success', true); + // expect(res.body).to.have.a.property('message').that.is.a('string'); + }) + .end(done); + }); + + it("should return the invited user's read receipt for a message sent in the main room", (done) => { + request + .post(methodCall('getReadReceipts')) + .set(credentials) + .send({ + message: JSON.stringify({ + method: 'getReadReceipts', + params: [{ messageId: firstMessage._id }], + id: 'id', + msg: 'method', + }), + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.a.property('success', true); + expect(res.body).to.have.a.property('message').that.is.a('string'); + + const data = JSON.parse(res.body.message); + console.log(data) + expect(data).to.have.a.property('result').that.is.an('array'); + expect(data.result.length).to.equal(2); + + const receiptsUserIds = [data.result[0].userId, data.result[1].userId]; + expect(receiptsUserIds).to.have.members([credentials['X-User-Id'], user._id]); + }) + .end(done); + }); + + it("should return only the sender's read receipt for a message sent in a thread after the main room is read", (done) => { + request + .post(methodCall('getReadReceipts')) + .set(credentials) + .send({ + message: JSON.stringify({ + method: 'getReadReceipts', + params: [{ messageId: firstThreadMessage._id }], + id: 'id', + msg: 'method', + }), + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.a.property('success', true); + expect(res.body).to.have.a.property('message').that.is.a('string'); + + const data = JSON.parse(res.body.message); + expect(data).to.have.a.property('result').that.is.an('array'); + expect(data.result.length).to.equal(1); + expect(data.result[0]).to.have.property('userId', credentials['X-User-Id']); + }) + .end(done); + }); + + it('should read thread messages with the invited user', (done) => { + request + .post(methodCall('getThreadMessages')) + .set(userCredentials) + .send({ + message: JSON.stringify({ + id: 'id', + msg: 'method', + method: 'getThreadMessages', + params: [ + { + tmid: firstMessage._id, + }, + ], + }), + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.a.property('success', true); + expect(res.body).to.have.a.property('message').that.is.a('string'); + + const data = JSON.parse(res.body.message); + expect(data).to.have.a.property('result').that.is.an('array'); + expect(data.result.length).to.equal(2); + }) + .end(done); + }); + + it("should return the invited user's read receipt for a message sent in a thread after it is read", (done) => { + request + .post(methodCall('getReadReceipts')) + .set(credentials) + .send({ + message: JSON.stringify({ + method: 'getReadReceipts', + params: [{ messageId: firstThreadMessage._id }], + id: 'id', + msg: 'method', + }), + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.a.property('success', true); + expect(res.body).to.have.a.property('message').that.is.a('string'); + + const data = JSON.parse(res.body.message); + expect(data).to.have.a.property('result').that.is.an('array'); + expect(data.result.length).to.equal(2); + + const receiptsUserIds = [data.result[0].userId, data.result[1].userId]; + expect(receiptsUserIds).to.have.members([credentials['X-User-Id'], user._id]); + }) + .end(done); + }); + + it('should mark the thread as read', (done) => { + request + .post(methodCall('readThreads')) + .set(userCredentials) + .send(firstThreadMessage._id) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + console.log(res.body) + expect(res.body).to.have.a.property('success', true); + expect(res.body).to.have.a.property('message').that.is.a('string'); + }) + .end(done); + }); + }); + describe('[@getMessages]', () => { let rid = false; let firstMessage = false; From 7257ce2eb287617e8d1509946c811e91e3bc8f4a Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Tue, 10 Oct 2023 14:22:37 -0300 Subject: [PATCH 3/8] Mark as read only messages sent after subscription's creation --- .../message-read-receipt/server/hooks/afterReadMessages.ts | 2 +- .../meteor/ee/server/lib/message-read-receipt/ReadReceipt.js | 2 +- apps/meteor/server/lib/readMessages.ts | 5 +++-- apps/meteor/server/models/raw/Messages.ts | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/meteor/ee/app/message-read-receipt/server/hooks/afterReadMessages.ts b/apps/meteor/ee/app/message-read-receipt/server/hooks/afterReadMessages.ts index 293ab0a17373..f96a9a773fec 100644 --- a/apps/meteor/ee/app/message-read-receipt/server/hooks/afterReadMessages.ts +++ b/apps/meteor/ee/app/message-read-receipt/server/hooks/afterReadMessages.ts @@ -15,7 +15,7 @@ callbacks.add( if (tmid) { await MessageReads.readThread(uid, tmid); - } else { + } else if (lastSeen) { await ReadReceipt.markMessagesAsRead(rid, uid, lastSeen); } }, diff --git a/apps/meteor/ee/server/lib/message-read-receipt/ReadReceipt.js b/apps/meteor/ee/server/lib/message-read-receipt/ReadReceipt.js index 710c1a10d451..bb957b8f2b14 100644 --- a/apps/meteor/ee/server/lib/message-read-receipt/ReadReceipt.js +++ b/apps/meteor/ee/server/lib/message-read-receipt/ReadReceipt.js @@ -40,7 +40,7 @@ export const ReadReceipt = { const room = await Rooms.findOneById(roomId, { projection: { lm: 1 } }); // if users last seen is greater than room's last message, it means the user already have this room marked as read - if (!room || (userLastSeen && userLastSeen > room.lm)) { + if (!room || userLastSeen > room.lm) { return; } diff --git a/apps/meteor/server/lib/readMessages.ts b/apps/meteor/server/lib/readMessages.ts index 00bf04bd3449..09aea69ca65a 100644 --- a/apps/meteor/server/lib/readMessages.ts +++ b/apps/meteor/server/lib/readMessages.ts @@ -6,7 +6,7 @@ import { callbacks } from '../../lib/callbacks'; export async function readMessages(rid: IRoom['_id'], uid: IUser['_id'], readThreads: boolean): Promise { await callbacks.run('beforeReadMessages', rid, uid); - const projection = { ls: 1, tunread: 1, alert: 1 }; + const projection = { ls: 1, tunread: 1, alert: 1, _updatedAt: 1 }; const sub = await Subscriptions.findOneByRoomIdAndUserId(rid, uid, { projection }); if (!sub) { throw new Error('error-invalid-subscription'); @@ -19,5 +19,6 @@ export async function readMessages(rid: IRoom['_id'], uid: IUser['_id'], readThr await NotificationQueue.clearQueueByUserId(uid); - callbacks.runAsync('afterReadMessages', rid, { uid, lastSeen: sub.ls }); + const lastSeen = sub.ls || sub._updatedAt; + callbacks.runAsync('afterReadMessages', rid, { uid, lastSeen }); } diff --git a/apps/meteor/server/models/raw/Messages.ts b/apps/meteor/server/models/raw/Messages.ts index 8f35e5efe020..49930a8c92ea 100644 --- a/apps/meteor/server/models/raw/Messages.ts +++ b/apps/meteor/server/models/raw/Messages.ts @@ -1536,7 +1536,7 @@ export class MessagesRaw extends BaseRaw implements IMessagesModel { ); } - findVisibleUnreadMessagesByRoomAndDate(rid: string, after?: Date): FindCursor> { + findVisibleUnreadMessagesByRoomAndDate(rid: string, after: Date): FindCursor> { const query = { unread: true, rid, From 6db185b7b79b1788ddcca213689bb74af7991575 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Tue, 10 Oct 2023 15:47:58 -0300 Subject: [PATCH 4/8] Fix lint --- .../meteor/tests/end-to-end/api/24-methods.js | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/24-methods.js b/apps/meteor/tests/end-to-end/api/24-methods.js index 9c39ce367764..a13a2e390f28 100644 --- a/apps/meteor/tests/end-to-end/api/24-methods.js +++ b/apps/meteor/tests/end-to-end/api/24-methods.js @@ -124,18 +124,19 @@ describe('Meteor.methods', function () { }); }); - describe('[@getReadReceipts]', () => { + describe('[@getReadReceipts]', () => { let firstMessage = false; let firstThreadMessage = false; - let channelName = false; - let user, userCredentials, room; + let user; + let userCredentials; + let room; - const roomName = `methods-test-channel-${ Date.now() }`; + const roomName = `methods-test-channel-${Date.now()}`; before(async () => { - user = await createUser(); - userCredentials = await login(user.username, password); - room = (await createRoom({ type: 'p', name: roomName, members: [user.username] })).body.group; + user = await createUser(); + userCredentials = await login(user.username, password); + room = (await createRoom({ type: 'p', name: roomName, members: [user.username] })).body.group; }); before(async () => { @@ -159,7 +160,7 @@ describe('Meteor.methods', function () { }) .expect('Content-Type', 'application/json') .expect(200) - .expect((res) => { + .expect((res) => { expect(res.body).to.have.property('success', true); firstMessage = res.body.message; }) @@ -220,11 +221,11 @@ describe('Meteor.methods', function () { }) .expect('Content-Type', 'application/json') .expect(200) - .expect((res) => { + .expect((res) => { expect(res.body).to.have.a.property('success', true); expect(res.body).to.have.a.property('message').that.is.a('string'); - const data = JSON.parse(res.body.message); + const data = JSON.parse(res.body.message); expect(data).to.have.a.property('result').that.is.an('array'); expect(data.result.length).to.equal(1); expect(data.result[0]).to.have.property('userId', credentials['X-User-Id']); @@ -263,8 +264,8 @@ describe('Meteor.methods', function () { .post(api('subscriptions.read')) .set(credentials) .send({ - rid: room._id, - readThreads: true, + rid: room._id, + readThreads: true, }) .expect('Content-Type', 'application/json') .expect(200) @@ -293,8 +294,7 @@ describe('Meteor.methods', function () { expect(res.body).to.have.a.property('success', true); expect(res.body).to.have.a.property('message').that.is.a('string'); - const data = JSON.parse(res.body.message); - console.log(data) + const data = JSON.parse(res.body.message); expect(data).to.have.a.property('result').that.is.an('array'); expect(data.result.length).to.equal(2); @@ -387,20 +387,19 @@ describe('Meteor.methods', function () { .end(done); }); - it('should mark the thread as read', (done) => { - request - .post(methodCall('readThreads')) - .set(userCredentials) - .send(firstThreadMessage._id) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - console.log(res.body) - expect(res.body).to.have.a.property('success', true); - expect(res.body).to.have.a.property('message').that.is.a('string'); - }) - .end(done); - }); + it('should mark the thread as read', (done) => { + request + .post(methodCall('readThreads')) + .set(userCredentials) + .send(firstThreadMessage._id) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.a.property('success', true); + expect(res.body).to.have.a.property('message').that.is.a('string'); + }) + .end(done); + }); }); describe('[@getMessages]', () => { From 800d7cb3ce6076d93c4697c356f7722eac2f9cc5 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Tue, 10 Oct 2023 15:54:40 -0300 Subject: [PATCH 5/8] Remove unrelated change --- packages/model-typings/src/models/IMessagesModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/model-typings/src/models/IMessagesModel.ts b/packages/model-typings/src/models/IMessagesModel.ts index 99d0a9a0e380..d681405850fa 100644 --- a/packages/model-typings/src/models/IMessagesModel.ts +++ b/packages/model-typings/src/models/IMessagesModel.ts @@ -258,7 +258,7 @@ export interface IMessagesModel extends IBaseModel { userId: string, after: Date, ): FindCursor>; - findVisibleUnreadMessagesByRoomAndDate(rid: string, after?: Date): FindCursor>; + findVisibleUnreadMessagesByRoomAndDate(rid: string, after: Date): FindCursor>; setAsReadById(_id: string): Promise; countThreads(): Promise; addThreadFollowerByThreadId(tmid: string, userId: string): Promise; From fa36418b745535efe68023217f99e1c5d6f1d5ba Mon Sep 17 00:00:00 2001 From: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:58:55 -0300 Subject: [PATCH 6/8] Create changesets --- .changeset/weak-cameras-pay.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/weak-cameras-pay.md diff --git a/.changeset/weak-cameras-pay.md b/.changeset/weak-cameras-pay.md new file mode 100644 index 000000000000..724f3af69a29 --- /dev/null +++ b/.changeset/weak-cameras-pay.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed issue with message read receipts not being created when accessing a room the first time From 230e653e46d31cdffb564e008f97adb095f69074 Mon Sep 17 00:00:00 2001 From: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:43:19 -0300 Subject: [PATCH 7/8] Remove new tests (to be added in new PR later) --- .../meteor/tests/end-to-end/api/24-methods.js | 281 +----------------- 1 file changed, 1 insertion(+), 280 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/24-methods.js b/apps/meteor/tests/end-to-end/api/24-methods.js index a13a2e390f28..899eb0db7d5f 100644 --- a/apps/meteor/tests/end-to-end/api/24-methods.js +++ b/apps/meteor/tests/end-to-end/api/24-methods.js @@ -1,11 +1,10 @@ import { expect } from 'chai'; import { after, before, beforeEach, describe, it } from 'mocha'; -import { api, credentials, getCredentials, methodCall, request } from '../../data/api-data.js'; +import { getCredentials, request, methodCall, api, credentials } from '../../data/api-data.js'; import { CI_MAX_ROOMS_PER_GUEST as maxRoomsPerGuest } from '../../data/constants'; import { updatePermission, updateSetting } from '../../data/permissions.helper'; import { createRoom } from '../../data/rooms.helper'; -import { password } from '../../data/user'; import { createUser, deleteUser, login } from '../../data/users.helper.js'; describe('Meteor.methods', function () { @@ -124,284 +123,6 @@ describe('Meteor.methods', function () { }); }); - describe('[@getReadReceipts]', () => { - let firstMessage = false; - let firstThreadMessage = false; - - let user; - let userCredentials; - let room; - - const roomName = `methods-test-channel-${Date.now()}`; - before(async () => { - user = await createUser(); - userCredentials = await login(user.username, password); - room = (await createRoom({ type: 'p', name: roomName, members: [user.username] })).body.group; - }); - - before(async () => { - await updateSetting('Message_Read_Receipt_Enabled', true); - await updateSetting('Message_Read_Receipt_Store_Users', true); - }); - - after(async () => { - deleteUser(user); - }); - - before('send sample message', (done) => { - request - .post(api('chat.sendMessage')) - .set(credentials) - .send({ - message: { - text: 'Sample message', - rid: room._id, - }, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - firstMessage = res.body.message; - }) - .end(done); - }); - - before('send sample message into thread', (done) => { - request - .post(api('chat.sendMessage')) - .set(credentials) - .send({ - message: { - text: 'Second Sample message', - rid: room._id, - tmid: firstMessage._id, - }, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - firstThreadMessage = res.body.message; - }) - .end(done); - }); - - it('should fail if not logged in', (done) => { - request - .post(methodCall('getReadReceipts')) - .send({ - message: JSON.stringify({ - method: 'getReadReceipts', - params: [], - id: 'id', - msg: 'method', - }), - }) - .expect('Content-Type', 'application/json') - .expect(401) - .expect((res) => { - expect(res.body).to.have.property('status', 'error'); - expect(res.body).to.have.property('message'); - }) - .end(done); - }); - - it("should return the sender's read receipt for a message sent in the main room", (done) => { - request - .post(methodCall('getReadReceipts')) - .set(credentials) - .send({ - message: JSON.stringify({ - method: 'getReadReceipts', - params: [{ messageId: firstMessage._id }], - id: 'id', - msg: 'method', - }), - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.a.property('success', true); - expect(res.body).to.have.a.property('message').that.is.a('string'); - - const data = JSON.parse(res.body.message); - expect(data).to.have.a.property('result').that.is.an('array'); - expect(data.result.length).to.equal(1); - expect(data.result[0]).to.have.property('userId', credentials['X-User-Id']); - }) - .end(done); - }); - - it("should return the sender's read receipt for a message sent in a thread", (done) => { - request - .post(methodCall('getReadReceipts')) - .set(credentials) - .send({ - message: JSON.stringify({ - method: 'getReadReceipts', - params: [{ messageId: firstThreadMessage._id }], - id: 'id', - msg: 'method', - }), - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.a.property('success', true); - expect(res.body).to.have.a.property('message').that.is.a('string'); - - const data = JSON.parse(res.body.message); - expect(data).to.have.a.property('result').that.is.an('array'); - expect(data.result.length).to.equal(1); - expect(data.result[0]).to.have.property('userId', credentials['X-User-Id']); - }) - .end(done); - }); - - it("should read all main room's messages with the invited user", (done) => { - request - .post(api('subscriptions.read')) - .set(credentials) - .send({ - rid: room._id, - readThreads: true, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.a.property('success', true); - // expect(res.body).to.have.a.property('message').that.is.a('string'); - }) - .end(done); - }); - - it("should return the invited user's read receipt for a message sent in the main room", (done) => { - request - .post(methodCall('getReadReceipts')) - .set(credentials) - .send({ - message: JSON.stringify({ - method: 'getReadReceipts', - params: [{ messageId: firstMessage._id }], - id: 'id', - msg: 'method', - }), - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.a.property('success', true); - expect(res.body).to.have.a.property('message').that.is.a('string'); - - const data = JSON.parse(res.body.message); - expect(data).to.have.a.property('result').that.is.an('array'); - expect(data.result.length).to.equal(2); - - const receiptsUserIds = [data.result[0].userId, data.result[1].userId]; - expect(receiptsUserIds).to.have.members([credentials['X-User-Id'], user._id]); - }) - .end(done); - }); - - it("should return only the sender's read receipt for a message sent in a thread after the main room is read", (done) => { - request - .post(methodCall('getReadReceipts')) - .set(credentials) - .send({ - message: JSON.stringify({ - method: 'getReadReceipts', - params: [{ messageId: firstThreadMessage._id }], - id: 'id', - msg: 'method', - }), - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.a.property('success', true); - expect(res.body).to.have.a.property('message').that.is.a('string'); - - const data = JSON.parse(res.body.message); - expect(data).to.have.a.property('result').that.is.an('array'); - expect(data.result.length).to.equal(1); - expect(data.result[0]).to.have.property('userId', credentials['X-User-Id']); - }) - .end(done); - }); - - it('should read thread messages with the invited user', (done) => { - request - .post(methodCall('getThreadMessages')) - .set(userCredentials) - .send({ - message: JSON.stringify({ - id: 'id', - msg: 'method', - method: 'getThreadMessages', - params: [ - { - tmid: firstMessage._id, - }, - ], - }), - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.a.property('success', true); - expect(res.body).to.have.a.property('message').that.is.a('string'); - - const data = JSON.parse(res.body.message); - expect(data).to.have.a.property('result').that.is.an('array'); - expect(data.result.length).to.equal(2); - }) - .end(done); - }); - - it("should return the invited user's read receipt for a message sent in a thread after it is read", (done) => { - request - .post(methodCall('getReadReceipts')) - .set(credentials) - .send({ - message: JSON.stringify({ - method: 'getReadReceipts', - params: [{ messageId: firstThreadMessage._id }], - id: 'id', - msg: 'method', - }), - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.a.property('success', true); - expect(res.body).to.have.a.property('message').that.is.a('string'); - - const data = JSON.parse(res.body.message); - expect(data).to.have.a.property('result').that.is.an('array'); - expect(data.result.length).to.equal(2); - - const receiptsUserIds = [data.result[0].userId, data.result[1].userId]; - expect(receiptsUserIds).to.have.members([credentials['X-User-Id'], user._id]); - }) - .end(done); - }); - - it('should mark the thread as read', (done) => { - request - .post(methodCall('readThreads')) - .set(userCredentials) - .send(firstThreadMessage._id) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.a.property('success', true); - expect(res.body).to.have.a.property('message').that.is.a('string'); - }) - .end(done); - }); - }); - describe('[@getMessages]', () => { let rid = false; let firstMessage = false; From dde591c4b34aa6c0e938d970c25a428380d02b93 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Fri, 13 Oct 2023 17:13:18 -0300 Subject: [PATCH 8/8] Use older timestamp so that more messages are marked as read --- apps/meteor/server/lib/readMessages.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/meteor/server/lib/readMessages.ts b/apps/meteor/server/lib/readMessages.ts index 09aea69ca65a..d7c8cf559288 100644 --- a/apps/meteor/server/lib/readMessages.ts +++ b/apps/meteor/server/lib/readMessages.ts @@ -6,7 +6,7 @@ import { callbacks } from '../../lib/callbacks'; export async function readMessages(rid: IRoom['_id'], uid: IUser['_id'], readThreads: boolean): Promise { await callbacks.run('beforeReadMessages', rid, uid); - const projection = { ls: 1, tunread: 1, alert: 1, _updatedAt: 1 }; + const projection = { ls: 1, tunread: 1, alert: 1, ts: 1 }; const sub = await Subscriptions.findOneByRoomIdAndUserId(rid, uid, { projection }); if (!sub) { throw new Error('error-invalid-subscription'); @@ -19,6 +19,6 @@ export async function readMessages(rid: IRoom['_id'], uid: IUser['_id'], readThr await NotificationQueue.clearQueueByUserId(uid); - const lastSeen = sub.ls || sub._updatedAt; + const lastSeen = sub.ls || sub.ts; callbacks.runAsync('afterReadMessages', rid, { uid, lastSeen }); }