diff --git a/packages/smooth_app/lib/cards/category_cards/svg_safe_network.dart b/packages/smooth_app/lib/cards/category_cards/svg_safe_network.dart index 61c81fd365d..5c3988b0e4c 100644 --- a/packages/smooth_app/lib/cards/category_cards/svg_safe_network.dart +++ b/packages/smooth_app/lib/cards/category_cards/svg_safe_network.dart @@ -125,11 +125,21 @@ class _SvgSafeNetworkState extends State { } } if (snapshot.error != null) { - final bool serverOrConnectionIssue = - snapshot.error.toString().contains("Failed host lookup: '"); - if (!serverOrConnectionIssue) { + if (snapshot.error.toString().contains("Failed host lookup: '")) { + Logs.w( + 'Failed host lookup for "$_url"', + ex: snapshot.error, + ); + } else if (snapshot.error + .toString() + .contains('Connection timed out')) { + Logs.w( + 'Connection timed out for "$_url"', + ex: snapshot.error, + ); + } else { Logs.e( - 'Could not download "$_url"', + 'Could really not download "$_url"', ex: snapshot.error, ); } diff --git a/packages/smooth_app/lib/data_models/preferences/user_preferences.dart b/packages/smooth_app/lib/data_models/preferences/user_preferences.dart index 168ccac1d8e..d2cd3f32d86 100644 --- a/packages/smooth_app/lib/data_models/preferences/user_preferences.dart +++ b/packages/smooth_app/lib/data_models/preferences/user_preferences.dart @@ -176,8 +176,20 @@ class UserPreferences extends ChangeNotifier { String _getLazyCountTag(final String tag) => '$_TAG_LAZY_COUNT_PREFIX$tag'; - Future setLazyCount(final int value, final String suffixTag) async => - _sharedPreferences.setInt(_getLazyCountTag(suffixTag), value); + Future setLazyCount( + final int value, + final String suffixTag, { + required final bool notify, + }) async { + final int? oldValue = getLazyCount(suffixTag); + if (value == oldValue) { + return; + } + await _sharedPreferences.setInt(_getLazyCountTag(suffixTag), value); + if (notify) { + notifyListeners(); + } + } int? getLazyCount(final String suffixTag) => _sharedPreferences.getInt(_getLazyCountTag(suffixTag)); diff --git a/packages/smooth_app/lib/database/dao_product.dart b/packages/smooth_app/lib/database/dao_product.dart index bbf0935c5c7..de35e277776 100644 --- a/packages/smooth_app/lib/database/dao_product.dart +++ b/packages/smooth_app/lib/database/dao_product.dart @@ -278,23 +278,42 @@ class DaoProduct extends AbstractSqlDao implements BulkDeletable { required final int limit, required final List excludeBarcodes, }) async { - final List> queryResults = - await localDatabase.database.rawQuery( - 'select p.$_TABLE_PRODUCT_COLUMN_BARCODE ' - 'from' - ' $_TABLE_PRODUCT p' - ' left outer join ${DaoProductLastAccess.TABLE} a' - ' on p.$_TABLE_PRODUCT_COLUMN_BARCODE = a.${DaoProductLastAccess.COLUMN_BARCODE} ' - 'where' - ' p.$_TABLE_PRODUCT_COLUMN_LANGUAGE is null' - ' or p.$_TABLE_PRODUCT_COLUMN_LANGUAGE != ? ' - 'order by a.${DaoProductLastAccess.COLUMN_LAST_ACCESS} desc nulls last ' - 'limit ?', - [ - language.offTag, - limit + excludeBarcodes.length, - ], - ); + /// Unfortunately, some SQFlite implementations don't support "nulls last" + String getRawQuery(final bool withNullsLast) => + 'select p.$_TABLE_PRODUCT_COLUMN_BARCODE ' + 'from' + ' $_TABLE_PRODUCT p' + ' left outer join ${DaoProductLastAccess.TABLE} a' + ' on p.$_TABLE_PRODUCT_COLUMN_BARCODE = a.${DaoProductLastAccess.COLUMN_BARCODE} ' + 'where' + ' p.$_TABLE_PRODUCT_COLUMN_LANGUAGE is null' + ' or p.$_TABLE_PRODUCT_COLUMN_LANGUAGE != ? ' + 'order by a.${DaoProductLastAccess.COLUMN_LAST_ACCESS} desc ${withNullsLast ? 'nulls last' : ''} ' + 'limit ?'; + + List> queryResults = >[]; + try { + queryResults = await localDatabase.database.rawQuery( + getRawQuery(true), + [ + language.offTag, + limit + excludeBarcodes.length, + ], + ); + } catch (e) { + if (!e.toString().startsWith( + 'DatabaseException(near "nulls": syntax error (code 1 SQLITE_ERROR[1])', + )) { + rethrow; + } + queryResults = await localDatabase.database.rawQuery( + getRawQuery(false), + [ + language.offTag, + limit + excludeBarcodes.length, + ], + ); + } final List result = []; diff --git a/packages/smooth_app/lib/pages/preferences/lazy_counter.dart b/packages/smooth_app/lib/pages/preferences/lazy_counter.dart index a8a4cea02b0..a14432500ea 100644 --- a/packages/smooth_app/lib/pages/preferences/lazy_counter.dart +++ b/packages/smooth_app/lib/pages/preferences/lazy_counter.dart @@ -16,9 +16,14 @@ abstract class LazyCounter { /// Sets the value cached locally; Future setLocalCount( final int value, - final UserPreferences userPreferences, - ) => - userPreferences.setLazyCount(value, getSuffixTag()); + final UserPreferences userPreferences, { + required final bool notify, + }) => + userPreferences.setLazyCount( + value, + getSuffixTag(), + notify: notify, + ); /// Returns the suffix tag used to cache the value locally; @protected diff --git a/packages/smooth_app/lib/pages/preferences/lazy_counter_widget.dart b/packages/smooth_app/lib/pages/preferences/lazy_counter_widget.dart index d3861fe8c39..b3f0ebdf6fc 100644 --- a/packages/smooth_app/lib/pages/preferences/lazy_counter_widget.dart +++ b/packages/smooth_app/lib/pages/preferences/lazy_counter_widget.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:smooth_app/data_models/preferences/user_preferences.dart'; @@ -15,41 +17,44 @@ class LazyCounterWidget extends StatefulWidget { class _LazyCounterWidgetState extends State { bool _loading = false; - int? _count; @override void initState() { super.initState(); final UserPreferences userPreferences = context.read(); - _count = widget.lazyCounter.getLocalCount(userPreferences); - if (_count == null) { - _asyncLoad(); + final int? count = widget.lazyCounter.getLocalCount(userPreferences); + if (count == null) { + unawaited(_asyncLoad()); } } @override - Widget build(BuildContext context) => Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - if (_count != null) Text(_count.toString()), - if (_loading) - const Padding( - padding: EdgeInsets.symmetric(horizontal: 12), - child: SizedBox( - width: 24, - height: 24, - child: CircularProgressIndicator.adaptive(), - ), - ) - else - IconButton( - onPressed: () => _asyncLoad(), - icon: const Icon(Icons.refresh), + Widget build(BuildContext context) { + final UserPreferences userPreferences = context.watch(); + final int? count = widget.lazyCounter.getLocalCount(userPreferences); + return Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (count != null) Text(count.toString()), + if (_loading) + const Padding( + padding: EdgeInsets.symmetric(horizontal: 12), + child: SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator.adaptive(), ), - ], - ); + ) + else + IconButton( + onPressed: () => _asyncLoad(), + icon: const Icon(Icons.refresh), + ), + ], + ); + } Future _asyncLoad() async { if (_loading) { @@ -63,8 +68,11 @@ class _LazyCounterWidgetState extends State { try { final int? value = await widget.lazyCounter.getServerCount(); if (value != null) { - await widget.lazyCounter.setLocalCount(value, userPreferences); - _count = value; + await widget.lazyCounter.setLocalCount( + value, + userPreferences, + notify: false, + ); } } catch (e) { // diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_account.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_account.dart index 7f6d04a2dbf..8a951e8e08d 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_account.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_account.dart @@ -278,6 +278,7 @@ class UserPreferencesAccount extends AbstractUserPreferences { uriHelper: ProductQuery.uriPricesHelper, ), title: appLocalizations.all_search_prices_latest_title, + lazyCounterPrices: const LazyCounterPrices(null), ), ), ), diff --git a/packages/smooth_app/lib/pages/prices/get_prices_model.dart b/packages/smooth_app/lib/pages/prices/get_prices_model.dart index 765a3b8bba2..dd350af6ad4 100644 --- a/packages/smooth_app/lib/pages/prices/get_prices_model.dart +++ b/packages/smooth_app/lib/pages/prices/get_prices_model.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/pages/preferences/lazy_counter.dart'; import 'package:smooth_app/pages/prices/price_meta_product.dart'; import 'package:smooth_app/pages/prices/product_price_add_page.dart'; import 'package:smooth_app/query/product_query.dart'; @@ -13,6 +14,7 @@ class GetPricesModel { required this.displayProduct, required this.uri, required this.title, + this.lazyCounterPrices, this.enableCountButton = true, this.subtitle, this.addButton, @@ -84,5 +86,8 @@ class GetPricesModel { /// "Enable the count button?". Typically "false" for product price pages. final bool enableCountButton; + /// Lazy Counter to refresh. + final LazyCounterPrices? lazyCounterPrices; + static const int pageSize = 10; } diff --git a/packages/smooth_app/lib/pages/prices/price_user_button.dart b/packages/smooth_app/lib/pages/prices/price_user_button.dart index 070af93e861..02f1a9ce414 100644 --- a/packages/smooth_app/lib/pages/prices/price_user_button.dart +++ b/packages/smooth_app/lib/pages/prices/price_user_button.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/pages/preferences/lazy_counter.dart'; import 'package:smooth_app/pages/prices/get_prices_model.dart'; import 'package:smooth_app/pages/prices/price_button.dart'; import 'package:smooth_app/pages/prices/prices_page.dart'; @@ -46,6 +47,7 @@ class PriceUserButton extends StatelessWidget { ), title: showUserTitle(user: user, context: context), subtitle: user, + lazyCounterPrices: LazyCounterPrices(user), ), ), ), 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 257b80dcdda..4d595624e4a 100644 --- a/packages/smooth_app/lib/pages/prices/product_prices_list.dart +++ b/packages/smooth_app/lib/pages/prices/product_prices_list.dart @@ -1,7 +1,11 @@ +import 'dart:async'; + 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:provider/provider.dart'; +import 'package:smooth_app/data_models/preferences/user_preferences.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; import 'package:smooth_app/pages/prices/get_prices_model.dart'; @@ -50,6 +54,15 @@ class _ProductPricesListState extends State return Text(snapshot.data!.error!); } final GetPricesResult result = snapshot.data!.value; + if (widget.model.lazyCounterPrices != null && result.total != null) { + unawaited( + widget.model.lazyCounterPrices!.setLocalCount( + result.total!, + context.read(), + notify: true, + ), + ); + } // highly improbable if (result.items == null) { return const Text('empty list');