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);
@@ -4,6 +4,7 @@ import '../../../../../api/mhsl/customTimetableEvent/get/getCustomTimetableEvent
import '../../../../../api/webuntis/queries/getHolidays/getHolidaysResponse.dart';
import '../../../../../api/webuntis/queries/getRooms/getRoomsResponse.dart';
import '../../../../../api/webuntis/queries/getSubjects/getSubjectsResponse.dart';
import '../../../../../api/webuntis/queries/getTimegridUnits/getTimegridUnitsResponse.dart';
import '../../../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart';
part 'timetable_state.freezed.dart';
@@ -18,6 +19,7 @@ abstract class TimetableState with _$TimetableState {
GetRoomsResponse? rooms,
GetSubjectsResponse? subjects,
GetHolidaysResponse? schoolHolidays,
GetTimegridUnitsResponse? timegrid,
GetCustomTimetableEventResponse? customEvents,
required DateTime startDate,
required DateTime endDate,
@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$TimetableState {
Map<String, GetTimetableResponse> get weekCache; GetRoomsResponse? get rooms; GetSubjectsResponse? get subjects; GetHolidaysResponse? get schoolHolidays; GetCustomTimetableEventResponse? get customEvents; DateTime get startDate; DateTime get endDate; int get dataVersion;
Map<String, GetTimetableResponse> get weekCache; GetRoomsResponse? get rooms; GetSubjectsResponse? get subjects; GetHolidaysResponse? get schoolHolidays; GetTimegridUnitsResponse? get timegrid; GetCustomTimetableEventResponse? get customEvents; DateTime get startDate; DateTime get endDate; int get dataVersion;
/// Create a copy of TimetableState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -28,16 +28,16 @@ $TimetableStateCopyWith<TimetableState> get copyWith => _$TimetableStateCopyWith
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TimetableState&&const DeepCollectionEquality().equals(other.weekCache, weekCache)&&(identical(other.rooms, rooms) || other.rooms == rooms)&&(identical(other.subjects, subjects) || other.subjects == subjects)&&(identical(other.schoolHolidays, schoolHolidays) || other.schoolHolidays == schoolHolidays)&&(identical(other.customEvents, customEvents) || other.customEvents == customEvents)&&(identical(other.startDate, startDate) || other.startDate == startDate)&&(identical(other.endDate, endDate) || other.endDate == endDate)&&(identical(other.dataVersion, dataVersion) || other.dataVersion == dataVersion));
return identical(this, other) || (other.runtimeType == runtimeType&&other is TimetableState&&const DeepCollectionEquality().equals(other.weekCache, weekCache)&&(identical(other.rooms, rooms) || other.rooms == rooms)&&(identical(other.subjects, subjects) || other.subjects == subjects)&&(identical(other.schoolHolidays, schoolHolidays) || other.schoolHolidays == schoolHolidays)&&(identical(other.timegrid, timegrid) || other.timegrid == timegrid)&&(identical(other.customEvents, customEvents) || other.customEvents == customEvents)&&(identical(other.startDate, startDate) || other.startDate == startDate)&&(identical(other.endDate, endDate) || other.endDate == endDate)&&(identical(other.dataVersion, dataVersion) || other.dataVersion == dataVersion));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(weekCache),rooms,subjects,schoolHolidays,customEvents,startDate,endDate,dataVersion);
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(weekCache),rooms,subjects,schoolHolidays,timegrid,customEvents,startDate,endDate,dataVersion);
@override
String toString() {
return 'TimetableState(weekCache: $weekCache, rooms: $rooms, subjects: $subjects, schoolHolidays: $schoolHolidays, customEvents: $customEvents, startDate: $startDate, endDate: $endDate, dataVersion: $dataVersion)';
return 'TimetableState(weekCache: $weekCache, rooms: $rooms, subjects: $subjects, schoolHolidays: $schoolHolidays, timegrid: $timegrid, customEvents: $customEvents, startDate: $startDate, endDate: $endDate, dataVersion: $dataVersion)';
}
@@ -48,7 +48,7 @@ abstract mixin class $TimetableStateCopyWith<$Res> {
factory $TimetableStateCopyWith(TimetableState value, $Res Function(TimetableState) _then) = _$TimetableStateCopyWithImpl;
@useResult
$Res call({
Map<String, GetTimetableResponse> weekCache, GetRoomsResponse? rooms, GetSubjectsResponse? subjects, GetHolidaysResponse? schoolHolidays, GetCustomTimetableEventResponse? customEvents, DateTime startDate, DateTime endDate, int dataVersion
Map<String, GetTimetableResponse> weekCache, GetRoomsResponse? rooms, GetSubjectsResponse? subjects, GetHolidaysResponse? schoolHolidays, GetTimegridUnitsResponse? timegrid, GetCustomTimetableEventResponse? customEvents, DateTime startDate, DateTime endDate, int dataVersion
});
@@ -65,13 +65,14 @@ class _$TimetableStateCopyWithImpl<$Res>
/// Create a copy of TimetableState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? weekCache = null,Object? rooms = freezed,Object? subjects = freezed,Object? schoolHolidays = freezed,Object? customEvents = freezed,Object? startDate = null,Object? endDate = null,Object? dataVersion = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? weekCache = null,Object? rooms = freezed,Object? subjects = freezed,Object? schoolHolidays = freezed,Object? timegrid = freezed,Object? customEvents = freezed,Object? startDate = null,Object? endDate = null,Object? dataVersion = null,}) {
return _then(_self.copyWith(
weekCache: null == weekCache ? _self.weekCache : weekCache // ignore: cast_nullable_to_non_nullable
as Map<String, GetTimetableResponse>,rooms: freezed == rooms ? _self.rooms : rooms // ignore: cast_nullable_to_non_nullable
as GetRoomsResponse?,subjects: freezed == subjects ? _self.subjects : subjects // ignore: cast_nullable_to_non_nullable
as GetSubjectsResponse?,schoolHolidays: freezed == schoolHolidays ? _self.schoolHolidays : schoolHolidays // ignore: cast_nullable_to_non_nullable
as GetHolidaysResponse?,customEvents: freezed == customEvents ? _self.customEvents : customEvents // ignore: cast_nullable_to_non_nullable
as GetHolidaysResponse?,timegrid: freezed == timegrid ? _self.timegrid : timegrid // ignore: cast_nullable_to_non_nullable
as GetTimegridUnitsResponse?,customEvents: freezed == customEvents ? _self.customEvents : customEvents // ignore: cast_nullable_to_non_nullable
as GetCustomTimetableEventResponse?,startDate: null == startDate ? _self.startDate : startDate // ignore: cast_nullable_to_non_nullable
as DateTime,endDate: null == endDate ? _self.endDate : endDate // ignore: cast_nullable_to_non_nullable
as DateTime,dataVersion: null == dataVersion ? _self.dataVersion : dataVersion // ignore: cast_nullable_to_non_nullable
@@ -160,10 +161,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( Map<String, GetTimetableResponse> weekCache, GetRoomsResponse? rooms, GetSubjectsResponse? subjects, GetHolidaysResponse? schoolHolidays, GetCustomTimetableEventResponse? customEvents, DateTime startDate, DateTime endDate, int dataVersion)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( Map<String, GetTimetableResponse> weekCache, GetRoomsResponse? rooms, GetSubjectsResponse? subjects, GetHolidaysResponse? schoolHolidays, GetTimegridUnitsResponse? timegrid, GetCustomTimetableEventResponse? customEvents, DateTime startDate, DateTime endDate, int dataVersion)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _TimetableState() when $default != null:
return $default(_that.weekCache,_that.rooms,_that.subjects,_that.schoolHolidays,_that.customEvents,_that.startDate,_that.endDate,_that.dataVersion);case _:
return $default(_that.weekCache,_that.rooms,_that.subjects,_that.schoolHolidays,_that.timegrid,_that.customEvents,_that.startDate,_that.endDate,_that.dataVersion);case _:
return orElse();
}
@@ -181,10 +182,10 @@ return $default(_that.weekCache,_that.rooms,_that.subjects,_that.schoolHolidays,
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( Map<String, GetTimetableResponse> weekCache, GetRoomsResponse? rooms, GetSubjectsResponse? subjects, GetHolidaysResponse? schoolHolidays, GetCustomTimetableEventResponse? customEvents, DateTime startDate, DateTime endDate, int dataVersion) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( Map<String, GetTimetableResponse> weekCache, GetRoomsResponse? rooms, GetSubjectsResponse? subjects, GetHolidaysResponse? schoolHolidays, GetTimegridUnitsResponse? timegrid, GetCustomTimetableEventResponse? customEvents, DateTime startDate, DateTime endDate, int dataVersion) $default,) {final _that = this;
switch (_that) {
case _TimetableState():
return $default(_that.weekCache,_that.rooms,_that.subjects,_that.schoolHolidays,_that.customEvents,_that.startDate,_that.endDate,_that.dataVersion);case _:
return $default(_that.weekCache,_that.rooms,_that.subjects,_that.schoolHolidays,_that.timegrid,_that.customEvents,_that.startDate,_that.endDate,_that.dataVersion);case _:
throw StateError('Unexpected subclass');
}
@@ -201,10 +202,10 @@ return $default(_that.weekCache,_that.rooms,_that.subjects,_that.schoolHolidays,
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( Map<String, GetTimetableResponse> weekCache, GetRoomsResponse? rooms, GetSubjectsResponse? subjects, GetHolidaysResponse? schoolHolidays, GetCustomTimetableEventResponse? customEvents, DateTime startDate, DateTime endDate, int dataVersion)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( Map<String, GetTimetableResponse> weekCache, GetRoomsResponse? rooms, GetSubjectsResponse? subjects, GetHolidaysResponse? schoolHolidays, GetTimegridUnitsResponse? timegrid, GetCustomTimetableEventResponse? customEvents, DateTime startDate, DateTime endDate, int dataVersion)? $default,) {final _that = this;
switch (_that) {
case _TimetableState() when $default != null:
return $default(_that.weekCache,_that.rooms,_that.subjects,_that.schoolHolidays,_that.customEvents,_that.startDate,_that.endDate,_that.dataVersion);case _:
return $default(_that.weekCache,_that.rooms,_that.subjects,_that.schoolHolidays,_that.timegrid,_that.customEvents,_that.startDate,_that.endDate,_that.dataVersion);case _:
return null;
}
@@ -216,7 +217,7 @@ return $default(_that.weekCache,_that.rooms,_that.subjects,_that.schoolHolidays,
@JsonSerializable()
class _TimetableState extends TimetableState {
const _TimetableState({final Map<String, GetTimetableResponse> weekCache = const <String, GetTimetableResponse>{}, this.rooms, this.subjects, this.schoolHolidays, this.customEvents, required this.startDate, required this.endDate, this.dataVersion = 0}): _weekCache = weekCache,super._();
const _TimetableState({final Map<String, GetTimetableResponse> weekCache = const <String, GetTimetableResponse>{}, this.rooms, this.subjects, this.schoolHolidays, this.timegrid, this.customEvents, required this.startDate, required this.endDate, this.dataVersion = 0}): _weekCache = weekCache,super._();
factory _TimetableState.fromJson(Map<String, dynamic> json) => _$TimetableStateFromJson(json);
final Map<String, GetTimetableResponse> _weekCache;
@@ -229,6 +230,7 @@ class _TimetableState extends TimetableState {
@override final GetRoomsResponse? rooms;
@override final GetSubjectsResponse? subjects;
@override final GetHolidaysResponse? schoolHolidays;
@override final GetTimegridUnitsResponse? timegrid;
@override final GetCustomTimetableEventResponse? customEvents;
@override final DateTime startDate;
@override final DateTime endDate;
@@ -247,16 +249,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _TimetableState&&const DeepCollectionEquality().equals(other._weekCache, _weekCache)&&(identical(other.rooms, rooms) || other.rooms == rooms)&&(identical(other.subjects, subjects) || other.subjects == subjects)&&(identical(other.schoolHolidays, schoolHolidays) || other.schoolHolidays == schoolHolidays)&&(identical(other.customEvents, customEvents) || other.customEvents == customEvents)&&(identical(other.startDate, startDate) || other.startDate == startDate)&&(identical(other.endDate, endDate) || other.endDate == endDate)&&(identical(other.dataVersion, dataVersion) || other.dataVersion == dataVersion));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _TimetableState&&const DeepCollectionEquality().equals(other._weekCache, _weekCache)&&(identical(other.rooms, rooms) || other.rooms == rooms)&&(identical(other.subjects, subjects) || other.subjects == subjects)&&(identical(other.schoolHolidays, schoolHolidays) || other.schoolHolidays == schoolHolidays)&&(identical(other.timegrid, timegrid) || other.timegrid == timegrid)&&(identical(other.customEvents, customEvents) || other.customEvents == customEvents)&&(identical(other.startDate, startDate) || other.startDate == startDate)&&(identical(other.endDate, endDate) || other.endDate == endDate)&&(identical(other.dataVersion, dataVersion) || other.dataVersion == dataVersion));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_weekCache),rooms,subjects,schoolHolidays,customEvents,startDate,endDate,dataVersion);
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_weekCache),rooms,subjects,schoolHolidays,timegrid,customEvents,startDate,endDate,dataVersion);
@override
String toString() {
return 'TimetableState(weekCache: $weekCache, rooms: $rooms, subjects: $subjects, schoolHolidays: $schoolHolidays, customEvents: $customEvents, startDate: $startDate, endDate: $endDate, dataVersion: $dataVersion)';
return 'TimetableState(weekCache: $weekCache, rooms: $rooms, subjects: $subjects, schoolHolidays: $schoolHolidays, timegrid: $timegrid, customEvents: $customEvents, startDate: $startDate, endDate: $endDate, dataVersion: $dataVersion)';
}
@@ -267,7 +269,7 @@ abstract mixin class _$TimetableStateCopyWith<$Res> implements $TimetableStateCo
factory _$TimetableStateCopyWith(_TimetableState value, $Res Function(_TimetableState) _then) = __$TimetableStateCopyWithImpl;
@override @useResult
$Res call({
Map<String, GetTimetableResponse> weekCache, GetRoomsResponse? rooms, GetSubjectsResponse? subjects, GetHolidaysResponse? schoolHolidays, GetCustomTimetableEventResponse? customEvents, DateTime startDate, DateTime endDate, int dataVersion
Map<String, GetTimetableResponse> weekCache, GetRoomsResponse? rooms, GetSubjectsResponse? subjects, GetHolidaysResponse? schoolHolidays, GetTimegridUnitsResponse? timegrid, GetCustomTimetableEventResponse? customEvents, DateTime startDate, DateTime endDate, int dataVersion
});
@@ -284,13 +286,14 @@ class __$TimetableStateCopyWithImpl<$Res>
/// Create a copy of TimetableState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? weekCache = null,Object? rooms = freezed,Object? subjects = freezed,Object? schoolHolidays = freezed,Object? customEvents = freezed,Object? startDate = null,Object? endDate = null,Object? dataVersion = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? weekCache = null,Object? rooms = freezed,Object? subjects = freezed,Object? schoolHolidays = freezed,Object? timegrid = freezed,Object? customEvents = freezed,Object? startDate = null,Object? endDate = null,Object? dataVersion = null,}) {
return _then(_TimetableState(
weekCache: null == weekCache ? _self._weekCache : weekCache // ignore: cast_nullable_to_non_nullable
as Map<String, GetTimetableResponse>,rooms: freezed == rooms ? _self.rooms : rooms // ignore: cast_nullable_to_non_nullable
as GetRoomsResponse?,subjects: freezed == subjects ? _self.subjects : subjects // ignore: cast_nullable_to_non_nullable
as GetSubjectsResponse?,schoolHolidays: freezed == schoolHolidays ? _self.schoolHolidays : schoolHolidays // ignore: cast_nullable_to_non_nullable
as GetHolidaysResponse?,customEvents: freezed == customEvents ? _self.customEvents : customEvents // ignore: cast_nullable_to_non_nullable
as GetHolidaysResponse?,timegrid: freezed == timegrid ? _self.timegrid : timegrid // ignore: cast_nullable_to_non_nullable
as GetTimegridUnitsResponse?,customEvents: freezed == customEvents ? _self.customEvents : customEvents // ignore: cast_nullable_to_non_nullable
as GetCustomTimetableEventResponse?,startDate: null == startDate ? _self.startDate : startDate // ignore: cast_nullable_to_non_nullable
as DateTime,endDate: null == endDate ? _self.endDate : endDate // ignore: cast_nullable_to_non_nullable
as DateTime,dataVersion: null == dataVersion ? _self.dataVersion : dataVersion // ignore: cast_nullable_to_non_nullable
@@ -29,6 +29,11 @@ _TimetableState _$TimetableStateFromJson(Map<String, dynamic> json) =>
: GetHolidaysResponse.fromJson(
json['schoolHolidays'] as Map<String, dynamic>,
),
timegrid: json['timegrid'] == null
? null
: GetTimegridUnitsResponse.fromJson(
json['timegrid'] as Map<String, dynamic>,
),
customEvents: json['customEvents'] == null
? null
: GetCustomTimetableEventResponse.fromJson(
@@ -45,6 +50,7 @@ Map<String, dynamic> _$TimetableStateToJson(_TimetableState instance) =>
'rooms': instance.rooms,
'subjects': instance.subjects,
'schoolHolidays': instance.schoolHolidays,
'timegrid': instance.timegrid,
'customEvents': instance.customEvents,
'startDate': instance.startDate.toIso8601String(),
'endDate': instance.endDate.toIso8601String(),
@@ -1,5 +1,3 @@
import 'dart:async';
import 'package:intl/intl.dart';
import '../../../../../api/mhsl/customTimetableEvent/add/addCustomTimetableEvent.dart';
@@ -18,6 +16,8 @@ import '../../../../../api/webuntis/queries/getRooms/getRoomsCache.dart';
import '../../../../../api/webuntis/queries/getRooms/getRoomsResponse.dart';
import '../../../../../api/webuntis/queries/getSubjects/getSubjectsCache.dart';
import '../../../../../api/webuntis/queries/getSubjects/getSubjectsResponse.dart';
import '../../../../../api/webuntis/queries/getTimegridUnits/getTimegridUnitsCache.dart';
import '../../../../../api/webuntis/queries/getTimegridUnits/getTimegridUnitsResponse.dart';
import '../../../../../api/webuntis/queries/getTimetable/getTimetableCache.dart';
import '../../../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart';
import '../../../../../model/accountData.dart';
@@ -25,55 +25,116 @@ import '../../../../../model/accountData.dart';
class TimetableDataProvider {
static final DateFormat _dateFormat = DateFormat('yyyyMMdd');
Future<GetTimetableResponse> getWeek(DateTime startDate, DateTime endDate) {
final completer = Completer<GetTimetableResponse>();
GetTimetableCache(
Future<GetTimetableResponse> getWeek(
DateTime startDate,
DateTime endDate, {
void Function(Object)? onError,
bool renew = false,
}) async {
GetTimetableResponse? latest;
Object? capturedError;
final cache = GetTimetableCache(
startdate: int.parse(_dateFormat.format(startDate)),
enddate: int.parse(_dateFormat.format(endDate)),
onUpdate: (data) {
if (!completer.isCompleted) completer.complete(data);
},
renew: renew,
onUpdate: (data) => latest = data,
onError: (e) {
if (!completer.isCompleted) completer.completeError(e);
capturedError = e;
onError?.call(e);
},
);
return completer.future;
await cache.ready;
if (latest != null) return latest!;
throw capturedError ?? Exception('No data and no error from getWeek');
}
Future<GetRoomsResponse> getRooms() {
final completer = Completer<GetRoomsResponse>();
GetRoomsCache(onUpdate: (data) {
if (!completer.isCompleted) completer.complete(data);
});
return completer.future;
Future<GetRoomsResponse> getRooms({
void Function(Object)? onError,
bool renew = false,
}) async {
GetRoomsResponse? latest;
Object? capturedError;
final cache = GetRoomsCache(
renew: renew,
onUpdate: (data) => latest = data,
onError: (e) {
capturedError = e;
onError?.call(e);
},
);
await cache.ready;
if (latest != null) return latest!;
throw capturedError ?? Exception('No data and no error from getRooms');
}
Future<GetSubjectsResponse> getSubjects() {
final completer = Completer<GetSubjectsResponse>();
GetSubjectsCache(onUpdate: (data) {
if (!completer.isCompleted) completer.complete(data);
});
return completer.future;
Future<GetSubjectsResponse> getSubjects({
void Function(Object)? onError,
bool renew = false,
}) async {
GetSubjectsResponse? latest;
Object? capturedError;
final cache = GetSubjectsCache(
renew: renew,
onUpdate: (data) => latest = data,
onError: (e) {
capturedError = e;
onError?.call(e);
},
);
await cache.ready;
if (latest != null) return latest!;
throw capturedError ?? Exception('No data and no error from getSubjects');
}
Future<GetHolidaysResponse> getSchoolHolidays() {
final completer = Completer<GetHolidaysResponse>();
GetHolidaysCache(onUpdate: (data) {
if (!completer.isCompleted) completer.complete(data);
});
return completer.future;
Future<GetHolidaysResponse> getSchoolHolidays({
void Function(Object)? onError,
bool renew = false,
}) async {
GetHolidaysResponse? latest;
Object? capturedError;
final cache = GetHolidaysCache(
renew: renew,
onUpdate: (data) => latest = data,
onError: (e) {
capturedError = e;
onError?.call(e);
},
);
await cache.ready;
if (latest != null) return latest!;
throw capturedError ?? Exception('No data and no error from getSchoolHolidays');
}
Future<GetCustomTimetableEventResponse> getCustomEvents({bool renew = false}) {
final completer = Completer<GetCustomTimetableEventResponse>();
GetCustomTimetableEventCache(
Future<GetTimegridUnitsResponse> getTimegrid({bool renew = false}) async {
GetTimegridUnitsResponse? latest;
Object? capturedError;
final cache = GetTimegridUnitsCache(
renew: renew,
onUpdate: (data) => latest = data,
);
await cache.ready;
if (latest != null) return latest!;
throw capturedError ?? Exception('No data and no error from getTimegrid');
}
Future<GetCustomTimetableEventResponse> getCustomEvents({
bool renew = false,
void Function(Object)? onError,
}) async {
GetCustomTimetableEventResponse? latest;
Object? capturedError;
final cache = GetCustomTimetableEventCache(
GetCustomTimetableEventParams(AccountData().getUserSecret()),
renew: renew,
onUpdate: (data) {
if (!completer.isCompleted) completer.complete(data);
onUpdate: (data) => latest = data,
onError: (e) {
capturedError = e;
onError?.call(e);
},
);
return completer.future;
await cache.ready;
if (latest != null) return latest!;
throw capturedError ?? Exception('No data and no error from getCustomEvents');
}
Future<void> addCustomEvent(CustomTimetableEvent event) =>