Renaming screens returns (#829)

* Rename: StartChat -> StartChatScreen

* Rename: Login -> LoginScreen

* Rename: ServerSelection -> ServerSelectionScreen

* Rename: SoftLogout -> SoftLogoutScreen
This commit is contained in:
Stefan Ceriu
2023-04-25 12:13:03 +03:00
committed by GitHub
parent 24572c6582
commit b726cd149a
31 changed files with 286 additions and 286 deletions
@@ -220,7 +220,7 @@ class AppCoordinator: AppCoordinatorProtocol {
displayName = name displayName = name
} }
let credentials = SoftLogoutCredentials(userId: userSession.userID, let credentials = SoftLogoutScreenCredentials(userId: userSession.userID,
homeserverName: userSession.homeserver, homeserverName: userSession.homeserver,
userDisplayName: displayName, userDisplayName: displayName,
deviceId: userSession.deviceID) deviceId: userSession.deviceID)
@@ -228,10 +228,10 @@ class AppCoordinator: AppCoordinatorProtocol {
let authenticationService = AuthenticationServiceProxy(userSessionStore: userSessionStore) let authenticationService = AuthenticationServiceProxy(userSessionStore: userSessionStore)
_ = await authenticationService.configure(for: userSession.homeserver) _ = await authenticationService.configure(for: userSession.homeserver)
let parameters = SoftLogoutCoordinatorParameters(authenticationService: authenticationService, let parameters = SoftLogoutScreenCoordinatorParameters(authenticationService: authenticationService,
credentials: credentials, credentials: credentials,
keyBackupNeeded: false) keyBackupNeeded: false)
let coordinator = SoftLogoutCoordinator(parameters: parameters) let coordinator = SoftLogoutScreenCoordinator(parameters: parameters)
coordinator.callback = { result in coordinator.callback = { result in
switch result { switch result {
case .signedIn(let session): case .signedIn(let session):
@@ -72,10 +72,10 @@ class AuthenticationCoordinator: CoordinatorProtocol {
} }
private func showServerSelectionScreen() { private func showServerSelectionScreen() {
let parameters = ServerSelectionCoordinatorParameters(authenticationService: authenticationService, let parameters = ServerSelectionScreenCoordinatorParameters(authenticationService: authenticationService,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
isModallyPresented: false) isModallyPresented: false)
let coordinator = ServerSelectionCoordinator(parameters: parameters) let coordinator = ServerSelectionScreenCoordinator(parameters: parameters)
coordinator.callback = { [weak self] action in coordinator.callback = { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -92,9 +92,9 @@ class AuthenticationCoordinator: CoordinatorProtocol {
} }
private func showLoginScreen() { private func showLoginScreen() {
let parameters = LoginCoordinatorParameters(authenticationService: authenticationService, let parameters = LoginScreenCoordinatorParameters(authenticationService: authenticationService,
navigationStackCoordinator: navigationStackCoordinator) navigationStackCoordinator: navigationStackCoordinator)
let coordinator = LoginCoordinator(parameters: parameters) let coordinator = LoginScreenCoordinator(parameters: parameters)
coordinator.callback = { [weak self] action in coordinator.callback = { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -17,21 +17,21 @@
import AppAuth import AppAuth
import SwiftUI import SwiftUI
struct LoginCoordinatorParameters { struct LoginScreenCoordinatorParameters {
/// The service used to authenticate the user. /// The service used to authenticate the user.
let authenticationService: AuthenticationServiceProxyProtocol let authenticationService: AuthenticationServiceProxyProtocol
/// The navigation router used to present the server selection screen. /// The navigation router used to present the server selection screen.
let navigationStackCoordinator: NavigationStackCoordinator let navigationStackCoordinator: NavigationStackCoordinator
} }
enum LoginCoordinatorAction { enum LoginScreenCoordinatorAction {
/// Login was successful. /// Login was successful.
case signedIn(UserSessionProtocol) case signedIn(UserSessionProtocol)
} }
final class LoginCoordinator: CoordinatorProtocol { final class LoginScreenCoordinator: CoordinatorProtocol {
private let parameters: LoginCoordinatorParameters private let parameters: LoginScreenCoordinatorParameters
private var viewModel: LoginViewModelProtocol private var viewModel: LoginScreenViewModelProtocol
private let hostingController: UIViewController private let hostingController: UIViewController
/// Passed to the OIDC service to provide a view controller from which to present the authentication session. /// Passed to the OIDC service to provide a view controller from which to present the authentication session.
private let oidcUserAgent: OIDExternalUserAgentIOS? private let oidcUserAgent: OIDExternalUserAgentIOS?
@@ -45,14 +45,14 @@ final class LoginCoordinator: CoordinatorProtocol {
private var authenticationService: AuthenticationServiceProxyProtocol { parameters.authenticationService } private var authenticationService: AuthenticationServiceProxyProtocol { parameters.authenticationService }
private var navigationStackCoordinator: NavigationStackCoordinator { parameters.navigationStackCoordinator } private var navigationStackCoordinator: NavigationStackCoordinator { parameters.navigationStackCoordinator }
var callback: (@MainActor (LoginCoordinatorAction) -> Void)? var callback: (@MainActor (LoginScreenCoordinatorAction) -> Void)?
// MARK: - Setup // MARK: - Setup
init(parameters: LoginCoordinatorParameters) { init(parameters: LoginScreenCoordinatorParameters) {
self.parameters = parameters self.parameters = parameters
viewModel = LoginViewModel(homeserver: parameters.authenticationService.homeserver) viewModel = LoginScreenViewModel(homeserver: parameters.authenticationService.homeserver)
hostingController = UIHostingController(rootView: LoginScreen(context: viewModel.context)) hostingController = UIHostingController(rootView: LoginScreen(context: viewModel.context))
oidcUserAgent = OIDExternalUserAgentIOS(presenting: hostingController) oidcUserAgent = OIDExternalUserAgentIOS(presenting: hostingController)
@@ -199,11 +199,11 @@ final class LoginCoordinator: CoordinatorProtocol {
/// Presents the server selection screen as a modal. /// Presents the server selection screen as a modal.
private func presentServerSelectionScreen() { private func presentServerSelectionScreen() {
let parameters = ServerSelectionCoordinatorParameters(authenticationService: authenticationService, let parameters = ServerSelectionScreenCoordinatorParameters(authenticationService: authenticationService,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
isModallyPresented: false) isModallyPresented: false)
let coordinator = ServerSelectionCoordinator(parameters: parameters) let coordinator = ServerSelectionScreenCoordinator(parameters: parameters)
coordinator.callback = { [weak self, weak coordinator] action in coordinator.callback = { [weak self, weak coordinator] action in
guard let self, let coordinator else { return } guard let self, let coordinator else { return }
self.serverSelectionCoordinator(coordinator, didCompleteWith: action) self.serverSelectionCoordinator(coordinator, didCompleteWith: action)
@@ -213,8 +213,8 @@ final class LoginCoordinator: CoordinatorProtocol {
} }
/// Handles the result from the server selection modal, dismissing it after updating the view. /// Handles the result from the server selection modal, dismissing it after updating the view.
private func serverSelectionCoordinator(_ coordinator: ServerSelectionCoordinator, private func serverSelectionCoordinator(_ coordinator: ServerSelectionScreenCoordinator,
didCompleteWith action: ServerSelectionCoordinatorAction) { didCompleteWith action: ServerSelectionScreenCoordinatorAction) {
if action == .updated { if action == .updated {
updateViewModel() updateViewModel()
} }
@@ -16,7 +16,7 @@
import Foundation import Foundation
enum LoginViewModelAction: CustomStringConvertible { enum LoginScreenViewModelAction: CustomStringConvertible {
/// The user would like to select another server. /// The user would like to select another server.
case selectServer case selectServer
/// Parse the username and update the homeserver if included. /// Parse the username and update the homeserver if included.
@@ -45,13 +45,13 @@ enum LoginViewModelAction: CustomStringConvertible {
} }
} }
struct LoginViewState: BindableState { struct LoginScreenViewState: BindableState {
/// Data about the selected homeserver. /// Data about the selected homeserver.
var homeserver: LoginHomeserver var homeserver: LoginHomeserver
/// Whether a new homeserver is currently being loaded. /// Whether a new homeserver is currently being loaded.
var isLoading = false var isLoading = false
/// View state that can be bound to from SwiftUI. /// View state that can be bound to from SwiftUI.
var bindings: LoginBindings var bindings: LoginScreenBindings
/// The types of login supported by the homeserver. /// The types of login supported by the homeserver.
var loginMode: LoginMode { homeserver.loginMode } var loginMode: LoginMode { homeserver.loginMode }
@@ -67,16 +67,16 @@ struct LoginViewState: BindableState {
} }
} }
struct LoginBindings { struct LoginScreenBindings {
/// The username input by the user. /// The username input by the user.
var username = "" var username = ""
/// The password input by the user. /// The password input by the user.
var password = "" var password = ""
/// Information describing the currently displayed alert. /// Information describing the currently displayed alert.
var alertInfo: AlertInfo<LoginErrorType>? var alertInfo: AlertInfo<LoginScreenErrorType>?
} }
enum LoginViewAction { enum LoginScreenViewAction {
/// The user would like to select another server. /// The user would like to select another server.
case selectServer case selectServer
/// Parse the username to detect if a homeserver is included. /// Parse the username to detect if a homeserver is included.
@@ -89,7 +89,7 @@ enum LoginViewAction {
case continueWithOIDC case continueWithOIDC
} }
enum LoginErrorType: Hashable { enum LoginScreenErrorType: Hashable {
/// A specific error message shown in an alert. /// A specific error message shown in an alert.
case alert(String) case alert(String)
/// Looking up the homeserver from the username failed. /// Looking up the homeserver from the username failed.
@@ -16,19 +16,19 @@
import SwiftUI import SwiftUI
typealias LoginViewModelType = StateStoreViewModel<LoginViewState, LoginViewAction> typealias LoginScreenViewModelType = StateStoreViewModel<LoginScreenViewState, LoginScreenViewAction>
class LoginViewModel: LoginViewModelType, LoginViewModelProtocol { class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtocol {
var callback: (@MainActor (LoginViewModelAction) -> Void)? var callback: (@MainActor (LoginScreenViewModelAction) -> Void)?
init(homeserver: LoginHomeserver) { init(homeserver: LoginHomeserver) {
let bindings = LoginBindings() let bindings = LoginScreenBindings()
let viewState = LoginViewState(homeserver: homeserver, bindings: bindings) let viewState = LoginScreenViewState(homeserver: homeserver, bindings: bindings)
super.init(initialViewState: viewState) super.init(initialViewState: viewState)
} }
override func process(viewAction: LoginViewAction) { override func process(viewAction: LoginScreenViewAction) {
switch viewAction { switch viewAction {
case .selectServer: case .selectServer:
callback?(.selectServer) callback?(.selectServer)
@@ -52,7 +52,7 @@ class LoginViewModel: LoginViewModelType, LoginViewModelProtocol {
state.homeserver = homeserver state.homeserver = homeserver
} }
func displayError(_ type: LoginErrorType) { func displayError(_ type: LoginScreenErrorType) {
switch type { switch type {
case .alert(let message): case .alert(let message):
state.bindings.alertInfo = AlertInfo(id: type, state.bindings.alertInfo = AlertInfo(id: type,
@@ -17,9 +17,9 @@
import Foundation import Foundation
@MainActor @MainActor
protocol LoginViewModelProtocol { protocol LoginScreenViewModelProtocol {
var callback: (@MainActor (LoginViewModelAction) -> Void)? { get set } var callback: (@MainActor (LoginScreenViewModelAction) -> Void)? { get set }
var context: LoginViewModelType.Context { get } var context: LoginScreenViewModelType.Context { get }
/// Update the view to reflect that a new homeserver is being loaded. /// Update the view to reflect that a new homeserver is being loaded.
/// - Parameter isLoading: Whether or not the homeserver is being loaded. /// - Parameter isLoading: Whether or not the homeserver is being loaded.
@@ -31,5 +31,5 @@ protocol LoginViewModelProtocol {
/// Display an error to the user. /// Display an error to the user.
/// - Parameter type: The type of error to be displayed. /// - Parameter type: The type of error to be displayed.
func displayError(_ type: LoginErrorType) func displayError(_ type: LoginScreenErrorType)
} }
@@ -22,7 +22,7 @@ struct LoginScreen: View {
/// The focus state of the password text field. /// The focus state of the password text field.
@FocusState private var isPasswordFocused: Bool @FocusState private var isPasswordFocused: Bool
@ObservedObject var context: LoginViewModel.Context @ObservedObject var context: LoginScreenViewModel.Context
var body: some View { var body: some View {
ScrollView { ScrollView {
@@ -146,26 +146,26 @@ struct LoginScreen: View {
// MARK: - Previews // MARK: - Previews
struct Login_Previews: PreviewProvider { struct LoginScreen_Previews: PreviewProvider {
static let credentialsViewModel: LoginViewModel = { static let credentialsViewModel: LoginScreenViewModel = {
let viewModel = LoginViewModel(homeserver: .mockMatrixDotOrg) let viewModel = LoginScreenViewModel(homeserver: .mockMatrixDotOrg)
viewModel.context.username = "alice" viewModel.context.username = "alice"
viewModel.context.password = "password" viewModel.context.password = "password"
return viewModel return viewModel
}() }()
static var previews: some View { static var previews: some View {
screen(for: LoginViewModel(homeserver: .mockMatrixDotOrg)) screen(for: LoginScreenViewModel(homeserver: .mockMatrixDotOrg))
.previewDisplayName("matrix.org") .previewDisplayName("matrix.org")
screen(for: credentialsViewModel) screen(for: credentialsViewModel)
.previewDisplayName("Credentials Entered") .previewDisplayName("Credentials Entered")
screen(for: LoginViewModel(homeserver: .mockOIDC)) screen(for: LoginScreenViewModel(homeserver: .mockOIDC))
.previewDisplayName("OIDC") .previewDisplayName("OIDC")
screen(for: LoginViewModel(homeserver: .mockUnsupported)) screen(for: LoginScreenViewModel(homeserver: .mockUnsupported))
.previewDisplayName("Unsupported") .previewDisplayName("Unsupported")
} }
static func screen(for viewModel: LoginViewModel) -> some View { static func screen(for viewModel: LoginScreenViewModel) -> some View {
NavigationStack { NavigationStack {
LoginScreen(context: viewModel.context) LoginScreen(context: viewModel.context)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
@@ -23,21 +23,21 @@ enum MockServerSelectionScreenState: CaseIterable {
case nonModal case nonModal
/// Generate the view struct for the screen state. /// Generate the view struct for the screen state.
@MainActor var viewModel: ServerSelectionViewModel { @MainActor var viewModel: ServerSelectionScreenViewModel {
switch self { switch self {
case .matrix: case .matrix:
return ServerSelectionViewModel(homeserverAddress: "https://matrix.org", return ServerSelectionScreenViewModel(homeserverAddress: "https://matrix.org",
isModallyPresented: true) isModallyPresented: true)
case .emptyAddress: case .emptyAddress:
return ServerSelectionViewModel(homeserverAddress: "", return ServerSelectionScreenViewModel(homeserverAddress: "",
isModallyPresented: true) isModallyPresented: true)
case .invalidAddress: case .invalidAddress:
let viewModel = ServerSelectionViewModel(homeserverAddress: "thisisbad", let viewModel = ServerSelectionScreenViewModel(homeserverAddress: "thisisbad",
isModallyPresented: true) isModallyPresented: true)
viewModel.displayError(.footerMessage(L10n.errorUnknown)) viewModel.displayError(.footerMessage(L10n.errorUnknown))
return viewModel return viewModel
case .nonModal: case .nonModal:
return ServerSelectionViewModel(homeserverAddress: "https://matrix.org", return ServerSelectionScreenViewModel(homeserverAddress: "https://matrix.org",
isModallyPresented: false) isModallyPresented: false)
} }
} }
@@ -16,7 +16,7 @@
import SwiftUI import SwiftUI
struct ServerSelectionCoordinatorParameters { struct ServerSelectionScreenCoordinatorParameters {
/// The service used to authenticate the user. /// The service used to authenticate the user.
let authenticationService: AuthenticationServiceProxyProtocol let authenticationService: AuthenticationServiceProxyProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
@@ -24,22 +24,22 @@ struct ServerSelectionCoordinatorParameters {
let isModallyPresented: Bool let isModallyPresented: Bool
} }
enum ServerSelectionCoordinatorAction { enum ServerSelectionScreenCoordinatorAction {
case updated case updated
case dismiss case dismiss
} }
final class ServerSelectionCoordinator: CoordinatorProtocol { final class ServerSelectionScreenCoordinator: CoordinatorProtocol {
private let parameters: ServerSelectionCoordinatorParameters private let parameters: ServerSelectionScreenCoordinatorParameters
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private var viewModel: ServerSelectionViewModelProtocol private var viewModel: ServerSelectionScreenViewModelProtocol
private var authenticationService: AuthenticationServiceProxyProtocol { parameters.authenticationService } private var authenticationService: AuthenticationServiceProxyProtocol { parameters.authenticationService }
var callback: (@MainActor (ServerSelectionCoordinatorAction) -> Void)? var callback: (@MainActor (ServerSelectionScreenCoordinatorAction) -> Void)?
init(parameters: ServerSelectionCoordinatorParameters) { init(parameters: ServerSelectionScreenCoordinatorParameters) {
self.parameters = parameters self.parameters = parameters
viewModel = ServerSelectionViewModel(homeserverAddress: parameters.authenticationService.homeserver.address, viewModel = ServerSelectionScreenViewModel(homeserverAddress: parameters.authenticationService.homeserver.address,
isModallyPresented: parameters.isModallyPresented) isModallyPresented: parameters.isModallyPresented)
userIndicatorController = parameters.userIndicatorController userIndicatorController = parameters.userIndicatorController
} }
@@ -16,14 +16,14 @@
import Foundation import Foundation
enum ServerSelectionViewModelAction { enum ServerSelectionScreenViewModelAction {
/// The user would like to use the homeserver at the given address. /// The user would like to use the homeserver at the given address.
case confirm(homeserverAddress: String) case confirm(homeserverAddress: String)
/// Dismiss the view without using the entered address. /// Dismiss the view without using the entered address.
case dismiss case dismiss
} }
struct ServerSelectionViewState: BindableState { struct ServerSelectionScreenViewState: BindableState {
/// The message to be shown in the text field footer when no error has occurred. /// The message to be shown in the text field footer when no error has occurred.
private let regularFooterMessage = { private let regularFooterMessage = {
let linkPlaceholder = "{link}" let linkPlaceholder = "{link}"
@@ -35,7 +35,7 @@ struct ServerSelectionViewState: BindableState {
}() }()
/// View state that can be bound to from SwiftUI. /// View state that can be bound to from SwiftUI.
var bindings: ServerSelectionBindings var bindings: ServerSelectionScreenBindings
/// An error message to be shown in the text field footer. /// An error message to be shown in the text field footer.
var footerErrorMessage: String? var footerErrorMessage: String?
/// Whether the screen is presented modally or within a navigation stack. /// Whether the screen is presented modally or within a navigation stack.
@@ -62,14 +62,14 @@ struct ServerSelectionViewState: BindableState {
} }
} }
struct ServerSelectionBindings { struct ServerSelectionScreenBindings {
/// The homeserver address input by the user. /// The homeserver address input by the user.
var homeserverAddress: String var homeserverAddress: String
/// Information describing the currently displayed alert. /// Information describing the currently displayed alert.
var alertInfo: AlertInfo<ServerSelectionErrorType>? var alertInfo: AlertInfo<ServerSelectionScreenErrorType>?
} }
enum ServerSelectionViewAction { enum ServerSelectionScreenViewAction {
/// The user would like to use the homeserver at the input address. /// The user would like to use the homeserver at the input address.
case confirm case confirm
/// Dismiss the view without using the entered address. /// Dismiss the view without using the entered address.
@@ -78,7 +78,7 @@ enum ServerSelectionViewAction {
case clearFooterError case clearFooterError
} }
enum ServerSelectionErrorType: Hashable { enum ServerSelectionScreenErrorType: Hashable {
/// An error message to be shown in the text field footer. /// An error message to be shown in the text field footer.
case footerMessage(String) case footerMessage(String)
/// An alert that allows the user to learn about sliding sync. /// An alert that allows the user to learn about sliding sync.
@@ -16,19 +16,19 @@
import SwiftUI import SwiftUI
typealias ServerSelectionViewModelType = StateStoreViewModel<ServerSelectionViewState, ServerSelectionViewAction> typealias ServerSelectionScreenViewModelType = StateStoreViewModel<ServerSelectionScreenViewState, ServerSelectionScreenViewAction>
class ServerSelectionViewModel: ServerSelectionViewModelType, ServerSelectionViewModelProtocol { class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, ServerSelectionScreenViewModelProtocol {
var callback: (@MainActor (ServerSelectionViewModelAction) -> Void)? var callback: (@MainActor (ServerSelectionScreenViewModelAction) -> Void)?
init(homeserverAddress: String, isModallyPresented: Bool) { init(homeserverAddress: String, isModallyPresented: Bool) {
let bindings = ServerSelectionBindings(homeserverAddress: homeserverAddress) let bindings = ServerSelectionScreenBindings(homeserverAddress: homeserverAddress)
super.init(initialViewState: ServerSelectionViewState(bindings: bindings, super.init(initialViewState: ServerSelectionScreenViewState(bindings: bindings,
isModallyPresented: isModallyPresented)) isModallyPresented: isModallyPresented))
} }
override func process(viewAction: ServerSelectionViewAction) { override func process(viewAction: ServerSelectionScreenViewAction) {
switch viewAction { switch viewAction {
case .confirm: case .confirm:
callback?(.confirm(homeserverAddress: state.bindings.homeserverAddress)) callback?(.confirm(homeserverAddress: state.bindings.homeserverAddress))
@@ -39,7 +39,7 @@ class ServerSelectionViewModel: ServerSelectionViewModelType, ServerSelectionVie
} }
} }
func displayError(_ type: ServerSelectionErrorType) { func displayError(_ type: ServerSelectionScreenErrorType) {
switch type { switch type {
case .footerMessage(let message): case .footerMessage(let message):
withElementAnimation { withElementAnimation {
@@ -17,10 +17,10 @@
import Foundation import Foundation
@MainActor @MainActor
protocol ServerSelectionViewModelProtocol { protocol ServerSelectionScreenViewModelProtocol {
var callback: (@MainActor (ServerSelectionViewModelAction) -> Void)? { get set } var callback: (@MainActor (ServerSelectionScreenViewModelAction) -> Void)? { get set }
var context: ServerSelectionViewModelType.Context { get } var context: ServerSelectionScreenViewModelType.Context { get }
/// Displays an error to the user. /// Displays an error to the user.
func displayError(_ type: ServerSelectionErrorType) func displayError(_ type: ServerSelectionScreenErrorType)
} }
@@ -17,7 +17,7 @@
import SwiftUI import SwiftUI
struct ServerSelectionScreen: View { struct ServerSelectionScreen: View {
@ObservedObject var context: ServerSelectionViewModel.Context @ObservedObject var context: ServerSelectionScreenViewModel.Context
var body: some View { var body: some View {
ScrollView { ScrollView {
@@ -1,68 +0,0 @@
//
// Copyright 2022 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 Foundation
import SwiftUI
/// Using an enum for the screen allows you define the different state cases with
/// the relevant associated data for each case.
enum MockSoftLogoutScreenState: String, CaseIterable {
// A case for each state you want to represent
// with specific, minimal associated data that will allow you
// mock that screen.
case emptyPassword
case enteredPassword
case oidc
case unsupported
case keyBackupNeeded
/// Generate the view struct for the screen state.
@MainActor var viewModel: SoftLogoutViewModel {
let credentials = SoftLogoutCredentials(userId: "@mock:matrix.org",
homeserverName: "matrix.org",
userDisplayName: "mock",
deviceId: nil)
switch self {
case .emptyPassword:
return SoftLogoutViewModel(credentials: credentials,
homeserver: .mockMatrixDotOrg,
keyBackupNeeded: false)
case .enteredPassword:
return SoftLogoutViewModel(credentials: credentials,
homeserver: .mockMatrixDotOrg,
keyBackupNeeded: false,
password: "12345678")
case .oidc:
return SoftLogoutViewModel(credentials: credentials,
homeserver: .mockOIDC,
keyBackupNeeded: false)
case .unsupported:
return SoftLogoutViewModel(credentials: credentials,
homeserver: .mockUnsupported,
keyBackupNeeded: false)
case .keyBackupNeeded:
return SoftLogoutViewModel(credentials: credentials,
homeserver: .mockMatrixDotOrg,
keyBackupNeeded: true)
}
}
}
extension MockSoftLogoutScreenState: Identifiable {
var id: String {
rawValue
}
}
@@ -0,0 +1,68 @@
//
// Copyright 2022 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 Foundation
import SwiftUI
/// Using an enum for the screen allows you define the different state cases with
/// the relevant associated data for each case.
enum MockSoftLogoutScreenState: String, CaseIterable {
// A case for each state you want to represent
// with specific, minimal associated data that will allow you
// mock that screen.
case emptyPassword
case enteredPassword
case oidc
case unsupported
case keyBackupNeeded
/// Generate the view struct for the screen state.
@MainActor var viewModel: SoftLogoutScreenViewModel {
let credentials = SoftLogoutScreenCredentials(userId: "@mock:matrix.org",
homeserverName: "matrix.org",
userDisplayName: "mock",
deviceId: nil)
switch self {
case .emptyPassword:
return SoftLogoutScreenViewModel(credentials: credentials,
homeserver: .mockMatrixDotOrg,
keyBackupNeeded: false)
case .enteredPassword:
return SoftLogoutScreenViewModel(credentials: credentials,
homeserver: .mockMatrixDotOrg,
keyBackupNeeded: false,
password: "12345678")
case .oidc:
return SoftLogoutScreenViewModel(credentials: credentials,
homeserver: .mockOIDC,
keyBackupNeeded: false)
case .unsupported:
return SoftLogoutScreenViewModel(credentials: credentials,
homeserver: .mockUnsupported,
keyBackupNeeded: false)
case .keyBackupNeeded:
return SoftLogoutScreenViewModel(credentials: credentials,
homeserver: .mockMatrixDotOrg,
keyBackupNeeded: true)
}
}
}
extension MockSoftLogoutScreenState: Identifiable {
var id: String {
rawValue
}
}
@@ -17,13 +17,13 @@
import AppAuth import AppAuth
import SwiftUI import SwiftUI
struct SoftLogoutCoordinatorParameters { struct SoftLogoutScreenCoordinatorParameters {
let authenticationService: AuthenticationServiceProxyProtocol let authenticationService: AuthenticationServiceProxyProtocol
let credentials: SoftLogoutCredentials let credentials: SoftLogoutScreenCredentials
let keyBackupNeeded: Bool let keyBackupNeeded: Bool
} }
enum SoftLogoutCoordinatorResult: CustomStringConvertible { enum SoftLogoutScreenCoordinatorResult: CustomStringConvertible {
/// Login was successful. /// Login was successful.
case signedIn(UserSessionProtocol) case signedIn(UserSessionProtocol)
/// Clear all user data /// Clear all user data
@@ -40,9 +40,9 @@ enum SoftLogoutCoordinatorResult: CustomStringConvertible {
} }
} }
final class SoftLogoutCoordinator: CoordinatorProtocol { final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
private let parameters: SoftLogoutCoordinatorParameters private let parameters: SoftLogoutScreenCoordinatorParameters
private var viewModel: SoftLogoutViewModelProtocol private var viewModel: SoftLogoutScreenViewModelProtocol
private let hostingController: UIViewController private let hostingController: UIViewController
/// Passed to the OIDC service to provide a view controller from which to present the authentication session. /// Passed to the OIDC service to provide a view controller from which to present the authentication session.
private let oidcUserAgent: OIDExternalUserAgentIOS? private let oidcUserAgent: OIDExternalUserAgentIOS?
@@ -50,14 +50,14 @@ final class SoftLogoutCoordinator: CoordinatorProtocol {
/// The wizard used to handle the registration flow. /// The wizard used to handle the registration flow.
private var authenticationService: AuthenticationServiceProxyProtocol { parameters.authenticationService } private var authenticationService: AuthenticationServiceProxyProtocol { parameters.authenticationService }
var callback: (@MainActor (SoftLogoutCoordinatorResult) -> Void)? var callback: (@MainActor (SoftLogoutScreenCoordinatorResult) -> Void)?
@MainActor init(parameters: SoftLogoutCoordinatorParameters) { @MainActor init(parameters: SoftLogoutScreenCoordinatorParameters) {
self.parameters = parameters self.parameters = parameters
let homeserver = parameters.authenticationService.homeserver let homeserver = parameters.authenticationService.homeserver
viewModel = SoftLogoutViewModel(credentials: parameters.credentials, viewModel = SoftLogoutScreenViewModel(credentials: parameters.credentials,
homeserver: homeserver, homeserver: homeserver,
keyBackupNeeded: parameters.keyBackupNeeded) keyBackupNeeded: parameters.keyBackupNeeded)
@@ -16,14 +16,14 @@
import SwiftUI import SwiftUI
struct SoftLogoutCredentials { struct SoftLogoutScreenCredentials {
let userId: String let userId: String
let homeserverName: String let homeserverName: String
let userDisplayName: String let userDisplayName: String
let deviceId: String? let deviceId: String?
} }
enum SoftLogoutViewModelAction: CustomStringConvertible { enum SoftLogoutScreenViewModelAction: CustomStringConvertible {
/// Login with password /// Login with password
case login(String) case login(String)
/// Forgot password /// Forgot password
@@ -48,9 +48,9 @@ enum SoftLogoutViewModelAction: CustomStringConvertible {
} }
} }
struct SoftLogoutViewState: BindableState { struct SoftLogoutScreenViewState: BindableState {
/// Soft logout credentials /// Soft logout credentials
var credentials: SoftLogoutCredentials var credentials: SoftLogoutScreenCredentials
/// Data about the selected homeserver. /// Data about the selected homeserver.
var homeserver: LoginHomeserver var homeserver: LoginHomeserver
@@ -59,7 +59,7 @@ struct SoftLogoutViewState: BindableState {
var keyBackupNeeded: Bool var keyBackupNeeded: Bool
/// View state that can be bound to from SwiftUI. /// View state that can be bound to from SwiftUI.
var bindings: SoftLogoutBindings var bindings: SoftLogoutScreenBindings
/// The types of login supported by the homeserver. /// The types of login supported by the homeserver.
var loginMode: LoginMode { homeserver.loginMode } var loginMode: LoginMode { homeserver.loginMode }
@@ -75,14 +75,14 @@ struct SoftLogoutViewState: BindableState {
} }
} }
struct SoftLogoutBindings { struct SoftLogoutScreenBindings {
/// The password input by the user. /// The password input by the user.
var password: String var password: String
/// Information describing the currently displayed alert. /// Information describing the currently displayed alert.
var alertInfo: AlertInfo<SoftLogoutErrorType>? var alertInfo: AlertInfo<SoftLogoutScreenErrorType>?
} }
enum SoftLogoutViewAction { enum SoftLogoutScreenViewAction {
/// Login. /// Login.
case login case login
/// Forgot password /// Forgot password
@@ -93,7 +93,7 @@ enum SoftLogoutViewAction {
case continueWithOIDC case continueWithOIDC
} }
enum SoftLogoutErrorType: Hashable { enum SoftLogoutScreenErrorType: Hashable {
/// A specific error message shown in an alert. /// A specific error message shown in an alert.
case alert(String) case alert(String)
/// An unknown error occurred. /// An unknown error occurred.
@@ -16,24 +16,24 @@
import SwiftUI import SwiftUI
typealias SoftLogoutViewModelType = StateStoreViewModel<SoftLogoutViewState, SoftLogoutViewAction> typealias SoftLogoutScreenViewModelType = StateStoreViewModel<SoftLogoutScreenViewState, SoftLogoutScreenViewAction>
class SoftLogoutViewModel: SoftLogoutViewModelType, SoftLogoutViewModelProtocol { class SoftLogoutScreenViewModel: SoftLogoutScreenViewModelType, SoftLogoutScreenViewModelProtocol {
var callback: (@MainActor (SoftLogoutViewModelAction) -> Void)? var callback: (@MainActor (SoftLogoutScreenViewModelAction) -> Void)?
init(credentials: SoftLogoutCredentials, init(credentials: SoftLogoutScreenCredentials,
homeserver: LoginHomeserver, homeserver: LoginHomeserver,
keyBackupNeeded: Bool, keyBackupNeeded: Bool,
password: String = "") { password: String = "") {
let bindings = SoftLogoutBindings(password: password) let bindings = SoftLogoutScreenBindings(password: password)
let viewState = SoftLogoutViewState(credentials: credentials, let viewState = SoftLogoutScreenViewState(credentials: credentials,
homeserver: homeserver, homeserver: homeserver,
keyBackupNeeded: keyBackupNeeded, keyBackupNeeded: keyBackupNeeded,
bindings: bindings) bindings: bindings)
super.init(initialViewState: viewState) super.init(initialViewState: viewState)
} }
override func process(viewAction: SoftLogoutViewAction) { override func process(viewAction: SoftLogoutScreenViewAction) {
switch viewAction { switch viewAction {
case .login: case .login:
callback?(.login(state.bindings.password)) callback?(.login(state.bindings.password))
@@ -46,7 +46,7 @@ class SoftLogoutViewModel: SoftLogoutViewModelType, SoftLogoutViewModelProtocol
} }
} }
@MainActor func displayError(_ type: SoftLogoutErrorType) { @MainActor func displayError(_ type: SoftLogoutScreenErrorType) {
switch type { switch type {
case .alert(let message): case .alert(let message):
state.bindings.alertInfo = AlertInfo(id: type, state.bindings.alertInfo = AlertInfo(id: type,
@@ -16,10 +16,10 @@
import Foundation import Foundation
protocol SoftLogoutViewModelProtocol { protocol SoftLogoutScreenViewModelProtocol {
var callback: (@MainActor (SoftLogoutViewModelAction) -> Void)? { get set } var callback: (@MainActor (SoftLogoutScreenViewModelAction) -> Void)? { get set }
var context: SoftLogoutViewModelType.Context { get } var context: SoftLogoutScreenViewModelType.Context { get }
/// Display an error to the user. /// Display an error to the user.
@MainActor func displayError(_ type: SoftLogoutErrorType) @MainActor func displayError(_ type: SoftLogoutScreenErrorType)
} }
@@ -22,7 +22,7 @@ struct SoftLogoutScreen: View {
/// The focus state of the password text field. /// The focus state of the password text field.
@FocusState private var isPasswordFocused: Bool @FocusState private var isPasswordFocused: Bool
@ObservedObject var context: SoftLogoutViewModel.Context @ObservedObject var context: SoftLogoutScreenViewModel.Context
var body: some View { var body: some View {
ScrollView { ScrollView {
@@ -171,14 +171,14 @@ struct SoftLogoutScreen: View {
// MARK: - Previews // MARK: - Previews
struct SoftLogout_Previews: PreviewProvider { struct SoftLogoutScreen_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
ForEach(MockSoftLogoutScreenState.allCases) { state in ForEach(MockSoftLogoutScreenState.allCases) { state in
screen(for: state.viewModel) screen(for: state.viewModel)
} }
} }
static func screen(for viewModel: SoftLogoutViewModel) -> some View { static func screen(for viewModel: SoftLogoutScreenViewModel) -> some View {
NavigationStack { NavigationStack {
SoftLogoutScreen(context: viewModel.context) SoftLogoutScreen(context: viewModel.context)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
@@ -17,32 +17,32 @@
import Combine import Combine
import SwiftUI import SwiftUI
struct StartChatCoordinatorParameters { struct StartChatScreenCoordinatorParameters {
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
weak var userIndicatorController: UserIndicatorControllerProtocol? weak var userIndicatorController: UserIndicatorControllerProtocol?
let navigationStackCoordinator: NavigationStackCoordinator? let navigationStackCoordinator: NavigationStackCoordinator?
let userDiscoveryService: UserDiscoveryServiceProtocol let userDiscoveryService: UserDiscoveryServiceProtocol
} }
enum StartChatCoordinatorAction { enum StartChatScreenCoordinatorAction {
case close case close
case openRoom(withIdentifier: String) case openRoom(withIdentifier: String)
} }
final class StartChatCoordinator: CoordinatorProtocol { final class StartChatScreenCoordinator: CoordinatorProtocol {
private let parameters: StartChatCoordinatorParameters private let parameters: StartChatScreenCoordinatorParameters
private var viewModel: StartChatViewModelProtocol private var viewModel: StartChatScreenViewModelProtocol
private let actionsSubject: PassthroughSubject<StartChatCoordinatorAction, Never> = .init() private let actionsSubject: PassthroughSubject<StartChatScreenCoordinatorAction, Never> = .init()
private var cancellables: Set<AnyCancellable> = .init() private var cancellables: Set<AnyCancellable> = .init()
var actions: AnyPublisher<StartChatCoordinatorAction, Never> { var actions: AnyPublisher<StartChatScreenCoordinatorAction, Never> {
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(parameters: StartChatCoordinatorParameters) { init(parameters: StartChatScreenCoordinatorParameters) {
self.parameters = parameters self.parameters = parameters
viewModel = StartChatViewModel(userSession: parameters.userSession, userIndicatorController: parameters.userIndicatorController, userDiscoveryService: parameters.userDiscoveryService) viewModel = StartChatScreenViewModel(userSession: parameters.userSession, userIndicatorController: parameters.userIndicatorController, userDiscoveryService: parameters.userDiscoveryService)
} }
func start() { func start() {
@@ -16,18 +16,18 @@
import Foundation import Foundation
enum StartChatErrorType: Error { enum StartChatScreenErrorType: Error {
case failedCreatingRoom case failedCreatingRoom
case unknown case unknown
} }
enum StartChatViewModelAction { enum StartChatScreenViewModelAction {
case close case close
case createRoom case createRoom
case openRoom(withIdentifier: String) case openRoom(withIdentifier: String)
} }
struct StartChatViewState: BindableState { struct StartChatScreenViewState: BindableState {
var bindings = StartChatScreenViewStateBindings() var bindings = StartChatScreenViewStateBindings()
var usersSection: UserDiscoverySection = .init(type: .suggestions, users: []) var usersSection: UserDiscoverySection = .init(type: .suggestions, users: [])
@@ -44,10 +44,10 @@ struct StartChatScreenViewStateBindings {
var searchQuery = "" var searchQuery = ""
/// Information describing the currently displayed alert. /// Information describing the currently displayed alert.
var alertInfo: AlertInfo<StartChatErrorType>? var alertInfo: AlertInfo<StartChatScreenErrorType>?
} }
enum StartChatViewAction { enum StartChatScreenViewAction {
case close case close
case createRoom case createRoom
case inviteFriends case inviteFriends
@@ -17,14 +17,14 @@
import Combine import Combine
import SwiftUI import SwiftUI
typealias StartChatViewModelType = StateStoreViewModel<StartChatViewState, StartChatViewAction> typealias StartChatScreenViewModelType = StateStoreViewModel<StartChatScreenViewState, StartChatScreenViewAction>
class StartChatViewModel: StartChatViewModelType, StartChatViewModelProtocol { class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenViewModelProtocol {
private let userSession: UserSessionProtocol private let userSession: UserSessionProtocol
private let actionsSubject: PassthroughSubject<StartChatViewModelAction, Never> = .init() private let actionsSubject: PassthroughSubject<StartChatScreenViewModelAction, Never> = .init()
private let userDiscoveryService: UserDiscoveryServiceProtocol private let userDiscoveryService: UserDiscoveryServiceProtocol
var actions: AnyPublisher<StartChatViewModelAction, Never> { var actions: AnyPublisher<StartChatScreenViewModelAction, Never> {
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
@@ -34,14 +34,14 @@ class StartChatViewModel: StartChatViewModelType, StartChatViewModelProtocol {
self.userSession = userSession self.userSession = userSession
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.userDiscoveryService = userDiscoveryService self.userDiscoveryService = userDiscoveryService
super.init(initialViewState: StartChatViewState(), imageProvider: userSession.mediaProvider) super.init(initialViewState: StartChatScreenViewState(), imageProvider: userSession.mediaProvider)
setupBindings() setupBindings()
} }
// MARK: - Public // MARK: - Public
override func process(viewAction: StartChatViewAction) { override func process(viewAction: StartChatScreenViewAction) {
switch viewAction { switch viewAction {
case .close: case .close:
actionsSubject.send(.close) actionsSubject.send(.close)
@@ -17,7 +17,7 @@
import Combine import Combine
@MainActor @MainActor
protocol StartChatViewModelProtocol { protocol StartChatScreenViewModelProtocol {
var actions: AnyPublisher<StartChatViewModelAction, Never> { get } var actions: AnyPublisher<StartChatScreenViewModelAction, Never> { get }
var context: StartChatViewModelType.Context { get } var context: StartChatScreenViewModelType.Context { get }
} }
@@ -17,7 +17,7 @@
import SwiftUI import SwiftUI
struct StartChatScreen: View { struct StartChatScreen: View {
@ObservedObject var context: StartChatViewModel.Context @ObservedObject var context: StartChatScreenViewModel.Context
var body: some View { var body: some View {
Form { Form {
@@ -130,14 +130,14 @@ struct StartChatScreen: View {
// MARK: - Previews // MARK: - Previews
struct StartChat_Previews: PreviewProvider { struct StartChatScreen_Previews: PreviewProvider {
static let viewModel = { static let viewModel = {
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"), let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"),
mediaProvider: MockMediaProvider()) mediaProvider: MockMediaProvider())
let userDiscoveryService = UserDiscoveryServiceMock() let userDiscoveryService = UserDiscoveryServiceMock()
userDiscoveryService.fetchSuggestionsReturnValue = .success([.mockAlice]) userDiscoveryService.fetchSuggestionsReturnValue = .success([.mockAlice])
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice]) userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice])
let viewModel = StartChatViewModel(userSession: userSession, userIndicatorController: nil, userDiscoveryService: userDiscoveryService) let viewModel = StartChatScreenViewModel(userSession: userSession, userIndicatorController: nil, userDiscoveryService: userDiscoveryService)
return viewModel return viewModel
}() }()
@@ -289,8 +289,8 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
let userIndicatorController = UserIndicatorController(rootCoordinator: startChatNavigationStackCoordinator) let userIndicatorController = UserIndicatorController(rootCoordinator: startChatNavigationStackCoordinator)
let userDiscoveryService = UserDiscoveryService(clientProxy: userSession.clientProxy) let userDiscoveryService = UserDiscoveryService(clientProxy: userSession.clientProxy)
let parameters = StartChatCoordinatorParameters(userSession: userSession, userIndicatorController: userIndicatorController, navigationStackCoordinator: startChatNavigationStackCoordinator, userDiscoveryService: userDiscoveryService) let parameters = StartChatScreenCoordinatorParameters(userSession: userSession, userIndicatorController: userIndicatorController, navigationStackCoordinator: startChatNavigationStackCoordinator, userDiscoveryService: userDiscoveryService)
let coordinator = StartChatCoordinator(parameters: parameters) let coordinator = StartChatScreenCoordinator(parameters: parameters)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
guard let self else { return } guard let self else { return }
switch action { switch action {
@@ -62,19 +62,19 @@ class MockScreen: Identifiable {
switch id { switch id {
case .login: case .login:
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = LoginCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(), let coordinator = LoginScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(),
navigationStackCoordinator: navigationStackCoordinator)) navigationStackCoordinator: navigationStackCoordinator))
navigationStackCoordinator.setRootCoordinator(coordinator) navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator return navigationStackCoordinator
case .serverSelection: case .serverSelection:
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = ServerSelectionCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(), let coordinator = ServerSelectionScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(),
userIndicatorController: MockUserIndicatorController(), userIndicatorController: MockUserIndicatorController(),
isModallyPresented: true)) isModallyPresented: true))
navigationStackCoordinator.setRootCoordinator(coordinator) navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator return navigationStackCoordinator
case .serverSelectionNonModal: case .serverSelectionNonModal:
return ServerSelectionCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(), return ServerSelectionScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(),
userIndicatorController: MockUserIndicatorController(), userIndicatorController: MockUserIndicatorController(),
isModallyPresented: false)) isModallyPresented: false))
case .analyticsPrompt: case .analyticsPrompt:
@@ -92,11 +92,11 @@ class MockScreen: Identifiable {
navigationStackCoordinator.setRootCoordinator(coordinator) navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator return navigationStackCoordinator
case .softLogout: case .softLogout:
let credentials = SoftLogoutCredentials(userId: "@mock:matrix.org", let credentials = SoftLogoutScreenCredentials(userId: "@mock:matrix.org",
homeserverName: "matrix.org", homeserverName: "matrix.org",
userDisplayName: "mock", userDisplayName: "mock",
deviceId: "ABCDEFGH") deviceId: "ABCDEFGH")
return SoftLogoutCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(), return SoftLogoutScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(),
credentials: credentials, credentials: credentials,
keyBackupNeeded: false)) keyBackupNeeded: false))
case .simpleRegular: case .simpleRegular:
@@ -321,8 +321,8 @@ class MockScreen: Identifiable {
userDiscoveryMock.fetchSuggestionsReturnValue = .success([.mockAlice, .mockBob, .mockCharlie]) userDiscoveryMock.fetchSuggestionsReturnValue = .success([.mockAlice, .mockBob, .mockCharlie])
userDiscoveryMock.searchProfilesWithReturnValue = .success([]) userDiscoveryMock.searchProfilesWithReturnValue = .success([])
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@mock:client.com"), mediaProvider: MockMediaProvider()) let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@mock:client.com"), mediaProvider: MockMediaProvider())
let parameters: StartChatCoordinatorParameters = .init(userSession: userSession, navigationStackCoordinator: navigationStackCoordinator, userDiscoveryService: userDiscoveryMock) let parameters: StartChatScreenCoordinatorParameters = .init(userSession: userSession, navigationStackCoordinator: navigationStackCoordinator, userDiscoveryService: userDiscoveryMock)
let coordinator = StartChatCoordinator(parameters: parameters) let coordinator = StartChatScreenCoordinator(parameters: parameters)
navigationStackCoordinator.setRootCoordinator(coordinator) navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator return navigationStackCoordinator
case .startChatWithSearchResults: case .startChatWithSearchResults:
@@ -332,7 +332,7 @@ class MockScreen: Identifiable {
userDiscoveryMock.fetchSuggestionsReturnValue = .success([]) userDiscoveryMock.fetchSuggestionsReturnValue = .success([])
userDiscoveryMock.searchProfilesWithReturnValue = .success([.mockBob, .mockBobby]) userDiscoveryMock.searchProfilesWithReturnValue = .success([.mockBob, .mockBobby])
let userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider()) let userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())
let coordinator = StartChatCoordinator(parameters: .init(userSession: userSession, navigationStackCoordinator: navigationStackCoordinator, userDiscoveryService: userDiscoveryMock)) let coordinator = StartChatScreenCoordinator(parameters: .init(userSession: userSession, navigationStackCoordinator: navigationStackCoordinator, userDiscoveryService: userDiscoveryMock))
navigationStackCoordinator.setRootCoordinator(coordinator) navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator return navigationStackCoordinator
case .roomMemberDetailsAccountOwner: case .roomMemberDetailsAccountOwner:
+4 -4
View File
@@ -21,11 +21,11 @@ import XCTest
@MainActor @MainActor
class LoginViewModelTests: XCTestCase { class LoginViewModelTests: XCTestCase {
let defaultHomeserver = LoginHomeserver.mockMatrixDotOrg let defaultHomeserver = LoginHomeserver.mockMatrixDotOrg
var viewModel: LoginViewModelProtocol! var viewModel: LoginScreenViewModelProtocol!
var context: LoginViewModelType.Context! var context: LoginScreenViewModelType.Context!
@MainActor override func setUp() async throws { @MainActor override func setUp() async throws {
viewModel = LoginViewModel(homeserver: defaultHomeserver) viewModel = LoginScreenViewModel(homeserver: defaultHomeserver)
context = viewModel.context context = viewModel.context
} }
@@ -135,7 +135,7 @@ class LoginViewModelTests: XCTestCase {
func testLogsForPassword() { func testLogsForPassword() {
// Given the coordinator and view model results that contain passwords. // Given the coordinator and view model results that contain passwords.
let password = "supersecretpassword" let password = "supersecretpassword"
let viewModelAction: LoginViewModelAction = .login(username: "Alice", password: password) let viewModelAction: LoginScreenViewModelAction = .login(username: "Alice", password: password)
// When creating a string representation of those results (e.g. for logging). // When creating a string representation of those results (e.g. for logging).
let viewModelActionString = "\(viewModelAction)" let viewModelActionString = "\(viewModelAction)"
@@ -24,11 +24,11 @@ class ServerSelectionViewModelTests: XCTestCase {
static let counterInitialValue = 0 static let counterInitialValue = 0
} }
var viewModel: ServerSelectionViewModelProtocol! var viewModel: ServerSelectionScreenViewModelProtocol!
var context: ServerSelectionViewModelType.Context! var context: ServerSelectionScreenViewModelType.Context!
@MainActor override func setUp() { @MainActor override func setUp() {
viewModel = ServerSelectionViewModel(homeserverAddress: "", isModallyPresented: true) viewModel = ServerSelectionScreenViewModel(homeserverAddress: "", isModallyPresented: true)
context = viewModel.context context = viewModel.context
} }
@@ -19,13 +19,13 @@ import XCTest
@testable import ElementX @testable import ElementX
class SoftLogoutViewModelTests: XCTestCase { class SoftLogoutViewModelTests: XCTestCase {
let credentials = SoftLogoutCredentials(userId: "mock_user_id", let credentials = SoftLogoutScreenCredentials(userId: "mock_user_id",
homeserverName: "https://matrix.org", homeserverName: "https://matrix.org",
userDisplayName: "mock_username", userDisplayName: "mock_username",
deviceId: "ABCDEFGH") deviceId: "ABCDEFGH")
@MainActor func testInitialStateForMatrixOrg() { @MainActor func testInitialStateForMatrixOrg() {
let viewModel = SoftLogoutViewModel(credentials: credentials, let viewModel = SoftLogoutScreenViewModel(credentials: credentials,
homeserver: .mockMatrixDotOrg, homeserver: .mockMatrixDotOrg,
keyBackupNeeded: true) keyBackupNeeded: true)
let context = viewModel.context let context = viewModel.context
@@ -38,7 +38,7 @@ class SoftLogoutViewModelTests: XCTestCase {
} }
@MainActor func testInitialStateForMatrixOrgPasswordEntered() { @MainActor func testInitialStateForMatrixOrgPasswordEntered() {
let viewModel = SoftLogoutViewModel(credentials: credentials, let viewModel = SoftLogoutScreenViewModel(credentials: credentials,
homeserver: .mockMatrixDotOrg, homeserver: .mockMatrixDotOrg,
keyBackupNeeded: true, keyBackupNeeded: true,
password: "12345678") password: "12345678")
@@ -51,7 +51,7 @@ class SoftLogoutViewModelTests: XCTestCase {
} }
@MainActor func testInitialStateForBasicServer() { @MainActor func testInitialStateForBasicServer() {
let viewModel = SoftLogoutViewModel(credentials: credentials, let viewModel = SoftLogoutScreenViewModel(credentials: credentials,
homeserver: .mockBasicServer, homeserver: .mockBasicServer,
keyBackupNeeded: false) keyBackupNeeded: false)
let context = viewModel.context let context = viewModel.context
@@ -64,7 +64,7 @@ class SoftLogoutViewModelTests: XCTestCase {
} }
@MainActor func testInitialStateForOIDC() { @MainActor func testInitialStateForOIDC() {
let viewModel = SoftLogoutViewModel(credentials: credentials, let viewModel = SoftLogoutScreenViewModel(credentials: credentials,
homeserver: .mockOIDC, homeserver: .mockOIDC,
keyBackupNeeded: false) keyBackupNeeded: false)
let context = viewModel.context let context = viewModel.context
@@ -77,7 +77,7 @@ class SoftLogoutViewModelTests: XCTestCase {
} }
@MainActor func testInitialStateForUnsupported() { @MainActor func testInitialStateForUnsupported() {
let viewModel = SoftLogoutViewModel(credentials: credentials, let viewModel = SoftLogoutScreenViewModel(credentials: credentials,
homeserver: .mockUnsupported, homeserver: .mockUnsupported,
keyBackupNeeded: false) keyBackupNeeded: false)
let context = viewModel.context let context = viewModel.context
@@ -20,11 +20,11 @@ import XCTest
@MainActor @MainActor
class StartChatScreenViewModelTests: XCTestCase { class StartChatScreenViewModelTests: XCTestCase {
var viewModel: StartChatViewModelProtocol! var viewModel: StartChatScreenViewModelProtocol!
var clientProxy: MockClientProxy! var clientProxy: MockClientProxy!
var userDiscoveryService: UserDiscoveryServiceMock! var userDiscoveryService: UserDiscoveryServiceMock!
var context: StartChatViewModel.Context { var context: StartChatScreenViewModel.Context {
viewModel.context viewModel.context
} }
@@ -34,7 +34,7 @@ class StartChatScreenViewModelTests: XCTestCase {
userDiscoveryService.fetchSuggestionsReturnValue = .success([]) userDiscoveryService.fetchSuggestionsReturnValue = .success([])
userDiscoveryService.searchProfilesWithReturnValue = .success([]) userDiscoveryService.searchProfilesWithReturnValue = .success([])
let userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider()) let userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())
viewModel = StartChatViewModel(userSession: userSession, userIndicatorController: nil, userDiscoveryService: userDiscoveryService) viewModel = StartChatScreenViewModel(userSession: userSession, userIndicatorController: nil, userDiscoveryService: userDiscoveryService)
setupAppSettings() setupAppSettings()
ServiceLocator.shared.settings.startChatUserSuggestionsEnabled = true ServiceLocator.shared.settings.startChatUserSuggestionsEnabled = true
@@ -65,7 +65,7 @@ class StartChatScreenViewModelTests: XCTestCase {
} }
@discardableResult @discardableResult
private func search(query: String) async -> StartChatViewState? { private func search(query: String) async -> StartChatScreenViewState? {
viewModel.context.searchQuery = query viewModel.context.searchQuery = query
return await context.$viewState.nextValue return await context.$viewState.nextValue
} }