Update room suggestions and search when space membership changes (#6316)

* Avoid repeated space membership scans in room search

Signed-off-by: Ivan Magda <imagda15@gmail.com>

* Refresh room candidates when space membership changes

Keep known space room IDs in a set updated by the room publisher and by
successful additions. Filter both suggestions and search results through
that set, retaining exclusions while the space list resets and reloads.

Preserve the initial recent-room eligibility filter so existing children
do not consume the five suggestion slots. Cover live membership updates
and partial-save filtering in both candidate lists.

---------

Signed-off-by: Ivan Magda <imagda15@gmail.com>
This commit is contained in:
Ivan Magda
2026-10-12 17:03:44 +03:00
committed by GitHub
parent f99b607f08
commit ad88f0da7c
2 changed files with 79 additions and 26 deletions
@@ -17,9 +17,8 @@ class SpaceAddRoomsScreenViewModel: SpaceAddRoomsScreenViewModelType, SpaceAddRo
private let userIndicatorController: UserIndicatorControllerProtocol
private var suggestedRooms: [SpaceAddRoomsScreenRoom] = []
/// A place to track rooms that were sucessfully added to the space in order to filter them out from
/// the roomsSection when a failure occurs part way through the array.
private var addedRoomIDs: Set<String> = []
/// Keep known children excluded while the space list resets and reloads after saving.
private var existingRoomIDs: Set<String>
private var actionsSubject: PassthroughSubject<SpaceAddRoomsScreenViewModelAction, Never> = .init()
var actions: AnyPublisher<SpaceAddRoomsScreenViewModelAction, Never> {
@@ -31,6 +30,7 @@ class SpaceAddRoomsScreenViewModel: SpaceAddRoomsScreenViewModelType, SpaceAddRo
roomSummaryProvider: RoomSummaryProviderProtocol,
userIndicatorController: UserIndicatorControllerProtocol) {
self.spaceRoomListProxy = spaceRoomListProxy
existingRoomIDs = Set(spaceRoomListProxy.spaceRoomsPublisher.value.map(\.id))
spaceServiceProxy = userSession.clientProxy.spaceService
self.roomSummaryProvider = roomSummaryProvider
self.userIndicatorController = userIndicatorController
@@ -38,6 +38,15 @@ class SpaceAddRoomsScreenViewModel: SpaceAddRoomsScreenViewModelType, SpaceAddRo
super.init(initialViewState: SpaceAddRoomsScreenViewState(roomsSection: .init(type: .suggestions, rooms: [])),
mediaProvider: userSession.mediaProvider)
spaceRoomListProxy.spaceRoomsPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] spaceRooms in
guard let self else { return }
existingRoomIDs.formUnion(spaceRooms.map(\.id))
updateRooms()
}
.store(in: &cancellables)
roomSummaryProvider.roomListPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
@@ -45,20 +54,18 @@ class SpaceAddRoomsScreenViewModel: SpaceAddRoomsScreenViewModelType, SpaceAddRo
}
.store(in: &cancellables)
Task {
let existingRooms = spaceRoomListProxy.spaceRoomsPublisher.value
Task { [existingRoomIDs] in
// Filter before the recent-room limit so existing children do not displace suggestions.
suggestedRooms = await userSession.clientProxy
.recentlyVisitedRooms { @MainActor roomProxy in
!roomProxy.infoPublisher.value.isDirect
&& !roomProxy.infoPublisher.value.isSpace
&& roomProxy.infoPublisher.value.membership == .joined
&& !existingRooms.contains { $0.id == roomProxy.id }
&& !existingRoomIDs.contains(roomProxy.id)
}
.map { .init(roomProxy: $0) }
if state.roomsSection.type == .suggestions {
state.roomsSection = .init(type: .suggestions, rooms: suggestedRooms)
}
updateRooms()
}
}
@@ -90,16 +97,14 @@ class SpaceAddRoomsScreenViewModel: SpaceAddRoomsScreenViewModelType, SpaceAddRo
private func updateRooms() {
guard !state.bindings.searchQuery.isEmpty else {
state.roomsSection = .init(type: .suggestions, rooms: suggestedRooms)
state.roomsSection = .init(type: .suggestions, rooms: suggestedRooms.filter { !existingRoomIDs.contains($0.id) })
return
}
let existingRooms = spaceRoomListProxy.spaceRoomsPublisher.value
let rooms = roomSummaryProvider.roomListPublisher.value
.lazy
.filter { [addedRoomIDs] summary in
!summary.isDirect && !existingRooms.contains { $0.id == summary.id } && !addedRoomIDs.contains(summary.id)
.filter { [existingRoomIDs] summary in
!summary.isDirect && !existingRoomIDs.contains(summary.id)
}
.map(SpaceAddRoomsScreenRoom.init)
@@ -142,13 +147,13 @@ class SpaceAddRoomsScreenViewModel: SpaceAddRoomsScreenViewModelType, SpaceAddRo
for room in state.selectedRooms {
switch await spaceServiceProxy.addChild(room.id, to: spaceRoomListProxy.id) {
case .success:
addedRoomIDs.insert(room.id)
existingRoomIDs.insert(room.id)
case .failure(let error):
MXLog.error("Failed adding room to space: \(error)")
showErrorIndicator()
// Hide rooms that were successfully added.
state.selectedRooms.removeAll { addedRoomIDs.contains($0.id) }
state.selectedRooms.removeAll { existingRoomIDs.contains($0.id) }
updateRooms()
return