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,18 +220,18 @@ class AppCoordinator: AppCoordinatorProtocol {
displayName = name
}
let credentials = SoftLogoutCredentials(userId: userSession.userID,
homeserverName: userSession.homeserver,
userDisplayName: displayName,
deviceId: userSession.deviceID)
let credentials = SoftLogoutScreenCredentials(userId: userSession.userID,
homeserverName: userSession.homeserver,
userDisplayName: displayName,
deviceId: userSession.deviceID)
let authenticationService = AuthenticationServiceProxy(userSessionStore: userSessionStore)
_ = await authenticationService.configure(for: userSession.homeserver)
let parameters = SoftLogoutCoordinatorParameters(authenticationService: authenticationService,
credentials: credentials,
keyBackupNeeded: false)
let coordinator = SoftLogoutCoordinator(parameters: parameters)
let parameters = SoftLogoutScreenCoordinatorParameters(authenticationService: authenticationService,
credentials: credentials,
keyBackupNeeded: false)
let coordinator = SoftLogoutScreenCoordinator(parameters: parameters)
coordinator.callback = { result in
switch result {
case .signedIn(let session):
@@ -72,10 +72,10 @@ class AuthenticationCoordinator: CoordinatorProtocol {
}
private func showServerSelectionScreen() {
let parameters = ServerSelectionCoordinatorParameters(authenticationService: authenticationService,
userIndicatorController: ServiceLocator.shared.userIndicatorController,
isModallyPresented: false)
let coordinator = ServerSelectionCoordinator(parameters: parameters)
let parameters = ServerSelectionScreenCoordinatorParameters(authenticationService: authenticationService,
userIndicatorController: ServiceLocator.shared.userIndicatorController,
isModallyPresented: false)
let coordinator = ServerSelectionScreenCoordinator(parameters: parameters)
coordinator.callback = { [weak self] action in
guard let self else { return }
@@ -92,10 +92,10 @@ class AuthenticationCoordinator: CoordinatorProtocol {
}
private func showLoginScreen() {
let parameters = LoginCoordinatorParameters(authenticationService: authenticationService,
navigationStackCoordinator: navigationStackCoordinator)
let coordinator = LoginCoordinator(parameters: parameters)
let parameters = LoginScreenCoordinatorParameters(authenticationService: authenticationService,
navigationStackCoordinator: navigationStackCoordinator)
let coordinator = LoginScreenCoordinator(parameters: parameters)
coordinator.callback = { [weak self] action in
guard let self else { return }
@@ -17,21 +17,21 @@
import AppAuth
import SwiftUI
struct LoginCoordinatorParameters {
struct LoginScreenCoordinatorParameters {
/// The service used to authenticate the user.
let authenticationService: AuthenticationServiceProxyProtocol
/// The navigation router used to present the server selection screen.
let navigationStackCoordinator: NavigationStackCoordinator
}
enum LoginCoordinatorAction {
enum LoginScreenCoordinatorAction {
/// Login was successful.
case signedIn(UserSessionProtocol)
}
final class LoginCoordinator: CoordinatorProtocol {
private let parameters: LoginCoordinatorParameters
private var viewModel: LoginViewModelProtocol
final class LoginScreenCoordinator: CoordinatorProtocol {
private let parameters: LoginScreenCoordinatorParameters
private var viewModel: LoginScreenViewModelProtocol
private let hostingController: UIViewController
/// Passed to the OIDC service to provide a view controller from which to present the authentication session.
private let oidcUserAgent: OIDExternalUserAgentIOS?
@@ -45,14 +45,14 @@ final class LoginCoordinator: CoordinatorProtocol {
private var authenticationService: AuthenticationServiceProxyProtocol { parameters.authenticationService }
private var navigationStackCoordinator: NavigationStackCoordinator { parameters.navigationStackCoordinator }
var callback: (@MainActor (LoginCoordinatorAction) -> Void)?
var callback: (@MainActor (LoginScreenCoordinatorAction) -> Void)?
// MARK: - Setup
init(parameters: LoginCoordinatorParameters) {
init(parameters: LoginScreenCoordinatorParameters) {
self.parameters = parameters
viewModel = LoginViewModel(homeserver: parameters.authenticationService.homeserver)
viewModel = LoginScreenViewModel(homeserver: parameters.authenticationService.homeserver)
hostingController = UIHostingController(rootView: LoginScreen(context: viewModel.context))
oidcUserAgent = OIDExternalUserAgentIOS(presenting: hostingController)
@@ -199,11 +199,11 @@ final class LoginCoordinator: CoordinatorProtocol {
/// Presents the server selection screen as a modal.
private func presentServerSelectionScreen() {
let parameters = ServerSelectionCoordinatorParameters(authenticationService: authenticationService,
userIndicatorController: ServiceLocator.shared.userIndicatorController,
isModallyPresented: false)
let parameters = ServerSelectionScreenCoordinatorParameters(authenticationService: authenticationService,
userIndicatorController: ServiceLocator.shared.userIndicatorController,
isModallyPresented: false)
let coordinator = ServerSelectionCoordinator(parameters: parameters)
let coordinator = ServerSelectionScreenCoordinator(parameters: parameters)
coordinator.callback = { [weak self, weak coordinator] action in
guard let self, let coordinator else { return }
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.
private func serverSelectionCoordinator(_ coordinator: ServerSelectionCoordinator,
didCompleteWith action: ServerSelectionCoordinatorAction) {
private func serverSelectionCoordinator(_ coordinator: ServerSelectionScreenCoordinator,
didCompleteWith action: ServerSelectionScreenCoordinatorAction) {
if action == .updated {
updateViewModel()
}
@@ -16,7 +16,7 @@
import Foundation
enum LoginViewModelAction: CustomStringConvertible {
enum LoginScreenViewModelAction: CustomStringConvertible {
/// The user would like to select another server.
case selectServer
/// 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.
var homeserver: LoginHomeserver
/// Whether a new homeserver is currently being loaded.
var isLoading = false
/// View state that can be bound to from SwiftUI.
var bindings: LoginBindings
var bindings: LoginScreenBindings
/// The types of login supported by the homeserver.
var loginMode: LoginMode { homeserver.loginMode }
@@ -67,16 +67,16 @@ struct LoginViewState: BindableState {
}
}
struct LoginBindings {
struct LoginScreenBindings {
/// The username input by the user.
var username = ""
/// The password input by the user.
var password = ""
/// 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.
case selectServer
/// Parse the username to detect if a homeserver is included.
@@ -89,7 +89,7 @@ enum LoginViewAction {
case continueWithOIDC
}
enum LoginErrorType: Hashable {
enum LoginScreenErrorType: Hashable {
/// A specific error message shown in an alert.
case alert(String)
/// Looking up the homeserver from the username failed.
@@ -16,19 +16,19 @@
import SwiftUI
typealias LoginViewModelType = StateStoreViewModel<LoginViewState, LoginViewAction>
typealias LoginScreenViewModelType = StateStoreViewModel<LoginScreenViewState, LoginScreenViewAction>
class LoginViewModel: LoginViewModelType, LoginViewModelProtocol {
var callback: (@MainActor (LoginViewModelAction) -> Void)?
class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtocol {
var callback: (@MainActor (LoginScreenViewModelAction) -> Void)?
init(homeserver: LoginHomeserver) {
let bindings = LoginBindings()
let viewState = LoginViewState(homeserver: homeserver, bindings: bindings)
let bindings = LoginScreenBindings()
let viewState = LoginScreenViewState(homeserver: homeserver, bindings: bindings)
super.init(initialViewState: viewState)
}
override func process(viewAction: LoginViewAction) {
override func process(viewAction: LoginScreenViewAction) {
switch viewAction {
case .selectServer:
callback?(.selectServer)
@@ -52,7 +52,7 @@ class LoginViewModel: LoginViewModelType, LoginViewModelProtocol {
state.homeserver = homeserver
}
func displayError(_ type: LoginErrorType) {
func displayError(_ type: LoginScreenErrorType) {
switch type {
case .alert(let message):
state.bindings.alertInfo = AlertInfo(id: type,
@@ -17,9 +17,9 @@
import Foundation
@MainActor
protocol LoginViewModelProtocol {
var callback: (@MainActor (LoginViewModelAction) -> Void)? { get set }
var context: LoginViewModelType.Context { get }
protocol LoginScreenViewModelProtocol {
var callback: (@MainActor (LoginScreenViewModelAction) -> Void)? { get set }
var context: LoginScreenViewModelType.Context { get }
/// Update the view to reflect that a new 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.
/// - 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.
@FocusState private var isPasswordFocused: Bool
@ObservedObject var context: LoginViewModel.Context
@ObservedObject var context: LoginScreenViewModel.Context
var body: some View {
ScrollView {
@@ -146,26 +146,26 @@ struct LoginScreen: View {
// MARK: - Previews
struct Login_Previews: PreviewProvider {
static let credentialsViewModel: LoginViewModel = {
let viewModel = LoginViewModel(homeserver: .mockMatrixDotOrg)
struct LoginScreen_Previews: PreviewProvider {
static let credentialsViewModel: LoginScreenViewModel = {
let viewModel = LoginScreenViewModel(homeserver: .mockMatrixDotOrg)
viewModel.context.username = "alice"
viewModel.context.password = "password"
return viewModel
}()
static var previews: some View {
screen(for: LoginViewModel(homeserver: .mockMatrixDotOrg))
screen(for: LoginScreenViewModel(homeserver: .mockMatrixDotOrg))
.previewDisplayName("matrix.org")
screen(for: credentialsViewModel)
.previewDisplayName("Credentials Entered")
screen(for: LoginViewModel(homeserver: .mockOIDC))
screen(for: LoginScreenViewModel(homeserver: .mockOIDC))
.previewDisplayName("OIDC")
screen(for: LoginViewModel(homeserver: .mockUnsupported))
screen(for: LoginScreenViewModel(homeserver: .mockUnsupported))
.previewDisplayName("Unsupported")
}
static func screen(for viewModel: LoginViewModel) -> some View {
static func screen(for viewModel: LoginScreenViewModel) -> some View {
NavigationStack {
LoginScreen(context: viewModel.context)
.navigationBarTitleDisplayMode(.inline)
@@ -23,22 +23,22 @@ enum MockServerSelectionScreenState: CaseIterable {
case nonModal
/// Generate the view struct for the screen state.
@MainActor var viewModel: ServerSelectionViewModel {
@MainActor var viewModel: ServerSelectionScreenViewModel {
switch self {
case .matrix:
return ServerSelectionViewModel(homeserverAddress: "https://matrix.org",
isModallyPresented: true)
return ServerSelectionScreenViewModel(homeserverAddress: "https://matrix.org",
isModallyPresented: true)
case .emptyAddress:
return ServerSelectionViewModel(homeserverAddress: "",
isModallyPresented: true)
return ServerSelectionScreenViewModel(homeserverAddress: "",
isModallyPresented: true)
case .invalidAddress:
let viewModel = ServerSelectionViewModel(homeserverAddress: "thisisbad",
isModallyPresented: true)
let viewModel = ServerSelectionScreenViewModel(homeserverAddress: "thisisbad",
isModallyPresented: true)
viewModel.displayError(.footerMessage(L10n.errorUnknown))
return viewModel
case .nonModal:
return ServerSelectionViewModel(homeserverAddress: "https://matrix.org",
isModallyPresented: false)
return ServerSelectionScreenViewModel(homeserverAddress: "https://matrix.org",
isModallyPresented: false)
}
}
}
@@ -16,7 +16,7 @@
import SwiftUI
struct ServerSelectionCoordinatorParameters {
struct ServerSelectionScreenCoordinatorParameters {
/// The service used to authenticate the user.
let authenticationService: AuthenticationServiceProxyProtocol
let userIndicatorController: UserIndicatorControllerProtocol
@@ -24,23 +24,23 @@ struct ServerSelectionCoordinatorParameters {
let isModallyPresented: Bool
}
enum ServerSelectionCoordinatorAction {
enum ServerSelectionScreenCoordinatorAction {
case updated
case dismiss
}
final class ServerSelectionCoordinator: CoordinatorProtocol {
private let parameters: ServerSelectionCoordinatorParameters
final class ServerSelectionScreenCoordinator: CoordinatorProtocol {
private let parameters: ServerSelectionScreenCoordinatorParameters
private let userIndicatorController: UserIndicatorControllerProtocol
private var viewModel: ServerSelectionViewModelProtocol
private var viewModel: ServerSelectionScreenViewModelProtocol
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
viewModel = ServerSelectionViewModel(homeserverAddress: parameters.authenticationService.homeserver.address,
isModallyPresented: parameters.isModallyPresented)
viewModel = ServerSelectionScreenViewModel(homeserverAddress: parameters.authenticationService.homeserver.address,
isModallyPresented: parameters.isModallyPresented)
userIndicatorController = parameters.userIndicatorController
}
@@ -16,14 +16,14 @@
import Foundation
enum ServerSelectionViewModelAction {
enum ServerSelectionScreenViewModelAction {
/// The user would like to use the homeserver at the given address.
case confirm(homeserverAddress: String)
/// Dismiss the view without using the entered address.
case dismiss
}
struct ServerSelectionViewState: BindableState {
struct ServerSelectionScreenViewState: BindableState {
/// The message to be shown in the text field footer when no error has occurred.
private let regularFooterMessage = {
let linkPlaceholder = "{link}"
@@ -35,7 +35,7 @@ struct ServerSelectionViewState: BindableState {
}()
/// 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.
var footerErrorMessage: String?
/// 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.
var homeserverAddress: String
/// 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.
case confirm
/// Dismiss the view without using the entered address.
@@ -78,7 +78,7 @@ enum ServerSelectionViewAction {
case clearFooterError
}
enum ServerSelectionErrorType: Hashable {
enum ServerSelectionScreenErrorType: Hashable {
/// An error message to be shown in the text field footer.
case footerMessage(String)
/// An alert that allows the user to learn about sliding sync.
@@ -16,19 +16,19 @@
import SwiftUI
typealias ServerSelectionViewModelType = StateStoreViewModel<ServerSelectionViewState, ServerSelectionViewAction>
typealias ServerSelectionScreenViewModelType = StateStoreViewModel<ServerSelectionScreenViewState, ServerSelectionScreenViewAction>
class ServerSelectionViewModel: ServerSelectionViewModelType, ServerSelectionViewModelProtocol {
var callback: (@MainActor (ServerSelectionViewModelAction) -> Void)?
class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, ServerSelectionScreenViewModelProtocol {
var callback: (@MainActor (ServerSelectionScreenViewModelAction) -> Void)?
init(homeserverAddress: String, isModallyPresented: Bool) {
let bindings = ServerSelectionBindings(homeserverAddress: homeserverAddress)
let bindings = ServerSelectionScreenBindings(homeserverAddress: homeserverAddress)
super.init(initialViewState: ServerSelectionViewState(bindings: bindings,
isModallyPresented: isModallyPresented))
super.init(initialViewState: ServerSelectionScreenViewState(bindings: bindings,
isModallyPresented: isModallyPresented))
}
override func process(viewAction: ServerSelectionViewAction) {
override func process(viewAction: ServerSelectionScreenViewAction) {
switch viewAction {
case .confirm:
callback?(.confirm(homeserverAddress: state.bindings.homeserverAddress))
@@ -39,7 +39,7 @@ class ServerSelectionViewModel: ServerSelectionViewModelType, ServerSelectionVie
}
}
func displayError(_ type: ServerSelectionErrorType) {
func displayError(_ type: ServerSelectionScreenErrorType) {
switch type {
case .footerMessage(let message):
withElementAnimation {
@@ -17,10 +17,10 @@
import Foundation
@MainActor
protocol ServerSelectionViewModelProtocol {
var callback: (@MainActor (ServerSelectionViewModelAction) -> Void)? { get set }
var context: ServerSelectionViewModelType.Context { get }
protocol ServerSelectionScreenViewModelProtocol {
var callback: (@MainActor (ServerSelectionScreenViewModelAction) -> Void)? { get set }
var context: ServerSelectionScreenViewModelType.Context { get }
/// Displays an error to the user.
func displayError(_ type: ServerSelectionErrorType)
func displayError(_ type: ServerSelectionScreenErrorType)
}
@@ -17,7 +17,7 @@
import SwiftUI
struct ServerSelectionScreen: View {
@ObservedObject var context: ServerSelectionViewModel.Context
@ObservedObject var context: ServerSelectionScreenViewModel.Context
var body: some View {
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 SwiftUI
struct SoftLogoutCoordinatorParameters {
struct SoftLogoutScreenCoordinatorParameters {
let authenticationService: AuthenticationServiceProxyProtocol
let credentials: SoftLogoutCredentials
let credentials: SoftLogoutScreenCredentials
let keyBackupNeeded: Bool
}
enum SoftLogoutCoordinatorResult: CustomStringConvertible {
enum SoftLogoutScreenCoordinatorResult: CustomStringConvertible {
/// Login was successful.
case signedIn(UserSessionProtocol)
/// Clear all user data
@@ -40,26 +40,26 @@ enum SoftLogoutCoordinatorResult: CustomStringConvertible {
}
}
final class SoftLogoutCoordinator: CoordinatorProtocol {
private let parameters: SoftLogoutCoordinatorParameters
private var viewModel: SoftLogoutViewModelProtocol
final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
private let parameters: SoftLogoutScreenCoordinatorParameters
private var viewModel: SoftLogoutScreenViewModelProtocol
private let hostingController: UIViewController
/// Passed to the OIDC service to provide a view controller from which to present the authentication session.
private let oidcUserAgent: OIDExternalUserAgentIOS?
/// The wizard used to handle the registration flow.
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
let homeserver = parameters.authenticationService.homeserver
viewModel = SoftLogoutViewModel(credentials: parameters.credentials,
homeserver: homeserver,
keyBackupNeeded: parameters.keyBackupNeeded)
viewModel = SoftLogoutScreenViewModel(credentials: parameters.credentials,
homeserver: homeserver,
keyBackupNeeded: parameters.keyBackupNeeded)
hostingController = UIHostingController(rootView: SoftLogoutScreen(context: viewModel.context))
oidcUserAgent = OIDExternalUserAgentIOS(presenting: hostingController)
@@ -16,14 +16,14 @@
import SwiftUI
struct SoftLogoutCredentials {
struct SoftLogoutScreenCredentials {
let userId: String
let homeserverName: String
let userDisplayName: String
let deviceId: String?
}
enum SoftLogoutViewModelAction: CustomStringConvertible {
enum SoftLogoutScreenViewModelAction: CustomStringConvertible {
/// Login with password
case login(String)
/// Forgot password
@@ -48,9 +48,9 @@ enum SoftLogoutViewModelAction: CustomStringConvertible {
}
}
struct SoftLogoutViewState: BindableState {
struct SoftLogoutScreenViewState: BindableState {
/// Soft logout credentials
var credentials: SoftLogoutCredentials
var credentials: SoftLogoutScreenCredentials
/// Data about the selected homeserver.
var homeserver: LoginHomeserver
@@ -59,7 +59,7 @@ struct SoftLogoutViewState: BindableState {
var keyBackupNeeded: Bool
/// View state that can be bound to from SwiftUI.
var bindings: SoftLogoutBindings
var bindings: SoftLogoutScreenBindings
/// The types of login supported by the homeserver.
var loginMode: LoginMode { homeserver.loginMode }
@@ -75,14 +75,14 @@ struct SoftLogoutViewState: BindableState {
}
}
struct SoftLogoutBindings {
struct SoftLogoutScreenBindings {
/// The password input by the user.
var password: String
/// Information describing the currently displayed alert.
var alertInfo: AlertInfo<SoftLogoutErrorType>?
var alertInfo: AlertInfo<SoftLogoutScreenErrorType>?
}
enum SoftLogoutViewAction {
enum SoftLogoutScreenViewAction {
/// Login.
case login
/// Forgot password
@@ -93,7 +93,7 @@ enum SoftLogoutViewAction {
case continueWithOIDC
}
enum SoftLogoutErrorType: Hashable {
enum SoftLogoutScreenErrorType: Hashable {
/// A specific error message shown in an alert.
case alert(String)
/// An unknown error occurred.
@@ -16,24 +16,24 @@
import SwiftUI
typealias SoftLogoutViewModelType = StateStoreViewModel<SoftLogoutViewState, SoftLogoutViewAction>
typealias SoftLogoutScreenViewModelType = StateStoreViewModel<SoftLogoutScreenViewState, SoftLogoutScreenViewAction>
class SoftLogoutViewModel: SoftLogoutViewModelType, SoftLogoutViewModelProtocol {
var callback: (@MainActor (SoftLogoutViewModelAction) -> Void)?
class SoftLogoutScreenViewModel: SoftLogoutScreenViewModelType, SoftLogoutScreenViewModelProtocol {
var callback: (@MainActor (SoftLogoutScreenViewModelAction) -> Void)?
init(credentials: SoftLogoutCredentials,
init(credentials: SoftLogoutScreenCredentials,
homeserver: LoginHomeserver,
keyBackupNeeded: Bool,
password: String = "") {
let bindings = SoftLogoutBindings(password: password)
let viewState = SoftLogoutViewState(credentials: credentials,
homeserver: homeserver,
keyBackupNeeded: keyBackupNeeded,
bindings: bindings)
let bindings = SoftLogoutScreenBindings(password: password)
let viewState = SoftLogoutScreenViewState(credentials: credentials,
homeserver: homeserver,
keyBackupNeeded: keyBackupNeeded,
bindings: bindings)
super.init(initialViewState: viewState)
}
override func process(viewAction: SoftLogoutViewAction) {
override func process(viewAction: SoftLogoutScreenViewAction) {
switch viewAction {
case .login:
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 {
case .alert(let message):
state.bindings.alertInfo = AlertInfo(id: type,
@@ -16,10 +16,10 @@
import Foundation
protocol SoftLogoutViewModelProtocol {
var callback: (@MainActor (SoftLogoutViewModelAction) -> Void)? { get set }
var context: SoftLogoutViewModelType.Context { get }
protocol SoftLogoutScreenViewModelProtocol {
var callback: (@MainActor (SoftLogoutScreenViewModelAction) -> Void)? { get set }
var context: SoftLogoutScreenViewModelType.Context { get }
/// 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.
@FocusState private var isPasswordFocused: Bool
@ObservedObject var context: SoftLogoutViewModel.Context
@ObservedObject var context: SoftLogoutScreenViewModel.Context
var body: some View {
ScrollView {
@@ -171,14 +171,14 @@ struct SoftLogoutScreen: View {
// MARK: - Previews
struct SoftLogout_Previews: PreviewProvider {
struct SoftLogoutScreen_Previews: PreviewProvider {
static var previews: some View {
ForEach(MockSoftLogoutScreenState.allCases) { state in
screen(for: state.viewModel)
}
}
static func screen(for viewModel: SoftLogoutViewModel) -> some View {
static func screen(for viewModel: SoftLogoutScreenViewModel) -> some View {
NavigationStack {
SoftLogoutScreen(context: viewModel.context)
.navigationBarTitleDisplayMode(.inline)
@@ -17,32 +17,32 @@
import Combine
import SwiftUI
struct StartChatCoordinatorParameters {
struct StartChatScreenCoordinatorParameters {
let userSession: UserSessionProtocol
weak var userIndicatorController: UserIndicatorControllerProtocol?
let navigationStackCoordinator: NavigationStackCoordinator?
let userDiscoveryService: UserDiscoveryServiceProtocol
}
enum StartChatCoordinatorAction {
enum StartChatScreenCoordinatorAction {
case close
case openRoom(withIdentifier: String)
}
final class StartChatCoordinator: CoordinatorProtocol {
private let parameters: StartChatCoordinatorParameters
private var viewModel: StartChatViewModelProtocol
private let actionsSubject: PassthroughSubject<StartChatCoordinatorAction, Never> = .init()
final class StartChatScreenCoordinator: CoordinatorProtocol {
private let parameters: StartChatScreenCoordinatorParameters
private var viewModel: StartChatScreenViewModelProtocol
private let actionsSubject: PassthroughSubject<StartChatScreenCoordinatorAction, Never> = .init()
private var cancellables: Set<AnyCancellable> = .init()
var actions: AnyPublisher<StartChatCoordinatorAction, Never> {
var actions: AnyPublisher<StartChatScreenCoordinatorAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
init(parameters: StartChatCoordinatorParameters) {
init(parameters: StartChatScreenCoordinatorParameters) {
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() {
@@ -16,18 +16,18 @@
import Foundation
enum StartChatErrorType: Error {
enum StartChatScreenErrorType: Error {
case failedCreatingRoom
case unknown
}
enum StartChatViewModelAction {
enum StartChatScreenViewModelAction {
case close
case createRoom
case openRoom(withIdentifier: String)
}
struct StartChatViewState: BindableState {
struct StartChatScreenViewState: BindableState {
var bindings = StartChatScreenViewStateBindings()
var usersSection: UserDiscoverySection = .init(type: .suggestions, users: [])
@@ -44,10 +44,10 @@ struct StartChatScreenViewStateBindings {
var searchQuery = ""
/// Information describing the currently displayed alert.
var alertInfo: AlertInfo<StartChatErrorType>?
var alertInfo: AlertInfo<StartChatScreenErrorType>?
}
enum StartChatViewAction {
enum StartChatScreenViewAction {
case close
case createRoom
case inviteFriends
@@ -17,14 +17,14 @@
import Combine
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 actionsSubject: PassthroughSubject<StartChatViewModelAction, Never> = .init()
private let actionsSubject: PassthroughSubject<StartChatScreenViewModelAction, Never> = .init()
private let userDiscoveryService: UserDiscoveryServiceProtocol
var actions: AnyPublisher<StartChatViewModelAction, Never> {
var actions: AnyPublisher<StartChatScreenViewModelAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
@@ -34,14 +34,14 @@ class StartChatViewModel: StartChatViewModelType, StartChatViewModelProtocol {
self.userSession = userSession
self.userIndicatorController = userIndicatorController
self.userDiscoveryService = userDiscoveryService
super.init(initialViewState: StartChatViewState(), imageProvider: userSession.mediaProvider)
super.init(initialViewState: StartChatScreenViewState(), imageProvider: userSession.mediaProvider)
setupBindings()
}
// MARK: - Public
override func process(viewAction: StartChatViewAction) {
override func process(viewAction: StartChatScreenViewAction) {
switch viewAction {
case .close:
actionsSubject.send(.close)
@@ -17,7 +17,7 @@
import Combine
@MainActor
protocol StartChatViewModelProtocol {
var actions: AnyPublisher<StartChatViewModelAction, Never> { get }
var context: StartChatViewModelType.Context { get }
protocol StartChatScreenViewModelProtocol {
var actions: AnyPublisher<StartChatScreenViewModelAction, Never> { get }
var context: StartChatScreenViewModelType.Context { get }
}
@@ -17,7 +17,7 @@
import SwiftUI
struct StartChatScreen: View {
@ObservedObject var context: StartChatViewModel.Context
@ObservedObject var context: StartChatScreenViewModel.Context
var body: some View {
Form {
@@ -130,14 +130,14 @@ struct StartChatScreen: View {
// MARK: - Previews
struct StartChat_Previews: PreviewProvider {
struct StartChatScreen_Previews: PreviewProvider {
static let viewModel = {
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"),
mediaProvider: MockMediaProvider())
let userDiscoveryService = UserDiscoveryServiceMock()
userDiscoveryService.fetchSuggestionsReturnValue = .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
}()
@@ -289,8 +289,8 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
let userIndicatorController = UserIndicatorController(rootCoordinator: startChatNavigationStackCoordinator)
let userDiscoveryService = UserDiscoveryService(clientProxy: userSession.clientProxy)
let parameters = StartChatCoordinatorParameters(userSession: userSession, userIndicatorController: userIndicatorController, navigationStackCoordinator: startChatNavigationStackCoordinator, userDiscoveryService: userDiscoveryService)
let coordinator = StartChatCoordinator(parameters: parameters)
let parameters = StartChatScreenCoordinatorParameters(userSession: userSession, userIndicatorController: userIndicatorController, navigationStackCoordinator: startChatNavigationStackCoordinator, userDiscoveryService: userDiscoveryService)
let coordinator = StartChatScreenCoordinator(parameters: parameters)
coordinator.actions.sink { [weak self] action in
guard let self else { return }
switch action {
@@ -62,21 +62,21 @@ class MockScreen: Identifiable {
switch id {
case .login:
let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = LoginCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(),
navigationStackCoordinator: navigationStackCoordinator))
let coordinator = LoginScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(),
navigationStackCoordinator: navigationStackCoordinator))
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
case .serverSelection:
let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = ServerSelectionCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(),
userIndicatorController: MockUserIndicatorController(),
isModallyPresented: true))
let coordinator = ServerSelectionScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(),
userIndicatorController: MockUserIndicatorController(),
isModallyPresented: true))
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
case .serverSelectionNonModal:
return ServerSelectionCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(),
userIndicatorController: MockUserIndicatorController(),
isModallyPresented: false))
return ServerSelectionScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(),
userIndicatorController: MockUserIndicatorController(),
isModallyPresented: false))
case .analyticsPrompt:
return AnalyticsPromptScreenCoordinator()
case .analyticsSettingsScreen:
@@ -92,13 +92,13 @@ class MockScreen: Identifiable {
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
case .softLogout:
let credentials = SoftLogoutCredentials(userId: "@mock:matrix.org",
homeserverName: "matrix.org",
userDisplayName: "mock",
deviceId: "ABCDEFGH")
return SoftLogoutCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(),
credentials: credentials,
keyBackupNeeded: false))
let credentials = SoftLogoutScreenCredentials(userId: "@mock:matrix.org",
homeserverName: "matrix.org",
userDisplayName: "mock",
deviceId: "ABCDEFGH")
return SoftLogoutScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(),
credentials: credentials,
keyBackupNeeded: false))
case .simpleRegular:
return TemplateScreenCoordinator(parameters: .init(promptType: .regular))
case .simpleUpgrade:
@@ -321,8 +321,8 @@ class MockScreen: Identifiable {
userDiscoveryMock.fetchSuggestionsReturnValue = .success([.mockAlice, .mockBob, .mockCharlie])
userDiscoveryMock.searchProfilesWithReturnValue = .success([])
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@mock:client.com"), mediaProvider: MockMediaProvider())
let parameters: StartChatCoordinatorParameters = .init(userSession: userSession, navigationStackCoordinator: navigationStackCoordinator, userDiscoveryService: userDiscoveryMock)
let coordinator = StartChatCoordinator(parameters: parameters)
let parameters: StartChatScreenCoordinatorParameters = .init(userSession: userSession, navigationStackCoordinator: navigationStackCoordinator, userDiscoveryService: userDiscoveryMock)
let coordinator = StartChatScreenCoordinator(parameters: parameters)
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
case .startChatWithSearchResults:
@@ -332,7 +332,7 @@ class MockScreen: Identifiable {
userDiscoveryMock.fetchSuggestionsReturnValue = .success([])
userDiscoveryMock.searchProfilesWithReturnValue = .success([.mockBob, .mockBobby])
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)
return navigationStackCoordinator
case .roomMemberDetailsAccountOwner: