Files
Client/lib/state/app/modules/timetable/bloc/timetable_bloc.dart
T

119 lines
3.8 KiB
Dart

import '../../../../../api/marianumconnect/queries/timetable_custom_events/custom_events_migration.dart';
import '../../../../../api/marianumconnect/queries/timetable_get_week/timetable_get_week_response.dart';
import '../../../../../api/mhsl/custom_timetable_event/custom_timetable_event.dart';
import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc.dart';
import '../../../infrastructure/utility_widgets/loadable_hydrated_bloc/loadable_hydrated_bloc_event.dart';
import '../data_provider/timetable_data_provider.dart';
import '../repository/timetable_repository.dart';
import 'timetable_event.dart';
import 'timetable_state.dart';
import 'timetable_week_loader.dart';
class TimetableBloc
extends
LoadableHydratedBloc<
TimetableEvent,
TimetableState,
TimetableRepository
>
with TimetableWeekLoader<TimetableRepository> {
/// Set by [retry] to force the next [gatherData] to bypass the custom-event
/// cache 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();
@override
TimetableDataProvider get referenceData => repo.data;
@override
Future<TimetableGetWeekResponse> fetchWeek(DateTime start, DateTime end) =>
repo.data.getWeek(start, end);
/// Persisted state may carry a stale `startDate`/`endDate` from the user's
/// last view. Reset on every cold start so the calendar always mounts on
/// the current week, not on whatever week the user closed the app on.
@override
TimetableState fromStorage(Map<String, dynamic> json) {
final (start, end) = currentTimetableWeek();
return TimetableState.fromJson(json).copyWith(
startDate: start,
endDate: end,
accessibleStartDate: null,
accessibleEndDate: null,
);
}
@override
Map<String, dynamic>? toStorage(TimetableState state) => state.toJson();
@override
Future<void> gatherData() {
final renew = _forceRenew;
_forceRenew = false;
return gatherWeek(
also: (onError) => _loadCustomEvents(onError: onError, renew: renew),
);
}
Future<void> addCustomEvent(CustomTimetableEvent event) async {
await repo.data.addCustomEvent(event);
await _refreshCustomEvents();
}
Future<void> updateCustomEvent(String id, CustomTimetableEvent event) async {
await repo.data.updateCustomEvent(id, event);
await _refreshCustomEvents();
}
Future<void> removeCustomEvent(String id) async {
await repo.data.removeCustomEvent(id);
await _refreshCustomEvents();
}
/// Setzt die persönliche Farbe eines Fachs (Schlüssel: Fach-Kürzel) und lädt
/// die Fächer neu, damit die eingebettete Farbe im Kalender wirkt.
Future<void> setSubjectColor(String subjectShort, String color) async {
await repo.data.setSubjectColor(subjectShort, color);
await _refreshSubjects();
}
Future<void> clearSubjectColor(String subjectShort) async {
await repo.data.removeSubjectColor(subjectShort);
await _refreshSubjects();
}
Future<void> _refreshSubjects() async {
final subjects = await repo.data.getSubjects();
add(DataGathered((s) => s.withReferenceData(subjects: subjects)));
}
Future<void> _loadCustomEvents({
void Function(Object)? onError,
bool renew = false,
}) async {
try {
await CustomEventsMigration.runOnce();
final events = await repo.data.getCustomEvents(
renew: renew,
onError: onError,
);
add(Emit((s) => s.withReferenceData(customEvents: events)));
} catch (e) {
onError?.call(e);
}
}
Future<void> _refreshCustomEvents() async {
final events = await repo.data.getCustomEvents(renew: true);
add(DataGathered((s) => s.withReferenceData(customEvents: events)));
}
}