import 'package:flutter/painting.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:smooth_app/background/background_task.dart'; import 'package:smooth_app/background/background_task_queue.dart'; import 'package:smooth_app/background/operation_type.dart'; import 'package:smooth_app/database/dao_product.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/l10n/app_localizations.dart'; import 'package:smooth_app/query/product_query.dart'; import 'package:smooth_app/query/search_products_manager.dart'; /// Background task about downloading products to translate. class BackgroundTaskLanguageRefresh extends BackgroundTask { BackgroundTaskLanguageRefresh._({ required super.processName, required super.uniqueId, required super.stamp, required this.excludeBarcodes, required this.productType, }); BackgroundTaskLanguageRefresh.fromJson(super.json) : excludeBarcodes = _getStringList(json, _jsonTagExcludeBarcodes), productType = ProductType.fromOffTag(json[_jsonTagProductType] as String?) ?? // for legacy reason (not refreshed products = no product type) ProductType.food, super.fromJson(); static List _getStringList( final Map json, final String tag, ) { final List dynamicList = json[_jsonTagExcludeBarcodes] as List; final List result = []; for (final dynamic item in dynamicList) { result.add(item.toString()); } return result; } final List excludeBarcodes; final ProductType productType; static const String _jsonTagExcludeBarcodes = 'excludeBarcodes'; static const String _jsonTagProductType = 'productType'; @override Map toJson() { final Map result = super.toJson(); result[_jsonTagExcludeBarcodes] = excludeBarcodes; result[_jsonTagProductType] = productType.offTag; return result; } static const OperationType _operationType = OperationType.languageRefresh; UriProductHelper get _uriProductHelper => ProductQuery.getUriProductHelper(productType: productType); static Future addTask(final LocalDatabase localDatabase) async { for (final ProductType productType in ProductType.values) { await _addTask( localDatabase, excludeBarcodes: [], productType: productType, ); } } static Future _addTask( final LocalDatabase localDatabase, { required final List excludeBarcodes, required final ProductType productType, }) async { final String uniqueId = await _operationType.getNewKey( localDatabase, productType: productType, ); final BackgroundTask task = _getNewTask( uniqueId, excludeBarcodes, productType, ); await task.addToManager(localDatabase, queue: BackgroundTaskQueue.longHaul); } @override (String, AlignmentGeometry)? getFloatingMessage( final AppLocalizations appLocalizations, ) => null; static BackgroundTask _getNewTask( final String uniqueId, final List excludeBarcodes, final ProductType productType, ) => BackgroundTaskLanguageRefresh._( processName: _operationType.processName, uniqueId: uniqueId, stamp: ';languageRefresh;${productType.offTag}', excludeBarcodes: excludeBarcodes, productType: productType, ); @override Future preExecute(final LocalDatabase localDatabase) async {} @override bool get hasImmediateNextTask => true; /// Number of products to download each time. static const int _pageSize = 20; @override Future execute(final LocalDatabase localDatabase) async { final DaoProduct daoProduct = DaoProduct(localDatabase); final OpenFoodFactsLanguage language = ProductQuery.getLanguage(); final List barcodes = await daoProduct.getTopProductsToTranslate( language, limit: _pageSize, excludeBarcodes: excludeBarcodes, productType: productType, ); if (barcodes.isEmpty) { return; } final SearchResult searchResult = await SearchProductsManager.searchProducts( getUser(), ProductSearchQueryConfiguration( fields: ProductQuery.fields, parametersList: [ const PageSize(size: _pageSize), const PageNumber(page: 1), BarcodeParameter.list(barcodes), ], language: language, country: ProductQuery.getCountry(), version: ProductQuery.productQueryVersion, ), uriHelper: _uriProductHelper, type: SearchProductsType.background, ); if (searchResult.products == null || searchResult.count == null) { throw Exception('Cannot refresh language'); } // save into database and refresh all visible products. await daoProduct.putAll( searchResult.products!, language, productType: productType, ); localDatabase.upToDate.setLatestDownloadedProducts(searchResult.products!); // Next page final List newExcludeBarcodes = []; // we keep the old "excluded" barcodes,... newExcludeBarcodes.addAll(excludeBarcodes); // ...add the new barcodes... newExcludeBarcodes.addAll(barcodes); // ...and remove barcodes we actually found on the server. for (final Product product in searchResult.products!) { newExcludeBarcodes.remove(product.barcode); } await _addTask( localDatabase, excludeBarcodes: newExcludeBarcodes, productType: productType, ); } }