UI POC for room list filters (#2382)

Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com>
This commit is contained in:
Mauro
2024-01-26 10:43:05 +01:00
committed by GitHub
parent e755be4cda
commit 25ec8d2dcb
17 changed files with 337 additions and 39 deletions
@@ -0,0 +1,77 @@
//
// 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 SwiftUI
struct RoomListFilterView: View {
let filter: RoomListFilter
@StateObject var state: RoomListFiltersState
var body: some View {
let binding = Binding<Bool>(get: {
state.isEnabled(filter)
}, set: { isEnabled, _ in
state.set(filter, isEnabled: isEnabled)
})
Toggle(isOn: binding) {
Text(filter.localizedName)
}
.toggleStyle(FilterToggleStyle())
}
}
struct RoomListFilterView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
RoomListFilterView(filter: .people, state: .init())
RoomListFilterView(filter: .people, state: .init(enabledFilters: [.people]))
}
}
private struct FilterToggleStyle: ToggleStyle {
private func strokeColor(isOn: Bool) -> Color {
isOn ? .compound.bgActionPrimaryRest : .compound.borderInteractiveSecondary
}
private func backgroundColor(isOn: Bool) -> Color {
isOn ? .compound.bgActionPrimaryRest : .compound.bgCanvasDefault
}
private func foregroundColor(isOn: Bool) -> Color {
isOn ? .compound.textOnSolidPrimary : .compound.textPrimary
}
func makeBody(configuration: Configuration) -> some View {
let shape = RoundedRectangle(cornerRadius: 20)
configuration.label
.font(.compound.bodyLG)
.foregroundColor(foregroundColor(isOn: configuration.isOn))
.padding(.horizontal, 16)
.padding(.vertical, 8)
.background(shape.fill(backgroundColor(isOn: configuration.isOn)))
.overlay {
shape
.inset(by: 0.5)
.stroke(strokeColor(isOn: configuration.isOn))
}
.drawingGroup()
// The button breaks the animation for some reason, so better to use the label directly with an onTapGesture
.onTapGesture {
withAnimation(.elementDefault) {
configuration.isOn.toggle()
}
}
}
}
@@ -0,0 +1,67 @@
//
// 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 SwiftUI
struct RoomListFiltersView: View {
@StateObject var state: RoomListFiltersState
var body: some View {
ScrollView(.horizontal) {
LazyHStack(spacing: 8) {
if state.isFiltering {
clearButton
} else {
// This solves a weird issue withe the LazyHStack
// where it is resized when the button appears and disappears
clearButton
.hidden()
.frame(width: 0)
}
ForEach(state.sortedEnabledFilters) { filter in
RoomListFilterView(filter: filter, state: state)
}
ForEach(state.sortedAvailableFilters) { filter in
RoomListFilterView(filter: filter, state: state)
}
}
.padding(.leading, !state.isFiltering ? 8 : 16)
.padding(.vertical, 12)
}
.scrollIndicators(.hidden)
}
private var clearButton: some View {
Button(action: {
withAnimation(.elementDefault) {
state.clearFilters()
}
}, label: {
Image(systemName: "xmark.circle.fill")
.font(.system(size: 24))
.foregroundColor(.compound.bgActionPrimaryRest)
})
}
}
// MARK: - Previews
struct RoomListFiltersView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
RoomListFiltersView(state: .init())
RoomListFiltersView(state: .init(enabledFilters: [.rooms, .favourites]))
}
}
@@ -52,10 +52,12 @@ struct HomeScreen: View {
.layoutPriority(1)
}
case .rooms:
topSection
LazyVStack(spacing: 0) {
HomeScreenRoomList(context: context)
LazyVStack(spacing: 0, pinnedViews: [.sectionHeaders]) {
Section {
HomeScreenRoomList(context: context)
} header: {
topSection
}
}
.searchable(text: $context.searchQuery)
.compoundSearchField()
@@ -191,20 +193,30 @@ struct HomeScreen: View {
@ViewBuilder
/// The session verification banner and invites button if either are needed.
private var topSection: some View {
if context.viewState.showSessionVerificationBanner {
HomeScreenSessionVerificationBanner(context: context)
} else if context.viewState.showRecoveryKeyConfirmationBanner {
HomeScreenRecoveryKeyConfirmationBanner(context: context)
}
if context.viewState.hasPendingInvitations, !context.isSearchFieldFocused {
HomeScreenInvitesButton(title: L10n.actionInvitesList, hasBadge: context.viewState.hasUnreadPendingInvitations) {
context.send(viewAction: .selectInvites)
VStack(spacing: 0) {
if context.viewState.shouldShowFilters {
filters
}
if context.viewState.showSessionVerificationBanner {
HomeScreenSessionVerificationBanner(context: context)
} else if context.viewState.showRecoveryKeyConfirmationBanner {
HomeScreenRecoveryKeyConfirmationBanner(context: context)
}
if context.viewState.hasPendingInvitations, !context.isSearchFieldFocused {
HomeScreenInvitesButton(title: L10n.actionInvitesList, hasBadge: context.viewState.hasUnreadPendingInvitations) {
context.send(viewAction: .selectInvites)
}
.accessibilityIdentifier(A11yIdentifiers.homeScreen.invites)
.frame(maxWidth: .infinity, alignment: .trailing)
}
.accessibilityIdentifier(A11yIdentifiers.homeScreen.invites)
.frame(maxWidth: .infinity, alignment: .trailing)
.padding(.vertical, -8.0)
}
.background(Color.compound.bgCanvasDefault)
}
private var filters: some View {
RoomListFiltersView(state: context.viewState.filtersState)
}
@ToolbarContentBuilder
@@ -34,7 +34,6 @@ struct HomeScreenInvitesButton: View {
}
.padding(.trailing, 16.0)
.padding(.leading, 64.0)
.padding(.vertical, 8.0)
}
}