New Room List: Prevent old tombstoned rooms from appearing in the list (#29881)

* Write failing playwright test

Basically when someone changes their name, any old tombstoned rooms that
were previously hidden would suddenly show up in the list.

* Split addRoom into two methods

- `reInsertRoom` that re-inserts a room that is already known by the skiplist.
- `addNewRoom` to add new rooms

The idea is that sometimes you only want to re-insert to noop, eg: when
you get an event in an old room that was upgraded.

* Use new methods in the RLS

Only use `addNewRoom` when absolutely necessary. Most events should
instead use `reInsertRoom` which will noop when the room isn't already
known by the skiplist.

* Fix broken tests

* Add new test

* Fix playwright test
This commit is contained in:
R Midhun Suresh
2025-05-06 21:27:13 +05:30
committed by GitHub
parent 8ac2f60720
commit 6ba21dafa7
5 changed files with 143 additions and 39 deletions

View File

@@ -211,23 +211,28 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
const oldMembership = getEffectiveMembership(payload.oldMembership);
const newMembership = getEffectiveMembershipTag(payload.room, payload.membership);
// If the user is kicked, re-insert the room and do nothing more.
const ownUserId = this.matrixClient.getSafeUserId();
const isKicked = (payload.room as Room).getMember(ownUserId)?.isKicked();
const shouldRemove =
!isKicked &&
if (isKicked) {
this.addRoomAndEmit(payload.room);
return;
}
// If the user has left this room, remove it from the skiplist.
if (
(payload.oldMembership === KnownMembership.Invite ||
payload.oldMembership === KnownMembership.Join) &&
payload.membership === KnownMembership.Leave;
if (shouldRemove) {
payload.membership === KnownMembership.Leave
) {
this.roomSkipList.removeRoom(payload.room);
this.emit(LISTS_UPDATE_EVENT);
return;
}
// If we're joining an upgraded room, we'll want to make sure we don't proliferate
// the dead room in the list.
if (oldMembership !== EffectiveMembership.Join && newMembership === EffectiveMembership.Join) {
// 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 = payload.room.currentState;
const predecessor = roomState.findPredecessor(this.msc3946ProcessDynamicPredecessor);
if (predecessor) {
@@ -236,7 +241,8 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
else logger.warn(`Unable to find predecessor room with id ${predecessor.roomId}`);
}
}
this.addRoomAndEmit(payload.room);
this.addRoomAndEmit(payload.room, true);
break;
}
}
@@ -260,7 +266,7 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
logger.warn(`${roomId} was found in DMs but the room is not in the store`);
continue;
}
this.roomSkipList!.addRoom(room);
this.roomSkipList!.reInsertRoom(room);
needsEmit = true;
}
}
@@ -274,7 +280,7 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
.map((id) => this.matrixClient?.getRoom(id))
.filter((room) => !!room);
for (const room of rooms) {
this.roomSkipList!.addRoom(room);
this.roomSkipList!.reInsertRoom(room);
needsEmit = true;
}
break;
@@ -303,10 +309,12 @@ export class RoomListStoreV3Class extends AsyncStoreWithClient<EmptyObject> {
/**
* Add a room to the skiplist and emit an update.
* @param room The room to add to the skiplist
* @param isNewRoom Set this to true if this a new room that the isn't already in the skiplist
*/
private addRoomAndEmit(room: Room): void {
private addRoomAndEmit(room: Room, isNewRoom = false): void {
if (!this.roomSkipList) throw new Error("roomSkipList hasn't been created yet!");
this.roomSkipList.addRoom(room);
if (isNewRoom) this.roomSkipList.addNewRoom(room);
else this.roomSkipList.reInsertRoom(room);
this.emit(LISTS_UPDATE_EVENT);
}

View File

@@ -90,15 +90,34 @@ export class RoomSkipList implements Iterable<Room> {
}
/**
* Adds a given room to the correct sorted position in the list.
* If the room is already present in the list, it is first removed.
* Re-inserts a room that is already in the skiplist.
* This method does nothing if the room isn't already in the skiplist.
* @param room the room to add
*/
public addRoom(room: Room): void {
/**
* Remove this room from the skip list if necessary.
*/
public reInsertRoom(room: Room): void {
if (!this.roomNodeMap.has(room.roomId)) {
return;
}
this.removeRoom(room);
this.addNewRoom(room);
}
/**
* Adds a new room to the skiplist.
* This method will throw an error if the room is already in the skiplist.
* @param room the room to add
*/
public addNewRoom(room: Room): void {
if (this.roomNodeMap.has(room.roomId)) {
throw new Error(`Can't add room to skiplist: ${room.roomId} is already in the skiplist!`);
}
this.insertRoom(room);
}
/**
* Adds a given room to the correct sorted position in the list.
*/
private insertRoom(room: Room): void {
const newNode = new RoomNode(room);
newNode.checkIfRoomBelongsToActiveSpace();
newNode.applyFilters(this.filters);