added guardian login with views for their assigned childs

This commit is contained in:
2026-09-20 11:11:58 +02:00
parent e4e2b1a4fb
commit 67c935c05b
117 changed files with 4784 additions and 1514 deletions
@@ -4,12 +4,17 @@ import '../../../../../api/marianumconnect/queries/timetable_custom_events/custo
import '../../../../../api/marianumconnect/queries/timetable_get_week/timetable_get_week_response.dart';
import '../../../../../api/mhsl/custom_timetable_event/custom_timetable_event.dart';
import '../../../../../extensions/date_time.dart';
import '../../../infrastructure/loadable_state/loadable_state.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 '../repository/timetable_repository.dart';
import '../subject/timetable_subject.dart';
import 'timetable_event.dart';
import 'timetable_state.dart';
/// Drives one [TimetableSubject]'s plan. The same class serves the own plan
/// and foreign element plans; everything subject-specific (endpoint,
/// persistence, custom events) is derived from [subject].
class TimetableBloc
extends
LoadableHydratedBloc<
@@ -17,6 +22,13 @@ class TimetableBloc
TimetableState,
TimetableRepository
> {
final TimetableSubject subject;
TimetableBloc({required this.subject});
@override
String get id => subject.storageId;
DateTime _lastWeekRequestStart = DateTime.fromMillisecondsSinceEpoch(0);
/// Set by [retry] to force the next [gatherData] to bypass cache freshness
@@ -59,8 +71,25 @@ class TimetableBloc
@override
Map<String, dynamic>? toStorage(TimetableState state) => state.toJson();
@override
Map<String, dynamic>? toJson(LoadableState<TimetableState> state) =>
subject.persistent ? super.toJson(state) : null;
@override
LoadableState<TimetableState> fromJson(Map<String, dynamic> json) =>
subject.persistent
? super.fromJson(json)
: const LoadableState(
isLoading: true,
data: null,
lastFetch: null,
reFetch: null,
error: null,
);
@override
Future<void> gatherData() async {
if (subject is NoTimetable) return;
final initial = innerState ?? fromNothing();
final renew = _forceRenew;
_forceRenew = false;
@@ -75,10 +104,10 @@ class TimetableBloc
initial.startDate,
initial.endDate,
onError: recordError,
renew: renew,
),
_loadStaticReferenceData(onError: recordError, renew: renew),
_loadCustomEvents(onError: recordError, renew: renew),
if (subject.supportsCustomEvents)
_loadCustomEvents(onError: recordError, renew: renew),
]);
if (firstError != null) throw firstError!;
@@ -102,17 +131,28 @@ class TimetableBloc
void refresh() => fetch();
/// Custom events belong to the signed-in user's own plan only — never to a
/// foreign plan or a guardian's view of a child.
void _requireCustomEvents() {
if (!subject.supportsCustomEvents) {
throw StateError('Custom events are not available for $subject');
}
}
Future<void> addCustomEvent(CustomTimetableEvent event) async {
_requireCustomEvents();
await repo.data.addCustomEvent(event);
await _refreshCustomEvents();
}
Future<void> updateCustomEvent(String id, CustomTimetableEvent event) async {
_requireCustomEvents();
await repo.data.updateCustomEvent(id, event);
await _refreshCustomEvents();
}
Future<void> removeCustomEvent(String id) async {
_requireCustomEvents();
await repo.data.removeCustomEvent(id);
await _refreshCustomEvents();
}
@@ -142,16 +182,15 @@ class TimetableBloc
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(
subject,
startDate,
endDate,
onError: onError,
renew: renew,
);
if (_lastWeekRequestStart.isAfter(requestStart)) return;
_writeWeekToCache(startDate, week);
@@ -237,7 +276,7 @@ class TimetableBloc
void _prefetchWeek(DateTime start, DateTime end) {
repo.data
.getWeek(start, end)
.getWeek(subject, start, end)
.then((week) => _writeWeekToCache(start, week))
.catchError((_) {});
}
@@ -265,3 +304,11 @@ class TimetableBloc
return DateTime(friday.year, friday.month, friday.day);
}
}
/// Type token for a page-scoped plan (e.g. a foreign element). Carries no
/// logic of its own; the distinct type keeps a page-local provider from
/// shadowing the app-wide [TimetableBloc] that sheets and root-navigator pages
/// (subject colours, custom events) read.
final class ScopedTimetableBloc extends TimetableBloc {
ScopedTimetableBloc({required super.subject});
}