From 0c9eadfcf0258de4feb71d946d0ca453214759a7 Mon Sep 17 00:00:00 2001 From: monsieurtanuki Date: Thu, 30 Jan 2025 18:23:47 +0100 Subject: [PATCH] feat: 5988 - new "top price products" page (#6310) * feat: 5988 - new "top price products" page New file: * `prices_products_page.dart`: Page that displays the top prices products. Impacted files: * `app_en.arb`: added 1 label for top product counts * `price_product_widget.dart`: minor refactoring * `product_prices_list.dart`: minor refactoring * `pubspec.lock`: wtf * `pubspec.yaml`: upgraded `openfoodfacts` to `3.19.0` * `user_preferences_prices.dart`: link to new page `PricesProductsPage` * Add new localization strings for product prices --------- Co-authored-by: Pierre Slamich --- packages/smooth_app/lib/l10n/app_en.arb | 16 +++ .../preferences/user_preferences_prices.dart | 26 ++-- .../pages/prices/price_product_widget.dart | 6 +- .../pages/prices/prices_products_page.dart | 126 ++++++++++++++++++ .../lib/pages/prices/product_prices_list.dart | 4 +- packages/smooth_app/pubspec.lock | 4 +- packages/smooth_app/pubspec.yaml | 2 +- 7 files changed, 158 insertions(+), 26 deletions(-) create mode 100644 packages/smooth_app/lib/pages/prices/prices_products_page.dart diff --git a/packages/smooth_app/lib/l10n/app_en.arb b/packages/smooth_app/lib/l10n/app_en.arb index 774b60322dad..0582a4de988d 100644 --- a/packages/smooth_app/lib/l10n/app_en.arb +++ b/packages/smooth_app/lib/l10n/app_en.arb @@ -3845,5 +3845,21 @@ "product_footer_action_data_quality_tags": "Data quality", "@product_footer_action_data_quality_tags": { "description": "Action to view data quality warnings" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, + "prices_products_list_length_many_pages": "Top {pageSize} products (total: {total})", + "@prices_products_list_length_many_pages": { + "description": "Number of products for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } } + } } \ No newline at end of file diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_prices.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_prices.dart index 9bd2b0313e30..2b1f05c95656 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_prices.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_prices.dart @@ -1,7 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; -import 'package:smooth_app/helpers/launch_url_helper.dart'; import 'package:smooth_app/pages/navigator/app_navigator.dart'; import 'package:smooth_app/pages/preferences/abstract_user_preferences.dart'; import 'package:smooth_app/pages/preferences/lazy_counter.dart'; @@ -14,6 +13,7 @@ import 'package:smooth_app/pages/prices/price_button.dart'; import 'package:smooth_app/pages/prices/price_user_button.dart'; import 'package:smooth_app/pages/prices/prices_locations_page.dart'; import 'package:smooth_app/pages/prices/prices_page.dart'; +import 'package:smooth_app/pages/prices/prices_products_page.dart'; import 'package:smooth_app/pages/prices/prices_proofs_page.dart'; import 'package:smooth_app/pages/prices/prices_users_page.dart'; import 'package:smooth_app/pages/prices/product_price_add_page.dart'; @@ -121,9 +121,14 @@ class UserPreferencesPrices extends AbstractUserPreferences { ), PriceButton.locationIconData, ), - _getPriceListTile( + _getListTile( appLocalizations.all_search_prices_top_product_title, - 'products', + () async => Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => const PricesProductsPage(), + ), + ), + PriceButton.productIconData, ), ]; } @@ -134,21 +139,6 @@ class UserPreferencesPrices extends AbstractUserPreferences { AppRoutes.PREFERENCES(PreferencePageType.PRICES), ); - UserPreferencesItem _getPriceListTile( - final String title, - final String path, - ) => - _getListTile( - title, - () async => LaunchUrlHelper.launchURL( - OpenPricesAPIClient.getUri( - path: path, - uriHelper: ProductQuery.uriPricesHelper, - ).toString(), - ), - Icons.open_in_new, - ); - UserPreferencesItem _getListTile( final String title, final VoidCallback onTap, diff --git a/packages/smooth_app/lib/pages/prices/price_product_widget.dart b/packages/smooth_app/lib/pages/prices/price_product_widget.dart index a3108578b1ef..ecf02d868dfb 100644 --- a/packages/smooth_app/lib/pages/prices/price_product_widget.dart +++ b/packages/smooth_app/lib/pages/prices/price_product_widget.dart @@ -17,11 +17,11 @@ import 'package:smooth_app/pages/prices/prices_page.dart'; class PriceProductWidget extends StatelessWidget { const PriceProductWidget( this.priceProduct, { - required this.model, + required this.enableCountButton, }); final PriceProduct priceProduct; - final GetPricesModel model; + final bool enableCountButton; @override Widget build(BuildContext context) { @@ -77,7 +77,7 @@ class PriceProductWidget extends StatelessWidget { children: [ PriceCountWidget( count: priceCount, - onPressed: !model.enableCountButton + onPressed: !enableCountButton ? null : () async { final LocalDatabase localDatabase = diff --git a/packages/smooth_app/lib/pages/prices/prices_products_page.dart b/packages/smooth_app/lib/pages/prices/prices_products_page.dart new file mode 100644 index 000000000000..e862430a7c8d --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/prices_products_page.dart @@ -0,0 +1,126 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:matomo_tracker/matomo_tracker.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; +import 'package:smooth_app/helpers/launch_url_helper.dart'; +import 'package:smooth_app/pages/prices/price_product_widget.dart'; +import 'package:smooth_app/query/product_query.dart'; +import 'package:smooth_app/widgets/smooth_app_bar.dart'; +import 'package:smooth_app/widgets/smooth_scaffold.dart'; + +/// Page that displays the top prices products. +class PricesProductsPage extends StatefulWidget { + const PricesProductsPage(); + + @override + State createState() => _PricesProductsPageState(); +} + +class _PricesProductsPageState extends State + with TraceableClientMixin { + late final Future> _products = + _showTopProducts(); + + // In this specific page, let's never try to go beyond the top 10. + // cf. https://github.com/openfoodfacts/smooth-app/pull/5383#issuecomment-2171117141 + static const int _pageSize = 10; + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return SmoothScaffold( + appBar: SmoothAppBar( + centerTitle: false, + leading: const SmoothBackButton(), + title: Text( + appLocalizations.all_search_prices_top_product_title, + ), + actions: [ + IconButton( + tooltip: appLocalizations.prices_app_button, + icon: const Icon(Icons.open_in_new), + onPressed: () async => LaunchUrlHelper.launchURL( + OpenPricesAPIClient.getUri( + path: 'products', + uriHelper: ProductQuery.uriPricesHelper, + ).toString(), + ), + ), + ], + ), + body: FutureBuilder>( + future: _products, + builder: ( + final BuildContext context, + final AsyncSnapshot> snapshot, + ) { + if (snapshot.connectionState != ConnectionState.done) { + return const CircularProgressIndicator(); + } + if (snapshot.hasError) { + return Text(snapshot.error!.toString()); + } + // highly improbable + if (!snapshot.hasData) { + return const Text('no data'); + } + if (snapshot.data!.isError) { + return Text(snapshot.data!.error!); + } + final GetPriceProductsResult result = snapshot.data!.value; + // highly improbable + if (result.items == null) { + return const Text('empty list'); + } + final List children = []; + + for (final PriceProduct item in result.items!) { + children.add( + SmoothCard( + child: PriceProductWidget( + item, + enableCountButton: true, + ), + ), + ); + } + final AppLocalizations appLocalizations = + AppLocalizations.of(context); + final String title = + appLocalizations.prices_products_list_length_many_pages( + _pageSize, + result.total!, + ); + children.insert( + 0, + SmoothCard(child: ListTile(title: Text(title))), + ); + // so that the last content gets not hidden by the FAB + children.add( + const SizedBox(height: 2 * MINIMUM_TOUCH_SIZE), + ); + return ListView( + children: children, + ); + }, + ), + ); + } + + static Future> _showTopProducts() async => + OpenPricesAPIClient.getPriceProducts( + GetPriceProductsParameters() + ..orderBy = >[ + const OrderBy( + field: GetPriceProductsOrderField.priceCount, + ascending: false, + ), + ] + ..pageSize = _pageSize + ..pageNumber = 1, + uriHelper: ProductQuery.uriPricesHelper, + ); +} diff --git a/packages/smooth_app/lib/pages/prices/product_prices_list.dart b/packages/smooth_app/lib/pages/prices/product_prices_list.dart index 26bf878a88d9..0cd7e8ce7510 100644 --- a/packages/smooth_app/lib/pages/prices/product_prices_list.dart +++ b/packages/smooth_app/lib/pages/prices/product_prices_list.dart @@ -82,7 +82,7 @@ class _ProductPricesListState extends State SmoothCard( child: PriceProductWidget( priceProduct, - model: widget.model, + enableCountButton: widget.model.enableCountButton, ), ), ); @@ -116,7 +116,7 @@ class _ProductPricesListState extends State if (widget.model.displayEachProduct && priceProduct != null) PriceProductWidget( priceProduct, - model: widget.model, + enableCountButton: widget.model.enableCountButton, ), PriceDataWidget( price, diff --git a/packages/smooth_app/pubspec.lock b/packages/smooth_app/pubspec.lock index ef79eb94a726..834db1d38161 100644 --- a/packages/smooth_app/pubspec.lock +++ b/packages/smooth_app/pubspec.lock @@ -1113,10 +1113,10 @@ packages: dependency: "direct main" description: name: openfoodfacts - sha256: c0d85d236aebc874b694bb0b52c34cee2d0d68169cffcec0d006605c2014ea04 + sha256: d23892da30f3cab7a767dcbfa894ed556ee44fa925b7c16f42305eb07880b84f url: "https://pub.dev" source: hosted - version: "3.18.1" + version: "3.19.0" openfoodfacts_flutter_lints: dependency: "direct dev" description: diff --git a/packages/smooth_app/pubspec.yaml b/packages/smooth_app/pubspec.yaml index c383dde96b9c..d1831cc767ad 100644 --- a/packages/smooth_app/pubspec.yaml +++ b/packages/smooth_app/pubspec.yaml @@ -99,7 +99,7 @@ dependencies: path: ../scanner/zxing - openfoodfacts: 3.18.1 + openfoodfacts: 3.19.0 # openfoodfacts: # path: ../../../openfoodfacts-dart