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
+44 -6
View File
@@ -9,6 +9,7 @@ import '../api/marianumconnect/queries/timetable_get_timegrid/timetable_get_time
import '../api/marianumconnect/queries/timetable_get_week/timetable_get_week_response.dart';
import '../api/mhsl/custom_timetable_event/custom_timetable_event.dart';
import '../api/mhsl/custom_timetable_event/get/get_custom_timetable_event_response.dart';
import '../extensions/date_time.dart';
import '../view/pages/timetable/data/lesson_merger.dart';
import '../view/pages/timetable/data/lesson_period_schedule.dart';
import '../view/pages/timetable/data/lesson_status.dart';
@@ -34,12 +35,20 @@ class WidgetDataMapper {
return candidate;
}
static DateTime resolveWeekAnchor(DateTime now) {
final anchor = resolveDayAnchor(now);
final monday = anchor.subtract(Duration(days: anchor.weekday - 1));
static DateTime resolveWeekAnchor(DateTime now) =>
startOfCalendarWeek(resolveDayAnchor(now));
/// Monday of the calendar week containing [reference] — no roll-forward,
/// unlike [resolveWeekAnchor]. Start of the week payload's 14-day window.
static DateTime startOfCalendarWeek(DateTime reference) {
final monday = reference.subtract(Duration(days: reference.weekday - 1));
return DateTime(monday.year, monday.month, monday.day);
}
/// Days covered by the week payload: current calendar week + the next, so
/// native renderers can roll the view forward without fresh data.
static const int weekWindowDays = 14;
static WidgetTimetableData buildDayData({
required DateTime now,
required Iterable<McTimetableEntry> lessons,
@@ -83,10 +92,14 @@ class WidgetDataMapper {
bool connectDoubleLessons = true,
}) {
final anchor = resolveWeekAnchor(now);
final endExclusive = anchor.add(const Duration(days: 5));
// The window is anchored at the *current* calendar week, not the
// (possibly rolled-forward) week anchor: on Friday evening the payload
// must still contain today for renderers that derive day slices.
final windowStart = startOfCalendarWeek(now);
final endExclusive = windowStart.add(const Duration(days: weekWindowDays));
final weekLessons = lessons.where((l) {
final dt = l.startDateTime;
return !dt.isBefore(anchor) && dt.isBefore(endExclusive);
return !dt.isBefore(windowStart) && dt.isBefore(endExclusive);
}).toList();
// Per-day merge: otherwise a 4th-period lesson on Mon would collapse with
// a 1st-period lesson on Tue if subject/teacher match.
@@ -95,13 +108,38 @@ class WidgetDataMapper {
: weekLessons;
final mapped = <WidgetLesson>[
...source.map((l) => _mapLesson(l, now, subjects, rooms)),
..._expandCustomEvents(customEvents, anchor, endExclusive),
..._expandCustomEvents(customEvents, windowStart, endExclusive),
]..sort((a, b) => a.start.compareTo(b.start));
final days = [
for (var i = 0; i < weekWindowDays; i++)
_dayInfo(windowStart.addDays(i), holidays),
];
// The anchor always lies inside the window; the orElse only guards the
// impossible.
final anchorInfo = days.firstWhere(
(d) => d.date == anchor,
orElse: () => _dayInfo(anchor, holidays),
);
return WidgetTimetableData(
fetchedAt: now,
anchorDate: anchor,
lessons: _resolveCollisions(mapped),
periods: _resolvePeriods(timegrid),
isHoliday: anchorInfo.isHoliday,
holidayName: anchorInfo.holidayName,
days: days,
);
}
static WidgetDayInfo _dayInfo(
DateTime day,
TimetableGetHolidaysResponse? holidays,
) {
final holiday = _findHoliday(day, holidays);
return WidgetDayInfo(
date: day,
isHoliday: holiday != null,
holidayName: holiday?.longName,
);
}