App/Sources/DailySummaryNotifications.swift
44 lines · 2309 bytes
3 symbols in this file
1import SkyEngine
2import SwiftUI
3import UserNotifications
4
5/// Local notifications for the daily summary. The next seven are worked out on the device and
6/// replaced each time the app becomes active or the location or setting changes.
7enum DailySummaryNotifications {
8 static let ids = (0..<7).map { "daily-summary-\($0)" }
9 static let defaultMinutes = 18 * 60
10
11 @MainActor
12 static func reschedule(_ model: SkyModel) async {
13 let center = UNUserNotificationCenter.current()
14 center.removePendingNotificationRequests(withIdentifiers: ids)
15 let defaults = UserDefaults.standard
16 guard defaults.bool(forKey: SettingsKey.dailySummary) else { return }
17 await model.refreshPasses()
18 let observer = model.observer, passes = model.passes
19 let minutes = defaults.object(forKey: SettingsKey.dailySummaryMinutes) as? Int ?? defaultMinutes
20 let planned = await Task.detached { plan(observer: observer, passes: passes, minutes: minutes) }.value
21 let calendar = Calendar.current
22 for (id, fire, summary) in planned {
23 let content = UNMutableNotificationContent()
24 content.title = summary.title
25 content.body = summary.body
26 let parts = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: fire)
27 try? await center.add(UNNotificationRequest(identifier: id, content: content, trigger: UNCalendarNotificationTrigger(dateMatching: parts, repeats: false)))
28 }
29 }
30
31 nonisolated static func plan(observer: Observer, passes: [SatellitePass], minutes: Int, now: Date = .now) -> [(String, Date, DailySummary)] {
32 let calendar = Calendar.current
33 let today = calendar.startOfDay(for: now)
34 let events = SkyEngine.events(from: today, days: 8, observer: observer)
35 return ids.enumerated().compactMap { i, id in
36 guard let day = calendar.date(byAdding: .day, value: i, to: today),
37 let fire = calendar.date(bySettingHour: minutes / 60, minute: minutes % 60, second: 0, of: day),
38 fire > now,
39 let summary = try? DailySummary.make(at: fire, observer: observer, passes: passes, events: events)
40 else { return nil }
41 return (id, fire, summary)
42 }
43 }
44}