Skip to content
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: 3897 - no language selector for gallery, only for swipeable page #3924

Merged
merged 3 commits into from
Apr 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions packages/smooth_app/lib/background/background_task_crop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ class BackgroundTaskCrop extends AbstractBackgroundTask {
/// Adds the background task about uploading a product image.
static Future<void> addTask(
final String barcode, {
required final OpenFoodFactsLanguage language,
required final int imageId,
required final ImageField imageField,
required final File croppedFile,
Expand All @@ -117,6 +118,7 @@ class BackgroundTaskCrop extends AbstractBackgroundTask {
barcode,
);
final AbstractBackgroundTask task = _getNewTask(
language,
barcode,
imageId,
imageField,
Expand All @@ -137,6 +139,7 @@ class BackgroundTaskCrop extends AbstractBackgroundTask {

/// Returns a new background task about cropping an existing image.
static BackgroundTaskCrop _getNewTask(
final OpenFoodFactsLanguage language,
final String barcode,
final int imageId,
final ImageField imageField,
Expand All @@ -160,13 +163,13 @@ class BackgroundTaskCrop extends AbstractBackgroundTask {
cropY1: cropY1,
cropX2: cropX2,
cropY2: cropY2,
languageCode: ProductQuery.getLanguage().code,
languageCode: language.code,
user: jsonEncode(ProductQuery.getUser().toJson()),
country: ProductQuery.getCountry()!.offTag,
stamp: BackgroundTaskImage.getStamp(
barcode,
imageField.offTag,
ProductQuery.getLanguage().code,
language.code,
),
);

Expand Down
7 changes: 5 additions & 2 deletions packages/smooth_app/lib/background/background_task_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ class BackgroundTaskImage extends AbstractBackgroundTask {
/// Adds the background task about uploading a product image.
static Future<void> addTask(
final String barcode, {
required final OpenFoodFactsLanguage language,
required final ImageField imageField,
required final File fullFile,
required final File croppedFile,
Expand All @@ -133,6 +134,7 @@ class BackgroundTaskImage extends AbstractBackgroundTask {
barcode,
);
final AbstractBackgroundTask task = _getNewTask(
language,
barcode,
imageField,
fullFile,
Expand All @@ -153,6 +155,7 @@ class BackgroundTaskImage extends AbstractBackgroundTask {

/// Returns a new background task about changing a product.
static BackgroundTaskImage _getNewTask(
final OpenFoodFactsLanguage language,
final String barcode,
final ImageField imageField,
final File fullFile,
Expand All @@ -176,13 +179,13 @@ class BackgroundTaskImage extends AbstractBackgroundTask {
cropY1: cropY1,
cropX2: cropX2,
cropY2: cropY2,
languageCode: ProductQuery.getLanguage().code,
languageCode: language.code,
user: jsonEncode(ProductQuery.getUser().toJson()),
country: ProductQuery.getCountry()!.offTag,
stamp: getStamp(
barcode,
imageField.offTag,
ProductQuery.getLanguage().code,
language.code,
),
);

Expand Down
26 changes: 8 additions & 18 deletions packages/smooth_app/lib/background/background_task_unselect.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:smooth_app/background/background_task_image.dart';
import 'package:smooth_app/background/background_task_refresh_later.dart';
import 'package:smooth_app/data_models/operation_type.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/helpers/image_field_extension.dart';
import 'package:smooth_app/query/product_query.dart';

/// Background task about unselecting a product image.
Expand Down Expand Up @@ -74,6 +75,7 @@ class BackgroundTaskUnselect extends AbstractBackgroundTask {
final String barcode, {
required final ImageField imageField,
required final State<StatefulWidget> widget,
required final OpenFoodFactsLanguage language,
}) async {
final LocalDatabase localDatabase = widget.context.read<LocalDatabase>();
final String uniqueId = await _operationType.getNewKey(
Expand All @@ -84,6 +86,7 @@ class BackgroundTaskUnselect extends AbstractBackgroundTask {
barcode,
imageField,
uniqueId,
language,
);
await task.addToManager(localDatabase, widget: widget);
}
Expand All @@ -97,20 +100,21 @@ class BackgroundTaskUnselect extends AbstractBackgroundTask {
final String barcode,
final ImageField imageField,
final String uniqueId,
final OpenFoodFactsLanguage language,
) =>
BackgroundTaskUnselect._(
uniqueId: uniqueId,
barcode: barcode,
processName: _PROCESS_NAME,
imageField: imageField.offTag,
languageCode: ProductQuery.getLanguage().code,
languageCode: language.code,
user: jsonEncode(ProductQuery.getUser().toJson()),
country: ProductQuery.getCountry()!.offTag,
// same stamp as image upload
stamp: BackgroundTaskImage.getStamp(
barcode,
imageField.offTag,
ProductQuery.getLanguage().code,
language.code,
),
);

Expand All @@ -123,6 +127,7 @@ class BackgroundTaskUnselect extends AbstractBackgroundTask {
final LocalDatabase localDatabase,
final bool success,
) async {
// TODO(monsieurtanuki): we should also remove the hypothetical transient file, shouldn't we?
localDatabase.upToDate.terminate(uniqueId);
localDatabase.notifyListeners();
if (success) {
Expand Down Expand Up @@ -150,22 +155,7 @@ class BackgroundTaskUnselect extends AbstractBackgroundTask {
/// Here we put an empty string instead, to be understood as "force to null!".
Product _getUnselectedProduct() {
final Product result = Product(barcode: barcode);
switch (ImageField.fromOffTag(imageField)!) {
case ImageField.FRONT:
result.imageFrontUrl = '';
break;
case ImageField.INGREDIENTS:
result.imageIngredientsUrl = '';
break;
case ImageField.NUTRITION:
result.imageNutritionUrl = '';
break;
case ImageField.PACKAGING:
result.imagePackagingUrl = '';
break;
case ImageField.OTHER:
// We do nothing. Actually we're not supposed to unselect other images.
}
ImageField.fromOffTag(imageField)!.setUrl(result, '');
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class _ImageUploadCardState extends State<ImageUploadCard> {
this,
barcode: widget.product.barcode!,
imageField: widget.productImageData.imageField,
language: ProductQuery.getLanguage(),
),
icon: const Icon(Icons.add_a_photo),
label: Text(
Expand Down
35 changes: 33 additions & 2 deletions packages/smooth_app/lib/database/transient_file.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,20 @@ class TransientFile {
return File(path);
}

/// Returns the key of the transient image for [imageField] and [barcode].
/// Returns the key of the transient image.
static String _getImageKey(
final ImageField imageField,
final String barcode,
final OpenFoodFactsLanguage language,
) =>
'$barcode;$imageField;${language.code}';
'${_getImageKeyPrefix(imageField, barcode)}${language.code}';

/// Returns the key prefix of the transient image (without language).
static String _getImageKeyPrefix(
final ImageField imageField,
final String barcode,
) =>
'$barcode;$imageField;';

/// Returns a way to display the image, either locally or from the server.
static ImageProvider? getImageProvider(
Expand Down Expand Up @@ -109,4 +116,28 @@ class TransientFile {
) =>
getImage(imageData.imageField, barcode, language) == null &&
imageData.imageUrl != null;

/// Returns the languages that have currently transient images.
static Iterable<OpenFoodFactsLanguage> getImageLanguages(
final ImageField imageField,
final String barcode,
) {
final Set<OpenFoodFactsLanguage> result = <OpenFoodFactsLanguage>{};
final String prefix = _getImageKeyPrefix(imageField, barcode);
final int prefixLength = prefix.length;
for (final String key in _transientFiles.keys) {
if (key.length <= prefixLength) {
continue;
}
if (!key.startsWith(prefix)) {
continue;
}
final String lc = key.substring(prefixLength);
final OpenFoodFactsLanguage language = LanguageHelper.fromJson(lc);
if (language != OpenFoodFactsLanguage.UNDEFINED) {
result.add(language);
}
}
return result;
}
}
44 changes: 43 additions & 1 deletion packages/smooth_app/lib/helpers/image_field_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@ extension ImageFieldSmoothieExtension on ImageField {
ImageField.PACKAGING,
];

String? getImageFieldUrl(final Product product) {
static const List<ImageField> orderedAll = <ImageField>[
ImageField.FRONT,
ImageField.INGREDIENTS,
ImageField.NUTRITION,
ImageField.PACKAGING,
ImageField.OTHER,
];

String? getUrl(final Product product) {
switch (this) {
case ImageField.FRONT:
return product.imageFrontUrl;
Expand All @@ -24,6 +32,25 @@ extension ImageFieldSmoothieExtension on ImageField {
}
}

void setUrl(final Product product, final String url) {
switch (this) {
case ImageField.FRONT:
product.imageFrontUrl = url;
break;
case ImageField.INGREDIENTS:
product.imageIngredientsUrl = url;
break;
case ImageField.NUTRITION:
product.imageNutritionUrl = url;
break;
case ImageField.PACKAGING:
product.imagePackagingUrl = url;
break;
case ImageField.OTHER:
// We do nothing.
}
}

String getProductImageButtonText(final AppLocalizations appLocalizations) {
switch (this) {
case ImageField.FRONT:
Expand Down Expand Up @@ -70,4 +97,19 @@ extension ImageFieldSmoothieExtension on ImageField {
return appLocalizations.more_photos;
}
}

String getAddPhotoButtonText(final AppLocalizations appLocalizations) {
switch (this) {
case ImageField.FRONT:
return appLocalizations.front_packaging_photo_button_label;
case ImageField.INGREDIENTS:
return appLocalizations.ingredients_photo_button_label;
case ImageField.NUTRITION:
return appLocalizations.nutritional_facts_photo_button_label;
case ImageField.PACKAGING:
return appLocalizations.recycling_photo_button_label;
case ImageField.OTHER:
return appLocalizations.other_interesting_photo_button_label;
}
}
}
49 changes: 18 additions & 31 deletions packages/smooth_app/lib/helpers/product_cards_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,15 @@ List<ProductImageData> getProductMainImagesData(
return result;
}

/// Returns data about the "best" image: for the language, or the default.
///
/// With [forceLanguage] you say you don't want the default as a fallback.
ProductImageData getProductImageData(
final Product product,
final ImageField imageField,
final OpenFoodFactsLanguage language,
) {
final OpenFoodFactsLanguage language, {
final bool forceLanguage = false,
}) {
final ProductImage? productImage = getLocalizedProductImage(
product,
imageField,
Expand All @@ -206,11 +210,12 @@ ProductImageData getProductImageData(
final String? imageUrl;
final OpenFoodFactsLanguage? imageLanguage;
if (productImage != null) {
// we found a localized version for this image
imageLanguage = language;
imageUrl = getLocalizedProductImageUrl(product, productImage);
} else {
imageLanguage = null;
imageUrl = imageField.getImageFieldUrl(product);
imageUrl = forceLanguage ? null : imageField.getUrl(product);
}

return ProductImageData(
Expand Down Expand Up @@ -257,29 +262,6 @@ List<MapEntry<ProductImageData, ImageProvider?>> getSelectedImages(
return result.entries.toList();
}

OpenFoodFactsLanguage? getProductImageUrlLanguage(
final Product product,
final ImageField imageField,
final String url,
) {
if (product.images == null) {
return null;
}
for (final ProductImage productImage in product.images!) {
if (productImage.field != imageField ||
productImage.language == null ||
productImage.rev == null) {
continue;
}
final String localizedUrl =
getLocalizedProductImageUrl(product, productImage);
if (url == localizedUrl) {
return productImage.language;
}
}
return null;
}

// TODO(monsieurtanuki): move to off-dart in ImageHelper
String _getBarcodeSubPath(final String barcode) {
if (barcode.length < 9) {
Expand All @@ -295,26 +277,31 @@ String _getBarcodeSubPath(final String barcode) {
return '$p1/$p2/$p3/$p4';
}

String _getImageRoot() =>
OpenFoodAPIConfiguration.globalQueryType == QueryType.PROD
? 'https://images.openfoodfacts.org/images/products'
: 'https://images.openfoodfacts.net/images/products';

String getLocalizedProductImageUrl(
final Product product,
final ProductImage productImage,
) =>
// TODO(monsieurtanuki): make it work in TEST env too (= .net)
'https://images.openfoodfacts.org/images/products/'
'${_getImageRoot()}/'
'${_getBarcodeSubPath(product.barcode!)}/'
'${ImageHelper.getProductImageFilename(productImage, imageSize: ImageSize.DISPLAY)}';

/// Returns the languages for which image fields have images.
/// Returns the languages for which [imageField] has images for that [product].
Iterable<OpenFoodFactsLanguage> getProductImageLanguages(
final Product product,
final Iterable<ImageField> imageFields,
final ImageField imageField,
) {
final Set<OpenFoodFactsLanguage> result = <OpenFoodFactsLanguage>{};
result.addAll(TransientFile.getImageLanguages(imageField, product.barcode!));
if (product.images == null) {
return result;
}
for (final ProductImage productImage in product.images!) {
if (imageFields.contains(productImage.field) &&
if (imageField == productImage.field &&
productImage.rev != null &&
productImage.language != null) {
result.add(productImage.language!);
Expand Down
8 changes: 8 additions & 0 deletions packages/smooth_app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1583,6 +1583,14 @@
"@edit_photo_select_existing_downloaded_none": {
"description": "Error message"
},
"edit_photo_language_not_this_one": "No image in that language yet",
"@edit_photo_language_not_this_one": {
"description": "Warning message: for this product and this field, there are 'translated' images, but not in that language"
},
"edit_photo_language_none": "No image yet",
"@edit_photo_language_none": {
"description": "Warning message: for this product and this field, there are no images at all, in any language"
},
"category_picker_screen_title": "Categories",
"@category_picker_screen_title": {
"description": "Categories picker screen title"
Expand Down
Loading