Files
scan-receipts/app/ios/ScanReceipts/Features/Auth/LoginView.swift

110 lines
3.7 KiB
Swift

import SwiftUI
/// "Anmelden" screen the default screen of `AuthFlowView`. Talks to auth
/// state exclusively through `AppState.login(email:password:)`; never calls
/// `AuthAPI` directly (see `AppState`'s own doc comment for why).
struct LoginView: View {
@EnvironmentObject private var appState: AppState
/// Switches `AuthFlowView` to the signup screen.
var onSwitchToSignup: () -> Void
@State private var email = ""
@State private var password = ""
@State private var isSubmitting = false
private var canSubmit: Bool {
!email.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
&& !password.isEmpty
&& !isSubmitting
}
var body: some View {
ScrollView {
VStack(spacing: ZenithSpacing.md) {
VStack(spacing: ZenithSpacing.xs) {
Image(systemName: "doc.text.viewfinder")
.font(.system(size: 40))
.foregroundStyle(Color.zenithBlack)
Text("ScanReceipts")
.zenithHeadlineLgStyle()
}
.padding(.top, ZenithSpacing.lg)
VStack(spacing: ZenithSpacing.sm) {
ZenithTextField(
label: "E-Mail",
text: $email,
keyboardType: .emailAddress,
textContentType: .username,
autocapitalization: .never,
autocorrectionDisabled: true
)
ZenithTextField(
label: "Passwort",
text: $password,
isSecure: true,
textContentType: .password
)
}
if let error = appState.lastError {
Text(error)
.zenithBodySmStyle(color: .zenithError)
.multilineTextAlignment(.center)
}
Button {
Task { await submit() }
} label: {
Group {
if isSubmitting {
ProgressView()
.tint(.white)
} else {
Text("Anmelden")
}
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.zenithPrimary)
.disabled(!canSubmit)
HStack(spacing: ZenithSpacing.sm) {
ZenithDivider()
Text("oder")
.zenithBodySmStyle()
ZenithDivider()
}
// No outer `.frame(height:)` here AppleSignInButton sizes
// its own button (minHeight: 50) and needs the flexibility
// to grow when it shows its own inline error text below it.
AppleSignInButton()
Button("Noch kein Konto? Registrieren") {
appState.lastError = nil
onSwitchToSignup()
}
.buttonStyle(.zenithPlain)
.padding(.top, ZenithSpacing.xs)
}
.padding(.horizontal, ZenithSpacing.md)
.padding(.bottom, ZenithSpacing.lg)
}
.scrollDismissesKeyboard(.interactively)
.background(Color.zenithBg)
}
private func submit() async {
guard canSubmit else { return }
isSubmitting = true
defer { isSubmitting = false }
await appState.login(
email: email.trimmingCharacters(in: .whitespacesAndNewlines),
password: password
)
}
}