Skip to content

Commit 9eb7290

Browse files
authored
Merge pull request #2583 from matrix-org/kegan/sync-v3
sliding sync bugfix: ensure history is treated as history and not live events
2 parents 8f13df2 + 2728d74 commit 9eb7290

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

spec/integ/sliding-sync-sdk.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
import { SlidingSyncSdk } from "../../src/sliding-sync-sdk";
2929
import { SyncState } from "../../src/sync";
3030
import { IStoredClientOpts } from "../../src/client";
31+
import { logger } from "../../src/logger";
3132

3233
describe("SlidingSyncSdk", () => {
3334
let client: MatrixClient = null;
@@ -372,6 +373,36 @@ describe("SlidingSyncSdk", () => {
372373
gotRoom.getUnreadNotificationCount(NotificationCountType.Total),
373374
).toEqual(1);
374375
});
376+
377+
// Regression test for a bug which caused the timeline entries to be out-of-order
378+
// when the same room appears twice with different timeline limits. E.g appears in
379+
// the list with timeline_limit:1 then appears again as a room subscription with
380+
// timeline_limit:50
381+
it("can return history with a larger timeline_limit", async () => {
382+
const timeline = data[roomA].timeline;
383+
const oldTimeline = [
384+
mkOwnEvent(EventType.RoomMessage, { body: "old event A" }),
385+
mkOwnEvent(EventType.RoomMessage, { body: "old event B" }),
386+
mkOwnEvent(EventType.RoomMessage, { body: "old event C" }),
387+
...timeline,
388+
];
389+
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomA, {
390+
timeline: oldTimeline,
391+
required_state: [],
392+
name: data[roomA].name,
393+
initial: true, // e.g requested via room subscription
394+
});
395+
const gotRoom = client.getRoom(roomA);
396+
expect(gotRoom).toBeDefined();
397+
398+
logger.log("want:", oldTimeline.map((e) => (e.type + " : " + (e.content || {}).body)));
399+
logger.log("got:", gotRoom.getLiveTimeline().getEvents().map(
400+
(e) => (e.getType() + " : " + e.getContent().body)),
401+
);
402+
403+
// we expect the timeline now to be oldTimeline (so the old events are in fact old)
404+
assertTimelineEvents(gotRoom.getLiveTimeline().getEvents(), oldTimeline);
405+
});
375406
});
376407
});
377408
});

src/sliding-sync-sdk.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,9 +406,51 @@ export class SlidingSyncSdk {
406406
// this helps large account to speed up faster
407407
// room::decryptCriticalEvent is in charge of decrypting all the events
408408
// required for a client to function properly
409-
const timelineEvents = mapEvents(this.client, room.roomId, roomData.timeline, false);
409+
let timelineEvents = mapEvents(this.client, room.roomId, roomData.timeline, false);
410410
const ephemeralEvents = []; // TODO this.mapSyncEventsFormat(joinObj.ephemeral);
411411

412+
// TODO: handle threaded / beacon events
413+
414+
if (roomData.initial) {
415+
// we should not know about any of these timeline entries if this is a genuinely new room.
416+
// If we do, then we've effectively done scrollback (e.g requesting timeline_limit: 1 for
417+
// this room, then timeline_limit: 50).
418+
const knownEvents = new Set<string>();
419+
room.getLiveTimeline().getEvents().forEach((e) => {
420+
knownEvents.add(e.getId());
421+
});
422+
// all unknown events BEFORE a known event must be scrollback e.g:
423+
// D E <-- what we know
424+
// A B C D E F <-- what we just received
425+
// means:
426+
// A B C <-- scrollback
427+
// D E <-- dupes
428+
// F <-- new event
429+
// We bucket events based on if we have seen a known event yet.
430+
const oldEvents: MatrixEvent[] = [];
431+
const newEvents: MatrixEvent[] = [];
432+
let seenKnownEvent = false;
433+
for (let i = timelineEvents.length-1; i >= 0; i--) {
434+
const recvEvent = timelineEvents[i];
435+
if (knownEvents.has(recvEvent.getId())) {
436+
seenKnownEvent = true;
437+
continue; // don't include this event, it's a dupe
438+
}
439+
if (seenKnownEvent) {
440+
// old -> new
441+
oldEvents.push(recvEvent);
442+
} else {
443+
// old -> new
444+
newEvents.unshift(recvEvent);
445+
}
446+
}
447+
timelineEvents = newEvents;
448+
if (oldEvents.length > 0) {
449+
// old events are scrollback, insert them now
450+
room.addEventsToTimeline(oldEvents, true, room.getLiveTimeline(), roomData.prev_batch);
451+
}
452+
}
453+
412454
const encrypted = this.client.isRoomEncrypted(room.roomId);
413455
// we do this first so it's correct when any of the events fire
414456
if (roomData.notification_count != null) {

0 commit comments

Comments
 (0)