added minimal error handling and user feedback

This commit is contained in:
2026-01-22 18:09:34 +01:00
parent 27c3804b1e
commit 06c5ca9910
5 changed files with 88 additions and 43 deletions

View File

@@ -30,8 +30,14 @@
"import": "Importieren", "import": "Importieren",
"activateJsonExport": "Immer als JSON speichern", "activateJsonExport": "Immer als JSON speichern",
"@@comment": "Info",
"exportSuccess": "Daten exportiert",
"importSuccess": "Daten importiert",
"@@comment": "Errors", "@@comment": "Errors",
"errorStoragePermisson": "Zugriff auf Speicher verwehrt", "errorStoragePermisson": "Zugriff auf Speicher verwehrt",
"errorCouldNotLaunchUrl": "Konnte Url nicht öffnen", "errorCouldNotLaunchUrl": "Konnte Url nicht öffnen",
"errorInvalidUrl": "Fehlerhafte Url" "errorInvalidUrl": "Fehlerhafte Url",
"exportFailed": "Export fehlgeschlagen",
"importFailed": "Import fehlgeschlagen"
} }

View File

@@ -29,9 +29,16 @@
"export": "Export", "export": "Export",
"import": "Import", "import": "Import",
"activateJsonExport": "Always save to JSON", "activateJsonExport": "Always save to JSON",
"@@comment": "Info",
"exportSuccess": "Exported data",
"importSuccess": "Imported data",
"@@comment": "Errors", "@@comment": "Errors",
"errorStoragePermisson": "Storage permissions denied", "errorStoragePermisson": "Storage permissions denied",
"errorCouldNotLaunchUrl": "Could not launch Url", "errorCouldNotLaunchUrl": "Could not launch Url",
"errorInvalidUrl": "Invalid Url" "errorInvalidUrl": "Invalid Url",
"exportFailed": "Export failed",
"importFailed": "Import failed"
} }

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
@@ -43,20 +45,42 @@ class _SettingsPageState extends State<SettingsPage> {
} }
void onActivateJsonExportPressed() async { void onActivateJsonExportPressed() async {
if (await checkStoragePermission) Storage.exportToJsonFile(); if (!await checkStoragePermission) return;
Storage.exportToJsonFile().then(showExportInfo);
} }
void onActivateJsonImportPressed() async { void onActivateJsonImportPressed() async {
if (await checkStoragePermission) Storage.importFromJsonFile(); if (!await checkStoragePermission) return;
Storage.importFromJsonFile().then(showImportInfo);
} }
Future<bool> get checkStoragePermission async { Future<bool> get checkStoragePermission async {
if (!(await PermissionService.requestStoragePermission).isGranted) { if (!(await PermissionService.requestStoragePermission).isGranted) {
if (mounted) { if (mounted) {
Notifying.showStoragePermissionErrorSnackbar(context); Notifying.showErrorSnackbar(
context,
AppLocalizations.of(context)!.errorStoragePermisson,
);
return false; return false;
} }
} }
return true; return true;
} }
void showExportInfo(bool success) => Notifying.showSnackbar(
context,
text: success
? AppLocalizations.of(context)!.exportSuccess
: AppLocalizations.of(context)!.exportFailed,
isError: success,
);
void showImportInfo(bool success) => Notifying.showSnackbar(
context,
text: success
? AppLocalizations.of(context)!.importSuccess
: AppLocalizations.of(context)!.importFailed,
isError: success,
);
} }

View File

@@ -13,48 +13,56 @@ class JsonFileService {
required List<Collection> collections, required List<Collection> collections,
required List<Bookmark> bookmarks, required List<Bookmark> bookmarks,
}) async { }) async {
final dir = await _directoryPath; try {
final dir = await _directoryPath;
if (dir.isEmpty) return false;
final data = { final data = {
'collections': collections.map((c) => c.toJson()).toList(), 'collections': collections.map((c) => c.toJson()).toList(),
'bookmarks': bookmarks.map((b) => b.toJson()).toList(), 'bookmarks': bookmarks.map((b) => b.toJson()).toList(),
}; };
final json = jsonEncode(data).codeUnits; final json = jsonEncode(data).codeUnits;
final file = XFile.fromData( final file = XFile.fromData(
Uint8List.fromList(json), Uint8List.fromList(json),
mimeType: 'application/json', mimeType: 'application/json',
name: constants.jsonFileName, name: constants.jsonFileName,
); );
file.saveTo('$dir/${constants.jsonFileName}');
file.saveTo('$dir/${constants.jsonFileName}');
} catch (e) {
return false;
}
return false; return false;
} }
static Future<({List<Collection> collections, List<Bookmark> bookmarks})> static Future<({List<Collection> collections, List<Bookmark> bookmarks})>
importFromJson() async { importFromJson() async {
const typeGroup = XTypeGroup(label: 'json', extensions: <String>['json']); try {
final XFile? file = await openFile( const typeGroup = XTypeGroup(label: 'json', extensions: <String>['json']);
acceptedTypeGroups: <XTypeGroup>[typeGroup], final XFile? file = await openFile(
); acceptedTypeGroups: <XTypeGroup>[typeGroup],
);
if (file == null) { if (file == null) {
return (collections: <Collection>[], bookmarks: <Bookmark>[]);
}
final jsonString = await file.readAsString();
final data = jsonDecode(jsonString) as Map<String, dynamic>;
final collections = (data['collections'] as List<dynamic>? ?? [])
.map((json) => Collection.fromJson(json as Map<String, dynamic>))
.toList();
final bookmarks = (data['bookmarks'] as List<dynamic>? ?? [])
.map((json) => Bookmark.fromJson(json as Map<String, dynamic>))
.toList();
return (collections: collections, bookmarks: bookmarks);
} catch (e) {
return (collections: <Collection>[], bookmarks: <Bookmark>[]); return (collections: <Collection>[], bookmarks: <Bookmark>[]);
} }
final jsonString = await file.readAsString();
final data = jsonDecode(jsonString) as Map<String, dynamic>;
final collections = (data['collections'] as List<dynamic>? ?? [])
.map((json) => Collection.fromJson(json as Map<String, dynamic>))
.toList();
final bookmarks = (data['bookmarks'] as List<dynamic>? ?? [])
.map((json) => Bookmark.fromJson(json as Map<String, dynamic>))
.toList();
return (collections: collections, bookmarks: bookmarks);
} }
static Future<String> get _directoryPath async { static Future<String> get _directoryPath async {

View File

@@ -54,11 +54,11 @@ class Notifying {
showSnackbar(context, text: errorText, isError: true); showSnackbar(context, text: errorText, isError: true);
} }
static void showStoragePermissionErrorSnackbar(BuildContext context) { static void showErrorSnackbar(BuildContext context, String message) {
showSnackbar( showSnackbar(context, text: message, isError: true);
context, }
text: AppLocalizations.of(context)!.errorStoragePermisson,
isError: true, static void showMessageSnackbar(BuildContext context, String message) {
); showSnackbar(context, text: message, isError: false);
} }
} }