From 995b4e3ff88d9f75591431bc1073fcacae1ad33d Mon Sep 17 00:00:00 2001 From: Felipe Parreira Date: Mon, 21 Sep 2020 19:44:00 -0300 Subject: [PATCH] [FIX] Purged threads still show as unread (#18944) Co-authored-by: Diego Sampaio Co-authored-by: Rodrigo Nascimento --- app/lib/server/functions/cleanRoomHistory.js | 18 ++++-- app/models/server/models/Messages.js | 23 ++++++++ app/models/server/models/Subscriptions.js | 19 ++++++ server/startup/migrations/index.js | 1 + server/startup/migrations/v206.js | 62 ++++++++++++++++++++ 5 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 server/startup/migrations/v206.js diff --git a/app/lib/server/functions/cleanRoomHistory.js b/app/lib/server/functions/cleanRoomHistory.js index 593fdaff09a4..a94f88d8ad50 100644 --- a/app/lib/server/functions/cleanRoomHistory.js +++ b/app/lib/server/functions/cleanRoomHistory.js @@ -1,9 +1,9 @@ import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { deleteRoom } from './deleteRoom'; -import { FileUpload } from '../../../file-upload'; -import { Messages, Rooms } from '../../../models'; -import { Notifications } from '../../../notifications'; +import { FileUpload } from '../../../file-upload/server'; +import { Messages, Rooms, Subscriptions } from '../../../models/server'; +import { Notifications } from '../../../notifications/server'; export const cleanRoomHistory = function({ rid, latest = new Date(), oldest = new Date('0001-01-01T00:00:00Z'), inclusive = true, limit = 0, excludePinned = true, ignoreDiscussion = true, filesOnly = false, fromUsers = [], ignoreThreads = true }) { const gt = inclusive ? '$gte' : '$gt'; @@ -39,7 +39,17 @@ export const cleanRoomHistory = function({ rid, latest = new Date(), oldest = ne .forEach(({ drid }) => deleteRoom(drid)); } - const count = Messages.removeByIdPinnedTimestampLimitAndUsers(rid, excludePinned, ignoreDiscussion, ts, limit, fromUsers); + if (!ignoreThreads) { + const threads = new Set(); + Messages.findThreadsByRoomIdPinnedTimestampAndUsers({ rid, pinned: excludePinned, ignoreDiscussion, ts, users: fromUsers }, { fields: { _id: 1 } }) + .forEach(({ _id }) => threads.add(_id)); + + if (threads.size > 0) { + Subscriptions.removeUnreadThreadsByRoomId(rid, [...threads]); + } + } + + const count = Messages.removeByIdPinnedTimestampLimitAndUsers(rid, excludePinned, ignoreDiscussion, ts, limit, fromUsers, ignoreThreads); if (count) { Rooms.resetLastMessageById(rid); Notifications.notifyRoom(rid, 'deleteMessageBulk', { diff --git a/app/models/server/models/Messages.js b/app/models/server/models/Messages.js index 7a6d11c3b715..c7e49e0b4f32 100644 --- a/app/models/server/models/Messages.js +++ b/app/models/server/models/Messages.js @@ -929,6 +929,29 @@ export class Messages extends Base { return this.remove({ rid: { $in: rids } }); } + findThreadsByRoomIdPinnedTimestampAndUsers({ rid, pinned, ignoreDiscussion = true, ts, users = [] }, options) { + const query = { + rid, + ts, + tlm: { $exists: 1 }, + tcount: { $exists: 1 }, + }; + + if (pinned) { + query.pinned = { $ne: true }; + } + + if (ignoreDiscussion) { + query.drid = { $exists: 0 }; + } + + if (users.length > 0) { + query['u.username'] = { $in: users }; + } + + return this.find(query, options); + } + removeByIdPinnedTimestampLimitAndUsers(rid, pinned, ignoreDiscussion = true, ts, limit, users = [], ignoreThreads = true) { const query = { rid, diff --git a/app/models/server/models/Subscriptions.js b/app/models/server/models/Subscriptions.js index ed87a1fad5fc..3615dbf2ae20 100644 --- a/app/models/server/models/Subscriptions.js +++ b/app/models/server/models/Subscriptions.js @@ -1420,11 +1420,30 @@ export class Subscriptions extends Base { const update = { $unset: { tunread: 1, + tunreadUser: 1, + tunreadGroup: 1, }, }; return this.update(query, update); } + + removeUnreadThreadsByRoomId(rid, tunread) { + const query = { + rid, + tunread, + }; + + const update = { + $pullAll: { + tunread, + tunreadUser: tunread, + tunreadGroup: tunread, + }, + }; + + return this.update(query, update, { multi: true }); + } } export default new Subscriptions('subscription', true); diff --git a/server/startup/migrations/index.js b/server/startup/migrations/index.js index 8b7c280b2ae4..94ffb34521bb 100644 --- a/server/startup/migrations/index.js +++ b/server/startup/migrations/index.js @@ -202,4 +202,5 @@ import './v202'; import './v203'; import './v204'; import './v205'; +import './v206'; import './xrun'; diff --git a/server/startup/migrations/v206.js b/server/startup/migrations/v206.js new file mode 100644 index 000000000000..f1b3efbffcc2 --- /dev/null +++ b/server/startup/migrations/v206.js @@ -0,0 +1,62 @@ +import { Migrations } from '../../../app/migrations'; +import { Subscriptions, Messages } from '../../../app/models/server/raw'; + + +async function migrate() { + const subs = await Subscriptions.find({ + $or: [{ + 'tunread.0': { $exists: true }, + }, { + 'tunreadUser.0': { $exists: true }, + }, { + 'tunreadGroup.0': { $exists: true }, + }], + }, { + projection: { + _id: 0, + tunread: 1, + tunreadUser: 1, + tunreadGroup: 1, + }, + }).toArray(); + + // Get unique thread ids + const tunreads = new Set(); + for (const { tunread = [], tunreadUser = [], tunreadGroup = [] } of subs) { + tunread.forEach((i) => tunreads.add(i)); + tunreadUser.forEach((i) => tunreads.add(i)); + tunreadGroup.forEach((i) => tunreads.add(i)); + } + + const inexistentThreads = new Set(); + for await (const tunread of tunreads) { + if (!await Messages.findOne({ _id: tunread }, { _id: 1 })) { + inexistentThreads.add(tunread); + } + } + + const inexistentThreadsArr = [...inexistentThreads]; + + await Subscriptions.update({ + $or: [{ + tunread: { $in: inexistentThreadsArr }, + }, { + tunreadUser: { $in: inexistentThreadsArr }, + }, { + tunreadGroup: { $in: inexistentThreadsArr }, + }], + }, { + $pullAll: { + tunread: inexistentThreadsArr, + tunreadUser: inexistentThreadsArr, + tunreadGroup: inexistentThreadsArr, + }, + }); +} + +Migrations.add({ + version: 206, + up() { + Promise.await(migrate()); + }, +});