diff --git a/.changeset/light-beans-sparkle.md b/.changeset/light-beans-sparkle.md new file mode 100644 index 0000000000000..3bf34f4584491 --- /dev/null +++ b/.changeset/light-beans-sparkle.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixes an issue, where multiple reconnections would subscribe multiple times to the same stream, only a frontend issue, since the stream cache prevents sending multiple times to the backend, but does not prevent running the callback multiple times diff --git a/apps/meteor/client/hooks/useLoadRoomForAllowedAnonymousRead.ts b/apps/meteor/client/hooks/useLoadRoomForAllowedAnonymousRead.ts index b53ed9dde343d..3e3b7e520b9c2 100644 --- a/apps/meteor/client/hooks/useLoadRoomForAllowedAnonymousRead.ts +++ b/apps/meteor/client/hooks/useLoadRoomForAllowedAnonymousRead.ts @@ -11,6 +11,10 @@ export const useLoadRoomForAllowedAnonymousRead = () => { if (!userId && accountsAllowAnonymousRead === true) { CachedChatRoom.init(); CachedChatSubscription.ready.set(true); + return () => { + CachedChatRoom.ready.set(false); + CachedChatSubscription.ready.set(false); + }; } }, [accountsAllowAnonymousRead, userId]); }; diff --git a/apps/meteor/client/lib/cachedCollections/CachedCollection.ts b/apps/meteor/client/lib/cachedCollections/CachedCollection.ts index c9664d46364f4..b1c3f37dc6d21 100644 --- a/apps/meteor/client/lib/cachedCollections/CachedCollection.ts +++ b/apps/meteor/client/lib/cachedCollections/CachedCollection.ts @@ -199,8 +199,8 @@ export abstract class CachedCollection { this.collection.state.replaceAll([]); } - protected async setupListener() { - sdk.stream(this.eventType, [this.eventName], (async (action: 'removed' | 'changed', record: U) => { + protected setupListener() { + return sdk.stream(this.eventType, [this.eventName], (async (action: 'removed' | 'changed', record: U) => { this.log('record received', action, record); await this.handleRecordEvent(action, record); }) as (...args: unknown[]) => void); @@ -295,7 +295,9 @@ export abstract class CachedCollection { return true; } - async init() { + private listenerUnsubscriber: (() => void) | undefined; + + private async performInitialization() { if (await this.loadFromCache()) { this.trySync(); } else { @@ -318,7 +320,34 @@ export abstract class CachedCollection { } }); - return this.setupListener(); + const subscription = this.setupListener(); + this.listenerUnsubscriber = () => { + subscription.stop(); + this.listenerUnsubscriber = undefined; + }; + } + + private initializationPromise: Promise | undefined; + + init() { + if (this.initializationPromise) { + return this.initializationPromise; + } + + this.initializationPromise = this.performInitialization().finally(() => { + this.initializationPromise = undefined; + }); + + return this.initializationPromise; + } + + async release() { + if (this.initializationPromise) { + await this.initializationPromise; + } + + this.listenerUnsubscriber?.(); + this.ready.set(false); } private reconnectionComputation: Tracker.Computation | undefined; @@ -353,7 +382,7 @@ export class PrivateCachedCollection extends }); Accounts.onLogout(() => { - this.ready.set(false); + this.release(); }); } } diff --git a/apps/meteor/client/lib/cachedCollections/DocumentMapStore.ts b/apps/meteor/client/lib/cachedCollections/DocumentMapStore.ts index 7ad258bce9cb0..ec848dd2cebe8 100644 --- a/apps/meteor/client/lib/cachedCollections/DocumentMapStore.ts +++ b/apps/meteor/client/lib/cachedCollections/DocumentMapStore.ts @@ -249,8 +249,9 @@ export const createDocumentMapStore = ({ const records = new Map(); for (const record of state.records.values()) { if (predicate(record)) { - if (onInvalidate) affected.push(record); - records.set(record._id, modifier(record)); + const newRecord = modifier(record); + records.set(record._id, newRecord); + if (onInvalidate) affected.push(newRecord); } else { records.set(record._id, record); } @@ -267,8 +268,9 @@ export const createDocumentMapStore = ({ for await (const record of get().records.values()) { if (predicate(record)) { - if (onInvalidate) affected.push(record); - records.set(record._id, await modifier(record)); + const newRecord = await modifier(record); + records.set(record._id, newRecord); + if (onInvalidate) affected.push(newRecord); } else { records.set(record._id, record); } diff --git a/apps/meteor/client/lib/settings/PrivateSettingsCachedCollection.ts b/apps/meteor/client/lib/settings/PrivateSettingsCachedCollection.ts index 74cb76ac9002a..95486509875fa 100644 --- a/apps/meteor/client/lib/settings/PrivateSettingsCachedCollection.ts +++ b/apps/meteor/client/lib/settings/PrivateSettingsCachedCollection.ts @@ -11,12 +11,16 @@ class PrivateSettingsCachedCollection extends PrivateCachedCollection }); } - async setupListener(): Promise { - sdk.stream('notify-logged', [this.eventName as 'private-settings-changed'], async (t: string, { _id, ...record }: { _id: string }) => { - this.log('record received', t, { _id, ...record }); - this.collection.update({ _id }, { $set: record }, { upsert: true }); - this.sync(); - }); + override setupListener() { + return sdk.stream( + 'notify-logged', + [this.eventName as 'private-settings-changed'], + async (t: string, { _id, ...record }: { _id: string }) => { + this.log('record received', t, { _id, ...record }); + this.collection.update({ _id }, { $set: record }, { upsert: true }); + this.sync(); + }, + ); } }