diff --git a/lib/storage/base/settingsProvider.dart b/lib/storage/base/settingsProvider.dart index a29c0eb..253fa2d 100644 --- a/lib/storage/base/settingsProvider.dart +++ b/lib/storage/base/settingsProvider.dart @@ -1,10 +1,10 @@ import 'dart:convert'; import 'dart:developer'; import 'package:flutter/material.dart'; -import 'package:marianum_mobile/storage/gradeAverages/gradeAveragesSettings.dart'; -import 'package:marianum_mobile/storage/timetable/timetableSettings.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../gradeAverages/gradeAveragesSettings.dart'; +import '../timetable/timetableSettings.dart'; import 'settings.dart'; class SettingsProvider extends ChangeNotifier { @@ -25,13 +25,15 @@ class SettingsProvider extends ChangeNotifier { init(); } - void init() async { + void init({bool resetConfig = false}) async { _storage = await SharedPreferences.getInstance(); - if(_storage.containsKey(_fieldName)) { - log("Settings from disk: ${_storage.getString(_fieldName)}"); + if(resetConfig) _storage.remove(_fieldName); + + try { _settings = Settings.fromJson(jsonDecode(_storage.getString(_fieldName)!)); - } else { + } catch(e) { + log("Settings are defective, using defaults: ${e.toString()}"); _settings = _defaults(); } diff --git a/lib/view/pages/more/gradeAverages/gradeAverage.dart b/lib/view/pages/more/gradeAverages/gradeAverage.dart index aa5cd08..765c3a6 100644 --- a/lib/view/pages/more/gradeAverages/gradeAverage.dart +++ b/lib/view/pages/more/gradeAverages/gradeAverage.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:marianum_mobile/storage/base/settingsProvider.dart'; import 'package:provider/provider.dart'; +import '../../../../storage/base/settingsProvider.dart'; import '../../../../widget/confirmDialog.dart'; class GradeAverage extends StatefulWidget { diff --git a/lib/view/pages/timetable/timetable.dart b/lib/view/pages/timetable/timetable.dart index 489b927..6e254fb 100644 --- a/lib/view/pages/timetable/timetable.dart +++ b/lib/view/pages/timetable/timetable.dart @@ -2,7 +2,6 @@ import 'dart:developer'; import 'package:flutter/material.dart'; -import 'package:marianum_mobile/storage/base/settingsProvider.dart'; import 'package:provider/provider.dart'; import 'package:syncfusion_flutter_calendar/calendar.dart'; @@ -10,6 +9,7 @@ import '../../../api/webuntis/queries/getHolidays/getHolidaysResponse.dart'; import '../../../api/webuntis/queries/getRooms/getRoomsResponse.dart'; import '../../../api/webuntis/queries/getSubjects/getSubjectsResponse.dart'; import '../../../model/timetable/timetableProps.dart'; +import '../../../storage/base/settingsProvider.dart'; import '../../../widget/errorView.dart'; import 'appointmenetComponent.dart'; import 'appointmentDetails.dart'; diff --git a/lib/view/settings/debug/cacheView.dart b/lib/view/settings/debug/cacheView.dart new file mode 100644 index 0000000..a4d20a3 --- /dev/null +++ b/lib/view/settings/debug/cacheView.dart @@ -0,0 +1,110 @@ + +import 'dart:async'; +import 'dart:convert'; +import 'package:filesize/filesize.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:jiffy/jiffy.dart'; +import 'package:localstore/localstore.dart'; + +import '../../../widget/errorView.dart'; +import 'jsonViewer.dart'; + +class CacheView extends StatefulWidget { + final collection = "MarianumMobile"; + const CacheView({Key? key}) : super(key: key); + + @override + State createState() => _CacheViewState(); + + void clear() { + Localstore.instance.collection(collection).delete(); + } + + Future totalSize() async { + var data = await Localstore.instance.collection(collection).get(); + return data!.values.reduce((a, b) => jsonEncode(a).length + jsonEncode(b).length) * 8; + } +} + +class _CacheViewState extends State { + final Localstore storage = Localstore.instance; + late Future?> files; + + @override + void initState() { + files = Localstore.instance.collection(widget.collection).get(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Lokaler cache"), + ), + body: FutureBuilder( + future: files, + builder: (context, snapshot) { + if(snapshot.hasData) { + return ListView.builder( + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + Map element = snapshot.data![snapshot.data!.keys.elementAt(index)]; + String filename = snapshot.data!.keys.elementAt(index).split("/").last; + + return ListTile( + leading: const Icon(Icons.text_snippet_outlined), + title: Text(filename), + subtitle: Text("${filesize(jsonEncode(element).length * 8)}, ${Jiffy.parseFromMillisecondsSinceEpoch(element['lastupdate']).fromNow()}"), + trailing: const Icon(Icons.arrow_right), + onTap: () { + Navigator.push(context, MaterialPageRoute(builder: (context) { + return JsonViewer(title: filename, data: jsonDecode(element['json'])); + },)); + }, + onLongPress: () { + showDialog(context: context, builder: (context) { + return SimpleDialog( + children: [ + const ListTile( + leading: Icon(Icons.delete_forever), + title: Text("Diese Datei löschen"), + ), + ListTile( + leading: const Icon(Icons.copy), + title: const Text("Dateitext kopieren"), + onTap: () { + Clipboard.setData(ClipboardData(text: jsonEncode(element))); + Navigator.of(context).pop(); + }, + ) + ], + ); + }); + }, + ); + }, + ); + } else if(snapshot.connectionState != ConnectionState.done) { + return const Center( + child: CircularProgressIndicator() + ); + } else { + return const Center( + child: ErrorView(icon: Icons.hourglass_empty, text: "Keine Daten"), + ); + } + }, + ), + ); + } +} + +extension FutureExtension on Future { + bool isCompleted() { + final completer = Completer(); + then(completer.complete).catchError(completer.completeError); + return completer.isCompleted; + } +} diff --git a/lib/view/settings/debug/debugOverview.dart b/lib/view/settings/debug/debugOverview.dart deleted file mode 100644 index c834bb1..0000000 --- a/lib/view/settings/debug/debugOverview.dart +++ /dev/null @@ -1,106 +0,0 @@ - -import 'dart:convert'; -import 'package:filesize/filesize.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:jiffy/jiffy.dart'; -import 'package:localstore/localstore.dart'; - -import 'jsonViewer.dart'; - -class DebugOverview extends StatefulWidget { - const DebugOverview({Key? key}) : super(key: key); - - @override - State createState() => _DebugOverviewState(); -} - -class _DebugOverviewState extends State { - - final Localstore storage = Localstore.instance; - Future?> files = Localstore.instance.collection("MarianumMobile").get(); - dynamic data; - - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text("Lokaler cache"), - ), - body: Column( - children: [ - ListTile( - leading: const Icon(Icons.delete_forever), - title: const Text("Cache löschen"), - onTap: () { - PaintingBinding.instance.imageCache.clear(); - storage.collection("MarianumMobile").delete().then((value) => { - Navigator.pop(context) - }); - }, - ), - - const Divider(), - FutureBuilder( - future: files, - builder: (context, snapshot) { - if(snapshot.hasData) { - List files = snapshot.data?.keys.map((e) => e.toString()).toList() ?? List.empty(); - - Map getValue(int index) { - return snapshot.data?[files[index]]; - } - - return Expanded( - flex: 5, - child: ListView.builder( - itemCount: files.length, - itemBuilder: (context, index) { - String filename = files[index].split("/").last; - - return ListTile( - leading: const Icon(Icons.text_snippet_outlined), - title: Text(filename), - subtitle: Text("${filesize(getValue(index).toString().length * 8)}, ${Jiffy.parseFromMillisecondsSinceEpoch(getValue(index)['lastupdate']).fromNow()}"), - trailing: const Icon(Icons.chevron_right), - textColor: Colors.black, - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) { - return JsonViewer(title: filename, data: {"lastupdate": getValue(index)['lastupdate'], "json": jsonDecode(getValue(index)['json'])}); - },)); - }, - onLongPress: () { - showDialog(context: context, builder: (context) { - return SimpleDialog( - children: [ - const ListTile( - leading: Icon(Icons.delete_forever), - title: Text("Diese Datei löschen"), - ), - ListTile( - leading: const Icon(Icons.copy), - title: const Text("Dateitext kopieren"), - onTap: () { - Clipboard.setData(ClipboardData(text: getValue(index).toString())); - Navigator.of(context).pop(); - }, - ) - ], - ); - }); - }, - ); - }, - ), - ); - } else { - return snapshot.data == null ? const Text("No data") : const Center(child: CircularProgressIndicator()); - } - }, - ), - ], - ), - ); - } -} diff --git a/lib/view/settings/settings.dart b/lib/view/settings/settings.dart index 048d024..96f361f 100644 --- a/lib/view/settings/settings.dart +++ b/lib/view/settings/settings.dart @@ -1,4 +1,5 @@ +import 'package:filesize/filesize.dart'; import 'package:flutter/material.dart'; import 'package:package_info/package_info.dart'; import 'package:provider/provider.dart'; @@ -8,7 +9,7 @@ import '../../model/accountModel.dart'; import '../../storage/base/settingsProvider.dart'; import '../../theming/appTheme.dart'; import '../../widget/confirmDialog.dart'; -import 'debug/debugOverview.dart'; +import 'debug/cacheView.dart'; import 'debug/jsonViewer.dart'; class Settings extends StatefulWidget { @@ -178,20 +179,59 @@ class _SettingsState extends State { ), ListTile( leading: const Icon(Icons.data_object), - title: const Text("Cache JSON dump"), + title: FutureBuilder( + future: const CacheView().totalSize(), + builder: (context, snapshot) { + return Text("Cache JSON dump (${snapshot.hasError ? "?" : snapshot.hasData ? "est. ${filesize(snapshot.data)}" : "Berechnen..."})"); + }, + ), + subtitle: const Text("Tippen und halten um zu löschen"), onTap: () { Navigator.push(context, MaterialPageRoute(builder: (context) { - return const DebugOverview(); + return const CacheView(); })); }, + onLongPress: () { + ConfirmDialog( + title: "Cache löschen", + content: "Alle cache Einträge werden gelöscht. Der cache wird bei benutzung der App erneut aufgebaut", + confirmButton: "Unwiederruflich löschen", + onConfirm: () => const CacheView().clear(), + ).asDialog(context); + }, trailing: const Icon(Icons.arrow_right), ), + ListTile( + leading: const Icon(Icons.broken_image), + title: const Text("Cached Thumbnails löschen"), + onTap: () { + ConfirmDialog( + title: "Thumbs cache löschen", + content: "Alle zwischengespeicherten Bilder werden gelöscht.", + confirmButton: "Unwiederruflich löschen", + onConfirm: () => PaintingBinding.instance.imageCache.clear(), + ).asDialog(context); + }, + ), ListTile( leading: const Icon(Icons.settings_applications_outlined), title: const Text("Storage JSON dump"), + subtitle: const Text("Tippen und halten um zu löschen"), onTap: () { JsonViewer.asDialog(context, settings.val().toJson()); }, + onLongPress: () { + ConfirmDialog( + title: "App-Speicher löschen", + content: "Alle Einstellungen und gespeicherten Inhalte gehen verloren! Accountdaten sind nicht betroffen.", + confirmButton: "Unwiederruflich Löschen", + onConfirm: () { + setState(() { + Provider.of(context, listen: false).init(resetConfig: true); + }); + }, + ).asDialog(context); + }, ), ], ),