From 92a83c8bedafb60716b04269bc06b21270050ce8 Mon Sep 17 00:00:00 2001
From: doodlezucc <electerminator@gmail.com>
Date: Wed, 25 Oct 2023 11:48:20 +0200
Subject: [PATCH] show fetch errors in dialog

---
 lib/dialogs/error_dialog.dart | 20 ++++++++++++++++++++
 lib/main.dart                 | 14 +++++++++++++-
 lib/untis/fetch.dart          | 27 ++++++++++++++++++++++-----
 3 files changed, 55 insertions(+), 6 deletions(-)
 create mode 100644 lib/dialogs/error_dialog.dart

diff --git a/lib/dialogs/error_dialog.dart b/lib/dialogs/error_dialog.dart
new file mode 100644
index 0000000..083491c
--- /dev/null
+++ b/lib/dialogs/error_dialog.dart
@@ -0,0 +1,20 @@
+import 'package:flutter/material.dart';
+
+class ErrorDialog extends StatelessWidget {
+  final String title;
+  final Object? error;
+
+  const ErrorDialog({super.key, required this.title, required this.error});
+
+  @override
+  Widget build(BuildContext context) {
+    return SimpleDialog(
+      title: Text(title),
+      children: [
+        SimpleDialogOption(
+          child: Text('An error occurred: ${error.runtimeType}\n$error'),
+        ),
+      ],
+    );
+  }
+}
diff --git a/lib/main.dart b/lib/main.dart
index 9fd4939..3fd81e6 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,9 +1,11 @@
 import 'dart:async';
+import 'dart:io';
 
 import 'package:flutter/material.dart';
 
 import 'branding.dart';
 import 'context.dart';
+import 'dialogs/error_dialog.dart';
 import 'drawer/drawer.dart';
 import 'timetable/timetable.dart';
 import 'toolbar/toolbar.dart';
@@ -63,7 +65,9 @@ class _MyHomePageState extends State<MyHomePage> {
   @override
   void initState() {
     super.initState();
-    loadProfile();
+    loadProfile().onError((error, _) {
+      _showErrorDialog('Failed to connect', error);
+    });
   }
 
   @override
@@ -74,6 +78,14 @@ class _MyHomePageState extends State<MyHomePage> {
     super.dispose();
   }
 
+  void _showErrorDialog(String title, Object? error) {
+    stderr.write(error);
+    showDialog(
+      context: context,
+      builder: (context) => ErrorDialog(title: title, error: error),
+    );
+  }
+
   Future<void> loadProfile() async {
     final profile = await appContext.profile.firstNotNull;
     final classes = await untis.allClasses.fetched;
diff --git a/lib/untis/fetch.dart b/lib/untis/fetch.dart
index e993038..793fc15 100644
--- a/lib/untis/fetch.dart
+++ b/lib/untis/fetch.dart
@@ -84,14 +84,21 @@ class UntisFetch {
     final responseString = await utf8.decodeStream(response);
     final onDownloadEnd = DateTime.now();
 
-    final bodyJson = jsonDecode(responseString);
+    if (response.statusCode != 200) {
+      throw FetchError(response.statusCode, responseString);
+    }
 
-    final onDecodeEnd = DateTime.now();
+    try {
+      final bodyJson = jsonDecode(responseString);
+      final onDecodeEnd = DateTime.now();
 
-    final stats =
-        FetchStats(onStart, onDownloadStart, onDownloadEnd, onDecodeEnd);
+      final stats =
+          FetchStats(onStart, onDownloadStart, onDownloadEnd, onDecodeEnd);
 
-    return (bodyJson as Json, resultCookies, stats);
+      return (bodyJson as Json, resultCookies, stats);
+    } on FormatException catch (_) {
+      throw FetchError(response.statusCode, responseString);
+    }
   }
 
   Future<Json> requestOptions(RequestOptions options) async {
@@ -148,3 +155,13 @@ class FetchStats {
         .join(', ');
   }
 }
+
+class FetchError extends Error {
+  final int statusCode;
+  final String body;
+
+  FetchError(this.statusCode, this.body);
+
+  @override
+  String toString() => 'Server responded with status code $statusCode\n$body';
+}
-- 
GitLab