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:
Stefan Ceriu
2023-06-20 12:20:26 +03:00
committed by GitHub
parent 3bbc16fd64
commit 863492985d
14 changed files with 199 additions and 107 deletions
+8
View File
@@ -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 {