From f0c26da28474380c2901ce3af3fd73b09f1e14cc Mon Sep 17 00:00:00 2001 From: monsieurtanuki Date: Fri, 8 Dec 2023 17:51:18 +0100 Subject: [PATCH] feat: 4674 - added the "other photos" section (#4866) * feat: 4674 - added the "other photos" section New files: * `product_image_gallery_other_view.dart`: Display of the other pictures of a product. * `product_image_other_page.dart`: Full page display of a raw product image. Impacted file: * `product_image_gallery_view.dart`: added the "other photos" section; fixed a "refresh not working" bug * feat: 4674 - added a "click button" step * feat: 4674 - different label for button Impacted files: * `app_en.arb`: add new label for "click for other photo" button * `app_fr.arb`: add new label for "click for other photo" button * `product_image_gallery_view.dart`: used new label for "click for other photo" button --- packages/smooth_app/lib/l10n/app_en.arb | 2 + packages/smooth_app/lib/l10n/app_fr.arb | 2 + .../product_image_gallery_other_view.dart | 103 ++++++++++++++++++ .../pages/image/product_image_other_page.dart | 39 +++++++ .../product/product_image_gallery_view.dart | 71 ++++++++---- 5 files changed, 194 insertions(+), 23 deletions(-) create mode 100644 packages/smooth_app/lib/pages/image/product_image_gallery_other_view.dart create mode 100644 packages/smooth_app/lib/pages/image/product_image_other_page.dart diff --git a/packages/smooth_app/lib/l10n/app_en.arb b/packages/smooth_app/lib/l10n/app_en.arb index 6cbc494bb313..f57722199e75 100644 --- a/packages/smooth_app/lib/l10n/app_en.arb +++ b/packages/smooth_app/lib/l10n/app_en.arb @@ -714,6 +714,8 @@ "nutrition_page_update_done": "Product updated!", "more_photos": "More interesting photos", "@more_photos": {}, + "view_more_photo_button": "View all existing photos for this product", + "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, "not_found": "not found:", diff --git a/packages/smooth_app/lib/l10n/app_fr.arb b/packages/smooth_app/lib/l10n/app_fr.arb index 302b13f23c8a..8eee0f0a6f11 100644 --- a/packages/smooth_app/lib/l10n/app_fr.arb +++ b/packages/smooth_app/lib/l10n/app_fr.arb @@ -714,6 +714,8 @@ "nutrition_page_update_done": "Produit mis à jour !", "more_photos": "Plus de photos intéressantes", "@more_photos": {}, + "view_more_photo_button": "Voir toutes les photos existantes pour ce produit", + "@view_more_photo_button": {}, "no_product_found": "Aucun produit trouvé", "@no_product_found": {}, "not_found": "non trouvé :", diff --git a/packages/smooth_app/lib/pages/image/product_image_gallery_other_view.dart b/packages/smooth_app/lib/pages/image/product_image_gallery_other_view.dart new file mode 100644 index 000000000000..80cb10d4378a --- /dev/null +++ b/packages/smooth_app/lib/pages/image/product_image_gallery_other_view.dart @@ -0,0 +1,103 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/generic_lib/widgets/images/smooth_image.dart'; +import 'package:smooth_app/pages/image/product_image_other_page.dart'; +import 'package:smooth_app/query/product_query.dart'; + +/// Display of the other pictures of a product. +class ProductImageGalleryOtherView extends StatefulWidget { + const ProductImageGalleryOtherView({ + required this.product, + }); + + final Product product; + + @override + State createState() => + _ProductImageGalleryOtherViewState(); +} + +class _ProductImageGalleryOtherViewState + extends State { + late final Future> _loading = _loadOtherPics(); + + /// Number of columns for the grid. + static const int _columns = 3; + + Future> _loadOtherPics() async => + OpenFoodAPIClient.getProductImageIds( + widget.product.barcode!, + uriHelper: ProductQuery.uriProductHelper, + user: ProductQuery.getUser(), + ); + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final double screenWidth = MediaQuery.of(context).size.width; + final double squareSize = screenWidth / _columns; + return FutureBuilder>( + future: _loading, + builder: ( + final BuildContext context, + final AsyncSnapshot> snapshot, + ) { + if (snapshot.connectionState != ConnectionState.done) { + return SizedBox( + width: squareSize, + height: squareSize, + child: const CircularProgressIndicator.adaptive(), + ); + } + if (snapshot.data == null) { + return Text( + snapshot.error?.toString() ?? + appLocalizations.loading_dialog_default_error_message, + ); + } + final List ids = snapshot.data!; + if (ids.isEmpty) { + // very unlikely btw. + return Text( + appLocalizations.edit_photo_select_existing_downloaded_none, + ); + } + return SizedBox( + height: (ids.length / _columns).ceil() * squareSize, + child: GridView.builder( + physics: const NeverScrollableScrollPhysics(), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: _columns, + ), + itemBuilder: (final BuildContext context, final int index) => + InkWell( + onTap: () async => Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => ProductImageOtherPage( + widget.product, + ids[index], + ), + ), + ), + child: SmoothImage( + width: squareSize, + height: squareSize, + imageProvider: NetworkImage( + ImageHelper.getUploadedImageUrl( + widget.product.barcode!, + ids[index], + ImageSize.DISPLAY, + ), + ), + ), + ), + itemCount: ids.length, + //scrollDirection: Axis.vertical, + ), + ); + }, + ); + } +} diff --git a/packages/smooth_app/lib/pages/image/product_image_other_page.dart b/packages/smooth_app/lib/pages/image/product_image_other_page.dart new file mode 100644 index 000000000000..0bce9f87d33b --- /dev/null +++ b/packages/smooth_app/lib/pages/image/product_image_other_page.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/helpers/product_cards_helper.dart'; +import 'package:smooth_app/widgets/smooth_app_bar.dart'; +import 'package:smooth_app/widgets/smooth_scaffold.dart'; + +/// Full page display of a raw product image. +class ProductImageOtherPage extends StatelessWidget { + const ProductImageOtherPage( + this.product, + this.imageId, + ); + + final Product product; + final int imageId; + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return SmoothScaffold( + appBar: SmoothAppBar( + centerTitle: false, + title: Text(appLocalizations.edit_product_form_item_photos_title), + subTitle: buildProductTitle(product, appLocalizations), + ), + body: Image( + image: NetworkImage( + ImageHelper.getUploadedImageUrl( + product.barcode!, + imageId, + ImageSize.ORIGINAL, + ), + ), + fit: BoxFit.cover, + ), + ); + } +} 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 821b3b8721a5..2e5e1f155941 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 @@ -5,12 +5,14 @@ import 'package:provider/provider.dart'; import 'package:smooth_app/data_models/up_to_date_mixin.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/database/transient_file.dart'; +import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/widgets/images/smooth_image.dart'; import 'package:smooth_app/generic_lib/widgets/language_selector.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; import 'package:smooth_app/helpers/image_field_extension.dart'; import 'package:smooth_app/helpers/product_cards_helper.dart'; +import 'package:smooth_app/pages/image/product_image_gallery_other_view.dart'; import 'package:smooth_app/pages/image_crop_page.dart'; import 'package:smooth_app/pages/product/common/product_refresher.dart'; import 'package:smooth_app/pages/product/product_image_swipeable_view.dart'; @@ -34,6 +36,7 @@ class ProductImageGalleryView extends StatefulWidget { class _ProductImageGalleryViewState extends State with UpToDateMixin { late OpenFoodFactsLanguage _language; + bool _clickedOtherPictureButton = false; @override void initState() { @@ -76,30 +79,49 @@ class _ProductImageGalleryViewState extends State barcode: barcode, context: context, ), - child: SingleChildScrollView( - child: Column( - children: [ - LanguageSelector( - setLanguage: (final OpenFoodFactsLanguage? newLanguage) async { - if (newLanguage == null || newLanguage == _language) { - return; - } - setState(() => _language = newLanguage); - }, - displayedLanguage: _language, - selectedLanguages: null, - padding: const EdgeInsetsDirectional.symmetric( - horizontal: 13.0, - vertical: SMALL_SPACE, + child: ListView( + children: [ + LanguageSelector( + setLanguage: (final OpenFoodFactsLanguage? newLanguage) async { + if (newLanguage == null || newLanguage == _language) { + return; + } + setState(() => _language = newLanguage); + }, + displayedLanguage: _language, + selectedLanguages: null, + padding: const EdgeInsetsDirectional.symmetric( + horizontal: 13.0, + vertical: SMALL_SPACE, + ), + ), + _ImageRow(row: 1, product: upToDateProduct, language: _language), + _TextRow(row: 1, product: upToDateProduct, language: _language), + _ImageRow(row: 2, product: upToDateProduct, language: _language), + _TextRow(row: 2, product: upToDateProduct, language: _language), + if (!_clickedOtherPictureButton) + Padding( + padding: const EdgeInsets.all(SMALL_SPACE), + child: SmoothLargeButtonWithIcon( + text: appLocalizations.view_more_photo_button, + icon: Icons.photo_camera_rounded, + onPressed: () => setState( + () => _clickedOtherPictureButton = true, + ), ), ), - _ImageRow(row: 1, product: upToDateProduct, language: _language), - _TextRow(row: 1, product: upToDateProduct, language: _language), - _ImageRow(row: 2, product: upToDateProduct, language: _language), - _TextRow(row: 2, product: upToDateProduct, language: _language), - // TODO(monsieurtanuki): add "other photos" for issue 4674 - ], - ), + if (_clickedOtherPictureButton) + Padding( + padding: const EdgeInsets.all(SMALL_SPACE), + child: Text( + appLocalizations.more_photos, + style: _getTextStyle(context), + ), + ), + if (_clickedOtherPictureButton) + ProductImageGalleryOtherView(product: upToDateProduct), + const SizedBox(height: 2 * VERY_LARGE_SPACE), + ], ), ), ); @@ -269,10 +291,13 @@ class _Text extends StatelessWidget { child: Center( child: Text( imageField.getProductImageTitle(AppLocalizations.of(context)), - style: Theme.of(context).textTheme.headlineMedium, + style: _getTextStyle(context), textAlign: TextAlign.center, ), ), ), ); } + +TextStyle? _getTextStyle(final BuildContext context) => + Theme.of(context).textTheme.headlineMedium;