widget refresh enhancements

This commit is contained in:
2026-08-06 20:22:32 +02:00
parent 646e2c0451
commit ab23422a86
22 changed files with 1027 additions and 99 deletions
@@ -100,13 +100,13 @@ struct TimetableDayView: View {
private func header(data: WidgetTimetableData) -> some View {
HStack(spacing: 4) {
Text(dayLabel(for: data.anchorDate))
Text(dayLabel(for: data.anchorDate, relativeTo: entry.date))
.font(.system(size: 13, weight: .semibold))
.foregroundStyle(palette.textPrimary)
.lineLimit(1)
.minimumScaleFactor(0.7)
Spacer(minLength: 4)
Text("Stand: \(freshnessLabel(for: data.fetchedAt))")
Text("Stand: \(freshnessLabel(for: data.fetchedAt, relativeTo: entry.date))")
.font(.system(size: 9))
.foregroundStyle(palette.textSecondary)
.lineLimit(1)
@@ -383,6 +383,7 @@ struct TimeGridView: View {
case .irregular: return Color(red: 143/255.0, green: 25/255.0, blue: 179/255.0)
case .teacherChanged: return Color(red: 41/255.0, green: 99/255.0, blue: 155/255.0)
case .event: return Color(red: 239/255.0, green: 108/255.0, blue: 0/255.0)
case .duty: return Color(red: 0/255.0, green: 121/255.0, blue: 107/255.0)
}
}
}
@@ -400,9 +401,31 @@ func periodBoundaries(_ periods: [WidgetPeriod]) -> [Int] {
return result.sorted()
}
func dayLabel(for date: Date) -> String {
/// Fixed-locale formatters cached once DateFormatter setup is the
/// expensive part and the multi-entry timelines render up to ~8 entries per
/// reload. Only touched from WidgetKit's archival rendering, so the shared
/// instances are safe.
enum WidgetDateFormatters {
static let shortDate = make("dd.MM.")
static let weekdayShort = make("EE")
static let weekdayDate = make("EEEE · dd.MM.")
static let time = make("HH:mm")
static let dateTime = make("dd.MM. HH:mm")
private static func make(_ format: String) -> DateFormatter {
let f = DateFormatter()
f.locale = Locale(identifier: "de_DE")
f.dateFormat = format
return f
}
}
/// `now` is the timeline entry's date, not `Date()`: WidgetKit archives
/// entries ahead of time, so wall-clock reads would be wrong for every
/// entry after the first.
func dayLabel(for date: Date, relativeTo now: Date) -> String {
let cal = Calendar.current
let today = cal.startOfDay(for: Date())
let today = cal.startOfDay(for: now)
let anchor = cal.startOfDay(for: date)
if anchor == today {
return "Heute · \(shortDate(date))"
@@ -410,35 +433,23 @@ func dayLabel(for date: Date) -> String {
if let tomorrow = cal.date(byAdding: .day, value: 1, to: today), anchor == tomorrow {
return "Morgen · \(shortDate(date))"
}
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "de_DE")
formatter.dateFormat = "EEEE · dd.MM."
return formatter.string(from: date)
return WidgetDateFormatters.weekdayDate.string(from: date)
}
func shortDate(_ date: Date) -> String {
let f = DateFormatter()
f.locale = Locale(identifier: "de_DE")
f.dateFormat = "dd.MM."
return f.string(from: date)
WidgetDateFormatters.shortDate.string(from: date)
}
func freshnessLabel(for fetchedAt: Date) -> String {
func freshnessLabel(for fetchedAt: Date, relativeTo now: Date) -> String {
let cal = Calendar.current
let today = cal.startOfDay(for: Date())
let today = cal.startOfDay(for: now)
let fetchedDay = cal.startOfDay(for: fetchedAt)
let timeFmt = DateFormatter()
timeFmt.locale = Locale(identifier: "de_DE")
timeFmt.dateFormat = "HH:mm"
if fetchedDay == today {
return timeFmt.string(from: fetchedAt)
return WidgetDateFormatters.time.string(from: fetchedAt)
}
if let yesterday = cal.date(byAdding: .day, value: -1, to: today),
fetchedDay == yesterday {
return "gestern \(timeFmt.string(from: fetchedAt))"
return "gestern \(WidgetDateFormatters.time.string(from: fetchedAt))"
}
let dateTimeFmt = DateFormatter()
dateTimeFmt.locale = Locale(identifier: "de_DE")
dateTimeFmt.dateFormat = "dd.MM. HH:mm"
return dateTimeFmt.string(from: fetchedAt)
return WidgetDateFormatters.dateTime.string(from: fetchedAt)
}