2024-06-19 15:15:27 +00:00
|
|
|
import Combine
|
|
|
|
import Martin
|
|
|
|
import SwiftUI
|
|
|
|
|
2024-08-11 00:28:01 +00:00
|
|
|
struct LoginScreen: View {
|
|
|
|
@EnvironmentObject var navigation: NavigationStore
|
|
|
|
@EnvironmentObject var clientsStore: ClientsStore
|
2024-06-19 15:15:27 +00:00
|
|
|
|
|
|
|
enum Field {
|
|
|
|
case userJid
|
|
|
|
case password
|
|
|
|
}
|
|
|
|
|
|
|
|
@FocusState private var focus: Field?
|
2024-08-11 00:28:01 +00:00
|
|
|
|
|
|
|
@State private var isLoading = false
|
|
|
|
@State private var isError = false
|
2024-06-19 15:15:27 +00:00
|
|
|
|
|
|
|
#if DEBUG
|
2024-07-22 12:02:33 +00:00
|
|
|
@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"
|
2024-06-19 15:15:27 +00:00
|
|
|
#else
|
|
|
|
@State private var jidStr: String = ""
|
|
|
|
@State private var pass: String = ""
|
|
|
|
#endif
|
|
|
|
|
|
|
|
public var body: some View {
|
|
|
|
ZStack {
|
|
|
|
// background
|
2024-07-04 08:21:12 +00:00
|
|
|
Color.Material.Background.light
|
2024-06-19 15:15:27 +00:00
|
|
|
.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)
|
2024-07-04 08:21:12 +00:00
|
|
|
.foregroundColor(.Material.Text.main)
|
2024-06-19 15:15:27 +00:00
|
|
|
.fixedSize(horizontal: true, vertical: false)
|
|
|
|
Text(L10n.Login.subtitle)
|
|
|
|
.font(.body2)
|
2024-07-04 08:21:12 +00:00
|
|
|
.foregroundColor(.Material.Text.sub)
|
2024-06-19 15:15:27 +00:00
|
|
|
.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 {
|
2024-08-11 00:28:01 +00:00
|
|
|
Task {
|
|
|
|
await tryLogin()
|
|
|
|
}
|
2024-06-19 15:15:27 +00:00
|
|
|
} label: {
|
|
|
|
Text(L10n.Login.btn)
|
|
|
|
}
|
|
|
|
.buttonStyle(PrimaryButtonStyle())
|
|
|
|
.disabled(!loginInputValid)
|
|
|
|
|
|
|
|
Button {
|
2024-08-11 00:28:01 +00:00
|
|
|
withAnimation {
|
|
|
|
navigation.flow = .entering(.welcome)
|
|
|
|
}
|
2024-06-19 15:15:27 +00:00
|
|
|
} label: {
|
|
|
|
Text("\(Image(systemName: "chevron.left")) \(L10n.Global.back)")
|
2024-07-04 08:21:12 +00:00
|
|
|
.foregroundColor(.Material.Elements.active)
|
2024-06-19 15:15:27 +00:00
|
|
|
.font(.body2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.padding(.horizontal, 32)
|
|
|
|
}
|
2024-08-11 00:28:01 +00:00
|
|
|
.if(isLoading) {
|
|
|
|
$0.loadingOverlay()
|
|
|
|
}
|
|
|
|
.alert(isPresented: $isError) {
|
2024-06-19 15:15:27 +00:00
|
|
|
Alert(
|
|
|
|
title: Text(L10n.Global.Error.title),
|
2024-08-11 00:28:01 +00:00
|
|
|
message: Text(L10n.Login.error),
|
|
|
|
dismissButton: .default(Text(L10n.Global.ok))
|
2024-06-19 15:15:27 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private var loginInputValid: Bool {
|
|
|
|
!jidStr.isEmpty && !pass.isEmpty && UniversalInputCollection.Validators.isEmail(jidStr)
|
|
|
|
}
|
2024-08-11 00:28:01 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2024-06-19 15:15:27 +00:00
|
|
|
}
|