From d693acc3ac044671e2544d5d477423acbe680211 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 28 Jul 2020 15:45:55 -0300 Subject: [PATCH] Fix mark room as read with thread unreads still --- app/models/server/models/Subscriptions.js | 18 ++++++++++----- app/models/server/raw/Subscriptions.js | 20 ++++++++++++++++ app/threads/server/functions.js | 10 +++++++- server/lib/markRoomAsRead.ts | 21 +++++++++++++++++ server/methods/readMessages.js | 28 +++++++---------------- 5 files changed, 70 insertions(+), 27 deletions(-) create mode 100644 server/lib/markRoomAsRead.ts diff --git a/app/models/server/models/Subscriptions.js b/app/models/server/models/Subscriptions.js index 2913a30370e6..f0e8e4514e2d 100644 --- a/app/models/server/models/Subscriptions.js +++ b/app/models/server/models/Subscriptions.js @@ -1396,17 +1396,23 @@ export class Subscriptions extends Base { }, { multi: true }); } - removeUnreadThreadByRoomIdAndUserId(rid, userId, tmid) { - return this.update({ - 'u._id': userId, - rid, - }, { + removeUnreadThreadByRoomIdAndUserId(rid, userId, tmid, clearAlert = false) { + const update = { $pull: { tunread: tmid, tunreadGroup: tmid, tunreadUser: tmid, }, - }); + }; + + if (clearAlert) { + update.$set = { alert: false }; + } + + return this.update({ + 'u._id': userId, + rid, + }, update); } removeAllUnreadThreadsByRoomIdAndUserId(rid, userId) { diff --git a/app/models/server/raw/Subscriptions.js b/app/models/server/raw/Subscriptions.js index 4fefcbc8d90a..8860ebc21328 100644 --- a/app/models/server/raw/Subscriptions.js +++ b/app/models/server/raw/Subscriptions.js @@ -34,4 +34,24 @@ export class SubscriptionsRaw extends BaseRaw { return this.findOne(query, { fields: { roles: 1 } }); } + + setAsReadByRoomIdAndUserId(rid, uid, alert = false) { + const query = { + rid, + 'u._id': uid, + }; + + const update = { + $set: { + open: true, + alert, + unread: 0, + userMentions: 0, + groupMentions: 0, + ls: new Date(), + }, + }; + + return this.col.update(query, update); + } } diff --git a/app/threads/server/functions.js b/app/threads/server/functions.js index f9d3d3fff81a..bf9a54fcc4b7 100644 --- a/app/threads/server/functions.js +++ b/app/threads/server/functions.js @@ -67,6 +67,14 @@ export const unfollow = ({ tmid, rid, uid }) => { return Messages.removeThreadFollowerByThreadId(tmid, uid); }; -export const readThread = ({ userId, rid, tmid }) => Subscriptions.removeUnreadThreadByRoomIdAndUserId(rid, userId, tmid); +export const readThread = ({ userId, rid, tmid }) => { + const fields = { tunread: 1 }; + const sub = Subscriptions.findOneByRoomIdAndUserId(rid, userId, { fields }); + + // if the thread being marked as read is the last one unread also clear the unread subscription flag + const clearAlert = sub.tunread?.length <= 1 && sub.tunread.includes(tmid); + + Subscriptions.removeUnreadThreadByRoomIdAndUserId(rid, userId, tmid, clearAlert); +}; export const readAllThreads = (rid, userId) => Subscriptions.removeAllUnreadThreadsByRoomIdAndUserId(rid, userId); diff --git a/server/lib/markRoomAsRead.ts b/server/lib/markRoomAsRead.ts new file mode 100644 index 000000000000..99c89c82eaca --- /dev/null +++ b/server/lib/markRoomAsRead.ts @@ -0,0 +1,21 @@ +import { callbacks } from '../../app/callbacks/server'; +import { NotificationQueue, Subscriptions } from '../../app/models/server/raw'; + +export async function markRoomAsRead(rid: string, uid: string): Promise { + callbacks.run('beforeReadMessages', rid, uid); + + const projection = { ls: 1, tunread: 1, alert: 1 }; + const sub = await Subscriptions.findOneByRoomIdAndUserId(rid, uid, { projection }); + if (!sub) { + throw new Error('error-invalid-subscription'); + } + + // do not mark room as read if there are still unread threads + const alert = sub.alert && sub.tunread?.length > 0; + + await Subscriptions.setAsReadByRoomIdAndUserId(rid, uid, alert); + + await NotificationQueue.clearQueueByUserId(uid); + + callbacks.runAsync('afterReadMessages', rid, { uid, lastSeen: sub.ls }); +} diff --git a/server/methods/readMessages.js b/server/methods/readMessages.js index 08b394f8c51d..d82b238dd6a1 100644 --- a/server/methods/readMessages.js +++ b/server/methods/readMessages.js @@ -1,39 +1,27 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; -import { callbacks } from '../../app/callbacks/server'; -import { Subscriptions } from '../../app/models/server'; -import { NotificationQueue } from '../../app/models/server/raw'; +import { markRoomAsRead } from '../lib/markRoomAsRead'; +import { canAccessRoom } from '../../app/authorization/server'; +import { Rooms } from '../../app/models/server'; Meteor.methods({ readMessages(rid) { check(rid, String); const userId = Meteor.userId(); - if (!userId) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'readMessages', }); } - callbacks.run('beforeReadMessages', rid, userId); - - // TODO: move this calls to an exported function - const userSubscription = Subscriptions.findOneByRoomIdAndUserId(rid, userId, { fields: { ls: 1 } }); - - if (!userSubscription) { - throw new Meteor.Error('error-invalid-subscription', 'Invalid subscription', { - method: 'readMessages', - }); + const user = Meteor.user(); + const room = Rooms.findOneById(rid); + if (!canAccessRoom(room, user)) { + throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'readMessages' }); } - Subscriptions.setAsReadByRoomIdAndUserId(rid, userId); - - NotificationQueue.clearQueueByUserId(userId); - - Meteor.defer(() => { - callbacks.run('afterReadMessages', rid, { userId, lastSeen: userSubscription.ls }); - }); + markRoomAsRead(rid, userId); }, });