import SwiftUI struct SettingsView: View { @Environment(AuthSession.self) private var auth @AppStorage("autoPauseEnabled") private var autoPauseEnabled = true @AppStorage("healthKitSyncEnabled") private var healthKitSyncEnabled = false @State private var showHealthPermissionAlert = false var body: some View { NavigationStack { Form { Section { HStack(spacing: 14) { avatarView .frame(width: 60, height: 60) .clipShape(Circle()) VStack(alignment: .leading, spacing: 2) { Text(displayName) .font(.headline) if let email = auth.profile?.email { Text(email) .font(.footnote) .foregroundStyle(.secondary) } } } .padding(.vertical, 4) } Section("Hund & Alltag") { NavigationLink { DogsListView() } label: { Label("Meine Hunde", systemImage: "pawprint.fill") } NavigationLink { TagebuchView() } label: { Label("Tagebuch", systemImage: "book.fill") } NavigationLink { ErsteHilfeView() } label: { Label("Erste Hilfe", systemImage: "cross.case.fill") } NavigationLink { WetterView() } label: { Label("Wetter", systemImage: "cloud.sun.fill") } NavigationLink { GassiZeitenView() } label: { Label("Gassi-Zeiten", systemImage: "alarm.fill") } NavigationLink { GiftkoederView() } label: { Label("Giftköder", systemImage: "exclamationmark.octagon.fill") } NavigationLink { VerloreneHundeView() } label: { Label("Verlorene Hunde", systemImage: "magnifyingglass.circle.fill") } NavigationLink { AusgabenView() } label: { Label("Ausgaben", systemImage: "eurosign.circle.fill") } } Section("Account") { LabeledContent("Rolle", value: rolleLabel) if auth.profile?.isFounderFlag == true { LabeledContent("Founder", value: founderLabel) } if auth.profile?.isPartnerFlag == true { LabeledContent("Partner", value: "Ja") } if let tier = auth.profile?.subscriptionTier, !tier.isEmpty { LabeledContent("Abo", value: tier.capitalized) } LabeledContent("Premium", value: premiumValue ? "Ja" : "Nein") } if let wohnort = auth.profile?.wohnort, !wohnort.isEmpty { Section("Ort") { Text(wohnort) } } Section { Toggle(isOn: $autoPauseEnabled) { Label("Auto-Pause", systemImage: "pause.circle") } Toggle(isOn: $healthKitSyncEnabled) { Label("Apple Health Sync", systemImage: "heart.fill") } .onChange(of: healthKitSyncEnabled) { _, newValue in if newValue { Task { let granted = await WalkHealthSync.shared.requestAuthorization() if !granted { healthKitSyncEnabled = false showHealthPermissionAlert = true } } } } } header: { Text("Aufnahme") } footer: { Text("Auto-Pause: pausiert die Aufnahme, wenn du 2 Minuten lang stehen bleibst.\nApple Health: schreibt jede gespeicherte Tour als Spaziergang-Workout mit Route in Health.") } Section("Mehr auf banyaro.app") { pwaLink("Forum", systemImage: "bubble.left.and.bubble.right.fill", fragment: "forum") pwaLink("Hunde-Profile bearbeiten", systemImage: "pawprint.fill", fragment: "dogs") pwaLink("Gassi-Treffen", systemImage: "person.2.fill", fragment: "walks") pwaLink("Profil & Einstellungen", systemImage: "gearshape.fill", fragment: "settings") } Section { if let url = URL(string: "https://banyaro.app") { Link(destination: url) { Label("banyaro.app in Safari öffnen", systemImage: "safari.fill") .foregroundStyle(.primary) } } } header: { Text("banyaro.app als Web-App") } footer: { VStack(alignment: .leading, spacing: 8) { Text("Du kannst banyaro.app zusätzlich als Web-App auf deinem Home-Bildschirm ablegen — praktisch für alle Features, die diese App nicht abbildet.") HStack(alignment: .firstTextBaseline, spacing: 4) { Text("**1.**") Text("Oben „in Safari öffnen“ tippen") } HStack(alignment: .firstTextBaseline, spacing: 4) { Text("**2.**") HStack(spacing: 4) { Text("In Safari unten das Teilen-Icon") Image(systemName: "square.and.arrow.up") Text("antippen") } } HStack(alignment: .firstTextBaseline, spacing: 4) { Text("**3.**") HStack(spacing: 4) { Text("„Zum Home-Bildschirm“") Image(systemName: "plus.square") Text("auswählen") } } Text("Eine native App kann eine Web-App leider nicht selbst installieren — Apple lässt das nur über Safari zu.") .padding(.top, 4) .foregroundStyle(.tertiary) } } Section { Button("Abmelden", role: .destructive) { auth.logout() } } Section("Über") { Text("Ban Yaro Go ist die native iOS-Ergänzung zur banyaro.app PWA. Phase 1: deine Touren ansehen.") .font(.footnote) .foregroundStyle(.secondary) } } .navigationTitle("Mehr") .refreshable { await auth.loadProfile() } .alert("Apple Health hat den Zugriff verweigert", isPresented: $showHealthPermissionAlert) { Button("OK", role: .cancel) {} } message: { Text("Du kannst die Berechtigung in den iOS-Einstellungen unter Datenschutz & Sicherheit → Health → Ban Yaro Go nachträglich ändern.") } } } private var displayName: String { auth.profile?.name ?? auth.userName ?? "—" } private var premiumValue: Bool { auth.profile?.isPremium ?? auth.isPremium } private var rolleLabel: String { switch auth.profile?.rolle?.lowercased() { case "admin": return "Admin" case "moderator": return "Moderator" case nil: return "—" default: return "Mitglied" } } private var founderLabel: String { if let n = auth.profile?.founderNumber { return "#\(n)" } return "Ja" } @ViewBuilder private var avatarView: some View { if let path = auth.profile?.avatarUrl, !path.isEmpty, let url = avatarURL(path) { AsyncImage(url: url) { phase in switch phase { case .success(let img): img.resizable().scaledToFill() default: avatarPlaceholder } } } else { avatarPlaceholder } } private var avatarPlaceholder: some View { ZStack { Color.accentColor.opacity(0.2) Image(systemName: "person.crop.circle.fill") .font(.system(size: 36)) .foregroundStyle(Color.accentColor) } } private func avatarURL(_ path: String) -> URL? { if path.hasPrefix("http") { return URL(string: path) } return URL(string: "https://banyaro.app\(path)") } @ViewBuilder private func pwaLink(_ title: String, systemImage: String, fragment: String) -> some View { if let url = URL(string: "https://banyaro.app/#\(fragment)") { Link(destination: url) { HStack { Label(title, systemImage: systemImage) .foregroundStyle(.primary) Spacer() Image(systemName: "arrow.up.right.square") .font(.caption) .foregroundStyle(.tertiary) } } } } }