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
+7 -4
View File
@@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../state/app/modules/timetable/bloc/timetable_state.dart';
import '../state/app/modules/timetable/subject/timetable_subject.dart';
import '../storage/settings.dart';
import 'widget_data_mapper.dart';
import 'widget_sync.dart';
@@ -21,8 +22,9 @@ class WidgetPublisher {
static Future<void> publishFromBlocState(
TimetableState state, {
required TimetableSubject subject,
Settings? settings,
bool isTeacher = false,
bool showClassInsteadOfTeacher = false,
}) async {
try {
final connectDouble =
@@ -32,7 +34,8 @@ class WidgetPublisher {
await Future.wait([
WidgetSync.setConnectDoubleLessons(connectDouble),
WidgetSync.setThemeMode(_themeName(settings?.appTheme)),
WidgetSync.setIsTeacher(isTeacher),
WidgetSync.setShowClassInsteadOfTeacher(showClassInsteadOfTeacher),
WidgetSync.setSubject(subject),
]);
final lessons = state.getAllKnownLessons();
final now = widgetNow();
@@ -45,7 +48,7 @@ class WidgetPublisher {
timegrid: state.timegrid,
customEvents: state.customEvents,
connectDoubleLessons: connectDouble,
showClassInsteadOfTeacher: isTeacher,
showClassInsteadOfTeacher: showClassInsteadOfTeacher,
);
final weekData = WidgetDataMapper.buildWeekData(
now: now,
@@ -56,7 +59,7 @@ class WidgetPublisher {
timegrid: state.timegrid,
customEvents: state.customEvents,
connectDoubleLessons: connectDouble,
showClassInsteadOfTeacher: isTeacher,
showClassInsteadOfTeacher: showClassInsteadOfTeacher,
);
await WidgetSync.writeDayData(dayData);
await WidgetSync.writeWeekData(weekData);
+45 -7
View File
@@ -4,6 +4,7 @@ import 'dart:developer';
import 'package:home_widget/home_widget.dart';
import '../state/app/modules/timetable/subject/timetable_subject.dart';
import 'widget_data.dart';
/// Bridge to the native widget host. All keys/names live here so the Kotlin
@@ -31,15 +32,50 @@ class WidgetSync {
static const String connectDoubleLessonsKey =
'widget_setting_connect_double_lessons_v1';
static const String themeModeKey = 'widget_setting_theme_mode_v1';
// Mirrored from CapabilitiesCubit so the background isolate can render
// teacher plans (class instead of teacher name) without bloc storage.
static const String isTeacherKey = 'widget_setting_is_teacher_v1';
// Mirrors the resolved TimetablePolicy flag so the background isolate
// renders tiles like the app does, without bloc storage. The key predates
// the policy (it used to mirror "is teacher") and keeps its name so
// existing installs stay consistent.
static const String showClassInsteadOfTeacherKey =
'widget_setting_is_teacher_v1';
// Mirrored so the background isolate hits the same Marianum-Connect base
// URL the in-app settings cubit currently has selected.
static const String marianumConnectBaseUrlKey = 'widget_setting_mc_base_url_v1';
static const String marianumConnectBaseUrlKey =
'widget_setting_mc_base_url_v1';
// Which plan the widget shows ('own' or 'child:<id>'), so the background
// isolate fetches the same subject as the app.
static const String subjectKey = 'widget_setting_subject_v1';
static bool _initialised = false;
/// Only the primary subjects can back the widget; foreign plans cannot.
static String? encodeSubject(TimetableSubject subject) => switch (subject) {
OwnTimetable() => 'own',
ChildTimetable(:final childId) => 'child:$childId',
ElementTimetable() || NoTimetable() => null,
};
/// A missing value means an install from before guardian accounts, which
/// always showed the own plan.
static TimetableSubject? decodeSubject(String? value) {
if (value == null || value == 'own') return const OwnTimetable();
if (value.startsWith('child:') && value.length > 'child:'.length) {
return ChildTimetable(value.substring('child:'.length));
}
return null;
}
static Future<void> setSubject(TimetableSubject subject) async {
await ensureInitialized();
await HomeWidget.saveWidgetData<String>(subjectKey, encodeSubject(subject));
}
static Future<TimetableSubject?> getSubject() async {
await ensureInitialized();
return decodeSubject(await HomeWidget.getWidgetData<String>(subjectKey));
}
static Future<void> ensureInitialized() async {
if (_initialised) return;
await HomeWidget.setAppGroupId(iosAppGroupId);
@@ -72,10 +108,11 @@ class WidgetSync {
static Future<bool> getConnectDoubleLessons() =>
_getBool(connectDoubleLessonsKey, defaultValue: true);
static Future<void> setIsTeacher(bool value) => _setBool(isTeacherKey, value);
static Future<void> setShowClassInsteadOfTeacher(bool value) =>
_setBool(showClassInsteadOfTeacherKey, value);
static Future<bool> getIsTeacher() =>
_getBool(isTeacherKey, defaultValue: false);
static Future<bool> getShowClassInsteadOfTeacher() =>
_getBool(showClassInsteadOfTeacherKey, defaultValue: false);
static Future<void> _setBool(String key, bool value) async {
await ensureInitialized();
@@ -114,6 +151,7 @@ class WidgetSync {
await HomeWidget.saveWidgetData<String>(weekDataKey, null);
await HomeWidget.saveWidgetData<String>(fetchedAtKey, null);
await HomeWidget.saveWidgetData<bool>(loggedInKey, false);
await HomeWidget.saveWidgetData<String>(subjectKey, null);
}
static Future<void> triggerUpdate() async {