mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 16:24:11 +00:00
Various UI tweaks (#1112)
* Tweak invites button frame and padding * Implement persistent selections on the room list * Stop showing rooms when focusing the room list search field with an empty query * Tweaks after code review * Improve invite button UI * Fix unit tests
This commit is contained in:
@@ -417,6 +417,7 @@
|
||||
A009BDFB0A6816D4C392ADCB /* SettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF715D4FD4710EBB637D661 /* SettingsScreenViewModelProtocol.swift */; };
|
||||
A021827B528F1EDC9101CA58 /* AppCoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBC776F301D374A3298C69DA /* AppCoordinatorProtocol.swift */; };
|
||||
A0A0D2A9564BDA3FDE2E360F /* FormattedBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F73FF1A33198F5FAE9D34B1F /* FormattedBodyText.swift */; };
|
||||
A10D6CCDE2010C09EEA1A593 /* HomeScreenRoomList.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7661EFFCAA307A97D71132A /* HomeScreenRoomList.swift */; };
|
||||
A14A9419105A1CD42F0511C4 /* UserIndicatorModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43005941B3A2C9671E23C85 /* UserIndicatorModalView.swift */; };
|
||||
A17FAD2EBC53E17B5FD384DB /* InviteUsersScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22730A30C50AC2E3D5BA8642 /* InviteUsersScreenViewModelProtocol.swift */; };
|
||||
A1D4033881320C9EB88196E6 /* ServerConfirmationScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE846DDA83BFD7EC5C03760B /* ServerConfirmationScreenUITests.swift */; };
|
||||
@@ -565,6 +566,7 @@
|
||||
D2A15D03F81342A09340BD56 /* AnalyticsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFEEE93B82937B2E86F92EB /* AnalyticsScreen.swift */; };
|
||||
D2D70B5DB1A5E4AF0CD88330 /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 033DB41C51865A2E83174E87 /* target.yml */; };
|
||||
D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5098DA7799946A61E34A2373 /* FileRoomTimelineItem.swift */; };
|
||||
D3986615892E7CF05C86518A /* HomeScreenUserMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3BC2D3573D900A9C9F8C191 /* HomeScreenUserMenuButton.swift */; };
|
||||
D3FD96913D2B1AAA3149DAC7 /* CreateRoomViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D42EE0102D2857933625DD /* CreateRoomViewModelTests.swift */; };
|
||||
D415764645491F10344FC6AC /* Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F18AECC9D38C2B6D85F99C /* Publisher.swift */; };
|
||||
D43F0503EF2CBC55272538FE /* SDKGeneratedMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2F079B5DBD0D85FEA687AAE /* SDKGeneratedMocks.swift */; };
|
||||
@@ -1210,6 +1212,7 @@
|
||||
C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderAvatarImage.swift; sourceTree = "<group>"; };
|
||||
C733D11B421CFE3A657EF230 /* test_image.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = test_image.png; sourceTree = "<group>"; };
|
||||
C75EF87651B00A176AB08E97 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
C7661EFFCAA307A97D71132A /* HomeScreenRoomList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRoomList.swift; sourceTree = "<group>"; };
|
||||
C789E7BFC066CF39B8AE0974 /* NetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = "<group>"; };
|
||||
C830A64609CBD152F06E0457 /* NotificationConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationConstants.swift; sourceTree = "<group>"; };
|
||||
C843CF833BF6485B64AC87E1 /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = "<group>"; };
|
||||
@@ -1316,6 +1319,7 @@
|
||||
F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixEntityRegexTests.swift; sourceTree = "<group>"; };
|
||||
F348B5F2C12F9D4F4B4D3884 /* VideoRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||
F36C0A6D59717193F49EA986 /* UserSessionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionTests.swift; sourceTree = "<group>"; };
|
||||
F3BC2D3573D900A9C9F8C191 /* HomeScreenUserMenuButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenUserMenuButton.swift; sourceTree = "<group>"; };
|
||||
F3EAE3E9D5EF4A6D5D9C6CFD /* EmojiPickerScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
F4548A9BDE5CB3AB864BCA9F /* EffectsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EffectsView.swift; sourceTree = "<group>"; };
|
||||
F506C6ADB1E1DA6638078E11 /* UITests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -2008,6 +2012,8 @@
|
||||
B902EA6CD3296B0E10EE432B /* HomeScreen.swift */,
|
||||
24227FF9A2797F6EA7F69CDD /* HomeScreenInvitesButton.swift */,
|
||||
ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */,
|
||||
C7661EFFCAA307A97D71132A /* HomeScreenRoomList.swift */,
|
||||
F3BC2D3573D900A9C9F8C191 /* HomeScreenUserMenuButton.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
@@ -3979,6 +3985,8 @@
|
||||
64C373ACCFA26D42BA45CFAD /* HomeScreenInvitesButton.swift in Sources */,
|
||||
8810A2A30A68252EBB54EE05 /* HomeScreenModels.swift in Sources */,
|
||||
0AE0AB1952F186EB86719B4F /* HomeScreenRoomCell.swift in Sources */,
|
||||
A10D6CCDE2010C09EEA1A593 /* HomeScreenRoomList.swift in Sources */,
|
||||
D3986615892E7CF05C86518A /* HomeScreenUserMenuButton.swift in Sources */,
|
||||
DE4F8C4E0F1DB4832F09DE97 /* HomeScreenViewModel.swift in Sources */,
|
||||
56F0A22972A3BB519DA2261C /* HomeScreenViewModelProtocol.swift in Sources */,
|
||||
03D684A3AE85A23B3DA3B43F /* Image.swift in Sources */,
|
||||
|
||||
@@ -36,6 +36,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
private let sidebarNavigationStackCoordinator: NavigationStackCoordinator
|
||||
private let detailNavigationStackCoordinator: NavigationStackCoordinator
|
||||
|
||||
private let selectedRoomSubject = CurrentValueSubject<String?, Never>(nil)
|
||||
|
||||
var callback: ((UserSessionFlowCoordinatorAction) -> Void)?
|
||||
|
||||
init(userSession: UserSessionProtocol,
|
||||
@@ -109,7 +111,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
// swiftlint:disable:next cyclomatic_complexity
|
||||
// swiftlint:disable:next cyclomatic_complexity function_body_length
|
||||
private func setupStateMachine() {
|
||||
stateMachine.addTransitionHandler { [weak self] context in
|
||||
guard let self else { return }
|
||||
@@ -160,6 +162,15 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
stateMachine.addTransitionHandler { [weak self] context in
|
||||
switch context.toState {
|
||||
case .roomList(let selectedRoomId):
|
||||
self?.selectedRoomSubject.send(selectedRoomId)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
stateMachine.addErrorHandler { context in
|
||||
fatalError("Failed transition with context: \(context)")
|
||||
}
|
||||
@@ -170,7 +181,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
let parameters = HomeScreenCoordinatorParameters(userSession: userSession,
|
||||
attributedStringBuilder: AttributedStringBuilder(),
|
||||
bugReportService: bugReportService,
|
||||
navigationStackCoordinator: detailNavigationStackCoordinator)
|
||||
navigationStackCoordinator: detailNavigationStackCoordinator,
|
||||
selectedRoomPublisher: selectedRoomSubject.asCurrentValuePublisher())
|
||||
let coordinator = HomeScreenCoordinator(parameters: parameters)
|
||||
|
||||
coordinator.callback = { [weak self] action in
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
/// A view modifier that applies a shimmering effect to the view.
|
||||
@@ -72,12 +73,13 @@ extension View {
|
||||
struct ShimmerOverlay_Previews: PreviewProvider {
|
||||
static let viewModel = HomeScreenViewModel(userSession: MockUserSession(clientProxy: MockClientProxy(userID: ""),
|
||||
mediaProvider: MockMediaProvider()),
|
||||
attributedStringBuilder: AttributedStringBuilder())
|
||||
attributedStringBuilder: AttributedStringBuilder(),
|
||||
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher())
|
||||
|
||||
static var previews: some View {
|
||||
VStack {
|
||||
ForEach(0...8, id: \.self) { _ in
|
||||
HomeScreenRoomCell(room: .placeholder(), context: viewModel.context)
|
||||
HomeScreenRoomCell(room: .placeholder(), context: viewModel.context, isSelected: false)
|
||||
}
|
||||
}
|
||||
.redacted(reason: .placeholder)
|
||||
|
||||
@@ -22,6 +22,7 @@ struct HomeScreenCoordinatorParameters {
|
||||
let attributedStringBuilder: AttributedStringBuilderProtocol
|
||||
let bugReportService: BugReportServiceProtocol
|
||||
let navigationStackCoordinator: NavigationStackCoordinator
|
||||
let selectedRoomPublisher: CurrentValuePublisher<String?, Never>
|
||||
}
|
||||
|
||||
enum HomeScreenCoordinatorAction {
|
||||
@@ -48,7 +49,8 @@ final class HomeScreenCoordinator: CoordinatorProtocol {
|
||||
self.parameters = parameters
|
||||
|
||||
viewModel = HomeScreenViewModel(userSession: parameters.userSession,
|
||||
attributedStringBuilder: parameters.attributedStringBuilder)
|
||||
attributedStringBuilder: parameters.attributedStringBuilder,
|
||||
selectedRoomPublisher: parameters.selectedRoomPublisher)
|
||||
|
||||
viewModel.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
@@ -74,6 +74,8 @@ struct HomeScreenViewState: BindableState {
|
||||
var hasPendingInvitations = false
|
||||
var hasUnreadPendingInvitations = false
|
||||
|
||||
var selectedRoomID: String?
|
||||
|
||||
var visibleRooms: [HomeScreenRoom] {
|
||||
if roomListMode == .skeletons {
|
||||
return placeholderRooms
|
||||
|
||||
@@ -31,7 +31,9 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
var callback: ((HomeScreenViewModelAction) -> Void)?
|
||||
|
||||
// swiftlint:disable:next function_body_length cyclomatic_complexity
|
||||
init(userSession: UserSessionProtocol, attributedStringBuilder: AttributedStringBuilderProtocol) {
|
||||
init(userSession: UserSessionProtocol,
|
||||
attributedStringBuilder: AttributedStringBuilderProtocol,
|
||||
selectedRoomPublisher: CurrentValuePublisher<String?, Never>) {
|
||||
self.userSession = userSession
|
||||
self.attributedStringBuilder = attributedStringBuilder
|
||||
|
||||
@@ -59,6 +61,10 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
.weakAssign(to: \.state.userAvatarURL, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
selectedRoomPublisher
|
||||
.weakAssign(to: \.state.selectedRoomID, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
guard let roomSummaryProvider, let inviteSummaryProvider else {
|
||||
MXLog.error("Room summary provider unavailable")
|
||||
return
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Compound
|
||||
import SwiftUI
|
||||
|
||||
@@ -25,27 +26,27 @@ struct HomeScreen: View {
|
||||
@ObservedObject var context: HomeScreenViewModel.Context
|
||||
|
||||
@State private var scrollViewAdapter = ScrollViewAdapter()
|
||||
@State private var showingLogoutConfirmation = false
|
||||
@State private var showingBottomToolbar = true
|
||||
@State private var isSearching = false
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
if context.viewState.showSessionVerificationBanner {
|
||||
sessionVerificationBanner
|
||||
}
|
||||
|
||||
if context.viewState.hasPendingInvitations {
|
||||
|
||||
if context.viewState.hasPendingInvitations, !isSearching {
|
||||
HomeScreenInvitesButton(title: L10n.actionInvitesList, hasBadge: context.viewState.hasUnreadPendingInvitations) {
|
||||
context.send(viewAction: .selectInvites)
|
||||
}
|
||||
.padding(.trailing, 16)
|
||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||
.padding(.vertical, -8.0)
|
||||
}
|
||||
|
||||
if context.viewState.roomListMode == .skeletons {
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(context.viewState.visibleRooms) { room in
|
||||
HomeScreenRoomCell(room: room, context: context)
|
||||
HomeScreenRoomCell(room: room, context: context, isSelected: false)
|
||||
.redacted(reason: .placeholder)
|
||||
}
|
||||
}
|
||||
@@ -53,29 +54,7 @@ struct HomeScreen: View {
|
||||
.disabled(true)
|
||||
} else {
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(context.viewState.visibleRooms) { room in
|
||||
Group {
|
||||
if room.isPlaceholder {
|
||||
HomeScreenRoomCell(room: room, context: context)
|
||||
.redacted(reason: .placeholder)
|
||||
} else {
|
||||
HomeScreenRoomCell(room: room, context: context)
|
||||
.contextMenu {
|
||||
Button {
|
||||
context.send(viewAction: .showRoomDetails(roomIdentifier: room.id))
|
||||
} label: {
|
||||
Label(L10n.commonSettings, systemImage: "gearshape")
|
||||
}
|
||||
|
||||
Button(role: .destructive) {
|
||||
context.send(viewAction: .leaveRoom(roomIdentifier: room.id))
|
||||
} label: {
|
||||
Label(L10n.actionLeaveRoom, systemImage: "rectangle.portrait.and.arrow.right")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
HomeScreenRoomList(context: context, isSearching: $isSearching)
|
||||
}
|
||||
.searchable(text: $context.searchQuery)
|
||||
.compoundSearchField()
|
||||
@@ -132,7 +111,7 @@ struct HomeScreen: View {
|
||||
@ToolbarContentBuilder
|
||||
private var toolbar: some ToolbarContent {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
userMenuButton
|
||||
HomeScreenUserMenuButton(context: context)
|
||||
}
|
||||
|
||||
ToolbarItemGroup(placement: .bottomBar) {
|
||||
@@ -140,51 +119,11 @@ struct HomeScreen: View {
|
||||
newRoomButton
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var userMenuButton: some View {
|
||||
Menu {
|
||||
Section {
|
||||
Button(action: settings) {
|
||||
Label(L10n.commonSettings, systemImage: "gearshape")
|
||||
}
|
||||
}
|
||||
Section {
|
||||
MatrixUserShareLink(userID: context.viewState.userID) {
|
||||
Label(L10n.actionInvite, systemImage: "square.and.arrow.up")
|
||||
}
|
||||
Button(action: feedback) {
|
||||
Label(L10n.commonReportABug, systemImage: "ladybug")
|
||||
}
|
||||
}
|
||||
Section {
|
||||
Button(role: .destructive) {
|
||||
showingLogoutConfirmation = true
|
||||
} label: {
|
||||
Label(L10n.screenSignoutPreferenceItem, systemImage: "rectangle.portrait.and.arrow.right")
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
LoadableAvatarImage(url: context.viewState.userAvatarURL,
|
||||
name: context.viewState.userDisplayName,
|
||||
contentID: context.viewState.userID,
|
||||
avatarSize: .user(on: .home),
|
||||
imageProvider: context.imageProvider)
|
||||
.accessibilityIdentifier(A11yIdentifiers.homeScreen.userAvatar)
|
||||
}
|
||||
.alert(L10n.screenSignoutConfirmationDialogTitle,
|
||||
isPresented: $showingLogoutConfirmation) {
|
||||
Button(L10n.screenSignoutConfirmationDialogSubmit,
|
||||
role: .destructive,
|
||||
action: signOut)
|
||||
} message: {
|
||||
Text(L10n.screenSignoutConfirmationDialogContent)
|
||||
}
|
||||
.accessibilityLabel(L10n.a11yUserMenu)
|
||||
}
|
||||
|
||||
private var newRoomButton: some View {
|
||||
Button(action: startChat) {
|
||||
Button {
|
||||
context.send(viewAction: .startChat)
|
||||
} label: {
|
||||
Image(systemName: "square.and.pencil")
|
||||
}
|
||||
}
|
||||
@@ -224,22 +163,6 @@ struct HomeScreen: View {
|
||||
.cornerRadius(14)
|
||||
.padding(.horizontal, 16)
|
||||
}
|
||||
|
||||
private func settings() {
|
||||
context.send(viewAction: .userMenu(action: .settings))
|
||||
}
|
||||
|
||||
private func startChat() {
|
||||
context.send(viewAction: .startChat)
|
||||
}
|
||||
|
||||
private func feedback() {
|
||||
context.send(viewAction: .userMenu(action: .feedback))
|
||||
}
|
||||
|
||||
private func signOut() {
|
||||
context.send(viewAction: .userMenu(action: .signOut))
|
||||
}
|
||||
|
||||
private func updateVisibleRange() {
|
||||
guard let scrollView = scrollViewAdapter.scrollView,
|
||||
@@ -292,7 +215,8 @@ struct HomeScreen_Previews: PreviewProvider {
|
||||
mediaProvider: MockMediaProvider())
|
||||
|
||||
let viewModel = HomeScreenViewModel(userSession: userSession,
|
||||
attributedStringBuilder: AttributedStringBuilder())
|
||||
attributedStringBuilder: AttributedStringBuilder(),
|
||||
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher())
|
||||
|
||||
return NavigationStack {
|
||||
HomeScreen(context: viewModel.context)
|
||||
|
||||
@@ -25,7 +25,7 @@ struct HomeScreenInvitesButton: View {
|
||||
|
||||
var body: some View {
|
||||
Button(action: action) {
|
||||
HStack(spacing: 8) {
|
||||
HStack(spacing: 8.0) {
|
||||
Text(title)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
.font(.compound.bodyMDSemibold)
|
||||
@@ -34,7 +34,9 @@ struct HomeScreenInvitesButton: View {
|
||||
badge
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
.padding(.trailing, 16.0)
|
||||
.padding(.leading, 64.0)
|
||||
.padding(.vertical, 8.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct HomeScreenRoomCell: View {
|
||||
@@ -22,6 +23,7 @@ struct HomeScreenRoomCell: View {
|
||||
|
||||
let room: HomeScreenRoom
|
||||
let context: HomeScreenViewModel.Context
|
||||
let isSelected: Bool
|
||||
|
||||
private let verticalInsets = 12.0
|
||||
private let horizontalInsets = 16.0
|
||||
@@ -47,7 +49,7 @@ struct HomeScreenRoomCell: View {
|
||||
.padding(.horizontal, horizontalInsets)
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
.buttonStyle(HomeScreenRoomCellButtonStyle(isSelected: false))
|
||||
.buttonStyle(HomeScreenRoomCellButtonStyle(isSelected: isSelected))
|
||||
.accessibilityIdentifier(A11yIdentifiers.homeScreen.roomName(room.name))
|
||||
}
|
||||
|
||||
@@ -149,8 +151,9 @@ struct HomeScreenRoomCellButtonStyle: ButtonStyle {
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.background(configuration.isPressed || isSelected ? Color.compound.bgSubtleSecondary : .clear)
|
||||
.background(isSelected ? Color.compound.bgSubtleSecondary : Color.compound.bgCanvasDefault)
|
||||
.contentShape(Rectangle())
|
||||
.animation(.elementDefault, value: isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +174,8 @@ struct HomeScreenRoomCell_Previews: PreviewProvider {
|
||||
mediaProvider: MockMediaProvider())
|
||||
|
||||
let viewModel = HomeScreenViewModel(userSession: userSession,
|
||||
attributedStringBuilder: AttributedStringBuilder())
|
||||
attributedStringBuilder: AttributedStringBuilder(),
|
||||
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher())
|
||||
|
||||
let rooms: [HomeScreenRoom] = summaryProvider.roomListPublisher.value.compactMap { summary -> HomeScreenRoom? in
|
||||
switch summary {
|
||||
@@ -190,14 +194,14 @@ struct HomeScreenRoomCell_Previews: PreviewProvider {
|
||||
|
||||
return VStack(spacing: 0) {
|
||||
ForEach(rooms) { room in
|
||||
HomeScreenRoomCell(room: room, context: viewModel.context)
|
||||
HomeScreenRoomCell(room: room, context: viewModel.context, isSelected: false)
|
||||
}
|
||||
|
||||
HomeScreenRoomCell(room: .placeholder(), context: viewModel.context)
|
||||
HomeScreenRoomCell(room: .placeholder(), context: viewModel.context, isSelected: false)
|
||||
.redacted(reason: .placeholder)
|
||||
HomeScreenRoomCell(room: .placeholder(), context: viewModel.context)
|
||||
HomeScreenRoomCell(room: .placeholder(), context: viewModel.context, isSelected: false)
|
||||
.redacted(reason: .placeholder)
|
||||
HomeScreenRoomCell(room: .placeholder(), context: viewModel.context)
|
||||
HomeScreenRoomCell(room: .placeholder(), context: viewModel.context, isSelected: false)
|
||||
.redacted(reason: .placeholder)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// Copyright 2023 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 HomeScreenRoomList: View {
|
||||
@Environment(\.isSearching) var isSearchFieldFocused
|
||||
|
||||
@ObservedObject var context: HomeScreenViewModel.Context
|
||||
@Binding var isSearching: Bool
|
||||
|
||||
var body: some View {
|
||||
content
|
||||
.onChange(of: isSearchFieldFocused) { isSearching = $0 }
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var content: some View {
|
||||
if isSearchFieldFocused, context.searchQuery.count == 0 {
|
||||
EmptyView()
|
||||
} else {
|
||||
ForEach(context.viewState.visibleRooms) { room in
|
||||
if room.isPlaceholder {
|
||||
HomeScreenRoomCell(room: room, context: context, isSelected: false)
|
||||
.redacted(reason: .placeholder)
|
||||
} else {
|
||||
let isSelected = context.viewState.selectedRoomID == room.id
|
||||
HomeScreenRoomCell(room: room, context: context, isSelected: isSelected)
|
||||
.contextMenu {
|
||||
Button {
|
||||
context.send(viewAction: .showRoomDetails(roomIdentifier: room.id))
|
||||
} label: {
|
||||
Label(L10n.commonSettings, systemImage: "gearshape")
|
||||
}
|
||||
|
||||
Button(role: .destructive) {
|
||||
context.send(viewAction: .leaveRoom(roomIdentifier: room.id))
|
||||
} label: {
|
||||
Label(L10n.actionLeaveRoom, systemImage: "rectangle.portrait.and.arrow.right")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// Copyright 2023 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 HomeScreenUserMenuButton: View {
|
||||
@State private var showingLogoutConfirmation = false
|
||||
|
||||
let context: HomeScreenViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
Menu {
|
||||
Section {
|
||||
Button {
|
||||
context.send(viewAction: .userMenu(action: .settings))
|
||||
} label: {
|
||||
Label(L10n.commonSettings, systemImage: "gearshape")
|
||||
}
|
||||
}
|
||||
Section {
|
||||
MatrixUserShareLink(userID: context.viewState.userID) {
|
||||
Label(L10n.actionInvite, systemImage: "square.and.arrow.up")
|
||||
}
|
||||
Button {
|
||||
context.send(viewAction: .userMenu(action: .feedback))
|
||||
} label: {
|
||||
Label(L10n.commonReportABug, systemImage: "ladybug")
|
||||
}
|
||||
}
|
||||
Section {
|
||||
Button(role: .destructive) {
|
||||
showingLogoutConfirmation = true
|
||||
} label: {
|
||||
Label(L10n.screenSignoutPreferenceItem, systemImage: "rectangle.portrait.and.arrow.right")
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
LoadableAvatarImage(url: context.viewState.userAvatarURL,
|
||||
name: context.viewState.userDisplayName,
|
||||
contentID: context.viewState.userID,
|
||||
avatarSize: .user(on: .home),
|
||||
imageProvider: context.imageProvider)
|
||||
.accessibilityIdentifier(A11yIdentifiers.homeScreen.userAvatar)
|
||||
}
|
||||
.alert(L10n.screenSignoutConfirmationDialogTitle,
|
||||
isPresented: $showingLogoutConfirmation) {
|
||||
Button(L10n.screenSignoutConfirmationDialogSubmit,
|
||||
role: .destructive) {
|
||||
context.send(viewAction: .userMenu(action: .signOut))
|
||||
}
|
||||
} message: {
|
||||
Text(L10n.screenSignoutConfirmationDialogContent)
|
||||
}
|
||||
.accessibilityLabel(L10n.a11yUserMenu)
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,6 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
// swiftlint:disable:next function_body_length
|
||||
private func setupSubscriptions() {
|
||||
timelineController.callbacks
|
||||
.receive(on: DispatchQueue.main)
|
||||
|
||||
@@ -134,7 +134,8 @@ class MockScreen: Identifiable {
|
||||
let coordinator = HomeScreenCoordinator(parameters: .init(userSession: session,
|
||||
attributedStringBuilder: AttributedStringBuilder(),
|
||||
bugReportService: BugReportServiceMock(),
|
||||
navigationStackCoordinator: navigationStackCoordinator))
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher()))
|
||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
return navigationStackCoordinator
|
||||
case .settings:
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import XCTest
|
||||
|
||||
@testable import ElementX
|
||||
@@ -31,7 +32,8 @@ class HomeScreenViewModelTests: XCTestCase {
|
||||
clientProxy = MockClientProxy(userID: "@mock:client.com")
|
||||
viewModel = HomeScreenViewModel(userSession: MockUserSession(clientProxy: clientProxy,
|
||||
mediaProvider: MockMediaProvider()),
|
||||
attributedStringBuilder: AttributedStringBuilder())
|
||||
attributedStringBuilder: AttributedStringBuilder(),
|
||||
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher())
|
||||
}
|
||||
|
||||
func testSelectRoom() async throws {
|
||||
|
||||
Reference in New Issue
Block a user