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