Skip to content
77 changes: 55 additions & 22 deletions apps/meteor/app/threads/server/functions.ts
Original file line number Diff line number Diff line change
@@ -1,64 +1,97 @@
import type { IMessage } from '@rocket.chat/core-typings';
import type { IMessage, IRoom } from '@rocket.chat/core-typings';
import { isEditedMessage } from '@rocket.chat/core-typings';
import { Messages, Subscriptions, ReadReceipts, NotificationQueue } from '@rocket.chat/models';
import { Messages, Subscriptions, ReadReceipts, NotificationQueue, Rooms } from '@rocket.chat/models';

import {
notifyOnSubscriptionChangedByRoomIdAndUserIds,
notifyOnSubscriptionChangedByRoomIdAndUserId,
} from '../../lib/server/lib/notifyListener';
import { getMentions, getUserIdsFromHighlights } from '../../lib/server/lib/notifyUsersOnMessage';

const filterUsersInRoom = async ({ roomId, userIds, room }: { roomId: string; userIds: string[]; room: IRoom | null }) => {
if (!userIds.length || !room) {
return [];
}

if (room.t === 'd') {
return userIds.filter((userId) => room.uids?.includes(userId));
}

if (room.t === 'p' || room.t === 'c') {
const subscriptions = await Subscriptions.findByRoomIdAndUserIds(roomId, userIds, {
projection: { u: 1 },
}).toArray();

return subscriptions.map((sub) => sub.u._id);
}

return [];
};

export async function reply({ tmid }: { tmid?: string }, message: IMessage, parentMessage: IMessage, followers: string[]) {
if (!tmid || isEditedMessage(message)) {
return false;
}

const { rid, ts, u } = message;
const room = await Rooms.findOneById(rid);

const { toAll, toHere, mentionIds } = await getMentions(message);

const addToReplies = [
...new Set([
...followers,
...mentionIds,
...(Array.isArray(parentMessage.replies) && parentMessage.replies.length ? [u._id] : [parentMessage.u._id, u._id]),
]),
];

await Messages.updateRepliesByThreadId(tmid, addToReplies, ts);

const [highlightsUids, threadFollowers] = await Promise.all([
getUserIdsFromHighlights(rid, message),
Messages.getThreadFollowsByThreadId(tmid),
]);

const threadFollowersUids = threadFollowers?.filter((userId) => userId !== u._id && !mentionIds.includes(userId)) || [];
const [followersInRoom, mentionIdsInRoom, highlightsUidsInRoom, threadFollowersInRoom] = await Promise.all([
filterUsersInRoom({ roomId: rid, userIds: followers, room }),
filterUsersInRoom({ roomId: rid, userIds: mentionIds, room }),
filterUsersInRoom({ roomId: rid, userIds: highlightsUids, room }),
filterUsersInRoom({ roomId: rid, userIds: threadFollowers || [], room }),
]);

const baseReplyUsers = Array.isArray(parentMessage.replies) && parentMessage.replies.length ? [u._id] : [parentMessage.u._id, u._id];

const baseReplyUsersInRoom = await filterUsersInRoom({
roomId: rid,
userIds: baseReplyUsers,
room,
});

const addToReplies = [...new Set([...followersInRoom, ...mentionIdsInRoom, ...baseReplyUsersInRoom])];

await Messages.updateRepliesByThreadId(tmid, addToReplies, ts);

const threadFollowersUids = threadFollowersInRoom.filter((userId) => userId !== u._id && !mentionIdsInRoom.includes(userId));

// Notify everyone involved in the thread
const notifyOptions = toAll || toHere ? { groupMention: true } : {};

// Notify message mentioned users and highlights
const mentionedUsers = [...new Set([...mentionIds, ...highlightsUids])];
// Notify everyone involved in the thread
const mentionedUsers = [...new Set([...mentionIdsInRoom, ...highlightsUidsInRoom])];

const promises = [
const promises: Promise<unknown>[] = [
ReadReceipts.setAsThreadById(tmid),
Subscriptions.addUnreadThreadByRoomIdAndUserIds(rid, threadFollowersUids, tmid, notifyOptions),
];

if (mentionedUsers.length) {
promises.push(Subscriptions.addUnreadThreadByRoomIdAndUserIds(rid, mentionedUsers, tmid, { userMention: true }));
promises.push(
Subscriptions.addUnreadThreadByRoomIdAndUserIds(rid, mentionedUsers, tmid, {
userMention: true,
}),
);
}

if (highlightsUids.length) {
if (highlightsUidsInRoom.length) {
promises.push(
Subscriptions.setAlertForRoomIdAndUserIds(rid, highlightsUids),
Subscriptions.setOpenForRoomIdAndUserIds(rid, highlightsUids),
Subscriptions.setAlertForRoomIdAndUserIds(rid, highlightsUidsInRoom),
Subscriptions.setOpenForRoomIdAndUserIds(rid, highlightsUidsInRoom),
);
}

await Promise.allSettled(promises);

void notifyOnSubscriptionChangedByRoomIdAndUserIds(rid, [...threadFollowersUids, ...mentionedUsers, ...highlightsUids]);
void notifyOnSubscriptionChangedByRoomIdAndUserIds(rid, [...threadFollowersUids, ...mentionedUsers, ...highlightsUidsInRoom]);
}

export async function follow({ tmid, uid }: { tmid: string; uid: string }) {
Expand Down
Loading