pageta-native/Sources/Features/AccountTabView.swift
Till JS 3c22bf7f65 Feedback: prominente Karte auf der Konto-Seite
ManaFeedbackButton-Zeile im extra-Slot ersetzt durch die geteilte
ManaFeedbackCard an fester Position via ManaAccountView.feedbackClient.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 01:28:57 +02:00

112 lines
3.9 KiB
Swift

import ManaAuthUI
import ManaCore
import ManaLLMUI
import ManaTokens
import SwiftUI
/// Konto-Tab — vereinheitlicht über das geteilte `ManaAccountView`
/// (E-Mail/Passwort ändern, 2FA, Konto löschen, Abmelden, Sign-In-CTA).
/// Pageta-spezifische Sektionen (Darstellung, Badge, lokale KI, Feedback,
/// Über) hängen im `extra`-Slot und sind — soft-auth — immer sichtbar.
struct AccountTabView: View {
@Environment(AuthClient.self) private var auth
@Environment(ManaAuthGate.self) private var authGate
@AppStorage(BadgeManager.userDefaultsKey) private var badgeEnabled = false
@AppStorage(ManaTheme.storageKey) private var variantRaw: String = ManaTheme.pagetaDefault
.rawValue
private var theme: ManaTheme { ManaTheme(rawValue: variantRaw) ?? .paper }
/// Kontext für die geteilten `ManaLLMUI`-Sections — Pageta-spezifisch.
/// Wird auch von der macOS-`SettingsView` konsumiert, deshalb static.
static let llmContext = ManaLLMContext(
useCaseShort: "Artikel-Zusammenfassung",
useCaseLong: "fasst Artikel auf Knopfdruck in zwei Sätze zusammen"
)
var body: some View {
NavigationStack {
ManaAccountView(
auth: auth,
config: .init(
changeEmailCallbackLink: URL(string: "https://pageta.com/auth/email-changed"),
signedOutMessage: "Marker, Notizen und dein Stapel funktionieren auch "
+ "ohne Konto — alles bleibt lokal auf diesem Gerät. Mit Anmeldung wird es "
+ "geräteübergreifend synchronisiert und gesichert; bestehende lokale Daten "
+ "werden dabei übernommen."
),
onSignIn: { authGate.require(reason: "konto") {} },
onSignOut: {
// Spotlight-Index leeren, sonst leaken die saved-Articles
// über Account-Wechsel.
await SpotlightIndex().clearAll()
await auth.signOut()
},
// Feedback an fester Position (geteilte ManaFeedbackCard, direkt
// nach dem Konto-Block) statt unscheinbar tief im extra-Slot.
feedbackClient: FeedbackClient(appId: "pageta") { try? await auth.freshAccessToken() },
extra: { appSections }
)
.navigationTitle("Konto")
.paperBackground()
.manaBrand(PagetaBrand.config(for: theme))
}
}
/// Pageta-spezifische Sektionen — immer sichtbar (auch ausgeloggt).
@ViewBuilder
private var appSections: some View {
Section {
PagetaAppearancePicker()
.listRowInsets(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
PagetaThemeGallery()
.listRowInsets(EdgeInsets(top: 4, leading: 16, bottom: 12, trailing: 16))
} header: {
Text("Darstellung")
} footer: {
Text(
"„Automatisch\" folgt dem System (Hell/Dunkel). Das Thema bestimmt die Farbwelt — frei wählbar, deine Wahl gilt sofort in der ganzen App."
)
}
Section {
Toggle("App-Icon-Badge zeigen", isOn: $badgeEnabled)
.onChange(of: badgeEnabled) { _, enabled in
Task { await handleBadgeToggle(enabled: enabled) }
}
} header: {
Text("Benachrichtigungen")
} footer: {
Text(
"Zeigt die Anzahl ungelesener Artikel am App-Icon. Beim Aktivieren wird die System-Berechtigung abgefragt."
)
}
ManaLLMSettingsView(context: AccountTabView.llmContext)
Section("Über") {
LabeledContent(
"Version",
value: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.1.0"
)
LabeledContent(
"Build",
value: Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "1"
)
Link("Web-App", destination: URL(string: "https://pageta.com")!)
Link("Verein", destination: URL(string: "https://mana-ev.ch")!)
}
}
/// Wenn der User Badge aktiviert: System-Permission anfragen, bei
/// Denial das Toggle zurücksetzen. Wenn deaktiviert: Badge auf 0
/// löschen, damit die alte Zahl nicht stehenbleibt.
private func handleBadgeToggle(enabled: Bool) async {
if enabled {
let granted = await BadgeManager.requestPermission()
if !granted {
badgeEnabled = false
}
} else {
await BadgeManager.setBadge(count: 0)
}
}
}