import SwiftUI import SwiftData // MARK: - ContentView /// Root navigation container for the CelestiaTrace app. /// Uses a TabView with three tabs: Home, Field Records, and Settings. struct ContentView: View { @Environment(\.modelContext) private var modelContext @Environment(\.scenePhase) private var scenePhase @Query(sort: \CelestiaSession.startTime, order: .reverse) private var sessions: [CelestiaSession] @ObservedObject private var authManager = AuthManager.shared @AppStorage(DeveloperLogStore.enabledKey) private var isDeveloperModeEnabled = false /// Currently selected tab. @State private var selectedTab: Tab = .home /// Session selected for detail playback navigation in Field Records tab. @State private var fieldRecordsSelectedSession: CelestiaSession? var body: some View { ZStack(alignment: .bottomTrailing) { TabView(selection: $selectedTab) { HomeView { session in fieldRecordsSelectedSession = session selectedTab = .fieldRecords } .tabItem { Label(Tab.home.title, systemImage: Tab.home.icon) } .tag(Tab.home) SessionListView( selectedSession: $fieldRecordsSelectedSession, onStartRecording: { selectedTab = .home } ) .tabItem { Label(Tab.fieldRecords.title, systemImage: Tab.fieldRecords.icon) } .tag(Tab.fieldRecords) SettingsView() .tabItem { Label(Tab.settings.title, systemImage: Tab.settings.icon) } .tag(Tab.settings) } .tint(.celestiaCyan) if isDeveloperModeEnabled { DeveloperLogOverlay() .transition(.scale(scale: 0.92, anchor: .bottomTrailing).combined(with: .opacity)) .zIndex(20) } } .animation(.easeInOut(duration: 0.2), value: isDeveloperModeEnabled) .onAppear { if isDeveloperModeEnabled { DeveloperLogStore.log("App", "开发者模式已启用,开始记录本次运行日志", level: .success) } if RecordingRecoveryStore.activeSessionID != nil || RecordingLiveActivityManager.shared.recoverableSessionID != nil { selectedTab = .home } } .task(id: authManager.currentUser?.id) { await synchronizeFieldRecords() } .onChange(of: scenePhase) { _, newPhase in guard newPhase == .active else { return } Task { await synchronizeFieldRecords() } } .onOpenURL { url in guard RecordingLiveActivityRoute(url: url) != nil else { return } selectedTab = .home } .onChange(of: isDeveloperModeEnabled) { _, enabled in if enabled { DeveloperLogStore.log("App", "开发者模式已开启", level: .success) } } } @MainActor private func synchronizeFieldRecords() async { guard let userID = authManager.currentUser?.id else { return } _ = await SyncManager.shared.sync( sessions: sessions, modelContext: modelContext, userID: userID ) } } // MARK: - Tab Definition extension ContentView { /// Defines the available tabs in the app's root navigation. enum Tab: Hashable { case home case fieldRecords case settings var title: String { switch self { case .home: "首页" case .fieldRecords: "现场记录" case .settings: "设置" } } var icon: String { switch self { case .home: "house.fill" case .fieldRecords: "waveform.path.ecg" case .settings: "gearshape.fill" } } } } // MARK: - Preview #Preview { ContentView() .modelContainer(for: CelestiaSession.self, inMemory: true) }