1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-07-31 15:24:23 +03:00

Support MSC4222 state_after (#4487)

* WIP support for state_after

* Fix sliding sync sdk / embedded tests

* Allow both state & state_after to be undefined

Since it must have allowed state to be undefined previously: the test
had it as such.

* Fix limited sync handling

* Need to use state_after being undefined

if state can be undefined anyway

* Make sliding sync sdk tests pass

* Remove deprecated interfaces & backwards-compat code

* Remove useless assignment

* Use updates unstable prefix

* Clarify docs

* Remove additional semi-backwards compatible overload

* Update unstable prefixes

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix test

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add test for MSC4222 behaviour

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Improve coverage

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix tests

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tidy

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add comments to explain why things work as they are.

* Fix sync accumulator for state_after sync handling

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add tests

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Revert "Fix room state being updated with old (now overwritten) state and emitting for those updates. (#4242)"

This reverts commit 957329b218.

* Fix Sync Accumulator toJSON putting start timeline state in state_after field

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update tests

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add test case

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: Hugh Nimmo-Smith <hughns@matrix.org>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: Timo <toger5@hotmail.de>
This commit is contained in:
David Baker
2024-11-27 11:40:41 +00:00
committed by GitHub
parent 66f099b2e7
commit 5bcd26e506
32 changed files with 1343 additions and 735 deletions

View File

@ -1327,7 +1327,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
const syncResponse = getSyncResponse(["@bob:xyz"]);
// Every 2 messages in the room, the session should be rotated
syncResponse.rooms[Category.Join][ROOM_ID].state.events[0].content = {
syncResponse.rooms[Category.Join][ROOM_ID].state!.events[0].content = {
algorithm: "m.megolm.v1.aes-sha2",
rotation_period_msgs: 2,
};
@ -1383,7 +1383,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
const oneHourInMs = 60 * 60 * 1000;
// Every 1h the session should be rotated
syncResponse.rooms[Category.Join][ROOM_ID].state.events[0].content = {
syncResponse.rooms[Category.Join][ROOM_ID].state!.events[0].content = {
algorithm: "m.megolm.v1.aes-sha2",
rotation_period_ms: oneHourInMs,
};

View File

@ -1144,7 +1144,7 @@ describe("MatrixClient event timelines", function () {
const prom = emitPromise(room, ThreadEvent.Update);
// Assume we're seeing the reply while loading backlog
await room.addLiveEvents([THREAD_REPLY2]);
await room.addLiveEvents([THREAD_REPLY2], { addToState: false });
httpBackend
.when(
"GET",
@ -1155,7 +1155,7 @@ describe("MatrixClient event timelines", function () {
});
await flushHttp(prom);
// but while loading the metadata, a new reply has arrived
await room.addLiveEvents([THREAD_REPLY3]);
await room.addLiveEvents([THREAD_REPLY3], { addToState: false });
const thread = room.getThread(THREAD_ROOT_UPDATED.event_id!)!;
// then the events should still be all in the right order
expect(thread.events.map((it) => it.getId())).toEqual([
@ -1247,7 +1247,7 @@ describe("MatrixClient event timelines", function () {
const prom = emitPromise(room, ThreadEvent.Update);
// Assume we're seeing the reply while loading backlog
await room.addLiveEvents([THREAD_REPLY2]);
await room.addLiveEvents([THREAD_REPLY2], { addToState: false });
httpBackend
.when(
"GET",
@ -1263,7 +1263,7 @@ describe("MatrixClient event timelines", function () {
});
await flushHttp(prom);
// but while loading the metadata, a new reply has arrived
await room.addLiveEvents([THREAD_REPLY3]);
await room.addLiveEvents([THREAD_REPLY3], { addToState: false });
const thread = room.getThread(THREAD_ROOT_UPDATED.event_id!)!;
// then the events should still be all in the right order
expect(thread.events.map((it) => it.getId())).toEqual([
@ -1560,7 +1560,7 @@ describe("MatrixClient event timelines", function () {
thread.initialEventsFetched = true;
const prom = emitPromise(room, ThreadEvent.NewReply);
respondToEvent(THREAD_ROOT_UPDATED);
await room.addLiveEvents([THREAD_REPLY2]);
await room.addLiveEvents([THREAD_REPLY2], { addToState: false });
await httpBackend.flushAllExpected();
await prom;
expect(thread.length).toBe(2);
@ -1685,7 +1685,7 @@ describe("MatrixClient event timelines", function () {
thread.initialEventsFetched = true;
const prom = emitPromise(room, ThreadEvent.Update);
respondToEvent(THREAD_ROOT_UPDATED);
await room.addLiveEvents([THREAD_REPLY_REACTION]);
await room.addLiveEvents([THREAD_REPLY_REACTION], { addToState: false });
await httpBackend.flushAllExpected();
await prom;
expect(thread.length).toBe(1); // reactions don't count towards the length of a thread

View File

@ -168,14 +168,17 @@ describe("MatrixClient", function () {
type: "test",
content: {},
});
room.addLiveEvents([
utils.mkMembership({
user: userId,
room: roomId,
mship: KnownMembership.Join,
event: true,
}),
]);
room.addLiveEvents(
[
utils.mkMembership({
user: userId,
room: roomId,
mship: KnownMembership.Join,
event: true,
}),
],
{ addToState: true },
);
httpBackend.verifyNoOutstandingRequests();
store.storeRoom(room);
@ -188,14 +191,17 @@ describe("MatrixClient", function () {
const roomId = "!roomId:server";
const roomAlias = "#my-fancy-room:server";
const room = new Room(roomId, client, userId);
room.addLiveEvents([
utils.mkMembership({
user: userId,
room: roomId,
mship: KnownMembership.Join,
event: true,
}),
]);
room.addLiveEvents(
[
utils.mkMembership({
user: userId,
room: roomId,
mship: KnownMembership.Join,
event: true,
}),
],
{ addToState: true },
);
store.storeRoom(room);
// The method makes a request to resolve the alias
@ -275,14 +281,17 @@ describe("MatrixClient", function () {
content: {},
});
room.addLiveEvents([
utils.mkMembership({
user: userId,
room: roomId,
mship: KnownMembership.Knock,
event: true,
}),
]);
room.addLiveEvents(
[
utils.mkMembership({
user: userId,
room: roomId,
mship: KnownMembership.Knock,
event: true,
}),
],
{ addToState: true },
);
httpBackend.verifyNoOutstandingRequests();
store.storeRoom(room);

View File

@ -556,7 +556,7 @@ describe("MatrixClient syncing", () => {
});
it("should resolve incoming invites from /sync", () => {
syncData.rooms.join[roomOne].state.events.push(
syncData.rooms.join[roomOne].state!.events.push(
utils.mkMembership({
room: roomOne,
mship: KnownMembership.Invite,
@ -589,7 +589,7 @@ describe("MatrixClient syncing", () => {
name: "The Ghost",
}) as IMinimalEvent,
];
syncData.rooms.join[roomOne].state.events.push(
syncData.rooms.join[roomOne].state!.events.push(
utils.mkMembership({
room: roomOne,
mship: KnownMembership.Invite,
@ -617,7 +617,7 @@ describe("MatrixClient syncing", () => {
name: "The Ghost",
}) as IMinimalEvent,
];
syncData.rooms.join[roomOne].state.events.push(
syncData.rooms.join[roomOne].state!.events.push(
utils.mkMembership({
room: roomOne,
mship: KnownMembership.Invite,
@ -644,7 +644,7 @@ describe("MatrixClient syncing", () => {
});
it("should no-op if resolveInvitesToProfiles is not set", () => {
syncData.rooms.join[roomOne].state.events.push(
syncData.rooms.join[roomOne].state!.events.push(
utils.mkMembership({
room: roomOne,
mship: KnownMembership.Invite,
@ -1373,6 +1373,114 @@ describe("MatrixClient syncing", () => {
expect(stateEventEmitCount).toEqual(2);
});
});
describe("msc4222", () => {
const roomOneSyncOne = {
"timeline": {
events: [
utils.mkMessage({
room: roomOne,
user: otherUserId,
msg: "hello",
}),
],
},
"org.matrix.msc4222.state_after": {
events: [
utils.mkEvent({
type: "m.room.name",
room: roomOne,
user: otherUserId,
content: {
name: "Initial room name",
},
}),
utils.mkMembership({
room: roomOne,
mship: KnownMembership.Join,
user: otherUserId,
}),
utils.mkMembership({
room: roomOne,
mship: KnownMembership.Join,
user: selfUserId,
}),
utils.mkEvent({
type: "m.room.create",
room: roomOne,
user: selfUserId,
content: {},
}),
],
},
};
const roomOneSyncTwo = {
"org.matrix.msc4222.state_after": {
events: [
utils.mkEvent({
type: "m.room.topic",
room: roomOne,
user: selfUserId,
content: { topic: "A new room topic" },
}),
],
},
"state": {
events: [
utils.mkEvent({
type: "m.room.name",
room: roomOne,
user: selfUserId,
content: { name: "A new room name" },
}),
],
},
};
it("should ignore state events in timeline when state_after is present", async () => {
httpBackend!.when("GET", "/sync").respond(200, {
rooms: {
join: { [roomOne]: roomOneSyncOne },
},
});
httpBackend!.when("GET", "/sync").respond(200, {
rooms: {
join: { [roomOne]: roomOneSyncTwo },
},
});
client!.startClient();
return Promise.all([httpBackend!.flushAllExpected(), awaitSyncEvent(2)]).then(() => {
const room = client!.getRoom(roomOne)!;
expect(room.name).toEqual("Initial room name");
expect(room.currentState.getStateEvents("m.room.topic", "")?.getContent().topic).toBe(
"A new room topic",
);
});
});
it("should respect state events in state_after for left rooms", async () => {
httpBackend!.when("GET", "/sync").respond(200, {
rooms: {
join: { [roomOne]: roomOneSyncOne },
},
});
httpBackend!.when("GET", "/sync").respond(200, {
rooms: {
leave: { [roomOne]: roomOneSyncTwo },
},
});
client!.startClient();
return Promise.all([httpBackend!.flushAllExpected(), awaitSyncEvent(2)]).then(() => {
const room = client!.getRoom(roomOne)!;
expect(room.name).toEqual("Initial room name");
expect(room.currentState.getStateEvents("m.room.topic", "")?.getContent().topic).toBe(
"A new room topic",
);
});
});
});
});
describe("timeline", () => {
@ -2274,6 +2382,57 @@ describe("MatrixClient syncing", () => {
}),
]);
});
describe("msc4222", () => {
it("should respect state events in state_after for left rooms", async () => {
httpBackend!.when("POST", "/filter").respond(200, {
filter_id: "another_id",
});
httpBackend!.when("GET", "/sync").respond(200, {
rooms: {
leave: {
[roomOne]: {
"org.matrix.msc4222.state_after": {
events: [
utils.mkEvent({
type: "m.room.topic",
room: roomOne,
user: selfUserId,
content: { topic: "A new room topic" },
}),
],
},
"state": {
events: [
utils.mkEvent({
type: "m.room.name",
room: roomOne,
user: selfUserId,
content: { name: "A new room name" },
}),
],
},
},
},
},
});
const [[room]] = await Promise.all([
client!.syncLeftRooms(),
// first flush the filter request; this will make syncLeftRooms make its /sync call
httpBackend!.flush("/filter").then(() => {
return httpBackend!.flushAllExpected();
}),
]);
expect(room.name).toEqual("Empty room");
expect(room.currentState.getStateEvents("m.room.topic", "")?.getContent().topic).toBe(
"A new room topic",
);
});
});
});
describe("peek", () => {

View File

@ -128,7 +128,7 @@ describe("MatrixClient syncing", () => {
const thread = mkThread({ room, client: client!, authorId: selfUserId, participantUserIds: [selfUserId] });
const threadReply = thread.events.at(-1)!;
await room.addLiveEvents([thread.rootEvent]);
await room.addLiveEvents([thread.rootEvent], { addToState: false });
// Initialize read receipt datastructure before testing the reaction
room.addReceiptToStructure(thread.rootEvent.getId()!, ReceiptType.Read, selfUserId, { ts: 1 }, false);

View File

@ -601,13 +601,13 @@ describe("SlidingSyncSdk", () => {
mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomId, {
initial: true,
name: "Room with Invite",
required_state: [],
timeline: [
required_state: [
mkOwnStateEvent(EventType.RoomCreate, {}, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Join }, selfUserId),
mkOwnStateEvent(EventType.RoomPowerLevels, { users: { [selfUserId]: 100 } }, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Invite }, invitee),
],
timeline: [],
});
await httpBackend!.flush("/profile", 1, 1000);
await emitPromise(client!, RoomMemberEvent.Name);
@ -921,13 +921,12 @@ describe("SlidingSyncSdk", () => {
const roomId = "!room:id";
mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomId, {
name: "Room with typing",
required_state: [],
timeline: [
required_state: [
mkOwnStateEvent(EventType.RoomCreate, {}, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Join }, selfUserId),
mkOwnStateEvent(EventType.RoomPowerLevels, { users: { [selfUserId]: 100 } }, ""),
mkOwnEvent(EventType.RoomMessage, { body: "hello" }),
],
timeline: [mkOwnEvent(EventType.RoomMessage, { body: "hello" })],
initial: true,
});
await emitPromise(client!, ClientEvent.Room);
@ -962,13 +961,12 @@ describe("SlidingSyncSdk", () => {
const roomId = "!room:id";
mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomId, {
name: "Room with typing",
required_state: [],
timeline: [
required_state: [
mkOwnStateEvent(EventType.RoomCreate, {}, ""),
mkOwnStateEvent(EventType.RoomMember, { membership: KnownMembership.Join }, selfUserId),
mkOwnStateEvent(EventType.RoomPowerLevels, { users: { [selfUserId]: 100 } }, ""),
mkOwnEvent(EventType.RoomMessage, { body: "hello" }),
],
timeline: [mkOwnEvent(EventType.RoomMessage, { body: "hello" })],
initial: true,
});
const room = client!.getRoom(roomId)!;

View File

@ -86,7 +86,7 @@ export function getSyncResponse(roomMembers: string[], roomId = TEST_ROOM_ID): I
};
for (let i = 0; i < roomMembers.length; i++) {
roomResponse.state.events.push(
roomResponse.state!.events.push(
mkMembershipCustom({
membership: KnownMembership.Join,
sender: roomMembers[i],

View File

@ -178,6 +178,6 @@ export const populateThread = ({
}: MakeThreadProps): MakeThreadResult => {
const ret = mkThread({ room, client, authorId, participantUserIds, length, ts });
ret.thread.initialEventsFetched = true;
room.addLiveEvents(ret.events);
room.addLiveEvents(ret.events, { addToState: false });
return ret;
};

View File

@ -261,7 +261,7 @@ describe("RoomWidgetClient", () => {
expect(injectSpy).toHaveBeenCalled();
const call = injectSpy.mock.calls[0] as any;
const injectedEv = call[2][0];
const injectedEv = call[3][0];
expect(injectedEv.getType()).toBe("org.matrix.rageshake_request");
expect(injectedEv.getUnsigned().transaction_id).toBe("widgetTxId");
});
@ -287,7 +287,7 @@ describe("RoomWidgetClient", () => {
expect(injectSpy).toHaveBeenCalled();
const call = injectSpy.mock.calls[0] as any;
const injectedEv = call[2][0];
const injectedEv = call[3][0];
expect(injectedEv.getType()).toBe("org.matrix.rageshake_request");
expect(injectedEv.getUnsigned().transaction_id).toBe("widgetTxId");
});
@ -326,13 +326,13 @@ describe("RoomWidgetClient", () => {
// it has been called with the event sent by ourselves
const call = injectSpy.mock.calls[0] as any;
const injectedEv = call[2][0];
const injectedEv = call[3][0];
expect(injectedEv.getType()).toBe("org.matrix.rageshake_request");
expect(injectedEv.getUnsigned().transaction_id).toBe("widgetTxId");
// It has been called by the event we blocked because of our send right afterwards
const call2 = injectSpy.mock.calls[1] as any;
const injectedEv2 = call2[2][0];
const injectedEv2 = call2[3][0];
expect(injectedEv2.getType()).toBe("org.matrix.rageshake_request");
expect(injectedEv2.getUnsigned().transaction_id).toBe("4567");
});

View File

@ -74,7 +74,7 @@ describe("eventMapperFor", function () {
const event = mapper(eventDefinition);
expect(event).toBeInstanceOf(MatrixEvent);
room.addLiveEvents([event]);
room.addLiveEvents([event], { addToState: false });
expect(room.findEventById(eventId)).toBe(event);
const event2 = mapper(eventDefinition);
@ -109,7 +109,7 @@ describe("eventMapperFor", function () {
room.oldState.setStateEvents([event]);
room.currentState.setStateEvents([event]);
room.addLiveEvents([event]);
room.addLiveEvents([event], { addToState: false });
expect(room.findEventById(eventId)).toBe(event);
const event2 = mapper(eventDefinition);

View File

@ -104,7 +104,7 @@ describe("EventTimelineSet", () => {
it("Adds event to the live timeline in the timeline set", () => {
const liveTimeline = eventTimelineSet.getLiveTimeline();
expect(liveTimeline.getEvents().length).toStrictEqual(0);
eventTimelineSet.addLiveEvent(messageEvent);
eventTimelineSet.addLiveEvent(messageEvent, { addToState: false });
expect(liveTimeline.getEvents().length).toStrictEqual(1);
});
@ -113,6 +113,7 @@ describe("EventTimelineSet", () => {
expect(liveTimeline.getEvents().length).toStrictEqual(0);
eventTimelineSet.addLiveEvent(messageEvent, {
duplicateStrategy: DuplicateStrategy.Replace,
addToState: false,
});
expect(liveTimeline.getEvents().length).toStrictEqual(1);
@ -130,6 +131,7 @@ describe("EventTimelineSet", () => {
// replace.
eventTimelineSet.addLiveEvent(duplicateMessageEvent, {
duplicateStrategy: DuplicateStrategy.Replace,
addToState: false,
});
const eventsInLiveTimeline = liveTimeline.getEvents();
@ -144,6 +146,7 @@ describe("EventTimelineSet", () => {
expect(liveTimeline.getEvents().length).toStrictEqual(0);
eventTimelineSet.addEventToTimeline(messageEvent, liveTimeline, {
toStartOfTimeline: true,
addToState: false,
});
expect(liveTimeline.getEvents().length).toStrictEqual(1);
});
@ -151,10 +154,17 @@ describe("EventTimelineSet", () => {
it("Make sure legacy overload passing options directly as parameters still works", () => {
const liveTimeline = eventTimelineSet.getLiveTimeline();
expect(() => {
eventTimelineSet.addEventToTimeline(messageEvent, liveTimeline, true);
eventTimelineSet.addEventToTimeline(messageEvent, liveTimeline, {
toStartOfTimeline: true,
addToState: false,
});
}).not.toThrow();
expect(() => {
eventTimelineSet.addEventToTimeline(messageEvent, liveTimeline, true, false);
eventTimelineSet.addEventToTimeline(messageEvent, liveTimeline, {
toStartOfTimeline: true,
fromCache: false,
addToState: false,
});
}).not.toThrow();
});
@ -167,11 +177,13 @@ describe("EventTimelineSet", () => {
expect(liveTimeline.getEvents().length).toStrictEqual(0);
eventTimelineSet.addEventToTimeline(reactionEvent, liveTimeline, {
toStartOfTimeline: true,
addToState: false,
});
expect(liveTimeline.getEvents().length).toStrictEqual(0);
eventTimelineSet.addEventToTimeline(messageEvent, liveTimeline, {
toStartOfTimeline: true,
addToState: false,
});
expect(liveTimeline.getEvents()).toHaveLength(1);
const [event] = liveTimeline.getEvents();
@ -202,6 +214,7 @@ describe("EventTimelineSet", () => {
expect(() => {
eventTimelineSet.addEventToTimeline(messageEvent, liveTimeline2, {
toStartOfTimeline: true,
addToState: false,
});
}).toThrow();
});
@ -214,6 +227,7 @@ describe("EventTimelineSet", () => {
eventTimelineSet.addEventToTimeline(threadedReplyEvent, liveTimeline, {
toStartOfTimeline: true,
addToState: false,
});
expect(liveTimeline.getEvents().length).toStrictEqual(0);
});
@ -232,6 +246,7 @@ describe("EventTimelineSet", () => {
eventTimelineSetForThread.addEventToTimeline(normalMessage, liveTimeline, {
toStartOfTimeline: true,
addToState: false,
});
expect(liveTimeline.getEvents().length).toStrictEqual(0);
});
@ -248,6 +263,7 @@ describe("EventTimelineSet", () => {
expect(nonRoomEventTimeline.getEvents().length).toStrictEqual(0);
nonRoomEventTimelineSet.addEventToTimeline(messageEvent, nonRoomEventTimeline, {
toStartOfTimeline: true,
addToState: false,
});
expect(nonRoomEventTimeline.getEvents().length).toStrictEqual(1);
});
@ -257,7 +273,7 @@ describe("EventTimelineSet", () => {
describe("aggregateRelations", () => {
describe("with unencrypted events", () => {
beforeEach(() => {
eventTimelineSet.addEventsToTimeline([messageEvent, replyEvent], true, eventTimeline, "foo");
eventTimelineSet.addEventsToTimeline([messageEvent, replyEvent], true, false, eventTimeline, "foo");
});
itShouldReturnTheRelatedEvents();
@ -279,7 +295,7 @@ describe("EventTimelineSet", () => {
replyEventShouldAttemptDecryptionSpy.mockReturnValue(true);
replyEventIsDecryptionFailureSpy = jest.spyOn(messageEvent, "isDecryptionFailure");
eventTimelineSet.addEventsToTimeline([messageEvent, replyEvent], true, eventTimeline, "foo");
eventTimelineSet.addEventsToTimeline([messageEvent, replyEvent], true, false, eventTimeline, "foo");
});
it("should not return the related events", () => {

View File

@ -98,7 +98,7 @@ describe("EventTimeline", function () {
expect(function () {
timeline.initialiseState(state);
}).not.toThrow();
timeline.addEvent(event, { toStartOfTimeline: false });
timeline.addEvent(event, { toStartOfTimeline: false, addToState: false });
expect(function () {
timeline.initialiseState(state);
}).toThrow();
@ -182,9 +182,9 @@ describe("EventTimeline", function () {
];
it("should be able to add events to the end", function () {
timeline.addEvent(events[0], { toStartOfTimeline: false });
timeline.addEvent(events[0], { toStartOfTimeline: false, addToState: false });
const initialIndex = timeline.getBaseIndex();
timeline.addEvent(events[1], { toStartOfTimeline: false });
timeline.addEvent(events[1], { toStartOfTimeline: false, addToState: false });
expect(timeline.getBaseIndex()).toEqual(initialIndex);
expect(timeline.getEvents().length).toEqual(2);
expect(timeline.getEvents()[0]).toEqual(events[0]);
@ -192,9 +192,9 @@ describe("EventTimeline", function () {
});
it("should be able to add events to the start", function () {
timeline.addEvent(events[0], { toStartOfTimeline: true });
timeline.addEvent(events[0], { toStartOfTimeline: true, addToState: false });
const initialIndex = timeline.getBaseIndex();
timeline.addEvent(events[1], { toStartOfTimeline: true });
timeline.addEvent(events[1], { toStartOfTimeline: true, addToState: false });
expect(timeline.getBaseIndex()).toEqual(initialIndex + 1);
expect(timeline.getEvents().length).toEqual(2);
expect(timeline.getEvents()[0]).toEqual(events[1]);
@ -238,9 +238,9 @@ describe("EventTimeline", function () {
content: { name: "Old Room Name" },
});
timeline.addEvent(newEv, { toStartOfTimeline: false });
timeline.addEvent(newEv, { toStartOfTimeline: false, addToState: false });
expect(newEv.sender).toEqual(sentinel);
timeline.addEvent(oldEv, { toStartOfTimeline: true });
timeline.addEvent(oldEv, { toStartOfTimeline: true, addToState: false });
expect(oldEv.sender).toEqual(oldSentinel);
});
@ -280,9 +280,9 @@ describe("EventTimeline", function () {
skey: userA,
event: true,
});
timeline.addEvent(newEv, { toStartOfTimeline: false });
timeline.addEvent(newEv, { toStartOfTimeline: false, addToState: false });
expect(newEv.target).toEqual(sentinel);
timeline.addEvent(oldEv, { toStartOfTimeline: true });
timeline.addEvent(oldEv, { toStartOfTimeline: true, addToState: false });
expect(oldEv.target).toEqual(oldSentinel);
});
@ -308,8 +308,8 @@ describe("EventTimeline", function () {
}),
];
timeline.addEvent(events[0], { toStartOfTimeline: false });
timeline.addEvent(events[1], { toStartOfTimeline: false });
timeline.addEvent(events[0], { toStartOfTimeline: false, addToState: true });
timeline.addEvent(events[1], { toStartOfTimeline: false, addToState: true });
expect(timeline.getState(EventTimeline.FORWARDS)!.setStateEvents).toHaveBeenCalledWith([events[0]], {
timelineWasEmpty: undefined,
@ -347,8 +347,8 @@ describe("EventTimeline", function () {
}),
];
timeline.addEvent(events[0], { toStartOfTimeline: true });
timeline.addEvent(events[1], { toStartOfTimeline: true });
timeline.addEvent(events[0], { toStartOfTimeline: true, addToState: true });
timeline.addEvent(events[1], { toStartOfTimeline: true, addToState: true });
expect(timeline.getState(EventTimeline.BACKWARDS)!.setStateEvents).toHaveBeenCalledWith([events[0]], {
timelineWasEmpty: undefined,
@ -365,11 +365,15 @@ describe("EventTimeline", function () {
);
it("Make sure legacy overload passing options directly as parameters still works", () => {
expect(() => timeline.addEvent(events[0], { toStartOfTimeline: true })).not.toThrow();
expect(() => timeline.addEvent(events[0], { toStartOfTimeline: true, addToState: false })).not.toThrow();
// @ts-ignore stateContext is not a valid param
expect(() => timeline.addEvent(events[0], { stateContext: new RoomState(roomId) })).not.toThrow();
expect(() =>
timeline.addEvent(events[0], { toStartOfTimeline: false, roomState: new RoomState(roomId) }),
timeline.addEvent(events[0], {
toStartOfTimeline: false,
addToState: false,
roomState: new RoomState(roomId),
}),
).not.toThrow();
});
});
@ -397,8 +401,8 @@ describe("EventTimeline", function () {
];
it("should remove events", function () {
timeline.addEvent(events[0], { toStartOfTimeline: false });
timeline.addEvent(events[1], { toStartOfTimeline: false });
timeline.addEvent(events[0], { toStartOfTimeline: false, addToState: false });
timeline.addEvent(events[1], { toStartOfTimeline: false, addToState: false });
expect(timeline.getEvents().length).toEqual(2);
let ev = timeline.removeEvent(events[0].getId()!);
@ -411,9 +415,9 @@ describe("EventTimeline", function () {
});
it("should update baseIndex", function () {
timeline.addEvent(events[0], { toStartOfTimeline: false });
timeline.addEvent(events[1], { toStartOfTimeline: true });
timeline.addEvent(events[2], { toStartOfTimeline: false });
timeline.addEvent(events[0], { toStartOfTimeline: false, addToState: false });
timeline.addEvent(events[1], { toStartOfTimeline: true, addToState: false });
timeline.addEvent(events[2], { toStartOfTimeline: false, addToState: false });
expect(timeline.getEvents().length).toEqual(3);
expect(timeline.getBaseIndex()).toEqual(1);
@ -430,11 +434,11 @@ describe("EventTimeline", function () {
// - removing the last event got baseIndex into such a state that
// further addEvent(ev, false) calls made the index increase.
it("should not make baseIndex assplode when removing the last event", function () {
timeline.addEvent(events[0], { toStartOfTimeline: true });
timeline.addEvent(events[0], { toStartOfTimeline: true, addToState: false });
timeline.removeEvent(events[0].getId()!);
const initialIndex = timeline.getBaseIndex();
timeline.addEvent(events[1], { toStartOfTimeline: false });
timeline.addEvent(events[2], { toStartOfTimeline: false });
timeline.addEvent(events[1], { toStartOfTimeline: false, addToState: false });
timeline.addEvent(events[2], { toStartOfTimeline: false, addToState: false });
expect(timeline.getBaseIndex()).toEqual(initialIndex);
expect(timeline.getEvents().length).toEqual(2);
});

View File

@ -2799,24 +2799,28 @@ describe("MatrixClient", function () {
roomCreateEvent(room1.roomId, replacedByCreate1.roomId),
predecessorEvent(room1.roomId, replacedByDynamicPredecessor1.roomId),
],
{},
{ addToState: true },
);
room2.addLiveEvents(
[
roomCreateEvent(room2.roomId, replacedByCreate2.roomId),
predecessorEvent(room2.roomId, replacedByDynamicPredecessor2.roomId),
],
{},
{ addToState: true },
);
replacedByCreate1.addLiveEvents([tombstoneEvent(room1.roomId, replacedByCreate1.roomId)], {});
replacedByCreate2.addLiveEvents([tombstoneEvent(room2.roomId, replacedByCreate2.roomId)], {});
replacedByCreate1.addLiveEvents([tombstoneEvent(room1.roomId, replacedByCreate1.roomId)], {
addToState: true,
});
replacedByCreate2.addLiveEvents([tombstoneEvent(room2.roomId, replacedByCreate2.roomId)], {
addToState: true,
});
replacedByDynamicPredecessor1.addLiveEvents(
[tombstoneEvent(room1.roomId, replacedByDynamicPredecessor1.roomId)],
{},
{ addToState: true },
);
replacedByDynamicPredecessor2.addLiveEvents(
[tombstoneEvent(room2.roomId, replacedByDynamicPredecessor2.roomId)],
{},
{ addToState: true },
);
return {
@ -2854,10 +2858,10 @@ describe("MatrixClient", function () {
const room2 = new Room("room2", client, "@daryl:alexandria.example.com");
client.store = new StubStore();
client.store.getRooms = () => [room1, replacedRoom1, replacedRoom2, room2];
room1.addLiveEvents([roomCreateEvent(room1.roomId, replacedRoom1.roomId)], {});
room2.addLiveEvents([roomCreateEvent(room2.roomId, replacedRoom2.roomId)], {});
replacedRoom1.addLiveEvents([tombstoneEvent(room1.roomId, replacedRoom1.roomId)], {});
replacedRoom2.addLiveEvents([tombstoneEvent(room2.roomId, replacedRoom2.roomId)], {});
room1.addLiveEvents([roomCreateEvent(room1.roomId, replacedRoom1.roomId)], { addToState: true });
room2.addLiveEvents([roomCreateEvent(room2.roomId, replacedRoom2.roomId)], { addToState: true });
replacedRoom1.addLiveEvents([tombstoneEvent(room1.roomId, replacedRoom1.roomId)], { addToState: true });
replacedRoom2.addLiveEvents([tombstoneEvent(room2.roomId, replacedRoom2.roomId)], { addToState: true });
// When we ask for the visible rooms
const rooms = client.getVisibleRooms();
@ -2937,15 +2941,15 @@ describe("MatrixClient", function () {
const room4 = new Room("room4", client, "@michonne:hawthorne.example.com");
if (creates) {
room2.addLiveEvents([roomCreateEvent(room2.roomId, room1.roomId)]);
room3.addLiveEvents([roomCreateEvent(room3.roomId, room2.roomId)]);
room4.addLiveEvents([roomCreateEvent(room4.roomId, room3.roomId)]);
room2.addLiveEvents([roomCreateEvent(room2.roomId, room1.roomId)], { addToState: true });
room3.addLiveEvents([roomCreateEvent(room3.roomId, room2.roomId)], { addToState: true });
room4.addLiveEvents([roomCreateEvent(room4.roomId, room3.roomId)], { addToState: true });
}
if (tombstones) {
room1.addLiveEvents([tombstoneEvent(room2.roomId, room1.roomId)], {});
room2.addLiveEvents([tombstoneEvent(room3.roomId, room2.roomId)], {});
room3.addLiveEvents([tombstoneEvent(room4.roomId, room3.roomId)], {});
room1.addLiveEvents([tombstoneEvent(room2.roomId, room1.roomId)], { addToState: true });
room2.addLiveEvents([tombstoneEvent(room3.roomId, room2.roomId)], { addToState: true });
room3.addLiveEvents([tombstoneEvent(room4.roomId, room3.roomId)], { addToState: true });
}
mocked(store.getRoom).mockImplementation((roomId: string) => {
@ -2980,17 +2984,17 @@ describe("MatrixClient", function () {
const dynRoom4 = new Room("dynRoom4", client, "@rick:grimes.example.com");
const dynRoom5 = new Room("dynRoom5", client, "@rick:grimes.example.com");
dynRoom1.addLiveEvents([tombstoneEvent(dynRoom2.roomId, dynRoom1.roomId)], {});
dynRoom2.addLiveEvents([predecessorEvent(dynRoom2.roomId, dynRoom1.roomId)]);
dynRoom1.addLiveEvents([tombstoneEvent(dynRoom2.roomId, dynRoom1.roomId)], { addToState: true });
dynRoom2.addLiveEvents([predecessorEvent(dynRoom2.roomId, dynRoom1.roomId)], { addToState: true });
dynRoom2.addLiveEvents([tombstoneEvent(room3.roomId, dynRoom2.roomId)], {});
room3.addLiveEvents([predecessorEvent(room3.roomId, dynRoom2.roomId)]);
dynRoom2.addLiveEvents([tombstoneEvent(room3.roomId, dynRoom2.roomId)], { addToState: true });
room3.addLiveEvents([predecessorEvent(room3.roomId, dynRoom2.roomId)], { addToState: true });
room3.addLiveEvents([tombstoneEvent(dynRoom4.roomId, room3.roomId)], {});
dynRoom4.addLiveEvents([predecessorEvent(dynRoom4.roomId, room3.roomId)]);
room3.addLiveEvents([tombstoneEvent(dynRoom4.roomId, room3.roomId)], { addToState: true });
dynRoom4.addLiveEvents([predecessorEvent(dynRoom4.roomId, room3.roomId)], { addToState: true });
dynRoom4.addLiveEvents([tombstoneEvent(dynRoom5.roomId, dynRoom4.roomId)], {});
dynRoom5.addLiveEvents([predecessorEvent(dynRoom5.roomId, dynRoom4.roomId)]);
dynRoom4.addLiveEvents([tombstoneEvent(dynRoom5.roomId, dynRoom4.roomId)], { addToState: true });
dynRoom5.addLiveEvents([predecessorEvent(dynRoom5.roomId, dynRoom4.roomId)], { addToState: true });
mocked(store.getRoom)
.mockClear()

View File

@ -99,7 +99,7 @@ describe("MatrixEvent", () => {
const room = new Room("!roomid:e.xyz", mockClient, "myname");
const ev = createEvent("$event1:server");
await room.addLiveEvents([ev]);
await room.addLiveEvents([ev], { addToState: false });
await room.createThreadsTimelineSets();
expect(ev.threadRootId).toBeUndefined();
expect(mainTimelineLiveEventIds(room)).toEqual([ev.getId()]);
@ -120,7 +120,7 @@ describe("MatrixEvent", () => {
const threadRoot = createEvent("$threadroot:server");
const ev = createThreadedEvent("$event1:server", threadRoot.getId()!);
await room.addLiveEvents([threadRoot, ev]);
await room.addLiveEvents([threadRoot, ev], { addToState: false });
await room.createThreadsTimelineSets();
expect(threadRoot.threadRootId).toEqual(threadRoot.getId());
expect(mainTimelineLiveEventIds(room)).toEqual([threadRoot.getId()]);
@ -143,7 +143,7 @@ describe("MatrixEvent", () => {
const threadRoot = createEvent("$threadroot:server");
const ev = createThreadedEvent("$event1:server", threadRoot.getId()!);
await room.addLiveEvents([threadRoot, ev]);
await room.addLiveEvents([threadRoot, ev], { addToState: false });
await room.createThreadsTimelineSets();
expect(ev.threadRootId).toEqual(threadRoot.getId());
expect(mainTimelineLiveEventIds(room)).toEqual([threadRoot.getId()]);
@ -167,7 +167,7 @@ describe("MatrixEvent", () => {
const ev = createThreadedEvent("$event1:server", threadRoot.getId()!);
const reaction = createReactionEvent("$reaction:server", ev.getId()!);
await room.addLiveEvents([threadRoot, ev, reaction]);
await room.addLiveEvents([threadRoot, ev, reaction], { addToState: false });
await room.createThreadsTimelineSets();
expect(reaction.threadRootId).toEqual(threadRoot.getId());
expect(mainTimelineLiveEventIds(room)).toEqual([threadRoot.getId()]);
@ -191,7 +191,7 @@ describe("MatrixEvent", () => {
const ev = createThreadedEvent("$event1:server", threadRoot.getId()!);
const edit = createEditEvent("$edit:server", ev.getId()!);
await room.addLiveEvents([threadRoot, ev, edit]);
await room.addLiveEvents([threadRoot, ev, edit], { addToState: false });
await room.createThreadsTimelineSets();
expect(edit.threadRootId).toEqual(threadRoot.getId());
expect(mainTimelineLiveEventIds(room)).toEqual([threadRoot.getId()]);
@ -217,7 +217,7 @@ describe("MatrixEvent", () => {
const reply2 = createReplyEvent("$reply2:server", reply1.getId()!);
const reaction = createReactionEvent("$reaction:server", reply2.getId()!);
await room.addLiveEvents([threadRoot, ev, reply1, reply2, reaction]);
await room.addLiveEvents([threadRoot, ev, reply1, reply2, reaction], { addToState: false });
await room.createThreadsTimelineSets();
expect(reaction.threadRootId).toEqual(threadRoot.getId());
expect(mainTimelineLiveEventIds(room)).toEqual([threadRoot.getId()]);

View File

@ -36,7 +36,7 @@ describe("RoomReceipts", () => {
// Given there are no receipts in the room
const room = createRoom();
const [event] = createEvent();
room.addLiveEvents([event]);
room.addLiveEvents([event], { addToState: false });
// When I ask about any event, then it is unread
expect(room.hasUserReadEvent(readerId, event.getId()!)).toBe(false);
@ -46,7 +46,7 @@ describe("RoomReceipts", () => {
// Given there are no receipts in the room
const room = createRoom();
const [event] = createEventSentBy(readerId);
room.addLiveEvents([event]);
room.addLiveEvents([event], { addToState: false });
// When I ask about an event I sent, it is read (because a synthetic
// receipt was created and stored in RoomReceipts)
@ -57,7 +57,7 @@ describe("RoomReceipts", () => {
// Given my event exists and is unread
const room = createRoom();
const [event, eventId] = createEvent();
room.addLiveEvents([event]);
room.addLiveEvents([event], { addToState: false });
expect(room.hasUserReadEvent(readerId, eventId)).toBe(false);
// When we receive a receipt for this event+user
@ -72,7 +72,7 @@ describe("RoomReceipts", () => {
const room = createRoom();
const [event1, event1Id] = createEvent();
const [event2] = createEvent();
room.addLiveEvents([event1, event2]);
room.addLiveEvents([event1, event2], { addToState: false });
// When we receive a receipt for the later event
room.addReceipt(createReceipt(readerId, event2));
@ -86,7 +86,7 @@ describe("RoomReceipts", () => {
const room = createRoom();
const [oldEvent, oldEventId] = createEvent();
const [liveEvent] = createEvent();
room.addLiveEvents([liveEvent]);
room.addLiveEvents([liveEvent], { addToState: false });
createOldTimeline(room, [oldEvent]);
// When we receive a receipt for the live event
@ -120,7 +120,7 @@ describe("RoomReceipts", () => {
const room = createRoom();
const [event1] = createEvent();
const [event2, event2Id] = createEvent();
room.addLiveEvents([event1, event2]);
room.addLiveEvents([event1, event2], { addToState: false });
// When we receive a receipt for the earlier event
room.addReceipt(createReceipt(readerId, event1));
@ -133,7 +133,7 @@ describe("RoomReceipts", () => {
// Given my event exists and is unread
const room = createRoom();
const [event, eventId] = createEvent();
room.addLiveEvents([event]);
room.addLiveEvents([event], { addToState: false });
expect(room.hasUserReadEvent(readerId, eventId)).toBe(false);
// When we receive a receipt for another user
@ -151,7 +151,7 @@ describe("RoomReceipts", () => {
const room = createRoom();
const [previousEvent] = createEvent();
const [myEvent] = createEventSentBy(readerId);
room.addLiveEvents([previousEvent, myEvent]);
room.addLiveEvents([previousEvent, myEvent], { addToState: false });
// And I just received a receipt for the previous event
room.addReceipt(createReceipt(readerId, previousEvent));
@ -165,7 +165,7 @@ describe("RoomReceipts", () => {
const room = createRoom();
const [myEvent] = createEventSentBy(readerId);
const [laterEvent] = createEvent();
room.addLiveEvents([myEvent, laterEvent]);
room.addLiveEvents([myEvent, laterEvent], { addToState: false });
// When I ask about the later event, it is unread (because it's after the synthetic receipt)
expect(room.hasUserReadEvent(readerId, laterEvent.getId()!)).toBe(false);
@ -177,7 +177,7 @@ describe("RoomReceipts", () => {
const [event1] = createEvent();
const [event2, event2Id] = createEvent();
const [event3, event3Id] = createEvent();
room.addLiveEvents([event1, event2, event3]);
room.addLiveEvents([event1, event2, event3], { addToState: false });
// When we receive receipts for the older events out of order
room.addReceipt(createReceipt(readerId, event2));
@ -192,7 +192,7 @@ describe("RoomReceipts", () => {
// Given my event exists and is unread
const room = createRoom();
const [event, eventId] = createEvent();
room.addLiveEvents([event]);
room.addLiveEvents([event], { addToState: false });
expect(room.hasUserReadEvent(readerId, eventId)).toBe(false);
// When we receive a receipt for this event+user
@ -208,7 +208,7 @@ describe("RoomReceipts", () => {
const [root, rootId] = createEvent();
const [event, eventId] = createThreadedEvent(root);
setupThread(room, root);
room.addLiveEvents([root, event]);
room.addLiveEvents([root, event], { addToState: false });
expect(room.hasUserReadEvent(readerId, eventId)).toBe(false);
// When we receive a receipt for this event on this thread
@ -225,7 +225,7 @@ describe("RoomReceipts", () => {
const [event1, event1Id] = createThreadedEvent(root);
const [event2] = createThreadedEvent(root);
setupThread(room, root);
room.addLiveEvents([root, event1, event2]);
room.addLiveEvents([root, event1, event2], { addToState: false });
// When we receive a receipt for the later event
room.addReceipt(createThreadedReceipt(readerId, event2, rootId));
@ -241,7 +241,7 @@ describe("RoomReceipts", () => {
const [event1] = createThreadedEvent(root);
const [event2, event2Id] = createThreadedEvent(root);
setupThread(room, root);
room.addLiveEvents([root, event1, event2]);
room.addLiveEvents([root, event1, event2], { addToState: false });
// When we receive a receipt for the earlier event
room.addReceipt(createThreadedReceipt(readerId, event1, rootId));
@ -256,7 +256,7 @@ describe("RoomReceipts", () => {
const [root, rootId] = createEvent();
const [event, eventId] = createThreadedEvent(root);
setupThread(room, root);
room.addLiveEvents([root, event]);
room.addLiveEvents([root, event], { addToState: false });
expect(room.hasUserReadEvent(readerId, eventId)).toBe(false);
// When we receive a receipt for another user
@ -278,7 +278,7 @@ describe("RoomReceipts", () => {
const [thread2] = createThreadedEvent(root2);
setupThread(room, root1);
setupThread(room, root2);
room.addLiveEvents([root1, root2, thread1, thread2]);
room.addLiveEvents([root1, root2, thread1, thread2], { addToState: false });
// When we receive a receipt for the later event
room.addReceipt(createThreadedReceipt(readerId, thread2, root2.getId()!));
@ -295,7 +295,7 @@ describe("RoomReceipts", () => {
const [event2, event2Id] = createThreadedEvent(root);
const [event3, event3Id] = createThreadedEvent(root);
setupThread(room, root);
room.addLiveEvents([root, event1, event2, event3]);
room.addLiveEvents([root, event1, event2, event3], { addToState: false });
// When we receive receipts for the older events out of order
room.addReceipt(createThreadedReceipt(readerId, event2, rootId));
@ -329,7 +329,7 @@ describe("RoomReceipts", () => {
const [thread2b, thread2bId] = createThreadedEvent(main2);
setupThread(room, main1);
setupThread(room, main2);
room.addLiveEvents([main1, thread1a, thread1b, main2, thread2a, main3, thread2b]);
room.addLiveEvents([main1, thread1a, thread1b, main2, thread2a, main3, thread2b], { addToState: false });
// And the timestamps on the events are consistent with the order above
main1.event.origin_server_ts = 1;
@ -377,7 +377,7 @@ describe("RoomReceipts", () => {
// Add the event to the room
// The receipt is removed from the dangling state
room.addLiveEvents([event]);
room.addLiveEvents([event], { addToState: false });
// Then the event is read
expect(room.hasUserReadEvent(readerId, eventId)).toBe(true);
@ -398,7 +398,7 @@ describe("RoomReceipts", () => {
// Add the events to the room
// The receipt is removed from the dangling state
room.addLiveEvents([root, event]);
room.addLiveEvents([root, event], { addToState: false });
// Then the event is read
expect(room.hasUserReadEvent(readerId, eventId)).toBe(true);
@ -418,7 +418,7 @@ describe("RoomReceipts", () => {
// Add the event to the room
// The two receipts should be processed
room.addLiveEvents([event]);
room.addLiveEvents([event], { addToState: false });
// Then the event is read
// We expect that the receipt of `otherUserId` didn't replace/erase the receipt of `readerId`
@ -528,7 +528,7 @@ function createThreadedReceipt(userId: string, referencedEvent: MatrixEvent, thr
*/
function createOldTimeline(room: Room, events: MatrixEvent[]) {
const oldTimeline = room.getUnfilteredTimelineSet().addTimeline();
room.getUnfilteredTimelineSet().addEventsToTimeline(events, true, oldTimeline);
room.getUnfilteredTimelineSet().addEventsToTimeline(events, true, false, oldTimeline);
}
/**

View File

@ -801,7 +801,7 @@ async function createThread(client: MatrixClient, user: string, roomId: string):
// Ensure the root is in the room timeline
root.setThreadId(root.getId());
await room.addLiveEvents([root]);
await room.addLiveEvents([root], { addToState: false });
// Create the thread and wait for it to be initialised
const thread = room.createThread(root.getId()!, root, [], false);

View File

@ -106,7 +106,7 @@ describe("fixNotificationCountOnDecryption", () => {
mockClient,
);
room.addLiveEvents([event]);
room.addLiveEvents([event], { addToState: false });
THREAD_ID = event.getId()!;
threadEvent = mkEvent({

View File

@ -198,8 +198,8 @@ describe("Relations", function () {
});
const timelineSet = new EventTimelineSet(room);
timelineSet.addLiveEvent(targetEvent);
timelineSet.addLiveEvent(relationEvent);
timelineSet.addLiveEvent(targetEvent, { addToState: false });
timelineSet.addLiveEvent(relationEvent, { addToState: false });
await relationsCreated;
}
@ -212,8 +212,8 @@ describe("Relations", function () {
});
const timelineSet = new EventTimelineSet(room);
timelineSet.addLiveEvent(relationEvent);
timelineSet.addLiveEvent(targetEvent);
timelineSet.addLiveEvent(relationEvent, { addToState: false });
timelineSet.addLiveEvent(targetEvent, { addToState: false });
await relationsCreated;
}

File diff suppressed because it is too large Load Diff

View File

@ -17,8 +17,10 @@ limitations under the License.
import { ReceiptType } from "../../src/@types/read_receipts";
import {
IJoinedRoom,
Category,
IInvitedRoom,
IInviteState,
IJoinedRoom,
IKnockedRoom,
IKnockState,
ILeftRoom,
@ -27,7 +29,6 @@ import {
IStrippedState,
ISyncResponse,
SyncAccumulator,
IInviteState,
} from "../../src/sync-accumulator";
import { IRoomSummary } from "../../src";
import * as utils from "../test-utils/test-utils";
@ -85,6 +86,7 @@ describe("SyncAccumulator", function () {
// technically cheating since we also cheekily pre-populate keys we
// know that the sync accumulator will pre-populate.
// It isn't 100% transitive.
const events = [member("alice", KnownMembership.Join), member("bob", KnownMembership.Join)];
const res = {
next_batch: "abc",
rooms: {
@ -92,18 +94,17 @@ describe("SyncAccumulator", function () {
leave: {},
join: {
"!foo:bar": {
account_data: { events: [] },
ephemeral: { events: [] },
unread_notifications: {},
state: {
events: [member("alice", KnownMembership.Join), member("bob", KnownMembership.Join)],
},
summary: {
"account_data": { events: [] },
"ephemeral": { events: [] },
"unread_notifications": {},
"org.matrix.msc4222.state_after": { events },
"state": { events },
"summary": {
"m.heroes": undefined,
"m.joined_member_count": undefined,
"m.invited_member_count": undefined,
},
timeline: {
"timeline": {
events: [msg("alice", "hi")],
prev_batch: "something",
},
@ -882,6 +883,147 @@ describe("SyncAccumulator", function () {
).not.toBeUndefined();
});
});
describe("msc4222", () => {
it("should accumulate state_after events", () => {
const initState = {
events: [member("alice", KnownMembership.Knock)],
};
sa.accumulate(
syncSkeleton({
"org.matrix.msc4222.state_after": initState,
}),
);
expect(sa.getJSON().roomsData[Category.Join]["!foo:bar"].state).toEqual(initState);
sa.accumulate(
syncSkeleton({
"org.matrix.msc4222.state_after": {
events: [
utils.mkEvent({
user: "alice",
room: "!knock:bar",
type: "m.room.name",
content: {
name: "Room 1",
},
skey: "",
}) as IStateEvent,
],
},
}),
);
expect(
sa.getJSON().roomsData[Category.Join]["!foo:bar"].state?.events.find((e) => e.type === "m.room.name")
?.content.name,
).toEqual("Room 1");
sa.accumulate(
syncSkeleton({
"org.matrix.msc4222.state_after": {
events: [
utils.mkEvent({
user: "alice",
room: "!knock:bar",
type: "m.room.name",
content: {
name: "Room 2",
},
skey: "",
}) as IStateEvent,
],
},
}),
);
expect(
sa.getJSON().roomsData[Category.Join]["!foo:bar"].state?.events.find((e) => e.type === "m.room.name")
?.content.name,
).toEqual("Room 2");
});
it("should ignore state events in timeline", () => {
const initState = {
events: [member("alice", KnownMembership.Knock)],
};
sa.accumulate(
syncSkeleton({
"org.matrix.msc4222.state_after": initState,
}),
);
expect(sa.getJSON().roomsData[Category.Join]["!foo:bar"].state).toEqual(initState);
sa.accumulate(
syncSkeleton({
"org.matrix.msc4222.state_after": {
events: [],
},
"timeline": {
events: [
utils.mkEvent({
user: "alice",
room: "!knock:bar",
type: "m.room.name",
content: {
name: "Room 1",
},
skey: "",
}) as IStateEvent,
],
prev_batch: "something",
},
}),
);
expect(
sa.getJSON().roomsData[Category.Join]["!foo:bar"].state?.events.find((e) => e.type === "m.room.name")
?.content.name,
).not.toEqual("Room 1");
});
it("should not rewind state_after to start of timeline in toJSON", () => {
const initState = {
events: [member("alice", KnownMembership.Knock)],
};
sa.accumulate(
syncSkeleton({
"org.matrix.msc4222.state_after": initState,
"timeline": {
events: initState.events,
prev_batch: null,
},
}),
);
expect(sa.getJSON().roomsData[Category.Join]["!foo:bar"].state).toEqual(initState);
const joinEvent = member("alice", KnownMembership.Join);
joinEvent.unsigned = { prev_content: initState.events[0].content, prev_sender: initState.events[0].sender };
sa.accumulate(
syncSkeleton({
"org.matrix.msc4222.state_after": {
events: [joinEvent],
},
"timeline": {
events: [joinEvent],
prev_batch: "something",
},
}),
);
const roomData = sa.getJSON().roomsData[Category.Join]["!foo:bar"];
expect(roomData.state?.events.find((e) => e.type === "m.room.member")?.content.membership).toEqual(
KnownMembership.Knock,
);
expect(
roomData["org.matrix.msc4222.state_after"]?.events.find((e) => e.type === "m.room.member")?.content
.membership,
).toEqual(KnownMembership.Join);
expect(roomData.timeline?.events.find((e) => e.type === "m.room.member")?.content.membership).toEqual(
KnownMembership.Join,
);
});
});
});
function syncSkeleton(
@ -961,5 +1103,6 @@ function member(localpart: string, membership: Membership) {
state_key: "@" + localpart + ":localhost",
sender: "@" + localpart + ":localhost",
type: "m.room.member",
unsigned: {},
};
}

View File

@ -62,7 +62,7 @@ function addEventsToTimeline(timeline: EventTimeline, numEvents: number, toStart
user: USER_ID,
event: true,
}),
{ toStartOfTimeline },
{ toStartOfTimeline, addToState: false },
);
}
}
@ -451,8 +451,8 @@ describe("TimelineWindow", function () {
const liveEvents = createEvents(5);
const [, , e3, e4, e5] = oldEvents;
const [, e7, e8, e9, e10] = liveEvents;
room.addLiveEvents(liveEvents);
room.addEventsToTimeline(oldEvents, true, oldTimeline);
room.addLiveEvents(liveEvents, { addToState: false });
room.addEventsToTimeline(oldEvents, true, false, oldTimeline);
// And 2 windows over the timelines in this room
const oldWindow = new TimelineWindow(mockClient, timelineSet);

View File

@ -1566,16 +1566,19 @@ describe("Group Call", function () {
async (roomId, eventType, content, stateKey) => {
const eventId = `$${Math.random()}`;
if (roomId === room.roomId) {
room.addLiveEvents([
new MatrixEvent({
event_id: eventId,
type: eventType,
room_id: roomId,
sender: FAKE_USER_ID_2,
content,
state_key: stateKey,
}),
]);
room.addLiveEvents(
[
new MatrixEvent({
event_id: eventId,
type: eventType,
room_id: roomId,
sender: FAKE_USER_ID_2,
content,
state_key: stateKey,
}),
],
{ addToState: true },
);
}
return { event_id: eventId };
},