mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
Renaming screens returns (#829)
* Rename: StartChat -> StartChatScreen * Rename: Login -> LoginScreen * Rename: ServerSelection -> ServerSelectionScreen * Rename: SoftLogout -> SoftLogoutScreen
This commit is contained in:
@@ -220,18 +220,18 @@ 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)
|
||||||
|
|
||||||
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,10 +92,10 @@ 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 }
|
||||||
|
|
||||||
|
|||||||
+14
-14
@@ -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()
|
||||||
}
|
}
|
||||||
+7
-7
@@ -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.
|
||||||
+7
-7
@@ -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,
|
||||||
+4
-4
@@ -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)
|
||||||
|
|||||||
+9
-9
@@ -23,22 +23,22 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+9
-9
@@ -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,23 +24,23 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
+7
-7
@@ -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.
|
||||||
+9
-9
@@ -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 {
|
||||||
+4
-4
@@ -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)
|
||||||
}
|
}
|
||||||
+1
-1
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+68
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
-13
@@ -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,26 +40,26 @@ 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?
|
||||||
|
|
||||||
/// 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)?
|
|
||||||
|
|
||||||
@MainActor init(parameters: SoftLogoutCoordinatorParameters) {
|
var callback: (@MainActor (SoftLogoutScreenCoordinatorResult) -> Void)?
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|
||||||
hostingController = UIHostingController(rootView: SoftLogoutScreen(context: viewModel.context))
|
hostingController = UIHostingController(rootView: SoftLogoutScreen(context: viewModel.context))
|
||||||
oidcUserAgent = OIDExternalUserAgentIOS(presenting: hostingController)
|
oidcUserAgent = OIDExternalUserAgentIOS(presenting: hostingController)
|
||||||
+9
-9
@@ -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.
|
||||||
+11
-11
@@ -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,
|
||||||
+4
-4
@@ -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)
|
||||||
}
|
}
|
||||||
+3
-3
@@ -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)
|
||||||
+9
-9
@@ -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() {
|
||||||
+5
-5
@@ -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
|
||||||
+6
-6
@@ -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)
|
||||||
+3
-3
@@ -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 }
|
||||||
}
|
}
|
||||||
+3
-3
@@ -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,21 +62,21 @@ 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:
|
||||||
return AnalyticsPromptScreenCoordinator()
|
return AnalyticsPromptScreenCoordinator()
|
||||||
case .analyticsSettingsScreen:
|
case .analyticsSettingsScreen:
|
||||||
@@ -92,13 +92,13 @@ 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:
|
||||||
return TemplateScreenCoordinator(parameters: .init(promptType: .regular))
|
return TemplateScreenCoordinator(parameters: .init(promptType: .regular))
|
||||||
case .simpleUpgrade:
|
case .simpleUpgrade:
|
||||||
@@ -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:
|
||||||
|
|||||||
@@ -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,17 +19,17 @@ 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
|
||||||
|
|
||||||
// Given a view model where the user hasn't yet sent the verification email.
|
// Given a view model where the user hasn't yet sent the verification email.
|
||||||
XCTAssert(context.password.isEmpty, "The view model should start with an empty password.")
|
XCTAssert(context.password.isEmpty, "The view model should start with an empty password.")
|
||||||
XCTAssertFalse(context.viewState.canSubmit, "The view model should start with an invalid password.")
|
XCTAssertFalse(context.viewState.canSubmit, "The view model should start with an invalid password.")
|
||||||
@@ -38,10 +38,10 @@ 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")
|
||||||
let context = viewModel.context
|
let context = viewModel.context
|
||||||
|
|
||||||
// Given a view model where the user hasn't yet sent the verification email.
|
// Given a view model where the user hasn't yet sent the verification email.
|
||||||
@@ -49,13 +49,13 @@ class SoftLogoutViewModelTests: XCTestCase {
|
|||||||
XCTAssertEqual(context.viewState.loginMode, .password, "The view model should show login form for the given homeserver.")
|
XCTAssertEqual(context.viewState.loginMode, .password, "The view model should show login form for the given homeserver.")
|
||||||
XCTAssert(context.viewState.showRecoverEncryptionKeysMessage, "The view model should show recover encryption keys message.")
|
XCTAssert(context.viewState.showRecoverEncryptionKeysMessage, "The view model should show recover encryption keys message.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@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
|
||||||
|
|
||||||
// Given a view model where the user hasn't yet sent the verification email.
|
// Given a view model where the user hasn't yet sent the verification email.
|
||||||
XCTAssert(context.password.isEmpty, "The view model should start with an empty password.")
|
XCTAssert(context.password.isEmpty, "The view model should start with an empty password.")
|
||||||
XCTAssertFalse(context.viewState.canSubmit, "The view model should start with an invalid password.")
|
XCTAssertFalse(context.viewState.canSubmit, "The view model should start with an invalid password.")
|
||||||
@@ -64,22 +64,22 @@ 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
|
||||||
|
|
||||||
// Given a view model where the user hasn't yet sent the verification email.
|
// Given a view model where the user hasn't yet sent the verification email.
|
||||||
XCTAssert(context.password.isEmpty, "The view model should start with an empty password.")
|
XCTAssert(context.password.isEmpty, "The view model should start with an empty password.")
|
||||||
XCTAssertFalse(context.viewState.canSubmit, "The view model should start with an invalid password.")
|
XCTAssertFalse(context.viewState.canSubmit, "The view model should start with an invalid password.")
|
||||||
XCTAssertTrue(context.viewState.loginMode.supportsOIDCFlow, "The view model should show OIDC button for the given homeserver.")
|
XCTAssertTrue(context.viewState.loginMode.supportsOIDCFlow, "The view model should show OIDC button for the given homeserver.")
|
||||||
XCTAssertFalse(context.viewState.showRecoverEncryptionKeysMessage, "The view model should not show recover encryption keys message.")
|
XCTAssertFalse(context.viewState.showRecoverEncryptionKeysMessage, "The view model should not show recover encryption keys message.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@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
|
||||||
|
|
||||||
// Given a view model where the user hasn't yet sent the verification email.
|
// Given a view model where the user hasn't yet sent the verification email.
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user