import ManaCore import SwiftUI /// Top-Level-View: TabView mit drei Tabs. /// /// **Phase ζ-1:** Lesen + Erkunden laden `zitare.com` via `WebShellView`. /// Universal-Links auf `zitare.com/q/` / `/a/` etc. öffnen /// die App und routen in den passenden Tab. struct RootView: View { @Environment(AuthClient.self) private var auth @State private var selectedTab: AppTab = .read @State private var readTarget = WebTarget(url: AppConfig.webBaseURL) @State private var exploreTarget = WebTarget( url: AppConfig.webBaseURL.appendingPathComponent("explore") ) @State private var reloadCounter: Int = 0 @State private var healthStatus: HealthStatus = .unknown var body: some View { TabView(selection: $selectedTab) { WebShellView(target: readTarget) .tabItem { Label("Lesen", systemImage: "book") } .tag(AppTab.read) WebShellView(target: exploreTarget) .tabItem { Label("Erkunden", systemImage: "sparkle.magnifyingglass") } .tag(AppTab.explore) AccountView(healthStatus: healthStatus) .tabItem { Label("Konto", systemImage: "person.circle") } .tag(AppTab.account) } .task { await probeHealth() } .onOpenURL { url in handle(url: url) } .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { activity in if let url = activity.webpageURL { handle(url: url) } } } private func probeHealth() async { let api = ZitareAPI(auth: auth) do { let ok = try await api.healthCheck() healthStatus = ok ? .ok : .down Log.api.info("Healthz: \(ok ? "OK" : "DOWN")") } catch { healthStatus = .down Log.api.warning( "Healthz fehlgeschlagen: \(String(describing: error), privacy: .public)" ) } } /// Universal-Link- und Custom-URL-Routing. Wird sowohl von /// `onOpenURL` (Custom-Scheme `zitare://...`) als auch von /// `onContinueUserActivity` (Universal-Links auf `zitare.com/...`) /// aufgerufen. /// /// Routing-Regeln (gespiegelt zu `app-manifest.json#link_patterns`): /// - `/q/`, `/a/`, `/c/` → Lesen-Tab /// - `/heute`, `/random`, `/feed.rss` → Lesen-Tab /// - `/explore`, `/region/...`, `/thema/...`, `/rolle/...`, /// `/epoche/...`, `/sprache/...`, `/search`, `/t/...` → Erkunden-Tab /// - alles andere unter `zitare.com` → Lesen-Tab, Root-Pfad /// /// Custom-Scheme `zitare://quote/` wird auf /// `https://zitare.com/q/` umgemappt. private func handle(url: URL) { Log.app.info("Deep-Link empfangen: \(url.absoluteString, privacy: .public)") let routed = DeepLinkRouter.route(url, base: AppConfig.webBaseURL) reloadCounter += 1 if routed.isExplore { exploreTarget = WebTarget(url: routed.url, reloadToken: reloadCounter) selectedTab = .explore } else { readTarget = WebTarget(url: routed.url, reloadToken: reloadCounter) selectedTab = .read } } } enum AppTab: Hashable { case read case explore case account } enum HealthStatus { case unknown case ok case down }