Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quiet-cars-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

Fixes client slowdown for users with large amount of channels
53 changes: 33 additions & 20 deletions apps/meteor/client/views/root/hooks/loggedIn/useUnread.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { manageFavicon } from '@rocket.chat/favicon';
import { useSession, useSessionDispatch, useUserPreference, useUserSubscriptions } from '@rocket.chat/ui-contexts';
import { useEffect } from 'react';
import { useEffect, useRef } from 'react';

import { useFireGlobalEvent } from '../../../../hooks/useFireGlobalEvent';

const query = { open: { $ne: false }, hideUnreadStatus: { $ne: true }, archived: { $ne: true } };
const options = { fields: { unread: 1, alert: 1, rid: 1, t: 1, name: 1, ls: 1, unreadAlert: 1, fname: 1, prid: 1 } };
const updateFavicon = manageFavicon();

type UnreadData = { unread: number; alert: boolean | undefined; unreadAlert: string | undefined };

export const useUnread = () => {
const unreadAlertEnabled = useUserPreference('unreadAlert');
const setUnread = useSessionDispatch('unread');
Expand All @@ -18,37 +20,48 @@ export const useUnread = () => {

const subscriptions = useUserSubscriptions(query, options);

// We keep a lightweight snapshot of the last emitted per-subscription unread state so we only
// fire "unread-changed-by-subscription" for subscriptions whose unread-relevant fields changed.
// Previously we emitted one global event per subscription on ANY change, which scaled O(N)
// with the user subscription count (thousands) for every single message event, dominating CPU.
const prevSubsRef = useRef(new Map<string, UnreadData>());

useEffect(() => {
let unreadAlert: false | '•' = false;
let badgeIndicator: false | '•' = false;
let unreadCount = 0;
const nextSnapshot = new Map<string, UnreadData>();

const unreadCount = subscriptions.reduce((ret, subscription) => {
fireEventUnreadChangedBySubscription(subscription);
for (const subscription of subscriptions) {
const { rid, unread: unreadValue, alert, unreadAlert: subscriptionUnreadAlert } = subscription;
const prev = prevSubsRef.current.get(rid);
// Emit per-sub event only if something that influences unread UI changed.
if (!prev || prev.unread !== unreadValue || prev.alert !== alert || prev.unreadAlert !== subscriptionUnreadAlert) {
fireEventUnreadChangedBySubscription(subscription);
}
nextSnapshot.set(rid, { unread: unreadValue, alert, unreadAlert: subscriptionUnreadAlert });

if (subscription.alert || subscription.unread > 0) {
// Increment the total unread count.
if (subscription.alert === true && subscription.unreadAlert !== 'nothing') {
if (subscription.unreadAlert === 'all' || unreadAlertEnabled !== false) {
unreadAlert = '•';
if (alert || unreadValue > 0) {
if (alert === true && subscriptionUnreadAlert !== 'nothing') {
if (subscriptionUnreadAlert === 'all' || unreadAlertEnabled !== false) {
badgeIndicator = '•';
}
}
return ret + subscription.unread;
unreadCount += unreadValue;
}
return ret;
}, 0);
}

prevSubsRef.current = nextSnapshot; // swap snapshot

if (unreadCount > 0) {
if (unreadCount > 999) {
setUnread('999+');
} else {
setUnread(unreadCount);
}
} else if (unreadAlert !== false) {
setUnread(unreadAlert);
setUnread(unreadCount > 999 ? '999+' : unreadCount);
} else if (badgeIndicator !== false) {
setUnread(badgeIndicator);
} else {
setUnread('');
}

fireEventUnreadChanged(unreadCount);
}, [setUnread, unread, subscriptions, unreadAlertEnabled, fireEventUnreadChangedBySubscription, fireEventUnreadChanged]);
}, [setUnread, subscriptions, unreadAlertEnabled, fireEventUnreadChangedBySubscription, fireEventUnreadChanged]);

useEffect(() => {
updateFavicon(unread);
Expand Down
Loading