Skip to content

Commit 2dd06e3

Browse files
author
Germain
authored
Fix infinite loop when restoring cached read receipts (#2963)
1 parent fc501de commit 2dd06e3

File tree

2 files changed

+29
-12
lines changed

2 files changed

+29
-12
lines changed

src/models/room.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2079,8 +2079,13 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
20792079
room: this,
20802080
client: this.client,
20812081
pendingEventOrdering: this.opts.pendingEventOrdering,
2082+
receipts: this.cachedThreadReadReceipts.get(threadId) ?? [],
20822083
});
20832084

2085+
// All read receipts should now come down from sync, we do not need to keep
2086+
// a reference to the cached receipts anymore.
2087+
this.cachedThreadReadReceipts.delete(threadId);
2088+
20842089
// This is necessary to be able to jump to events in threads:
20852090
// If we jump to an event in a thread where neither the event, nor the root,
20862091
// nor any thread event are loaded yet, we'll load the event as well as the thread root, create the thread,
@@ -2110,17 +2115,6 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
21102115
if (this.threadsReady) {
21112116
this.updateThreadRootEvents(thread, toStartOfTimeline, false);
21122117
}
2113-
2114-
// Pulling all the cached thread read receipts we've discovered when we
2115-
// did an initial sync, and applying them to the thread now that it exists
2116-
// on the client side
2117-
if (this.cachedThreadReadReceipts.has(threadId)) {
2118-
for (const { event, synthetic } of this.cachedThreadReadReceipts.get(threadId)!) {
2119-
this.addReceipt(event, synthetic);
2120-
}
2121-
this.cachedThreadReadReceipts.delete(threadId);
2122-
}
2123-
21242118
this.emit(ThreadEvent.New, thread, toStartOfTimeline);
21252119

21262120
return thread;

src/models/thread.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { RoomState } from "./room-state";
2727
import { ServerControlledNamespacedValue } from "../NamespacedValue";
2828
import { logger } from "../logger";
2929
import { ReadReceipt } from "./read-receipt";
30-
import { ReceiptType } from "../@types/read_receipts";
30+
import { Receipt, ReceiptContent, ReceiptType } from "../@types/read_receipts";
3131

3232
export enum ThreadEvent {
3333
New = "Thread.new",
@@ -50,6 +50,7 @@ interface IThreadOpts {
5050
room: Room;
5151
client: MatrixClient;
5252
pendingEventOrdering?: PendingEventOrdering;
53+
receipts?: { event: MatrixEvent; synthetic: boolean }[];
5354
}
5455

5556
export enum FeatureSupport {
@@ -127,6 +128,8 @@ export class Thread extends ReadReceipt<EmittedEvents, EventHandlerMap> {
127128
this.room.on(RoomEvent.LocalEchoUpdated, this.onEcho);
128129
this.timelineSet.on(RoomEvent.Timeline, this.onTimelineEvent);
129130

131+
this.processReceipts(opts.receipts);
132+
130133
// even if this thread is thought to be originating from this client, we initialise it as we may be in a
131134
// gappy sync and a thread around this event may already exist.
132135
this.updateThreadMetadata();
@@ -284,6 +287,26 @@ export class Thread extends ReadReceipt<EmittedEvents, EventHandlerMap> {
284287
this.timeline = this.events;
285288
}
286289

290+
/**
291+
* Processes the receipts that were caught during initial sync
292+
* When clients become aware of a thread, they try to retrieve those read receipts
293+
* and apply them to the current thread
294+
* @param receipts - A collection of the receipts cached from initial sync
295+
*/
296+
private processReceipts(receipts: { event: MatrixEvent; synthetic: boolean }[] = []): void {
297+
for (const { event, synthetic } of receipts) {
298+
const content = event.getContent<ReceiptContent>();
299+
Object.keys(content).forEach((eventId: string) => {
300+
Object.keys(content[eventId]).forEach((receiptType: ReceiptType | string) => {
301+
Object.keys(content[eventId][receiptType]).forEach((userId: string) => {
302+
const receipt = content[eventId][receiptType][userId] as Receipt;
303+
this.addReceiptToStructure(eventId, receiptType as ReceiptType, userId, receipt, synthetic);
304+
});
305+
});
306+
});
307+
}
308+
}
309+
287310
private getRootEventBundledRelationship(rootEvent = this.rootEvent): IThreadBundledRelationship | undefined {
288311
return rootEvent?.getServerAggregatedRelation<IThreadBundledRelationship>(THREAD_RELATION_TYPE.name);
289312
}

0 commit comments

Comments
 (0)