refactored timetable

This commit is contained in:
2026-05-05 13:49:45 +02:00
parent 551c1bf1fa
commit e8faa77e70
29 changed files with 1574 additions and 300 deletions
@@ -14,6 +14,16 @@ class TimetableBloc extends LoadableHydratedBloc<TimetableEvent, TimetableState,
DateTime _lastWeekRequestStart = DateTime.fromMillisecondsSinceEpoch(0);
/// Set by [retry] to force the next [gatherData] to bypass cache freshness
/// checks and actually hit the network. Cleared at the top of [gatherData].
bool _forceRenew = false;
@override
void retry() {
_forceRenew = true;
super.retry();
}
@override
TimetableRepository repository() => TimetableRepository();
@@ -35,11 +45,23 @@ class TimetableBloc extends LoadableHydratedBloc<TimetableEvent, TimetableState,
@override
Future<void> gatherData() async {
final initial = innerState ?? fromNothing();
final renew = _forceRenew;
_forceRenew = false;
Object? firstError;
void recordError(Object e) {
firstError ??= e;
}
await Future.wait([
_loadCurrentWeek(initial.startDate, initial.endDate),
_loadStaticReferenceData(),
_loadCustomEvents(),
_loadCurrentWeek(initial.startDate, initial.endDate, onError: recordError, renew: renew),
_loadStaticReferenceData(onError: recordError, renew: renew),
_loadCustomEvents(onError: recordError, renew: renew),
]);
if (firstError != null) throw firstError!;
add(DataGathered((s) => s));
_prefetchAdjacentWeeks(initial.startDate, initial.endDate);
}
@@ -73,41 +95,69 @@ class TimetableBloc extends LoadableHydratedBloc<TimetableEvent, TimetableState,
await _refreshCustomEvents();
}
Future<void> _loadCurrentWeek(DateTime startDate, DateTime endDate) async {
Future<void> _loadCurrentWeek(
DateTime startDate,
DateTime endDate, {
void Function(Object)? onError,
bool renew = false,
}) async {
final requestStart = DateTime.now();
_lastWeekRequestStart = requestStart;
try {
final week = await repo.data.getWeek(startDate, endDate);
final week = await repo.data.getWeek(startDate, endDate, onError: onError, renew: renew);
if (_lastWeekRequestStart.isAfter(requestStart)) return;
_writeWeekToCache(startDate, week);
} catch (_) {
// Errors are surfaced via LoadableHydratedBloc.fetch's catchError.
rethrow;
} catch (e) {
onError?.call(e);
}
}
Future<void> _loadStaticReferenceData() async {
final (rooms, subjects, schoolHolidays) = await (
repo.data.getRooms(),
repo.data.getSubjects(),
repo.data.getSchoolHolidays(),
).wait;
Future<void> _loadStaticReferenceData({
void Function(Object)? onError,
bool renew = false,
}) async {
try {
final (rooms, subjects, schoolHolidays) = await (
repo.data.getRooms(onError: onError, renew: renew),
repo.data.getSubjects(onError: onError, renew: renew),
repo.data.getSchoolHolidays(onError: onError, renew: renew),
).wait;
add(DataGathered((s) => s.copyWith(
rooms: rooms,
subjects: subjects,
schoolHolidays: schoolHolidays,
dataVersion: s.dataVersion + 1,
)));
add(Emit((s) => s.copyWith(
rooms: rooms,
subjects: subjects,
schoolHolidays: schoolHolidays,
dataVersion: s.dataVersion + 1,
)));
} catch (e) {
onError?.call(e);
}
try {
final timegrid = await repo.data.getTimegrid(renew: renew);
add(Emit((s) => s.copyWith(timegrid: timegrid, dataVersion: s.dataVersion + 1)));
} catch (_) {
// Timegrid load failure falls back to a hardcoded schedule in the UI layer.
}
}
Future<void> _loadCustomEvents({bool renew = false}) async {
final events = await repo.data.getCustomEvents(renew: renew);
Future<void> _loadCustomEvents({
void Function(Object)? onError,
bool renew = false,
}) async {
try {
final events = await repo.data.getCustomEvents(renew: renew, onError: onError);
add(Emit((s) => s.copyWith(customEvents: events, dataVersion: s.dataVersion + 1)));
} catch (e) {
onError?.call(e);
}
}
Future<void> _refreshCustomEvents() async {
final events = await repo.data.getCustomEvents(renew: true);
add(DataGathered((s) => s.copyWith(customEvents: events, dataVersion: s.dataVersion + 1)));
}
Future<void> _refreshCustomEvents() => _loadCustomEvents(renew: true);
void _prefetchAdjacentWeeks(DateTime start, DateTime end) {
_prefetchWeek(start.subtract(_weekSpan), end.subtract(_weekSpan));
_prefetchWeek(start.add(_weekSpan), end.add(_weekSpan));
@@ -119,7 +169,7 @@ class TimetableBloc extends LoadableHydratedBloc<TimetableEvent, TimetableState,
void _writeWeekToCache(DateTime weekStart, GetTimetableResponse week) {
final key = _weekKeyFormat.format(weekStart);
add(DataGathered((s) {
add(Emit((s) {
final updated = Map<String, GetTimetableResponse>.of(s.weekCache);
updated[key] = week;
return s.copyWith(weekCache: updated, dataVersion: s.dataVersion + 1);