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
@@ -0,0 +1,18 @@
import '../../../../../api/marianumconnect/queries/get_capabilities/guardian_child.dart';
import '../../../../../session/session.dart';
import '../../children/child_selection_cubit.dart';
import '../subject/timetable_subject.dart';
/// Whose plan the timetable tab shows for the active session.
TimetableSubject resolvePrimarySubject({
required Session? session,
required List<GuardianChild> children,
required String? selectedChildId,
}) => switch (session) {
null => const NoTimetable(),
CredentialSession() => const OwnTimetable(),
GuardianSession() => switch (effectiveChild(children, selectedChildId)) {
null => const NoTimetable(),
final child => ChildTimetable(child.id),
},
};
@@ -0,0 +1,81 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../../session/session_manager.dart';
import '../../account/bloc/account_bloc.dart';
import '../../account/bloc/account_state.dart';
import '../../capabilities/bloc/capabilities_cubit.dart';
import '../../capabilities/bloc/capabilities_state.dart';
import '../../children/child_selection_cubit.dart';
import '../bloc/timetable_bloc.dart';
import '../subject/timetable_subject.dart';
import 'primary_subject_resolver.dart';
/// Provides the app-wide [TimetableBloc] for the session's primary subject
/// (own plan, or the selected child for guardians) and swaps it for a fresh
/// instance when that subject changes.
///
/// Sits above MaterialApp so root-navigator pages (subject colours, custom
/// events) reach it. The widget subtree is kept on a swap — only the provided
/// instance changes, which BlocBuilder/BlocListener pick up — so switching
/// the child does not reset the navigation. A new instance per subject (rather
/// than retargeting one bloc) keeps late responses for the previous child out
/// of the new child's week cache.
class PrimaryTimetableScope extends StatefulWidget {
final Widget child;
const PrimaryTimetableScope({required this.child, super.key});
@override
State<PrimaryTimetableScope> createState() => _PrimaryTimetableScopeState();
}
class _PrimaryTimetableScopeState extends State<PrimaryTimetableScope> {
late TimetableBloc _bloc;
@override
void initState() {
super.initState();
_bloc = TimetableBloc(subject: _resolve());
}
@override
void dispose() {
_bloc.close();
super.dispose();
}
TimetableSubject _resolve() => resolvePrimarySubject(
session: SessionManager().current,
children: context.read<CapabilitiesCubit>().state.children,
selectedChildId: context.read<ChildSelectionCubit>().state,
);
void _sync() {
final subject = _resolve();
if (subject == _bloc.subject) return;
final previous = _bloc;
setState(() => _bloc = TimetableBloc(subject: subject));
// Dependents re-subscribe during the next build; close afterwards.
WidgetsBinding.instance.addPostFrameCallback((_) => previous.close());
}
@override
Widget build(BuildContext context) => MultiBlocListener(
listeners: [
BlocListener<AccountBloc, AccountState>(
listenWhen: (a, b) => a.status != b.status,
listener: (_, _) => _sync(),
),
BlocListener<CapabilitiesCubit, CapabilitiesState>(
listenWhen: (a, b) => a.children != b.children,
listener: (_, _) => _sync(),
),
BlocListener<ChildSelectionCubit, String?>(listener: (_, _) => _sync()),
],
child: BlocProvider<TimetableBloc>.value(
value: _bloc,
child: widget.child,
),
);
}