RocketChat/Rocket.Chat

View on GitHub
apps/meteor/app/message-mark-as-unread/server/unreadMessages.ts

Summary

Maintainability
B
4 hrs
Test Coverage
import type { IMessage, IRoom } from '@rocket.chat/core-typings';
import type { ServerMethods } from '@rocket.chat/ddp-client';
import { Messages, Subscriptions } from '@rocket.chat/models';
import { Meteor } from 'meteor/meteor';

import { notifyOnSubscriptionChangedByRoomIdAndUserId } from '../../lib/server/lib/notifyListener';
import logger from './logger';

declare module '@rocket.chat/ddp-client' {
    // eslint-disable-next-line @typescript-eslint/naming-convention
    interface ServerMethods {
        unreadMessages(firstUnreadMessage?: IMessage, room?: IRoom['_id']): void;
    }
}

Meteor.methods<ServerMethods>({
    async unreadMessages(firstUnreadMessage, room) {
        const userId = Meteor.userId();
        if (!userId) {
            throw new Meteor.Error('error-invalid-user', 'Invalid user', {
                method: 'unreadMessages',
            });
        }

        if (room && typeof room === 'string') {
            const lastMessage = (
                await Messages.findVisibleByRoomId(room, {
                    limit: 1,
                    sort: { ts: -1 },
                }).toArray()
            )[0];

            if (!lastMessage) {
                throw new Meteor.Error('error-no-message-for-unread', 'There are no messages to mark unread', {
                    method: 'unreadMessages',
                    action: 'Unread_messages',
                });
            }

            const setAsUnreadResponse = await Subscriptions.setAsUnreadByRoomIdAndUserId(lastMessage.rid, userId, lastMessage.ts);
            if (setAsUnreadResponse.modifiedCount) {
                void notifyOnSubscriptionChangedByRoomIdAndUserId(lastMessage.rid, userId);
            }

            return;
        }

        if (typeof firstUnreadMessage?._id !== 'string') {
            throw new Meteor.Error('error-action-not-allowed', 'Not allowed', {
                method: 'unreadMessages',
                action: 'Unread_messages',
            });
        }

        const originalMessage = await Messages.findOneById(firstUnreadMessage._id, {
            projection: {
                u: 1,
                rid: 1,
                file: 1,
                ts: 1,
            },
        });
        if (!originalMessage || userId === originalMessage.u._id) {
            throw new Meteor.Error('error-action-not-allowed', 'Not allowed', {
                method: 'unreadMessages',
                action: 'Unread_messages',
            });
        }
        const lastSeen = (await Subscriptions.findOneByRoomIdAndUserId(originalMessage.rid, userId))?.ls;
        if (!lastSeen) {
            throw new Meteor.Error('error-subscription-not-found', 'Subscription not found', {
                method: 'unreadMessages',
                action: 'Unread_messages',
            });
        }

        if (firstUnreadMessage.ts >= lastSeen) {
            return logger.debug('Provided message is already marked as unread');
        }

        logger.debug(`Updating unread message of ${originalMessage.ts} as the first unread`);
        const setAsUnreadResponse = await Subscriptions.setAsUnreadByRoomIdAndUserId(originalMessage.rid, userId, originalMessage.ts);
        if (setAsUnreadResponse.modifiedCount) {
            void notifyOnSubscriptionChangedByRoomIdAndUserId(originalMessage.rid, userId);
        }
    },
});