You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-07-30 04:23:07 +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:
@ -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,
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -168,14 +168,17 @@ describe("MatrixClient", function () {
|
||||
type: "test",
|
||||
content: {},
|
||||
});
|
||||
room.addLiveEvents([
|
||||
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([
|
||||
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([
|
||||
room.addLiveEvents(
|
||||
[
|
||||
utils.mkMembership({
|
||||
user: userId,
|
||||
room: roomId,
|
||||
mship: KnownMembership.Knock,
|
||||
event: true,
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
|
||||
httpBackend.verifyNoOutstandingRequests();
|
||||
store.storeRoom(room);
|
||||
|
@ -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", () => {
|
||||
|
@ -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);
|
||||
|
@ -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)!;
|
||||
|
@ -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],
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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");
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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", () => {
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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()
|
||||
|
@ -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()]);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -106,7 +106,7 @@ describe("fixNotificationCountOnDecryption", () => {
|
||||
mockClient,
|
||||
);
|
||||
|
||||
room.addLiveEvents([event]);
|
||||
room.addLiveEvents([event], { addToState: false });
|
||||
|
||||
THREAD_ID = event.getId()!;
|
||||
threadEvent = mkEvent({
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -29,6 +29,9 @@ import {
|
||||
EventStatus,
|
||||
EventTimelineSet,
|
||||
EventType,
|
||||
Filter,
|
||||
FILTER_RELATED_BY_REL_TYPES,
|
||||
FILTER_RELATED_BY_SENDERS,
|
||||
IContent,
|
||||
IEvent,
|
||||
IRelationsRequestOpts,
|
||||
@ -175,7 +178,7 @@ describe("Room", function () {
|
||||
*/
|
||||
const mkMessageInRoom = async (room: Room, timestamp: number) => {
|
||||
const message = mkMessage({ ts: timestamp });
|
||||
await room.addLiveEvents([message]);
|
||||
await room.addLiveEvents([message], { addToState: false });
|
||||
return message;
|
||||
};
|
||||
|
||||
@ -188,7 +191,7 @@ describe("Room", function () {
|
||||
*/
|
||||
const mkMessageInThread = (thread: Thread, timestamp: number) => {
|
||||
const message = mkThreadResponse(thread.rootEvent!, { ts: timestamp });
|
||||
thread.liveTimeline.addEvent(message, { toStartOfTimeline: false });
|
||||
thread.liveTimeline.addEvent(message, { toStartOfTimeline: false, addToState: false });
|
||||
return message;
|
||||
};
|
||||
|
||||
@ -202,14 +205,14 @@ describe("Room", function () {
|
||||
if (thread1EventTs !== null) {
|
||||
const { rootEvent: thread1RootEvent, thread: thread1 } = mkThread({ room });
|
||||
const thread1Event = mkThreadResponse(thread1RootEvent, { ts: thread1EventTs });
|
||||
thread1.liveTimeline.addEvent(thread1Event, { toStartOfTimeline: true });
|
||||
thread1.liveTimeline.addEvent(thread1Event, { toStartOfTimeline: true, addToState: false });
|
||||
result.thread1 = thread1;
|
||||
}
|
||||
|
||||
if (thread2EventTs !== null) {
|
||||
const { rootEvent: thread2RootEvent, thread: thread2 } = mkThread({ room });
|
||||
const thread2Event = mkThreadResponse(thread2RootEvent, { ts: thread2EventTs });
|
||||
thread2.liveTimeline.addEvent(thread2Event, { toStartOfTimeline: true });
|
||||
thread2.liveTimeline.addEvent(thread2Event, { toStartOfTimeline: true, addToState: false });
|
||||
result.thread2 = thread2;
|
||||
}
|
||||
|
||||
@ -347,10 +350,11 @@ describe("Room", function () {
|
||||
event: true,
|
||||
});
|
||||
dupe.event.event_id = events[0].getId();
|
||||
await room.addLiveEvents(events);
|
||||
await room.addLiveEvents(events, { addToState: false });
|
||||
expect(room.timeline[0]).toEqual(events[0]);
|
||||
await room.addLiveEvents([dupe], {
|
||||
duplicateStrategy: DuplicateStrategy.Replace,
|
||||
addToState: false,
|
||||
});
|
||||
expect(room.timeline[0]).toEqual(dupe);
|
||||
});
|
||||
@ -364,7 +368,7 @@ describe("Room", function () {
|
||||
event: true,
|
||||
});
|
||||
dupe.event.event_id = events[0].getId();
|
||||
await room.addLiveEvents(events);
|
||||
await room.addLiveEvents(events, { addToState: false });
|
||||
expect(room.timeline[0]).toEqual(events[0]);
|
||||
// @ts-ignore
|
||||
await room.addLiveEvents([dupe], {
|
||||
@ -382,7 +386,7 @@ describe("Room", function () {
|
||||
expect(emitRoom).toEqual(room);
|
||||
expect(toStart).toBeFalsy();
|
||||
});
|
||||
await room.addLiveEvents(events);
|
||||
await room.addLiveEvents(events, { addToState: false });
|
||||
expect(callCount).toEqual(2);
|
||||
});
|
||||
|
||||
@ -405,7 +409,7 @@ describe("Room", function () {
|
||||
},
|
||||
}),
|
||||
];
|
||||
await room.addLiveEvents(events);
|
||||
await room.addLiveEvents(events, { addToState: true });
|
||||
expect(room.currentState.setStateEvents).toHaveBeenCalledWith([events[0]], { timelineWasEmpty: false });
|
||||
expect(room.currentState.setStateEvents).toHaveBeenCalledWith([events[1]], { timelineWasEmpty: false });
|
||||
expect(events[0].forwardLooking).toBe(true);
|
||||
@ -425,7 +429,7 @@ describe("Room", function () {
|
||||
}
|
||||
return null;
|
||||
});
|
||||
await room.addLiveEvents(events);
|
||||
await room.addLiveEvents(events, { addToState: false });
|
||||
expect(room.getEventReadUpTo(userA)).toEqual(events[1].getId());
|
||||
});
|
||||
|
||||
@ -460,7 +464,7 @@ describe("Room", function () {
|
||||
expect(stub.mock.calls[0][3]).toBeUndefined();
|
||||
|
||||
// then the remoteEvent
|
||||
await room.addLiveEvents([remoteEvent]);
|
||||
await room.addLiveEvents([remoteEvent], { addToState: false });
|
||||
expect(room.timeline.length).toEqual(1);
|
||||
|
||||
expect(stub).toHaveBeenCalledTimes(2);
|
||||
@ -498,7 +502,7 @@ describe("Room", function () {
|
||||
// then /sync returns the remoteEvent, it should de-dupe based on the event ID.
|
||||
const remoteEvent = new MatrixEvent(Object.assign({ event_id: realEventId }, eventJson));
|
||||
expect(remoteEvent.getTxnId()).toBeUndefined();
|
||||
await room.addLiveEvents([remoteEvent]);
|
||||
await room.addLiveEvents([remoteEvent], { addToState: false });
|
||||
// the duplicate strategy code should ensure we don't add a 2nd event to the live timeline
|
||||
expect(room.timeline.length).toEqual(1);
|
||||
// but without the event ID matching we will still have the local event in pending events
|
||||
@ -528,7 +532,7 @@ describe("Room", function () {
|
||||
const realEventId = "$real-event-id";
|
||||
const remoteEvent = new MatrixEvent(Object.assign({ event_id: realEventId }, eventJson));
|
||||
expect(remoteEvent.getUnsigned().transaction_id).toBeUndefined();
|
||||
await room.addLiveEvents([remoteEvent]);
|
||||
await room.addLiveEvents([remoteEvent], { addToState: false });
|
||||
expect(room.timeline.length).toEqual(2); // impossible to de-dupe as no txn ID or matching event ID
|
||||
|
||||
// then the /send request returns the real event ID.
|
||||
@ -550,7 +554,7 @@ describe("Room", function () {
|
||||
remoteEvent.event.unsigned = { transaction_id: "TXN_ID" };
|
||||
|
||||
// add the remoteEvent
|
||||
await room.addLiveEvents([remoteEvent]);
|
||||
await room.addLiveEvents([remoteEvent], { addToState: false });
|
||||
expect(room.timeline.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
@ -589,12 +593,12 @@ describe("Room", function () {
|
||||
|
||||
it("should not be able to add events to the end", function () {
|
||||
expect(function () {
|
||||
room.addEventsToTimeline(events, false, room.getLiveTimeline());
|
||||
room.addEventsToTimeline(events, false, false, room.getLiveTimeline());
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it("should be able to add events to the start", function () {
|
||||
room.addEventsToTimeline(events, true, room.getLiveTimeline());
|
||||
room.addEventsToTimeline(events, true, false, room.getLiveTimeline());
|
||||
expect(room.timeline.length).toEqual(2);
|
||||
expect(room.timeline[0]).toEqual(events[1]);
|
||||
expect(room.timeline[1]).toEqual(events[0]);
|
||||
@ -609,7 +613,7 @@ describe("Room", function () {
|
||||
expect(emitRoom).toEqual(room);
|
||||
expect(toStart).toBe(true);
|
||||
});
|
||||
room.addEventsToTimeline(events, true, room.getLiveTimeline());
|
||||
room.addEventsToTimeline(events, true, false, room.getLiveTimeline());
|
||||
expect(callCount).toEqual(2);
|
||||
});
|
||||
});
|
||||
@ -653,9 +657,9 @@ describe("Room", function () {
|
||||
event: true,
|
||||
content: { name: "Old Room Name" },
|
||||
});
|
||||
await room.addLiveEvents([newEv]);
|
||||
await room.addLiveEvents([newEv], { addToState: false });
|
||||
expect(newEv.sender).toEqual(sentinel);
|
||||
room.addEventsToTimeline([oldEv], true, room.getLiveTimeline());
|
||||
room.addEventsToTimeline([oldEv], true, false, room.getLiveTimeline());
|
||||
expect(oldEv.sender).toEqual(oldSentinel);
|
||||
});
|
||||
|
||||
@ -697,9 +701,9 @@ describe("Room", function () {
|
||||
skey: userA,
|
||||
event: true,
|
||||
});
|
||||
await room.addLiveEvents([newEv]);
|
||||
await room.addLiveEvents([newEv], { addToState: false });
|
||||
expect(newEv.target).toEqual(sentinel);
|
||||
room.addEventsToTimeline([oldEv], true, room.getLiveTimeline());
|
||||
room.addEventsToTimeline([oldEv], true, false, room.getLiveTimeline());
|
||||
expect(oldEv.target).toEqual(oldSentinel);
|
||||
});
|
||||
|
||||
@ -725,7 +729,7 @@ describe("Room", function () {
|
||||
}),
|
||||
];
|
||||
|
||||
room.addEventsToTimeline(events, true, room.getLiveTimeline());
|
||||
room.addEventsToTimeline(events, true, true, room.getLiveTimeline());
|
||||
expect(room.oldState.setStateEvents).toHaveBeenCalledWith([events[0]], { timelineWasEmpty: undefined });
|
||||
expect(room.oldState.setStateEvents).toHaveBeenCalledWith([events[1]], { timelineWasEmpty: undefined });
|
||||
expect(events[0].forwardLooking).toBe(false);
|
||||
@ -767,11 +771,11 @@ describe("Room", function () {
|
||||
});
|
||||
|
||||
it("should copy state from previous timeline", async function () {
|
||||
await room.addLiveEvents([events[0], events[1]]);
|
||||
await room.addLiveEvents([events[0], events[1]], { addToState: false });
|
||||
expect(room.getLiveTimeline().getEvents().length).toEqual(2);
|
||||
room.resetLiveTimeline("sometoken", "someothertoken");
|
||||
|
||||
await room.addLiveEvents([events[2]]);
|
||||
await room.addLiveEvents([events[2]], { addToState: false });
|
||||
const oldState = room.getLiveTimeline().getState(EventTimeline.BACKWARDS);
|
||||
const newState = room.getLiveTimeline().getState(EventTimeline.FORWARDS);
|
||||
expect(room.getLiveTimeline().getEvents().length).toEqual(1);
|
||||
@ -780,7 +784,7 @@ describe("Room", function () {
|
||||
});
|
||||
|
||||
it("should reset the legacy timeline fields", async function () {
|
||||
await room.addLiveEvents([events[0], events[1]]);
|
||||
await room.addLiveEvents([events[0], events[1]], { addToState: false });
|
||||
expect(room.timeline.length).toEqual(2);
|
||||
|
||||
const oldStateBeforeRunningReset = room.oldState;
|
||||
@ -801,7 +805,7 @@ describe("Room", function () {
|
||||
|
||||
room.resetLiveTimeline("sometoken", "someothertoken");
|
||||
|
||||
await room.addLiveEvents([events[2]]);
|
||||
await room.addLiveEvents([events[2]], { addToState: false });
|
||||
const newLiveTimeline = room.getLiveTimeline();
|
||||
expect(room.timeline).toEqual(newLiveTimeline.getEvents());
|
||||
expect(room.oldState).toEqual(newLiveTimeline.getState(EventTimeline.BACKWARDS));
|
||||
@ -828,7 +832,7 @@ describe("Room", function () {
|
||||
});
|
||||
|
||||
it("should " + (timelineSupport ? "remember" : "forget") + " old timelines", async function () {
|
||||
await room.addLiveEvents([events[0]]);
|
||||
await room.addLiveEvents([events[0]], { addToState: false });
|
||||
expect(room.timeline.length).toEqual(1);
|
||||
const firstLiveTimeline = room.getLiveTimeline();
|
||||
room.resetLiveTimeline("sometoken", "someothertoken");
|
||||
@ -872,7 +876,7 @@ describe("Room", function () {
|
||||
];
|
||||
|
||||
it("should handle events in the same timeline", async function () {
|
||||
await room.addLiveEvents(events);
|
||||
await room.addLiveEvents(events, { addToState: false });
|
||||
|
||||
expect(
|
||||
room.getUnfilteredTimelineSet().compareEventOrdering(events[0].getId()!, events[1].getId()!),
|
||||
@ -890,8 +894,8 @@ describe("Room", function () {
|
||||
oldTimeline.setNeighbouringTimeline(room.getLiveTimeline(), Direction.Forward);
|
||||
room.getLiveTimeline().setNeighbouringTimeline(oldTimeline, Direction.Backward);
|
||||
|
||||
room.addEventsToTimeline([events[0]], false, oldTimeline);
|
||||
await room.addLiveEvents([events[1]]);
|
||||
room.addEventsToTimeline([events[0]], false, false, oldTimeline);
|
||||
await room.addLiveEvents([events[1]], { addToState: false });
|
||||
|
||||
expect(
|
||||
room.getUnfilteredTimelineSet().compareEventOrdering(events[0].getId()!, events[1].getId()!),
|
||||
@ -904,8 +908,8 @@ describe("Room", function () {
|
||||
it("should return null for events in non-adjacent timelines", async function () {
|
||||
const oldTimeline = room.addTimeline();
|
||||
|
||||
room.addEventsToTimeline([events[0]], false, oldTimeline);
|
||||
await room.addLiveEvents([events[1]]);
|
||||
room.addEventsToTimeline([events[0]], false, false, oldTimeline);
|
||||
await room.addLiveEvents([events[1]], { addToState: false });
|
||||
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[0].getId()!, events[1].getId()!)).toBe(
|
||||
null,
|
||||
@ -916,7 +920,7 @@ describe("Room", function () {
|
||||
});
|
||||
|
||||
it("should return null for unknown events", async function () {
|
||||
await room.addLiveEvents(events);
|
||||
await room.addLiveEvents(events, { addToState: false });
|
||||
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering(events[0].getId()!, "xxx")).toBe(null);
|
||||
expect(room.getUnfilteredTimelineSet().compareEventOrdering("xxx", events[0].getId()!)).toBe(null);
|
||||
@ -994,7 +998,8 @@ describe("Room", function () {
|
||||
|
||||
describe("recalculate", function () {
|
||||
const setJoinRule = async function (rule: JoinRule) {
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkEvent({
|
||||
type: EventType.RoomJoinRules,
|
||||
room: roomId,
|
||||
@ -1004,10 +1009,13 @@ describe("Room", function () {
|
||||
},
|
||||
event: true,
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
};
|
||||
const setAltAliases = async function (aliases: string[]) {
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkEvent({
|
||||
type: EventType.RoomCanonicalAlias,
|
||||
room: roomId,
|
||||
@ -1017,10 +1025,13 @@ describe("Room", function () {
|
||||
},
|
||||
event: true,
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
};
|
||||
const setAlias = async function (alias: string) {
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkEvent({
|
||||
type: EventType.RoomCanonicalAlias,
|
||||
room: roomId,
|
||||
@ -1028,10 +1039,13 @@ describe("Room", function () {
|
||||
content: { alias },
|
||||
event: true,
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
};
|
||||
const setRoomName = async function (name: string) {
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkEvent({
|
||||
type: EventType.RoomName,
|
||||
room: roomId,
|
||||
@ -1041,7 +1055,9 @@ describe("Room", function () {
|
||||
},
|
||||
event: true,
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
};
|
||||
const addMember = async function (userId: string, state = KnownMembership.Join, opts: any = {}) {
|
||||
opts.room = roomId;
|
||||
@ -1050,7 +1066,7 @@ describe("Room", function () {
|
||||
opts.skey = userId;
|
||||
opts.event = true;
|
||||
const event = utils.mkMembership(opts);
|
||||
await room.addLiveEvents([event]);
|
||||
await room.addLiveEvents([event], { addToState: true });
|
||||
return event;
|
||||
};
|
||||
|
||||
@ -1658,7 +1674,7 @@ describe("Room", function () {
|
||||
}),
|
||||
];
|
||||
|
||||
await room.addLiveEvents(events);
|
||||
await room.addLiveEvents(events, { addToState: false });
|
||||
const ts = 13787898424;
|
||||
|
||||
// check it initialises correctly
|
||||
@ -1696,7 +1712,7 @@ describe("Room", function () {
|
||||
}),
|
||||
];
|
||||
|
||||
await room.addLiveEvents(events);
|
||||
await room.addLiveEvents(events, { addToState: false });
|
||||
const ts = 13787898424;
|
||||
|
||||
// check it initialises correctly
|
||||
@ -1727,7 +1743,7 @@ describe("Room", function () {
|
||||
describe("hasUserReadUpTo", function () {
|
||||
it("returns true if there is a receipt for this event (main timeline)", function () {
|
||||
const ts = 13787898424;
|
||||
room.addLiveEvents([eventToAck]);
|
||||
room.addLiveEvents([eventToAck], { addToState: false });
|
||||
room.addReceipt(mkReceipt(roomId, [mkRecord(eventToAck.getId()!, "m.read", userB, ts)]));
|
||||
room.findEventById = jest.fn().mockReturnValue({ getThread: jest.fn() } as unknown as MatrixEvent);
|
||||
expect(room.hasUserReadEvent(userB, eventToAck.getId()!)).toEqual(true);
|
||||
@ -1755,7 +1771,7 @@ describe("Room", function () {
|
||||
event: true,
|
||||
}),
|
||||
];
|
||||
await room.addLiveEvents(events);
|
||||
await room.addLiveEvents(events, { addToState: false });
|
||||
|
||||
// When I add a receipt for the latest one
|
||||
room.addReceipt(mkReceipt(roomId, [mkRecord(events[2].getId()!, "m.read", userB, 102)]));
|
||||
@ -1778,7 +1794,7 @@ describe("Room", function () {
|
||||
// Given a thread exists in the room
|
||||
const { thread, events } = mkThread({ room, length: 3 });
|
||||
thread.initialEventsFetched = true;
|
||||
await room.addLiveEvents(events);
|
||||
await room.addLiveEvents(events, { addToState: false });
|
||||
|
||||
// When I add an unthreaded receipt for the latest thread message
|
||||
room.addReceipt(mkReceipt(roomId, [mkRecord(events[2].getId()!, "m.read", userB, 102)]));
|
||||
@ -1857,9 +1873,9 @@ describe("Room", function () {
|
||||
msg: "remote 2",
|
||||
event: true,
|
||||
});
|
||||
await room.addLiveEvents([eventA]);
|
||||
await room.addLiveEvents([eventA], { addToState: false });
|
||||
room.addPendingEvent(eventB, "TXN1");
|
||||
await room.addLiveEvents([eventC]);
|
||||
await room.addLiveEvents([eventC], { addToState: false });
|
||||
expect(room.timeline).toEqual([eventA, eventC]);
|
||||
expect(room.getPendingEvents()).toEqual([eventB]);
|
||||
},
|
||||
@ -1890,9 +1906,9 @@ describe("Room", function () {
|
||||
msg: "remote 2",
|
||||
event: true,
|
||||
});
|
||||
await room.addLiveEvents([eventA]);
|
||||
await room.addLiveEvents([eventA], { addToState: false });
|
||||
room.addPendingEvent(eventB, "TXN1");
|
||||
await room.addLiveEvents([eventC]);
|
||||
await room.addLiveEvents([eventC], { addToState: false });
|
||||
expect(room.timeline).toEqual([eventA, eventB, eventC]);
|
||||
},
|
||||
);
|
||||
@ -2169,14 +2185,17 @@ describe("Room", function () {
|
||||
});
|
||||
it("should return first member that isn't self", async function () {
|
||||
const room = new Room(roomId, new TestClient(userA).client, userA);
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkMembership({
|
||||
user: userB,
|
||||
mship: KnownMembership.Join,
|
||||
room: roomId,
|
||||
event: true,
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
expect(room.guessDMUserId()).toEqual(userB);
|
||||
});
|
||||
it("should return self if only member present", function () {
|
||||
@ -2307,7 +2326,8 @@ describe("Room", function () {
|
||||
|
||||
it("should return a display name if one other member is in the room", async function () {
|
||||
const room = new Room(roomId, new TestClient(userA).client, userA);
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkMembership({
|
||||
user: userA,
|
||||
mship: KnownMembership.Join,
|
||||
@ -2322,13 +2342,16 @@ describe("Room", function () {
|
||||
event: true,
|
||||
name: "User B",
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
expect(room.getDefaultRoomName(userA)).toEqual("User B");
|
||||
});
|
||||
|
||||
it("should return a display name if one other member is banned", async function () {
|
||||
const room = new Room(roomId, new TestClient(userA).client, userA);
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkMembership({
|
||||
user: userA,
|
||||
mship: KnownMembership.Join,
|
||||
@ -2343,13 +2366,16 @@ describe("Room", function () {
|
||||
event: true,
|
||||
name: "User B",
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
expect(room.getDefaultRoomName(userA)).toEqual("Empty room (was User B)");
|
||||
});
|
||||
|
||||
it("should return a display name if one other member is invited", async function () {
|
||||
const room = new Room(roomId, new TestClient(userA).client, userA);
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkMembership({
|
||||
user: userA,
|
||||
mship: KnownMembership.Join,
|
||||
@ -2364,13 +2390,16 @@ describe("Room", function () {
|
||||
event: true,
|
||||
name: "User B",
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
expect(room.getDefaultRoomName(userA)).toEqual("User B");
|
||||
});
|
||||
|
||||
it("should return 'Empty room (was User B)' if User B left the room", async function () {
|
||||
const room = new Room(roomId, new TestClient(userA).client, userA);
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkMembership({
|
||||
user: userA,
|
||||
mship: KnownMembership.Join,
|
||||
@ -2385,13 +2414,16 @@ describe("Room", function () {
|
||||
event: true,
|
||||
name: "User B",
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
expect(room.getDefaultRoomName(userA)).toEqual("Empty room (was User B)");
|
||||
});
|
||||
|
||||
it("should return 'User B and User C' if in a room with two other users", async function () {
|
||||
const room = new Room(roomId, new TestClient(userA).client, userA);
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkMembership({
|
||||
user: userA,
|
||||
mship: KnownMembership.Join,
|
||||
@ -2413,13 +2445,16 @@ describe("Room", function () {
|
||||
event: true,
|
||||
name: "User C",
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
expect(room.getDefaultRoomName(userA)).toEqual("User B and User C");
|
||||
});
|
||||
|
||||
it("should return 'User B and 2 others' if in a room with three other users", async function () {
|
||||
const room = new Room(roomId, new TestClient(userA).client, userA);
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkMembership({
|
||||
user: userA,
|
||||
mship: KnownMembership.Join,
|
||||
@ -2448,7 +2483,9 @@ describe("Room", function () {
|
||||
event: true,
|
||||
name: "User D",
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
expect(room.getDefaultRoomName(userA)).toEqual("User B and 2 others");
|
||||
});
|
||||
});
|
||||
@ -2456,7 +2493,8 @@ describe("Room", function () {
|
||||
describe("io.element.functional_users", function () {
|
||||
it("should return a display name (default behaviour) if no one is marked as a functional member", async function () {
|
||||
const room = new Room(roomId, new TestClient(userA).client, userA);
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkMembership({
|
||||
user: userA,
|
||||
mship: KnownMembership.Join,
|
||||
@ -2480,13 +2518,16 @@ describe("Room", function () {
|
||||
service_members: [],
|
||||
},
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
expect(room.getDefaultRoomName(userA)).toEqual("User B");
|
||||
});
|
||||
|
||||
it("should return a display name (default behaviour) if service members is a number (invalid)", async function () {
|
||||
const room = new Room(roomId, new TestClient(userA).client, userA);
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkMembership({
|
||||
user: userA,
|
||||
mship: KnownMembership.Join,
|
||||
@ -2510,13 +2551,16 @@ describe("Room", function () {
|
||||
service_members: 1,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
expect(room.getDefaultRoomName(userA)).toEqual("User B");
|
||||
});
|
||||
|
||||
it("should return a display name (default behaviour) if service members is a string (invalid)", async function () {
|
||||
const room = new Room(roomId, new TestClient(userA).client, userA);
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkMembership({
|
||||
user: userA,
|
||||
mship: KnownMembership.Join,
|
||||
@ -2540,13 +2584,16 @@ describe("Room", function () {
|
||||
service_members: userB,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
expect(room.getDefaultRoomName(userA)).toEqual("User B");
|
||||
});
|
||||
|
||||
it("should return 'Empty room' if the only other member is a functional member", async function () {
|
||||
const room = new Room(roomId, new TestClient(userA).client, userA);
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkMembership({
|
||||
user: userA,
|
||||
mship: KnownMembership.Join,
|
||||
@ -2570,13 +2617,16 @@ describe("Room", function () {
|
||||
service_members: [userB],
|
||||
},
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
expect(room.getDefaultRoomName(userA)).toEqual("Empty room");
|
||||
});
|
||||
|
||||
it("should return 'User B' if User B is the only other member who isn't a functional member", async function () {
|
||||
const room = new Room(roomId, new TestClient(userA).client, userA);
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkMembership({
|
||||
user: userA,
|
||||
mship: KnownMembership.Join,
|
||||
@ -2608,13 +2658,16 @@ describe("Room", function () {
|
||||
service_members: [userC],
|
||||
},
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
expect(room.getDefaultRoomName(userA)).toEqual("User B");
|
||||
});
|
||||
|
||||
it("should return 'Empty room' if all other members are functional members", async function () {
|
||||
const room = new Room(roomId, new TestClient(userA).client, userA);
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkMembership({
|
||||
user: userA,
|
||||
mship: KnownMembership.Join,
|
||||
@ -2646,13 +2699,16 @@ describe("Room", function () {
|
||||
service_members: [userB, userC],
|
||||
},
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
expect(room.getDefaultRoomName(userA)).toEqual("Empty room");
|
||||
});
|
||||
|
||||
it("should not break if an unjoined user is marked as a service user", async function () {
|
||||
const room = new Room(roomId, new TestClient(userA).client, userA);
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
utils.mkMembership({
|
||||
user: userA,
|
||||
mship: KnownMembership.Join,
|
||||
@ -2677,7 +2733,9 @@ describe("Room", function () {
|
||||
service_members: [userC],
|
||||
},
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
expect(room.getDefaultRoomName(userA)).toEqual("User B");
|
||||
});
|
||||
});
|
||||
@ -2783,7 +2841,7 @@ describe("Room", function () {
|
||||
});
|
||||
|
||||
const prom = emitPromise(room, ThreadEvent.New);
|
||||
await room.addLiveEvents([randomMessage, threadRoot, threadResponse]);
|
||||
await room.addLiveEvents([randomMessage, threadRoot, threadResponse], { addToState: false });
|
||||
const thread: Thread = await prom;
|
||||
await emitPromise(room, ThreadEvent.Update);
|
||||
|
||||
@ -2812,7 +2870,10 @@ describe("Room", function () {
|
||||
// XXX: If we add the relation to the thread response before the thread finishes fetching via /relations
|
||||
// then the test will fail
|
||||
await emitPromise(room, ThreadEvent.Update);
|
||||
await Promise.all([emitPromise(room, ThreadEvent.Update), room.addLiveEvents([threadResponseEdit])]);
|
||||
await Promise.all([
|
||||
emitPromise(room, ThreadEvent.Update),
|
||||
room.addLiveEvents([threadResponseEdit], { addToState: false }),
|
||||
]);
|
||||
expect(thread.replyToEvent!.getContent().body).toBe(threadResponseEdit.getContent()["m.new_content"].body);
|
||||
});
|
||||
|
||||
@ -2823,12 +2884,12 @@ describe("Room", function () {
|
||||
const threadRoot = mkMessage();
|
||||
const threadResponse1 = mkThreadResponse(threadRoot);
|
||||
|
||||
await room.addLiveEvents([threadRoot]);
|
||||
await room.addLiveEvents([threadRoot], { addToState: false });
|
||||
|
||||
const onEvent = jest.fn();
|
||||
room.on(RoomEvent.Timeline, onEvent);
|
||||
|
||||
await room.addLiveEvents([threadResponse1]);
|
||||
await room.addLiveEvents([threadResponse1], { addToState: false });
|
||||
|
||||
expect(onEvent).toHaveBeenCalled();
|
||||
});
|
||||
@ -2841,7 +2902,7 @@ describe("Room", function () {
|
||||
const threadResponse1 = mkThreadResponse(threadRoot);
|
||||
|
||||
const newThreadEventPromise = emitPromise(room, ThreadEvent.New);
|
||||
await room.addLiveEvents([threadRoot, threadResponse1]);
|
||||
await room.addLiveEvents([threadRoot, threadResponse1], { addToState: false });
|
||||
const thread = await newThreadEventPromise;
|
||||
|
||||
expect(thread.timeline).toContain(threadResponse1);
|
||||
@ -2873,7 +2934,7 @@ describe("Room", function () {
|
||||
});
|
||||
|
||||
let prom = emitPromise(room, ThreadEvent.New);
|
||||
await room.addLiveEvents([threadRoot, threadResponse1, threadResponse2]);
|
||||
await room.addLiveEvents([threadRoot, threadResponse1, threadResponse2], { addToState: false });
|
||||
const thread = await prom;
|
||||
|
||||
expect(thread).toHaveLength(2);
|
||||
@ -2907,7 +2968,7 @@ describe("Room", function () {
|
||||
|
||||
prom = emitPromise(thread, ThreadEvent.Update);
|
||||
const threadResponse1Redaction = mkRedaction(threadResponse1);
|
||||
await room.addLiveEvents([threadResponse1Redaction]);
|
||||
await room.addLiveEvents([threadResponse1Redaction], { addToState: false });
|
||||
await prom;
|
||||
expect(thread).toHaveLength(1);
|
||||
expect(thread.replyToEvent.getId()).toBe(threadResponse2.getId());
|
||||
@ -2944,7 +3005,9 @@ describe("Room", function () {
|
||||
});
|
||||
|
||||
const prom = emitPromise(room, ThreadEvent.New);
|
||||
await room.addLiveEvents([threadRoot, threadResponse1, threadResponse2, threadResponse2Reaction]);
|
||||
await room.addLiveEvents([threadRoot, threadResponse1, threadResponse2, threadResponse2Reaction], {
|
||||
addToState: false,
|
||||
});
|
||||
const thread = await prom;
|
||||
await emitPromise(room, ThreadEvent.Update);
|
||||
|
||||
@ -2952,7 +3015,7 @@ describe("Room", function () {
|
||||
expect(thread.replyToEvent.getId()).toBe(threadResponse2.getId());
|
||||
|
||||
const threadResponse2ReactionRedaction = mkRedaction(threadResponse2Reaction);
|
||||
await room.addLiveEvents([threadResponse2ReactionRedaction]);
|
||||
await room.addLiveEvents([threadResponse2ReactionRedaction], { addToState: false });
|
||||
expect(thread).toHaveLength(2);
|
||||
expect(thread.replyToEvent.getId()).toBe(threadResponse2.getId());
|
||||
});
|
||||
@ -2984,14 +3047,16 @@ describe("Room", function () {
|
||||
});
|
||||
|
||||
const prom = emitPromise(room, ThreadEvent.New);
|
||||
await room.addLiveEvents([threadRoot, threadResponse1, threadResponse2, threadResponse2Reaction]);
|
||||
await room.addLiveEvents([threadRoot, threadResponse1, threadResponse2, threadResponse2Reaction], {
|
||||
addToState: false,
|
||||
});
|
||||
const thread = await prom;
|
||||
|
||||
expect(thread).toHaveLength(2);
|
||||
expect(thread.replyToEvent.getId()).toBe(threadResponse2.getId());
|
||||
|
||||
const threadRootRedaction = mkRedaction(threadRoot);
|
||||
await room.addLiveEvents([threadRootRedaction]);
|
||||
await room.addLiveEvents([threadRootRedaction], { addToState: false });
|
||||
|
||||
// We can't wait for a thread update here because there shouldn't be one (which is
|
||||
// what we're asserting). Flush any promises to try to get more certainty that an
|
||||
@ -3048,12 +3113,12 @@ describe("Room", function () {
|
||||
});
|
||||
|
||||
let prom = emitPromise(room, ThreadEvent.New);
|
||||
await room.addLiveEvents([threadRoot, threadResponse1]);
|
||||
await room.addLiveEvents([threadRoot, threadResponse1], { addToState: false });
|
||||
const thread: Thread = await prom;
|
||||
await emitPromise(room, ThreadEvent.Update);
|
||||
|
||||
expect(thread.initialEventsFetched).toBeTruthy();
|
||||
await room.addLiveEvents([threadResponse2]);
|
||||
await room.addLiveEvents([threadResponse2], { addToState: false });
|
||||
expect(thread).toHaveLength(2);
|
||||
expect(thread.replyToEvent!.getId()).toBe(threadResponse2.getId());
|
||||
|
||||
@ -3074,7 +3139,7 @@ describe("Room", function () {
|
||||
|
||||
await emitPromise(room, ThreadEvent.Update);
|
||||
const threadResponse2Redaction = mkRedaction(threadResponse2);
|
||||
await room.addLiveEvents([threadResponse2Redaction]);
|
||||
await room.addLiveEvents([threadResponse2Redaction], { addToState: false });
|
||||
expect(thread).toHaveLength(1);
|
||||
expect(thread.replyToEvent!.getId()).toBe(threadResponse1.getId());
|
||||
|
||||
@ -3096,19 +3161,42 @@ describe("Room", function () {
|
||||
prom = emitPromise(room, ThreadEvent.Delete);
|
||||
const prom2 = emitPromise(room, RoomEvent.Timeline);
|
||||
const threadResponse1Redaction = mkRedaction(threadResponse1);
|
||||
await room.addLiveEvents([threadResponse1Redaction]);
|
||||
await room.addLiveEvents([threadResponse1Redaction], { addToState: false });
|
||||
await prom;
|
||||
await prom2;
|
||||
expect(thread).toHaveLength(0);
|
||||
expect(thread.replyToEvent!.getId()).toBe(threadRoot.getId());
|
||||
});
|
||||
|
||||
it("should add event to thread without server side support", async () => {
|
||||
room.client.supportsThreads = () => true;
|
||||
Thread.setServerSideSupport(FeatureSupport.None);
|
||||
|
||||
const threadRoot = mkMessage();
|
||||
const threadResponse1 = mkThreadResponse(threadRoot);
|
||||
threadResponse1.getContent()["m.relates_to"]!.rel_type = "io.element.thread";
|
||||
|
||||
const thread = room.createThread(threadRoot.getId()!, threadRoot, [threadResponse1], false)!;
|
||||
|
||||
expect(thread.events).toContain(threadResponse1);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
// Clear the latch created by `Thread.setServerSideSupport(FeatureSupport.None);`
|
||||
FILTER_RELATED_BY_SENDERS.setPreferUnstable(false);
|
||||
FILTER_RELATED_BY_REL_TYPES.setPreferUnstable(false);
|
||||
THREAD_RELATION_TYPE.setPreferUnstable(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("eventShouldLiveIn", () => {
|
||||
const client = new TestClient(userA).client;
|
||||
const room = new Room(roomId, client, userA);
|
||||
|
||||
beforeEach(() => {
|
||||
client.supportsThreads = () => true;
|
||||
Thread.setServerSideSupport(FeatureSupport.Stable);
|
||||
const room = new Room(roomId, client, userA);
|
||||
});
|
||||
|
||||
it("thread root and its relations&redactions should be in main timeline", () => {
|
||||
const randomMessage = mkMessage();
|
||||
@ -3258,7 +3346,7 @@ describe("Room", function () {
|
||||
const events = [threadRoot, rootReaction, threadResponse, threadReaction];
|
||||
|
||||
const prom = emitPromise(room, ThreadEvent.New);
|
||||
await room.addLiveEvents(events);
|
||||
await room.addLiveEvents(events, { addToState: false });
|
||||
const thread = await prom;
|
||||
expect(thread).toBe(threadRoot.getThread());
|
||||
expect(thread.rootEvent).toBe(threadRoot);
|
||||
@ -3893,22 +3981,22 @@ describe("Room", function () {
|
||||
|
||||
it("Returns null if the create event has no predecessor", async () => {
|
||||
const room = new Room("roomid", client!, "@u:example.com");
|
||||
await room.addLiveEvents([roomCreateEvent("roomid", null)]);
|
||||
await room.addLiveEvents([roomCreateEvent("roomid", null)], { addToState: true });
|
||||
expect(room.findPredecessor()).toBeNull();
|
||||
});
|
||||
|
||||
it("Returns the predecessor ID if one is provided via create event", async () => {
|
||||
const room = new Room("roomid", client!, "@u:example.com");
|
||||
await room.addLiveEvents([roomCreateEvent("roomid", "replacedroomid")]);
|
||||
await room.addLiveEvents([roomCreateEvent("roomid", "replacedroomid")], { addToState: true });
|
||||
expect(room.findPredecessor()).toEqual({ roomId: "replacedroomid", eventId: "id_of_last_known_event" });
|
||||
});
|
||||
|
||||
it("Prefers the m.predecessor event if one exists", async () => {
|
||||
const room = new Room("roomid", client!, "@u:example.com");
|
||||
await room.addLiveEvents([
|
||||
roomCreateEvent("roomid", "replacedroomid"),
|
||||
predecessorEvent("roomid", "otherreplacedroomid"),
|
||||
]);
|
||||
await room.addLiveEvents(
|
||||
[roomCreateEvent("roomid", "replacedroomid"), predecessorEvent("roomid", "otherreplacedroomid")],
|
||||
{ addToState: true },
|
||||
);
|
||||
const useMsc3946 = true;
|
||||
expect(room.findPredecessor(useMsc3946)).toEqual({
|
||||
roomId: "otherreplacedroomid",
|
||||
@ -3919,10 +4007,16 @@ describe("Room", function () {
|
||||
|
||||
it("uses the m.predecessor event ID if provided", async () => {
|
||||
const room = new Room("roomid", client!, "@u:example.com");
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
roomCreateEvent("roomid", "replacedroomid"),
|
||||
predecessorEvent("roomid", "otherreplacedroomid", "lstevtid", ["one.example.com", "two.example.com"]),
|
||||
]);
|
||||
predecessorEvent("roomid", "otherreplacedroomid", "lstevtid", [
|
||||
"one.example.com",
|
||||
"two.example.com",
|
||||
]),
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
const useMsc3946 = true;
|
||||
expect(room.findPredecessor(useMsc3946)).toEqual({
|
||||
roomId: "otherreplacedroomid",
|
||||
@ -3933,10 +4027,10 @@ describe("Room", function () {
|
||||
|
||||
it("Ignores the m.predecessor event if we don't ask to use it", async () => {
|
||||
const room = new Room("roomid", client!, "@u:example.com");
|
||||
await room.addLiveEvents([
|
||||
roomCreateEvent("roomid", "replacedroomid"),
|
||||
predecessorEvent("roomid", "otherreplacedroomid"),
|
||||
]);
|
||||
await room.addLiveEvents(
|
||||
[roomCreateEvent("roomid", "replacedroomid"), predecessorEvent("roomid", "otherreplacedroomid")],
|
||||
{ addToState: true },
|
||||
);
|
||||
// Don't provide an argument for msc3946ProcessDynamicPredecessor -
|
||||
// we should ignore the predecessor event.
|
||||
expect(room.findPredecessor()).toEqual({ roomId: "replacedroomid", eventId: "id_of_last_known_event" });
|
||||
@ -3944,10 +4038,13 @@ describe("Room", function () {
|
||||
|
||||
it("Ignores the m.predecessor event and returns null if we don't ask to use it", async () => {
|
||||
const room = new Room("roomid", client!, "@u:example.com");
|
||||
await room.addLiveEvents([
|
||||
await room.addLiveEvents(
|
||||
[
|
||||
roomCreateEvent("roomid", null), // Create event has no predecessor
|
||||
predecessorEvent("roomid", "otherreplacedroomid", "lastevtid"),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
// Don't provide an argument for msc3946ProcessDynamicPredecessor -
|
||||
// we should ignore the predecessor event.
|
||||
expect(room.findPredecessor()).toBeNull();
|
||||
@ -4077,4 +4174,64 @@ describe("Room", function () {
|
||||
expect(client.fetchCapabilities).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getOrCreateFilteredTimelineSet", () => {
|
||||
it("should locally filter events if prepopulateTimeline=true", () => {
|
||||
room.addLiveEvents(
|
||||
[
|
||||
utils.mkEvent(
|
||||
{
|
||||
event: true,
|
||||
type: EventType.RoomMessage,
|
||||
user: userA,
|
||||
room: roomId,
|
||||
content: {
|
||||
body: "ev1",
|
||||
},
|
||||
},
|
||||
room.client,
|
||||
),
|
||||
utils.mkEvent(
|
||||
{
|
||||
event: true,
|
||||
type: "custom.event.type",
|
||||
user: userA,
|
||||
room: roomId,
|
||||
content: {
|
||||
body: "ev2",
|
||||
},
|
||||
},
|
||||
room.client,
|
||||
),
|
||||
utils.mkEvent(
|
||||
{
|
||||
event: true,
|
||||
type: EventType.RoomMessage,
|
||||
user: userA,
|
||||
room: roomId,
|
||||
content: {
|
||||
body: "ev3",
|
||||
},
|
||||
},
|
||||
room.client,
|
||||
),
|
||||
],
|
||||
{
|
||||
addToState: false,
|
||||
},
|
||||
);
|
||||
|
||||
const filter = Filter.fromJson(room.client.getUserId(), "filterId", {
|
||||
room: {
|
||||
timeline: {
|
||||
types: ["custom.event.type"],
|
||||
},
|
||||
},
|
||||
});
|
||||
const timelineSet = room.getOrCreateFilteredTimelineSet(filter, { prepopulateTimeline: true });
|
||||
const filteredEvents = timelineSet.getLiveTimeline().getEvents();
|
||||
expect(filteredEvents).toHaveLength(1);
|
||||
expect(filteredEvents[0].getContent().body).toEqual("ev2");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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: {},
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -1566,7 +1566,8 @@ describe("Group Call", function () {
|
||||
async (roomId, eventType, content, stateKey) => {
|
||||
const eventId = `$${Math.random()}`;
|
||||
if (roomId === room.roomId) {
|
||||
room.addLiveEvents([
|
||||
room.addLiveEvents(
|
||||
[
|
||||
new MatrixEvent({
|
||||
event_id: eventId,
|
||||
type: eventType,
|
||||
@ -1575,7 +1576,9 @@ describe("Group Call", function () {
|
||||
content,
|
||||
state_key: stateKey,
|
||||
}),
|
||||
]);
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
}
|
||||
return { event_id: eventId };
|
||||
},
|
||||
|
@ -6136,7 +6136,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
room.partitionThreadedEvents(matrixEvents);
|
||||
|
||||
this.processAggregatedTimelineEvents(room, timelineEvents);
|
||||
room.addEventsToTimeline(timelineEvents, true, room.getLiveTimeline());
|
||||
room.addEventsToTimeline(timelineEvents, true, true, room.getLiveTimeline());
|
||||
this.processThreadEvents(room, threadedEvents, true);
|
||||
unknownRelations.forEach((event) => room.relations.aggregateChildEvent(event));
|
||||
|
||||
@ -6248,7 +6248,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
}
|
||||
|
||||
const [timelineEvents, threadedEvents, unknownRelations] = timelineSet.room.partitionThreadedEvents(events);
|
||||
timelineSet.addEventsToTimeline(timelineEvents, true, timeline, res.start);
|
||||
timelineSet.addEventsToTimeline(timelineEvents, true, false, timeline, res.start);
|
||||
// The target event is not in a thread but process the contextual events, so we can show any threads around it.
|
||||
this.processThreadEvents(timelineSet.room, threadedEvents, true);
|
||||
this.processAggregatedTimelineEvents(timelineSet.room, timelineEvents);
|
||||
@ -6342,10 +6342,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
timeline.initialiseState(res.state.map(mapper));
|
||||
}
|
||||
|
||||
timelineSet.addEventsToTimeline(events, true, timeline, resNewer.next_batch);
|
||||
timelineSet.addEventsToTimeline(events, true, false, timeline, resNewer.next_batch);
|
||||
if (!resOlder.next_batch) {
|
||||
const originalEvent = await this.fetchRoomEvent(timelineSet.room.roomId, thread.id);
|
||||
timelineSet.addEventsToTimeline([mapper(originalEvent)], true, timeline, null);
|
||||
timelineSet.addEventsToTimeline([mapper(originalEvent)], true, false, timeline, null);
|
||||
}
|
||||
timeline.setPaginationToken(resOlder.next_batch ?? null, Direction.Backward);
|
||||
timeline.setPaginationToken(resNewer.next_batch ?? null, Direction.Forward);
|
||||
@ -6399,10 +6399,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
const timeline = timelineSet.getLiveTimeline();
|
||||
timeline.getState(EventTimeline.BACKWARDS)!.setUnknownStateEvents(res.state.map(mapper));
|
||||
|
||||
timelineSet.addEventsToTimeline(events, true, timeline, null);
|
||||
timelineSet.addEventsToTimeline(events, true, false, timeline, null);
|
||||
if (!resOlder.next_batch) {
|
||||
const originalEvent = await this.fetchRoomEvent(timelineSet.room.roomId, thread.id);
|
||||
timelineSet.addEventsToTimeline([mapper(originalEvent)], true, timeline, null);
|
||||
timelineSet.addEventsToTimeline([mapper(originalEvent)], true, false, timeline, null);
|
||||
}
|
||||
timeline.setPaginationToken(resOlder.next_batch ?? null, Direction.Backward);
|
||||
timeline.setPaginationToken(null, Direction.Forward);
|
||||
@ -6665,7 +6665,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
// No need to partition events for threads here, everything lives
|
||||
// in the notification timeline set
|
||||
const timelineSet = eventTimeline.getTimelineSet();
|
||||
timelineSet.addEventsToTimeline(matrixEvents, backwards, eventTimeline, token);
|
||||
timelineSet.addEventsToTimeline(matrixEvents, backwards, false, eventTimeline, token);
|
||||
this.processAggregatedTimelineEvents(timelineSet.room, matrixEvents);
|
||||
|
||||
// if we've hit the end of the timeline, we need to stop trying to
|
||||
@ -6708,7 +6708,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
const matrixEvents = res.chunk.filter(noUnsafeEventProps).map(this.getEventMapper());
|
||||
|
||||
const timelineSet = eventTimeline.getTimelineSet();
|
||||
timelineSet.addEventsToTimeline(matrixEvents, backwards, eventTimeline, token);
|
||||
timelineSet.addEventsToTimeline(matrixEvents, backwards, false, eventTimeline, token);
|
||||
this.processAggregatedTimelineEvents(room, matrixEvents);
|
||||
this.processThreadRoots(room, matrixEvents, backwards);
|
||||
|
||||
@ -6756,12 +6756,12 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
const newToken = res.next_batch;
|
||||
|
||||
const timelineSet = eventTimeline.getTimelineSet();
|
||||
timelineSet.addEventsToTimeline(matrixEvents, backwards, eventTimeline, newToken ?? null);
|
||||
timelineSet.addEventsToTimeline(matrixEvents, backwards, false, eventTimeline, newToken ?? null);
|
||||
if (!newToken && backwards) {
|
||||
const originalEvent =
|
||||
thread.rootEvent ??
|
||||
mapper(await this.fetchRoomEvent(eventTimeline.getRoomId() ?? "", thread.id));
|
||||
timelineSet.addEventsToTimeline([originalEvent], true, eventTimeline, null);
|
||||
timelineSet.addEventsToTimeline([originalEvent], true, false, eventTimeline, null);
|
||||
}
|
||||
this.processAggregatedTimelineEvents(timelineSet.room, matrixEvents);
|
||||
|
||||
@ -6800,7 +6800,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
|
||||
const timelineSet = eventTimeline.getTimelineSet();
|
||||
const [timelineEvents, , unknownRelations] = room.partitionThreadedEvents(matrixEvents);
|
||||
timelineSet.addEventsToTimeline(timelineEvents, backwards, eventTimeline, token);
|
||||
timelineSet.addEventsToTimeline(timelineEvents, backwards, false, eventTimeline, token);
|
||||
this.processAggregatedTimelineEvents(room, timelineEvents);
|
||||
this.processThreadRoots(
|
||||
room,
|
||||
|
@ -284,7 +284,13 @@ export class RoomWidgetClient extends MatrixClient {
|
||||
const rawEvents = await this.widgetApi.readStateEvents(eventType, undefined, stateKey, [this.roomId]);
|
||||
const events = rawEvents.map((rawEvent) => new MatrixEvent(rawEvent as Partial<IEvent>));
|
||||
|
||||
await this.syncApi!.injectRoomEvents(this.room!, [], events);
|
||||
if (this.syncApi instanceof SyncApi) {
|
||||
// Passing undefined for `stateAfterEventList` allows will make `injectRoomEvents` run in legacy mode
|
||||
// -> state events in `timelineEventList` will update the state.
|
||||
await this.syncApi.injectRoomEvents(this.room!, undefined, events);
|
||||
} else {
|
||||
await this.syncApi!.injectRoomEvents(this.room!, events); // Sliding Sync
|
||||
}
|
||||
events.forEach((event) => {
|
||||
this.emit(ClientEvent.Event, event);
|
||||
logger.info(`Backfilled event ${event.getId()} ${event.getType()} ${event.getStateKey()}`);
|
||||
@ -567,7 +573,34 @@ export class RoomWidgetClient extends MatrixClient {
|
||||
|
||||
// Only inject once we have update the txId
|
||||
await this.updateTxId(event);
|
||||
await this.syncApi!.injectRoomEvents(this.room!, [], [event]);
|
||||
|
||||
// The widget API does not tell us whether a state event came from `state_after` or not so we assume legacy behaviour for now.
|
||||
if (this.syncApi instanceof SyncApi) {
|
||||
// The code will want to be something like:
|
||||
// ```
|
||||
// if (!params.addToTimeline && !params.addToState) {
|
||||
// // Passing undefined for `stateAfterEventList` makes `injectRoomEvents` run in "legacy mode"
|
||||
// // -> state events part of the `timelineEventList` parameter will update the state.
|
||||
// this.injectRoomEvents(this.room!, [], undefined, [event]);
|
||||
// } else {
|
||||
// this.injectRoomEvents(this.room!, undefined, params.addToState ? [event] : [], params.addToTimeline ? [event] : []);
|
||||
// }
|
||||
// ```
|
||||
|
||||
// Passing undefined for `stateAfterEventList` allows will make `injectRoomEvents` run in legacy mode
|
||||
// -> state events in `timelineEventList` will update the state.
|
||||
await this.syncApi.injectRoomEvents(this.room!, [], undefined, [event]);
|
||||
} else {
|
||||
// The code will want to be something like:
|
||||
// ```
|
||||
// if (!params.addToTimeline && !params.addToState) {
|
||||
// this.injectRoomEvents(this.room!, [], [event]);
|
||||
// } else {
|
||||
// this.injectRoomEvents(this.room!, params.addToState ? [event] : [], params.addToTimeline ? [event] : []);
|
||||
// }
|
||||
// ```
|
||||
await this.syncApi!.injectRoomEvents(this.room!, [], [event]); // Sliding Sync
|
||||
}
|
||||
this.emit(ClientEvent.Event, event);
|
||||
this.setSyncState(SyncState.Syncing);
|
||||
logger.info(`Received event ${event.getId()} ${event.getType()} ${event.getStateKey()}`);
|
||||
|
@ -58,13 +58,13 @@ export interface IRoomTimelineData {
|
||||
}
|
||||
|
||||
export interface IAddEventToTimelineOptions
|
||||
extends Pick<IAddEventOptions, "toStartOfTimeline" | "roomState" | "timelineWasEmpty"> {
|
||||
extends Pick<IAddEventOptions, "toStartOfTimeline" | "roomState" | "timelineWasEmpty" | "addToState"> {
|
||||
/** Whether the sync response came from cache */
|
||||
fromCache?: boolean;
|
||||
}
|
||||
|
||||
export interface IAddLiveEventOptions
|
||||
extends Pick<IAddEventToTimelineOptions, "fromCache" | "roomState" | "timelineWasEmpty"> {
|
||||
extends Pick<IAddEventToTimelineOptions, "fromCache" | "roomState" | "timelineWasEmpty" | "addToState"> {
|
||||
/** Applies to events in the timeline only. If this is 'replace' then if a
|
||||
* duplicate is encountered, the event passed to this function will replace
|
||||
* the existing event in the timeline. If this is not specified, or is
|
||||
@ -391,6 +391,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
public addEventsToTimeline(
|
||||
events: MatrixEvent[],
|
||||
toStartOfTimeline: boolean,
|
||||
addToState: boolean,
|
||||
timeline: EventTimeline,
|
||||
paginationToken?: string | null,
|
||||
): void {
|
||||
@ -495,6 +496,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
// we don't know about this event yet. Just add it to the timeline.
|
||||
this.addEventToTimeline(event, timeline, {
|
||||
toStartOfTimeline,
|
||||
addToState,
|
||||
});
|
||||
lastEventWasNew = true;
|
||||
didUpdate = true;
|
||||
@ -592,7 +594,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
*/
|
||||
public addLiveEvent(
|
||||
event: MatrixEvent,
|
||||
{ duplicateStrategy, fromCache, roomState, timelineWasEmpty }: IAddLiveEventOptions = {},
|
||||
{ duplicateStrategy, fromCache, roomState, timelineWasEmpty, addToState }: IAddLiveEventOptions,
|
||||
): void {
|
||||
if (this.filter) {
|
||||
const events = this.filter.filterRoomTimeline([event]);
|
||||
@ -630,6 +632,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
fromCache,
|
||||
roomState,
|
||||
timelineWasEmpty,
|
||||
addToState,
|
||||
});
|
||||
}
|
||||
|
||||
@ -649,40 +652,8 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
public addEventToTimeline(
|
||||
event: MatrixEvent,
|
||||
timeline: EventTimeline,
|
||||
{ toStartOfTimeline, fromCache, roomState, timelineWasEmpty }: IAddEventToTimelineOptions,
|
||||
): void;
|
||||
/**
|
||||
* @deprecated In favor of the overload with `IAddEventToTimelineOptions`
|
||||
*/
|
||||
public addEventToTimeline(
|
||||
event: MatrixEvent,
|
||||
timeline: EventTimeline,
|
||||
toStartOfTimeline: boolean,
|
||||
fromCache?: boolean,
|
||||
roomState?: RoomState,
|
||||
): void;
|
||||
public addEventToTimeline(
|
||||
event: MatrixEvent,
|
||||
timeline: EventTimeline,
|
||||
toStartOfTimelineOrOpts: boolean | IAddEventToTimelineOptions,
|
||||
fromCache = false,
|
||||
roomState?: RoomState,
|
||||
{ toStartOfTimeline, fromCache = false, roomState, timelineWasEmpty, addToState }: IAddEventToTimelineOptions,
|
||||
): void {
|
||||
let toStartOfTimeline = !!toStartOfTimelineOrOpts;
|
||||
let timelineWasEmpty: boolean | undefined;
|
||||
if (typeof toStartOfTimelineOrOpts === "object") {
|
||||
({ toStartOfTimeline, fromCache = false, roomState, timelineWasEmpty } = toStartOfTimelineOrOpts);
|
||||
} else if (toStartOfTimelineOrOpts !== undefined) {
|
||||
// Deprecation warning
|
||||
// FIXME: Remove after 2023-06-01 (technical debt)
|
||||
logger.warn(
|
||||
"Overload deprecated: " +
|
||||
"`EventTimelineSet.addEventToTimeline(event, timeline, toStartOfTimeline, fromCache?, roomState?)` " +
|
||||
"is deprecated in favor of the overload with " +
|
||||
"`EventTimelineSet.addEventToTimeline(event, timeline, IAddEventToTimelineOptions)`",
|
||||
);
|
||||
}
|
||||
|
||||
if (timeline.getTimelineSet() !== this) {
|
||||
throw new Error(`EventTimelineSet.addEventToTimeline: Timeline=${timeline.toString()} does not belong " +
|
||||
"in timelineSet(threadId=${this.thread?.id})`);
|
||||
@ -713,6 +684,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
toStartOfTimeline,
|
||||
roomState,
|
||||
timelineWasEmpty,
|
||||
addToState,
|
||||
});
|
||||
this._eventIdToTimeline.set(eventId, timeline);
|
||||
|
||||
@ -741,7 +713,12 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
* @remarks
|
||||
* Fires {@link RoomEvent.Timeline}
|
||||
*/
|
||||
public insertEventIntoTimeline(event: MatrixEvent, timeline: EventTimeline, roomState: RoomState): void {
|
||||
public insertEventIntoTimeline(
|
||||
event: MatrixEvent,
|
||||
timeline: EventTimeline,
|
||||
roomState: RoomState,
|
||||
addToState: boolean,
|
||||
): void {
|
||||
if (timeline.getTimelineSet() !== this) {
|
||||
throw new Error(`EventTimelineSet.insertEventIntoTimeline: Timeline=${timeline.toString()} does not belong " +
|
||||
"in timelineSet(threadId=${this.thread?.id})`);
|
||||
@ -777,6 +754,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
fromCache: false,
|
||||
timelineWasEmpty: false,
|
||||
roomState,
|
||||
addToState,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -799,7 +777,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
// If we got to the end of the loop, insertIndex points at the end of
|
||||
// the list.
|
||||
|
||||
timeline.insertEvent(event, insertIndex, roomState);
|
||||
timeline.insertEvent(event, insertIndex, roomState, addToState);
|
||||
this._eventIdToTimeline.set(eventId, timeline);
|
||||
|
||||
const data: IRoomTimelineData = {
|
||||
@ -832,6 +810,7 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
|
||||
} else if (!this.filter || this.filter.filterRoomTimeline([localEvent]).length) {
|
||||
this.addEventToTimeline(localEvent, this.liveTimeline, {
|
||||
toStartOfTimeline: false,
|
||||
addToState: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,11 @@ export interface IAddEventOptions extends Pick<IMarkerFoundOptions, "timelineWas
|
||||
toStartOfTimeline: boolean;
|
||||
/** The state events to reconcile metadata from */
|
||||
roomState?: RoomState;
|
||||
/** Whether to add timeline events to the state as was done in legacy sync v2.
|
||||
* If true then timeline events will be added to the state.
|
||||
* In sync v2 with org.matrix.msc4222.use_state_after and simplified sliding sync,
|
||||
* all state arrives explicitly and timeline events should not be added. */
|
||||
addToState: boolean;
|
||||
}
|
||||
|
||||
export enum Direction {
|
||||
@ -362,7 +367,7 @@ export class EventTimeline {
|
||||
*/
|
||||
public addEvent(
|
||||
event: MatrixEvent,
|
||||
{ toStartOfTimeline, roomState, timelineWasEmpty }: IAddEventOptions = { toStartOfTimeline: false },
|
||||
{ toStartOfTimeline, roomState, timelineWasEmpty, addToState }: IAddEventOptions,
|
||||
): void {
|
||||
if (!roomState) {
|
||||
roomState = toStartOfTimeline ? this.startState : this.endState;
|
||||
@ -374,7 +379,7 @@ export class EventTimeline {
|
||||
EventTimeline.setEventMetadata(event, roomState!, toStartOfTimeline);
|
||||
|
||||
// modify state but only on unfiltered timelineSets
|
||||
if (event.isState() && timelineSet.room.getUnfilteredTimelineSet() === timelineSet) {
|
||||
if (addToState && event.isState() && timelineSet.room.getUnfilteredTimelineSet() === timelineSet) {
|
||||
roomState?.setStateEvents([event], { timelineWasEmpty });
|
||||
// it is possible that the act of setting the state event means we
|
||||
// can set more metadata (specifically sender/target props), so try
|
||||
@ -417,14 +422,14 @@ export class EventTimeline {
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public insertEvent(event: MatrixEvent, insertIndex: number, roomState: RoomState): void {
|
||||
public insertEvent(event: MatrixEvent, insertIndex: number, roomState: RoomState, addToState: boolean): void {
|
||||
const timelineSet = this.getTimelineSet();
|
||||
|
||||
if (timelineSet.room) {
|
||||
EventTimeline.setEventMetadata(event, roomState, false);
|
||||
|
||||
// modify state but only on unfiltered timelineSets
|
||||
if (event.isState() && timelineSet.room.getUnfilteredTimelineSet() === timelineSet) {
|
||||
if (addToState && event.isState() && timelineSet.room.getUnfilteredTimelineSet() === timelineSet) {
|
||||
roomState.setStateEvents([event], {});
|
||||
// it is possible that the act of setting the state event means we
|
||||
// can set more metadata (specifically sender/target props), so try
|
||||
|
@ -1250,7 +1250,9 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
|
||||
const timeline = room.getLiveTimeline();
|
||||
// We use insertEventIntoTimeline to insert it in timestamp order,
|
||||
// because we don't know where it should go (until we have MSC4033).
|
||||
timeline.getTimelineSet().insertEventIntoTimeline(this, timeline, timeline.getState(EventTimeline.FORWARDS)!);
|
||||
timeline
|
||||
.getTimelineSet()
|
||||
.insertEventIntoTimeline(this, timeline, timeline.getState(EventTimeline.FORWARDS)!, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1739,10 +1739,11 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
public addEventsToTimeline(
|
||||
events: MatrixEvent[],
|
||||
toStartOfTimeline: boolean,
|
||||
addToState: boolean,
|
||||
timeline: EventTimeline,
|
||||
paginationToken?: string,
|
||||
): void {
|
||||
timeline.getTimelineSet().addEventsToTimeline(events, toStartOfTimeline, timeline, paginationToken);
|
||||
timeline.getTimelineSet().addEventsToTimeline(events, toStartOfTimeline, addToState, timeline, paginationToken);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1907,7 +1908,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
// see https://github.com/vector-im/vector-web/issues/2109
|
||||
|
||||
unfilteredLiveTimeline.getEvents().forEach(function (event) {
|
||||
timelineSet.addLiveEvent(event);
|
||||
timelineSet.addLiveEvent(event, { addToState: false }); // Filtered timeline sets should not track state
|
||||
});
|
||||
|
||||
// find the earliest unfiltered timeline
|
||||
@ -1994,6 +1995,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
if (filterType !== ThreadFilterType.My || currentUserParticipated) {
|
||||
timelineSet.getLiveTimeline().addEvent(thread.rootEvent!, {
|
||||
toStartOfTimeline: false,
|
||||
addToState: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -2068,6 +2070,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
const opts = {
|
||||
duplicateStrategy: DuplicateStrategy.Ignore,
|
||||
fromCache: false,
|
||||
addToState: false,
|
||||
roomState,
|
||||
};
|
||||
this.threadsTimelineSets[0]?.addLiveEvent(rootEvent, opts);
|
||||
@ -2190,6 +2193,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
duplicateStrategy: DuplicateStrategy.Replace,
|
||||
fromCache: false,
|
||||
roomState,
|
||||
addToState: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -2381,9 +2385,13 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
duplicateStrategy: DuplicateStrategy.Replace,
|
||||
fromCache: false,
|
||||
roomState: this.currentState,
|
||||
addToState: false,
|
||||
});
|
||||
} else {
|
||||
timelineSet.addEventToTimeline(thread.rootEvent, timelineSet.getLiveTimeline(), { toStartOfTimeline });
|
||||
timelineSet.addEventToTimeline(thread.rootEvent, timelineSet.getLiveTimeline(), {
|
||||
toStartOfTimeline,
|
||||
addToState: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -2540,7 +2548,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
* Fires {@link RoomEvent.Timeline}
|
||||
*/
|
||||
private addLiveEvent(event: MatrixEvent, addLiveEventOptions: IAddLiveEventOptions): void {
|
||||
const { duplicateStrategy, timelineWasEmpty, fromCache } = addLiveEventOptions;
|
||||
const { duplicateStrategy, timelineWasEmpty, fromCache, addToState } = addLiveEventOptions;
|
||||
|
||||
// add to our timeline sets
|
||||
for (const timelineSet of this.timelineSets) {
|
||||
@ -2548,6 +2556,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
duplicateStrategy,
|
||||
fromCache,
|
||||
timelineWasEmpty,
|
||||
addToState,
|
||||
});
|
||||
}
|
||||
|
||||
@ -2631,11 +2640,13 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
if (timelineSet.getFilter()!.filterRoomTimeline([event]).length) {
|
||||
timelineSet.addEventToTimeline(event, timelineSet.getLiveTimeline(), {
|
||||
toStartOfTimeline: false,
|
||||
addToState: false, // We don't support localEcho of state events yet
|
||||
});
|
||||
}
|
||||
} else {
|
||||
timelineSet.addEventToTimeline(event, timelineSet.getLiveTimeline(), {
|
||||
toStartOfTimeline: false,
|
||||
addToState: false, // We don't support localEcho of state events yet
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -2886,8 +2897,8 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
* @param addLiveEventOptions - addLiveEvent options
|
||||
* @throws If `duplicateStrategy` is not falsey, 'replace' or 'ignore'.
|
||||
*/
|
||||
public async addLiveEvents(events: MatrixEvent[], addLiveEventOptions?: IAddLiveEventOptions): Promise<void> {
|
||||
const { duplicateStrategy, fromCache, timelineWasEmpty = false } = addLiveEventOptions ?? {};
|
||||
public async addLiveEvents(events: MatrixEvent[], addLiveEventOptions: IAddLiveEventOptions): Promise<void> {
|
||||
const { duplicateStrategy, fromCache, timelineWasEmpty = false, addToState } = addLiveEventOptions;
|
||||
if (duplicateStrategy && ["replace", "ignore"].indexOf(duplicateStrategy) === -1) {
|
||||
throw new Error("duplicateStrategy MUST be either 'replace' or 'ignore'");
|
||||
}
|
||||
@ -2902,6 +2913,7 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
|
||||
duplicateStrategy,
|
||||
fromCache,
|
||||
timelineWasEmpty,
|
||||
addToState,
|
||||
};
|
||||
|
||||
// List of extra events to check for being parents of any relations encountered
|
||||
|
@ -208,6 +208,7 @@ export class Thread extends ReadReceipt<ThreadEmittedEvents, ThreadEventHandlerM
|
||||
|
||||
public static setServerSideSupport(status: FeatureSupport): void {
|
||||
Thread.hasServerSideSupport = status;
|
||||
// XXX: This global latching behaviour is really unexpected and means that you can't undo when moving to a server without support
|
||||
if (status !== FeatureSupport.Stable) {
|
||||
FILTER_RELATED_BY_SENDERS.setPreferUnstable(true);
|
||||
FILTER_RELATED_BY_REL_TYPES.setPreferUnstable(true);
|
||||
@ -317,6 +318,7 @@ export class Thread extends ReadReceipt<ThreadEmittedEvents, ThreadEventHandlerM
|
||||
toStartOfTimeline,
|
||||
fromCache: false,
|
||||
roomState: this.roomState,
|
||||
addToState: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -343,7 +345,7 @@ export class Thread extends ReadReceipt<ThreadEmittedEvents, ThreadEventHandlerM
|
||||
if (this.findEventById(eventId)) {
|
||||
return;
|
||||
}
|
||||
this.timelineSet.insertEventIntoTimeline(event, this.liveTimeline, this.roomState);
|
||||
this.timelineSet.insertEventIntoTimeline(event, this.liveTimeline, this.roomState, false);
|
||||
}
|
||||
|
||||
public addEvents(events: MatrixEvent[], toStartOfTimeline: boolean): void {
|
||||
@ -618,7 +620,7 @@ export class Thread extends ReadReceipt<ThreadEmittedEvents, ThreadEventHandlerM
|
||||
// if the thread has regular events, this will just load the last reply.
|
||||
// if the thread is newly created, this will load the root event.
|
||||
if (this.replyCount === 0 && this.rootEvent) {
|
||||
this.timelineSet.addEventsToTimeline([this.rootEvent], true, this.liveTimeline, null);
|
||||
this.timelineSet.addEventsToTimeline([this.rootEvent], true, false, this.liveTimeline, null);
|
||||
this.liveTimeline.setPaginationToken(null, Direction.Backward);
|
||||
} else {
|
||||
this.initalEventFetchProm = this.client.paginateEventTimeline(this.liveTimeline, {
|
||||
|
@ -612,7 +612,7 @@ export class SlidingSyncSdk {
|
||||
timelineEvents = newEvents;
|
||||
if (oldEvents.length > 0) {
|
||||
// old events are scrollback, insert them now
|
||||
room.addEventsToTimeline(oldEvents, true, room.getLiveTimeline(), roomData.prev_batch);
|
||||
room.addEventsToTimeline(oldEvents, true, false, room.getLiveTimeline(), roomData.prev_batch);
|
||||
}
|
||||
}
|
||||
|
||||
@ -754,7 +754,7 @@ export class SlidingSyncSdk {
|
||||
/**
|
||||
* Injects events into a room's model.
|
||||
* @param stateEventList - A list of state events. This is the state
|
||||
* at the *START* of the timeline list if it is supplied.
|
||||
* at the *END* of the timeline list if it is supplied.
|
||||
* @param timelineEventList - A list of timeline events. Lower index
|
||||
* is earlier in time. Higher index is later.
|
||||
* @param numLive - the number of events in timelineEventList which just happened,
|
||||
@ -763,13 +763,9 @@ export class SlidingSyncSdk {
|
||||
public async injectRoomEvents(
|
||||
room: Room,
|
||||
stateEventList: MatrixEvent[],
|
||||
timelineEventList?: MatrixEvent[],
|
||||
numLive?: number,
|
||||
timelineEventList: MatrixEvent[] = [],
|
||||
numLive: number = 0,
|
||||
): Promise<void> {
|
||||
timelineEventList = timelineEventList || [];
|
||||
stateEventList = stateEventList || [];
|
||||
numLive = numLive || 0;
|
||||
|
||||
// If there are no events in the timeline yet, initialise it with
|
||||
// the given state events
|
||||
const liveTimeline = room.getLiveTimeline();
|
||||
@ -820,16 +816,17 @@ export class SlidingSyncSdk {
|
||||
timelineEventList = timelineEventList.slice(0, -1 * liveTimelineEvents.length);
|
||||
}
|
||||
|
||||
// execute the timeline events. This will continue to diverge the current state
|
||||
// if the timeline has any state events in it.
|
||||
// Execute the timeline events.
|
||||
// This also needs to be done before running push rules on the events as they need
|
||||
// to be decorated with sender etc.
|
||||
await room.addLiveEvents(timelineEventList, {
|
||||
fromCache: true,
|
||||
addToState: false,
|
||||
});
|
||||
if (liveTimelineEvents.length > 0) {
|
||||
await room.addLiveEvents(liveTimelineEvents, {
|
||||
fromCache: false,
|
||||
addToState: false,
|
||||
});
|
||||
}
|
||||
|
||||
@ -966,7 +963,7 @@ export class SlidingSyncSdk {
|
||||
return a.getTs() - b.getTs();
|
||||
});
|
||||
this.notifEvents.forEach((event) => {
|
||||
this.client.getNotifTimelineSet()?.addLiveEvent(event);
|
||||
this.client.getNotifTimelineSet()?.addLiveEvent(event, { addToState: false });
|
||||
});
|
||||
this.notifEvents = [];
|
||||
}
|
||||
|
@ -77,7 +77,9 @@ export interface ITimeline {
|
||||
|
||||
export interface IJoinedRoom {
|
||||
"summary": IRoomSummary;
|
||||
"state": IState;
|
||||
// One of `state` or `state_after` is required.
|
||||
"state"?: IState;
|
||||
"org.matrix.msc4222.state_after"?: IState; // https://github.com/matrix-org/matrix-spec-proposals/pull/4222
|
||||
"timeline": ITimeline;
|
||||
"ephemeral": IEphemeral;
|
||||
"account_data": IAccountData;
|
||||
@ -106,9 +108,11 @@ export interface IInvitedRoom {
|
||||
}
|
||||
|
||||
export interface ILeftRoom {
|
||||
state: IState;
|
||||
timeline: ITimeline;
|
||||
account_data: IAccountData;
|
||||
// One of `state` or `state_after` is required.
|
||||
"state"?: IState;
|
||||
"org.matrix.msc4222.state_after"?: IState;
|
||||
"timeline": ITimeline;
|
||||
"account_data": IAccountData;
|
||||
}
|
||||
|
||||
export interface IKnockedRoom {
|
||||
@ -481,13 +485,18 @@ export class SyncAccumulator {
|
||||
// Work out the current state. The deltas need to be applied in the order:
|
||||
// - existing state which didn't come down /sync.
|
||||
// - State events under the 'state' key.
|
||||
// - State events in the 'timeline'.
|
||||
// - State events under the 'state_after' key OR state events in the 'timeline' if 'state_after' is not present.
|
||||
data.state?.events?.forEach((e) => {
|
||||
setState(currentData._currentState, e);
|
||||
});
|
||||
data["org.matrix.msc4222.state_after"]?.events?.forEach((e) => {
|
||||
setState(currentData._currentState, e);
|
||||
});
|
||||
data.timeline?.events?.forEach((e, index) => {
|
||||
if (!data["org.matrix.msc4222.state_after"]) {
|
||||
// this nops if 'e' isn't a state event
|
||||
setState(currentData._currentState, e);
|
||||
}
|
||||
// append the event to the timeline. The back-pagination token
|
||||
// corresponds to the first event in the timeline
|
||||
let transformedEvent: TaggedEvent;
|
||||
@ -563,17 +572,22 @@ export class SyncAccumulator {
|
||||
});
|
||||
Object.keys(this.joinRooms).forEach((roomId) => {
|
||||
const roomData = this.joinRooms[roomId];
|
||||
const roomJson: IJoinedRoom = {
|
||||
ephemeral: { events: [] },
|
||||
account_data: { events: [] },
|
||||
state: { events: [] },
|
||||
timeline: {
|
||||
const roomJson: IJoinedRoom & {
|
||||
// We track both `state` and `state_after` for downgrade compatibility
|
||||
"state": IState;
|
||||
"org.matrix.msc4222.state_after": IState;
|
||||
} = {
|
||||
"ephemeral": { events: [] },
|
||||
"account_data": { events: [] },
|
||||
"state": { events: [] },
|
||||
"org.matrix.msc4222.state_after": { events: [] },
|
||||
"timeline": {
|
||||
events: [],
|
||||
prev_batch: null,
|
||||
},
|
||||
unread_notifications: roomData._unreadNotifications,
|
||||
unread_thread_notifications: roomData._unreadThreadNotifications,
|
||||
summary: roomData._summary as IRoomSummary,
|
||||
"unread_notifications": roomData._unreadNotifications,
|
||||
"unread_thread_notifications": roomData._unreadThreadNotifications,
|
||||
"summary": roomData._summary as IRoomSummary,
|
||||
};
|
||||
// Add account data
|
||||
Object.keys(roomData._accountData).forEach((evType) => {
|
||||
@ -650,8 +664,11 @@ export class SyncAccumulator {
|
||||
Object.keys(roomData._currentState).forEach((evType) => {
|
||||
Object.keys(roomData._currentState[evType]).forEach((stateKey) => {
|
||||
let ev = roomData._currentState[evType][stateKey];
|
||||
// Push to both fields to provide downgrade compatibility in the sync accumulator db
|
||||
// the code will prefer `state_after` if it is present
|
||||
roomJson["org.matrix.msc4222.state_after"].events.push(ev);
|
||||
// Roll the state back to the value at the start of the timeline if it was changed
|
||||
if (rollBackState[evType] && rollBackState[evType][stateKey]) {
|
||||
// use the reverse clobbered event instead.
|
||||
ev = rollBackState[evType][stateKey];
|
||||
}
|
||||
roomJson.state.events.push(ev);
|
||||
|
158
src/sync.ts
158
src/sync.ts
@ -175,14 +175,15 @@ export enum SetPresence {
|
||||
}
|
||||
|
||||
interface ISyncParams {
|
||||
filter?: string;
|
||||
timeout: number;
|
||||
since?: string;
|
||||
"filter"?: string;
|
||||
"timeout": number;
|
||||
"since"?: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
full_state?: boolean;
|
||||
"full_state"?: boolean;
|
||||
// eslint-disable-next-line camelcase
|
||||
set_presence?: SetPresence;
|
||||
_cacheBuster?: string | number; // not part of the API itself
|
||||
"set_presence"?: SetPresence;
|
||||
"_cacheBuster"?: string | number; // not part of the API itself
|
||||
"org.matrix.msc4222.use_state_after"?: boolean; // https://github.com/matrix-org/matrix-spec-proposals/pull/4222
|
||||
}
|
||||
|
||||
type WrappedRoom<T> = T & {
|
||||
@ -344,8 +345,9 @@ export class SyncApi {
|
||||
);
|
||||
|
||||
const qps: ISyncParams = {
|
||||
timeout: 0, // don't want to block since this is a single isolated req
|
||||
filter: filterId,
|
||||
"timeout": 0, // don't want to block since this is a single isolated req
|
||||
"filter": filterId,
|
||||
"org.matrix.msc4222.use_state_after": true,
|
||||
};
|
||||
|
||||
const data = await client.http.authedRequest<ISyncResponse>(Method.Get, "/sync", qps as any, undefined, {
|
||||
@ -375,21 +377,18 @@ export class SyncApi {
|
||||
prev_batch: null,
|
||||
events: [],
|
||||
};
|
||||
const events = this.mapSyncEventsFormat(leaveObj.timeline, room);
|
||||
|
||||
const stateEvents = this.mapSyncEventsFormat(leaveObj.state, room);
|
||||
|
||||
// set the back-pagination token. Do this *before* adding any
|
||||
// events so that clients can start back-paginating.
|
||||
room.getLiveTimeline().setPaginationToken(leaveObj.timeline.prev_batch, EventTimeline.BACKWARDS);
|
||||
|
||||
await this.injectRoomEvents(room, stateEvents, events);
|
||||
const { timelineEvents } = await this.mapAndInjectRoomEvents(leaveObj);
|
||||
|
||||
room.recalculate();
|
||||
client.store.storeRoom(room);
|
||||
client.emit(ClientEvent.Room, room);
|
||||
|
||||
this.processEventsForNotifs(room, events);
|
||||
this.processEventsForNotifs(room, timelineEvents);
|
||||
return room;
|
||||
}),
|
||||
);
|
||||
@ -464,6 +463,7 @@ export class SyncApi {
|
||||
this._peekRoom.addEventsToTimeline(
|
||||
messages.reverse(),
|
||||
true,
|
||||
true,
|
||||
this._peekRoom.getLiveTimeline(),
|
||||
response.messages.start,
|
||||
);
|
||||
@ -551,7 +551,7 @@ export class SyncApi {
|
||||
})
|
||||
.map(this.client.getEventMapper());
|
||||
|
||||
await peekRoom.addLiveEvents(events);
|
||||
await peekRoom.addLiveEvents(events, { addToState: true });
|
||||
this.peekPoll(peekRoom, res.end);
|
||||
},
|
||||
(err) => {
|
||||
@ -976,7 +976,11 @@ export class SyncApi {
|
||||
filter = this.getGuestFilter();
|
||||
}
|
||||
|
||||
const qps: ISyncParams = { filter, timeout };
|
||||
const qps: ISyncParams = {
|
||||
filter,
|
||||
timeout,
|
||||
"org.matrix.msc4222.use_state_after": true,
|
||||
};
|
||||
|
||||
if (this.opts.disablePresence) {
|
||||
qps.set_presence = SetPresence.Offline;
|
||||
@ -1242,7 +1246,7 @@ export class SyncApi {
|
||||
const room = inviteObj.room;
|
||||
const stateEvents = this.mapSyncEventsFormat(inviteObj.invite_state, room);
|
||||
|
||||
await this.injectRoomEvents(room, stateEvents);
|
||||
await this.injectRoomEvents(room, stateEvents, undefined);
|
||||
|
||||
const inviter = room.currentState.getStateEvents(EventType.RoomMember, client.getUserId()!)?.getSender();
|
||||
|
||||
@ -1282,15 +1286,24 @@ export class SyncApi {
|
||||
await promiseMapSeries(joinRooms, async (joinObj) => {
|
||||
const room = joinObj.room;
|
||||
const stateEvents = this.mapSyncEventsFormat(joinObj.state, room);
|
||||
const stateAfterEvents = this.mapSyncEventsFormat(joinObj["org.matrix.msc4222.state_after"], room);
|
||||
// Prevent events from being decrypted ahead of time
|
||||
// this helps large account to speed up faster
|
||||
// room::decryptCriticalEvent is in charge of decrypting all the events
|
||||
// required for a client to function properly
|
||||
const events = this.mapSyncEventsFormat(joinObj.timeline, room, false);
|
||||
const timelineEvents = this.mapSyncEventsFormat(joinObj.timeline, room, false);
|
||||
const ephemeralEvents = this.mapSyncEventsFormat(joinObj.ephemeral);
|
||||
const accountDataEvents = this.mapSyncEventsFormat(joinObj.account_data);
|
||||
|
||||
const encrypted = this.isRoomEncrypted(room, stateEvents, events);
|
||||
// If state_after is present, this is the events that form the state at the end of the timeline block and
|
||||
// regular timeline events do *not* count towards state. If it's not present, then the state is formed by
|
||||
// the state events plus the timeline events. Note mapSyncEventsFormat returns an empty array if the field
|
||||
// is absent so we explicitly check the field on the original object.
|
||||
const eventsFormingFinalState = joinObj["org.matrix.msc4222.state_after"]
|
||||
? stateAfterEvents
|
||||
: stateEvents.concat(timelineEvents);
|
||||
|
||||
const encrypted = this.isRoomEncrypted(room, eventsFormingFinalState);
|
||||
// We store the server-provided value first so it's correct when any of the events fire.
|
||||
if (joinObj.unread_notifications) {
|
||||
/**
|
||||
@ -1378,8 +1391,8 @@ export class SyncApi {
|
||||
// which we'll try to paginate but not get any new events (which
|
||||
// will stop us linking the empty timeline into the chain).
|
||||
//
|
||||
for (let i = events.length - 1; i >= 0; i--) {
|
||||
const eventId = events[i].getId()!;
|
||||
for (let i = timelineEvents.length - 1; i >= 0; i--) {
|
||||
const eventId = timelineEvents[i].getId()!;
|
||||
if (room.getTimelineForEvent(eventId)) {
|
||||
debuglog(`Already have event ${eventId} in limited sync - not resetting`);
|
||||
limited = false;
|
||||
@ -1387,7 +1400,7 @@ export class SyncApi {
|
||||
// we might still be missing some of the events before i;
|
||||
// we don't want to be adding them to the end of the
|
||||
// timeline because that would put them out of order.
|
||||
events.splice(0, i);
|
||||
timelineEvents.splice(0, i);
|
||||
|
||||
// XXX: there's a problem here if the skipped part of the
|
||||
// timeline modifies the state set in stateEvents, because
|
||||
@ -1419,8 +1432,9 @@ export class SyncApi {
|
||||
// avoids a race condition if the application tries to send a message after the
|
||||
// state event is processed, but before crypto is enabled, which then causes the
|
||||
// crypto layer to complain.
|
||||
|
||||
if (this.syncOpts.cryptoCallbacks) {
|
||||
for (const e of stateEvents.concat(events)) {
|
||||
for (const e of eventsFormingFinalState) {
|
||||
if (e.isState() && e.getType() === EventType.RoomEncryption && e.getStateKey() === "") {
|
||||
await this.syncOpts.cryptoCallbacks.onCryptoEvent(room, e);
|
||||
}
|
||||
@ -1428,7 +1442,17 @@ export class SyncApi {
|
||||
}
|
||||
|
||||
try {
|
||||
await this.injectRoomEvents(room, stateEvents, events, syncEventData.fromCache);
|
||||
if ("org.matrix.msc4222.state_after" in joinObj) {
|
||||
await this.injectRoomEvents(
|
||||
room,
|
||||
undefined,
|
||||
stateAfterEvents,
|
||||
timelineEvents,
|
||||
syncEventData.fromCache,
|
||||
);
|
||||
} else {
|
||||
await this.injectRoomEvents(room, stateEvents, undefined, timelineEvents, syncEventData.fromCache);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(`Failed to process events on room ${room.roomId}:`, e);
|
||||
}
|
||||
@ -1452,11 +1476,11 @@ export class SyncApi {
|
||||
client.emit(ClientEvent.Room, room);
|
||||
}
|
||||
|
||||
this.processEventsForNotifs(room, events);
|
||||
this.processEventsForNotifs(room, timelineEvents);
|
||||
|
||||
const emitEvent = (e: MatrixEvent): boolean => client.emit(ClientEvent.Event, e);
|
||||
stateEvents.forEach(emitEvent);
|
||||
events.forEach(emitEvent);
|
||||
timelineEvents.forEach(emitEvent);
|
||||
ephemeralEvents.forEach(emitEvent);
|
||||
accountDataEvents.forEach(emitEvent);
|
||||
|
||||
@ -1469,11 +1493,9 @@ export class SyncApi {
|
||||
// Handle leaves (e.g. kicked rooms)
|
||||
await promiseMapSeries(leaveRooms, async (leaveObj) => {
|
||||
const room = leaveObj.room;
|
||||
const stateEvents = this.mapSyncEventsFormat(leaveObj.state, room);
|
||||
const events = this.mapSyncEventsFormat(leaveObj.timeline, room);
|
||||
const { timelineEvents, stateEvents, stateAfterEvents } = await this.mapAndInjectRoomEvents(leaveObj);
|
||||
const accountDataEvents = this.mapSyncEventsFormat(leaveObj.account_data);
|
||||
|
||||
await this.injectRoomEvents(room, stateEvents, events);
|
||||
room.addAccountData(accountDataEvents);
|
||||
|
||||
room.recalculate();
|
||||
@ -1482,12 +1504,15 @@ export class SyncApi {
|
||||
client.emit(ClientEvent.Room, room);
|
||||
}
|
||||
|
||||
this.processEventsForNotifs(room, events);
|
||||
this.processEventsForNotifs(room, timelineEvents);
|
||||
|
||||
stateEvents.forEach(function (e) {
|
||||
stateEvents?.forEach(function (e) {
|
||||
client.emit(ClientEvent.Event, e);
|
||||
});
|
||||
events.forEach(function (e) {
|
||||
stateAfterEvents?.forEach(function (e) {
|
||||
client.emit(ClientEvent.Event, e);
|
||||
});
|
||||
timelineEvents.forEach(function (e) {
|
||||
client.emit(ClientEvent.Event, e);
|
||||
});
|
||||
accountDataEvents.forEach(function (e) {
|
||||
@ -1500,7 +1525,7 @@ export class SyncApi {
|
||||
const room = knockObj.room;
|
||||
const stateEvents = this.mapSyncEventsFormat(knockObj.knock_state, room);
|
||||
|
||||
await this.injectRoomEvents(room, stateEvents);
|
||||
await this.injectRoomEvents(room, stateEvents, undefined);
|
||||
|
||||
if (knockObj.isBrandNewRoom) {
|
||||
room.recalculate();
|
||||
@ -1525,7 +1550,7 @@ export class SyncApi {
|
||||
return a.getTs() - b.getTs();
|
||||
});
|
||||
this.notifEvents.forEach(function (event) {
|
||||
client.getNotifTimelineSet()?.addLiveEvent(event);
|
||||
client.getNotifTimelineSet()?.addLiveEvent(event, { addToState: true });
|
||||
});
|
||||
}
|
||||
|
||||
@ -1669,7 +1694,7 @@ export class SyncApi {
|
||||
}
|
||||
|
||||
private mapSyncEventsFormat(
|
||||
obj: IInviteState | ITimeline | IEphemeral,
|
||||
obj: IInviteState | ITimeline | IEphemeral | undefined,
|
||||
room?: Room,
|
||||
decrypt = true,
|
||||
): MatrixEvent[] {
|
||||
@ -1737,28 +1762,69 @@ export class SyncApi {
|
||||
|
||||
// When processing the sync response we cannot rely on Room.hasEncryptionStateEvent we actually
|
||||
// inject the events into the room object, so we have to inspect the events themselves.
|
||||
private isRoomEncrypted(room: Room, stateEventList: MatrixEvent[], timelineEventList?: MatrixEvent[]): boolean {
|
||||
return (
|
||||
room.hasEncryptionStateEvent() ||
|
||||
!!this.findEncryptionEvent(stateEventList) ||
|
||||
!!this.findEncryptionEvent(timelineEventList)
|
||||
private isRoomEncrypted(room: Room, eventsFormingFinalState: MatrixEvent[]): boolean {
|
||||
return room.hasEncryptionStateEvent() || !!this.findEncryptionEvent(eventsFormingFinalState);
|
||||
}
|
||||
|
||||
private async mapAndInjectRoomEvents(wrappedRoom: WrappedRoom<ILeftRoom>): Promise<{
|
||||
timelineEvents: MatrixEvent[];
|
||||
stateEvents?: MatrixEvent[];
|
||||
stateAfterEvents?: MatrixEvent[];
|
||||
}> {
|
||||
const stateEvents = this.mapSyncEventsFormat(wrappedRoom.state, wrappedRoom.room);
|
||||
const stateAfterEvents = this.mapSyncEventsFormat(
|
||||
wrappedRoom["org.matrix.msc4222.state_after"],
|
||||
wrappedRoom.room,
|
||||
);
|
||||
const timelineEvents = this.mapSyncEventsFormat(wrappedRoom.timeline, wrappedRoom.room);
|
||||
|
||||
if ("org.matrix.msc4222.state_after" in wrappedRoom) {
|
||||
await this.injectRoomEvents(wrappedRoom.room, undefined, stateAfterEvents, timelineEvents);
|
||||
} else {
|
||||
await this.injectRoomEvents(wrappedRoom.room, stateEvents, undefined, timelineEvents);
|
||||
}
|
||||
|
||||
return { timelineEvents, stateEvents, stateAfterEvents };
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects events into a room's model.
|
||||
* @param stateEventList - A list of state events. This is the state
|
||||
* at the *START* of the timeline list if it is supplied.
|
||||
* @param stateAfterEventList - A list of state events. This is the state
|
||||
* at the *END* of the timeline list if it is supplied.
|
||||
* @param timelineEventList - A list of timeline events, including threaded. Lower index
|
||||
* is earlier in time. Higher index is later.
|
||||
* @param fromCache - whether the sync response came from cache
|
||||
*
|
||||
* No more than one of stateEventList and stateAfterEventList must be supplied. If
|
||||
* stateEventList is supplied, the events in timelineEventList are added to the state
|
||||
* after stateEventList. If stateAfterEventList is supplied, the events in timelineEventList
|
||||
* are not added to the state.
|
||||
*/
|
||||
public async injectRoomEvents(
|
||||
room: Room,
|
||||
stateEventList: MatrixEvent[],
|
||||
stateAfterEventList: undefined,
|
||||
timelineEventList?: MatrixEvent[],
|
||||
fromCache?: boolean,
|
||||
): Promise<void>;
|
||||
public async injectRoomEvents(
|
||||
room: Room,
|
||||
stateEventList: undefined,
|
||||
stateAfterEventList: MatrixEvent[],
|
||||
timelineEventList?: MatrixEvent[],
|
||||
fromCache?: boolean,
|
||||
): Promise<void>;
|
||||
public async injectRoomEvents(
|
||||
room: Room,
|
||||
stateEventList: MatrixEvent[] | undefined,
|
||||
stateAfterEventList: MatrixEvent[] | undefined,
|
||||
timelineEventList?: MatrixEvent[],
|
||||
fromCache = false,
|
||||
): Promise<void> {
|
||||
const eitherStateEventList = stateAfterEventList ?? stateEventList!;
|
||||
|
||||
// If there are no events in the timeline yet, initialise it with
|
||||
// the given state events
|
||||
const liveTimeline = room.getLiveTimeline();
|
||||
@ -1772,10 +1838,11 @@ export class SyncApi {
|
||||
// push actions cache elsewhere so we can freeze MatrixEvents, or otherwise
|
||||
// find some solution where MatrixEvents are immutable but allow for a cache
|
||||
// field.
|
||||
for (const ev of stateEventList) {
|
||||
|
||||
for (const ev of eitherStateEventList) {
|
||||
this.client.getPushActionsForEvent(ev);
|
||||
}
|
||||
liveTimeline.initialiseState(stateEventList, {
|
||||
liveTimeline.initialiseState(eitherStateEventList, {
|
||||
timelineWasEmpty,
|
||||
});
|
||||
}
|
||||
@ -1807,17 +1874,18 @@ export class SyncApi {
|
||||
// XXX: As above, don't do this...
|
||||
//room.addLiveEvents(stateEventList || []);
|
||||
// Do this instead...
|
||||
room.oldState.setStateEvents(stateEventList || []);
|
||||
room.currentState.setStateEvents(stateEventList || []);
|
||||
room.oldState.setStateEvents(eitherStateEventList);
|
||||
room.currentState.setStateEvents(eitherStateEventList);
|
||||
}
|
||||
|
||||
// Execute the timeline events. This will continue to diverge the current state
|
||||
// if the timeline has any state events in it.
|
||||
// Execute the timeline events. If addToState is true the timeline has any state
|
||||
// events in it, this will continue to diverge the current state.
|
||||
// This also needs to be done before running push rules on the events as they need
|
||||
// to be decorated with sender etc.
|
||||
await room.addLiveEvents(timelineEventList || [], {
|
||||
fromCache,
|
||||
timelineWasEmpty,
|
||||
addToState: stateAfterEventList === undefined,
|
||||
});
|
||||
this.client.processBeaconEvents(room, timelineEventList);
|
||||
}
|
||||
|
Reference in New Issue
Block a user