// // LoginView.swift // PadelClub // // Created by Laurent Morvillier on 19/02/2024. // import SwiftUI import LeStorage enum LoginReason { case loginRequiredForFeature var title: String { switch self { case .loginRequiredForFeature: return "Compte requis" } } var systemImage: String { switch self { case .loginRequiredForFeature: return "person.crop.circle.fill" } } var description: String { switch self { case .loginRequiredForFeature: return "Pour accéder à cette fonctionnalité, veuillez-vous connecter ou créer un compte" } } } typealias Credentials = (username: String, password: String) struct LoginView: View { @EnvironmentObject var dataStore: DataStore @EnvironmentObject var networkMonitor: NetworkMonitor @Environment(NavigationViewModel.self) private var navigation: NavigationViewModel @State var username: String = "" @State var password: String = "" @State var isLoading: Bool = false @State var showEmailPopup: Bool = false @State var showUserCreationForm: Bool = false @State var showSubscriptionView: Bool = false @State var credentials: Credentials? @State var errorText: String? = nil @FocusState var focusedField: UserLogin? var reason: LoginReason? = nil var showEmailValidationMessage: Bool { credentials != nil } enum UserLogin { case username case password } var loginFailed: Binding { Binding { errorText != nil } set: { newValue in if newValue == false { errorText = nil } } } var handler: (CustomUser) -> () var body: some View { Form { if self.showEmailValidationMessage || (dataStore.appSettings.didCreateAccount == true && dataStore.appSettings.didRegisterAccount == false) { Section { ContentUnavailableView { Label("Vérifiez vos emails.", systemImage: "envelope.badge") } description: { Text("Vous pourrez ensuite vous connecter ici. N'oubliez pas de vérifiez vos spams !") } actions: { SupportButtonView(contentIsUnavailable: true) } } } if let reason { Section { ContentUnavailableView { Label(reason.title, systemImage: reason.systemImage) } description: { Text(reason.description) } } } Section { TextField("Nom d'utilisateur ou email", text: self.$username) .autocorrectionDisabled(true) .keyboardType(.asciiCapable) .textContentType(.init(rawValue: "")) .textInputAutocapitalization(.never) .focused($focusedField, equals: .username) .submitLabel(.next) .onSubmit(of: .text) { focusedField = .password } PasswordField("Mot de passe", text: self.$password) .submitLabel(.send) .onSubmit(of: .text) { if password.isEmpty == false && username.isEmpty == false { Task { await self._login() } } } } Section { RowButtonView("Connexion") { await self._login() } .disabled(password.isEmpty || username.isEmpty) // if let error = self.errorText { // Text(error).font(.callout).foregroundStyle(.logoRed) // } } if !self.showEmailValidationMessage { Section { RowButtonView("Créer un compte") { self.showUserCreationForm = true } .sheet(isPresented: self.$showUserCreationForm, onDismiss: { if let credentials { self.username = credentials.username self.password = credentials.password } }) { UserCreationFormView(isPresented: self.$showUserCreationForm, credentials: self.$credentials) } } Section { // NavigationLink("Créer un compte") { // UserCreationView() // } RowButtonView("Mot passe oublié") { self.showEmailPopup = true } .alert( Text("Changer de mot de passe") , isPresented: self.$showEmailPopup ) { EmailConfirmationView() } message: { Text("Veuillez entrer votre email") } } } } .alert("Un problème est survenu", isPresented: loginFailed) { Button("OK") { } } message: { let message = [networkMonitor.connected == false ? "L'appareil n'est pas connecté à internet." as String? : nil, errorText].compacted().joined(separator: "\n") Text(message) } .navigationTitle("Connexion") .onAppear { #if DEBUG if let username = PListReader.readString(plist: "local", key: "username") { self.username = username } if let password = PListReader.readString(plist: "local", key: "password") { self.password = password } #endif } } fileprivate func _login() async { self.errorText = nil // reset error self.isLoading = true do { let service = try StoreCenter.main.service() let user: CustomUser = try await service.login( username: self.username, password: self.password) self.dataStore.user = user self.isLoading = false await MainActor.run { dataStore.appSettings.didRegisterAccount = true dataStore.appSettingsStorage.write() } self.handler(user) navigation.umpirePath.removeAll() } catch { self.isLoading = false self.errorText = ErrorUtils.message(error: error) Logger.error(error) } } } struct EmailConfirmationView: View { @State var email: String = "" @State var errorMessage: String = "" var body: some View { VStack { TextField("Email", text: self.$email) .keyboardType(.emailAddress) Button { self._forgottenPassword() } label: { Text("Envoyer l'email") } } } fileprivate func _forgottenPassword() { Task { do { let service = try StoreCenter.main.service() try await service.forgotPassword(email: self.email) } catch { Logger.error(error) self.errorMessage = ErrorUtils.message(error: error) } } } } //#Preview { // NavigationStack { // LoginView(handler: { _ in }) // } //} //#Preview { // EmailConfirmationView() //}