Merge pull request #71 from flixcoo/enhance/68-improve-file-import

Improve file import
This commit is contained in:
2025-07-01 23:00:16 +02:00
committed by GitHub
8 changed files with 180 additions and 35 deletions

View File

@@ -81,7 +81,16 @@
"import_data": "Daten importieren", "import_data": "Daten importieren",
"export_data": "Daten exportieren", "export_data": "Daten exportieren",
"error": "Fehler", "error": "Fehler",
"error_import": "Datei konnte nicht importiert werden",
"import_success_title": "Import erfolgreich",
"import_success_message":"Die Spieldaten wurden erfolgreich importiert.",
"import_validation_error_title": "Validierung fehlgeschlagen",
"import_validation_error_message": "Es wurden keine Cabo-Counter Spieldaten gefunden. Bitte stellen Sie sicher, dass es sich um eine gültige Cabo-Counter Exportdatei handelt.",
"import_format_error_title": "Falsches Format",
"import_format_error_message": "Die Datei ist kein gültiges JSON-Format oder enthält ungültige Daten.",
"import_generic_error_title": "Import fehlgeschlagen",
"import_generic_error_message": "Der Import ist fehlgeschlagen.",
"error_export": "Datei konnte nicht exportiert werden", "error_export": "Datei konnte nicht exportiert werden",
"error_found": "Fehler gefunden?", "error_found": "Fehler gefunden?",
"create_issue": "Issue erstellen", "create_issue": "Issue erstellen",

View File

@@ -81,7 +81,16 @@
"import_data": "Import Data", "import_data": "Import Data",
"export_data": "Export Data", "export_data": "Export Data",
"error": "Error", "error": "Error",
"error_import": "Could not import file",
"import_success_title": "Import successful",
"import_success_message":"The game data has been successfully imported.",
"import_validation_error_title": "Validation failed",
"import_validation_error_message": "No Cabo-Counter game data was found. Please make sure that this is a valid Cabo-Counter export file.",
"import_format_error_title": "Wrong format",
"import_format_error_message": "The file is not a valid JSON format or contains invalid data.",
"import_generic_error_title": "Import failed",
"import_generic_error_message": "The import has failed.",
"error_export": "Could not export file", "error_export": "Could not export file",
"error_found": "Found a bug?", "error_found": "Found a bug?",
"create_issue": "Create Issue", "create_issue": "Create Issue",

View File

@@ -452,11 +452,53 @@ abstract class AppLocalizations {
/// **'Fehler'** /// **'Fehler'**
String get error; String get error;
/// No description provided for @error_import. /// No description provided for @import_success_title.
/// ///
/// In de, this message translates to: /// In de, this message translates to:
/// **'Datei konnte nicht importiert werden'** /// **'Import erfolgreich'**
String get error_import; String get import_success_title;
/// No description provided for @import_success_message.
///
/// In de, this message translates to:
/// **'Die Spieldaten wurden erfolgreich importiert.'**
String get import_success_message;
/// No description provided for @import_validation_error_title.
///
/// In de, this message translates to:
/// **'Validierung fehlgeschlagen'**
String get import_validation_error_title;
/// No description provided for @import_validation_error_message.
///
/// In de, this message translates to:
/// **'Es wurden keine Cabo-Counter Spieldaten gefunden. Bitte stellen Sie sicher, dass es sich um eine gültige Cabo-Counter Exportdatei handelt.'**
String get import_validation_error_message;
/// No description provided for @import_format_error_title.
///
/// In de, this message translates to:
/// **'Falsches Format'**
String get import_format_error_title;
/// No description provided for @import_format_error_message.
///
/// In de, this message translates to:
/// **'Die Datei ist kein gültiges JSON-Format oder enthält ungültige Daten.'**
String get import_format_error_message;
/// No description provided for @import_generic_error_title.
///
/// In de, this message translates to:
/// **'Import fehlgeschlagen'**
String get import_generic_error_title;
/// No description provided for @import_generic_error_message.
///
/// In de, this message translates to:
/// **'Der Import ist fehlgeschlagen.'**
String get import_generic_error_message;
/// No description provided for @error_export. /// No description provided for @error_export.
/// ///

View File

@@ -195,7 +195,31 @@ class AppLocalizationsDe extends AppLocalizations {
String get error => 'Fehler'; String get error => 'Fehler';
@override @override
String get error_import => 'Datei konnte nicht importiert werden'; String get import_success_title => 'Import erfolgreich';
@override
String get import_success_message =>
'Die Spieldaten wurden erfolgreich importiert.';
@override
String get import_validation_error_title => 'Validierung fehlgeschlagen';
@override
String get import_validation_error_message =>
'Es wurden keine Cabo-Counter Spieldaten gefunden. Bitte stellen Sie sicher, dass es sich um eine gültige Cabo-Counter Exportdatei handelt.';
@override
String get import_format_error_title => 'Falsches Format';
@override
String get import_format_error_message =>
'Die Datei ist kein gültiges JSON-Format oder enthält ungültige Daten.';
@override
String get import_generic_error_title => 'Import fehlgeschlagen';
@override
String get import_generic_error_message => 'Der Import ist fehlgeschlagen.';
@override @override
String get error_export => 'Datei konnte nicht exportiert werden'; String get error_export => 'Datei konnte nicht exportiert werden';

View File

@@ -192,7 +192,31 @@ class AppLocalizationsEn extends AppLocalizations {
String get error => 'Error'; String get error => 'Error';
@override @override
String get error_import => 'Could not import file'; String get import_success_title => 'Import successful';
@override
String get import_success_message =>
'The game data has been successfully imported.';
@override
String get import_validation_error_title => 'Validation failed';
@override
String get import_validation_error_message =>
'No Cabo-Counter game data was found. Please make sure that this is a valid Cabo-Counter export file.';
@override
String get import_format_error_title => 'Wrong format';
@override
String get import_format_error_message =>
'The file is not a valid JSON format or contains invalid data.';
@override
String get import_generic_error_title => 'Import failed';
@override
String get import_generic_error_message => 'The import has failed.';
@override @override
String get error_export => 'Could not export file'; String get error_export => 'Could not export file';

View File

@@ -9,6 +9,14 @@ import 'package:flutter/services.dart';
import 'package:json_schema/json_schema.dart'; import 'package:json_schema/json_schema.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
enum ImportStatus {
success,
canceled,
validationError,
formatError,
genericError
}
class LocalStorageService { class LocalStorageService {
static const String _fileName = 'game_data.json'; static const String _fileName = 'game_data.json';
@@ -111,7 +119,7 @@ class LocalStorageService {
} }
/// Opens the file picker to import a JSON file and loads the game data from it. /// Opens the file picker to import a JSON file and loads the game data from it.
static Future<bool> importJsonFile() async { static Future<ImportStatus> importJsonFile() async {
final result = await FilePicker.platform.pickFiles( final result = await FilePicker.platform.pickFiles(
dialogTitle: 'Wähle eine Datei mit Spieldaten aus', dialogTitle: 'Wähle eine Datei mit Spieldaten aus',
type: FileType.custom, type: FileType.custom,
@@ -121,14 +129,14 @@ class LocalStorageService {
if (result == null) { if (result == null) {
print( print(
'[local_storage_service.dart] Der Filepicker-Dialog wurde abgebrochen'); '[local_storage_service.dart] Der Filepicker-Dialog wurde abgebrochen');
return false; return ImportStatus.canceled;
} }
try { try {
final jsonString = await _readFileContent(result.files.single); final jsonString = await _readFileContent(result.files.single);
if (!await validateJsonSchema(jsonString)) { if (!await validateJsonSchema(jsonString)) {
return false; return ImportStatus.validationError;
} }
final jsonData = json.decode(jsonString) as List<dynamic>; final jsonData = json.decode(jsonString) as List<dynamic>;
gameManager.gameList = jsonData gameManager.gameList = jsonData
@@ -137,15 +145,16 @@ class LocalStorageService {
.toList(); .toList();
print( print(
'[local_storage_service.dart] Die Datei wurde erfolgreich Importiertn'); '[local_storage_service.dart] Die Datei wurde erfolgreich Importiertn');
return true; await saveGameSessions();
return ImportStatus.success;
} on FormatException catch (e) { } on FormatException catch (e) {
print( print(
'[local_storage_service.dart] Ungültiges JSON-Format. Exception: $e'); '[local_storage_service.dart] Ungültiges JSON-Format. Exception: $e');
return false; return ImportStatus.formatError;
} on Exception catch (e) { } on Exception catch (e) {
print( print(
'[local_storage_service.dart] Fehler beim Dateizugriff. Exception: $e'); '[local_storage_service.dart] Fehler beim Dateizugriff. Exception: $e');
return false; return ImportStatus.genericError;
} }
} }

View File

@@ -125,27 +125,7 @@ class _SettingsViewState extends State<SettingsView> {
onPressed: () async { onPressed: () async {
final success = final success =
await LocalStorageService.importJsonFile(); await LocalStorageService.importJsonFile();
if (!success && context.mounted) { showFeedbackDialog(success);
showCupertinoDialog(
context: context,
builder: (context) => CupertinoAlertDialog(
title: Text(
AppLocalizations.of(context)
.error),
content: Text(
AppLocalizations.of(context)
.error_import),
actions: [
CupertinoDialogAction(
child: Text(
AppLocalizations.of(context)
.ok),
onPressed: () =>
Navigator.pop(context),
),
],
));
}
}), }),
const SizedBox( const SizedBox(
width: 20, width: 20,
@@ -236,4 +216,52 @@ class _SettingsViewState extends State<SettingsView> {
Future<PackageInfo> _getPackageInfo() async { Future<PackageInfo> _getPackageInfo() async {
return await PackageInfo.fromPlatform(); return await PackageInfo.fromPlatform();
} }
void showFeedbackDialog(ImportStatus status) {
if (status == ImportStatus.canceled) return;
final (title, message) = _getDialogContent(status);
showCupertinoDialog(
context: context,
builder: (context) {
return CupertinoAlertDialog(
title: Text(title),
content: Text(message),
actions: [
CupertinoDialogAction(
child: Text(AppLocalizations.of(context).ok),
onPressed: () => Navigator.pop(context),
),
],
);
});
}
(String, String) _getDialogContent(ImportStatus status) {
switch (status) {
case ImportStatus.success:
return (
AppLocalizations.of(context).import_success_title,
AppLocalizations.of(context).import_success_message
);
case ImportStatus.validationError:
return (
AppLocalizations.of(context).import_validation_error_title,
AppLocalizations.of(context).import_validation_error_message
);
case ImportStatus.formatError:
return (
AppLocalizations.of(context).import_format_error_title,
AppLocalizations.of(context).import_format_error_message
);
case ImportStatus.genericError:
return (
AppLocalizations.of(context).import_generic_error_title,
AppLocalizations.of(context).import_generic_error_message
);
case ImportStatus.canceled:
return ('', '');
}
}
} }

View File

@@ -2,7 +2,7 @@ name: cabo_counter
description: "Mobile app for the card game Cabo" description: "Mobile app for the card game Cabo"
publish_to: 'none' publish_to: 'none'
version: 0.3.3+256 version: 0.3.3+265
environment: environment:
sdk: ^3.5.4 sdk: ^3.5.4