fix: 5576 - first step towards multi product types (#5593)

Impacted files:
* `add_basic_details_page.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `background_task.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `background_task_download_products.dart`: saved new field `Product.productType`
* `dao_product.dart`: now we're storing the `Product.productType` field
* `edit_new_packagings.dart`: now using new field `Product.productType`
* `edit_new_packagings_component.dart`: now using new field `Product.productType`
* `edit_product_page.dart`: specific icon for "food" categories; no nutrition for "beauty" and "product"
* `forgot_password_page.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `lazy_counter.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `login_result.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `newsfeed_provider.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `ocr_ingredients_helper.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `ocr_packaging_helper.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `onboarding_data_product.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `ordered_nutrients_cache.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `paged_product_query.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `product_cards_helper.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `product_image_crop_button.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `product_image_gallery_other_view.dart`: now using new field `Product.productType`
* `product_image_other_page.dart`: now using new field `Product.productType`
* `product_image_server_button.dart`: now using new field `Product.productType`
* `product_image_widget.dart`: now using new field `Product.productType`
* `product_list_page.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `product_query.dart`: now when we look for a barcode (or refresh a product), we use all productTypes if needed
* `product_refresher.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `pubspec.lock`: wtf
* `pubspec.yaml`: upgraded `openfoodfacts` to `3.15.0` for new `Product.productType` field
* `sign_up_page.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `simple_input_page_helpers.dart`: new class dedicated to non-food categories
* `simple_input_text_field.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `simple_input_widget.dart`: now using new field `Product.productType`
* `temp_product_list_share_helper.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
* `uploaded_image_gallery.dart`: now using new field `Product.productType`
* `user_preferences_dev_debug_info.dart`: replaced getter `ProductQuery.uriProductHelper` with method `ProductQuery.getUriProductHelper`
This commit is contained in:
monsieurtanuki
2024-09-25 10:28:57 +02:00
committed by GitHub
parent 42d0201876
commit 93fc899301
33 changed files with 278 additions and 98 deletions

View File

@ -183,7 +183,7 @@ abstract class BackgroundTask {
// TODO(monsieurtanuki): store the uriProductHelper as well // TODO(monsieurtanuki): store the uriProductHelper as well
@protected @protected
UriProductHelper get uriProductHelper => ProductQuery.uriProductHelper; UriProductHelper get uriProductHelper => ProductQuery.getUriProductHelper();
/// Returns true if tasks with the same stamp would overwrite each-other. /// Returns true if tasks with the same stamp would overwrite each-other.
bool isDeduplicable() => true; bool isDeduplicable() => true;

View File

@ -133,9 +133,15 @@ class BackgroundTaskDownloadProducts extends BackgroundTaskProgressing {
throw Exception('Something bad happened downloading products'); throw Exception('Something bad happened downloading products');
} }
final DaoProduct daoProduct = DaoProduct(localDatabase); final DaoProduct daoProduct = DaoProduct(localDatabase);
final ProductType? productType =
ProductQuery.extractProductType(uriProductHelper);
for (final Product product in downloadedProducts) { for (final Product product in downloadedProducts) {
if (await _shouldBeUpdated(daoProduct, product.barcode!)) { if (await _shouldBeUpdated(daoProduct, product.barcode!)) {
await daoProduct.put(product, language); await daoProduct.put(
product,
language,
productType: productType,
);
} }
} }
final int deleted = await daoWorkBarcode.deleteBarcodes(work, barcodes); final int deleted = await daoWorkBarcode.deleteBarcodes(work, barcodes);

View File

@ -38,7 +38,7 @@ class LoginResult {
try { try {
final LoginStatus? loginStatus = await OpenFoodAPIClient.login2( final LoginStatus? loginStatus = await OpenFoodAPIClient.login2(
user, user,
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(),
); );
if (loginStatus == null) { if (loginStatus == null) {
return const LoginResult(LoginResultType.serverIssue); return const LoginResult(LoginResultType.serverIssue);

View File

@ -106,7 +106,8 @@ class AppNewsProvider extends ChangeNotifier {
/// or [https://world.openfoodfacts.[org/net]/resources/files/tagline-off-android-v3.json] /// or [https://world.openfoodfacts.[org/net]/resources/files/tagline-off-android-v3.json]
Future<String?> _fetchJSON() async { Future<String?> _fetchJSON() async {
try { try {
final UriProductHelper uriProductHelper = ProductQuery.uriProductHelper; final UriProductHelper uriProductHelper =
ProductQuery.getUriProductHelper();
final Map<String, String> headers = <String, String>{}; final Map<String, String> headers = <String, String>{};
final Uri uri; final Uri uri;

View File

@ -40,7 +40,9 @@ class OnboardingDataProduct extends AbstractOnboardingData<Product> {
AbstractOnboardingData.barcode, AbstractOnboardingData.barcode,
ProductQuery.getLanguage(), ProductQuery.getLanguage(),
), ),
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(
productType: ProductType.food,
),
).timeout(SnackBarDuration.long); ).timeout(SnackBarDuration.long);
@override @override

View File

@ -94,25 +94,34 @@ class DaoProduct extends AbstractSqlDao implements BulkDeletable {
Future<void> put( Future<void> put(
final Product product, final Product product,
final OpenFoodFactsLanguage language, final OpenFoodFactsLanguage language, {
) async => final ProductType? productType,
}) async =>
putAll( putAll(
<Product>[product], <Product>[product],
language, language,
productType: productType,
); );
/// Replaces products in database /// Replaces products in database
Future<void> putAll( Future<void> putAll(
final Iterable<Product> products, final Iterable<Product> products,
final OpenFoodFactsLanguage language, final OpenFoodFactsLanguage language, {
) async => final ProductType? productType,
localDatabase.database.transaction( }) async {
if (productType != null) {
for (final Product product in products) {
product.productType = productType;
}
}
await localDatabase.database.transaction(
(final Transaction transaction) async => _bulkReplaceLoop( (final Transaction transaction) async => _bulkReplaceLoop(
transaction, transaction,
products, products,
language, language,
), ),
); );
}
Future<List<String>> getAllKeys() async { Future<List<String>> getAllKeys() async {
final List<String> result = <String>[]; final List<String> result = <String>[];

View File

@ -271,7 +271,9 @@ ProductImageData getProductImageData(
imageUrl: productImage.getUrl( imageUrl: productImage.getUrl(
product.barcode!, product.barcode!,
imageSize: ImageSize.DISPLAY, imageSize: ImageSize.DISPLAY,
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(
productType: product.productType,
),
), ),
language: language, language: language,
); );

View File

@ -6,7 +6,7 @@ Uri shareProductList(List<String> barcodes) {
final String barcodesString = barcodes.join(','); final String barcodesString = barcodes.join(',');
return UriHelper.replaceSubdomain( return UriHelper.replaceSubdomain(
ProductQuery.uriProductHelper.getUri( ProductQuery.getUriProductHelper().getUri(
path: 'products/$barcodesString', path: 'products/$barcodesString',
addUserAgentParameters: false, addUserAgentParameters: false,
), ),

View File

@ -148,6 +148,7 @@ class _RawGridGallery extends StatelessWidget {
squareSize: squareSize, squareSize: squareSize,
imageSize: imageSize, imageSize: imageSize,
heroTag: heroTag, heroTag: heroTag,
productType: product.productType,
), ),
), ),
); );

View File

@ -66,6 +66,7 @@ class _ProductImageOtherPageState extends State<ProductImageOtherPage> {
barcode: widget.product.barcode!, barcode: widget.product.barcode!,
heroTag: heroTag:
widget.currentImage == image ? widget.heroTag : null, widget.currentImage == image ? widget.heroTag : null,
productType: widget.product.productType,
); );
}, },
).toList(growable: false), ).toList(growable: false),
@ -89,11 +90,13 @@ class _ProductImageViewer extends StatelessWidget {
required this.image, required this.image,
required this.barcode, required this.barcode,
this.heroTag, this.heroTag,
required this.productType,
}); });
final ProductImage image; final ProductImage image;
final String barcode; final String barcode;
final String? heroTag; final String? heroTag;
final ProductType? productType;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -111,7 +114,9 @@ class _ProductImageViewer extends StatelessWidget {
image: NetworkImage( image: NetworkImage(
image.getUrl( image.getUrl(
barcode, barcode,
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(
productType: productType,
),
), ),
), ),
fit: BoxFit.cover, fit: BoxFit.cover,
@ -153,6 +158,7 @@ class _ProductImageViewer extends StatelessWidget {
_ProductImageDetailsButton( _ProductImageDetailsButton(
image: image, image: image,
barcode: barcode, barcode: barcode,
productType: productType,
), ),
const Spacer(), const Spacer(),
if (image.expired) _ProductImageOutdatedLabel(colors: colors), if (image.expired) _ProductImageOutdatedLabel(colors: colors),
@ -211,10 +217,12 @@ class _ProductImageDetailsButton extends StatelessWidget {
const _ProductImageDetailsButton({ const _ProductImageDetailsButton({
required this.image, required this.image,
required this.barcode, required this.barcode,
required this.productType,
}); });
final ProductImage image; final ProductImage image;
final String barcode; final String barcode;
final ProductType? productType;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -222,7 +230,9 @@ class _ProductImageDetailsButton extends StatelessWidget {
final String url = image.url ?? final String url = image.url ??
image.getUrl( image.getUrl(
barcode, barcode,
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(
productType: productType,
),
); );
return DecoratedBox( return DecoratedBox(

View File

@ -17,6 +17,7 @@ class ProductImageWidget extends StatelessWidget {
required this.productImage, required this.productImage,
required this.barcode, required this.barcode,
required this.squareSize, required this.squareSize,
required this.productType,
this.imageSize, this.imageSize,
this.heroTag, this.heroTag,
}); });
@ -25,6 +26,7 @@ class ProductImageWidget extends StatelessWidget {
final String barcode; final String barcode;
final double squareSize; final double squareSize;
final String? heroTag; final String? heroTag;
final ProductType? productType;
/// Allows to fetch the optimized version of the image /// Allows to fetch the optimized version of the image
final ImageSize? imageSize; final ImageSize? imageSize;
@ -45,7 +47,9 @@ class ProductImageWidget extends StatelessWidget {
imageProvider: NetworkImage( imageProvider: NetworkImage(
productImage.getUrl( productImage.getUrl(
barcode, barcode,
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(
productType: productType,
),
imageSize: imageSize, imageSize: imageSize,
), ),
), ),

View File

@ -25,12 +25,14 @@ class UploadedImageGallery extends StatelessWidget {
required this.imageField, required this.imageField,
required this.language, required this.language,
required this.isLoggedInMandatory, required this.isLoggedInMandatory,
required this.productType,
}); });
final String barcode; final String barcode;
final List<ProductImage> rawImages; final List<ProductImage> rawImages;
final ImageField imageField; final ImageField imageField;
final bool isLoggedInMandatory; final bool isLoggedInMandatory;
final ProductType? productType;
/// Language for which we'll save the cropped image. /// Language for which we'll save the cropped image.
final OpenFoodFactsLanguage language; final OpenFoodFactsLanguage language;
@ -70,7 +72,9 @@ class UploadedImageGallery extends StatelessWidget {
rawImage.getUrl( rawImage.getUrl(
barcode, barcode,
imageSize: ImageSize.ORIGINAL, imageSize: ImageSize.ORIGINAL,
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(
productType: productType,
),
), ),
DaoInt(localDatabase), DaoInt(localDatabase),
); );
@ -102,6 +106,7 @@ class UploadedImageGallery extends StatelessWidget {
productImage: rawImage, productImage: rawImage,
barcode: barcode, barcode: barcode,
squareSize: columnWidth, squareSize: columnWidth,
productType: productType,
), ),
); );
}, },

View File

@ -79,7 +79,7 @@ class LazyCounterUserSearch extends LazyCounter {
final SearchResult result = await OpenFoodAPIClient.searchProducts( final SearchResult result = await OpenFoodAPIClient.searchProducts(
user, user,
configuration, configuration,
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(),
); );
return result.count; return result.count;
} catch (e) { } catch (e) {

View File

@ -27,10 +27,10 @@ class _UserPreferencesDebugInfoState extends State<UserPreferencesDebugInfo> {
'IsLoggedIn': ProductQuery.isLoggedIn().toString(), 'IsLoggedIn': ProductQuery.isLoggedIn().toString(),
'UUID': OpenFoodAPIConfiguration.uuid.toString(), 'UUID': OpenFoodAPIConfiguration.uuid.toString(),
'Matomo Visitor ID': AnalyticsHelper.matomoVisitorId, 'Matomo Visitor ID': AnalyticsHelper.matomoVisitorId,
'QueryType': ProductQuery.uriProductHelper.isTestMode 'QueryType': ProductQuery.getUriProductHelper().isTestMode
? 'QueryType.TEST' ? 'QueryType.TEST'
: 'QueryType.PROD', : 'QueryType.PROD',
'Domain': ProductQuery.uriProductHelper.domain, 'Domain': ProductQuery.getUriProductHelper().domain,
'UserAgent-name': '${OpenFoodAPIConfiguration.userAgent?.name}', 'UserAgent-name': '${OpenFoodAPIConfiguration.userAgent?.name}',
'UserAgent-system': '${OpenFoodAPIConfiguration.userAgent?.system}', 'UserAgent-system': '${OpenFoodAPIConfiguration.userAgent?.system}',
}; };

View File

@ -207,7 +207,9 @@ class _AddBasicDetailsPageState extends State<AddBasicDetailsPage> {
user: ProductQuery.getReadUser(), user: ProductQuery.getReadUser(),
limit: 25, limit: 25,
fuzziness: Fuzziness.none, fuzziness: Fuzziness.none,
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(
productType: widget.product.productType,
),
), ),
), ),
), ),

View File

@ -450,7 +450,7 @@ class _ProductListPageState extends State<ProductListPage>
barcodes, barcodes,
language, language,
), ),
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(),
); );
final List<Product>? freshProducts = searchResult.products; final List<Product>? freshProducts = searchResult.products;
if (freshProducts == null) { if (freshProducts == null) {

View File

@ -153,6 +153,36 @@ class ProductRefresher {
return true; return true;
} }
/// Returns the product type stored locally for that product.
static Future<ProductType?> getCurrentProductType({
required final LocalDatabase localDatabase,
required final String barcode,
}) async {
final Product? localProduct = await DaoProduct(localDatabase).get(barcode);
return localProduct?.productType;
}
/// Returns the list of types to use for that barcode.
Future<List<ProductType>> getOrderedProductTypes({
required final LocalDatabase localDatabase,
required final String barcode,
}) async {
final List<ProductType> result = <ProductType>[];
final ProductType? productType = await getCurrentProductType(
localDatabase: localDatabase,
barcode: barcode,
);
if (productType != null) {
result.add(productType);
}
for (final ProductType value in ProductType.values) {
if (!result.contains(value)) {
result.add(value);
}
}
return result;
}
/// Fetches the product from the server and refreshes the local database. /// Fetches the product from the server and refreshes the local database.
/// ///
/// Silent version. /// Silent version.
@ -160,21 +190,35 @@ class ProductRefresher {
required final LocalDatabase localDatabase, required final LocalDatabase localDatabase,
required final String barcode, required final String barcode,
}) async { }) async {
try { final List<ProductType> productTypes = await getOrderedProductTypes(
localDatabase: localDatabase,
barcode: barcode,
);
late UriProductHelper uriProductHelper;
final OpenFoodFactsLanguage language = ProductQuery.getLanguage(); final OpenFoodFactsLanguage language = ProductQuery.getLanguage();
try {
for (final ProductType productType in productTypes) {
uriProductHelper = ProductQuery.getUriProductHelper(
productType: productType,
);
final ProductResultV3 result = await OpenFoodAPIClient.getProductV3( final ProductResultV3 result = await OpenFoodAPIClient.getProductV3(
getBarcodeQueryConfiguration( getBarcodeQueryConfiguration(
barcode, barcode,
language, language,
), ),
uriHelper: ProductQuery.uriProductHelper, uriHelper: uriProductHelper,
user: ProductQuery.getReadUser(), user: ProductQuery.getReadUser(),
); );
if (result.product != null) { if (result.product != null) {
await DaoProduct(localDatabase).put(result.product!, language); await DaoProduct(localDatabase).put(
result.product!,
language,
productType: productType,
);
localDatabase.upToDate.setLatestDownloadedProduct(result.product!); localDatabase.upToDate.setLatestDownloadedProduct(result.product!);
return FetchedProduct.found(result.product!); return FetchedProduct.found(result.product!);
} }
}
return const FetchedProduct.internetNotFound(); return const FetchedProduct.internetNotFound();
} catch (e) { } catch (e) {
Logs.e('Refresh from server error', ex: e); Logs.e('Refresh from server error', ex: e);
@ -186,7 +230,7 @@ class ProductRefresher {
isConnected: false, isConnected: false,
); );
} }
final String host = ProductQuery.uriProductHelper.host; final String host = uriProductHelper.host;
final PingData result = await Ping(host, count: 1).stream.first; final PingData result = await Ping(host, count: 1).stream.first;
return FetchedProduct.error( return FetchedProduct.error(
exceptionString: e.toString(), exceptionString: e.toString(),
@ -208,7 +252,7 @@ class ProductRefresher {
final SearchResult searchResult = await OpenFoodAPIClient.searchProducts( final SearchResult searchResult = await OpenFoodAPIClient.searchProducts(
ProductQuery.getReadUser(), ProductQuery.getReadUser(),
getBarcodeListQueryConfiguration(barcodes, language), getBarcodeListQueryConfiguration(barcodes, language),
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(),
); );
if (searchResult.products == null) { if (searchResult.products == null) {
return null; return null;

View File

@ -117,6 +117,7 @@ class _EditNewPackagingsState extends State<EditNewPackagings>
setState(() => _removePackagingAt(deleteIndex)), setState(() => _removePackagingAt(deleteIndex)),
helper: _helpers[index], helper: _helpers[index],
categories: upToDateProduct.categories, categories: upToDateProduct.categories,
productType: upToDateProduct.productType,
), ),
), ),
); );

View File

@ -17,12 +17,14 @@ class EditNewPackagingsComponent extends StatefulWidget {
required this.deleteCallback, required this.deleteCallback,
required this.helper, required this.helper,
required this.categories, required this.categories,
required this.productType,
}); });
final String title; final String title;
final VoidCallback deleteCallback; final VoidCallback deleteCallback;
final EditNewPackagingsHelper helper; final EditNewPackagingsHelper helper;
final String? categories; final String? categories;
final ProductType? productType;
@override @override
State<EditNewPackagingsComponent> createState() => State<EditNewPackagingsComponent> createState() =>
@ -58,6 +60,7 @@ class _EditNewPackagingsComponentState
iconColor: iconColor, iconColor: iconColor,
minLengthForSuggestions: 0, minLengthForSuggestions: 0,
categories: widget.categories, categories: widget.categories,
productType: widget.productType,
), ),
_EditTextLine( _EditTextLine(
title: appLocalizations.edit_packagings_element_field_material, title: appLocalizations.edit_packagings_element_field_material,
@ -69,6 +72,7 @@ class _EditNewPackagingsComponentState
minLengthForSuggestions: 0, minLengthForSuggestions: 0,
categories: widget.categories, categories: widget.categories,
shapeProvider: () => widget.helper.controllerShape.text, shapeProvider: () => widget.helper.controllerShape.text,
productType: widget.productType,
), ),
_EditTextLine( _EditTextLine(
title: appLocalizations.edit_packagings_element_field_recycling, title: appLocalizations.edit_packagings_element_field_recycling,
@ -76,12 +80,14 @@ class _EditNewPackagingsComponentState
tagType: TagType.PACKAGING_RECYCLING, tagType: TagType.PACKAGING_RECYCLING,
iconName: 'recycling', iconName: 'recycling',
iconColor: iconColor, iconColor: iconColor,
productType: widget.productType,
), ),
_EditTextLine( _EditTextLine(
title: appLocalizations.edit_packagings_element_field_quantity, title: appLocalizations.edit_packagings_element_field_quantity,
controller: widget.helper.controllerQuantity, controller: widget.helper.controllerQuantity,
iconName: 'quantity', iconName: 'quantity',
iconColor: iconColor, iconColor: iconColor,
productType: widget.productType,
), ),
_EditNumberLine( _EditNumberLine(
title: appLocalizations.edit_packagings_element_field_weight, title: appLocalizations.edit_packagings_element_field_weight,
@ -133,6 +139,7 @@ class _EditTextLine extends StatefulWidget {
this.minLengthForSuggestions = 1, this.minLengthForSuggestions = 1,
this.categories, this.categories,
this.shapeProvider, this.shapeProvider,
required this.productType,
}); });
final String title; final String title;
@ -144,6 +151,7 @@ class _EditTextLine extends StatefulWidget {
final int minLengthForSuggestions; final int minLengthForSuggestions;
final String? categories; final String? categories;
final String? Function()? shapeProvider; final String? Function()? shapeProvider;
final ProductType? productType;
@override @override
State<_EditTextLine> createState() => _EditTextLineState(); State<_EditTextLine> createState() => _EditTextLineState();
@ -195,6 +203,7 @@ class _EditTextLineState extends State<_EditTextLine> {
minLengthForSuggestions: widget.minLengthForSuggestions, minLengthForSuggestions: widget.minLengthForSuggestions,
categories: widget.categories, categories: widget.categories,
shapeProvider: widget.shapeProvider, shapeProvider: widget.shapeProvider,
productType: widget.productType,
), ),
), ),
), ),

View File

@ -90,7 +90,9 @@ class OcrIngredientsHelper extends OcrHelper {
getUser(), getUser(),
product.barcode!, product.barcode!,
language, language,
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(
productType: product.productType,
),
); );
return result.ingredientsTextFromImage; return result.ingredientsTextFromImage;
} }

View File

@ -92,7 +92,9 @@ class OcrPackagingHelper extends OcrHelper {
getUser(), getUser(),
product.barcode!, product.barcode!,
language, language,
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(
productType: product.productType,
),
); );
return result.textFromImage; return result.textFromImage;
} }

View File

@ -181,7 +181,13 @@ class _EditProductPageState extends State<EditProductPage> with UpToDateMixin {
product: upToDateProduct, product: upToDateProduct,
), ),
), ),
_getSimpleListTileItem(SimpleInputPageCategoryHelper()), if (upToDateProduct.productType == null ||
upToDateProduct.productType == ProductType.food)
_getSimpleListTileItem(SimpleInputPageCategoryHelper())
else
_getSimpleListTileItem(SimpleInputPageCategoryNotFoodHelper()),
if (upToDateProduct.productType != ProductType.beauty &&
upToDateProduct.productType != ProductType.product)
_ListTitleItem( _ListTitleItem(
leading: leading:
const SvgIcon('assets/cacheTintable/scale-balance.svg'), const SvgIcon('assets/cacheTintable/scale-balance.svg'),

View File

@ -59,7 +59,7 @@ class OrderedNutrientsCache {
final String string = await OpenFoodAPIClient.getOrderedNutrientsJsonString( final String string = await OpenFoodAPIClient.getOrderedNutrientsJsonString(
country: ProductQuery.getCountry(), country: ProductQuery.getCountry(),
language: ProductQuery.getLanguage(), language: ProductQuery.getLanguage(),
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(),
); );
final OrderedNutrients result = OrderedNutrients.fromJson( final OrderedNutrients result = OrderedNutrients.fromJson(
jsonDecode(string) as Map<String, dynamic>, jsonDecode(string) as Map<String, dynamic>,
@ -75,6 +75,6 @@ class OrderedNutrientsCache {
return 'nutrients.pl' return 'nutrients.pl'
'/${country.offTag}' '/${country.offTag}'
'/${language.code}' '/${language.code}'
'/${ProductQuery.uriProductHelper.domain}'; '/${ProductQuery.getUriProductHelper().domain}';
} }
} }

View File

@ -53,7 +53,12 @@ class ProductImageCropButton extends ProductImageButton {
if (productImage != null) { if (productImage != null) {
final int? imageId = int.tryParse(productImage.imgid!); final int? imageId = int.tryParse(productImage.imgid!);
if (imageId != null) { if (imageId != null) {
await _openCropAgainPage(context, imageId, productImage); await _openCropAgainPage(
context,
imageId,
productImage,
product.productType,
);
return; return;
} }
} }
@ -84,6 +89,7 @@ class ProductImageCropButton extends ProductImageButton {
final BuildContext context, final BuildContext context,
final int imageId, final int imageId,
final ProductImage productImage, final ProductImage productImage,
final ProductType? productType,
) async { ) async {
final NavigatorState navigatorState = Navigator.of(context); final NavigatorState navigatorState = Navigator.of(context);
final LocalDatabase localDatabase = context.read<LocalDatabase>(); final LocalDatabase localDatabase = context.read<LocalDatabase>();
@ -94,7 +100,9 @@ class ProductImageCropButton extends ProductImageButton {
size: ImageSize.ORIGINAL, size: ImageSize.ORIGINAL,
).getUrl( ).getUrl(
barcode, barcode,
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(
productType: productType,
),
), ),
DaoInt(localDatabase), DaoInt(localDatabase),
); );

View File

@ -51,7 +51,11 @@ class ProductImageServerButton extends ProductImageButton {
ImageSize.DISPLAY, ImageSize.DISPLAY,
); );
if (rawImages.isNotEmpty) { if (rawImages.isNotEmpty) {
await _openGallery(context: context, rawImages: rawImages); await _openGallery(
context: context,
rawImages: rawImages,
productType: product.productType,
);
return; return;
} }
@ -95,12 +99,17 @@ class ProductImageServerButton extends ProductImageButton {
); );
return; return;
} }
await _openGallery(context: context, rawImages: rawImages); await _openGallery(
context: context,
rawImages: rawImages,
productType: product.productType,
);
} }
Future<void> _openGallery({ Future<void> _openGallery({
required final BuildContext context, required final BuildContext context,
required final List<ProductImage> rawImages, required final List<ProductImage> rawImages,
required final ProductType? productType,
}) => }) =>
Navigator.push<void>( Navigator.push<void>(
context, context,
@ -111,6 +120,7 @@ class ProductImageServerButton extends ProductImageButton {
imageField: imageField, imageField: imageField,
language: language, language: language,
isLoggedInMandatory: isLoggedInMandatory, isLoggedInMandatory: isLoggedInMandatory,
productType: productType,
), ),
), ),
); );

View File

@ -445,6 +445,12 @@ class SimpleInputPageCategoryHelper extends AbstractSimpleInputPageHelper {
AnalyticsEditEvents getAnalyticsEditEvent() => AnalyticsEditEvents.categories; AnalyticsEditEvents getAnalyticsEditEvent() => AnalyticsEditEvents.categories;
} }
class SimpleInputPageCategoryNotFoodHelper
extends SimpleInputPageCategoryHelper {
@override
Widget getIcon() => const Icon(Icons.edit);
}
/// Implementation for "Countries" of an [AbstractSimpleInputPageHelper]. /// Implementation for "Countries" of an [AbstractSimpleInputPageHelper].
class SimpleInputPageCountryHelper extends AbstractSimpleInputPageHelper { class SimpleInputPageCountryHelper extends AbstractSimpleInputPageHelper {
@override @override

View File

@ -18,6 +18,7 @@ class SimpleInputTextField extends StatefulWidget {
this.categories, this.categories,
this.shapeProvider, this.shapeProvider,
this.padding, this.padding,
required this.productType,
}); });
final FocusNode focusNode; final FocusNode focusNode;
@ -31,6 +32,7 @@ class SimpleInputTextField extends StatefulWidget {
final String? categories; final String? categories;
final String? Function()? shapeProvider; final String? Function()? shapeProvider;
final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry? padding;
final ProductType? productType;
@override @override
State<SimpleInputTextField> createState() => _SimpleInputTextFieldState(); State<SimpleInputTextField> createState() => _SimpleInputTextFieldState();
@ -54,7 +56,9 @@ class _SimpleInputTextFieldState extends State<SimpleInputTextField> {
user: ProductQuery.getReadUser(), user: ProductQuery.getReadUser(),
// number of suggestions the user can scroll through: compromise between quantity and readability of the suggestions // number of suggestions the user can scroll through: compromise between quantity and readability of the suggestions
limit: 15, limit: 15,
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(
productType: widget.productType,
),
), ),
); );
} }

View File

@ -95,6 +95,7 @@ class _SimpleInputWidgetState extends State<SimpleInputWidget> {
padding: const EdgeInsetsDirectional.only( padding: const EdgeInsetsDirectional.only(
start: 9.0, start: 9.0,
), ),
productType: widget.product.productType,
), ),
), ),
Tooltip( Tooltip(

View File

@ -37,7 +37,7 @@ class _ForgotPasswordPageState extends State<ForgotPasswordPage>
_userIdController.text, _userIdController.text,
country: ProductQuery.getCountry(), country: ProductQuery.getCountry(),
language: ProductQuery.getLanguage(), language: ProductQuery.getLanguage(),
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(),
); );
if (status.status == 200) { if (status.status == 200) {
_send = true; _send = true;

View File

@ -336,7 +336,7 @@ class _SignUpPageState extends State<SignUpPage> with TraceableClientMixin {
orgName: _foodProducer ? _brandController.trimmedText : null, orgName: _foodProducer ? _brandController.trimmedText : null,
country: ProductQuery.getCountry(), country: ProductQuery.getCountry(),
language: ProductQuery.getLanguage(), language: ProductQuery.getLanguage(),
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(),
), ),
title: appLocalisations.sign_up_page_action_doing_it, title: appLocalisations.sign_up_page_action_doing_it,
); );

View File

@ -38,7 +38,7 @@ abstract class PagedProductQuery {
OpenFoodAPIClient.searchProducts( OpenFoodAPIClient.searchProducts(
ProductQuery.getReadUser(), ProductQuery.getReadUser(),
getQueryConfiguration(), getQueryConfiguration(),
uriHelper: ProductQuery.uriProductHelper, uriHelper: ProductQuery.getUriProductHelper(),
); );
AbstractQueryConfiguration getQueryConfiguration(); AbstractQueryConfiguration getQueryConfiguration();

View File

@ -164,7 +164,7 @@ abstract class ProductQuery {
comment: 'Test user for project smoothie', comment: 'Test user for project smoothie',
); );
static late UriProductHelper uriProductHelper; static late UriProductHelper _uriProductHelper;
/// Product helper only for prices. /// Product helper only for prices.
static late UriProductHelper uriPricesHelper; static late UriProductHelper uriPricesHelper;
@ -178,7 +178,7 @@ abstract class ProductQuery {
? uriHelperFoodProd ? uriHelperFoodProd
: getTestUriProductHelper(userPreferences); : getTestUriProductHelper(userPreferences);
uriProductHelper = getProductHelper( _uriProductHelper = getProductHelper(
UserPreferencesDevMode.userPreferencesFlagProd, UserPreferencesDevMode.userPreferencesFlagProd,
); );
uriPricesHelper = getProductHelper( uriPricesHelper = getProductHelper(
@ -201,6 +201,42 @@ abstract class ProductQuery {
); );
} }
static ProductType? extractProductType(
final UriProductHelper uriProductHelper,
) {
final String domain = uriProductHelper.domain;
for (final ProductType productType in ProductType.values) {
if (domain.contains(productType.getDomain())) {
return productType;
}
}
return null;
}
// TODO(monsieurtanuki): make the parameter "required"
static UriProductHelper getUriProductHelper({
final ProductType? productType,
}) {
final UriProductHelper currentUriProductHelper = _uriProductHelper;
if (productType == null) {
return currentUriProductHelper;
}
final ProductType? currentProductType =
extractProductType(currentUriProductHelper);
if (currentProductType == null) {
return currentUriProductHelper;
}
if (currentProductType == productType) {
return currentUriProductHelper;
}
return UriProductHelper(
domain: currentUriProductHelper.domain.replaceFirst(
currentProductType.getDomain(),
productType.getDomain(),
),
);
}
static List<ProductField> get fields => const <ProductField>[ static List<ProductField> get fields => const <ProductField>[
ProductField.NAME, ProductField.NAME,
ProductField.NAME_ALL_LANGUAGES, ProductField.NAME_ALL_LANGUAGES,
@ -255,3 +291,12 @@ abstract class ProductQuery {
ProductField.OWNER_FIELDS, ProductField.OWNER_FIELDS,
]; ];
} }
extension ProductTypeExtension on ProductType {
String getDomain() => switch (this) {
ProductType.food => 'openfoodfacts',
ProductType.beauty => 'openbeautyfacts',
ProductType.petFood => 'openpetfoodfacts',
ProductType.product => 'openproductsfacts',
};
}

View File

@ -964,18 +964,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.5" version: "10.0.4"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_flutter_testing name: leak_tracker_flutter_testing
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.5" version: "3.0.3"
leak_tracker_testing: leak_tracker_testing:
dependency: transitive dependency: transitive
description: description:
@ -1028,10 +1028,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.11.1" version: "0.8.0"
matomo_tracker: matomo_tracker:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1044,10 +1044,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.15.0" version: "1.12.0"
mgrs_dart: mgrs_dart:
dependency: transitive dependency: transitive
description: description:
@ -1269,10 +1269,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: platform name: platform
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.5" version: "3.1.4"
plugin_platform_interface: plugin_platform_interface:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -1599,10 +1599,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.2" version: "0.7.0"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -1743,10 +1743,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.2.5" version: "14.2.1"
watcher: watcher:
dependency: transitive dependency: transitive
description: description: