139 lines
4.2 KiB
Swift
139 lines
4.2 KiB
Swift
import SwiftUI
|
|
import WidgetKit
|
|
|
|
@main
|
|
struct MarianumWidgetBundle: WidgetBundle {
|
|
var body: some Widget {
|
|
TimetableDayWidget()
|
|
TimetableWeekWidget()
|
|
}
|
|
}
|
|
|
|
// MARK: - Day widget
|
|
|
|
struct TimetableDayWidget: Widget {
|
|
let kind: String = "TimetableDayWidget"
|
|
|
|
var body: some WidgetConfiguration {
|
|
StaticConfiguration(kind: kind, provider: TimetableDayProvider()) { entry in
|
|
TimetableDayView(entry: entry).widgetContainerBackground()
|
|
}
|
|
.configurationDisplayName("Marianum · Heute")
|
|
.description("Stundenplan und Vertretungen für den anstehenden Schultag.")
|
|
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
|
|
}
|
|
}
|
|
|
|
struct TimetableDayProvider: TimelineProvider {
|
|
func placeholder(in context: Context) -> TimetableEntry {
|
|
TimetableEntry.placeholder()
|
|
}
|
|
|
|
func getSnapshot(in context: Context, completion: @escaping (TimetableEntry) -> Void) {
|
|
completion(TimetableEntry.current(variant: .day))
|
|
}
|
|
|
|
func getTimeline(
|
|
in context: Context,
|
|
completion: @escaping (Timeline<TimetableEntry>) -> Void
|
|
) {
|
|
let entry = TimetableEntry.current(variant: .day)
|
|
// 30 min mirrors the Dart workmanager cadence. iOS treats this as
|
|
// advisory; the "Stand:" label tells the user when data is stale.
|
|
let next = Calendar.current.date(byAdding: .minute, value: 30, to: Date()) ?? Date()
|
|
completion(Timeline(entries: [entry], policy: .after(next)))
|
|
}
|
|
}
|
|
|
|
// MARK: - Week widget
|
|
|
|
struct TimetableWeekWidget: Widget {
|
|
let kind: String = "TimetableWeekWidget"
|
|
|
|
var body: some WidgetConfiguration {
|
|
StaticConfiguration(kind: kind, provider: TimetableWeekProvider()) { entry in
|
|
TimetableWeekView(entry: entry).widgetContainerBackground()
|
|
}
|
|
.configurationDisplayName("Marianum · Woche")
|
|
.description("Stundenplan und Vertretungen für die ganze Schulwoche.")
|
|
.supportedFamilies([.systemMedium, .systemLarge, .systemExtraLarge])
|
|
}
|
|
}
|
|
|
|
struct TimetableWeekProvider: TimelineProvider {
|
|
func placeholder(in context: Context) -> TimetableEntry {
|
|
TimetableEntry.placeholder()
|
|
}
|
|
|
|
func getSnapshot(in context: Context, completion: @escaping (TimetableEntry) -> Void) {
|
|
completion(TimetableEntry.current(variant: .week))
|
|
}
|
|
|
|
func getTimeline(
|
|
in context: Context,
|
|
completion: @escaping (Timeline<TimetableEntry>) -> Void
|
|
) {
|
|
let entry = TimetableEntry.current(variant: .week)
|
|
let next = Calendar.current.date(byAdding: .minute, value: 30, to: Date()) ?? Date()
|
|
completion(Timeline(entries: [entry], policy: .after(next)))
|
|
}
|
|
}
|
|
|
|
// MARK: - Entry
|
|
|
|
enum TimetableVariant { case day, week }
|
|
|
|
struct TimetableEntry: TimelineEntry {
|
|
let date: Date
|
|
let variant: TimetableVariant
|
|
let data: WidgetTimetableData?
|
|
let isLoggedIn: Bool
|
|
let themeMode: String
|
|
|
|
static func placeholder() -> TimetableEntry {
|
|
TimetableEntry(
|
|
date: Date(),
|
|
variant: .day,
|
|
data: nil,
|
|
isLoggedIn: true,
|
|
themeMode: "system"
|
|
)
|
|
}
|
|
|
|
static func current(variant: TimetableVariant) -> TimetableEntry {
|
|
let isLoggedIn = WidgetDataLoader.isLoggedIn()
|
|
let data = isLoggedIn
|
|
? (variant == .day ? WidgetDataLoader.loadDay() : WidgetDataLoader.loadWeek())
|
|
: nil
|
|
return TimetableEntry(
|
|
date: Date(),
|
|
variant: variant,
|
|
data: data,
|
|
isLoggedIn: isLoggedIn,
|
|
themeMode: WidgetDataLoader.themeMode()
|
|
)
|
|
}
|
|
}
|
|
|
|
extension View {
|
|
@ViewBuilder
|
|
func widgetThemeOverride(_ mode: String) -> some View {
|
|
switch mode {
|
|
case "light": self.environment(\.colorScheme, .light)
|
|
case "dark": self.environment(\.colorScheme, .dark)
|
|
default: self
|
|
}
|
|
}
|
|
|
|
/// `.containerBackground(_:for:)` is iOS 17+. Older iOS uses the
|
|
/// implicit `.background(...)` model and renders fine without it.
|
|
@ViewBuilder
|
|
func widgetContainerBackground() -> some View {
|
|
if #available(iOS 17.0, *) {
|
|
self.containerBackground(.fill.tertiary, for: .widget)
|
|
} else {
|
|
self
|
|
}
|
|
}
|
|
}
|