diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 0a3905e4e..f64dedaaa 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -1437,6 +1437,7 @@ E3E1E255DC8CB34BD8573E0D /* UserIndicatorControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12D3B1BCF920880CA8BBB6B /* UserIndicatorControllerProtocol.swift */; }; E3EBC3BF7CE3960B41757BAA /* Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7310D8DFE01AF45F0689C3AA /* Publisher.swift */; }; E468CC731C3F4D678499E52F /* LAContextMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BA5A62DA4B543827FF82354 /* LAContextMock.swift */; }; + E471DCA49612AEBF4DD8C8D4 /* RoomSelectionScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69A3015DB39E3465B00B8BF /* RoomSelectionScreenViewModelTests.swift */; }; E481C8FDCB6C089963C95344 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = BC01130651CB23340B899032 /* DeviceKit */; }; E49F74BD93230BDEFFE5EA51 /* RoomNotificationSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D295F0081084F38DB20893 /* RoomNotificationSettingsScreenViewModelTests.swift */; }; E4B07FF075C99D04D9AF792D /* AppLockSetupPINScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B410B32B72C90BF94E481F33 /* AppLockSetupPINScreenModels.swift */; }; @@ -2948,6 +2949,7 @@ C5CB64E711E86270B722FAF7 /* LiveLocationManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveLocationManagerTests.swift; sourceTree = ""; }; C5F06F2F09B2EDD067DC2174 /* NotificationSettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsScreen.swift; sourceTree = ""; }; C687844F60BFF532D49A994C /* AnalyticsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsTests.swift; sourceTree = ""; }; + C69A3015DB39E3465B00B8BF /* RoomSelectionScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSelectionScreenViewModelTests.swift; sourceTree = ""; }; C6A9F49B3EE59147AF2F70BB /* SeparatorRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorRoomTimelineItem.swift; sourceTree = ""; }; C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderAvatarImage.swift; sourceTree = ""; }; C715CFE00686DACA59D836EA /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = fa.lproj/SAS.strings; sourceTree = ""; }; @@ -5322,6 +5324,7 @@ B40233F2989AD49906BB310D /* RoomPollsHistoryScreenViewModelTests.swift */, 48FEFF746DB341CDB18D7AAA /* RoomRolesAndPermissionsScreenViewModelTests.swift */, 93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */, + C69A3015DB39E3465B00B8BF /* RoomSelectionScreenViewModelTests.swift */, AEEAFB646E583655652C3D04 /* RoomStateEventStringBuilderTests.swift */, F46E441BA50705E6CEC89FE0 /* RoomSummaryProviderTests.swift */, 046C0D3F53B0B5EF0A1F5BEA /* RoomSummaryTests.swift */, @@ -8440,6 +8443,7 @@ 7B1605C6FFD4D195F264A684 /* RoomPollsHistoryScreenViewModelTests.swift in Sources */, 84C631E734FD2555B39B681C /* RoomRolesAndPermissionsScreenViewModelTests.swift in Sources */, 46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */, + E471DCA49612AEBF4DD8C8D4 /* RoomSelectionScreenViewModelTests.swift in Sources */, CC0D088F505F33A20DC5590F /* RoomStateEventStringBuilderTests.swift in Sources */, 6AB306367E56A6F6DFA0E2FF /* RoomSummaryProviderTests.swift in Sources */, 15913A5B07118C1268A840E4 /* RoomSummaryTests.swift in Sources */, diff --git a/ElementX/Sources/Mocks/RoomSummaryProviderMock.swift b/ElementX/Sources/Mocks/RoomSummaryProviderMock.swift index ff1f376b1..fea209e7c 100644 --- a/ElementX/Sources/Mocks/RoomSummaryProviderMock.swift +++ b/ElementX/Sources/Mocks/RoomSummaryProviderMock.swift @@ -43,8 +43,8 @@ extension RoomSummaryProviderMock { setFilterClosure = { [initialRooms, roomListSubject] filter in switch filter { - case let .search(query, joinedOnly): - var rooms = joinedOnly ? initialRooms.filter { $0.joinRequestType == nil } : initialRooms + case let .search(query): + var rooms = initialRooms if !query.isEmpty { rooms = rooms.filter { $0.name.localizedCaseInsensitiveContains(query) } @@ -73,10 +73,12 @@ extension RoomSummary { static func mock(id: String, name: String, isSpace: Bool = false, + isJoined: Bool = true, canonicalAlias: String? = nil) -> RoomSummary { RoomSummary(room: RoomSDKMock(.init()), id: id, joinRequestType: nil, + isJoined: isJoined, name: name, isDirect: false, isSpace: isSpace, @@ -105,6 +107,7 @@ extension Array where Element == RoomSummary { RoomSummary(room: RoomSDKMock(), id: "1", joinRequestType: nil, + isJoined: true, name: "Foundation 🔭🪐🌌", isDirect: false, isSpace: false, @@ -128,6 +131,7 @@ extension Array where Element == RoomSummary { RoomSummary(room: RoomSDKMock(), id: "2", joinRequestType: nil, + isJoined: true, name: "Foundation and Empire", isDirect: false, isSpace: false, @@ -151,6 +155,7 @@ extension Array where Element == RoomSummary { RoomSummary(room: RoomSDKMock(), id: "3", joinRequestType: nil, + isJoined: true, name: "Second Foundation", isDirect: false, isSpace: false, @@ -174,6 +179,7 @@ extension Array where Element == RoomSummary { RoomSummary(room: RoomSDKMock(), id: "4", joinRequestType: nil, + isJoined: true, name: "Foundation's Edge", isDirect: false, isSpace: false, @@ -197,6 +203,7 @@ extension Array where Element == RoomSummary { RoomSummary(room: RoomSDKMock(), id: "5", joinRequestType: nil, + isJoined: true, name: "Foundation and Earth", isDirect: true, isSpace: false, @@ -220,6 +227,7 @@ extension Array where Element == RoomSummary { RoomSummary(room: RoomSDKMock(), id: "6", joinRequestType: nil, + isJoined: true, name: "Prelude to Foundation", isDirect: true, isSpace: false, @@ -243,6 +251,7 @@ extension Array where Element == RoomSummary { RoomSummary(room: RoomSDKMock(), id: "7", joinRequestType: nil, + isJoined: true, name: "Tombstoned", isDirect: false, isSpace: false, @@ -266,6 +275,7 @@ extension Array where Element == RoomSummary { RoomSummary(room: RoomSDKMock(), id: "0", joinRequestType: nil, + isJoined: true, name: "Unknown", isDirect: false, isSpace: false, @@ -322,6 +332,7 @@ extension Array where Element == RoomSummary { RoomSummary(room: RoomSDKMock(), id: "someAwesomeRoomId1", joinRequestType: .invite(inviter: RoomMemberProxyMock.mockCharlie), + isJoined: false, name: "First room", isDirect: false, isSpace: false, @@ -345,6 +356,7 @@ extension Array where Element == RoomSummary { RoomSummary(room: RoomSDKMock(), id: "someAwesomeRoomId2", joinRequestType: .invite(inviter: RoomMemberProxyMock.mockCharlie), + isJoined: false, name: "Second room", isDirect: true, isSpace: false, @@ -371,6 +383,7 @@ extension Array where Element == RoomSummary { RoomSummary(room: RoomSDKMock(), id: "!space1:matrix.org", joinRequestType: .invite(inviter: RoomMemberProxyMock.mockCharlie), + isJoined: false, name: "First space", isDirect: false, isSpace: true, @@ -394,6 +407,7 @@ extension Array where Element == RoomSummary { RoomSummary(room: RoomSDKMock(), id: "!space2:matrix.org", joinRequestType: .invite(inviter: RoomMemberProxyMock.mockCharlie), + isJoined: false, name: "Second space", isDirect: false, isSpace: true, diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenInviteCell.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenInviteCell.swift index 31e968161..6d620b8b8 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenInviteCell.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenInviteCell.swift @@ -218,6 +218,7 @@ private extension HomeScreenRoom { let summary = RoomSummary(room: RoomSDKMock(), id: "@someone:somewhere.com", joinRequestType: .invite(inviter: inviter), + isJoined: false, name: "Some Guy", isDirect: true, isSpace: false, @@ -254,6 +255,7 @@ private extension HomeScreenRoom { let summary = RoomSummary(room: RoomSDKMock(), id: "@someone:somewhere.com", joinRequestType: .invite(inviter: inviter), + isJoined: false, name: name, isDirect: false, isSpace: isSpace, diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenKnockedCell.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenKnockedCell.swift index 93f16192e..f78fa8225 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenKnockedCell.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenKnockedCell.swift @@ -150,6 +150,7 @@ private extension HomeScreenRoom { let summary = RoomSummary(room: RoomSDKMock(), id: "@someone:somewhere.com", joinRequestType: .invite(inviter: inviter), + isJoined: false, name: "Some Guy", isDirect: true, isSpace: false, @@ -183,6 +184,7 @@ private extension HomeScreenRoom { let summary = RoomSummary(room: RoomSDKMock(), id: "@someone:somewhere.com", joinRequestType: .invite(inviter: inviter), + isJoined: false, name: "Awesome Room", isDirect: false, isSpace: false, diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift index 040ae85c9..90830d19d 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift @@ -268,6 +268,7 @@ struct HomeScreenRoomCell_Previews: PreviewProvider, TestablePreview { let summary = RoomSummary(room: RoomSDKMock(), id: UUID().uuidString, joinRequestType: nil, + isJoined: true, name: name, isDirect: heroes.count == 1, isSpace: false, diff --git a/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenViewModel.swift b/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenViewModel.swift index 873dff16e..4cc24ac6b 100644 --- a/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenViewModel.swift +++ b/ElementX/Sources/Screens/MessageForwardingScreen/MessageForwardingScreenViewModel.swift @@ -88,7 +88,7 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me private func updateRooms() { state.rooms = roomSummaryProvider.roomListPublisher.value - .filter { $0.id != forwardingPayload.roomID } + .filter { $0.id != forwardingPayload.roomID && !$0.isTombstoned } .map(MessageForwardingRoom.init(summary:)) } @@ -99,7 +99,7 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me state.suggestedRooms = roomIDs .filter { $0 != forwardingPayload.roomID } .compactMap(clientProxy.roomSummaryForIdentifier) - .filter { $0.joinRequestType == nil && !$0.isSpace && !$0.isTombstoned } + .filter { $0.isJoined && !$0.isSpace && !$0.isTombstoned } .prefix(Self.maxSuggestedRoomCount) .map(MessageForwardingRoom.init(summary:)) } diff --git a/ElementX/Sources/Screens/RoomSelectionScreen/RoomSelectionScreenViewModel.swift b/ElementX/Sources/Screens/RoomSelectionScreen/RoomSelectionScreenViewModel.swift index 5e775a6e3..e6d3e6506 100644 --- a/ElementX/Sources/Screens/RoomSelectionScreen/RoomSelectionScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomSelectionScreen/RoomSelectionScreenViewModel.swift @@ -77,7 +77,7 @@ class RoomSelectionScreenViewModel: RoomSelectionScreenViewModelType, RoomSelect private func updateRooms() { var rooms = [RoomSelectionRoom]() - for summary in roomSummaryProvider.roomListPublisher.value { + for summary in roomSummaryProvider.roomListPublisher.value where !summary.isTombstoned { rooms.append(.init(id: summary.id, title: summary.name, description: summary.roomListDescription, diff --git a/ElementX/Sources/Screens/SearchScreen/SearchScreenViewModel.swift b/ElementX/Sources/Screens/SearchScreen/SearchScreenViewModel.swift index 2ba00118f..0d03b14f0 100644 --- a/ElementX/Sources/Screens/SearchScreen/SearchScreenViewModel.swift +++ b/ElementX/Sources/Screens/SearchScreen/SearchScreenViewModel.swift @@ -194,7 +194,7 @@ class SearchScreenViewModel: SearchScreenViewModelType, SearchScreenViewModelPro switch mode { case .rooms: searchedQueries[mode] = searchQuery - roomSummaryProvider.setFilter(.search(query: searchQuery, joinedOnly: true)) + roomSummaryProvider.setFilter(.search(query: searchQuery)) case .messages: setQueryTask = Task { [weak self] in // Debounce message queries; superseded keystrokes cancel this before it commits. @@ -215,7 +215,7 @@ class SearchScreenViewModel: SearchScreenViewModelType, SearchScreenViewModelPro return .query(query) case .room(let roomID): // Drop rooms the user has since left or is only invited to, we've got nothing to show for them. - guard let summary = clientProxy.roomSummaryForIdentifier(roomID), summary.joinRequestType == nil else { return nil } + guard let summary = clientProxy.roomSummaryForIdentifier(roomID), summary.isJoined else { return nil } return .room(SearchScreenRoom(summary)) } } diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 1888faf1b..136fa02c1 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -1582,6 +1582,7 @@ private struct ClientProxyServices { alternateRoomSummaryProvider = RoomSummaryProvider(roomListService: roomListService, eventStringBuilder: eventStringBuilder, name: "AlternateAllRooms", + includesOnlyJoinedRooms: true, notificationSettings: notificationSettings, userSettings: userSettings) diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index aa37bac07..c372ed036 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -149,7 +149,7 @@ protocol ClientProxyProtocol: AnyObject { /// Used for listing rooms that shouldn't be affected by the main `roomSummaryProvider` filtering /// But can still be filtered by queries, since this may be shared across multiple views, remember to reset - /// The filtering state when you are done with it + /// The filtering state when you are done with it. Only lists joined rooms. var alternateRoomSummaryProvider: RoomSummaryProviderProtocol { get } /// Used for listing rooms, can't be filtered nor its state observed diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummary.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummary.swift index 9459daeae..f11a170f0 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummary.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummary.swift @@ -30,6 +30,7 @@ nonisolated struct RoomSummary { let id: String let joinRequestType: JoinRequestType? + let isJoined: Bool let name: String let isDirect: Bool @@ -118,6 +119,7 @@ nonisolated extension RoomSummary { RoomSummary(room: room, id: room.id(), joinRequestType: nil, + isJoined: false, name: room.id(), isDirect: false, isSpace: false, @@ -165,6 +167,7 @@ nonisolated extension RoomSummary { activeCallIntent = nil joinRequestType = nil + isJoined = true isMarkedUnread = false isFavourite = false isTombstoned = false diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift index 58ca185e0..4240bdadf 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift @@ -17,6 +17,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { private let shouldUpdateVisibleRange: Bool private let notificationSettings: NotificationSettingsProxyProtocol private let userSettings: UserSettings + private let baseFilter: [RoomListEntriesDynamicFilterKind] private let roomListPageSize: UInt32 /// Remember how many rooms we had on the previous requests so we can deduplicate @@ -62,10 +63,12 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { /// - shouldUpdateVisibleRange: whether this summary provider should forward visible ranges /// to the room list service through the `applyInput(input: .viewport(ranges` api. Only useful for /// lists that need to update the visible range on Sliding Sync + /// - includesOnlyJoinedRooms: whether to leave out invites and other rooms the user hasn't joined, whatever the filter init(roomListService: RoomListServiceProtocol, eventStringBuilder: RoomEventStringBuilder, name: String, shouldUpdateVisibleRange: Bool = false, + includesOnlyJoinedRooms: Bool = false, roomListPageSize: UInt32 = 100, notificationSettings: NotificationSettingsProxyProtocol, userSettings: UserSettings) { @@ -77,6 +80,10 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { self.userSettings = userSettings self.roomListPageSize = roomListPageSize + baseFilter = [.any(filters: [.all(filters: [.nonSpace, .nonLeft]), + .all(filters: [.space, .invite])]), + .deduplicateVersions] + (includesOnlyJoinedRooms ? [.joined] : []) + let (diffsStream, diffsContinuation) = AsyncStream<[RoomListEntriesUpdate]>.makeStream() self.diffsContinuation = diffsContinuation @@ -120,8 +127,9 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { }) // Forces the listener above to be called with the current state + let filter = currentFilter ?? .all(filters: []) currentFilter = nil - setFilter(.all(filters: [])) + setFilter(filter) let stateUpdatesSubscriptionResult = try roomList.loadingState(listener: SDKListener { [loadingStateContinuation] state in loadingStateContinuation.yield(state) @@ -146,15 +154,11 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { currentFilter = filter - let baseFilter: [RoomListEntriesDynamicFilterKind] = [.any(filters: [.all(filters: [.nonSpace, .nonLeft]), - .all(filters: [.space, .invite])]), - .deduplicateVersions] - switch filter { case .excludeAll: _ = listUpdatesSubscriptionResult?.controller().setFilter(kind: .none) - case let .search(query, joinedOnly): - let filters = nameFilter(for: query) + baseFilter + (joinedOnly ? [.joined] : []) + case let .search(query): + let filters = nameFilter(for: query) + baseFilter _ = listUpdatesSubscriptionResult?.controller().setFilter(kind: .all(filters: filters)) case .rooms(let roomIDs, let filters): var rustFilters = filters.map(\.rustFilter) + baseFilter @@ -372,6 +376,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { return RoomSummary(room: room, id: roomInfo.id, joinRequestType: joinRequestType, + isJoined: roomInfo.membership == .joined, name: roomInfo.displayName ?? roomInfo.id, isDirect: roomInfo.isDirect, isSpace: roomInfo.isSpace, diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProviderProtocol.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProviderProtocol.swift index 5729af7cf..659f9adb5 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProviderProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProviderProtocol.swift @@ -36,8 +36,8 @@ enum RoomSummaryProviderState { enum RoomSummaryProviderFilter: Equatable { /// Filters out everything case excludeAll - /// Includes only the items that satisfy the predicate logic, optionally restricted to joined rooms - case search(query: String, joinedOnly: Bool = false) + /// Includes only the items that satisfy the predicate logic + case search(query: String) /// Includes only what satisfies the filters used case all(filters: Set) /// Include only rooms from the given that satisfy the given filters diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPad-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPad-en-GB.png index 1bbae6df0..a2d62d0a9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPad-en-GB.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPad-en-GB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:175fc36dbb3f8b53610b87a06fb8f69d9a7c03416699a732678dc719ea7227e0 -size 170430 +oid sha256:3d518a38be7144846c00dcf2f1fa8c610c9d081d2ae4f1f3916268a34b4bd384 +size 166840 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPad-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPad-pseudo.png index 621930925..88807ceb7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPad-pseudo.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPad-pseudo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1a433bd71d9eae827259513c29d4765202c38aac1a97c4e6e0527ca624779ac -size 174737 +oid sha256:6353e5a7813a4eb3a58286d31c45923fdf3b53ebf2dae152c096735ad00bdf1c +size 171136 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPhone-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPhone-en-GB.png index fb762bb62..94ae298c1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPhone-en-GB.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPhone-en-GB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:143c4962bc624c86d81996a122894b18b76acbd587942942ca736f3ac5213c5d -size 118839 +oid sha256:4e255a55f696f81294ea7706f61f7fe6377ab3b46a8ae8b078e5b10e65f23eae +size 115043 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPhone-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPhone-pseudo.png index a4eb35bd0..53d85fb76 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPhone-pseudo.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/messageForwardingScreen.Suggestions-iPhone-pseudo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78e40d11b882b09e271b345bbbe941216d15629a501191382ed5047e0f227c54 -size 119142 +oid sha256:cbc6fe61ccf86ee6e8c23d282e008f533ccd2aef40935bd0e2cc4105bb36aba4 +size 115346 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPad-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPad-en-GB-0.png index 3efc1e0ba..a336a7211 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPad-en-GB-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPad-en-GB-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ace5cc2b095335d929c4209d96aa07bdd251ca8195eaf0e99949c978edca743 -size 160670 +oid sha256:b650e360efd5fcb17b5756b5eb582bb6b9a5e25b46d4247ed87f652b1befd972 +size 156916 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPad-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPad-pseudo-0.png index 542aa1dd7..6fe0dd4da 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPad-pseudo-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPad-pseudo-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b7ee1aeecfabf71746ca20c3e1211659c6ec0acb2c1a434c5ce39e9bac62295 -size 162114 +oid sha256:bc1633ed6b191833ff4df4835e5298b4460efc38663e253517b82b0a64aac746 +size 158360 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPhone-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPhone-en-GB-0.png index b2891d30f..f1abf9f9b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPhone-en-GB-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPhone-en-GB-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:065d06fb64af86ad92bdae8ed3f9df7adc0b00a9ac574668a318281c899d3f2c -size 111147 +oid sha256:6589ac4a2211cf44fcb0c83c131299528d534aee9d5ccb291025621d962a4e48 +size 107113 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPhone-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPhone-pseudo-0.png index 45d75d951..1425ab245 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPhone-pseudo-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomSelectionScreen.iPhone-pseudo-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89f6c4e043bf495a143c26649bd485e638643dbe5750f662d7c7dbbff37fb640 -size 110563 +oid sha256:562a960d20d2071d90e3bf618ae624f9bc73fd06e2f0cdf26ca9bd1b32516483 +size 106529 diff --git a/UnitTests/Sources/HomeScreenRoomTests.swift b/UnitTests/Sources/HomeScreenRoomTests.swift index 08db9eafb..0379720d8 100644 --- a/UnitTests/Sources/HomeScreenRoomTests.swift +++ b/UnitTests/Sources/HomeScreenRoomTests.swift @@ -25,6 +25,7 @@ struct HomeScreenRoomTests { roomSummary = RoomSummary(room: RoomSDKMock(), id: "Test room", joinRequestType: nil, + isJoined: true, name: "Test room", isDirect: false, isSpace: false, diff --git a/UnitTests/Sources/LoggingTests.swift b/UnitTests/Sources/LoggingTests.swift index 7b4929508..980606434 100644 --- a/UnitTests/Sources/LoggingTests.swift +++ b/UnitTests/Sources/LoggingTests.swift @@ -73,6 +73,7 @@ final class LoggingTests { let roomSummary = RoomSummary(room: RoomSDKMock(), id: "myroomid", joinRequestType: nil, + isJoined: true, name: roomName, isDirect: true, isSpace: false, diff --git a/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift b/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift index 37fc333e3..44d7e9fb0 100644 --- a/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift +++ b/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift @@ -35,6 +35,7 @@ struct MessageForwardingScreenViewModelTests { @Test func initialState() { #expect(!context.viewState.rooms.contains { $0.id == forwardingPayload.roomID }, "The source room ID shouldn't be shown") + #expect(!context.viewState.rooms.contains { $0.id == "7" }, "Upgraded rooms shouldn't be shown") } @Test @@ -50,7 +51,7 @@ struct MessageForwardingScreenViewModelTests { @Test mutating func suggestionsOnlyIncludeRoomsThatCanBeForwardedTo() async throws { - viewModel = makeViewModel(recentlyVisitedRoomIDs: [forwardingPayload.roomID, "space", "7", "someAwesomeRoomId1", "!unknown:matrix.org", "2"]) + viewModel = makeViewModel(recentlyVisitedRoomIDs: [forwardingPayload.roomID, "space", "7", "someAwesomeRoomId1", "left", "!unknown:matrix.org", "2"]) context = viewModel.context let deferred = deferFulfillment(context.$viewState) { !$0.suggestedRooms.isEmpty } @@ -289,7 +290,8 @@ struct MessageForwardingScreenViewModelTests { let clientProxy = ClientProxyMock(.init()) clientProxy.roomForIdentifierClosure = { .joined(JoinedRoomProxyMock(.init(id: $0))) } clientProxy.recentlyVisitedRoomIDsReturnValue = .success(recentlyVisitedRoomIDs) - let knownRooms: [RoomSummary] = .mockRooms + .mockInvites + [.mock(id: "space", name: "Space", isSpace: true)] + let knownRooms: [RoomSummary] = .mockRooms + .mockInvites + [.mock(id: "space", name: "Space", isSpace: true), + .mock(id: "left", name: "Left", isJoined: false)] clientProxy.roomSummaryForIdentifierClosure = { roomID in knownRooms.first { $0.id == roomID } } return MessageForwardingScreenViewModel(forwardingPayload: forwardingPayload, diff --git a/UnitTests/Sources/RoomSelectionScreenViewModelTests.swift b/UnitTests/Sources/RoomSelectionScreenViewModelTests.swift new file mode 100644 index 000000000..bf1f83767 --- /dev/null +++ b/UnitTests/Sources/RoomSelectionScreenViewModelTests.swift @@ -0,0 +1,20 @@ +// +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +// Please see LICENSE files in the repository root for full details. +// + +@testable import ElementX +import Testing + +struct RoomSelectionScreenViewModelTests { + @Test + func upgradedRoomsAreNotListed() { + let viewModel = RoomSelectionScreenViewModel(userSession: UserSessionMock(.init()), + roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))) + + #expect(!viewModel.context.viewState.rooms.isEmpty) + #expect(!viewModel.context.viewState.rooms.contains { $0.id == "7" }, "Upgraded rooms shouldn't be shown") + } +} diff --git a/UnitTests/Sources/RoomSummaryProviderTests.swift b/UnitTests/Sources/RoomSummaryProviderTests.swift index b7083db84..663c0cf93 100644 --- a/UnitTests/Sources/RoomSummaryProviderTests.swift +++ b/UnitTests/Sources/RoomSummaryProviderTests.swift @@ -41,6 +41,38 @@ final class RoomSummaryProviderTests { #expect(dynamicEntriesController.setFilterKindReceivedInvocations.last == .all(filters: [.all(filters: [.favourite, .joined])] + baseFilters)) } + @Test + func joinedRoomsOnlyRustFilters() async { + // Given a new room provider that only includes joined rooms. + setup(includesOnlyJoinedRooms: true) + await Task.yield() + + // Then the joined filter should be added to the default Rust filters. + #expect(dynamicEntriesController.setFilterKindReceivedInvocations.last == .all(filters: baseFilters + [.joined])) + + // When searching. + roomSummaryProvider.setFilter(.search(query: "Foundation")) + await Task.yield() + + // Then the search should be restricted to joined rooms too. + #expect(dynamicEntriesController.setFilterKindReceivedInvocations.last == .all(filters: [.normalizedMatchRoomName(pattern: "Foundation")] + baseFilters + [.joined])) + } + + @Test + func filterSetBeforeTheRoomListIsKept() async { + // Given a new room provider that is filtered before it has a room list. + setup(setsRoomList: false) + roomSummaryProvider.setFilter(.search(query: "Foundation")) + + // When setting its room list. + roomSummaryProvider.setRoomList(roomList) + await Task.yield() + + // Then the filter should be applied instead of the default one. + #expect(dynamicEntriesController.setFilterKindCallsCount == 1) + #expect(dynamicEntriesController.setFilterKindReceivedInvocations.last == .all(filters: [.normalizedMatchRoomName(pattern: "Foundation")] + baseFilters)) + } + @Test func lowPriorityRustFilters() async { // Given a new room provider with the low priority filter enabled. @@ -89,7 +121,7 @@ final class RoomSummaryProviderTests { // MARK: - Helpers - private func setup(isLowPriorityFilterEnabled: Bool = false) { + private func setup(isLowPriorityFilterEnabled: Bool = false, includesOnlyJoinedRooms: Bool = false, setsRoomList: Bool = true) { userSettings = UserSettings.volatile() userSettings.lowPriorityFilterEnabled = isLowPriorityFilterEnabled @@ -103,6 +135,7 @@ final class RoomSummaryProviderTests { roomSummaryProvider = RoomSummaryProvider(roomListService: RoomListServiceSDKMock(), eventStringBuilder: eventStringBuilder, name: "Test", + includesOnlyJoinedRooms: includesOnlyJoinedRooms, notificationSettings: NotificationSettingsProxyMock(with: .init()), userSettings: userSettings) @@ -113,6 +146,9 @@ final class RoomSummaryProviderTests { roomList = RoomListSDKMock() roomList.entriesWithDynamicAdaptersPageSizeListenerReturnValue = dynamicAdaptersResult roomList.loadingStateListenerReturnValue = .some(.init(state: .notLoaded, stateStream: TaskHandleSDKMock())) - roomSummaryProvider.setRoomList(roomList) + + if setsRoomList { + roomSummaryProvider.setRoomList(roomList) + } } } diff --git a/UnitTests/Sources/RoomSummaryTests.swift b/UnitTests/Sources/RoomSummaryTests.swift index 1fb2da855..1ad6836a9 100644 --- a/UnitTests/Sources/RoomSummaryTests.swift +++ b/UnitTests/Sources/RoomSummaryTests.swift @@ -100,6 +100,7 @@ struct RoomSummaryTests { RoomSummary(room: RoomSDKMock(), id: roomDetails.id, joinRequestType: nil, + isJoined: true, name: roomDetails.name, isDirect: isDirect, isSpace: isSpace, diff --git a/UnitTests/Sources/SearchScreenViewModelTests.swift b/UnitTests/Sources/SearchScreenViewModelTests.swift index 6cc35922a..7f8eb1401 100644 --- a/UnitTests/Sources/SearchScreenViewModelTests.swift +++ b/UnitTests/Sources/SearchScreenViewModelTests.swift @@ -73,25 +73,6 @@ struct SearchScreenViewModelTests { #expect(!searchService.setQueryCalled) } - @Test - func searchingExcludesRoomsThatAreNotJoined() async throws { - let roomSummaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms + .mockInvites))) - let clientProxy = ClientProxyMock(.init()) - clientProxy.searchService = searchService - let viewModel = SearchScreenViewModel(roomSummaryProvider: roomSummaryProvider, - clientProxy: clientProxy, - mediaProvider: MediaProviderMock(.init()), - userIndicatorController: userIndicatorController, - userSettings: userSettings) - - let deferred = deferFulfillment(viewModel.context.observe(\.viewState.rooms)) { !$0.isEmpty } - viewModel.context.searchQuery = "room" - try await deferred.fulfill() - - #expect(roomSummaryProvider.setFilterReceivedFilter == .search(query: "room", joinedOnly: true)) - #expect(!viewModel.context.viewState.rooms.contains { $0.title == "First room" }) - } - @Test func messageSearch() async throws { let deferred = deferFulfillment(setQuerySubject) { $0 == "Foundation" } @@ -194,6 +175,17 @@ struct SearchScreenViewModelTests { #expect(context.viewState.breadcrumbs.map(\.id) == ["query-Second", "room-2"]) } + @Test + func breadcrumbsOfRoomsThatAreNotJoinedAreHidden() async throws { + staticRoomListSubject.send([.mock(id: "2", name: "Second"), .mock(id: "left", name: "Left", isJoined: false)]) + + let deferred = deferFulfillment(context.observe(\.viewState.breadcrumbs)) { !$0.isEmpty } + userSettings.searchBreadcrumbs = [.room(roomID: "left"), .room(roomID: "2")] + try await deferred.fulfill() + + #expect(context.viewState.breadcrumbs.map(\.id) == ["room-2"]) + } + /// The room list is empty when the app launches, so the rooms need resolving again once it has loaded. @Test func roomBreadcrumbsAreDisplayedAfterTheRoomListLoads() async throws {