diff --git a/apps/meteor/app/api/server/v1/chat.ts b/apps/meteor/app/api/server/v1/chat.ts index dddedc3bfd35..7569f321fa20 100644 --- a/apps/meteor/app/api/server/v1/chat.ts +++ b/apps/meteor/app/api/server/v1/chat.ts @@ -34,6 +34,7 @@ import { escapeRegExp } from '@rocket.chat/string-helpers'; import { Meteor } from 'meteor/meteor'; import { reportMessage } from '../../../../server/lib/moderation/reportMessage'; +import { ignoreUser } from '../../../../server/methods/ignoreUser'; import { messageSearch } from '../../../../server/methods/messageSearch'; import { getMessageHistory } from '../../../../server/publications/messages'; import { roomAccessAttributes } from '../../../authorization/server'; @@ -42,13 +43,17 @@ import { canSendMessageAsync } from '../../../authorization/server/functions/can import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { deleteMessageValidatingPermission } from '../../../lib/server/functions/deleteMessage'; import { processWebhookMessage } from '../../../lib/server/functions/processWebhookMessage'; +import { getSingleMessage } from '../../../lib/server/methods/getSingleMessage'; import { executeSendMessage } from '../../../lib/server/methods/sendMessage'; import { executeUpdateMessage } from '../../../lib/server/methods/updateMessage'; import { applyAirGappedRestrictionsValidation } from '../../../license/server/airGappedRestrictionsWrapper'; -import { pinMessage } from '../../../message-pin/server/pinMessage'; +import { pinMessage, unpinMessage } from '../../../message-pin/server/pinMessage'; +import { starMessage } from '../../../message-star/server/starMessage'; import { OEmbed } from '../../../oembed/server/server'; import { executeSetReaction } from '../../../reactions/server/setReaction'; import { settings } from '../../../settings/server'; +import { followMessage } from '../../../threads/server/methods/followMessage'; +import { unfollowMessage } from '../../../threads/server/methods/unfollowMessage'; import { MessageTypes } from '../../../ui-utils/server'; import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; import { API } from '../api'; @@ -148,7 +153,11 @@ API.v1.addRoute( }, { async get() { - const msg = await Meteor.callAsync('getSingleMessage', this.queryParams.msgId); + if (!this.queryParams.msgId) { + return API.v1.failure('The "msgId" query parameter must be provided.'); + } + + const msg = await getSingleMessage(this.userId, this.queryParams.msgId); if (!msg) { return API.v1.failure(); @@ -289,7 +298,7 @@ API.v1.addRoute( throw new Meteor.Error('error-message-not-found', 'The provided "messageId" does not match any existing message.'); } - await Meteor.callAsync('starMessage', { + await starMessage(this.userId, { _id: msg._id, rid: msg.rid, starred: true, @@ -311,7 +320,7 @@ API.v1.addRoute( throw new Meteor.Error('error-message-not-found', 'The provided "messageId" does not match any existing message.'); } - await Meteor.callAsync('unpinMessage', msg); + await unpinMessage(this.userId, msg); return API.v1.success(); }, @@ -329,7 +338,7 @@ API.v1.addRoute( throw new Meteor.Error('error-message-not-found', 'The provided "messageId" does not match any existing message.'); } - await Meteor.callAsync('starMessage', { + await starMessage(this.userId, { _id: msg._id, rid: msg.rid, starred: false, @@ -437,7 +446,15 @@ API.v1.addRoute( ignore = typeof ignore === 'string' ? /true|1/.test(ignore) : ignore; - await Meteor.callAsync('ignoreUser', { rid, userId, ignore }); + if (!rid?.trim()) { + throw new Meteor.Error('error-room-id-param-not-provided', 'The required "rid" param is missing.'); + } + + if (!userId?.trim()) { + throw new Meteor.Error('error-user-id-param-not-provided', 'The required "userId" param is missing.'); + } + + await ignoreUser(this.userId, { rid, userId, ignore }); return API.v1.success(); }, @@ -685,7 +702,11 @@ API.v1.addRoute( async post() { const { mid } = this.bodyParams; - await Meteor.callAsync('followMessage', { mid }); + if (!mid) { + throw new Meteor.Error('The required "mid" body param is missing.'); + } + + await followMessage(this.userId, { mid }); return API.v1.success(); }, @@ -699,7 +720,11 @@ API.v1.addRoute( async post() { const { mid } = this.bodyParams; - await Meteor.callAsync('unfollowMessage', { mid }); + if (!mid) { + throw new Meteor.Error('The required "mid" body param is missing.'); + } + + await unfollowMessage(this.userId, { mid }); return API.v1.success(); }, diff --git a/apps/meteor/app/lib/server/methods/getSingleMessage.ts b/apps/meteor/app/lib/server/methods/getSingleMessage.ts index c4b6f065296b..3408d1e6c579 100644 --- a/apps/meteor/app/lib/server/methods/getSingleMessage.ts +++ b/apps/meteor/app/lib/server/methods/getSingleMessage.ts @@ -13,6 +13,20 @@ declare module '@rocket.chat/ddp-client' { } } +export const getSingleMessage = async (userId: string, mid: IMessage['_id']): Promise => { + const msg = await Messages.findOneById(mid); + + if (!msg?.rid) { + return null; + } + + if (!(await canAccessRoomIdAsync(msg.rid, userId))) { + throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'getSingleMessage' }); + } + + return msg; +}; + Meteor.methods({ async getSingleMessage(mid) { check(mid, String); @@ -23,16 +37,6 @@ Meteor.methods({ throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getSingleMessage' }); } - const msg = await Messages.findOneById(mid); - - if (!msg?.rid) { - return null; - } - - if (!(await canAccessRoomIdAsync(msg.rid, uid))) { - throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'getSingleMessage' }); - } - - return msg; + return getSingleMessage(uid, mid); }, }); diff --git a/apps/meteor/app/message-pin/server/pinMessage.ts b/apps/meteor/app/message-pin/server/pinMessage.ts index f3622ff7bcad..012c9bb2a23d 100644 --- a/apps/meteor/app/message-pin/server/pinMessage.ts +++ b/apps/meteor/app/message-pin/server/pinMessage.ts @@ -43,7 +43,15 @@ declare module '@rocket.chat/ddp-client' { } } -export async function pinMessage(originalMessage: IMessage, userId: string, pinnedAt?: Date) { +export async function pinMessage(message: IMessage, userId: string, pinnedAt?: Date) { + let originalMessage = await Messages.findOneById(message._id); + if (!originalMessage?.rid) { + throw new Meteor.Error('error-invalid-message', 'Message you are pinning was not found', { + method: 'pinMessage', + action: 'Message_pinning', + }); + } + if (!settings.get('Message_AllowPinning')) { throw new Meteor.Error('error-action-not-allowed', 'Message pinning not allowed', { method: 'pinMessage', @@ -118,6 +126,81 @@ export async function pinMessage(originalMessage: IMessage, userId: string, pinn }); } +export const unpinMessage = async (userId: string, message: IMessage) => { + if (!settings.get('Message_AllowPinning')) { + throw new Meteor.Error('error-action-not-allowed', 'Message pinning not allowed', { + method: 'unpinMessage', + action: 'Message_pinning', + }); + } + + let originalMessage = await Messages.findOneById(message._id); + if (originalMessage == null || originalMessage._id == null) { + throw new Meteor.Error('error-invalid-message', 'Message you are unpinning was not found', { + method: 'unpinMessage', + action: 'Message_pinning', + }); + } + + const subscription = await Subscriptions.findOneByRoomIdAndUserId(originalMessage.rid, userId, { projection: { _id: 1 } }); + if (!subscription) { + // If it's a valid message but on a room that the user is not subscribed to, report that the message was not found. + throw new Meteor.Error('error-invalid-message', 'Message you are unpinning was not found', { + method: 'unpinMessage', + action: 'Message_pinning', + }); + } + + if (!(await hasPermissionAsync(userId, 'pin-message', originalMessage.rid))) { + throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'unpinMessage' }); + } + + const me = await Users.findOneById(userId); + if (!me) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'unpinMessage' }); + } + + // If we keep history of edits, insert a new message to store history information + if (settings.get('Message_KeepHistory') && isRegisterUser(me)) { + await Messages.cloneAndSaveAsHistoryById(originalMessage._id, me); + } + + originalMessage.pinned = false; + originalMessage.pinnedBy = { + _id: userId, + username: me.username, + }; + + const room = await Rooms.findOneById(originalMessage.rid, { projection: { ...roomAccessAttributes, lastMessage: 1 } }); + if (!room) { + throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'unpinMessage' }); + } + + if (!(await canAccessRoomAsync(room, { _id: userId }))) { + throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'unpinMessage' }); + } + + originalMessage = await Message.beforeSave({ message: originalMessage, room, user: me }); + + if (isTheLastMessage(room, message)) { + await Rooms.setLastMessagePinned(room._id, originalMessage.pinnedBy, originalMessage.pinned); + void notifyOnRoomChangedById(room._id); + } + + // App IPostMessagePinned event hook + await Apps.self?.triggerEvent(AppEvents.IPostMessagePinned, originalMessage, await Meteor.userAsync(), originalMessage.pinned); + + await Messages.setPinnedByIdAndUserId(originalMessage._id, originalMessage.pinnedBy, originalMessage.pinned); + if (settings.get('Message_Read_Receipt_Store_Users')) { + await ReadReceipts.setPinnedByMessageId(originalMessage._id, originalMessage.pinned); + } + void notifyOnMessageChange({ + id: message._id, + }); + + return true; +}; + Meteor.methods({ async pinMessage(message, pinnedAt) { check(message._id, String); @@ -129,15 +212,7 @@ Meteor.methods({ }); } - const originalMessage = await Messages.findOneById(message._id); - if (!originalMessage?.rid) { - throw new Meteor.Error('error-invalid-message', 'Message you are pinning was not found', { - method: 'pinMessage', - action: 'Message_pinning', - }); - } - - return pinMessage(originalMessage, userId, pinnedAt); + return pinMessage(message, userId, pinnedAt); }, async unpinMessage(message) { check(message._id, String); @@ -150,77 +225,6 @@ Meteor.methods({ }); } - if (!settings.get('Message_AllowPinning')) { - throw new Meteor.Error('error-action-not-allowed', 'Message pinning not allowed', { - method: 'unpinMessage', - action: 'Message_pinning', - }); - } - - let originalMessage = await Messages.findOneById(message._id); - if (originalMessage == null || originalMessage._id == null) { - throw new Meteor.Error('error-invalid-message', 'Message you are unpinning was not found', { - method: 'unpinMessage', - action: 'Message_pinning', - }); - } - - const subscription = await Subscriptions.findOneByRoomIdAndUserId(originalMessage.rid, userId, { projection: { _id: 1 } }); - if (!subscription) { - // If it's a valid message but on a room that the user is not subscribed to, report that the message was not found. - throw new Meteor.Error('error-invalid-message', 'Message you are unpinning was not found', { - method: 'unpinMessage', - action: 'Message_pinning', - }); - } - - if (!(await hasPermissionAsync(userId, 'pin-message', originalMessage.rid))) { - throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'unpinMessage' }); - } - - const me = await Users.findOneById(userId); - if (!me) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'unpinMessage' }); - } - - // If we keep history of edits, insert a new message to store history information - if (settings.get('Message_KeepHistory') && isRegisterUser(me)) { - await Messages.cloneAndSaveAsHistoryById(originalMessage._id, me); - } - - originalMessage.pinned = false; - originalMessage.pinnedBy = { - _id: userId, - username: me.username, - }; - - const room = await Rooms.findOneById(originalMessage.rid, { projection: { ...roomAccessAttributes, lastMessage: 1 } }); - if (!room) { - throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'unpinMessage' }); - } - - if (!(await canAccessRoomAsync(room, { _id: userId }))) { - throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'unpinMessage' }); - } - - originalMessage = await Message.beforeSave({ message: originalMessage, room, user: me }); - - if (isTheLastMessage(room, message)) { - await Rooms.setLastMessagePinned(room._id, originalMessage.pinnedBy, originalMessage.pinned); - void notifyOnRoomChangedById(room._id); - } - - // App IPostMessagePinned event hook - await Apps.self?.triggerEvent(AppEvents.IPostMessagePinned, originalMessage, await Meteor.userAsync(), originalMessage.pinned); - - await Messages.setPinnedByIdAndUserId(originalMessage._id, originalMessage.pinnedBy, originalMessage.pinned); - if (settings.get('Message_Read_Receipt_Store_Users')) { - await ReadReceipts.setPinnedByMessageId(originalMessage._id, originalMessage.pinned); - } - void notifyOnMessageChange({ - id: message._id, - }); - - return true; + return unpinMessage(userId, message); }, }); diff --git a/apps/meteor/app/message-star/server/starMessage.ts b/apps/meteor/app/message-star/server/starMessage.ts index 36c67c1f4020..96b342f8bfa6 100644 --- a/apps/meteor/app/message-star/server/starMessage.ts +++ b/apps/meteor/app/message-star/server/starMessage.ts @@ -12,60 +12,64 @@ import { settings } from '../../settings/server'; declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { - starMessage(message: Omit & { starred: boolean }): boolean; + starMessage(message: Pick & { starred: boolean }): boolean; } } -Meteor.methods({ - async starMessage(message) { - const uid = Meteor.userId(); +export const starMessage = async (userId: string, message: Pick & { starred: boolean }): Promise => { + if (!settings.get('Message_AllowStarring')) { + throw new Meteor.Error('error-action-not-allowed', 'Message starring not allowed', { + method: 'starMessage', + action: 'Message_starring', + }); + } - if (!uid) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { - method: 'starMessage', - }); - } + const subscription = await Subscriptions.findOneByRoomIdAndUserId(message.rid, userId, { + projection: { _id: 1 }, + }); + if (!subscription) { + return false; + } + if (!(await Messages.findOneByRoomIdAndMessageId(message.rid, message._id))) { + return false; + } - if (!settings.get('Message_AllowStarring')) { - throw new Meteor.Error('error-action-not-allowed', 'Message starring not allowed', { - method: 'starMessage', - action: 'Message_starring', - }); - } + const room = await Rooms.findOneById(message.rid, { projection: { ...roomAccessAttributes, lastMessage: 1 } }); - const subscription = await Subscriptions.findOneByRoomIdAndUserId(message.rid, uid, { - projection: { _id: 1 }, - }); - if (!subscription) { - return false; - } - if (!(await Messages.findOneByRoomIdAndMessageId(message.rid, message._id))) { - return false; - } + if (!room) { + throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'starMessage' }); + } - const room = await Rooms.findOneById(message.rid, { projection: { ...roomAccessAttributes, lastMessage: 1 } }); + if (!(await canAccessRoomAsync(room, { _id: userId }))) { + throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'starMessage' }); + } - if (!room) { - throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'starMessage' }); - } + if (isTheLastMessage(room, message)) { + await Rooms.updateLastMessageStar(room._id, userId, message.starred); + void notifyOnRoomChangedById(room._id); + } - if (!(await canAccessRoomAsync(room, { _id: uid }))) { - throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'starMessage' }); - } + await Apps.self?.triggerEvent(AppEvents.IPostMessageStarred, message, await Meteor.userAsync(), message.starred); - if (isTheLastMessage(room, message)) { - await Rooms.updateLastMessageStar(room._id, uid, message.starred); - void notifyOnRoomChangedById(room._id); - } + await Messages.updateUserStarById(message._id, userId, message.starred); - await Apps.self?.triggerEvent(AppEvents.IPostMessageStarred, message, await Meteor.userAsync(), message.starred); + void notifyOnMessageChange({ + id: message._id, + }); - await Messages.updateUserStarById(message._id, uid, message.starred); + return true; +}; - void notifyOnMessageChange({ - id: message._id, - }); +Meteor.methods({ + async starMessage(message) { + const uid = Meteor.userId(); + + if (!uid) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { + method: 'starMessage', + }); + } - return true; + return starMessage(uid, message); }, }); diff --git a/apps/meteor/app/threads/server/methods/followMessage.ts b/apps/meteor/app/threads/server/methods/followMessage.ts index 8ed7093e00d4..88d1b6274002 100644 --- a/apps/meteor/app/threads/server/methods/followMessage.ts +++ b/apps/meteor/app/threads/server/methods/followMessage.ts @@ -18,42 +18,46 @@ declare module '@rocket.chat/ddp-client' { } } -Meteor.methods({ - async followMessage({ mid }) { - check(mid, String); +export const followMessage = async (userId: string, { mid }: { mid: IMessage['_id'] }): Promise => { + if (mid && !settings.get('Threads_enabled')) { + throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'followMessage' }); + } - const uid = Meteor.userId(); - if (!uid) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'followMessage' }); - } + const message = await Messages.findOneById(mid); + if (!message) { + throw new Meteor.Error('error-invalid-message', 'Invalid message', { + method: 'followMessage', + }); + } - if (mid && !settings.get('Threads_enabled')) { - throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'followMessage' }); - } + if (!(await canAccessRoomIdAsync(message.rid, userId))) { + throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'followMessage' }); + } - const message = await Messages.findOneById(mid); - if (!message) { - throw new Meteor.Error('error-invalid-message', 'Invalid message', { - method: 'followMessage', - }); - } + const id = message.tmid || message._id; - if (!(await canAccessRoomIdAsync(message.rid, uid))) { - throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'followMessage' }); - } + const followResult = await follow({ tmid: id, uid: userId }); - const id = message.tmid || message._id; + void notifyOnMessageChange({ + id, + }); - const followResult = await follow({ tmid: id, uid }); + const isFollowed = true; + await Apps.self?.triggerEvent(AppEvents.IPostMessageFollowed, message, await Meteor.userAsync(), isFollowed); - void notifyOnMessageChange({ - id, - }); + return followResult; +}; - const isFollowed = true; - await Apps.self?.triggerEvent(AppEvents.IPostMessageFollowed, message, await Meteor.userAsync(), isFollowed); +Meteor.methods({ + async followMessage({ mid }) { + check(mid, String); + + const uid = Meteor.userId(); + if (!uid) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'followMessage' }); + } - return followResult; + return followMessage(uid, { mid }); }, }); diff --git a/apps/meteor/app/threads/server/methods/unfollowMessage.ts b/apps/meteor/app/threads/server/methods/unfollowMessage.ts index de4f2683be41..d19bdf604005 100644 --- a/apps/meteor/app/threads/server/methods/unfollowMessage.ts +++ b/apps/meteor/app/threads/server/methods/unfollowMessage.ts @@ -18,42 +18,46 @@ declare module '@rocket.chat/ddp-client' { } } -Meteor.methods({ - async unfollowMessage({ mid }) { - check(mid, String); +export const unfollowMessage = async (userId: string, { mid }: { mid: IMessage['_id'] }): Promise => { + if (mid && !settings.get('Threads_enabled')) { + throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'unfollowMessage' }); + } - const uid = Meteor.userId(); - if (!uid) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'unfollowMessage' }); - } + const message = await Messages.findOneById(mid); + if (!message) { + throw new Meteor.Error('error-invalid-message', 'Invalid message', { + method: 'unfollowMessage', + }); + } - if (mid && !settings.get('Threads_enabled')) { - throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'unfollowMessage' }); - } + if (!(await canAccessRoomIdAsync(message.rid, userId))) { + throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'unfollowMessage' }); + } - const message = await Messages.findOneById(mid); - if (!message) { - throw new Meteor.Error('error-invalid-message', 'Invalid message', { - method: 'unfollowMessage', - }); - } + const id = message.tmid || message._id; - if (!(await canAccessRoomIdAsync(message.rid, uid))) { - throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'unfollowMessage' }); - } + const unfollowResult = await unfollow({ rid: message.rid, tmid: id, uid: userId }); - const id = message.tmid || message._id; + void notifyOnMessageChange({ + id, + }); - const unfollowResult = await unfollow({ rid: message.rid, tmid: id, uid }); + const isFollowed = false; + await Apps.self?.triggerEvent(AppEvents.IPostMessageFollowed, message, await Meteor.userAsync(), isFollowed); - void notifyOnMessageChange({ - id, - }); + return unfollowResult; +}; - const isFollowed = false; - await Apps.self?.triggerEvent(AppEvents.IPostMessageFollowed, message, await Meteor.userAsync(), isFollowed); +Meteor.methods({ + async unfollowMessage({ mid }) { + check(mid, String); + + const uid = Meteor.userId(); + if (!uid) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'unfollowMessage' }); + } - return unfollowResult; + return unfollowMessage(uid, { mid }); }, }); diff --git a/apps/meteor/server/methods/ignoreUser.ts b/apps/meteor/server/methods/ignoreUser.ts index a8739a910b37..8e679221396a 100644 --- a/apps/meteor/server/methods/ignoreUser.ts +++ b/apps/meteor/server/methods/ignoreUser.ts @@ -12,6 +12,36 @@ declare module '@rocket.chat/ddp-client' { } } +export const ignoreUser = async ( + fromUserId: string, + { rid, userId: ignoredUser, ignore }: { rid: string; userId: string; ignore?: boolean }, +): Promise => { + const [subscription, subscriptionIgnoredUser] = await Promise.all([ + Subscriptions.findOneByRoomIdAndUserId(rid, fromUserId), + Subscriptions.findOneByRoomIdAndUserId(rid, ignoredUser), + ]); + + if (!subscription) { + throw new Meteor.Error('error-invalid-subscription', 'Invalid subscription', { + method: 'ignoreUser', + }); + } + + if (!subscriptionIgnoredUser) { + throw new Meteor.Error('error-invalid-subscription', 'Invalid subscription', { + method: 'ignoreUser', + }); + } + + const result = await Subscriptions.ignoreUser({ _id: subscription._id, ignoredUser, ignore }); + + if (result.modifiedCount) { + void notifyOnSubscriptionChangedById(subscription._id); + } + + return !!result; +}; + Meteor.methods({ async ignoreUser({ rid, userId: ignoredUser, ignore = true }) { check(ignoredUser, String); @@ -25,29 +55,6 @@ Meteor.methods({ }); } - const [subscription, subscriptionIgnoredUser] = await Promise.all([ - Subscriptions.findOneByRoomIdAndUserId(rid, userId), - Subscriptions.findOneByRoomIdAndUserId(rid, ignoredUser), - ]); - - if (!subscription) { - throw new Meteor.Error('error-invalid-subscription', 'Invalid subscription', { - method: 'ignoreUser', - }); - } - - if (!subscriptionIgnoredUser) { - throw new Meteor.Error('error-invalid-subscription', 'Invalid subscription', { - method: 'ignoreUser', - }); - } - - const result = await Subscriptions.ignoreUser({ _id: subscription._id, ignoredUser, ignore }); - - if (result.modifiedCount) { - void notifyOnSubscriptionChangedById(subscription._id); - } - - return !!result; + return ignoreUser(userId, { rid, userId: ignoredUser, ignore }); }, });