import Combine import Martin import SwiftUI struct LoginScreen: View { @EnvironmentObject var navigation: NavigationStore @EnvironmentObject var clientsStore: ClientsStore enum Field { case userJid case password } @FocusState private var focus: Field? @State private var isLoading = false @State private var isError = false #if DEBUG @State private var jidStr: String = "nartest1@conversations.im" @State private var pass: String = "nartest12345" // @State private var jidStr: String = "test1@test.anal.company" // @State private var pass: String = "12345" #else @State private var jidStr: String = "" @State private var pass: String = "" #endif public var body: some View { ZStack { // background Color.Material.Background.light .ignoresSafeArea() // content VStack(spacing: 32) { // icon Image.logo .resizable() .aspectRatio(contentMode: .fit) .frame(width: 120, height: 120) // texts VStack(spacing: 10) { Text(L10n.Login.title) .font(.head1l) .foregroundColor(.Material.Text.main) .fixedSize(horizontal: true, vertical: false) Text(L10n.Login.subtitle) .font(.body2) .foregroundColor(.Material.Text.sub) .multilineTextAlignment(.center) .fixedSize(horizontal: false, vertical: true) } VStack(spacing: 16) { UniversalInputCollection.TextField( prompt: L10n.Login.Hint.jid, text: $jidStr, focus: $focus, fieldType: .userJid, contentType: .emailAddress, keyboardType: .emailAddress, submitLabel: .next, action: { focus = .password } ) UniversalInputCollection.SecureField( prompt: L10n.Login.Hint.password, text: $pass, focus: $focus, fieldType: .password, submitLabel: .go, action: { focus = nil } ) Button { Task { await tryLogin() } } label: { Text(L10n.Login.btn) } .buttonStyle(PrimaryButtonStyle()) .disabled(!loginInputValid) Button { withAnimation { navigation.flow = .entering(.welcome) } } label: { Text("\(Image(systemName: "chevron.left")) \(L10n.Global.back)") .foregroundColor(.Material.Elements.active) .font(.body2) } } } .padding(.horizontal, 32) } .if(isLoading) { $0.loadingOverlay() } .alert(isPresented: $isError) { Alert( title: Text(L10n.Global.Error.title), message: Text(L10n.Login.error), dismissButton: .default(Text(L10n.Global.ok)) ) } } private var loginInputValid: Bool { !jidStr.isEmpty && !pass.isEmpty && UniversalInputCollection.Validators.isEmail(jidStr) } private func tryLogin() async { isLoading = true // login with fake timeout async let sleep: Void? = try? await Task.sleep(nanoseconds: 1 * NSEC_PER_SEC) async let request = await Client.tryLogin(with: .init(bareJid: jidStr, pass: pass, isActive: true)) let result = await(request, sleep).0 switch result { case .success(let client): clientsStore.addNewClient(client) isLoading = false isError = false if navigation.flow == .entering(.login) { navigation.flow = .main(.contacts) } case .failure: isLoading = false isError = true } } }