// // Solsnu_Widget.swift // Solsnu.Widget // // Created by Ivar Løvlie on 15/12/2025. // import WidgetKit import SwiftUI // MARK: - Provider struct Provider: TimelineProvider { func placeholder(in context: Context) -> SolvervEntry { .placeholder } func getSnapshot(in context: Context, completion: @escaping (SolvervEntry) -> Void) { completion(makeEntry(for: Date())) } func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { let entry = makeEntry(for: Date()) let nextMidnight = Calendar.current.nextDate( after: Date(), matching: DateComponents(hour: 0, minute: 0, second: 0), matchingPolicy: .nextTime ) ?? Date().addingTimeInterval(86_400) completion(Timeline(entries: [entry], policy: .after(nextMidnight))) } private func makeEntry(for date: Date) -> SolvervEntry { let progress = SolsticeData.shared.progressToNextEvent(from: date) let ratio = progress.map { max(0, min(1, Double($0.elapsed) / Double($0.total))) } ?? 0 let sunTimes = AppGroupManager.shared.getLocation().map { SunTimes(latitude: $0.latitude, longitude: $0.longitude, date: date) } return SolvervEntry( date: date, nextEvent: SolsticeData.shared.nextEvent(from: date), currentEvent: SolsticeData.shared.currentEvent(from: date), progressRatio: ratio, sunriseTime: sunTimes?.sunrise(), sunsetTime: sunTimes?.sunset() ) } } // MARK: - Entry struct SolvervEntry: TimelineEntry { let date: Date let nextEvent: SolsticeEvent? let currentEvent: SolsticeEvent? let progressRatio: Double let sunriseTime: Date? let sunsetTime: Date? static let placeholder = SolvervEntry( date: Date(), nextEvent: nil, currentEvent: nil, progressRatio: 0.4, sunriseTime: nil, sunsetTime: nil ) } // MARK: - Family dispatch struct SolsticeWidgetView: View { let entry: SolvervEntry @Environment(\.widgetFamily) var widgetFamily var body: some View { switch widgetFamily { case .systemMedium: MediumWidgetView(entry: entry) default: SmallWidgetView(entry: entry) } } } // MARK: - Widget struct Solsnu_Widget: Widget { let kind = "Solsnu_Widget" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: Provider()) { entry in SolsticeWidgetView(entry: entry) } .contentMarginsDisabled() .configurationDisplayName("Solstice Countdown") .description("Days until next solstice or equinox") .supportedFamilies([.systemSmall, .systemMedium]) } } // MARK: - Previews #Preview(as: .systemSmall) { Solsnu_Widget() } timeline: { let now = Date() SolvervEntry( date: now, nextEvent: SolsticeData.shared.nextEvent(from: now), currentEvent: SolsticeData.shared.currentEvent(from: now), progressRatio: 0.4, sunriseTime: Calendar.current.date(bySettingHour: 6, minute: 28, second: 0, of: now), sunsetTime: Calendar.current.date(bySettingHour: 21, minute: 15, second: 0, of: now) ) SolvervEntry.placeholder } #Preview(as: .systemMedium) { Solsnu_Widget() } timeline: { let now = Date() SolvervEntry( date: now, nextEvent: SolsticeData.shared.nextEvent(from: now), currentEvent: SolsticeData.shared.currentEvent(from: now), progressRatio: 0.4, sunriseTime: Calendar.current.date(bySettingHour: 6, minute: 28, second: 0, of: now), sunsetTime: Calendar.current.date(bySettingHour: 21, minute: 15, second: 0, of: now) ) SolvervEntry.placeholder }