feat(auth): ManaAuthUI-Migration — vollständige Auth-Reise nativ
Phase 4a aus dem Native-Auth-Vollausbau-Plan. - project.yml: ManaSwiftUI/ManaAuthUI als Package-Dep - Sources/Core/Theme/CardsBrand.swift: Bridge zwischen CardsTheme (forest-HSL) und ManaBrandConfig — wird im RootView via .manaBrand(...) gesetzt - Sources/App/RootView.swift: alte LoginView() durch ManaLoginView ersetzt, Sheets für SignUp/ForgotPassword/ResetPassword. Universal- Link-Handler erweitert um /auth/reset?token=… → ManaResetPasswordView - Sources/Features/Account/LoginView.swift: gelöscht — komplett durch ManaLoginView aus ManaAuthUI abgedeckt - Sources/Features/Account/AccountView.swift: Email-ändern + PW-ändern + Account-löschen Sheets (App-Store-Guideline 5.1.1(v) erfüllt) BUILD SUCCEEDED gegen mana-swift-core@v1.1.0 und mana-swift-ui@v0.1.0. Account-Sheets (Change/Delete) funktionieren erst nach Phase-3- Server-PR (Bearer-Plugin in mana-auth) — UI ist fertig, Wire ist fertig, Server zieht nach. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
710ede6acd
commit
da6679770b
5 changed files with 173 additions and 99 deletions
|
|
@ -1,13 +1,17 @@
|
|||
import ManaAuthUI
|
||||
import ManaCore
|
||||
import SwiftUI
|
||||
|
||||
struct AccountView: View {
|
||||
@Environment(AuthClient.self) private var auth
|
||||
@State private var showChangeEmail = false
|
||||
@State private var showChangePassword = false
|
||||
@State private var showDeleteAccount = false
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
CardsTheme.background.ignoresSafeArea()
|
||||
VStack(spacing: 24) {
|
||||
VStack(spacing: 20) {
|
||||
Image(systemName: "person.crop.circle.fill")
|
||||
.resizable()
|
||||
.frame(width: 80, height: 80)
|
||||
|
|
@ -19,20 +23,24 @@ struct AccountView: View {
|
|||
.foregroundStyle(CardsTheme.foreground)
|
||||
}
|
||||
|
||||
NavigationLink {
|
||||
SettingsView()
|
||||
} label: {
|
||||
Label("Einstellungen", systemImage: "gear")
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 12)
|
||||
.background(CardsTheme.surface, in: RoundedRectangle(cornerRadius: 8))
|
||||
.foregroundStyle(CardsTheme.foreground)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.stroke(CardsTheme.border, lineWidth: 1)
|
||||
)
|
||||
VStack(spacing: 12) {
|
||||
NavigationLink {
|
||||
SettingsView()
|
||||
} label: {
|
||||
rowLabel("Einstellungen", systemImage: "gear")
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
|
||||
Button { showChangeEmail = true } label: {
|
||||
rowLabel("Email ändern", systemImage: "envelope")
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
|
||||
Button { showChangePassword = true } label: {
|
||||
rowLabel("Passwort ändern", systemImage: "key")
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(.horizontal, 32)
|
||||
|
||||
Spacer()
|
||||
|
|
@ -47,6 +55,17 @@ struct AccountView: View {
|
|||
.foregroundStyle(CardsTheme.error)
|
||||
}
|
||||
.padding(.horizontal, 32)
|
||||
|
||||
// App-Store-Guideline 5.1.1(v): jede App mit Sign-Up MUSS
|
||||
// eine Account-Löschung anbieten.
|
||||
Button(role: .destructive) {
|
||||
showDeleteAccount = true
|
||||
} label: {
|
||||
Text("Account löschen…")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(CardsTheme.mutedForeground)
|
||||
}
|
||||
.padding(.bottom, 16)
|
||||
}
|
||||
.padding(.top, 48)
|
||||
}
|
||||
|
|
@ -54,6 +73,43 @@ struct AccountView: View {
|
|||
#if os(iOS)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
#endif
|
||||
.manaBrand(CardsBrand.manaBrand)
|
||||
.sheet(isPresented: $showChangeEmail) {
|
||||
ManaChangeEmailView(
|
||||
auth: auth,
|
||||
callbackUniversalLink: URL(string: "https://cardecky.mana.how/auth/email-changed"),
|
||||
onDone: { showChangeEmail = false }
|
||||
)
|
||||
.manaBrand(CardsBrand.manaBrand)
|
||||
}
|
||||
.sheet(isPresented: $showChangePassword) {
|
||||
ManaChangePasswordView(
|
||||
auth: auth,
|
||||
onDone: { showChangePassword = false }
|
||||
)
|
||||
.manaBrand(CardsBrand.manaBrand)
|
||||
}
|
||||
.sheet(isPresented: $showDeleteAccount) {
|
||||
ManaDeleteAccountView(
|
||||
auth: auth,
|
||||
onDone: { showDeleteAccount = false }
|
||||
)
|
||||
.manaBrand(CardsBrand.manaBrand)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func rowLabel(_ title: String, systemImage: String) -> some View {
|
||||
Label(title, systemImage: systemImage)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.vertical, 12)
|
||||
.padding(.horizontal, 16)
|
||||
.background(CardsTheme.surface, in: RoundedRectangle(cornerRadius: 8))
|
||||
.foregroundStyle(CardsTheme.foreground)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.stroke(CardsTheme.border, lineWidth: 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
import ManaCore
|
||||
import SwiftUI
|
||||
|
||||
struct LoginView: View {
|
||||
@Environment(AuthClient.self) private var auth
|
||||
@State private var email = ""
|
||||
@State private var password = ""
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
CardsTheme.background.ignoresSafeArea()
|
||||
VStack(spacing: 24) {
|
||||
Text("Cardecky")
|
||||
.font(.system(size: 48, weight: .bold))
|
||||
.foregroundStyle(CardsTheme.primary)
|
||||
Text("Karteikarten des Vereins mana e.V.")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(CardsTheme.mutedForeground)
|
||||
|
||||
VStack(spacing: 12) {
|
||||
TextField("Email", text: $email)
|
||||
.textContentType(.emailAddress)
|
||||
.keyboardType(.emailAddress)
|
||||
.textInputAutocapitalization(.never)
|
||||
.autocorrectionDisabled()
|
||||
.padding(.vertical, 12)
|
||||
.padding(.horizontal, 16)
|
||||
.background(CardsTheme.surface, in: RoundedRectangle(cornerRadius: 8))
|
||||
|
||||
SecureField("Passwort", text: $password)
|
||||
.textContentType(.password)
|
||||
.padding(.vertical, 12)
|
||||
.padding(.horizontal, 16)
|
||||
.background(CardsTheme.surface, in: RoundedRectangle(cornerRadius: 8))
|
||||
}
|
||||
.padding(.horizontal, 32)
|
||||
|
||||
Button {
|
||||
Task { await auth.signIn(email: email, password: password) }
|
||||
} label: {
|
||||
HStack {
|
||||
if case .signingIn = auth.status {
|
||||
ProgressView()
|
||||
.controlSize(.small)
|
||||
.tint(CardsTheme.primaryForeground)
|
||||
}
|
||||
Text("Anmelden")
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 14)
|
||||
.background(CardsTheme.primary, in: RoundedRectangle(cornerRadius: 8))
|
||||
.foregroundStyle(CardsTheme.primaryForeground)
|
||||
}
|
||||
.padding(.horizontal, 32)
|
||||
.disabled(isSigningIn || email.isEmpty || password.isEmpty)
|
||||
|
||||
if case let .error(message) = auth.status {
|
||||
Text(message)
|
||||
.font(.footnote)
|
||||
.foregroundStyle(CardsTheme.error)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 32)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var isSigningIn: Bool {
|
||||
if case .signingIn = auth.status { return true }
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
LoginView()
|
||||
.environment(AuthClient(config: AppConfig.manaAppConfig))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue