import ManaAuthUI import ManaCore import SwiftData import SwiftUI import WidgetKit @main struct ZitareNativeApp: App { @State private var auth: AuthClient @State private var authGate: ManaAuthGate @State private var submissionQueue: SubmissionQueue private let snapshotContainer: ModelContainer? private let reachability = ReachabilityWatcher() init() { let auth = AuthClient(config: AppConfig.manaAppConfig) auth.bootstrap() _auth = State(initialValue: auth) _authGate = State(initialValue: ManaAuthGate(auth: auth)) do { snapshotContainer = try SnapshotContainer.make() } catch { Log.snapshot.error( "SnapshotContainer init fehlgeschlagen: \(String(describing: error), privacy: .public)" ) snapshotContainer = nil } let pending: ModelContainer do { pending = try PendingSubmissionContainer.make() } catch { Log.app.error( "PendingSubmissionContainer-Disk init fehlgeschlagen, falle auf in-memory zurück: \(String(describing: error), privacy: .public)" ) // In-memory-Fallback statt nil. Submissions sind dann nur // für die aktuelle Session persistiert — besser als Crash. pending = try! PendingSubmissionContainer.make(inMemory: true) } _submissionQueue = State(initialValue: SubmissionQueue(container: pending)) Log.app.info( "Zitare starting — auth status: \(String(describing: auth.status), privacy: .public)" ) } var body: some Scene { WindowGroup { RootView() .environment(auth) .environment(authGate) .environment(submissionQueue) .tint(ZitareTheme.primary) .task { await refreshSnapshot() await flushPending() startReachability() } } } private func flushPending() async { let api = ZitareAPI(auth: auth) let sent = await submissionQueue.tryFlush(api: api) if sent > 0 { Log.app.info("Auto-flushed \(sent) pending submission(s) at launch") } } /// Startet den NWPathMonitor und hooked Reconnect → Flush. /// `Task.detached` + `await MainActor`, weil das Reconnect-Closure /// vom Network-Framework auf einer privaten Queue feuert. private func startReachability() { let queue = submissionQueue let auth = auth reachability.start { [weak queue] in guard let queue else { return } Task { @MainActor in let api = ZitareAPI(auth: auth) let sent = await queue.tryFlush(api: api) if sent > 0 { Log.app.info("Reachability-Flush: \(sent) Submission(s) nachgereicht") } } } } private func refreshSnapshot() async { guard let container = snapshotContainer else { return } let sync = SnapshotSync(container: container) await sync.tryRefresh() // Widget-Timeline neu erstellen lassen, sodass der nächste // Render-Pass den frischen Snapshot sieht. WidgetCenter.shared.reloadAllTimelines() } }