diff --git a/Outpost/OutpostApp.swift b/Outpost/OutpostApp.swift index ab99140..59cb668 100644 --- a/Outpost/OutpostApp.swift +++ b/Outpost/OutpostApp.swift @@ -6,27 +6,83 @@ // import SwiftUI -import SwiftData + +#if os(macOS) +import AppKit +#endif @main struct OutpostApp: App { - var sharedModelContainer: ModelContainer = { - let schema = Schema([ - Item.self, - ]) - let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) + @State private var session = SessionStore() + @AppStorage("outpost.appearance") private var appearance: AppAppearance = .system - do { - return try ModelContainer(for: schema, configurations: [modelConfiguration]) - } catch { - fatalError("Could not create ModelContainer: \(error)") - } - }() + #if os(macOS) + @Environment(\.openWindow) private var openWindow + @State private var isShowingLogoutConfirmation = false + #endif var body: some Scene { WindowGroup { - ContentView() + RootView() + .environment(session) + #if os(iOS) + .preferredColorScheme(appearance.colorScheme) + #endif + #if os(macOS) + .onAppear { applyMacAppearance() } + .onChange(of: appearance) { _, _ in applyMacAppearance() } + .logoutConfirmationDialog(isPresented: $isShowingLogoutConfirmation, session: session) + #endif } - .modelContainer(sharedModelContainer) + #if os(macOS) + .commands { + CommandGroup(replacing: .appInfo) { + Button("About Outpost") { + openWindow(id: "about") + } + } + CommandGroup(after: .appSettings) { + Divider() + Button("Log Out…") { + isShowingLogoutConfirmation = true + } + .disabled(!session.isSignedIn) + } + } + #endif + + #if os(macOS) + Window("About Outpost", id: "about") { + AboutView() + } + .windowResizability(.contentSize) + + Window("Keyboard Shortcuts", id: "keyboard-shortcuts") { + KeyboardShortcutsView() + } + .windowResizability(.contentSize) + + Settings { + PreferencesView() + .environment(session) + } + #endif } + + #if os(macOS) + /// `.preferredColorScheme(nil)` doesn't reliably reset the window's real + /// `NSAppearance` back to "follow system" after an explicit override — this + /// sets the actual AppKit-level appearance directly instead, which every + /// window (present and future) inherits. + private func applyMacAppearance() { + switch appearance { + case .system: + NSApp.appearance = nil + case .light: + NSApp.appearance = NSAppearance(named: .aqua) + case .dark: + NSApp.appearance = NSAppearance(named: .darkAqua) + } + } + #endif } diff --git a/Outpost/Support/AppAppearance.swift b/Outpost/Support/AppAppearance.swift new file mode 100644 index 0000000..2405e97 --- /dev/null +++ b/Outpost/Support/AppAppearance.swift @@ -0,0 +1,23 @@ +import SwiftUI + +enum AppAppearance: String, CaseIterable, Identifiable { + case system, light, dark + + var id: String { rawValue } + + var label: String { + switch self { + case .system: return "System" + case .light: return "Light" + case .dark: return "Dark" + } + } + + var colorScheme: ColorScheme? { + switch self { + case .system: return nil + case .light: return .light + case .dark: return .dark + } + } +}