added guardian login with views for their assigned childs
This commit is contained in:
@@ -6,6 +6,7 @@ import 'package:flutter/widgets.dart';
|
||||
import 'package:workmanager/workmanager.dart';
|
||||
|
||||
import '../api/marianumconnect/marianumconnect_endpoint.dart';
|
||||
import '../api/marianumconnect/queries/timetable_custom_events/timetable_custom_events_get.dart';
|
||||
import '../api/marianumconnect/queries/timetable_get_holidays/timetable_get_holidays.dart';
|
||||
import '../api/marianumconnect/queries/timetable_get_holidays/timetable_get_holidays_response.dart';
|
||||
import '../api/marianumconnect/queries/timetable_get_rooms/timetable_get_rooms.dart';
|
||||
@@ -14,11 +15,11 @@ import '../api/marianumconnect/queries/timetable_get_subjects/timetable_get_subj
|
||||
import '../api/marianumconnect/queries/timetable_get_subjects/timetable_get_subjects_response.dart';
|
||||
import '../api/marianumconnect/queries/timetable_get_timegrid/timetable_get_timegrid.dart';
|
||||
import '../api/marianumconnect/queries/timetable_get_timegrid/timetable_get_timegrid_response.dart';
|
||||
import '../api/marianumconnect/queries/timetable_get_week/timetable_get_week.dart';
|
||||
import '../api/mhsl/custom_timetable_event/get/get_custom_timetable_event.dart';
|
||||
import '../api/mhsl/custom_timetable_event/get/get_custom_timetable_event_params.dart';
|
||||
import '../api/mhsl/custom_timetable_event/get/get_custom_timetable_event_response.dart';
|
||||
import '../model/account_data.dart';
|
||||
import '../session/session.dart';
|
||||
import '../session/session_manager.dart';
|
||||
import '../state/app/modules/timetable/data_provider/timetable_data_provider.dart';
|
||||
import '../state/app/modules/timetable/subject/timetable_subject.dart';
|
||||
import '../widget_data/widget_data_mapper.dart';
|
||||
import '../widget_data/widget_publisher.dart';
|
||||
import '../widget_data/widget_sync.dart';
|
||||
@@ -81,17 +82,17 @@ class WidgetBackgroundTask {
|
||||
/// Throws on fetch failure so the worker path can signal a retry.
|
||||
static Future<void> runRefreshNow({bool force = false}) async {
|
||||
await WidgetSync.ensureInitialized();
|
||||
bool populated;
|
||||
Session? session;
|
||||
try {
|
||||
// Bounded: a hanging keystore read must not stall the caller's budget
|
||||
// (FCM handler ~25s on iOS) forever.
|
||||
populated = await AccountData().waitForPopulation().timeout(
|
||||
session = await SessionManager().waitForLoad().timeout(
|
||||
const Duration(seconds: 10),
|
||||
);
|
||||
} on TimeoutException {
|
||||
populated = false;
|
||||
session = null;
|
||||
}
|
||||
if (!populated) {
|
||||
if (session == null) {
|
||||
// Deliberately does NOT flip the widget to logged-out: a failed or slow
|
||||
// keychain read (locked iOS device during the 06:00 silent push) is
|
||||
// indistinguishable from "never logged in" here, and blanking the
|
||||
@@ -101,11 +102,23 @@ class WidgetBackgroundTask {
|
||||
return;
|
||||
}
|
||||
final fetchedAt = await WidgetSync.getFetchedAt();
|
||||
if (shouldSkipRefresh(fetchedAt: fetchedAt, now: DateTime.now(), force: force)) {
|
||||
if (shouldSkipRefresh(
|
||||
fetchedAt: fetchedAt,
|
||||
now: DateTime.now(),
|
||||
force: force,
|
||||
)) {
|
||||
log('[widget-refresh] snapshot is fresh, skipping refresh');
|
||||
return;
|
||||
}
|
||||
await _refresh();
|
||||
final subject = widgetRefreshSubject(
|
||||
session: session,
|
||||
stored: await WidgetSync.getSubject(),
|
||||
);
|
||||
if (subject == null) {
|
||||
log('[widget-refresh] no plan selected yet, skipping refresh');
|
||||
return;
|
||||
}
|
||||
await _refresh(subject);
|
||||
}
|
||||
|
||||
static Future<void> cancelAll() async {
|
||||
@@ -126,6 +139,16 @@ bool shouldSkipRefresh({
|
||||
return !age.isNegative && age < WidgetBackgroundTask.refreshDebounce;
|
||||
}
|
||||
|
||||
/// The plan the background refresh loads. Guardians only get one once the app
|
||||
/// has published a child; before that there is nothing sensible to fetch.
|
||||
TimetableSubject? widgetRefreshSubject({
|
||||
required Session session,
|
||||
required TimetableSubject? stored,
|
||||
}) => switch (session) {
|
||||
CredentialSession() => const OwnTimetable(),
|
||||
GuardianSession() => stored is ChildTimetable ? stored : null,
|
||||
};
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void _callbackDispatcher() {
|
||||
Workmanager().executeTask((task, inputData) async {
|
||||
@@ -142,7 +165,7 @@ void _callbackDispatcher() {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _refresh() async {
|
||||
Future<void> _refresh(TimetableSubject subject) async {
|
||||
await WidgetSync.ensureInitialized();
|
||||
// The background isolate doesn't go through main.dart's BlocBuilder, so we
|
||||
// re-apply the endpoint the foreground last persisted. Without this the
|
||||
@@ -165,9 +188,11 @@ Future<void> _refresh() async {
|
||||
// latency is the slowest request, not the sum (matters for the push path's
|
||||
// hard time budget). Reference-data failures fall through to null in the
|
||||
// mapper rather than aborting the whole refresh.
|
||||
final timetableFuture = TimetableGetWeek().run(
|
||||
final until = weekEndExclusive.subtract(const Duration(days: 1));
|
||||
final timetableFuture = TimetableDataProvider.fetchWeek(
|
||||
subject,
|
||||
from: weekStart,
|
||||
until: weekEndExclusive.subtract(const Duration(days: 1)),
|
||||
until: until,
|
||||
);
|
||||
final subjectsFuture = _runOrNull<TimetableGetSubjectsResponse>(
|
||||
() => TimetableGetSubjects().run(),
|
||||
@@ -181,11 +206,11 @@ Future<void> _refresh() async {
|
||||
final timegridFuture = _runOrNull<TimetableGetTimegridResponse>(
|
||||
() => TimetableGetTimegrid().run(),
|
||||
);
|
||||
final customEventsFuture = _runOrNull<GetCustomTimetableEventResponse>(
|
||||
() => GetCustomTimetableEvent(
|
||||
GetCustomTimetableEventParams(AccountData().getUserSecret()),
|
||||
).run(),
|
||||
);
|
||||
final customEventsFuture = subject.supportsCustomEvents
|
||||
? _runOrNull<GetCustomTimetableEventResponse>(
|
||||
() => TimetableCustomEventsGet().run(),
|
||||
)
|
||||
: Future<GetCustomTimetableEventResponse?>.value();
|
||||
final timetable = await timetableFuture;
|
||||
final subjects = await subjectsFuture;
|
||||
final rooms = await roomsFuture;
|
||||
@@ -195,9 +220,9 @@ Future<void> _refresh() async {
|
||||
|
||||
final lessons = timetable.entries;
|
||||
|
||||
final [connectDouble, isTeacher] = await Future.wait([
|
||||
final [connectDouble, showClassInsteadOfTeacher] = await Future.wait([
|
||||
WidgetSync.getConnectDoubleLessons(),
|
||||
WidgetSync.getIsTeacher(),
|
||||
WidgetSync.getShowClassInsteadOfTeacher(),
|
||||
]);
|
||||
final dayData = WidgetDataMapper.buildDayData(
|
||||
now: now,
|
||||
@@ -208,7 +233,7 @@ Future<void> _refresh() async {
|
||||
timegrid: timegrid,
|
||||
customEvents: customEvents,
|
||||
connectDoubleLessons: connectDouble,
|
||||
showClassInsteadOfTeacher: isTeacher,
|
||||
showClassInsteadOfTeacher: showClassInsteadOfTeacher,
|
||||
);
|
||||
final weekData = WidgetDataMapper.buildWeekData(
|
||||
now: now,
|
||||
@@ -219,9 +244,15 @@ Future<void> _refresh() async {
|
||||
timegrid: timegrid,
|
||||
customEvents: customEvents,
|
||||
connectDoubleLessons: connectDouble,
|
||||
showClassInsteadOfTeacher: isTeacher,
|
||||
showClassInsteadOfTeacher: showClassInsteadOfTeacher,
|
||||
);
|
||||
|
||||
// The user may have switched the child while the requests ran; writing now
|
||||
// would show the previous child's plan.
|
||||
if (await WidgetSync.getSubject() != subject) {
|
||||
log('[widget-bg] subject changed during refresh, discarding');
|
||||
return;
|
||||
}
|
||||
await WidgetSync.writeDayData(dayData);
|
||||
await WidgetSync.writeWeekData(weekData);
|
||||
await WidgetSync.setLoggedIn(true);
|
||||
|
||||
Reference in New Issue
Block a user