From 97d8cab82bf43992da4a56af85b4c0391dcdcc45 Mon Sep 17 00:00:00 2001
From: doodlezucc <electerminator@gmail.com>
Date: Wed, 25 Oct 2023 09:03:17 +0200
Subject: [PATCH] make context profile listenable

---
 lib/context.dart                  |  4 ++--
 lib/drawer/class_list_item.dart   |  3 ++-
 lib/drawer/lecture_list_item.dart |  3 ++-
 lib/main.dart                     | 35 ++++++++++++++++---------------
 lib/timetable/lesson_dialog.dart  |  6 +++---
 lib/timetable/timetable.dart      | 25 +++++++++++++---------
 lib/util/listenable.dart          |  5 +++++
 7 files changed, 47 insertions(+), 34 deletions(-)

diff --git a/lib/context.dart b/lib/context.dart
index 19feb26..0240ceb 100644
--- a/lib/context.dart
+++ b/lib/context.dart
@@ -6,10 +6,10 @@ import 'util/listenable.dart';
 
 class AppContext {
   final UntisFetch untis;
-  Profile? profile;
+  final Listenable<Profile?> profile = Listenable(null);
   List<Class>? classes;
   List<Lecture>? lectures;
-  Listenable<DateTime> date = Listenable(DateTime.now());
+  final Listenable<DateTime> date = Listenable(DateTime.now());
 
   AppContext(this.untis);
 }
diff --git a/lib/drawer/class_list_item.dart b/lib/drawer/class_list_item.dart
index b979106..d27d5fb 100644
--- a/lib/drawer/class_list_item.dart
+++ b/lib/drawer/class_list_item.dart
@@ -21,7 +21,8 @@ class ClassListItem extends StatefulWidget {
 }
 
 class _ClassListItemState extends State<ClassListItem> {
-  Set<int> get selectedClasses => widget.appContext.profile!.selectedClasses;
+  Set<int> get selectedClasses =>
+      widget.appContext.profile.value!.selectedClasses;
   bool get isSelected => selectedClasses.contains(widget.cls.id);
 
   List<Lecture>? get lectures => widget.appContext.lectures == null
diff --git a/lib/drawer/lecture_list_item.dart b/lib/drawer/lecture_list_item.dart
index 2e0fea3..ed47fb4 100644
--- a/lib/drawer/lecture_list_item.dart
+++ b/lib/drawer/lecture_list_item.dart
@@ -20,7 +20,8 @@ class LectureListItem extends StatefulWidget {
 }
 
 class _LectureListItemState extends State<LectureListItem> {
-  Set<int> get hiddenLectures => widget.appContext.profile!.hiddenLectures;
+  Set<int> get hiddenLectures =>
+      widget.appContext.profile.value!.hiddenLectures;
 
   bool get isVisible => !hiddenLectures.contains(widget.lecture.id);
 
diff --git a/lib/main.dart b/lib/main.dart
index 89a72cf..8ae8389 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -10,6 +10,7 @@ import 'toolbar/toolbar.dart';
 import 'untis/fetch.dart';
 import 'untis/timetable.dart';
 import 'user/profile.dart';
+import 'util/listenable.dart';
 
 void main() {
   runApp(const MyApp());
@@ -59,7 +60,7 @@ class _MyHomePageState extends State<MyHomePage> {
     final profile = Profile(selectedClasses: {19997, 20000});
     final classes = await untis.allClasses.fetched;
 
-    appContext.profile = profile;
+    appContext.profile.value = profile;
     appContext.classes = classes;
     fetchTimetable();
 
@@ -73,24 +74,26 @@ class _MyHomePageState extends State<MyHomePage> {
 
   Future<void> fetchTimetable() async {
     final date = appContext.date.value;
-    TimetableData? mergedTimetable;
 
-    final selectedClasses = appContext.profile!.selectedClasses;
+    timetable = null;
+    setState(() {});
+
+    final profile = await appContext.profile.firstNotNull;
+    final selectedClasses = profile.selectedClasses;
 
     final allTimetables = await Future.wait(selectedClasses.map((classid) {
       return Future(() => untis.timetableOf(classid, date).fetched);
     }));
 
-    for (var fetched in allTimetables) {
-      if (mergedTimetable == null) {
-        mergedTimetable = fetched;
+    for (final fetched in allTimetables) {
+      if (timetable == null) {
+        timetable = fetched;
       } else {
-        mergedTimetable = TimetableData.merge(mergedTimetable, fetched);
+        timetable = TimetableData.merge(timetable!, fetched);
       }
-    }
 
-    timetable = mergedTimetable;
-    setState(() {});
+      setState(() {});
+    }
   }
 
   @override
@@ -99,19 +102,17 @@ class _MyHomePageState extends State<MyHomePage> {
       appBar: AppBar(
         title: const Text(appName),
       ),
-      drawer: appContext.profile == null
+      drawer: appContext.profile.value == null
           ? null
           : ClassDrawer(appContext: appContext),
       body: Column(
         children: [
           Toolbar(appContext: appContext),
           Expanded(
-            child: timetable != null && appContext.profile != null
-                ? Timetable(
-                    data: timetable!,
-                    appContext: appContext,
-                  )
-                : Container(),
+            child: Timetable(
+              data: timetable,
+              appContext: appContext,
+            ),
           ),
         ],
       ),
diff --git a/lib/timetable/lesson_dialog.dart b/lib/timetable/lesson_dialog.dart
index c44b1f2..8e3dd0c 100644
--- a/lib/timetable/lesson_dialog.dart
+++ b/lib/timetable/lesson_dialog.dart
@@ -19,10 +19,10 @@ class LessonDialog extends StatefulWidget {
 
 class _LessonDialogState extends State<LessonDialog> {
   bool get lectureVisible =>
-      !widget.appContext.profile!.hiddenLectures.contains(lectureId);
+      !widget.appContext.profile.value!.hiddenLectures.contains(lectureId);
   set lectureVisible(bool value) {
-    setState(() =>
-        widget.appContext.profile!.hiddenLectures.toggle(lectureId, !value));
+    setState(() => widget.appContext.profile.value!.hiddenLectures
+        .toggle(lectureId, !value));
   }
 
   Lesson get lesson => widget.lesson;
diff --git a/lib/timetable/timetable.dart b/lib/timetable/timetable.dart
index 14410c1..1e87eb9 100644
--- a/lib/timetable/timetable.dart
+++ b/lib/timetable/timetable.dart
@@ -7,6 +7,7 @@ import '../untis/lecture.dart';
 import '../untis/lesson.dart';
 import '../untis/period.dart';
 import '../untis/timetable.dart';
+import '../util/listenable.dart';
 import 'period_background.dart';
 import 'weekday_content.dart';
 import 'weekday_header.dart';
@@ -14,7 +15,7 @@ import 'weekday_header.dart';
 class Timetable extends StatefulWidget {
   static const weekdayCount = 5;
   static const mergeDistanceInMinutes = 30;
-  final TimetableData data;
+  final TimetableData? data;
   final AppContext appContext;
 
   const Timetable({
@@ -28,31 +29,36 @@ class Timetable extends StatefulWidget {
 }
 
 class _TimetableState extends State<Timetable> {
-  late StreamSubscription _subscription;
+  StreamSubscription? _subscription;
 
   @override
   void initState() {
     super.initState();
 
-    final profile = widget.appContext.profile!;
-    _subscription =
-        profile.hiddenLectures.onChange.listen((_) => setState(() {}));
+    widget.appContext.profile.firstNotNull.then((profile) {
+      _subscription =
+          profile.hiddenLectures.onChange.listen((_) => setState(() {}));
+    });
   }
 
   @override
   void dispose() {
-    _subscription.cancel();
+    _subscription?.cancel();
     super.dispose();
   }
 
   List<UntisLesson> _getLessonsOfDay(int index) {
-    return widget.data.lessons
+    return (widget.data?.lessons ?? <UntisLesson>{})
         .where((lesson) => lesson.weekdayIndex == index)
         .toList();
   }
 
   List<Lesson> getMergedLessonsOfDay(int index) {
-    return mergeLessons(_getLessonsOfDay(index));
+    final profile = widget.appContext.profile.value;
+
+    return profile == null
+        ? []
+        : mergeLessons(_getLessonsOfDay(index), profile.hiddenLectures);
   }
 
   @override
@@ -113,8 +119,7 @@ class _TimetableState extends State<Timetable> {
     );
   }
 
-  List<Lesson> mergeLessons(List<UntisLesson> lessons) {
-    final hiddenIds = widget.appContext.profile!.hiddenLectures;
+  List<Lesson> mergeLessons(List<UntisLesson> lessons, Set<int> hiddenIds) {
     final uniqueLectures = <Lecture, List<Lesson>>{};
 
     for (final lesson in lessons) {
diff --git a/lib/util/listenable.dart b/lib/util/listenable.dart
index 5e9c120..4ed2674 100644
--- a/lib/util/listenable.dart
+++ b/lib/util/listenable.dart
@@ -13,3 +13,8 @@ class Listenable<T> {
 
   Listenable(T value) : _value = value;
 }
+
+extension NullListenable<T> on Listenable<T?> {
+  Future<T> get firstNotNull async =>
+      value ?? (await onChange.firstWhere((element) => element != null))!;
+}
-- 
GitLab