mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
Move the core logic in LoginScreenCoordinator into the ViewModel. (#3348)
This commit is contained in:
@@ -244,8 +244,9 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
private func showLoginScreen() {
|
||||
let parameters = LoginScreenCoordinatorParameters(authenticationService: authenticationService,
|
||||
analytics: analytics,
|
||||
userIndicatorController: userIndicatorController)
|
||||
slidingSyncLearnMoreURL: appSettings.slidingSyncLearnMoreURL,
|
||||
userIndicatorController: userIndicatorController,
|
||||
analytics: analytics)
|
||||
let coordinator = LoginScreenCoordinator(parameters: parameters)
|
||||
|
||||
coordinator.actions
|
||||
|
||||
@@ -20,10 +20,8 @@ enum LoginMode: Equatable {
|
||||
|
||||
var supportsOIDCFlow: Bool {
|
||||
switch self {
|
||||
case .oidc:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
case .oidc: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ import SwiftUI
|
||||
struct LoginScreenCoordinatorParameters {
|
||||
/// The service used to authenticate the user.
|
||||
let authenticationService: AuthenticationServiceProtocol
|
||||
|
||||
let analytics: AnalyticsService
|
||||
let slidingSyncLearnMoreURL: URL
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
let analytics: AnalyticsService
|
||||
}
|
||||
|
||||
enum LoginScreenCoordinatorAction {
|
||||
@@ -42,8 +42,10 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
|
||||
init(parameters: LoginScreenCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
|
||||
viewModel = LoginScreenViewModel(homeserver: parameters.authenticationService.homeserver.value,
|
||||
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL)
|
||||
viewModel = LoginScreenViewModel(authenticationService: parameters.authenticationService,
|
||||
slidingSyncLearnMoreURL: parameters.slidingSyncLearnMoreURL,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
analytics: parameters.analytics)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
@@ -54,119 +56,20 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .parseUsername(let username):
|
||||
parseUsername(username)
|
||||
case .forgotPassword:
|
||||
showForgotPasswordScreen()
|
||||
case .login(let username, let password):
|
||||
login(username: username, password: password)
|
||||
case .configuredForOIDC:
|
||||
actionsSubject.send(.configuredForOIDC)
|
||||
case .signedIn(let userSession):
|
||||
actionsSubject.send(.signedIn(userSession))
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func stop() {
|
||||
stopLoading()
|
||||
viewModel.stopLoading()
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(LoginScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private static let loadingIndicatorIdentifier = "\(LoginScreenCoordinatorAction.self)-Loading"
|
||||
|
||||
private func startLoading(isInteractionBlocking: Bool) {
|
||||
if isInteractionBlocking {
|
||||
parameters.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier,
|
||||
type: .modal,
|
||||
title: L10n.commonLoading,
|
||||
persistent: true))
|
||||
} else {
|
||||
viewModel.update(isLoading: true)
|
||||
}
|
||||
}
|
||||
|
||||
private func stopLoading() {
|
||||
viewModel.update(isLoading: false)
|
||||
parameters.userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier)
|
||||
}
|
||||
|
||||
/// Processes an error to either update the flow or display it to the user.
|
||||
private func handleError(_ error: AuthenticationServiceError) {
|
||||
MXLog.info("Error occurred: \(error)")
|
||||
|
||||
switch error {
|
||||
case .invalidCredentials:
|
||||
viewModel.displayError(.alert(L10n.screenLoginErrorInvalidCredentials))
|
||||
case .accountDeactivated:
|
||||
viewModel.displayError(.alert(L10n.screenLoginErrorDeactivatedAccount))
|
||||
case .invalidWellKnown(let error):
|
||||
viewModel.displayError(.invalidWellKnownAlert(error))
|
||||
case .slidingSyncNotAvailable:
|
||||
viewModel.displayError(.slidingSyncAlert)
|
||||
case .sessionTokenRefreshNotSupported:
|
||||
viewModel.displayError(.refreshTokenAlert)
|
||||
default:
|
||||
viewModel.displayError(.alert(L10n.errorUnknown))
|
||||
}
|
||||
}
|
||||
|
||||
/// Requests the authentication coordinator to log in using the specified credentials.
|
||||
private func login(username: String, password: String) {
|
||||
MXLog.info("Starting login with password.")
|
||||
startLoading(isInteractionBlocking: true)
|
||||
|
||||
Task {
|
||||
parameters.analytics.signpost.beginLogin()
|
||||
switch await authenticationService.login(username: username,
|
||||
password: password,
|
||||
initialDeviceName: UIDevice.current.initialDeviceName,
|
||||
deviceID: nil) {
|
||||
case .success(let userSession):
|
||||
actionsSubject.send(.signedIn(userSession))
|
||||
parameters.analytics.signpost.endLogin()
|
||||
stopLoading()
|
||||
case .failure(let error):
|
||||
stopLoading()
|
||||
parameters.analytics.signpost.endLogin()
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the specified username and looks up the homeserver when a Matrix ID is entered.
|
||||
private func parseUsername(_ username: String) {
|
||||
guard MatrixEntityRegex.isMatrixUserIdentifier(username) else { return }
|
||||
|
||||
let homeserverDomain = String(username.split(separator: ":")[1])
|
||||
|
||||
startLoading(isInteractionBlocking: false)
|
||||
|
||||
Task {
|
||||
switch await authenticationService.configure(for: homeserverDomain, flow: .login) {
|
||||
case .success:
|
||||
stopLoading()
|
||||
if authenticationService.homeserver.value.loginMode == .oidc {
|
||||
actionsSubject.send(.configuredForOIDC)
|
||||
} else {
|
||||
updateViewModel()
|
||||
}
|
||||
case .failure(let error):
|
||||
stopLoading()
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the view model with a different homeserver.
|
||||
private func updateViewModel() {
|
||||
viewModel.update(homeserver: authenticationService.homeserver.value)
|
||||
}
|
||||
|
||||
/// Shows the forgot password screen.
|
||||
private func showForgotPasswordScreen() {
|
||||
viewModel.displayError(.alert("Not implemented."))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,23 +7,16 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
enum LoginScreenViewModelAction: CustomStringConvertible {
|
||||
/// Parse the username and update the homeserver if included.
|
||||
case parseUsername(String)
|
||||
/// The user would like to reset their password.
|
||||
case forgotPassword
|
||||
/// Login using the supplied credentials.
|
||||
case login(username: String, password: String)
|
||||
enum LoginScreenViewModelAction {
|
||||
/// The homeserver was updated to one that supports OIDC.
|
||||
case configuredForOIDC
|
||||
/// Login was successful.
|
||||
case signedIn(UserSessionProtocol)
|
||||
|
||||
/// A string representation of the action, ignoring any associated values that could leak PII.
|
||||
var description: String {
|
||||
var isConfiguredForOIDC: Bool {
|
||||
switch self {
|
||||
case .parseUsername:
|
||||
return "parseUsername"
|
||||
case .forgotPassword:
|
||||
return "forgotPassword"
|
||||
case .login:
|
||||
return "login"
|
||||
case .configuredForOIDC: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +27,7 @@ struct LoginScreenViewState: BindableState {
|
||||
/// Whether a new homeserver is currently being loaded.
|
||||
var isLoading = false
|
||||
/// View state that can be bound to from SwiftUI.
|
||||
var bindings: LoginScreenBindings
|
||||
var bindings = LoginScreenBindings()
|
||||
|
||||
/// The types of login supported by the homeserver.
|
||||
var loginMode: LoginMode { homeserver.loginMode }
|
||||
@@ -62,8 +55,6 @@ struct LoginScreenBindings {
|
||||
enum LoginScreenViewAction {
|
||||
/// Parse the username to detect if a homeserver is included.
|
||||
case parseUsername
|
||||
/// The user would like to reset their password.
|
||||
case forgotPassword
|
||||
/// Continue using the input username and password.
|
||||
case next
|
||||
}
|
||||
@@ -71,8 +62,10 @@ enum LoginScreenViewAction {
|
||||
enum LoginScreenErrorType: Hashable {
|
||||
/// A specific error message shown in an alert.
|
||||
case alert(String)
|
||||
/// Looking up the homeserver from the username failed.
|
||||
case invalidHomeserver
|
||||
/// An alert that informs the user to check their username/password.
|
||||
case credentialsAlert
|
||||
/// An alert that informs the user that their account has been deactivated.
|
||||
case deactivatedAlert
|
||||
/// An alert that informs the user about a bad well-known file.
|
||||
case invalidWellKnownAlert(String)
|
||||
/// An alert that allows the user to learn about sliding sync.
|
||||
|
||||
@@ -11,57 +11,129 @@ import SwiftUI
|
||||
typealias LoginScreenViewModelType = StateStoreViewModel<LoginScreenViewState, LoginScreenViewAction>
|
||||
|
||||
class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtocol {
|
||||
private let authenticationService: AuthenticationServiceProtocol
|
||||
private let slidingSyncLearnMoreURL: URL
|
||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||
private let analytics: AnalyticsService
|
||||
|
||||
private var actionsSubject: PassthroughSubject<LoginScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<LoginScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(homeserver: LoginHomeserver, slidingSyncLearnMoreURL: URL) {
|
||||
init(authenticationService: AuthenticationServiceProtocol,
|
||||
slidingSyncLearnMoreURL: URL,
|
||||
userIndicatorController: UserIndicatorControllerProtocol,
|
||||
analytics: AnalyticsService) {
|
||||
self.authenticationService = authenticationService
|
||||
self.slidingSyncLearnMoreURL = slidingSyncLearnMoreURL
|
||||
let bindings = LoginScreenBindings()
|
||||
let viewState = LoginScreenViewState(homeserver: homeserver, bindings: bindings)
|
||||
self.userIndicatorController = userIndicatorController
|
||||
self.analytics = analytics
|
||||
|
||||
let viewState = LoginScreenViewState(homeserver: authenticationService.homeserver.value)
|
||||
|
||||
super.init(initialViewState: viewState)
|
||||
|
||||
authenticationService.homeserver
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.homeserver, on: self)
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
override func process(viewAction: LoginScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .parseUsername:
|
||||
actionsSubject.send(.parseUsername(state.bindings.username))
|
||||
case .forgotPassword:
|
||||
actionsSubject.send(.forgotPassword)
|
||||
parseUsername()
|
||||
case .next:
|
||||
actionsSubject.send(.login(username: state.bindings.username, password: state.bindings.password))
|
||||
login()
|
||||
}
|
||||
}
|
||||
|
||||
func update(isLoading: Bool) {
|
||||
guard state.isLoading != isLoading else { return }
|
||||
state.isLoading = isLoading
|
||||
func stopLoading() {
|
||||
state.isLoading = false
|
||||
userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier)
|
||||
}
|
||||
|
||||
func update(homeserver: LoginHomeserver) {
|
||||
state.homeserver = homeserver
|
||||
// MARK: - Private
|
||||
|
||||
/// Parses the specified username and looks up the homeserver when a Matrix ID is entered.
|
||||
private func parseUsername() {
|
||||
let username = state.bindings.username
|
||||
|
||||
guard MatrixEntityRegex.isMatrixUserIdentifier(username) else { return }
|
||||
|
||||
let homeserverDomain = String(username.split(separator: ":")[1])
|
||||
|
||||
startLoading(isInteractionBlocking: false)
|
||||
|
||||
Task {
|
||||
switch await authenticationService.configure(for: homeserverDomain, flow: .login) {
|
||||
case .success:
|
||||
if authenticationService.homeserver.value.loginMode == .oidc {
|
||||
actionsSubject.send(.configuredForOIDC)
|
||||
}
|
||||
stopLoading()
|
||||
case .failure(let error):
|
||||
stopLoading()
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func displayError(_ type: LoginScreenErrorType) {
|
||||
switch type {
|
||||
case .alert(let message):
|
||||
state.bindings.alertInfo = AlertInfo(id: type,
|
||||
/// Requests the authentication coordinator to log in using the specified credentials.
|
||||
private func login() {
|
||||
MXLog.info("Starting login with password.")
|
||||
startLoading(isInteractionBlocking: true)
|
||||
|
||||
Task {
|
||||
analytics.signpost.beginLogin()
|
||||
switch await authenticationService.login(username: state.bindings.username,
|
||||
password: state.bindings.password,
|
||||
initialDeviceName: UIDevice.current.initialDeviceName,
|
||||
deviceID: nil) {
|
||||
case .success(let userSession):
|
||||
actionsSubject.send(.signedIn(userSession))
|
||||
analytics.signpost.endLogin()
|
||||
stopLoading()
|
||||
case .failure(let error):
|
||||
stopLoading()
|
||||
analytics.signpost.endLogin()
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static let loadingIndicatorIdentifier = "\(LoginScreenCoordinatorAction.self)-Loading"
|
||||
|
||||
private func startLoading(isInteractionBlocking: Bool) {
|
||||
if isInteractionBlocking {
|
||||
userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier,
|
||||
type: .modal,
|
||||
title: L10n.commonLoading,
|
||||
persistent: true))
|
||||
} else {
|
||||
state.isLoading = true
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes an error to either update the flow or display it to the user.
|
||||
private func handleError(_ error: AuthenticationServiceError) {
|
||||
MXLog.info("Error occurred: \(error)")
|
||||
|
||||
switch error {
|
||||
case .invalidCredentials:
|
||||
state.bindings.alertInfo = AlertInfo(id: .credentialsAlert,
|
||||
title: L10n.commonError,
|
||||
message: message)
|
||||
case .invalidHomeserver:
|
||||
state.bindings.alertInfo = AlertInfo(id: type,
|
||||
message: L10n.screenLoginErrorInvalidCredentials)
|
||||
case .accountDeactivated:
|
||||
state.bindings.alertInfo = AlertInfo(id: .deactivatedAlert,
|
||||
title: L10n.commonError,
|
||||
message: L10n.screenLoginErrorInvalidUserId)
|
||||
case .invalidWellKnownAlert(let error):
|
||||
message: L10n.screenLoginErrorDeactivatedAccount)
|
||||
case .invalidWellKnown(let error):
|
||||
state.bindings.alertInfo = AlertInfo(id: .slidingSyncAlert,
|
||||
title: L10n.commonServerNotSupported,
|
||||
message: L10n.screenChangeServerErrorInvalidWellKnown(error))
|
||||
case .slidingSyncAlert:
|
||||
case .slidingSyncNotAvailable:
|
||||
let openURL = { UIApplication.shared.open(self.slidingSyncLearnMoreURL) }
|
||||
state.bindings.alertInfo = AlertInfo(id: .slidingSyncAlert,
|
||||
title: L10n.commonServerNotSupported,
|
||||
@@ -71,12 +143,12 @@ class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtoc
|
||||
|
||||
// Clear out the invalid username to avoid an attempted login to matrix.org
|
||||
state.bindings.username = ""
|
||||
case .refreshTokenAlert:
|
||||
state.bindings.alertInfo = AlertInfo(id: type,
|
||||
case .sessionTokenRefreshNotSupported:
|
||||
state.bindings.alertInfo = AlertInfo(id: .refreshTokenAlert,
|
||||
title: L10n.commonServerNotSupported,
|
||||
message: L10n.screenLoginErrorRefreshTokens)
|
||||
case .unknown:
|
||||
state.bindings.alertInfo = AlertInfo(id: type)
|
||||
default:
|
||||
state.bindings.alertInfo = AlertInfo(id: .unknown)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-11
@@ -12,15 +12,6 @@ protocol LoginScreenViewModelProtocol {
|
||||
var actions: AnyPublisher<LoginScreenViewModelAction, Never> { get }
|
||||
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.
|
||||
func update(isLoading: Bool)
|
||||
|
||||
/// Update the view with new homeserver information.
|
||||
/// - Parameter homeserver: The view data for the homeserver. This can be generated using `AuthenticationService.Homeserver.viewData`.
|
||||
func update(homeserver: LoginHomeserver)
|
||||
|
||||
/// Display an error to the user.
|
||||
/// - Parameter type: The type of error to be displayed.
|
||||
func displayError(_ type: LoginScreenErrorType)
|
||||
/// Update the view to reflect that loaded has finished.
|
||||
func stopLoading()
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ struct LoginScreen: View {
|
||||
// This should never be shown.
|
||||
ProgressView()
|
||||
default:
|
||||
// This should never be shown either.
|
||||
loginUnavailableText
|
||||
}
|
||||
}
|
||||
@@ -37,6 +38,7 @@ struct LoginScreen: View {
|
||||
.padding(.bottom, 16)
|
||||
}
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.alert(item: $context.alertInfo)
|
||||
}
|
||||
|
||||
@@ -124,35 +126,45 @@ struct LoginScreen: View {
|
||||
// MARK: - Previews
|
||||
|
||||
struct LoginScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let credentialsViewModel: LoginScreenViewModel = {
|
||||
let viewModel = LoginScreenViewModel(homeserver: .mockMatrixDotOrg, slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL)
|
||||
viewModel.context.username = "alice"
|
||||
viewModel.context.password = "password"
|
||||
return viewModel
|
||||
}()
|
||||
static let viewModel = makeViewModel()
|
||||
static let credentialsViewModel = makeViewModel(withCredentials: true)
|
||||
static let unconfiguredViewModel = makeViewModel(homeserverAddress: "somethingtofailconfiguration")
|
||||
|
||||
static var previews: some View {
|
||||
screen(for: LoginScreenViewModel(homeserver: .mockMatrixDotOrg, slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL))
|
||||
.previewDisplayName("matrix.org")
|
||||
screen(for: credentialsViewModel)
|
||||
.previewDisplayName("Credentials Entered")
|
||||
screen(for: LoginScreenViewModel(homeserver: .mockMatrixDotOrg, slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL))
|
||||
.previewDisplayName("Unsupported")
|
||||
screen(for: LoginScreenViewModel(homeserver: .mockMatrixDotOrg, slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL))
|
||||
.previewDisplayName("OIDC Fallback")
|
||||
}
|
||||
|
||||
static func screen(for viewModel: LoginScreenViewModel) -> some View {
|
||||
NavigationStack {
|
||||
LoginScreen(context: viewModel.context)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button { } label: {
|
||||
Text("\(Image(systemName: "chevron.backward")) Back")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.previewDisplayName("matrix.org")
|
||||
.snapshotPreferences(delay: 0.1)
|
||||
|
||||
NavigationStack {
|
||||
LoginScreen(context: credentialsViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Credentials Entered")
|
||||
.snapshotPreferences(delay: 0.1)
|
||||
|
||||
NavigationStack {
|
||||
LoginScreen(context: unconfiguredViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Unsupported")
|
||||
.snapshotPreferences(delay: 0.1)
|
||||
}
|
||||
|
||||
static func makeViewModel(homeserverAddress: String = "matrix.org", withCredentials: Bool = false) -> LoginScreenViewModel {
|
||||
let authenticationService = AuthenticationService.mock
|
||||
|
||||
Task { await authenticationService.configure(for: homeserverAddress, flow: .login) }
|
||||
|
||||
let viewModel = LoginScreenViewModel(authenticationService: authenticationService,
|
||||
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
analytics: ServiceLocator.shared.analytics)
|
||||
|
||||
if withCredentials {
|
||||
viewModel.context.username = "alice"
|
||||
viewModel.context.password = "password"
|
||||
}
|
||||
|
||||
return viewModel
|
||||
}
|
||||
}
|
||||
|
||||
+2
-4
@@ -37,10 +37,8 @@ class ServerConfirmationScreenViewModel: ServerConfirmationScreenViewModelType,
|
||||
|
||||
authenticationService.homeserver
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] homeserver in
|
||||
guard let self else { return }
|
||||
state.homeserverAddress = homeserver.address
|
||||
}
|
||||
.map(\.address)
|
||||
.weakAssign(to: \.state.homeserverAddress, on: self)
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
@@ -204,9 +204,11 @@ class AuthenticationService: AuthenticationServiceProtocol {
|
||||
// MARK: - Mocks
|
||||
|
||||
extension AuthenticationService {
|
||||
static var mock = AuthenticationService(userSessionStore: UserSessionStoreMock(configuration: .init()),
|
||||
encryptionKeyProvider: EncryptionKeyProvider(),
|
||||
clientBuilderFactory: AuthenticationClientBuilderFactoryMock(configuration: .init()),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
appHooks: AppHooks())
|
||||
static var mock: AuthenticationService {
|
||||
AuthenticationService(userSessionStore: UserSessionStoreMock(configuration: .init()),
|
||||
encryptionKeyProvider: EncryptionKeyProvider(),
|
||||
clientBuilderFactory: AuthenticationClientBuilderFactoryMock(configuration: .init()),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
appHooks: AppHooks())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user