From b7cd28bd2983d50ab140084a196f4d1ad9dcd928 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Fri, 3 Feb 2023 09:14:44 +0000 Subject: [PATCH] Support MSC3946 in RoomListStore (#10054) --- src/stores/room-list/RoomListStore.ts | 8 +-- test/stores/room-list/RoomListStore-test.ts | 56 +++++++++++++++++++-- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/stores/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts index 39d66aa9bf..09d9cca2e4 100644 --- a/src/stores/room-list/RoomListStore.ts +++ b/src/stores/room-list/RoomListStore.ts @@ -286,9 +286,9 @@ export class RoomListStoreClass extends AsyncStoreWithClient implements // If we're joining an upgraded room, we'll want to make sure we don't proliferate // the dead room in the list. const roomState: RoomState = membershipPayload.room.currentState; - const createEvent = roomState.getStateEvents(EventType.RoomCreate, ""); - if (createEvent && createEvent.getContent()["predecessor"]) { - const prevRoom = this.matrixClient.getRoom(createEvent.getContent()["predecessor"]["room_id"]); + const predecessor = roomState.findPredecessor(SettingsStore.getValue("feature_dynamic_room_predecessors")); + if (predecessor) { + const prevRoom = this.matrixClient.getRoom(predecessor.roomId); if (prevRoom) { const isSticky = this.algorithm.stickyRoom === prevRoom; if (isSticky) { @@ -298,6 +298,8 @@ export class RoomListStoreClass extends AsyncStoreWithClient implements // Note: we hit the algorithm instead of our handleRoomUpdate() function to // avoid redundant updates. this.algorithm.handleRoomUpdate(prevRoom, RoomUpdateCause.RoomRemoved); + } else { + logger.warn(`Unable to find predecessor room with id ${predecessor.roomId}`); } } diff --git a/test/stores/room-list/RoomListStore-test.ts b/test/stores/room-list/RoomListStore-test.ts index 7ceb6393a2..8ce3dff22a 100644 --- a/test/stores/room-list/RoomListStore-test.ts +++ b/test/stores/room-list/RoomListStore-test.ts @@ -17,6 +17,7 @@ limitations under the License. import { EventType, MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; import { MatrixDispatcher } from "../../../src/dispatcher/dispatcher"; +import SettingsStore from "../../../src/settings/SettingsStore"; import { ListAlgorithm, SortAlgorithm } from "../../../src/stores/room-list/algorithms/models"; import { OrderedDefaultTagIDs, RoomUpdateCause } from "../../../src/stores/room-list/models"; import RoomListStore, { RoomListStoreClass } from "../../../src/stores/room-list/RoomListStore"; @@ -24,14 +25,14 @@ import { stubClient, upsertRoomStateEvents } from "../../test-utils"; describe("RoomListStore", () => { const client = stubClient(); - const roomWithCreatePredecessorId = "!roomid:example.com"; + const newRoomId = "!roomid:example.com"; const roomNoPredecessorId = "!roomnopreid:example.com"; const oldRoomId = "!oldroomid:example.com"; const userId = "@user:example.com"; const createWithPredecessor = new MatrixEvent({ type: EventType.RoomCreate, sender: userId, - room_id: roomWithCreatePredecessorId, + room_id: newRoomId, content: { predecessor: { room_id: oldRoomId, event_id: "tombstone_event_id" }, }, @@ -41,19 +42,32 @@ describe("RoomListStore", () => { const createNoPredecessor = new MatrixEvent({ type: EventType.RoomCreate, sender: userId, - room_id: roomWithCreatePredecessorId, + room_id: newRoomId, content: {}, event_id: "$create", state_key: "", }); - const roomWithCreatePredecessor = new Room(roomWithCreatePredecessorId, client, userId, {}); + const predecessor = new MatrixEvent({ + type: EventType.RoomPredecessor, + sender: userId, + room_id: newRoomId, + content: { + predecessor_room_id: oldRoomId, + last_known_event_id: "tombstone_event_id", + }, + event_id: "$pred", + state_key: "", + }); + const roomWithPredecessorEvent = new Room(newRoomId, client, userId, {}); + upsertRoomStateEvents(roomWithPredecessorEvent, [predecessor]); + const roomWithCreatePredecessor = new Room(newRoomId, client, userId, {}); upsertRoomStateEvents(roomWithCreatePredecessor, [createWithPredecessor]); const roomNoPredecessor = new Room(roomNoPredecessorId, client, userId, {}); upsertRoomStateEvents(roomNoPredecessor, [createNoPredecessor]); const oldRoom = new Room(oldRoomId, client, userId, {}); client.getRoom = jest.fn().mockImplementation((roomId) => { switch (roomId) { - case roomWithCreatePredecessorId: + case newRoomId: return roomWithCreatePredecessor; case oldRoomId: return oldRoom; @@ -123,4 +137,36 @@ describe("RoomListStore", () => { // And no other updates happen expect(handleRoomUpdate).toHaveBeenCalledTimes(1); }); + + describe("When feature_dynamic_room_predecessors = true", () => { + beforeEach(() => { + jest.spyOn(SettingsStore, "getValue").mockImplementation( + (settingName) => settingName === "feature_dynamic_room_predecessors", + ); + }); + + afterEach(() => { + jest.spyOn(SettingsStore, "getValue").mockReset(); + }); + + it("Removes old room if it finds a predecessor in the m.predecessor event", () => { + // Given a store we can spy on + const { store, handleRoomUpdate } = createStore(); + + // When we tell it we joined a new room that has an old room as + // predecessor in the create event + const payload = { + oldMembership: "invite", + membership: "join", + room: roomWithPredecessorEvent, + }; + store.onDispatchMyMembership(payload); + + // Then the old room is removed + expect(handleRoomUpdate).toHaveBeenCalledWith(oldRoom, RoomUpdateCause.RoomRemoved); + + // And the new room is added + expect(handleRoomUpdate).toHaveBeenCalledWith(roomWithPredecessorEvent, RoomUpdateCause.NewRoom); + }); + }); });