Skip to content

Commit

Permalink
Room List Filters implementation (#2423)
Browse files Browse the repository at this point in the history
  • Loading branch information
Velin92 authored Feb 8, 2024
1 parent e1d1e99 commit 224e4e4
Show file tree
Hide file tree
Showing 18 changed files with 292 additions and 128 deletions.
10 changes: 9 additions & 1 deletion ElementX.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@
4BB51476A29E7E27BC14EA22 /* UserDetailsEditScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 022E6BD64CB4610B9C95FC02 /* UserDetailsEditScreenViewModel.swift */; };
4C356F5CCB4CDC99BFA45185 /* AppLockSetupPINScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7884BD256C091EB511B2EDF /* AppLockSetupPINScreenViewModelProtocol.swift */; };
4C5A638DAA8AF64565BA4866 /* EncryptedRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5351EBD7A0B9610548E4B7B2 /* EncryptedRoomTimelineItem.swift */; };
4C8C0C9FC10BA73AB7780534 /* RoomListFiltersStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE0C9653870803E4F91F474 /* RoomListFiltersStateTests.swift */; };
4E0D9E09B52CEC4C0E6211A8 /* MediaPickerScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F49FB9EE2913234F06CE68 /* MediaPickerScreenCoordinator.swift */; };
4E8A2A2CFEB212F14E49E1A1 /* AppLockSetupSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5484457C81B325660901B161 /* AppLockSetupSettingsScreen.swift */; };
4E8F17EBA24FBBA6ABB62ECB /* MockBackgroundTaskService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3948D16F021DFDB2CD26EAA8 /* MockBackgroundTaskService.swift */; };
Expand Down Expand Up @@ -968,6 +969,7 @@
F421FD5979EF53C8204BDC77 /* SecureBackupLogoutConfirmationScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CC09F30B0E1010951952BDC /* SecureBackupLogoutConfirmationScreenUITests.swift */; };
F4433EF57B4BB3C077F8B00E /* SessionVerificationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD9E0FFA29EAACFF3AB9732 /* SessionVerificationScreenViewModel.swift */; };
F4971845B5C4F270F6BC5745 /* ScaledFrameModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D82F234B3576BD6268C7950 /* ScaledFrameModifier.swift */; };
F4996C82A4B3A5FF0C8EDD03 /* RoomListFilterModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = E06AAD6D9D3F5833E7A5A2F9 /* RoomListFilterModels.swift */; };
F508683B76EF7B23BB2CBD6D /* TimelineItemPlainStylerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94BCC8A9C73C1F838122C645 /* TimelineItemPlainStylerView.swift */; };
F50A6FCE26714E27FE5495DD /* PollOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F23B21CF15F9F4BAA0788B /* PollOptionView.swift */; };
F519DE17A3A0F760307B2E6D /* InviteUsersScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D155E09BF961BBA8F85263 /* InviteUsersScreenViewModel.swift */; };
Expand Down Expand Up @@ -1565,6 +1567,7 @@
8977176AB534AA41630395BC /* LegalInformationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenViewModelProtocol.swift; sourceTree = "<group>"; };
897DF5E9A70CE05A632FC8AF /* UTType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTType.swift; sourceTree = "<group>"; };
8AB10FA6570DD08B3966C159 /* WelcomeScreenScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreenScreenUITests.swift; sourceTree = "<group>"; };
8AE0C9653870803E4F91F474 /* RoomListFiltersStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomListFiltersStateTests.swift; sourceTree = "<group>"; };
8AE78FA0011E07920AE83135 /* PlainMentionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlainMentionBuilder.swift; sourceTree = "<group>"; };
8AFCE895ECFFA53FEE64D62B /* MediaLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaLoader.swift; sourceTree = "<group>"; };
8BEBF0E59F25E842EDB6FD11 /* LocationSharingScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSharingScreenModels.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1883,6 +1886,7 @@
DF05DA24F71B455E8EFEBC3B /* SessionVerificationViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationViewModelTests.swift; sourceTree = "<group>"; };
DF3D25B3EDB283B5807EADCF /* ReadMarkerRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerRoomTimelineItem.swift; sourceTree = "<group>"; };
E062C1750EFC8627DE4CAB8E /* MapTilerAuthorization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerAuthorization.swift; sourceTree = "<group>"; };
E06AAD6D9D3F5833E7A5A2F9 /* RoomListFilterModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomListFilterModels.swift; sourceTree = "<group>"; };
E0FCA0957FAA0E15A9F5579D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Untranslated.stringsdict; sourceTree = "<group>"; };
E10DA51DBC8C7E1460DBCCBD /* UserProfileListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileListRow.swift; sourceTree = "<group>"; };
E1573D28C8A9FB6399D0EEFB /* SecureBackupLogoutConfirmationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenCoordinator.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2130,6 +2134,7 @@
037A5661B26EC6BE068188D7 /* Filters */ = {
isa = PBXGroup;
children = (
E06AAD6D9D3F5833E7A5A2F9 /* RoomListFilterModels.swift */,
E6372DD10DED30E7AD7BCE21 /* RoomListFiltersView.swift */,
24EC819497BB5F8C4998D760 /* RoomListFilterView.swift */,
);
Expand Down Expand Up @@ -3343,6 +3348,7 @@
00E5B2CBEF8F96424F095508 /* RoomDetailsEditScreenViewModelTests.swift */,
2EFE1922F39398ABFB36DF3F /* RoomDetailsViewModelTests.swift */,
4FCB2126C091EEF2454B4D56 /* RoomFlowCoordinatorTests.swift */,
8AE0C9653870803E4F91F474 /* RoomListFiltersStateTests.swift */,
EC589E641AE46EFB2962534D /* RoomMemberDetailsViewModelTests.swift */,
69B63F817FE305548DB4B512 /* RoomMembersListViewModelTests.swift */,
58D295F0081084F38DB20893 /* RoomNotificationSettingsScreenViewModelTests.swift */,
Expand Down Expand Up @@ -5328,6 +5334,7 @@
9DD84E014ADFB2DD813022D5 /* RoomDetailsEditScreenViewModelTests.swift in Sources */,
EA974337FA7D040E7C74FE6E /* RoomDetailsViewModelTests.swift in Sources */,
095D3906CF2F940C2D2D17CC /* RoomFlowCoordinatorTests.swift in Sources */,
4C8C0C9FC10BA73AB7780534 /* RoomListFiltersStateTests.swift in Sources */,
6B31508C6334C617360C2EAB /* RoomMemberDetailsViewModelTests.swift in Sources */,
CAF8755E152204F55F8D6B5B /* RoomMembersListViewModelTests.swift in Sources */,
E49F74BD93230BDEFFE5EA51 /* RoomNotificationSettingsScreenViewModelTests.swift in Sources */,
Expand Down Expand Up @@ -5796,6 +5803,7 @@
42F1C8731166633E35A6D7E6 /* RoomEventStringBuilder.swift in Sources */,
D55AF9B5B55FEED04771A461 /* RoomFlowCoordinator.swift in Sources */,
04A16B45228F7678A027C079 /* RoomHeaderView.swift in Sources */,
F4996C82A4B3A5FF0C8EDD03 /* RoomListFilterModels.swift in Sources */,
4A9CEEE612D6D8B3DDBD28BA /* RoomListFilterView.swift in Sources */,
33F1FB19F222BA9930AB1A00 /* RoomListFiltersView.swift in Sources */,
FA4296218444C48BC890F46B /* RoomMemberDetails.swift in Sources */,
Expand Down Expand Up @@ -6763,7 +6771,7 @@
repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift";
requirement = {
kind = exactVersion;
version = 1.1.37;
version = 1.1.38;
};
};
821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-rust-components-swift",
"state" : {
"revision" : "4d0d75004f8361530d7424ab198e363027823718",
"version" : "1.1.37"
"revision" : "691d8b0f0994d9669fadbd2452bef7270f3713ad",
"version" : "1.1.38"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class GlobalSearchScreenViewModel: GlobalSearchScreenViewModelType, GlobalSearch
.map(\.bindings.searchQuery)
.removeDuplicates()
.sink { [weak self] searchQuery in
self?.roomSummaryProvider.setFilter(.normalizedMatchRoomName(searchQuery))
self?.roomSummaryProvider.setFilter(.include(.init(query: searchQuery)))
}
.store(in: &cancellables)

Expand All @@ -60,7 +60,7 @@ class GlobalSearchScreenViewModel: GlobalSearchScreenViewModelType, GlobalSearch
switch viewAction {
case .dismiss:
actionsSubject.send(.dismiss)
roomSummaryProvider.setFilter(.all) // This is a shared provider
roomSummaryProvider.setFilter(.include(.all)) // This is a shared provider
case .select(let roomID):
actionsSubject.send(.select(roomID: roomID))
case .reachedTop:
Expand Down
85 changes: 0 additions & 85 deletions ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -212,88 +212,3 @@ extension HomeScreenRoom {
avatarURL: details.avatarURL)
}
}

enum RoomListFilter: Int, CaseIterable, Identifiable {
var id: Int {
rawValue
}

case people
case rooms
case unreads
case favourites
case lowPriority

var localizedName: String {
switch self {
case .people:
return L10n.screenRoomlistFilterPeople
case .rooms:
return L10n.screenRoomlistFilterRooms
case .unreads:
return L10n.screenRoomlistFilterUnreads
case .favourites:
return L10n.screenRoomlistFilterFavourites
case .lowPriority:
return L10n.screenRoomlistFilterLowPriority
}
}

var complementaryFilter: RoomListFilter? {
switch self {
case .people:
return .rooms
case .rooms:
return .people
case .unreads:
return nil
case .favourites:
return .lowPriority
case .lowPriority:
return .favourites
}
}
}

final class RoomListFiltersState: ObservableObject {
@Published private var enabledFilters: Set<RoomListFilter>

init(enabledFilters: Set<RoomListFilter> = []) {
self.enabledFilters = enabledFilters
}

var sortedEnabledFilters: [RoomListFilter] {
enabledFilters.sorted(by: { $0.rawValue < $1.rawValue })
}

var sortedAvailableFilters: [RoomListFilter] {
var availableFilters = Set(RoomListFilter.allCases)
for filter in enabledFilters {
availableFilters.remove(filter)
if let complementaryFilter = filter.complementaryFilter {
availableFilters.remove(complementaryFilter)
}
}
return availableFilters.sorted(by: { $0.rawValue < $1.rawValue })
}

var isFiltering: Bool {
!enabledFilters.isEmpty
}

func set(_ filter: RoomListFilter, isEnabled: Bool) {
if isEnabled {
enabledFilters.insert(filter)
} else {
enabledFilters.remove(filter)
}
}

func clearFilters() {
enabledFilters.removeAll()
}

func isEnabled(_ filter: RoomListFilter) -> Bool {
enabledFilters.contains(filter)
}
}
22 changes: 17 additions & 5 deletions ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,17 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
.store(in: &cancellables)

appSettings.$roomListFiltersEnabled
.weakAssign(to: \.state.shouldShowFilters, on: self)
.sink { [weak self] value in
guard let self else {
return
}
if !value {
state.shouldShowFilters = false
state.filtersState.clearFilters()
} else {
state.shouldShowFilters = true
}
}
.store(in: &cancellables)

appSettings.$markAsUnreadEnabled
Expand All @@ -96,8 +106,9 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol

let isSearchFieldFocused = context.$viewState.map(\.bindings.isSearchFieldFocused)
let searchQuery = context.$viewState.map(\.bindings.searchQuery)
let enabledFilters = context.viewState.filtersState.$activeFilters
isSearchFieldFocused
.combineLatest(searchQuery)
.combineLatest(searchQuery, enabledFilters)
.removeDuplicates { $0 == $1 }
.map { _ in () }
.sink { [weak self] in
Expand Down Expand Up @@ -196,12 +207,13 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol

private func updateFilter() {
if state.shouldHideRoomList {
roomSummaryProvider?.setFilter(.none)
roomSummaryProvider?.setFilter(.excludeAll)
} else {
if state.bindings.isSearchFieldFocused {
roomSummaryProvider?.setFilter(.normalizedMatchRoomName(state.bindings.searchQuery))
roomSummaryProvider?.setFilter(.include(.init(query: state.bindings.searchQuery,
filters: state.filtersState.activeFilters)))
} else {
roomSummaryProvider?.setFilter(.all)
roomSummaryProvider?.setFilter(.include(.init(filters: state.filtersState.activeFilters)))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//
// Copyright 2024 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Combine
import Foundation

import MatrixRustSDK

enum RoomListFilter: Int, CaseIterable, Identifiable {
var id: Int {
rawValue
}

case people
case rooms
case unreads
case favourites

var localizedName: String {
switch self {
case .people:
return L10n.screenRoomlistFilterPeople
case .rooms:
return L10n.screenRoomlistFilterRooms
case .unreads:
return L10n.screenRoomlistFilterUnreads
case .favourites:
return L10n.screenRoomlistFilterFavourites
}
}

var incompatibleFilter: RoomListFilter? {
switch self {
case .people:
return .rooms
case .rooms:
return .people
case .unreads:
return nil
case .favourites:
// When we will have Low Priority we may need to return it here
return nil
}
}

var rustFilter: RoomListEntriesDynamicFilterKind? {
switch self {
case .people:
return .category(expect: .people)
case .rooms:
return .category(expect: .group)
case .unreads:
return .unread
case .favourites:
// Not implemented yet
return nil
}
}
}

final class RoomListFiltersState: ObservableObject {
@Published private(set) var activeFilters: Set<RoomListFilter>

init(activeFilters: Set<RoomListFilter> = []) {
self.activeFilters = activeFilters
}

var sortedActiveFilters: [RoomListFilter] {
activeFilters.sorted(by: { $0.rawValue < $1.rawValue })
}

var availableFilters: [RoomListFilter] {
var availableFilters = Set(RoomListFilter.allCases)
for filter in activeFilters {
availableFilters.remove(filter)
if let complementaryFilter = filter.incompatibleFilter {
availableFilters.remove(complementaryFilter)
}
}
return availableFilters.sorted(by: { $0.rawValue < $1.rawValue })
}

var isFiltering: Bool {
!activeFilters.isEmpty
}

func activateFilter(_ filter: RoomListFilter) {
if let incompatibleFilter = filter.incompatibleFilter,
activeFilters.contains(incompatibleFilter) {
fatalError("[RoomListFiltersState] adding mutually exclusive filters is not allowed")
}
activeFilters.insert(filter)
}

func deactivateFilter(_ filter: RoomListFilter) {
activeFilters.remove(filter)
}

func clearFilters() {
activeFilters.removeAll()
}

func isFilterActive(_ filter: RoomListFilter) -> Bool {
activeFilters.contains(filter)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ struct RoomListFilterView: View {

var body: some View {
let binding = Binding<Bool>(get: {
state.isEnabled(filter)
state.isFilterActive(filter)
}, set: { isEnabled, _ in
state.set(filter, isEnabled: isEnabled)
isEnabled ? state.activateFilter(filter) : state.deactivateFilter(filter)
})
Toggle(isOn: binding) {
Text(filter.localizedName)
Expand All @@ -36,7 +36,7 @@ struct RoomListFilterView: View {
struct RoomListFilterView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
RoomListFilterView(filter: .people, state: .init())
RoomListFilterView(filter: .people, state: .init(enabledFilters: [.people]))
RoomListFilterView(filter: .people, state: .init(activeFilters: [.people]))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ struct RoomListFiltersView: View {
.hidden()
.frame(width: 0)
}
ForEach(state.sortedEnabledFilters) { filter in
ForEach(state.sortedActiveFilters) { filter in
RoomListFilterView(filter: filter, state: state)
}
ForEach(state.sortedAvailableFilters) { filter in
ForEach(state.availableFilters) { filter in
RoomListFilterView(filter: filter, state: state)
}
}
Expand Down Expand Up @@ -62,6 +62,6 @@ struct RoomListFiltersView: View {
struct RoomListFiltersView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
RoomListFiltersView(state: .init())
RoomListFiltersView(state: .init(enabledFilters: [.rooms, .favourites]))
RoomListFiltersView(state: .init(activeFilters: [.rooms, .favourites]))
}
}
Loading

0 comments on commit 224e4e4

Please sign in to comment.