import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:kulinar_app/constants.dart'; import 'package:kulinar_app/util/notifications.dart'; import 'package:kulinar_app/widgets/toastbar_widget.dart'; import 'package:kulinar_app/models/data/recipe_data_class.dart'; import 'package:kulinar_app/models/data/settings_data_class.dart'; import 'package:file_picker/file_picker.dart'; import 'package:external_path/external_path.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; enum ExportType { settings, recipes, recipe, full, } /// Handles all the things related to file serialization / deserialization and other file interaction. class FileHandler { /// Serializes the given `data` into a json style File. Sends a confirmation Toast to the given `context` afterwards. static Future serializeFile(BuildContext context, Map data) async { if (await Permission.storage.request().isGranted) { try { final _directory = await ExternalPath.getExternalStoragePublicDirectory(ExternalPath.DIRECTORY_DOWNLOADS); final _file = File("$_directory/Export-${DateTime.now().toUtc().toString().split(" ")[0]}.kulinar"); await _file.writeAsString(jsonEncode(await encodeDataAsMap(ExportType.full, data))); ToastBar.showToastBar(context, AppLocalizations.of(context)!.exportSuccess, actionLabel: ""); Notifications.notify(AppLocalizations.of(context)!.exportSuccess, AppLocalizations.of(context)!.tapHint, _file.path); } catch (e) { debugPrint("$e"); ToastBar.showToastBar(context, AppLocalizations.of(context)!.exportError, actionLabel: ""); } } } /// Deserializes the given `file` into the returned Map. Sends a confirmation Toast to the given `context` afterwards. static Future deserializeFile(BuildContext context, File file) async { final dynamic _content = jsonDecode(await file.readAsString()); SettingsData.decode(_content["settings"]); RecipeData.decode(_content["recipes"]); ToastBar.showToastBar(context, AppLocalizations.of(context)!.importSuccess, actionLabel: ""); } /// Parses a given possible deserializable `file` for useful information and returns a map of it. static Future> deserializeFileInformation(File file) async { final Map _content = jsonDecode(await file.readAsString()); Map _map = Map(); _map["version"] = _content["version"]; _map["type"] = _content["type"].split(".")[1]; _map["size"] = "${await file.length()} Bytes"; if (_content["type"] == ExportType.full.toString()) { int a = jsonDecode(_content["recipes"]).length; int b = jsonDecode(_content["settings"]).length; _map["entries"] = (a + b).toString(); } return _map; } /// Opens the native file picker and returns the picked `.kulinar` file. static Future pickDeserializableFile(BuildContext context) async { if (await Permission.storage.request().isGranted) { try { FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.any); //, allowedExtensions: ["kulinar"]); if (result != null) { return File(result.files.single.path!); } } catch (e) { debugPrint("$e"); ToastBar.showToastBar(context, AppLocalizations.of(context)!.importError, actionLabel: ""); } } return null; } /// Encodes the given `data` together with some additional information into a serializable map. static Future> encodeDataAsMap(ExportType type, Map data) async { Map _map = Map(); _map["version"] = cVersion; _map["type"] = type.toString(); // TODO: IMPLEMENT: Base64 Images and image count on export _map.addAll(data as Map); return _map; } /// Decodes the given `map` into /// TODO: IMPLEMENT: decodeExportMap // static Future> decodeMapToData(ExportType type) async {} }