Alle UI-Strings der Feature-/App-Schicht ziehen aus mana/i18n (Listen/Detail/Marker/Feed/Rückblick/Zusammenfassen/Settings/Account + Archiv/Bulk/Note-Sheet/Palette); Interpolation als generierte Funktionen. Verwaisten-Check grün (0 hartkodierte UI-Strings). Onboarding bleibt seine eigene SOT (mana/content/onboarding). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
141 lines
3.1 KiB
Swift
141 lines
3.1 KiB
Swift
#if os(macOS)
|
|
|
|
import ManaAuthUI
|
|
import ManaCore
|
|
import ManaLLMUI
|
|
import SwiftUI
|
|
|
|
/// macOS-Settings-Scene (`Settings { ... }` in `PagetaNativeApp`),
|
|
/// erreichbar über **⌘,** — der erwartete native Pattern für
|
|
/// App-Konfiguration auf dem Mac. Vier Sub-Tabs:
|
|
///
|
|
/// - **Konto**: Email-Status, Abmelden
|
|
/// - **Darstellung**: Light/Dark/Automatisch-Picker
|
|
/// - **Benachrichtigungen**: App-Icon-Badge
|
|
/// - **KI**: lokales LLM für Artikel-Zusammenfassung (geteilt mit
|
|
/// anderen mana-Apps via App-Group)
|
|
///
|
|
/// Auf iOS lebt die identische Konfig im normalen Konto-Tab
|
|
/// — `AccountTabView` ist dort der Settings-Hub.
|
|
struct SettingsView: View {
|
|
var body: some View {
|
|
TabView {
|
|
AccountSettingsPane()
|
|
.tabItem { Label("Konto", systemImage: "person.crop.circle") }
|
|
|
|
AppearanceSettingsPane()
|
|
.tabItem { Label("Darstellung", systemImage: "paintbrush") }
|
|
|
|
NotificationSettingsPane()
|
|
.tabItem { Label("Benachrichtigungen", systemImage: "bell") }
|
|
|
|
AISettingsPane()
|
|
.tabItem { Label("KI", systemImage: "sparkles") }
|
|
}
|
|
.frame(width: 520, height: 360)
|
|
}
|
|
}
|
|
|
|
private struct AISettingsPane: View {
|
|
var body: some View {
|
|
Form {
|
|
ManaLLMSettingsView(context: AccountTabView.llmContext)
|
|
}
|
|
.formStyle(.grouped)
|
|
.padding(20)
|
|
}
|
|
}
|
|
|
|
private struct AccountSettingsPane: View {
|
|
@Environment(AuthClient.self) private var auth
|
|
@Environment(ManaAuthGate.self) private var authGate
|
|
|
|
private var isSignedIn: Bool {
|
|
if case .signedIn = auth.status { return true }
|
|
return false
|
|
}
|
|
|
|
var body: some View {
|
|
Form {
|
|
Section {
|
|
if let email = auth.currentEmail {
|
|
LabeledContent("E-Mail", value: email)
|
|
} else {
|
|
Text(PagetaStrings.settingsNotSignedIn)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
|
|
Section {
|
|
if isSignedIn {
|
|
HStack {
|
|
Spacer()
|
|
Button("Abmelden", role: .destructive) {
|
|
Task {
|
|
await SpotlightIndex().clearAll()
|
|
await auth.signOut()
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
Button {
|
|
authGate.require(reason: "konto") {}
|
|
} label: {
|
|
Label(
|
|
PagetaStrings.settingsSignIn,
|
|
systemImage: "person.crop.circle.badge.checkmark"
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.formStyle(.grouped)
|
|
.padding(20)
|
|
}
|
|
}
|
|
|
|
private struct AppearanceSettingsPane: View {
|
|
var body: some View {
|
|
Form {
|
|
PagetaAppearanceSection()
|
|
}
|
|
.formStyle(.grouped)
|
|
.padding(20)
|
|
}
|
|
}
|
|
|
|
private struct NotificationSettingsPane: View {
|
|
@AppStorage(BadgeManager.userDefaultsKey) private var badgeEnabled = false
|
|
|
|
var body: some View {
|
|
Form {
|
|
Section {
|
|
Toggle("App-Icon-Badge zeigen", isOn: $badgeEnabled)
|
|
.onChange(of: badgeEnabled) { _, enabled in
|
|
Task { await handleBadgeToggle(enabled: enabled) }
|
|
}
|
|
} footer: {
|
|
Text(
|
|
PagetaStrings.settingsBadgeHintMac
|
|
)
|
|
.font(.footnote)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
.formStyle(.grouped)
|
|
.padding(20)
|
|
}
|
|
|
|
private func handleBadgeToggle(enabled: Bool) async {
|
|
if enabled {
|
|
let granted = await BadgeManager.requestPermission()
|
|
if !granted {
|
|
badgeEnabled = false
|
|
}
|
|
} else {
|
|
await BadgeManager.setBadge(count: 0)
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|