import SwiftUI struct LoginView: View { @Environment(AppState.self) private var app @State private var serverURL: String = "" @State private var username: String = "" @State private var password: String = "" @State private var remember: Bool = true @State private var isLoading: Bool = false var body: some View { VStack(spacing: 16) { Spacer() Image(systemName: "books.vertical.fill") .font(.system(size: 48)) .foregroundStyle(.tint) Text("ABS Client") .font(.largeTitle).bold() Text("Verbinde dich mit deinem Audiobookshelf-Server") .foregroundStyle(.secondary) VStack(alignment: .leading, spacing: 12) { LabeledField(label: "Server-URL", placeholder: "https://abs.example.com") { TextField("", text: $serverURL) .textFieldStyle(.roundedBorder) .disableAutocorrection(true) } LabeledField(label: "Benutzername", placeholder: "user") { TextField("", text: $username) .textFieldStyle(.roundedBorder) .disableAutocorrection(true) } LabeledField(label: "Passwort", placeholder: "••••••") { SecureField("", text: $password) .textFieldStyle(.roundedBorder) } Toggle("Anmeldung merken", isOn: $remember) .toggleStyle(.checkbox) } .frame(maxWidth: 380) if let err = app.auth.errorMessage { Text(err) .foregroundStyle(.red) .font(.callout) .multilineTextAlignment(.center) .frame(maxWidth: 380) } Button(action: doLogin) { if isLoading { ProgressView().controlSize(.small) } else { Text("Einloggen") .frame(maxWidth: 200) } } .buttonStyle(.borderedProminent) .controlSize(.large) .disabled(isLoading || serverURL.isEmpty || username.isEmpty || password.isEmpty) .keyboardShortcut(.defaultAction) Spacer() } .padding(32) } private func doLogin() { isLoading = true Task { await app.auth.login( serverURL: serverURL, username: username, password: password, remember: remember ) isLoading = false } } } private struct LabeledField: View { let label: String let placeholder: String @ViewBuilder let content: Content var body: some View { VStack(alignment: .leading, spacing: 4) { Text(label).font(.subheadline).foregroundStyle(.secondary) content } } }