App/Sources/MoonriseApp.swift
74 lines · 3443 bytes
1import AppIntents
2import SkyRenderer
3import SwiftUI
4import UIKit
5import UserNotifications
6import os
7
8let log = Logger(subsystem: "sh.krz.moonrise", category: "app")
9
10@main
11struct MoonriseApp: App {
12 @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
13 @AppStorage(SettingsKey.onboarded) private var onboarded = false
14 @AppStorage(SettingsKey.nightActive) private var nightActive = false
15 @State private var router = Router.shared
16
17 var body: some Scene {
18 WindowGroup {
19 TabView(selection: $router.tab) {
20 Tab("Sky", systemImage: "sparkles", value: "sky") { SkyScreen() }
21 Tab("System", systemImage: "circle.circle", value: "system") { SystemScreen() }
22 Tab("Sun", systemImage: "sun.max", value: "sun") { SunScreen() }
23 Tab("News & launches", systemImage: "airplane.departure", value: "launches") { LaunchesScreen() }
24 Tab("Library", systemImage: "books.vertical", value: "library") { LibraryScreen() }
25 }
26 .tabViewStyle(.sidebarAdaptable)
27 // Red tab highlight while the Sky tab is in night mode.
28 .tint(router.tab == "sky" && nightActive ? SkyStyle.nightRed : nil)
29 .preferredColorScheme(.dark)
30 .environment(\.openURL, InAppBrowser.action)
31 .fullScreenCover(isPresented: .constant(!onboarded)) {
32 OnboardingScreen().preferredColorScheme(.dark)
33 }
34 .sheet(item: $router.libraryPage) { page in
35 NavigationStack { LibraryPageView(page: page) }
36 }
37 }
38 }
39}
40
41final class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
42 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
43 ImageLoader.configureURLCache()
44 UNUserNotificationCenter.current().delegate = self
45 application.registerForRemoteNotifications()
46 MoonriseShortcuts.updateAppShortcutParameters()
47 Task.detached(priority: .background) { await SpotlightIndexer.indexIfNeeded() }
48 return true
49 }
50
51 func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
52 let token = deviceToken.map { String(format: "%02x", $0) }.joined()
53 log.info("APNs device token: \(token, privacy: .public)")
54 Task { @MainActor in AuroraAlerts.shared.receivedToken(token) }
55 }
56
57 /// A news digest opens News & launches on the News switch.
58 nonisolated func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async {
59 guard response.notification.request.content.userInfo["tab"] as? String == "news" else { return }
60 await MainActor.run {
61 Router.shared.tab = "launches"
62 Router.shared.showNews = true
63 }
64 }
65
66 /// Show alerts as banners while the app is open.
67 nonisolated func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
68 [.banner, .sound]
69 }
70
71 func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
72 log.error("APNs registration failed: \(error.localizedDescription, privacy: .public)")
73 }
74}