From 2becf35fe061c39692891ae19cc0cdedae3d287d Mon Sep 17 00:00:00 2001 From: monsieurtanuki Date: Thu, 3 Oct 2024 20:00:47 +0200 Subject: [PATCH] feat: 5643 - removed irrelevant editors for non-FOOD products Impacted files: * `add_new_product_page.dart`: editors different when not FOOD; minor refactoring * `edit_product_page.dart`: no ingredient editor for PRODUCTS * `image_field_extension.dart`: now the main image fields depend on product type; minor refactoring (more compact `switch` syntax) * `knowledge_panels_builder.dart`: minor fix * `product_cards_helper.dart`: now the main image fields depend on product type * `product_image_gallery_view.dart`: now the main image fields depend on product type * `product_image_swipeable_view.dart`: now the main image fields depend on product type --- .../lib/helpers/image_field_extension.dart | 116 ++++++++---------- .../lib/helpers/product_cards_helper.dart | 4 +- .../knowledge_panels_builder.dart | 11 +- .../pages/product/add_new_product_page.dart | 85 +++++++++---- .../lib/pages/product/edit_product_page.dart | 18 +-- .../product/product_image_gallery_view.dart | 13 +- .../product/product_image_swipeable_view.dart | 4 +- 7 files changed, 144 insertions(+), 107 deletions(-) diff --git a/packages/smooth_app/lib/helpers/image_field_extension.dart b/packages/smooth_app/lib/helpers/image_field_extension.dart index 5919f0f1693..369933f0fd1 100644 --- a/packages/smooth_app/lib/helpers/image_field_extension.dart +++ b/packages/smooth_app/lib/helpers/image_field_extension.dart @@ -5,12 +5,26 @@ import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dar import 'package:smooth_app/pages/product/product_image_swipeable_view.dart'; extension ImageFieldSmoothieExtension on ImageField { - static const List orderedMain = [ - ImageField.FRONT, - ImageField.INGREDIENTS, - ImageField.NUTRITION, - ImageField.PACKAGING, - ]; + static List getOrderedMainImageFields( + final ProductType? productType, + ) => + switch (productType) { + ProductType.product => const [ + ImageField.FRONT, + ImageField.PACKAGING, + ], + ProductType.beauty => const [ + ImageField.FRONT, + ImageField.INGREDIENTS, + ImageField.PACKAGING, + ], + null || ProductType.food || ProductType.petFood => const [ + ImageField.FRONT, + ImageField.INGREDIENTS, + ImageField.NUTRITION, + ImageField.PACKAGING, + ], + }; void setUrl(final Product product, final String url) { switch (this) { @@ -31,67 +45,45 @@ extension ImageFieldSmoothieExtension on ImageField { } } - String getProductImageButtonText(final AppLocalizations appLocalizations) { - switch (this) { - case ImageField.FRONT: - return appLocalizations.front_photo; - case ImageField.INGREDIENTS: - return appLocalizations.ingredients_photo; - case ImageField.NUTRITION: - return appLocalizations.nutrition_facts_photo; - case ImageField.PACKAGING: - return appLocalizations.packaging_information_photo; - case ImageField.OTHER: - return appLocalizations.more_photos; - } - } + String getProductImageButtonText(final AppLocalizations appLocalizations) => + switch (this) { + ImageField.FRONT => appLocalizations.front_photo, + ImageField.INGREDIENTS => appLocalizations.ingredients_photo, + ImageField.NUTRITION => appLocalizations.nutrition_facts_photo, + ImageField.PACKAGING => appLocalizations.packaging_information_photo, + ImageField.OTHER => appLocalizations.more_photos, + }; /// Returns a verbose description of the image field. - String getImagePageTitle(final AppLocalizations appLocalizations) { - switch (this) { - case ImageField.FRONT: - return appLocalizations.front_packaging_photo_title; - case ImageField.INGREDIENTS: - return appLocalizations.ingredients_photo_title; - case ImageField.NUTRITION: - return appLocalizations.nutritional_facts_photo_title; - case ImageField.PACKAGING: - return appLocalizations.recycling_photo_title; - case ImageField.OTHER: - return appLocalizations.take_more_photo_title; - } - } + String getImagePageTitle(final AppLocalizations appLocalizations) => + switch (this) { + ImageField.FRONT => appLocalizations.front_packaging_photo_title, + ImageField.INGREDIENTS => appLocalizations.ingredients_photo_title, + ImageField.NUTRITION => appLocalizations.nutritional_facts_photo_title, + ImageField.PACKAGING => appLocalizations.recycling_photo_title, + ImageField.OTHER => appLocalizations.take_more_photo_title, + }; /// Returns a compact description of the image field. - String getProductImageTitle(final AppLocalizations appLocalizations) { - switch (this) { - case ImageField.FRONT: - return appLocalizations.product; - case ImageField.INGREDIENTS: - return appLocalizations.ingredients; - case ImageField.NUTRITION: - return appLocalizations.nutrition; - case ImageField.PACKAGING: - return appLocalizations.packaging_information; - case ImageField.OTHER: - return appLocalizations.more_photos; - } - } + String getProductImageTitle(final AppLocalizations appLocalizations) => + switch (this) { + ImageField.FRONT => appLocalizations.product, + ImageField.INGREDIENTS => appLocalizations.ingredients, + ImageField.NUTRITION => appLocalizations.nutrition, + ImageField.PACKAGING => appLocalizations.packaging_information, + ImageField.OTHER => appLocalizations.more_photos, + }; - String getAddPhotoButtonText(final AppLocalizations appLocalizations) { - switch (this) { - case ImageField.FRONT: - return appLocalizations.front_packaging_photo_button_label; - case ImageField.INGREDIENTS: - return appLocalizations.ingredients_photo_button_label; - case ImageField.NUTRITION: - return appLocalizations.nutritional_facts_photo_button_label; - case ImageField.PACKAGING: - return appLocalizations.recycling_photo_button_label; - case ImageField.OTHER: - return appLocalizations.take_more_photo_button_label; - } - } + String getAddPhotoButtonText(final AppLocalizations appLocalizations) => + switch (this) { + ImageField.FRONT => appLocalizations.front_packaging_photo_button_label, + ImageField.INGREDIENTS => + appLocalizations.ingredients_photo_button_label, + ImageField.NUTRITION => + appLocalizations.nutritional_facts_photo_button_label, + ImageField.PACKAGING => appLocalizations.recycling_photo_button_label, + ImageField.OTHER => appLocalizations.take_more_photo_button_label, + }; Widget getPhotoButton( final BuildContext context, diff --git a/packages/smooth_app/lib/helpers/product_cards_helper.dart b/packages/smooth_app/lib/helpers/product_cards_helper.dart index f023ede0411..4f387736d44 100644 --- a/packages/smooth_app/lib/helpers/product_cards_helper.dart +++ b/packages/smooth_app/lib/helpers/product_cards_helper.dart @@ -246,7 +246,9 @@ List getProductMainImagesData( final OpenFoodFactsLanguage language, ) { final List result = []; - for (final ImageField imageField in ImageFieldSmoothieExtension.orderedMain) { + for (final ImageField imageField + in ImageFieldSmoothieExtension.getOrderedMainImageFields( + product.productType)) { result.add(getProductImageData(product, imageField, language)); } return result; diff --git a/packages/smooth_app/lib/knowledge_panel/knowledge_panels_builder.dart b/packages/smooth_app/lib/knowledge_panel/knowledge_panels_builder.dart index ed463987c3a..feaff5bacb7 100644 --- a/packages/smooth_app/lib/knowledge_panel/knowledge_panels_builder.dart +++ b/packages/smooth_app/lib/knowledge_panel/knowledge_panels_builder.dart @@ -201,9 +201,14 @@ class KnowledgePanelsBuilder { if (panel == null) { // happened in https://github.com/openfoodfacts/smooth-app/issues/2682 // due to some inconsistencies in the data sent by the server - Logs.w( - 'unknown panel "$panelId" for barcode "${product.barcode}"', - ); + if (panelId == 'ecoscore' && + (product.productType ?? ProductType.food) != ProductType.food) { + // just ignore + } else { + Logs.w( + 'unknown panel "$panelId" for barcode "${product.barcode}"', + ); + } return null; } return KnowledgePanelCard( diff --git a/packages/smooth_app/lib/pages/product/add_new_product_page.dart b/packages/smooth_app/lib/pages/product/add_new_product_page.dart index 47706058c9c..9abc25f1dd9 100644 --- a/packages/smooth_app/lib/pages/product/add_new_product_page.dart +++ b/packages/smooth_app/lib/pages/product/add_new_product_page.dart @@ -82,9 +82,20 @@ class _AddNewProductPageState extends State with TraceableClientMixin, UpToDateMixin { /// Count of "other" pictures uploaded. int _otherCount = 0; - int _totalPages = 0; - double _progress = 0.0; - bool _isLastPage = false; + + /// The behavior is different for FOOD. And we don't know about it at first. + bool get _probablyFood => + (_inputProductType ?? ProductType.food) == ProductType.food; + + /// Total number of pages: depends on product type. + int get _totalPages => + (_probablyFood ? 3 : 1) + + (widget.displayProductType ? 1 : 0) + + (_probablyFood && widget.displayMisc ? 1 : 0) + + (widget.displayPictures ? 1 : 0); + + double get _progress => (_pageNumber + 1) / _totalPages; + bool get _isLastPage => (_pageNumber + 1) == _totalPages; ProductType? _inputProductType; late ColorScheme _colorScheme; @@ -112,7 +123,8 @@ class _AddNewProductPageState extends State bool _ecoscoreExpanded = false; - int get _pageNumber => _pageController.page!.round(); + int get _pageNumber => + _pageController.hasClients ? _pageController.page!.round() : 0; @override String get actionName => 'Opened add_new_product_page'; @@ -159,18 +171,7 @@ class _AddNewProductPageState extends State widget.events[EditProductAction.openPage]!, barcode: barcode, ); - _totalPages = 3 + - (widget.displayProductType ? 1 : 0) + - (widget.displayMisc ? 1 : 0) + - (widget.displayPictures ? 1 : 0); - _progress = 1 / _totalPages; - - _pageController.addListener(() { - setState(() { - _progress = (_pageNumber + 1) / _totalPages; - _isLastPage = (_pageNumber + 1) == _totalPages; - }); - }); + _pageController.addListener(() => setState(() {})); } Future _onWillPop() async { @@ -262,10 +263,12 @@ class _AddNewProductPageState extends State _buildCard(_getProductTypes(context)), if (widget.displayPictures) _buildCard(_getImageRows(context)), - _buildCard(_getNutriscoreRows(context)), - _buildCard(_getEcoscoreRows(context)), - _buildCard(_getNovaRows(context)), - if (widget.displayMisc) _buildCard(_getMiscRows(context)), + if (_probablyFood) _buildCard(_getNutriscoreRows(context)), + if (_probablyFood) _buildCard(_getEcoscoreRows(context)), + if (_probablyFood) _buildCard(_getNovaRows(context)), + if (!_probablyFood) _buildCard(_getOxFRows(context)), + if (_probablyFood && widget.displayMisc) + _buildCard(_getMiscRows(context)), ], ), ), @@ -387,7 +390,7 @@ class _AddNewProductPageState extends State ); }, child: Text( - (_pageController.hasClients ? _pageNumber : 0) >= 1 + _pageNumber >= 1 ? appLocalizations.previous_label : appLocalizations.cancel, style: const TextStyle( @@ -623,6 +626,31 @@ class _AddNewProductPageState extends State return rows; } + /// More compact, for non-FOOD only. + List _getOxFRows(final BuildContext context) { + return [ + AddNewProductTitle(AppLocalizations.of(context).new_product_title_misc), + AddNewProductEditorButton( + upToDateProduct, + _categoryEditor, + isLoggedInMandatory: widget.isLoggedInMandatory, + ), + if (_inputProductType != ProductType.product) + AddNewProductEditorButton( + upToDateProduct, + _ingredientsEditor, + isLoggedInMandatory: widget.isLoggedInMandatory, + ), + if (_inputProductType == ProductType.petFood) + AddNewProductEditorButton( + upToDateProduct, + _nutritionEditor, + isLoggedInMandatory: widget.isLoggedInMandatory, + ), + _buildDetailsButton(context), + ]; + } + List _getImageRows(final BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); final List rows = []; @@ -635,7 +663,7 @@ class _AddNewProductPageState extends State appLocalizations.new_product_title_pictures_details), ); - // Main 4 images first. + // Main images first. final List productImagesData = getProductMainImagesData( upToDateProduct, ProductQuery.getLanguage(), @@ -726,13 +754,16 @@ class _AddNewProductPageState extends State AddNewProductTitle( AppLocalizations.of(context).new_product_title_misc, ), - AddNewProductEditorButton( - upToDateProduct, - _detailsEditor, - isLoggedInMandatory: widget.isLoggedInMandatory, - ), + _buildDetailsButton(context), ]; + Widget _buildDetailsButton(final BuildContext context) => + AddNewProductEditorButton( + upToDateProduct, + _detailsEditor, + isLoggedInMandatory: widget.isLoggedInMandatory, + ); + Widget _buildIngredientsButton( final BuildContext context, { final IconData? forceIconData, diff --git a/packages/smooth_app/lib/pages/product/edit_product_page.dart b/packages/smooth_app/lib/pages/product/edit_product_page.dart index 6ce68086de5..cbf05806325 100644 --- a/packages/smooth_app/lib/pages/product/edit_product_page.dart +++ b/packages/smooth_app/lib/pages/product/edit_product_page.dart @@ -172,15 +172,17 @@ class _EditProductPageState extends State with UpToDateMixin { SimpleInputPageCategoryHelper(), ], ), - _ListTitleItem( - leading: const SvgIcon('assets/cacheTintable/ingredients.svg'), - title: - appLocalizations.edit_product_form_item_ingredients_title, - onTap: () async => ProductFieldOcrIngredientEditor().edit( - context: context, - product: upToDateProduct, + if (upToDateProduct.productType != ProductType.product) + _ListTitleItem( + leading: + const SvgIcon('assets/cacheTintable/ingredients.svg'), + title: + appLocalizations.edit_product_form_item_ingredients_title, + onTap: () async => ProductFieldOcrIngredientEditor().edit( + context: context, + product: upToDateProduct, + ), ), - ), if (upToDateProduct.productType == null || upToDateProduct.productType == ProductType.food) _getSimpleListTileItem(SimpleInputPageCategoryHelper()) diff --git a/packages/smooth_app/lib/pages/product/product_image_gallery_view.dart b/packages/smooth_app/lib/pages/product/product_image_gallery_view.dart index 0ad9c6ccf09..6b71185b373 100644 --- a/packages/smooth_app/lib/pages/product/product_image_gallery_view.dart +++ b/packages/smooth_app/lib/pages/product/product_image_gallery_view.dart @@ -39,6 +39,7 @@ class ProductImageGalleryView extends StatefulWidget { class _ProductImageGalleryViewState extends State with UpToDateMixin { late OpenFoodFactsLanguage _language; + late final List _mainImageFields; bool _clickedOtherPictureButton = false; @override @@ -46,6 +47,9 @@ class _ProductImageGalleryViewState extends State super.initState(); initUpToDate(widget.product, context.read()); _language = ProductQuery.getLanguage(); + _mainImageFields = ImageFieldSmoothieExtension.getOrderedMainImageFields( + widget.product.productType, + ); } @override @@ -113,11 +117,12 @@ class _ProductImageGalleryViewState extends State (BuildContext context, int index) { return _PhotoRow( position: index, + imageField: _mainImageFields[index], product: upToDateProduct, language: _language, ); }, - childCount: 4, + childCount: _mainImageFields.length, ), ), SliverPadding( @@ -187,15 +192,16 @@ class _PhotoRow extends StatelessWidget { required this.position, required this.product, required this.language, + required this.imageField, }); final int position; final Product product; final OpenFoodFactsLanguage language; + final ImageField imageField; @override Widget build(BuildContext context) { - final ImageField imageField = _getImageField(position); final TransientFile transientFile = _getTransientFile(imageField); final bool expired = transientFile.expired; @@ -293,7 +299,4 @@ class _PhotoRow extends StatelessWidget { imageField, language, ); - - ImageField _getImageField(final int index) => - ImageFieldSmoothieExtension.orderedMain[index]; } diff --git a/packages/smooth_app/lib/pages/product/product_image_swipeable_view.dart b/packages/smooth_app/lib/pages/product/product_image_swipeable_view.dart index f2f5463a720..f37adb6e61e 100644 --- a/packages/smooth_app/lib/pages/product/product_image_swipeable_view.dart +++ b/packages/smooth_app/lib/pages/product/product_image_swipeable_view.dart @@ -65,7 +65,9 @@ class _ProductImageSwipeableViewState extends State if (widget.imageField != null) { _imageFields = [widget.imageField!]; } else { - _imageFields = ImageFieldSmoothieExtension.orderedMain; + _imageFields = ImageFieldSmoothieExtension.getOrderedMainImageFields( + widget.product.productType, + ); } }