From 33bb197557b6222d7db46e02c74fe8442833e295 Mon Sep 17 00:00:00 2001 From: davidpkj Date: Wed, 24 Aug 2022 15:38:58 +0200 Subject: Minor bug fixes and updates --- lib/main.dart | 5 ++- lib/models/data/recipe_data_class.dart | 15 ++++++- lib/models/data/settings_data_class.dart | 2 + lib/util/file_handler.dart | 15 +++---- lib/util/notifications.dart | 4 +- lib/views/info_view.dart | 36 ++++++++++++---- lib/views/main_view.dart | 2 +- lib/views/recipe_view.dart | 72 ++++++++++---------------------- lib/views/settings_view.dart | 4 +- lib/widgets/recipe_card_widget.dart | 5 +-- lib/widgets/utility_icon_row_widget.dart | 29 ++++++------- 11 files changed, 97 insertions(+), 92 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index d22d644..9bbfe49 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:kulinar_app/constants.dart'; -import 'package:kulinar_app/models/data/shoplist_data_class.dart'; import 'package:kulinar_app/views/main_view.dart'; import 'package:kulinar_app/util/notifications.dart'; import 'package:kulinar_app/models/data/recipe_data_class.dart'; +import 'package:kulinar_app/models/data/shoplist_data_class.dart'; import 'package:kulinar_app/models/data/settings_data_class.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; @@ -37,11 +37,12 @@ class _AppRootState extends State { @override Widget build(BuildContext context) { SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( - statusBarColor: Colors.transparent, + statusBarColor: cPrimaryColor, statusBarIconBrightness: Brightness.light, )); return MaterialApp( + debugShowCheckedModeBanner: false, localizationsDelegates: [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, diff --git a/lib/models/data/recipe_data_class.dart b/lib/models/data/recipe_data_class.dart index fd4ef22..3f707bc 100644 --- a/lib/models/data/recipe_data_class.dart +++ b/lib/models/data/recipe_data_class.dart @@ -38,14 +38,25 @@ class RecipeData { if (_result.isEmpty) return; + recipesIdentical(Recipe a, Recipe b) { + if (a.title != b.title) return false; + if (a.description != b.description) return false; + + return true; + } + _result.forEach((item) { - recipeList.add(Recipe( + Recipe recipe = Recipe( title: item["title"], description: item["description"], favorite: item["favorite"], rating: item["rating"], image: item["image"], - )); + ); + + if (RecipeData.recipeList.where((element) => recipesIdentical(element, recipe)).isEmpty) recipeList.add(recipe); }); + + save(); } } diff --git a/lib/models/data/settings_data_class.dart b/lib/models/data/settings_data_class.dart index e8cd604..5d3de60 100644 --- a/lib/models/data/settings_data_class.dart +++ b/lib/models/data/settings_data_class.dart @@ -29,5 +29,7 @@ class SettingsData { _result.forEach((key, value) { settings[key] = value; }); + + save(); } } diff --git a/lib/util/file_handler.dart b/lib/util/file_handler.dart index 0344c60..db1d725 100644 --- a/lib/util/file_handler.dart +++ b/lib/util/file_handler.dart @@ -32,25 +32,23 @@ class FileHandler { await _file.writeAsString(jsonEncode(await encodeDataAsMap(ExportType.full, data))); - ToastBar.showToastBar(context, AppLocalizations.of(context)!.exportSuccess); + ToastBar.showToastBar(context, AppLocalizations.of(context)!.exportSuccess, actionLabel: ""); Notifications.notify(AppLocalizations.of(context)!.exportSuccess, AppLocalizations.of(context)!.tapHint, _file.path); } catch (e) { print(e); - ToastBar.showToastBar(context, AppLocalizations.of(context)!.exportError); + 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 { + 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); - - return _content; + ToastBar.showToastBar(context, AppLocalizations.of(context)!.importSuccess, actionLabel: ""); } /// Parses a given possible deserializable `file` for useful information and returns a map of it. @@ -76,14 +74,14 @@ class FileHandler { static Future pickDeserializableFile(BuildContext context) async { if (await Permission.storage.request().isGranted) { try { - FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.custom, allowedExtensions: ["kulinar"]); + FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.any); //, allowedExtensions: ["kulinar"]); if (result != null) { return File(result.files.single.path!); } } catch (e) { print(e); - ToastBar.showToastBar(context, AppLocalizations.of(context)!.importError); + ToastBar.showToastBar(context, AppLocalizations.of(context)!.importError, actionLabel: ""); } } @@ -97,7 +95,6 @@ class FileHandler { _map["version"] = cVersion; _map["type"] = type.toString(); // TODO: IMPLEMENT: Base64 Images and image count on export - // map["imageCount"] = getImageCount; _map.addAll(data as Map); return _map; diff --git a/lib/util/notifications.dart b/lib/util/notifications.dart index 91280da..c652600 100644 --- a/lib/util/notifications.dart +++ b/lib/util/notifications.dart @@ -1,4 +1,4 @@ -import 'package:open_file/open_file.dart'; +// import 'package:open_file/open_file.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; /// Handles all the notification related things. @@ -19,7 +19,7 @@ class Notifications { /// Is called when the notification is pressed. static Future selectNotification(String? payload) async { if (payload != null) { - OpenFile.open(payload, type: "*/*"); // TODO: FIXME: Still doesnt work (sometimes) + // OpenFile.open(payload, type: "*/*"); } } diff --git a/lib/views/info_view.dart b/lib/views/info_view.dart index e2faeab..04c3887 100644 --- a/lib/views/info_view.dart +++ b/lib/views/info_view.dart @@ -14,8 +14,9 @@ class InfoView extends StatefulWidget { } class _InfoViewState extends State { - String websiteUrl = "https://davidpkj.github.io/kulinar_app.html"; - String sourceCodeUrl = "https://github.com/davidpkj/kulinar_app"; + String websiteURL = "https://davidpenkowoj.de"; + String sourceCodeURL = "http://git.davidpenkowoj.de/kulinar_app.git"; + String privacyNoticeURL = "https://davidpenkowoj.de/static/files/privacy-notice.html"; @override Widget build(BuildContext context) { @@ -38,26 +39,36 @@ class _InfoViewState extends State { ), ListTile( title: Text(AppLocalizations.of(context)!.info3, style: cDefaultTextStyle), - subtitle: Text(websiteUrl, style: cDetailsTextStyle, overflow: TextOverflow.ellipsis), + subtitle: Text(websiteURL, style: cDetailsTextStyle, overflow: TextOverflow.ellipsis), trailing: IconButton( icon: Icon(Icons.north_east_rounded), onPressed: () { - _launchURL(Uri.parse(websiteUrl)); + _launchURL(Uri.parse(websiteURL)); }, ), ), ListTile( title: Text(AppLocalizations.of(context)!.info4, style: cDefaultTextStyle), - subtitle: Text(sourceCodeUrl, style: cDetailsTextStyle, overflow: TextOverflow.ellipsis), + subtitle: Text(sourceCodeURL, style: cDetailsTextStyle, overflow: TextOverflow.ellipsis), trailing: IconButton( icon: Icon(Icons.north_east_rounded), onPressed: () { - _launchURL(Uri.parse(sourceCodeUrl)); + _launchURL(Uri.parse(sourceCodeURL)); }, ), ), ListTile( title: Text(AppLocalizations.of(context)!.info5, style: cDefaultTextStyle), + subtitle: Text(privacyNoticeURL, style: cDetailsTextStyle, overflow: TextOverflow.ellipsis), + trailing: IconButton( + icon: Icon(Icons.north_east_rounded), + onPressed: () { + _launchURL(Uri.parse(privacyNoticeURL)); + }, + ), + ), + ListTile( + title: Text(AppLocalizations.of(context)!.info6, style: cDefaultTextStyle), trailing: IconButton( icon: Icon(Icons.launch_rounded), onPressed: () { @@ -65,6 +76,15 @@ class _InfoViewState extends State { }, ), ), +/* ListTile( + title: Text(AppLocalizations.of(context)!.info7, style: cDefaultTextStyle), + trailing: IconButton( + icon: Icon(Icons.launch_rounded), + onPressed: () { + Navigator.push(context, SlideFromRightRoute(child: ChangelogView())); + }, + ), + ), */ ], ), ); @@ -81,9 +101,9 @@ class _InfoViewState extends State { _launchURL(Uri url) async { if (await canLaunchUrl(url)) { - await launchUrl(url); + await launchUrl(url, mode: LaunchMode.externalApplication); } else { - throw 'Could not launch $url'; + print('Could not launch $url'); } } } diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 678db4a..97ad04e 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -90,7 +90,7 @@ class _MainViewState extends State with SingleTickerProviderStateMixin onPressed: () async { await Navigator.push( context, - SlideFromBottomRoute(child: RecipeView(readonly: false)), + SlideFromBottomRoute(child: RecipeView()), ); setState(() {}); diff --git a/lib/views/recipe_view.dart b/lib/views/recipe_view.dart index a0304c3..92a50b7 100644 --- a/lib/views/recipe_view.dart +++ b/lib/views/recipe_view.dart @@ -3,13 +3,13 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:kulinar_app/constants.dart'; -import 'package:kulinar_app/models/data/shoplist_data_class.dart'; import 'package:kulinar_app/views/image_view.dart'; import 'package:kulinar_app/models/recipe_class.dart'; import 'package:kulinar_app/widgets/toastbar_widget.dart'; import 'package:kulinar_app/models/data/recipe_data_class.dart'; import 'package:kulinar_app/widgets/page_route_transitions.dart'; import 'package:kulinar_app/widgets/utility_icon_row_widget.dart'; +import 'package:kulinar_app/models/data/shoplist_data_class.dart'; import 'package:kulinar_app/models/data/settings_data_class.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; @@ -21,10 +21,9 @@ import 'package:http/http.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class RecipeView extends StatefulWidget { - const RecipeView({Key? key, this.remote = false, required this.readonly, this.recipe, this.redrawCallback, this.showToastCallback}) : super(key: key); + const RecipeView({Key? key, this.remote = false, this.recipe, this.redrawCallback, this.showToastCallback}) : super(key: key); final bool remote; - final bool readonly; final Recipe? recipe; final Function? redrawCallback; final Function? showToastCallback; @@ -41,7 +40,7 @@ class _RecipeViewState extends State { final FocusNode _detailsTitleFocus = FocusNode(); final FocusNode _detailsTextFocus = FocusNode(); Recipe _unsavedRecipe = Recipe(); - bool _readonlyOverride = false; + bool _isEditModeEnabled = false; Recipe? _removedRecipe; @override @@ -52,6 +51,7 @@ class _RecipeViewState extends State { if (_unsavedRecipe.isDefault()) { _controller1.clear(); _controller2.clear(); + _isEditModeEnabled = true; } else { _controller1.text = _unsavedRecipe.title ?? ""; _controller2.text = _unsavedRecipe.description ?? ""; @@ -97,16 +97,16 @@ class _RecipeViewState extends State { _unsavedRecipe.title = _controller1.text; _unsavedRecipe.description = _controller2.text; - if (!widget.readonly) { - RecipeData.recipeList.add(_unsavedRecipe); + if (_isEditModeEnabled) { + if (!RecipeData.recipeList.contains(_unsavedRecipe)) RecipeData.recipeList.add(_unsavedRecipe); RecipeData.save(); } + _isEditModeEnabled = false; + if (widget.recipe == null) { Navigator.pop(context); } else { - _readonlyOverride = false; - setState(() {}); } } @@ -131,13 +131,6 @@ class _RecipeViewState extends State { setState(() {}); } - bool _isEditingAllowed() { - if (_readonlyOverride) return true; - if (!widget.readonly) return true; - - return false; - } - // TODO: FIXME: This might introduce bugs later (sanetize); maybe use FileHandler? void downloadRecipe(BuildContext context, Recipe recipe) async { RecipeData.recipeList.add(recipe); @@ -186,13 +179,12 @@ class _RecipeViewState extends State { // _buildAppBarPhotoActions(recipe), return UtilityIconRow( remote: widget.remote, - readonly: !_isEditingAllowed(), + isEditModeEnabled: _isEditModeEnabled, unsavedRecipe: _unsavedRecipe, pickImageCallback: _pickImage, removeImageCallback: _removeImage, removeRecipeCallback: _removeRecipe, downloadRecipeCallback: downloadRecipe, - isEditingAllowedCallback: _isEditingAllowed, cacheUnsavedRecipeCallback: _cacheUnsavedRecipe, ); } @@ -205,7 +197,7 @@ class _RecipeViewState extends State { controller: _controller1, style: cRecipeTitleStyle, cursorColor: cPrimaryColor, - enabled: _isEditingAllowed(), + enabled: _isEditModeEnabled, focusNode: _detailsTitleFocus, textInputAction: TextInputAction.next, decoration: InputDecoration( @@ -257,7 +249,7 @@ class _RecipeViewState extends State { Widget _buildDescriptionInput() { return Padding( padding: const EdgeInsets.all(18.0), - child: _isEditingAllowed() ? _buildInputDescriptuion() : _buildMarkdownDescription(), + child: _isEditModeEnabled ? _buildInputDescriptuion() : _buildMarkdownDescription(), ); } @@ -287,30 +279,7 @@ class _RecipeViewState extends State { selectable: true, onTapLink: (text, href, title) => null, imageBuilder: (uri, title, alt) => Container(), - styleSheet: MarkdownStyleSheet( - tableBody: cRecipeDescriptionStyle, - a: cRecipeDescriptionStyle, - p: cRecipeDescriptionStyle, - h1: cRecipeSubtitleStyle, - h2: cRecipeSubtitleStyle, - h3: cRecipeSubtitleStyle, - h4: cRecipeSubtitleStyle, - h5: cRecipeSubtitleStyle, - h6: cRecipeSubtitleStyle, -/* TODO: remove code and quote block - blockquoteDecoration: BoxDecoration(color: Color.fromARGB(255, 255, 13, 13)), - code: cDetailsTextStyle, -*/ - listIndent: 15.0, - listBullet: cRecipeDescriptionStyle, - listBulletPadding: EdgeInsets.only(right: 10.0), - horizontalRuleDecoration: BoxDecoration( - border: Border.all( - color: Colors.grey, - width: 0.5, - ), - ), - ), + // styleSheet: CustomMarkdownStyle(), ); } @@ -321,6 +290,12 @@ class _RecipeViewState extends State { void _addToShoppingList(BuildContext _) { final _ingredients = _unsavedRecipe.description!.split("\n")..retainWhere((element) => element.startsWith("- ")); + if (_ingredients.isEmpty) { + // TODO: translation + widget.showToastCallback!("No ingredients found", ""); + return; + } + for (String _ingredient in _ingredients) { String _item = _ingredient.substring(2).trim(); @@ -333,10 +308,7 @@ class _RecipeViewState extends State { if (widget.showToastCallback != null) { // TODO: translation - widget.showToastCallback!("Added to Shoplist", "Ok", () { - _addRecipe(passedRecipe: _removedRecipe); - widget.redrawCallback!(); - }); + widget.showToastCallback!("Added to Shoplist", ""); } } @@ -403,7 +375,7 @@ class _RecipeViewState extends State { PreferredSizeWidget _buildAppBar(BuildContext context, Recipe recipe) { String _title = AppLocalizations.of(context)!.mode1; - if (_isEditingAllowed()) _title = AppLocalizations.of(context)!.mode2; + if (_isEditModeEnabled) _title = AppLocalizations.of(context)!.mode2; return AppBar( title: Text(_title), @@ -418,7 +390,7 @@ class _RecipeViewState extends State { } Widget _buildAppBarCheckActions() { - if (_isEditingAllowed()) { + if (_isEditModeEnabled) { return IconButton( icon: Icon(Icons.check), onPressed: _addRecipe, @@ -427,7 +399,7 @@ class _RecipeViewState extends State { return IconButton( icon: Icon(Icons.edit), onPressed: () { - _readonlyOverride = true; + _isEditModeEnabled = true; setState(() {}); }, diff --git a/lib/views/settings_view.dart b/lib/views/settings_view.dart index d217bb7..9bbde49 100644 --- a/lib/views/settings_view.dart +++ b/lib/views/settings_view.dart @@ -80,7 +80,9 @@ class _SettingsViewState extends State { onPressed: () async { final _file = await FileHandler.pickDeserializableFile(context); - await FileHandler.deserializeFile(context, _file!); + if (_file == null) return; + + await FileHandler.deserializeFile(context, _file); setState(() {}); }, diff --git a/lib/widgets/recipe_card_widget.dart b/lib/widgets/recipe_card_widget.dart index 22e50ef..4828b9a 100644 --- a/lib/widgets/recipe_card_widget.dart +++ b/lib/widgets/recipe_card_widget.dart @@ -56,7 +56,6 @@ class _RecipeCardState extends State { SlideFromBottomRoute( child: RecipeView( remote: widget.remote, - readonly: true, recipe: widget.recipe, redrawCallback: widget.redrawCallback, showToastCallback: widget.showToastCallback, @@ -103,10 +102,10 @@ class _RecipeInfo extends StatefulWidget { final Recipe recipe; @override - __RecipeInfoState createState() => __RecipeInfoState(); + _RecipeInfoState createState() => _RecipeInfoState(); } -class __RecipeInfoState extends State<_RecipeInfo> { +class _RecipeInfoState extends State<_RecipeInfo> { @override Widget build(BuildContext context) { return Row( diff --git a/lib/widgets/utility_icon_row_widget.dart b/lib/widgets/utility_icon_row_widget.dart index 3d45d15..c192354 100644 --- a/lib/widgets/utility_icon_row_widget.dart +++ b/lib/widgets/utility_icon_row_widget.dart @@ -9,24 +9,22 @@ class UtilityIconRow extends StatefulWidget { const UtilityIconRow({ Key? key, this.remote = false, - required this.readonly, required this.unsavedRecipe, + required this.isEditModeEnabled, required this.pickImageCallback, required this.removeImageCallback, required this.removeRecipeCallback, required this.downloadRecipeCallback, - required this.isEditingAllowedCallback, required this.cacheUnsavedRecipeCallback, }) : super(key: key); final bool remote; - final bool readonly; + final bool isEditModeEnabled; final Recipe unsavedRecipe; final Function pickImageCallback; final Function removeImageCallback; final Function removeRecipeCallback; final Function downloadRecipeCallback; - final Function isEditingAllowedCallback; final Function cacheUnsavedRecipeCallback; @override @@ -44,18 +42,21 @@ class _UtilityIconRowState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( - padding: const EdgeInsets.all(0), // EdgeInsets.only(left: 4.0), + padding: const EdgeInsets.only(left: 6.0), child: _buildButtonAddImage(), ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - _buildIconRating(context), - _buildIconFavorite(), - // _buildIconCalculate(), - // _buildIconShare(), - // _buildIconImportExport(context), - ], + Padding( + padding: const EdgeInsets.only(right: 6.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + _buildIconRating(context), + _buildIconFavorite(), + // _buildIconCalculate(), + // _buildIconShare(), + // _buildIconImportExport(context), + ], + ), ), ], ), -- cgit v1.2.3