From b9e0a2317d5b90650c728b0385cf46448c4f3978 Mon Sep 17 00:00:00 2001 From: Edouard Marquez Date: Tue, 28 Jan 2025 10:49:06 +0100 Subject: [PATCH] When an image is unavailable, we try to find in another language (#6297) --- .../product_cards/product_title_card.dart | 1 + .../smooth_product_card_found.dart | 1 + .../product_cards/smooth_product_image.dart | 49 ++++++++++++++++++- .../product_image_gallery_photo_row.dart | 1 + 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/smooth_app/lib/cards/product_cards/product_title_card.dart b/packages/smooth_app/lib/cards/product_cards/product_title_card.dart index 41609b5bbf58..740f05dd0e9d 100644 --- a/packages/smooth_app/lib/cards/product_cards/product_title_card.dart +++ b/packages/smooth_app/lib/cards/product_cards/product_title_card.dart @@ -79,6 +79,7 @@ class ProductTitleCard extends StatelessWidget { product: product, imageField: ImageField.FRONT, fallbackUrl: product.imageFrontUrl, + allowAlternativeLanguage: true, size: imageSize, showObsoleteIcon: true, imageFoundBorder: 1.0, diff --git a/packages/smooth_app/lib/cards/product_cards/smooth_product_card_found.dart b/packages/smooth_app/lib/cards/product_cards/smooth_product_card_found.dart index 4b3706b51963..82f643d0c372 100644 --- a/packages/smooth_app/lib/cards/product_cards/smooth_product_card_found.dart +++ b/packages/smooth_app/lib/cards/product_cards/smooth_product_card_found.dart @@ -211,6 +211,7 @@ class _SmoothProductItemPicture extends StatelessWidget { product: product, imageField: ImageField.FRONT, fallbackUrl: product.imageFrontUrl, + allowAlternativeLanguage: true, size: Size(size, size - (scoreWidget != null ? 25.0 : 5.0)), borderRadius: BorderRadius.vertical( top: hasScore ? Radius.zero : const Radius.circular(08.0), diff --git a/packages/smooth_app/lib/cards/product_cards/smooth_product_image.dart b/packages/smooth_app/lib/cards/product_cards/smooth_product_image.dart index e018e756e73d..655da4ced722 100644 --- a/packages/smooth_app/lib/cards/product_cards/smooth_product_image.dart +++ b/packages/smooth_app/lib/cards/product_cards/smooth_product_image.dart @@ -25,6 +25,7 @@ class ProductPicture extends StatefulWidget { required ImageField imageField, required Size size, OpenFoodFactsLanguage? language, + bool allowAlternativeLanguage = false, String? fallbackUrl, VoidCallback? onTap, String? heroTag, @@ -43,6 +44,7 @@ class ProductPicture extends StatefulWidget { product: product, imageField: imageField, language: language ?? ProductQuery.getLanguage(), + allowAlternativeLanguage: allowAlternativeLanguage, size: size, fallbackUrl: fallbackUrl, heroTag: heroTag, @@ -61,6 +63,7 @@ class ProductPicture extends StatefulWidget { required TransientFile transientFile, required Size size, OpenFoodFactsLanguage? language, + bool allowAlternativeLanguage = false, Product? product, ImageField? imageField, String? fallbackUrl, @@ -79,6 +82,7 @@ class ProductPicture extends StatefulWidget { product: product, imageField: imageField, language: language, + allowAlternativeLanguage: allowAlternativeLanguage, size: size, fallbackUrl: fallbackUrl, heroTag: heroTag, @@ -97,6 +101,7 @@ class ProductPicture extends StatefulWidget { required this.product, required this.imageField, required this.language, + required this.allowAlternativeLanguage, required this.transientFile, required this.size, required this.blurFilter, @@ -127,6 +132,10 @@ class ProductPicture extends StatefulWidget { final String? heroTag; + /// When an image is unavailable in the [language] or product main language, + /// we try to find another one in an alternative language. + final bool allowAlternativeLanguage; + /// Show the obsolete icon on top of the image final bool showObsoleteIcon; @@ -161,6 +170,7 @@ class _ProductPictureState extends State { final (ImageProvider?, bool)? imageProvider = _getImageProvider( widget.product, widget.transientFile, + widget.allowAlternativeLanguage, ); final Widget? inkWell = widget.onTap != null @@ -264,6 +274,7 @@ class _ProductPictureState extends State { (ImageProvider?, bool)? _getImageProvider( Product? product, TransientFile? transientFile, + bool allowAlternativeLanguage, ) { if (transientFile != null) { return (transientFile.getImageProvider(), transientFile.expired); @@ -279,8 +290,44 @@ class _ProductPictureState extends State { if (imageProvider != null) { return (imageProvider, productTransientFile.expired); - } else if (widget.fallbackUrl?.isNotEmpty == true) { + } + + if (widget.fallbackUrl?.isNotEmpty == true) { return (NetworkImage(widget.fallbackUrl!), false); + } else if (allowAlternativeLanguage) { + Iterable? images = widget.product?.images?.where( + (ProductImage image) => + image.field == widget.imageField && image.url != null, + ); + + if (images == null || images.isEmpty) { + return null; + } + + /// Let's try with English images + final Iterable englishImages = images.where( + (ProductImage image) => image.language == OpenFoodFactsLanguage.ENGLISH, + ); + + if (englishImages.isNotEmpty) { + images = englishImages; + } + + /// We prefer a display image + ProductImage? productImage; + + for (final ProductImage image in images) { + if (image.size == ImageSize.DISPLAY) { + productImage = image; + break; + } else if (image.size == ImageSize.ORIGINAL) { + productImage = image; + } + } + + productImage ??= images.first; + + return (NetworkImage(productImage.url!), false); } else { return null; } diff --git a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart index f56614134a2f..8a328a3a10ed 100644 --- a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart +++ b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart @@ -160,6 +160,7 @@ class _ImageGalleryPhotoRowState extends State { product: product, imageField: widget.imageField, language: widget.language, + allowAlternativeLanguage: false, transientFile: transientFile, size: Size(box.maxWidth, box.maxHeight), onTap: null,