Skip to content

Commit

Permalink
Fix threaded cache receipt when event holds multiple receipts (#3026)
Browse files Browse the repository at this point in the history
  • Loading branch information
Germain authored Jan 6, 2023
1 parent bb23df9 commit 896f622
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 16 deletions.
46 changes: 46 additions & 0 deletions spec/integ/matrix-client-syncing.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1543,6 +1543,52 @@ describe("MatrixClient syncing", () => {
});
});
});

it("only replays receipts relevant to the current context", async () => {
const THREAD_ID = "$unknownthread:localhost";

const receipt = {
type: "m.receipt",
room_id: "!foo:bar",
content: {
"$event1:localhost": {
[ReceiptType.Read]: {
"@alice:localhost": { ts: 666, thread_id: THREAD_ID },
},
},
"$otherevent:localhost": {
[ReceiptType.Read]: {
"@alice:localhost": { ts: 999, thread_id: "$otherthread:localhost" },
},
},
},
};
syncData.rooms.join[roomOne].ephemeral.events = [receipt];

httpBackend!.when("GET", "/sync").respond(200, syncData);
client!.startClient();

return Promise.all([httpBackend!.flushAllExpected(), awaitSyncEvent()]).then(() => {
const room = client?.getRoom(roomOne);
expect(room).toBeInstanceOf(Room);

expect(room?.cachedThreadReadReceipts.has(THREAD_ID)).toBe(true);

const thread = room!.createThread(THREAD_ID, undefined, [], true);

expect(room?.cachedThreadReadReceipts.has(THREAD_ID)).toBe(false);

const receipt = thread.getReadReceiptForUserId("@alice:localhost");

expect(receipt).toStrictEqual({
data: {
thread_id: "$unknownthread:localhost",
ts: 666,
},
eventId: "$event1:localhost",
});
});
});
});

describe("of a room", () => {
Expand Down
8 changes: 8 additions & 0 deletions src/@types/read_receipts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,11 @@ export type Receipts = {
[userId: string]: [WrappedReceipt | null, WrappedReceipt | null]; // Pair<real receipt, synthetic receipt> (both nullable)
};
};

export type CachedReceiptStructure = {
eventId: string;
receiptType: string | ReceiptType;
userId: string;
receipt: Receipt;
synthetic: boolean;
};
12 changes: 9 additions & 3 deletions src/models/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,13 @@ import {
FILTER_RELATED_BY_SENDERS,
ThreadFilterType,
} from "./thread";
import { MAIN_ROOM_TIMELINE, Receipt, ReceiptContent, ReceiptType } from "../@types/read_receipts";
import {
CachedReceiptStructure,
MAIN_ROOM_TIMELINE,
Receipt,
ReceiptContent,
ReceiptType,
} from "../@types/read_receipts";
import { IStateEventWithRoomId } from "../@types/search";
import { RelationsContainer } from "./relations-container";
import { ReadReceipt, synthesizeReceipt } from "./read-receipt";
Expand Down Expand Up @@ -302,7 +308,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
private txnToEvent: Record<string, MatrixEvent> = {}; // Pending in-flight requests { string: MatrixEvent }
private notificationCounts: NotificationCount = {};
private readonly threadNotifications = new Map<string, NotificationCount>();
public readonly cachedThreadReadReceipts = new Map<string, { event: MatrixEvent; synthetic: boolean }[]>();
public readonly cachedThreadReadReceipts = new Map<string, CachedReceiptStructure[]>();
private readonly timelineSets: EventTimelineSet[];
public readonly threadsTimelineSets: EventTimelineSet[] = [];
// any filtered timeline sets we're maintaining for this room
Expand Down Expand Up @@ -2718,7 +2724,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
// when the thread is created
this.cachedThreadReadReceipts.set(receipt.thread_id!, [
...(this.cachedThreadReadReceipts.get(receipt.thread_id!) ?? []),
{ event, synthetic },
{ eventId, receiptType, userId, receipt, synthetic },
]);
}
});
Expand Down
18 changes: 5 additions & 13 deletions src/models/thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { RoomState } from "./room-state";
import { ServerControlledNamespacedValue } from "../NamespacedValue";
import { logger } from "../logger";
import { ReadReceipt } from "./read-receipt";
import { Receipt, ReceiptContent, ReceiptType } from "../@types/read_receipts";
import { CachedReceiptStructure, ReceiptType } from "../@types/read_receipts";

export enum ThreadEvent {
New = "Thread.new",
Expand All @@ -50,7 +50,7 @@ interface IThreadOpts {
room: Room;
client: MatrixClient;
pendingEventOrdering?: PendingEventOrdering;
receipts?: { event: MatrixEvent; synthetic: boolean }[];
receipts?: CachedReceiptStructure[];
}

export enum FeatureSupport {
Expand Down Expand Up @@ -317,17 +317,9 @@ export class Thread extends ReadReceipt<EmittedEvents, EventHandlerMap> {
* and apply them to the current thread
* @param receipts - A collection of the receipts cached from initial sync
*/
private processReceipts(receipts: { event: MatrixEvent; synthetic: boolean }[] = []): void {
for (const { event, synthetic } of receipts) {
const content = event.getContent<ReceiptContent>();
Object.keys(content).forEach((eventId: string) => {
Object.keys(content[eventId]).forEach((receiptType: ReceiptType | string) => {
Object.keys(content[eventId][receiptType]).forEach((userId: string) => {
const receipt = content[eventId][receiptType][userId] as Receipt;
this.addReceiptToStructure(eventId, receiptType as ReceiptType, userId, receipt, synthetic);
});
});
});
private processReceipts(receipts: CachedReceiptStructure[] = []): void {
for (const { eventId, receiptType, userId, receipt, synthetic } of receipts) {
this.addReceiptToStructure(eventId, receiptType as ReceiptType, userId, receipt, synthetic);
}
}

Expand Down

0 comments on commit 896f622

Please sign in to comment.