feat: Erste Stufe der iOS-App - Anmeldung und Kontenliste
Neuer Ordner ios/ mit dem Geruest: Netzwerkschicht, Modelle, Offline-Ablage, Texte in Deutsch und Englisch, Navigation, der komplette Anmeldeweg (Server waehlen, anmelden, zweiter Faktor, Pflicht-Passwortwechsel) und als erste Fachansicht die Kontenliste. Die Anmeldung laeuft einmal ueber das Sitzungs-Cookie, holt sich dabei eine App-Marke und legt sie in den Schluesselbund. Danach traegt nur noch der Header. Kein Passwort auf dem Geraet, kein Ablauf nach 30 Tagen. Der Code ist nie uebersetzt worden - hier gibt es kein Xcode. Geprueft ist dafuer jedes Modellfeld gegen echte Antworten des laufenden Backends; dabei kam heraus, dass created_at ohne Zeitzone und mit Mikrosekunden kommt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
8a7545bf5b
commit
6b86a6bf91
@@ -0,0 +1,52 @@
|
||||
import SwiftUI
|
||||
|
||||
/// Stufe 1 zeigt hier nur, woran man ist, und lässt abmelden. Die echten
|
||||
/// Einstellungen (Hauptwährung, Budget-Starttag, Sprache, Gerätemarken)
|
||||
/// kommen in Stufe 5.
|
||||
struct EinstellungenAnsicht: View {
|
||||
@Environment(AppZustand.self) private var zustand
|
||||
@State private var zeigeAbmeldefrage = false
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section {
|
||||
if case .bereit(let benutzer) = zustand.lage {
|
||||
LabeledContent(String(localized: "einstellungen.angemeldetAls"),
|
||||
value: benutzer.anzeigename)
|
||||
}
|
||||
if let adresse = zustand.serverAdresse {
|
||||
LabeledContent(String(localized: "einstellungen.server"),
|
||||
value: adresse.host() ?? adresse.absoluteString)
|
||||
}
|
||||
} header: {
|
||||
Text(String(localized: "einstellungen.zugang"))
|
||||
} footer: {
|
||||
Text(String(localized: "einstellungen.markeErklaerung"))
|
||||
}
|
||||
|
||||
Section(String(localized: "einstellungen.ausDemServer")) {
|
||||
LabeledContent(String(localized: "einstellungen.hauptwaehrung"),
|
||||
value: zustand.einstellungen.basiswaehrung)
|
||||
LabeledContent(String(localized: "einstellungen.budgetStarttag"),
|
||||
value: "\(zustand.einstellungen.budgetStarttag)")
|
||||
}
|
||||
|
||||
Section {
|
||||
Button(String(localized: "allgemein.abmelden"), role: .destructive) {
|
||||
zeigeAbmeldefrage = true
|
||||
}
|
||||
} footer: {
|
||||
Text(String(localized: "einstellungen.abmeldenErklaerung"))
|
||||
}
|
||||
}
|
||||
.navigationTitle(String(localized: "reiter.einstellungen"))
|
||||
.refreshable { await zustand.aktualisiereEinstellungen() }
|
||||
.confirmationDialog(String(localized: "einstellungen.abmeldenFrage"),
|
||||
isPresented: $zeigeAbmeldefrage, titleVisibility: .visible) {
|
||||
Button(String(localized: "allgemein.abmelden"), role: .destructive) {
|
||||
Task { await zustand.meldeAb() }
|
||||
}
|
||||
Button(String(localized: "allgemein.abbrechen"), role: .cancel) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user