widget refresh enhancements
This commit is contained in:
@@ -41,11 +41,13 @@ struct TimetableDayProvider: TimelineProvider {
|
||||
in context: Context,
|
||||
completion: @escaping (Timeline<TimetableEntry>) -> Void
|
||||
) {
|
||||
let entry = TimetableEntry.current(variant: .day)
|
||||
let now = Date()
|
||||
// 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)))
|
||||
// advisory; the boundary entries below keep the rendered day correct
|
||||
// even when no reload is granted, and the "Stand:" label tells the
|
||||
// user when the underlying data is stale.
|
||||
let next = Calendar.current.date(byAdding: .minute, value: 30, to: now) ?? now
|
||||
completion(Timeline(entries: TimetableEntry.dayEntries(now: now), policy: .after(next)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +82,9 @@ struct TimetableWeekProvider: TimelineProvider {
|
||||
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)))
|
||||
let now = Date()
|
||||
let next = Calendar.current.date(byAdding: .minute, value: 30, to: now) ?? now
|
||||
completion(Timeline(entries: TimetableEntry.weekEntries(now: now), policy: .after(next)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +122,58 @@ struct TimetableEntry: TimelineEntry {
|
||||
themeMode: WidgetDataLoader.themeMode()
|
||||
)
|
||||
}
|
||||
|
||||
/// Day timeline derived from the 14-day week payload, so the widget shows
|
||||
/// the right day even when iOS grants no reload for days.
|
||||
static func dayEntries(now: Date) -> [TimetableEntry] {
|
||||
entries(now: now, variant: .day) { week, date in
|
||||
TimetableAnchor.slice(week: week, forDay: TimetableAnchor.resolveDayAnchor(date))
|
||||
}
|
||||
}
|
||||
|
||||
/// Week timeline: re-anchoring performs the Friday-evening/weekend jump
|
||||
/// into next week from cached data, and the midnight entries keep the
|
||||
/// "Stand:" freshness label honest.
|
||||
static func weekEntries(now: Date) -> [TimetableEntry] {
|
||||
entries(now: now, variant: .week) { week, date in
|
||||
TimetableAnchor.retarget(week: week, anchorDate: TimetableAnchor.resolveWeekAnchor(date))
|
||||
}
|
||||
}
|
||||
|
||||
/// Shared timeline skeleton: one entry now plus one per anchor boundary
|
||||
/// (midnight rollover, 17:00 cutoff). Boundaries that cannot change the
|
||||
/// render — same anchor and same calendar day for the header labels —
|
||||
/// are dropped.
|
||||
private static func entries(
|
||||
now: Date,
|
||||
variant: TimetableVariant,
|
||||
transform: (WidgetTimetableData, Date) -> WidgetTimetableData
|
||||
) -> [TimetableEntry] {
|
||||
guard WidgetDataLoader.isLoggedIn(), let week = WidgetDataLoader.loadWeek() else {
|
||||
// Logged out, or no v2 week snapshot yet (fresh app update):
|
||||
// fall back to the legacy single-entry payload.
|
||||
return [TimetableEntry.current(variant: variant)]
|
||||
}
|
||||
let theme = WidgetDataLoader.themeMode()
|
||||
let cal = Calendar.current
|
||||
var result: [TimetableEntry] = []
|
||||
for date in [now] + TimetableAnchor.boundaryDates(from: now) {
|
||||
let data = transform(week, date)
|
||||
if let previous = result.last, let previousData = previous.data,
|
||||
cal.isDate(previousData.anchorDate, inSameDayAs: data.anchorDate),
|
||||
cal.isDate(previous.date, inSameDayAs: date) {
|
||||
continue
|
||||
}
|
||||
result.append(TimetableEntry(
|
||||
date: date,
|
||||
variant: variant,
|
||||
data: data,
|
||||
isLoggedIn: true,
|
||||
themeMode: theme
|
||||
))
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
|
||||
Reference in New Issue
Block a user