diff --git a/Modules/Sources/Login/LoginEnvironment.swift b/Modules/Sources/Login/LoginEnvironment.swift new file mode 100644 index 0000000..60ed3bb --- /dev/null +++ b/Modules/Sources/Login/LoginEnvironment.swift @@ -0,0 +1,37 @@ +// +// File.swift +// +// +// Created by Nicolai Harbo on 20/04/2022. +// + +import APIClient +import AppVersion +import CombineSchedulers +import Localizations + +public struct LoginEnvironment { + + var mainQueue: AnySchedulerOf + var apiClient: APIClient + var date: () -> Date + var calendar: Calendar + var localizations: ObservableLocalizations + var appVersion: AppVersion + + public init( + mainQueue: AnySchedulerOf, + apiClient: APIClient, + date: @escaping () -> Date, + calendar: Calendar, + localizations: ObservableLocalizations, + appVersion: AppVersion + ) { + self.mainQueue = mainQueue + self.apiClient = apiClient + self.date = date + self.calendar = calendar + self.localizations = localizations + self.appVersion = appVersion + } +} diff --git a/Modules/Sources/Login/LoginView.swift b/Modules/Sources/Login/LoginView.swift index f371eeb..071dc8b 100644 --- a/Modules/Sources/Login/LoginView.swift +++ b/Modules/Sources/Login/LoginView.swift @@ -6,107 +6,12 @@ // import APIClient -import AppVersion import Combine -import CombineSchedulers -import Localizations import Model import Style import SwiftUI import SwiftUINavigation - -public struct LoginEnvironment { - - var mainQueue: AnySchedulerOf - var apiClient: APIClient - var date: () -> Date - var calendar: Calendar - var localizations: ObservableLocalizations - var appVersion: AppVersion - - public init( - mainQueue: AnySchedulerOf, - apiClient: APIClient, - date: @escaping () -> Date, - calendar: Calendar, - localizations: ObservableLocalizations, - appVersion: AppVersion - ) { - self.mainQueue = mainQueue - self.apiClient = apiClient - self.date = date - self.calendar = calendar - self.localizations = localizations - self.appVersion = appVersion - } -} - -public class LoginViewModel: ObservableObject { - - var environment: LoginEnvironment - var onSuccess: (APITokens, Username) -> Void - - enum Route { - case alert(AlertState) - } - @Published var route: Route? - - @Published var email: String = "" - @Published var password: String = "" - @Published var appVersion: String = "" - @Published var isAPICallInFlight = false - - var loginCancellable: AnyCancellable? - var cancellables: Set = [] - - public init( - onSuccess: @escaping (APITokens, Username) -> Void, - environment: LoginEnvironment - ) { - self.environment = environment - self.onSuccess = onSuccess - appVersion = "\(environment.appVersion.version())(\(environment.appVersion.build()))" - #if DEBUG - email = "711dk009@7-eleven.dk" - password = "1B57E5A0" - #endif - } - - /// Simple heuristics for email pattern - var isButtonEnabled: Bool { - email.contains("@") && email.contains(".") && email.count >= 6 - && !password.isEmpty && !isAPICallInFlight - } - - func loginButtonTapped() { - isAPICallInFlight = true - loginCancellable = environment.apiClient.authenticate( - Username(rawValue: email.trimmingCharacters(in: .whitespacesAndNewlines)), - Password(rawValue: password.trimmingCharacters(in: .whitespacesAndNewlines)) - ) - .receive(on: environment.mainQueue, options: nil) - .sink( - receiveCompletion: { [weak self, environment] in - if case .failure(let error) = $0 { - self?.route = .alert( - .withTitleAndMessage( - title: environment.localizations.error.errorTitle, - message: error.errorCode == "401" - ? environment.localizations.login.errorInvalidCredentials - : environment.localizations.error.serverError, - action1: .primary( - title: environment.localizations.defaultSection.ok.uppercased(), - action: { self?.route = nil }) - ) - ) - } - self?.isAPICallInFlight = false - }, - receiveValue: { [unowned self] in onSuccess($0, .init(rawValue: email)) } - ) - } - -} +import Localizations public struct LoginView: View { diff --git a/Modules/Sources/Login/LoginViewModel.swift b/Modules/Sources/Login/LoginViewModel.swift new file mode 100644 index 0000000..429e0ae --- /dev/null +++ b/Modules/Sources/Login/LoginViewModel.swift @@ -0,0 +1,83 @@ +// +// File.swift +// +// +// Created by Nicolai Harbo on 20/04/2022. +// + +import APIClient +import AppVersion +import Combine +import CombineSchedulers +import Localizations +import Model +import Style +import SwiftUI +import SwiftUINavigation + +public class LoginViewModel: ObservableObject { + + var environment: LoginEnvironment + var onSuccess: (APITokens, Username) -> Void + + enum Route { + case alert(AlertState) + } + @Published var route: Route? + + @Published var email: String = "" + @Published var password: String = "" + @Published var appVersion: String = "" + @Published var isAPICallInFlight = false + + var loginCancellable: AnyCancellable? + var cancellables: Set = [] + + public init( + onSuccess: @escaping (APITokens, Username) -> Void, + environment: LoginEnvironment + ) { + self.environment = environment + self.onSuccess = onSuccess + appVersion = "\(environment.appVersion.version())(\(environment.appVersion.build()))" + #if DEBUG + email = "711dk009@7-eleven.dk" + password = "1B57E5A0" + #endif + } + + /// Simple heuristics for email pattern + var isButtonEnabled: Bool { + email.contains("@") && email.contains(".") && email.count >= 6 + && !password.isEmpty && !isAPICallInFlight + } + + func loginButtonTapped() { + isAPICallInFlight = true + loginCancellable = environment.apiClient.authenticate( + Username(rawValue: email.trimmingCharacters(in: .whitespacesAndNewlines)), + Password(rawValue: password.trimmingCharacters(in: .whitespacesAndNewlines)) + ) + .receive(on: environment.mainQueue, options: nil) + .sink( + receiveCompletion: { [weak self, environment] in + if case .failure(let error) = $0 { + self?.route = .alert( + .withTitleAndMessage( + title: environment.localizations.error.errorTitle, + message: error.errorCode == "401" + ? environment.localizations.login.errorInvalidCredentials + : environment.localizations.error.serverError, + action1: .primary( + title: environment.localizations.defaultSection.ok.uppercased(), + action: { self?.route = nil }) + ) + ) + } + self?.isAPICallInFlight = false + }, + receiveValue: { [unowned self] in onSuccess($0, .init(rawValue: email)) } + ) + } + +}