Files
certain/ios/Einstellungen/EinstellungenAnsicht.swift
T
ScarriffleandClaude Opus 5 6b86a6bf91 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>
2026-10-20 16:20:03 +02:00

53 lines
2.3 KiB
Swift

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) {}
}
}
}