SwiftUI/SwiftData iOS-Client, redet mit https://banyaro.app FastAPI-Backend. Bundle-ID app.banyaro.ios, Xcode-26-Projekt mit synchronisierten Ordnern. Drin: - APIClient (URLSession + Bearer + convertFromSnakeCase Decoder) - KeychainStore + AuthSession (@Observable) für persistenten Login - LoginView, MainTabView, SettingsView (mit Logout) - RoutesListView + RouteDetailView mit MapKit-Polyline aus preview_track - DogsListView mit Foto-Avatar - App-Icon (Pfote auf Banyaro-Amber)
78 lines
2.5 KiB
Swift
78 lines
2.5 KiB
Swift
import SwiftUI
|
|
|
|
struct LoginView: View {
|
|
@Environment(AuthSession.self) private var auth
|
|
|
|
@State private var email = ""
|
|
@State private var password = ""
|
|
|
|
var body: some View {
|
|
VStack(spacing: 24) {
|
|
Spacer()
|
|
|
|
Image(systemName: "pawprint.fill")
|
|
.font(.system(size: 80))
|
|
.foregroundStyle(Color.accentColor)
|
|
|
|
VStack(spacing: 6) {
|
|
Text("Ban Yaro Go")
|
|
.font(.largeTitle.bold())
|
|
Text("Melde dich mit deinem banyaro-Account an.")
|
|
.font(.callout)
|
|
.foregroundStyle(.secondary)
|
|
.multilineTextAlignment(.center)
|
|
}
|
|
|
|
VStack(spacing: 12) {
|
|
TextField("E-Mail", text: $email)
|
|
.textContentType(.emailAddress)
|
|
.keyboardType(.emailAddress)
|
|
.textInputAutocapitalization(.never)
|
|
.autocorrectionDisabled()
|
|
.padding()
|
|
.background(.background.secondary, in: RoundedRectangle(cornerRadius: 12))
|
|
|
|
SecureField("Passwort", text: $password)
|
|
.textContentType(.password)
|
|
.padding()
|
|
.background(.background.secondary, in: RoundedRectangle(cornerRadius: 12))
|
|
}
|
|
|
|
if let error = auth.errorMessage {
|
|
Text(error)
|
|
.font(.footnote)
|
|
.foregroundStyle(.red)
|
|
.multilineTextAlignment(.center)
|
|
}
|
|
|
|
Button {
|
|
Task {
|
|
await auth.login(
|
|
email: email.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
password: password
|
|
)
|
|
}
|
|
} label: {
|
|
Group {
|
|
if auth.isLoggingIn {
|
|
ProgressView().tint(.white)
|
|
} else {
|
|
Text("Login").bold()
|
|
}
|
|
}
|
|
.frame(maxWidth: .infinity, minHeight: 50)
|
|
}
|
|
.background(Color.accentColor, in: RoundedRectangle(cornerRadius: 12))
|
|
.foregroundStyle(.white)
|
|
.disabled(auth.isLoggingIn || email.isEmpty || password.isEmpty)
|
|
|
|
Spacer()
|
|
}
|
|
.padding(.horizontal, 28)
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
LoginView()
|
|
.environment(AuthSession())
|
|
}
|