From 6651e8b0aba09977f675d59a3d8e49c8cb84055d Mon Sep 17 00:00:00 2001 From: monsieurtanuki Date: Sat, 12 Feb 2022 19:01:38 +0100 Subject: [PATCH] feat: #954 - actually saves nutrients on the BE and refreshes the local DB Please run some tests on DEV env to limit the potential impact on PROD! New file: * `rainforest-alliance.90x90.svg`: unrelated asset cache refresh Impacted files: * `knowledge_panels_builder.dart`: added an optional callback for page refresh after data saving; calls that callback after saving nutrients * `new_product_page.dart`: added a callback parameter for page refresh * `nutrition_page_loaded.dart`: actually saves the nutrients on the BE and refreshes the local database --- .../cache/rainforest-alliance.90x90.svg | 1 + .../knowledge_panels_builder.dart | 12 ++- .../lib/pages/product/new_product_page.dart | 4 +- .../pages/product/nutrition_page_loaded.dart | 82 ++++++++++++++----- 4 files changed, 73 insertions(+), 26 deletions(-) create mode 100644 packages/smooth_app/assets/cache/rainforest-alliance.90x90.svg diff --git a/packages/smooth_app/assets/cache/rainforest-alliance.90x90.svg b/packages/smooth_app/assets/cache/rainforest-alliance.90x90.svg new file mode 100644 index 00000000000..e2b24d6639e --- /dev/null +++ b/packages/smooth_app/assets/cache/rainforest-alliance.90x90.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/smooth_app/lib/cards/product_cards/knowledge_panels/knowledge_panels_builder.dart b/packages/smooth_app/lib/cards/product_cards/knowledge_panels/knowledge_panels_builder.dart index 44f60ebb3d1..5d77bad4bac 100644 --- a/packages/smooth_app/lib/cards/product_cards/knowledge_panels/knowledge_panels_builder.dart +++ b/packages/smooth_app/lib/cards/product_cards/knowledge_panels/knowledge_panels_builder.dart @@ -17,7 +17,10 @@ import 'package:smooth_app/widgets/loading_dialog.dart'; /// /// Panels display large data like all health data or environment data. class KnowledgePanelsBuilder { - const KnowledgePanelsBuilder(); + const KnowledgePanelsBuilder({this.setState}); + + /// Would for instance refresh the product page. + final VoidCallback? setState; /// Builds all panels. /// @@ -120,15 +123,18 @@ class KnowledgePanelsBuilder { await LoadingDialog.error(context: context); return; } - await Navigator.push( + final bool? refreshed = await Navigator.push( context, - MaterialPageRoute( + MaterialPageRoute( builder: (BuildContext context) => NutritionPageLoaded( product, orderedNutrients, ), ), ); + if (refreshed ?? false) { + setState?.call(); + } // TODO(monsieurtanuki): refresh the data if changed }, ), diff --git a/packages/smooth_app/lib/pages/product/new_product_page.dart b/packages/smooth_app/lib/pages/product/new_product_page.dart index a1515dd32ae..73c472cc1ac 100644 --- a/packages/smooth_app/lib/pages/product/new_product_page.dart +++ b/packages/smooth_app/lib/pages/product/new_product_page.dart @@ -174,7 +174,9 @@ class _ProductPageState extends State { List knowledgePanelWidgets = []; if (snapshot.hasData) { // Render all KnowledgePanels - knowledgePanelWidgets = const KnowledgePanelsBuilder().buildAll( + knowledgePanelWidgets = + KnowledgePanelsBuilder(setState: () => setState(() {})) + .buildAll( snapshot.data!, product: _product, context: context, diff --git a/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart b/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart index c63e0044cb3..21c9c9904dd 100644 --- a/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart +++ b/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart @@ -8,6 +8,9 @@ import 'package:openfoodfacts/model/OrderedNutrient.dart'; import 'package:openfoodfacts/model/OrderedNutrients.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:openfoodfacts/utils/UnitHelper.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/database/dao_product.dart'; +import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/database/product_query.dart'; import 'package:smooth_app/generic_lib/buttons/smooth_action_button.dart'; import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; @@ -84,6 +87,7 @@ class _NutritionPageLoadedState extends State { @override Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context)!; + final LocalDatabase localDatabase = context.read(); final List children = []; children.add(_switchNoNutrition(appLocalizations)); if (!_unspecified) { @@ -100,7 +104,10 @@ class _NutritionPageLoadedState extends State { } children.add(_addNutrientButton(appLocalizations)); } - children.add(_addCancelSaveButtons(appLocalizations)); + children.add(_addCancelSaveButtons( + appLocalizations, + localDatabase, + )); return Scaffold( appBar: AppBar(title: Text(appLocalizations.nutrition_page_title)), @@ -434,7 +441,11 @@ class _NutritionPageLoadedState extends State { label: Text(appLocalizations.nutrition_page_add_nutrient), ); - Widget _addCancelSaveButtons(final AppLocalizations appLocalizations) => Row( + Widget _addCancelSaveButtons( + final AppLocalizations appLocalizations, + final LocalDatabase localDatabase, + ) => + Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -447,14 +458,14 @@ class _NutritionPageLoadedState extends State { if (!_formKey.currentState!.validate()) { return; } - await _save(); + await _save(localDatabase); }, child: Text(appLocalizations.save), ), ], ); - Future _save() async { + Future _save(final LocalDatabase localDatabase) async { final Map map = {}; String? servingSize; for (final String key in _controllers.keys) { @@ -473,28 +484,24 @@ class _NutritionPageLoadedState extends State { } } final Nutriments nutriments = Nutriments.fromJson(map); - widget.product.nutriments = - nutriments; // TODO(monsieurtanuki): here we impact directly the product share with the previous screen, not nice! - widget.product.servingSize = servingSize; - - final Status? status = await LoadingDialog.run( - future: Future.delayed(const Duration(seconds: 2), () => Status() - /* TODO(monsieurtanuki): put back the actual call - OpenFoodAPIClient.saveProduct( - ProductQuery.getUser(), - widget.product, - */ - ), + // minimal product: we only want to save the nutrients + final Product inputProduct = Product( + barcode: widget.product.barcode, + nutriments: nutriments, + servingSize: servingSize, + ); + + final bool? savedAndRefreshed = await LoadingDialog.run( + future: _saveAndRefresh(inputProduct, localDatabase), context: context, - title: '${AppLocalizations.of(context)!.nutrition_page_update_running}' - ' (in fact just waiting 2 seconds)', + title: AppLocalizations.of(context)!.nutrition_page_update_running, ); - if (status == null) { + if (savedAndRefreshed == null) { // probably the end user stopped the dialog return; } - if (status.error != null) { - await LoadingDialog.error(context: context, title: status.error); + if (!savedAndRefreshed) { + await LoadingDialog.error(context: context); return; } await showDialog( @@ -508,6 +515,37 @@ class _NutritionPageLoadedState extends State { ], ), ); - Navigator.of(context).pop(); + Navigator.of(context).pop(true); + } + + /// Saves a product on the BE and refreshes the local database + Future _saveAndRefresh( + final Product inputProduct, + final LocalDatabase localDatabase, + ) async { + try { + final Status status = await OpenFoodAPIClient.saveProduct( + ProductQuery.getUser(), + inputProduct, + ); + if (status.error != null) { + return false; + } + final ProductQueryConfiguration configuration = ProductQueryConfiguration( + inputProduct.barcode!, + fields: ProductQuery.fields, + language: ProductQuery.getLanguage(), + country: ProductQuery.getCountry(), + ); + final ProductResult result = + await OpenFoodAPIClient.getProduct(configuration); + if (result.product != null) { + await DaoProduct(context.read()).put(result.product!); + return true; + } + } catch (e) { + // + } + return false; } }