Skip to content

Commit

Permalink
feat: "Use photo as" available directly in the gallery (+ a fix) (#5916)
Browse files Browse the repository at this point in the history
* Fix FAB showing/hiding with the TabBar

* "Use photo as" available in the gallery
  • Loading branch information
g123k authored Nov 24, 2024
1 parent 453bf9e commit b54e461
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 69 deletions.
2 changes: 1 addition & 1 deletion packages/smooth_app/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,6 @@ SPEC CHECKSUMS:
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
webview_flutter_wkwebview: 0982481e3d9c78fd5c6f62a002fcd24fc791f1e4

PODFILE CHECKSUM: 642d7227a22f6cf08664cf1937c611a52f51dfac
PODFILE CHECKSUM: 798cbe17fd0f5e52d4df92f6b2f3257201f5868e

COCOAPODS: 1.16.2
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,14 @@ class _RawGridGallery extends StatelessWidget {
heroTag: heroTag!,
language: language,
),
onLongPress: () async => _usePhotoAs(
context: context,
product: product,
rawImages: rawImages,
productImage: productImage,
heroTag: heroTag!,
language: language,
),
),
),
),
Expand Down Expand Up @@ -198,4 +206,19 @@ class _RawGridGallery extends StatelessWidget {
),
);
}

Future<ProductImagePageResult?> _usePhotoAs({
required final BuildContext context,
required final Product product,
required final List<ProductImage> rawImages,
required final ProductImage productImage,
required final String heroTag,
required final OpenFoodFactsLanguage language,
}) =>
ProductImageOtherPage.usePhotoAs(
context: context,
product: product,
language: language,
productImage: productImage,
);
}
160 changes: 92 additions & 68 deletions packages/smooth_app/lib/pages/image/product_image_other_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,88 @@ class ProductImageOtherPage extends StatefulWidget {

@override
State<ProductImageOtherPage> createState() => _ProductImageOtherPageState();

static Future<ProductImagePageResult?> usePhotoAs({
required final BuildContext context,
required final Product product,
required final OpenFoodFactsLanguage language,
required final ProductImage productImage,
}) async {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
final SmoothColorsThemeExtension extension =
context.extension<SmoothColorsThemeExtension>();

final List<ImageField> imageFields = <ImageField>[
ImageField.FRONT,
ImageField.INGREDIENTS,
ImageField.NUTRITION,
ImageField.PACKAGING,
];

final Widget existingPictureIcon = icons.Picture.checkAlt(
color: extension.success,
semanticLabel: appLocalizations.photo_already_exists,
);
final Widget missingPictureIcon = icons.Picture.error(
color: extension.error,
semanticLabel: appLocalizations.photo_missing,
);

final ImageField? selectedImageField =
await showSmoothListOfChoicesModalSheet<ImageField>(
context: context,
title: appLocalizations.photo_viewer_use_picture_as_title(
Languages().getNameInLanguage(language),
),
padding: const EdgeInsetsDirectional.only(
start: 15.0,
end: 19.0,
),
labels: <String>[
appLocalizations.photo_field_front,
appLocalizations.photo_field_ingredients,
appLocalizations.photo_field_nutrition,
appLocalizations.photo_field_packaging,
],
values: imageFields,
prefixIcons: <Widget>[
const icons.Milk.filled(),
const icons.Ingredients.alt(),
const icons.NutritionFacts(),
const icons.Recycling(),
],
suffixIcons: imageFields.map((final ImageField imageField) {
final bool exists = TransientFile.fromProduct(
product,
imageField,
language,
).isImageAvailable();
return exists ? existingPictureIcon : missingPictureIcon;
}).toList(growable: false),
);

if (context.mounted && selectedImageField != null) {
final CropParameters? cropParameters =
await UploadedImageGallery.useExistingPhotoFor(
context: context,
rawImage: productImage,
barcode: product.barcode!,
imageField: selectedImageField,
isLoggedInMandatory: true,
productType: product.productType,
language: language,
);

if (cropParameters != null) {
return ProductImagePageResult(
cropParameters: cropParameters,
imageField: selectedImageField,
);
}
}

return null;
}
}

class _ProductImageOtherPageState extends State<ProductImageOtherPage> {
Expand Down Expand Up @@ -122,76 +204,15 @@ class _ProductImageOtherPageState extends State<ProductImageOtherPage> {
}

Future<void> _usePhotoAs() async {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
final SmoothColorsThemeExtension extension =
context.extension<SmoothColorsThemeExtension>();

final List<ImageField> imageFields = <ImageField>[
ImageField.FRONT,
ImageField.INGREDIENTS,
ImageField.NUTRITION,
ImageField.PACKAGING,
];

final Widget existingPictureIcon = icons.Picture.checkAlt(
color: extension.success,
semanticLabel: appLocalizations.photo_already_exists,
);
final Widget missingPictureIcon = icons.Picture.error(
color: extension.error,
semanticLabel: appLocalizations.photo_missing,
);

final ImageField? selectedImageField =
await showSmoothListOfChoicesModalSheet<ImageField>(
final ProductImagePageResult? res = await ProductImageOtherPage.usePhotoAs(
context: context,
title: appLocalizations.photo_viewer_use_picture_as_title(
Languages().getNameInLanguage(widget.language),
),
padding: const EdgeInsetsDirectional.only(
start: 15.0,
end: 19.0,
),
labels: <String>[
appLocalizations.photo_field_front,
appLocalizations.photo_field_ingredients,
appLocalizations.photo_field_nutrition,
appLocalizations.photo_field_packaging,
],
values: imageFields,
prefixIcons: <Widget>[
const icons.Milk.filled(),
const icons.Ingredients.alt(),
const icons.NutritionFacts(),
const icons.Recycling(),
],
suffixIcons: imageFields.map((final ImageField imageField) {
final bool exists = TransientFile.fromProduct(
widget.product,
imageField,
widget.language,
).isImageAvailable();
return exists ? existingPictureIcon : missingPictureIcon;
}).toList(growable: false),
product: widget.product,
language: widget.language,
productImage: widget.currentImage,
);

if (mounted && selectedImageField != null) {
final CropParameters? parameters =
await UploadedImageGallery.useExistingPhotoFor(
context: context,
rawImage: widget.currentImage,
barcode: widget.product.barcode!,
imageField: selectedImageField,
isLoggedInMandatory: true,
productType: widget.product.productType,
language: widget.language,
);

if (mounted && parameters != null) {
Navigator.of(context).pop<ProductImagePageResult>(
ProductImagePageResult(parameters, selectedImageField),
);
}
if (mounted && res != null) {
Navigator.of(context).pop<ProductImagePageResult>(res);
}
}
}
Expand Down Expand Up @@ -488,7 +509,10 @@ class _ProductImagePageIndicator extends StatelessWidget {
}

class ProductImagePageResult {
ProductImagePageResult(this.cropParameters, this.imageField);
ProductImagePageResult({
required this.cropParameters,
required this.imageField,
});

final CropParameters cropParameters;
final ImageField imageField;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,10 @@ class _ProductImageGalleryFooterButtonState
}

void _handleScrollNotification(ScrollNotification notification) {
if (notification.metrics.axis == Axis.horizontal) {
return;
}

if (notification is ScrollStartNotification) {
_scrollInitialPosition = notification.metrics.extentBefore;
_scrollDirection = ScrollDirection.idle;
Expand Down

0 comments on commit b54e461

Please sign in to comment.