Skip to content

Commit

Permalink
fix: refresh images from gallery screen (openfoodfacts#3023)
Browse files Browse the repository at this point in the history
* refresh images from gallery screen

* fix extra images overflow creation

* private variables and refactoring

* , added for formatting

* removed un_necessary parametaers in fn's
  • Loading branch information
AshAman999 authored Sep 16, 2022
1 parent c62b3f9 commit 58dbd43
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,7 @@ class _ImageUploadCardState extends State<ImageUploadCard> {
context,
MaterialPageRoute<bool>(
builder: (BuildContext context) => ProductImageGalleryView(
imagesData: widget.allProductImagesData,
barcode: widget.product.barcode,
product: widget.product,
),
),
);
Expand Down
6 changes: 1 addition & 5 deletions packages/smooth_app/lib/pages/product/edit_product_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/data_models/product_image_data.dart';
import 'package:smooth_app/data_models/up_to_date_product_provider.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
Expand Down Expand Up @@ -160,16 +159,13 @@ class _EditProductPageState extends State<EditProductPage> {
if (!await ProductRefresher().checkIfLoggedIn(context)) {
return;
}
final List<ProductImageData> allProductImagesData =
getProductMainImagesData(_product, appLocalizations);
// TODO(monsieurtanuki): careful, waiting for pop'ed value
final bool? refreshed = await Navigator.push<bool>(
context,
MaterialPageRoute<bool>(
builder: (BuildContext context) =>
ProductImageGalleryView(
imagesData: allProductImagesData,
barcode: _product.barcode,
product: _product,
),
fullscreenDialog: true,
),
Expand Down
226 changes: 136 additions & 90 deletions packages/smooth_app/lib/pages/product/product_image_gallery_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/data_models/product_image_data.dart';
import 'package:smooth_app/data_models/up_to_date_product_provider.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/duration_constants.dart';
import 'package:smooth_app/generic_lib/widgets/images/smooth_images_sliver_grid.dart';
import 'package:smooth_app/generic_lib/widgets/images/smooth_images_sliver_list.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart';
import 'package:smooth_app/helpers/picture_capture_helper.dart';
import 'package:smooth_app/helpers/product_cards_helper.dart';
import 'package:smooth_app/pages/image_crop_page.dart';
import 'package:smooth_app/pages/product/common/product_refresher.dart';
import 'package:smooth_app/pages/product/product_image_viewer.dart';
import 'package:smooth_app/query/product_query.dart';
import 'package:smooth_app/widgets/smooth_scaffold.dart';
Expand All @@ -22,96 +28,114 @@ import 'package:smooth_app/widgets/smooth_scaffold.dart';
///
class ProductImageGalleryView extends StatefulWidget {
const ProductImageGalleryView({
required this.imagesData,
this.barcode,
required this.product,
});

final String? barcode;
final List<ProductImageData> imagesData;
final Product product;

@override
State<ProductImageGalleryView> createState() =>
_ProductImageGalleryViewState();
}

class _ProductImageGalleryViewState extends State<ProductImageGalleryView> {
late final Map<ProductImageData, ImageProvider?> selectedImages;
Map<ProductImageData, ImageProvider?> _selectedImages =
<ProductImageData, ImageProvider<Object>?>{};

final Map<ProductImageData, ImageProvider?> unselectedImages =
final Map<ProductImageData, ImageProvider?> _unselectedImages =
<ProductImageData, ImageProvider?>{};

bool _isRefreshed = false;
bool _isLoadingMore = true;

@override
void initState() {
selectedImages = Map<ProductImageData, ImageProvider?>.fromIterables(
widget.imagesData,
widget.imagesData.map(_provideImage),
);

_getProductImages(widget.barcode!)
.then((Iterable<ProductImageData>? loadedData) {
if (loadedData == null) {
return;
}

final Map<ProductImageData, ImageProvider<Object>?> newMap =
Map<ProductImageData, ImageProvider?>.fromIterables(
loadedData,
loadedData.map(_provideImage),
);

setState(() {
unselectedImages.addAll(newMap);
_isLoadingMore = false;
});
});

super.initState();
}

ImageProvider? _provideImage(ProductImageData imageData) =>
imageData.imageUrl == null ? null : NetworkImage(imageData.imageUrl!);

@override
Widget build(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
final ThemeData theme = Theme.of(context);
return Consumer<UpToDateProductProvider>(
builder: (
BuildContext context,
UpToDateProductProvider provider,
Widget? child,
) {
Product product = widget.product;

// When there is no data there should be no way to get to this page.
if (selectedImages.isEmpty) {
return SmoothScaffold(
body: Center(
child: Text(appLocalizations.error),
),
);
}
return SmoothScaffold(
appBar: AppBar(
title: Text(appLocalizations.edit_product_form_item_photos_title),
leading: SmoothBackButton(
onPressed: () => Navigator.maybePop(context, _isRefreshed),
),
),
body: Scrollbar(
child: CustomScrollView(
slivers: <Widget>[
_buildTitle(appLocalizations.selected_images, theme: theme),
SmoothImagesSliverList(
imagesData: selectedImages,
onTap: (ProductImageData data, _) =>
data.imageUrl != null ? _openImage(data) : _newImage(data),
final Product? refreshedProduct = provider.get(product);
if (refreshedProduct != null) {
product = refreshedProduct;
}
final List<ProductImageData> allProductImagesData =
getProductMainImagesData(product, appLocalizations);
_selectedImages = Map<ProductImageData, ImageProvider?>.fromIterables(
allProductImagesData,
allProductImagesData.map(_provideImage),
);

_getProductImages().then(
(Iterable<ProductImageData>? loadedData) {
if (loadedData == null) {
return;
}

final Map<ProductImageData, ImageProvider<Object>?> newMap =
Map<ProductImageData, ImageProvider?>.fromIterables(
loadedData,
loadedData.map(_provideImage),
);
if (mounted) {
setState(
() {
_unselectedImages.clear();
_unselectedImages.addAll(newMap);
_isLoadingMore = false;
},
);
}
},
);
if (_selectedImages.isEmpty) {
return SmoothScaffold(
body: Center(
child: Text(appLocalizations.error),
),
_buildTitle(appLocalizations.all_images, theme: theme),
SmoothImagesSliverGrid(
imagesData: unselectedImages,
loading: _isLoadingMore,
onTap: (ProductImageData data, _) => _openImage(data),
);
}
return SmoothScaffold(
appBar: AppBar(
title: Text(appLocalizations.edit_product_form_item_photos_title),
leading: SmoothBackButton(
onPressed: () => Navigator.maybePop(context, _isRefreshed),
),
],
),
),
),
body: RefreshIndicator(
onRefresh: () async {
_refreshProduct(context);
},
child: Scrollbar(
child: CustomScrollView(
slivers: <Widget>[
_buildTitle(appLocalizations.selected_images, theme: theme),
SmoothImagesSliverList(
imagesData: _selectedImages,
onTap: (ProductImageData data, _) => data.imageUrl != null
? _openImage(data)
: _newImage(data),
),
_buildTitle(appLocalizations.all_images, theme: theme),
SmoothImagesSliverGrid(
imagesData: _unselectedImages,
loading: _isLoadingMore,
onTap: (ProductImageData data, _) => _openImage(data),
),
],
),
),
),
);
},
);
}

Expand All @@ -126,37 +150,59 @@ class _ProductImageGalleryViewState extends State<ProductImageGalleryView> {
),
);

Future<bool> _refreshProduct(BuildContext context) async {
final LocalDatabase localDatabase = context.read<LocalDatabase>();
final bool success = await ProductRefresher().fetchAndRefresh(
context: context,
localDatabase: localDatabase,
barcode: widget.product.barcode!,
);
if (mounted && success) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(appLocalizations.product_refreshed),
duration: SnackBarDuration.short,
),
);
}
return success;
}

Future<void> _openImage(ProductImageData imageData) async =>
Navigator.push<void>(
context,
MaterialPageRoute<void>(
builder: (_) => ProductImageViewer(
barcode: widget.barcode!,
imageData: imageData,
),
));
context,
MaterialPageRoute<void>(
builder: (_) => ProductImageViewer(
barcode: widget.product.barcode!,
imageData: imageData,
),
),
);

Future<void> _newImage(ProductImageData data) async {
final File? croppedImageFile = await startImageCropping(context);
if (croppedImageFile == null) {
return;
}
setState(() {
final FileImage fileImage = FileImage(croppedImageFile);
if (selectedImages.containsKey(data)) {
selectedImages[data] = fileImage;
} else if (unselectedImages.containsKey(data)) {
unselectedImages[data] = fileImage;
} else {
throw ArgumentError('Could not find the type of $data');
}
});
if (mounted) {
setState(() {
final FileImage fileImage = FileImage(croppedImageFile);
if (_selectedImages.containsKey(data)) {
_selectedImages[data] = fileImage;
} else if (_unselectedImages.containsKey(data)) {
_unselectedImages[data] = fileImage;
} else {
throw ArgumentError('Could not find the type of $data');
}
});
}
if (!mounted) {
return;
}
final bool isUploaded = await uploadCapturedPicture(
context,
barcode: widget.barcode!,
barcode: widget.product.barcode!,
imageField: data.imageField,
imageUri: croppedImageFile.uri,
);
Expand All @@ -174,15 +220,15 @@ class _ProductImageGalleryViewState extends State<ProductImageGalleryView> {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
duration: const Duration(seconds: 3),
duration: SnackBarDuration.medium,
),
);
}
}

Future<Iterable<ProductImageData>?> _getProductImages(String barcode) async {
Future<Iterable<ProductImageData>?> _getProductImages() async {
final ProductQueryConfiguration configuration = ProductQueryConfiguration(
barcode,
widget.product.barcode!,
fields: <ProductField>[ProductField.IMAGES],
language: ProductQuery.getLanguage(),
country: ProductQuery.getCountry(),
Expand All @@ -199,12 +245,12 @@ class _ProductImageGalleryViewState extends State<ProductImageGalleryView> {
return null;
}

final Product? product = result.product;
if (product == null || product.images == null) {
final Product? resultProduct = result.product;
if (resultProduct == null || resultProduct.images == null) {
return null;
}

return _deduplicateImages(product.images!).map(_getProductImageData);
return _deduplicateImages(resultProduct.images!).map(_getProductImageData);
}

/// Groups the list of [ProductImage] by [ProductImage.imgid]
Expand All @@ -222,6 +268,6 @@ class _ProductImageGalleryViewState extends State<ProductImageGalleryView> {
// TODO(VaiTon): i18n
title: image.imgid ?? '',
buttonText: image.imgid ?? '',
imageUrl: ImageHelper.buildUrl(widget.barcode, image),
imageUrl: ImageHelper.buildUrl(widget.product.barcode, image),
);
}

0 comments on commit 58dbd43

Please sign in to comment.