implemented current schoolyear API and dynamic timetable scroll boundaries, added handling for out-of-range errors to narrow accessible dates, optimized holiday region rendering by collapsing overlaps, and refined holiday tile UI

This commit is contained in:
2026-05-14 15:07:48 +02:00
parent 2cb8321d07
commit 582eff8750
13 changed files with 368 additions and 51 deletions
@@ -62,33 +62,47 @@ class SpecialRegionsBuilder {
static String _dayKey(DateTime d) => '${d.year}-${d.month}-${d.day}';
Iterable<TimeRegion> _buildHolidayRegions() => holidays.result.expand((
holiday,
) {
final startDay = WebuntisTime.parse(holiday.startDate, 0);
final endDay = WebuntisTime.parse(holiday.endDate, 0);
// Webuntis treats endDate inclusively (last day of the break) — the
// `+ 1` covers single-day public holidays (where startDate == endDate)
// and the final day of a multi-day vacation, both of which would
// otherwise be skipped.
final dayCount = endDay.difference(startDay).inDays + 1;
final days = List<DateTime>.generate(
dayCount,
(i) => startDay.add(Duration(days: i)),
);
Iterable<TimeRegion> _buildHolidayRegions() {
// Multiple Webuntis holiday entries can cover the same day (e.g. a
// public holiday falling inside a vacation period). Collapse them
// per-day so we emit exactly one TimeRegion per day and the
// overlapping labels don't render on top of each other.
final byDay = <String, _HolidayDay>{};
for (final holiday in holidays.result) {
final startDay = WebuntisTime.parse(holiday.startDate, 0);
final endDay = WebuntisTime.parse(holiday.endDate, 0);
// Webuntis treats endDate inclusively (last day of the break) — the
// `+ 1` covers single-day public holidays (where startDate == endDate)
// and the final day of a multi-day vacation, both of which would
// otherwise be skipped.
final dayCount = endDay.difference(startDay).inDays + 1;
for (var i = 0; i < dayCount; i++) {
final day = startDay.add(Duration(days: i));
final key = _dayKey(day);
byDay.putIfAbsent(key, () => _HolidayDay(day, [])).names.add(
holiday.name,
);
}
}
final gridStartHour = kCalendarStartHour.floor();
final gridStartMinute = ((kCalendarStartHour - gridStartHour) * 60).round();
final gridEndHour = kCalendarEndHour.floor();
final gridEndMinute = ((kCalendarEndHour - gridEndHour) * 60).round();
return days.map(
(day) => TimeRegion(
startTime: day.copyWith(hour: gridStartHour, minute: gridStartMinute),
endTime: day.copyWith(hour: gridEndHour, minute: gridEndMinute),
text: '$kTimeRegionHolidayPrefix${holiday.name}',
return byDay.values.map(
(entry) => TimeRegion(
startTime: entry.day.copyWith(
hour: gridStartHour,
minute: gridStartMinute,
),
endTime: entry.day.copyWith(
hour: gridEndHour,
minute: gridEndMinute,
),
text: '$kTimeRegionHolidayPrefix${entry.names.join(" / ")}',
color: disabledColor.withAlpha(50),
),
);
});
}
TimeRegion _breakRegion(DateTime start, Duration duration) => TimeRegion(
startTime: start,
@@ -98,3 +112,9 @@ class SpecialRegionsBuilder {
iconData: Icons.restaurant,
);
}
class _HolidayDay {
final DateTime day;
final List<String> names;
_HolidayDay(this.day, this.names);
}