-
-
Notifications
You must be signed in to change notification settings - Fork 287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: 4941 - refresh products when switching app language #5016
Changes from all commits
8ba0da6
36bf98a
bb9f9ff
d7e95de
bbc049f
dbf55c9
4b1353b
857b8ac
95635ea
dce922a
4585575
ff79443
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import 'package:flutter/painting.dart'; | ||
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||
import 'package:openfoodfacts/openfoodfacts.dart'; | ||
import 'package:smooth_app/background/background_task.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/query/product_query.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, | ||
}); | ||
|
||
BackgroundTaskLanguageRefresh.fromJson(Map<String, dynamic> json) | ||
: excludeBarcodes = _getStringList(json, _jsonTagExcludeBarcodes), | ||
super.fromJson(json); | ||
|
||
static List<String> _getStringList( | ||
final Map<String, dynamic> json, final String tag) { | ||
final List<dynamic> dynamicList = | ||
json[_jsonTagExcludeBarcodes] as List<dynamic>; | ||
final List<String> result = <String>[]; | ||
for (final dynamic item in dynamicList) { | ||
result.add(item.toString()); | ||
} | ||
return result; | ||
} | ||
|
||
final List<String> excludeBarcodes; | ||
|
||
static const String _jsonTagExcludeBarcodes = 'excludeBarcodes'; | ||
|
||
@override | ||
Map<String, dynamic> toJson() { | ||
final Map<String, dynamic> result = super.toJson(); | ||
result[_jsonTagExcludeBarcodes] = excludeBarcodes; | ||
return result; | ||
} | ||
|
||
static const OperationType _operationType = OperationType.languageRefresh; | ||
|
||
static Future<void> addTask( | ||
final LocalDatabase localDatabase, { | ||
final List<String> excludeBarcodes = const <String>[], | ||
}) async { | ||
final String uniqueId = await _operationType.getNewKey( | ||
localDatabase, | ||
); | ||
final BackgroundTask task = _getNewTask( | ||
uniqueId, | ||
excludeBarcodes, | ||
); | ||
await task.addToManager(localDatabase); | ||
} | ||
|
||
@override | ||
(String, AlignmentGeometry)? getFloatingMessage( | ||
final AppLocalizations appLocalizations) => | ||
null; | ||
|
||
static BackgroundTask _getNewTask( | ||
final String uniqueId, | ||
final List<String> excludeBarcodes, | ||
) => | ||
BackgroundTaskLanguageRefresh._( | ||
processName: _operationType.processName, | ||
uniqueId: uniqueId, | ||
stamp: ';languageRefresh', | ||
excludeBarcodes: excludeBarcodes, | ||
); | ||
|
||
@override | ||
Future<void> preExecute(final LocalDatabase localDatabase) async {} | ||
|
||
@override | ||
bool get hasImmediateNextTask => true; | ||
|
||
/// Number of products to download each time. | ||
static const int _pageSize = 20; | ||
|
||
@override | ||
Future<void> execute(final LocalDatabase localDatabase) async { | ||
final DaoProduct daoProduct = DaoProduct(localDatabase); | ||
final OpenFoodFactsLanguage language = ProductQuery.getLanguage(); | ||
final List<String> barcodes = await daoProduct.getTopProductsToTranslate( | ||
language, | ||
limit: _pageSize, | ||
excludeBarcodes: excludeBarcodes, | ||
); | ||
if (barcodes.isEmpty) { | ||
return; | ||
} | ||
final SearchResult searchResult = await OpenFoodAPIClient.searchProducts( | ||
getUser(), | ||
ProductSearchQueryConfiguration( | ||
fields: ProductQuery.fields, | ||
parametersList: <Parameter>[ | ||
const PageSize(size: _pageSize), | ||
const PageNumber(page: 1), | ||
BarcodeParameter.list(barcodes), | ||
], | ||
language: language, | ||
country: ProductQuery.getCountry(), | ||
version: ProductQuery.productQueryVersion, | ||
), | ||
uriHelper: uriProductHelper, | ||
); | ||
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); | ||
localDatabase.upToDate.setLatestDownloadedProducts(searchResult.products!); | ||
|
||
// Next page | ||
final List<String> newExcludeBarcodes = <String>[]; | ||
// 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); | ||
Comment on lines
+127
to
+129
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Say we try to refresh a product that was removed in the time before the refresh, then we'll have a lopped until the Cannot refresh language exception Let's remove the ones we queried, not the ones we received There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
First we add all the barcodes we expected, and then remove all the barcodes we received. In the end we exclude the barcodes we expected but haven't received. // ...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); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @M123-dev Just to clarify a bit:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh okay, yeah makes more sense reading it now |
||
} | ||
await addTask( | ||
localDatabase, | ||
excludeBarcodes: newExcludeBarcodes, | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not always the case. I'm again doing a mobile review so I can't double-check the implications but I guess it's not causing any problems, is it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@M123-dev When we page our background tasks, task A will deal with only say 10 products and at the end will say "I know there are more products to deal with and I create task B". In that case we need the background manager to immediately restart, to deal with task B.
In case we have no more products to deal with, no additional task will be created and the manager will see an empty task queue, and do nothing.
As opposed to standard one-shot tasks (e.g. product detail change), that are not somehow chained to other tasks to be run immediately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many thanks @M123-dev, that's not the easiest PR to review on mobile ;)