diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 72dc41db8e..719b1636f7 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -570,6 +570,7 @@ 6A54F52443EC52AC5CD772C0 /* JoinRoomScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 869A8A4632E511351BFE2EC4 /* JoinRoomScreen.swift */; }; 6A64546ABE648ED9E6DBB459 /* RemoteSettingsHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D186A6DB8FAC5C9D0E4D61 /* RemoteSettingsHook.swift */; }; 6A916606A8B92FE8A990A219 /* XCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85A1941B874A3BE9CDDF43EF /* XCTestCase.swift */; }; + 6AB306367E56A6F6DFA0E2FF /* RoomSummaryProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46E441BA50705E6CEC89FE0 /* RoomSummaryProviderTests.swift */; }; 6AD722DD92E465E56D2885AB /* BugReportScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA919F521E9F0EE3638AFC85 /* BugReportScreen.swift */; }; 6AEB650311F694A5702255C9 /* UserProfileScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B4932E4EFBC8FAC10972CD /* UserProfileScreenCoordinator.swift */; }; 6B31508C6334C617360C2EAB /* RoomMemberDetailsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC589E641AE46EFB2962534D /* RoomMemberDetailsViewModelTests.swift */; }; @@ -2679,6 +2680,7 @@ F409D44C2E6BE50462E82F8A /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/Localizable.strings"; sourceTree = ""; }; F4469F6AE311BDC439B3A5EC /* UserSessionMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionMock.swift; sourceTree = ""; }; F4548A9BDE5CB3AB864BCA9F /* EffectsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EffectsView.swift; sourceTree = ""; }; + F46E441BA50705E6CEC89FE0 /* RoomSummaryProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProviderTests.swift; sourceTree = ""; }; F4CEB4590CCF70F0E3C0B171 /* GeneratedAccessibilityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneratedAccessibilityTests.swift; sourceTree = ""; }; F506C6ADB1E1DA6638078E11 /* UITests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F51D674A5B5F1FE1B878E20F /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = ""; }; @@ -4498,6 +4500,7 @@ 48FEFF746DB341CDB18D7AAA /* RoomRolesAndPermissionsScreenViewModelTests.swift */, 93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */, AEEAFB646E583655652C3D04 /* RoomStateEventStringBuilderTests.swift */, + F46E441BA50705E6CEC89FE0 /* RoomSummaryProviderTests.swift */, 046C0D3F53B0B5EF0A1F5BEA /* RoomSummaryTests.swift */, 2E88534A39781D76487D59DF /* SecureBackupKeyBackupScreenViewModelTests.swift */, 848F69921527D31CAACB93AF /* SecureBackupLogoutConfirmationScreenViewModelTests.swift */, @@ -7168,6 +7171,7 @@ 84C631E734FD2555B39B681C /* RoomRolesAndPermissionsScreenViewModelTests.swift in Sources */, 46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */, CC0D088F505F33A20DC5590F /* RoomStateEventStringBuilderTests.swift in Sources */, + 6AB306367E56A6F6DFA0E2FF /* RoomSummaryProviderTests.swift in Sources */, 15913A5B07118C1268A840E4 /* RoomSummaryTests.swift in Sources */, 7691233E3572A9173FD96CB3 /* SecureBackupKeyBackupScreenViewModelTests.swift in Sources */, EB87DF90CF6F8D5D12404C6E /* SecureBackupLogoutConfirmationScreenViewModelTests.swift in Sources */, diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 1d60bb51fc..e83e2ff1fb 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -1075,6 +1075,7 @@ "screen_roomlist_filter_favourites_empty_state_title" = "You don’t have favourite chats yet"; "screen_roomlist_filter_invites_empty_state_title" = "You don't have any pending invites."; "screen_roomlist_filter_low_priority" = "Low Priority"; +"screen_roomlist_filter_low_priority_empty_state_title" = "You don’t have any low priority chats yet"; "screen_roomlist_filter_mixed_empty_state_subtitle" = "You can deselect filters in order to see your other chats"; "screen_roomlist_filter_mixed_empty_state_title" = "You don’t have chats for this selection"; "screen_roomlist_filter_people_empty_state_title" = "You don’t have any DMs yet"; diff --git a/ElementX/Sources/Application/Settings/AppSettings.swift b/ElementX/Sources/Application/Settings/AppSettings.swift index 4cfbd9401b..1344b2a7e7 100644 --- a/ElementX/Sources/Application/Settings/AppSettings.swift +++ b/ElementX/Sources/Application/Settings/AppSettings.swift @@ -55,6 +55,7 @@ final class AppSettings { // Feature flags case publicSearchEnabled case fuzzyRoomListSearchEnabled + case lowPriorityFilterEnabled case enableOnlySignedDeviceIsolationMode case enableKeyShareOnInvite case knockingEnabled @@ -345,6 +346,9 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.fuzzyRoomListSearchEnabled, defaultValue: false, storageType: .userDefaults(store)) var fuzzyRoomListSearchEnabled + @UserPreference(key: UserDefaultsKeys.lowPriorityFilterEnabled, defaultValue: false, storageType: .userDefaults(store)) + var lowPriorityFilterEnabled + @UserPreference(key: UserDefaultsKeys.knockingEnabled, defaultValue: false, storageType: .userDefaults(store)) var knockingEnabled diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index ff17a3a968..47121f4c17 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -2581,6 +2581,8 @@ internal enum L10n { internal static var screenRoomlistFilterInvitesEmptyStateTitle: String { return L10n.tr("Localizable", "screen_roomlist_filter_invites_empty_state_title") } /// Low Priority internal static var screenRoomlistFilterLowPriority: String { return L10n.tr("Localizable", "screen_roomlist_filter_low_priority") } + /// You don’t have any low priority chats yet + internal static var screenRoomlistFilterLowPriorityEmptyStateTitle: String { return L10n.tr("Localizable", "screen_roomlist_filter_low_priority_empty_state_title") } /// You can deselect filters in order to see your other chats internal static var screenRoomlistFilterMixedEmptyStateSubtitle: String { return L10n.tr("Localizable", "screen_roomlist_filter_mixed_empty_state_subtitle") } /// You don’t have chats for this selection diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift index 280e98ecb0..d304aa2ebe 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift @@ -113,7 +113,7 @@ struct HomeScreenViewState: BindableState { return rooms } - var bindings = HomeScreenViewStateBindings() + var bindings: HomeScreenViewStateBindings var placeholderRooms: [HomeScreenRoom] { (1...10).map { _ in @@ -136,7 +136,7 @@ struct HomeScreenViewState: BindableState { } struct HomeScreenViewStateBindings { - var filtersState = RoomListFiltersState() + var filtersState: RoomListFiltersState var searchQuery = "" var isSearchFieldFocused = false diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index 9ecba805c6..da1813e150 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -40,7 +40,8 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol roomSummaryProvider = userSession.clientProxy.roomSummaryProvider - super.init(initialViewState: .init(userID: userSession.clientProxy.userID), + super.init(initialViewState: .init(userID: userSession.clientProxy.userID, + bindings: .init(filtersState: .init(appSettings: appSettings))), mediaProvider: userSession.mediaProvider) userSession.clientProxy.userAvatarURLPublisher diff --git a/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFilterModels.swift b/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFilterModels.swift index 88bdf1f38c..671c36ded5 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFilterModels.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFilterModels.swift @@ -21,6 +21,7 @@ enum RoomListFilter: Int, CaseIterable, Identifiable { case rooms case favourites case invites + case lowPriority static var availableFilters: [RoomListFilter] { RoomListFilter.allCases @@ -38,6 +39,8 @@ enum RoomListFilter: Int, CaseIterable, Identifiable { return L10n.screenRoomlistFilterFavourites case .invites: return L10n.screenRoomlistFilterInvites + case .lowPriority: + return L10n.screenRoomlistFilterLowPriority } } @@ -50,10 +53,11 @@ enum RoomListFilter: Int, CaseIterable, Identifiable { case .unreads: return [.invites] case .favourites: - // When we will have Low Priority we may need to return it here - return [.invites] + return [.invites, .lowPriority] case .invites: - return [.rooms, .people, .unreads, .favourites] + return [.rooms, .people, .unreads, .favourites, .lowPriority] + case .lowPriority: + return [.favourites, .invites] } } @@ -69,20 +73,29 @@ enum RoomListFilter: Int, CaseIterable, Identifiable { return .all(filters: [.favourite, .joined]) case .invites: return .invite + case .lowPriority: + // Note: When not activated, the setFilter method automatically applies the .nonLowPriority filter. + return .all(filters: [.lowPriority, .joined]) } } } struct RoomListFiltersState { private(set) var activeFilters: OrderedSet + private let appSettings: AppSettings - init(activeFilters: OrderedSet = []) { + init(activeFilters: OrderedSet = [], appSettings: AppSettings) { self.activeFilters = .init(activeFilters) + self.appSettings = appSettings } var availableFilters: [RoomListFilter] { var availableFilters = OrderedSet(RoomListFilter.availableFilters) + if !appSettings.lowPriorityFilterEnabled { + availableFilters.remove(.lowPriority) + } + for filter in activeFilters { availableFilters.remove(filter) filter.incompatibleFilters.forEach { availableFilters.remove($0) } diff --git a/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFilterView.swift b/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFilterView.swift index 04102b324d..762327e15a 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFilterView.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFilterView.swift @@ -19,13 +19,6 @@ struct RoomListFilterView: View { } } -struct RoomListFilterView_Previews: PreviewProvider, TestablePreview { - static var previews: some View { - RoomListFilterView(filter: .people, isActive: .constant(false)) - RoomListFilterView(filter: .people, isActive: .constant(true)) - } -} - private struct FilterToggleStyle: ToggleStyle { private func strokeColor(isOn: Bool) -> Color { isOn ? .compound.bgActionPrimaryRest : .compound.borderInteractiveSecondary @@ -59,3 +52,12 @@ private struct FilterToggleStyle: ToggleStyle { } } } + +// MARK: - Previews + +struct RoomListFilterView_Previews: PreviewProvider, TestablePreview { + static var previews: some View { + RoomListFilterView(filter: .people, isActive: .constant(false)) + RoomListFilterView(filter: .people, isActive: .constant(true)) + } +} diff --git a/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFiltersEmptyStateView.swift b/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFiltersEmptyStateView.swift index 5a46340475..a13ffae010 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFiltersEmptyStateView.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFiltersEmptyStateView.swift @@ -23,6 +23,8 @@ struct RoomListFiltersEmptyStateView: View { return L10n.screenRoomlistFilterFavouritesEmptyStateTitle case .invites: return L10n.screenRoomlistFilterInvitesEmptyStateTitle + case .lowPriority: + return L10n.screenRoomlistFilterLowPriorityEmptyStateTitle } } return L10n.screenRoomlistFilterMixedEmptyStateTitle @@ -55,9 +57,11 @@ struct RoomListFiltersEmptyStateView_Previews: PreviewProvider, TestablePreview static var previews: some View { VStack(spacing: 24) { ForEach(RoomListFilter.allCases) { filter in - RoomListFiltersEmptyStateView(state: .init(activeFilters: [filter])) + RoomListFiltersEmptyStateView(state: .init(activeFilters: [filter], + appSettings: ServiceLocator.shared.settings)) } - RoomListFiltersEmptyStateView(state: .init(activeFilters: [.people, .favourites])) + RoomListFiltersEmptyStateView(state: .init(activeFilters: [.people, .favourites], + appSettings: ServiceLocator.shared.settings)) } .padding(.bottom) .previewLayout(.sizeThatFits) diff --git a/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFiltersView.swift b/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFiltersView.swift index 0902b01126..3fba296e2a 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFiltersView.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/Filters/RoomListFiltersView.swift @@ -87,7 +87,10 @@ struct RoomListFiltersView: View { struct RoomListFiltersView_Previews: PreviewProvider, TestablePreview { static var previews: some View { - RoomListFiltersView(state: .constant(.init())) - RoomListFiltersView(state: .constant(.init(activeFilters: [.rooms, .favourites]))) + RoomListFiltersView(state: .constant(.init(appSettings: ServiceLocator.shared.settings))) + RoomListFiltersView(state: .constant(.init(activeFilters: [.rooms, .favourites], + appSettings: ServiceLocator.shared.settings))) + RoomListFiltersView(state: .constant(.init(activeFilters: [.lowPriority], + appSettings: ServiceLocator.shared.settings))) } } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index dc7ee9ad17..3aee2f39ef 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift @@ -50,6 +50,7 @@ protocol DeveloperOptionsProtocol: AnyObject { var publicSearchEnabled: Bool { get set } var fuzzyRoomListSearchEnabled: Bool { get set } + var lowPriorityFilterEnabled: Bool { get set } var knockingEnabled: Bool { get set } } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index cf1e122102..31894cc0d4 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -50,6 +50,10 @@ struct DeveloperOptionsScreen: View { Toggle(isOn: $context.fuzzyRoomListSearchEnabled) { Text("Fuzzy searching") } + + Toggle(isOn: $context.lowPriorityFilterEnabled) { + Text("Low priority filter") + } } Section("Timeline") { diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift index 72bac90579..0430604160 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift @@ -127,9 +127,14 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { } _ = listUpdatesSubscriptionResult?.controller().setFilter(kind: .all(filters: filters)) case let .all(filters): - var filters = filters.map(\.rustFilter) - filters.append(contentsOf: [.nonLeft, .nonSpace, .deduplicateVersions]) - _ = listUpdatesSubscriptionResult?.controller().setFilter(kind: .all(filters: filters)) + var rustFilters = filters.map(\.rustFilter) + rustFilters.append(contentsOf: [.nonLeft, .nonSpace, .deduplicateVersions]) + + if !filters.contains(.lowPriority), appSettings.lowPriorityFilterEnabled { + rustFilters.append(.all(filters: [.nonLowPriority, .joined])) + } + + _ = listUpdatesSubscriptionResult?.controller().setFilter(kind: .all(filters: rustFilters)) } } diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPad-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPad-en-GB-0.png index 618fc0052e..f0f16be484 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPad-en-GB-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPad-en-GB-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67c41f94a969c49bb95c6b7a4ded36f977a48909935ffd900104b5d70fc79cd2 -size 162378 +oid sha256:254c2126afc7adfd661cdf7a50fb92435d36ebb9d14acd7c9bbb15fa9d3ac81e +size 188013 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPad-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPad-pseudo-0.png index e14cce07da..4dfeac3041 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPad-pseudo-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPad-pseudo-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a47d1a0cb409ed8974781aa1a7af3d5ae4d6ca72234a649417a66cc5b301637d -size 207102 +oid sha256:b8d5e4b64670781c2bfdd76cef6a4386ac536e05d430355771f8c83599420b89 +size 237096 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPhone-16-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPhone-16-en-GB-0.png index 2878881c0b..d352c291c4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPhone-16-en-GB-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPhone-16-en-GB-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f4dc44555b904b2c9805608dfe0134f36c33d6a0861d607d5d576db3bb82516 -size 148274 +oid sha256:5d64fce19432bbc67d746c457b5b3d6df360ddd1c7c3272635d34a84350d604c +size 173394 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPhone-16-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPhone-16-pseudo-0.png index 029709a59c..230c5694e7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPhone-16-pseudo-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersEmptyStateView.iPhone-16-pseudo-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:821ed985065f371e0cd0c0016ac4e0acf73732603cc83927ecf369dc692ff07e -size 276396 +oid sha256:9d7271a3cae947dc1e08e349478f5e0b8960f1067f98033685e756df19517ca7 +size 323307 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersView.iPad-en-GB-2.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersView.iPad-en-GB-2.png new file mode 100644 index 0000000000..f6b331701f --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersView.iPad-en-GB-2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:298a6d99b6e2a010ca3db350524498210f988cd6e302474cd7c3aa902de0a5cb +size 81295 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersView.iPad-pseudo-2.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersView.iPad-pseudo-2.png new file mode 100644 index 0000000000..a3d389331f --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersView.iPad-pseudo-2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d17daf1432f41dbb4ff28d010500f85e48da50e605a6671a8c28e828497f8116 +size 82478 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersView.iPhone-16-en-GB-2.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersView.iPhone-16-en-GB-2.png new file mode 100644 index 0000000000..8a838bc2b3 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersView.iPhone-16-en-GB-2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92f896aab9210b8aec55600da87c0032fdcbcd87c3a1aa8b5fc3dba5a570a67e +size 38601 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersView.iPhone-16-pseudo-2.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersView.iPhone-16-pseudo-2.png new file mode 100644 index 0000000000..a9b8bf78f2 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/roomListFiltersView.iPhone-16-pseudo-2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d19d7e28fadb77f1cbb3a0a0b6ccae511910c6b1924b28507b5f105712f801a +size 37696 diff --git a/UnitTests/Sources/RoomListFiltersStateTests.swift b/UnitTests/Sources/RoomListFiltersStateTests.swift index 2728e8a6e3..71a79a422e 100644 --- a/UnitTests/Sources/RoomListFiltersStateTests.swift +++ b/UnitTests/Sources/RoomListFiltersStateTests.swift @@ -10,16 +10,25 @@ import XCTest @testable import ElementX final class RoomListFiltersStateTests: XCTestCase { + var appSettings: AppSettings! + var state: RoomListFiltersState! + var allCasesWithoutLowPriority = RoomListFilter.allCases.filter { $0 != .lowPriority } override func setUp() { - state = RoomListFiltersState() + AppSettings.resetAllSettings() + appSettings = AppSettings() + state = RoomListFiltersState(appSettings: appSettings) + } + + override func tearDown() { + AppSettings.resetAllSettings() } func testInitialState() { XCTAssertFalse(state.isFiltering) XCTAssertEqual(state.activeFilters, []) - XCTAssertEqual(state.availableFilters, RoomListFilter.allCases) + XCTAssertEqual(state.availableFilters, allCasesWithoutLowPriority) } func testSetAndUnsetFilters() { @@ -30,7 +39,7 @@ final class RoomListFiltersStateTests: XCTestCase { state.deactivateFilter(.unreads) XCTAssertFalse(state.isFiltering) XCTAssertEqual(state.activeFilters, []) - XCTAssertEqual(state.availableFilters, RoomListFilter.allCases) + XCTAssertEqual(state.availableFilters, allCasesWithoutLowPriority) } func testMutuallyExclusiveFilters() { @@ -42,7 +51,7 @@ final class RoomListFiltersStateTests: XCTestCase { state.deactivateFilter(.people) XCTAssertFalse(state.isFiltering) XCTAssertEqual(state.activeFilters, []) - XCTAssertEqual(state.availableFilters, RoomListFilter.allCases) + XCTAssertEqual(state.availableFilters, allCasesWithoutLowPriority) state.activateFilter(.rooms) XCTAssertTrue(state.isFiltering) @@ -71,7 +80,7 @@ final class RoomListFiltersStateTests: XCTestCase { state.clearFilters() XCTAssertFalse(state.isFiltering) XCTAssertEqual(state.activeFilters, []) - XCTAssertEqual(state.availableFilters, RoomListFilter.allCases) + XCTAssertEqual(state.availableFilters, allCasesWithoutLowPriority) } func testOrder() { @@ -81,7 +90,7 @@ final class RoomListFiltersStateTests: XCTestCase { state.deactivateFilter(.favourites) XCTAssertEqual(state.activeFilters, []) - XCTAssertEqual(state.availableFilters, RoomListFilter.allCases) + XCTAssertEqual(state.availableFilters, allCasesWithoutLowPriority) state.activateFilter(.rooms) XCTAssertEqual(state.activeFilters, [.rooms]) @@ -95,4 +104,25 @@ final class RoomListFiltersStateTests: XCTestCase { XCTAssertEqual(state.activeFilters, [.rooms]) XCTAssertEqual(state.availableFilters, [.unreads, .favourites]) } + + // MARK: Low Priority feature flag + + // Don't forget to add .lowPriority into the mix above when enabling the feature. + func testWithLowPriorityFeature() { + enableLowPriorityFeature() + XCTAssertFalse(state.isFiltering) + XCTAssertEqual(state.activeFilters, []) + XCTAssertEqual(state.availableFilters, RoomListFilter.allCases) + + state.activateFilter(.lowPriority) + XCTAssertEqual(state.activeFilters, [.lowPriority]) + XCTAssertEqual(state.availableFilters, [.unreads, .people, .rooms]) + } + + // MARK: - Helpers + + private func enableLowPriorityFeature() { + appSettings.lowPriorityFilterEnabled = true + state = RoomListFiltersState(appSettings: appSettings) + } } diff --git a/UnitTests/Sources/RoomSummaryProviderTests.swift b/UnitTests/Sources/RoomSummaryProviderTests.swift new file mode 100644 index 0000000000..0f9ef82540 --- /dev/null +++ b/UnitTests/Sources/RoomSummaryProviderTests.swift @@ -0,0 +1,116 @@ +// +// Copyright 2025 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +// Please see LICENSE files in the repository root for full details. +// + +import XCTest + +@testable import ElementX + +final class RoomSummaryProviderTests: XCTestCase { + var appSettings: AppSettings! + var roomList: RoomListSDKMock! + var dynamicEntriesController: RoomListDynamicEntriesControllerSDKMock! + + var roomSummaryProvider: RoomSummaryProvider! + + override func setUp() { + AppSettings.resetAllSettings() + appSettings = AppSettings() + } + + override func tearDown() { + AppSettings.resetAllSettings() + } + + func testDefaultRustFilters() async { + // Given a new room provider. + setupProvider() + await Task.yield() + + // Then it should have the default Rust filters enabled. + XCTAssertEqual(dynamicEntriesController.setFilterKindCallsCount, 1) + XCTAssertEqual(dynamicEntriesController.setFilterKindReceivedInvocations.last, .all(filters: [.nonLeft, + .nonSpace, + .deduplicateVersions])) + + // When setting one our user filters. + roomSummaryProvider.setFilter(.all(filters: [.favourites])) + await Task.yield() + + // Then that filter should be added to the default Rust filters. + XCTAssertEqual(dynamicEntriesController.setFilterKindCallsCount, 2) + XCTAssertEqual(dynamicEntriesController.setFilterKindReceivedInvocations.last, .all(filters: [.all(filters: [.favourite, .joined]), + .nonLeft, + .nonSpace, + .deduplicateVersions])) + } + + func testLowPriorityRustFilters() async { + // Given a new room provider with the low priority filter enabled. + setupProvider(isLowPriorityFilterEnabled: true) + await Task.yield() + + // Then the default Rust filters should include the non-low priority filter, + // so that low priority rooms are hidden from the top of the room list. + XCTAssertEqual(dynamicEntriesController.setFilterKindCallsCount, 1) + XCTAssertEqual(dynamicEntriesController.setFilterKindReceivedInvocations.last, .all(filters: [.nonLeft, + .nonSpace, + .deduplicateVersions, + .all(filters: [.nonLowPriority, .joined])])) + + // When setting the low priority filter. + roomSummaryProvider.setFilter(.all(filters: [.lowPriority])) + await Task.yield() + + // Then the non-low priority filter should be replaced with the low priority filter. + XCTAssertEqual(dynamicEntriesController.setFilterKindCallsCount, 2) + XCTAssertEqual(dynamicEntriesController.setFilterKindReceivedInvocations.last, .all(filters: [.all(filters: [.lowPriority, .joined]), + .nonLeft, + .nonSpace, + .deduplicateVersions])) + + // When setting another one of our filters. + roomSummaryProvider.setFilter(.all(filters: [.rooms])) + await Task.yield() + + // Then the filter should be combined with the non-low priority filter. + XCTAssertEqual(dynamicEntriesController.setFilterKindCallsCount, 3) + XCTAssertEqual(dynamicEntriesController.setFilterKindReceivedInvocations.last, .all(filters: [.all(filters: [.category(expect: .group), .joined]), + .nonLeft, + .nonSpace, + .deduplicateVersions, + .all(filters: [.nonLowPriority, .joined])])) + } + + // MARK: - Helpers + + private func setupProvider(isLowPriorityFilterEnabled: Bool = false) { + appSettings.lowPriorityFilterEnabled = isLowPriorityFilterEnabled + + let stateEventStringBuilder = RoomStateEventStringBuilder(userID: "@me:matrix.org") + let attributedStringBuilder = AttributedStringBuilder(mentionBuilder: MentionBuilder()) + let eventStringBuilder = RoomEventStringBuilder(stateEventStringBuilder: stateEventStringBuilder, + messageEventStringBuilder: RoomMessageEventStringBuilder(attributedStringBuilder: attributedStringBuilder, + destination: .roomList), + shouldDisambiguateDisplayNames: true, + shouldPrefixSenderName: true) + + roomSummaryProvider = RoomSummaryProvider(roomListService: RoomListServiceSDKMock(), + eventStringBuilder: eventStringBuilder, + name: "Test", + notificationSettings: NotificationSettingsProxyMock(with: .init()), + appSettings: appSettings) + + dynamicEntriesController = RoomListDynamicEntriesControllerSDKMock() + dynamicEntriesController.setFilterKindReturnValue = true + let dynamicAdaptersResult = RoomListEntriesWithDynamicAdaptersResultSDKMock() + dynamicAdaptersResult.controllerReturnValue = dynamicEntriesController + roomList = RoomListSDKMock() + roomList.entriesWithDynamicAdaptersPageSizeListenerReturnValue = dynamicAdaptersResult + roomList.loadingStateListenerReturnValue = .some(.init(state: .notLoaded, stateStream: .init(noPointer: .init()))) + roomSummaryProvider.setRoomList(roomList) + } +}