Skip to content

Commit

Permalink
feat: 4674 - added the "other photos" section (#4866)
Browse files Browse the repository at this point in the history
* feat: 4674 - added the "other photos" section

New files:
* `product_image_gallery_other_view.dart`: Display of the other pictures of a product.
* `product_image_other_page.dart`: Full page display of a raw product image.

Impacted file:
* `product_image_gallery_view.dart`: added the "other photos" section; fixed a "refresh not working" bug

* feat: 4674 - added a "click button" step

* feat: 4674 - different label for button

Impacted files:
* `app_en.arb`: add new label for "click for other photo" button
* `app_fr.arb`: add new label for "click for other photo" button
* `product_image_gallery_view.dart`: used new label for "click for other photo" button
  • Loading branch information
monsieurtanuki committed Dec 8, 2023
1 parent 60971d1 commit f0c26da
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 23 deletions.
2 changes: 2 additions & 0 deletions packages/smooth_app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,8 @@
"nutrition_page_update_done": "Product updated!",
"more_photos": "More interesting photos",
"@more_photos": {},
"view_more_photo_button": "View all existing photos for this product",
"@view_more_photo_button": {},
"no_product_found": "No product found",
"@no_product_found": {},
"not_found": "not found:",
Expand Down
2 changes: 2 additions & 0 deletions packages/smooth_app/lib/l10n/app_fr.arb
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,8 @@
"nutrition_page_update_done": "Produit mis à jour !",
"more_photos": "Plus de photos intéressantes",
"@more_photos": {},
"view_more_photo_button": "Voir toutes les photos existantes pour ce produit",
"@view_more_photo_button": {},
"no_product_found": "Aucun produit trouvé",
"@no_product_found": {},
"not_found": "non trouvé :",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:smooth_app/generic_lib/widgets/images/smooth_image.dart';
import 'package:smooth_app/pages/image/product_image_other_page.dart';
import 'package:smooth_app/query/product_query.dart';

/// Display of the other pictures of a product.
class ProductImageGalleryOtherView extends StatefulWidget {
const ProductImageGalleryOtherView({
required this.product,
});

final Product product;

@override
State<ProductImageGalleryOtherView> createState() =>
_ProductImageGalleryOtherViewState();
}

class _ProductImageGalleryOtherViewState
extends State<ProductImageGalleryOtherView> {
late final Future<List<int>> _loading = _loadOtherPics();

/// Number of columns for the grid.
static const int _columns = 3;

Future<List<int>> _loadOtherPics() async =>
OpenFoodAPIClient.getProductImageIds(
widget.product.barcode!,
uriHelper: ProductQuery.uriProductHelper,
user: ProductQuery.getUser(),
);

@override
Widget build(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
final double screenWidth = MediaQuery.of(context).size.width;
final double squareSize = screenWidth / _columns;
return FutureBuilder<List<int>>(
future: _loading,
builder: (
final BuildContext context,
final AsyncSnapshot<List<int>> snapshot,
) {
if (snapshot.connectionState != ConnectionState.done) {
return SizedBox(
width: squareSize,
height: squareSize,
child: const CircularProgressIndicator.adaptive(),
);
}
if (snapshot.data == null) {
return Text(
snapshot.error?.toString() ??
appLocalizations.loading_dialog_default_error_message,
);
}
final List<int> ids = snapshot.data!;
if (ids.isEmpty) {
// very unlikely btw.
return Text(
appLocalizations.edit_photo_select_existing_downloaded_none,
);
}
return SizedBox(
height: (ids.length / _columns).ceil() * squareSize,
child: GridView.builder(
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: _columns,
),
itemBuilder: (final BuildContext context, final int index) =>
InkWell(
onTap: () async => Navigator.push<void>(
context,
MaterialPageRoute<bool>(
builder: (BuildContext context) => ProductImageOtherPage(
widget.product,
ids[index],
),
),
),
child: SmoothImage(
width: squareSize,
height: squareSize,
imageProvider: NetworkImage(
ImageHelper.getUploadedImageUrl(
widget.product.barcode!,
ids[index],
ImageSize.DISPLAY,
),
),
),
),
itemCount: ids.length,
//scrollDirection: Axis.vertical,
),
);
},
);
}
}
39 changes: 39 additions & 0 deletions packages/smooth_app/lib/pages/image/product_image_other_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:smooth_app/helpers/product_cards_helper.dart';
import 'package:smooth_app/widgets/smooth_app_bar.dart';
import 'package:smooth_app/widgets/smooth_scaffold.dart';

/// Full page display of a raw product image.
class ProductImageOtherPage extends StatelessWidget {
const ProductImageOtherPage(
this.product,
this.imageId,
);

final Product product;
final int imageId;

@override
Widget build(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
return SmoothScaffold(
appBar: SmoothAppBar(
centerTitle: false,
title: Text(appLocalizations.edit_product_form_item_photos_title),
subTitle: buildProductTitle(product, appLocalizations),
),
body: Image(
image: NetworkImage(
ImageHelper.getUploadedImageUrl(
product.barcode!,
imageId,
ImageSize.ORIGINAL,
),
),
fit: BoxFit.cover,
),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import 'package:provider/provider.dart';
import 'package:smooth_app/data_models/up_to_date_mixin.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/database/transient_file.dart';
import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/widgets/images/smooth_image.dart';
import 'package:smooth_app/generic_lib/widgets/language_selector.dart';
import 'package:smooth_app/helpers/analytics_helper.dart';
import 'package:smooth_app/helpers/image_field_extension.dart';
import 'package:smooth_app/helpers/product_cards_helper.dart';
import 'package:smooth_app/pages/image/product_image_gallery_other_view.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_swipeable_view.dart';
Expand All @@ -34,6 +36,7 @@ class ProductImageGalleryView extends StatefulWidget {
class _ProductImageGalleryViewState extends State<ProductImageGalleryView>
with UpToDateMixin {
late OpenFoodFactsLanguage _language;
bool _clickedOtherPictureButton = false;

@override
void initState() {
Expand Down Expand Up @@ -76,30 +79,49 @@ class _ProductImageGalleryViewState extends State<ProductImageGalleryView>
barcode: barcode,
context: context,
),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
LanguageSelector(
setLanguage: (final OpenFoodFactsLanguage? newLanguage) async {
if (newLanguage == null || newLanguage == _language) {
return;
}
setState(() => _language = newLanguage);
},
displayedLanguage: _language,
selectedLanguages: null,
padding: const EdgeInsetsDirectional.symmetric(
horizontal: 13.0,
vertical: SMALL_SPACE,
child: ListView(
children: <Widget>[
LanguageSelector(
setLanguage: (final OpenFoodFactsLanguage? newLanguage) async {
if (newLanguage == null || newLanguage == _language) {
return;
}
setState(() => _language = newLanguage);
},
displayedLanguage: _language,
selectedLanguages: null,
padding: const EdgeInsetsDirectional.symmetric(
horizontal: 13.0,
vertical: SMALL_SPACE,
),
),
_ImageRow(row: 1, product: upToDateProduct, language: _language),
_TextRow(row: 1, product: upToDateProduct, language: _language),
_ImageRow(row: 2, product: upToDateProduct, language: _language),
_TextRow(row: 2, product: upToDateProduct, language: _language),
if (!_clickedOtherPictureButton)
Padding(
padding: const EdgeInsets.all(SMALL_SPACE),
child: SmoothLargeButtonWithIcon(
text: appLocalizations.view_more_photo_button,
icon: Icons.photo_camera_rounded,
onPressed: () => setState(
() => _clickedOtherPictureButton = true,
),
),
),
_ImageRow(row: 1, product: upToDateProduct, language: _language),
_TextRow(row: 1, product: upToDateProduct, language: _language),
_ImageRow(row: 2, product: upToDateProduct, language: _language),
_TextRow(row: 2, product: upToDateProduct, language: _language),
// TODO(monsieurtanuki): add "other photos" for issue 4674
],
),
if (_clickedOtherPictureButton)
Padding(
padding: const EdgeInsets.all(SMALL_SPACE),
child: Text(
appLocalizations.more_photos,
style: _getTextStyle(context),
),
),
if (_clickedOtherPictureButton)
ProductImageGalleryOtherView(product: upToDateProduct),
const SizedBox(height: 2 * VERY_LARGE_SPACE),
],
),
),
);
Expand Down Expand Up @@ -269,10 +291,13 @@ class _Text extends StatelessWidget {
child: Center(
child: Text(
imageField.getProductImageTitle(AppLocalizations.of(context)),
style: Theme.of(context).textTheme.headlineMedium,
style: _getTextStyle(context),
textAlign: TextAlign.center,
),
),
),
);
}

TextStyle? _getTextStyle(final BuildContext context) =>
Theme.of(context).textTheme.headlineMedium;

0 comments on commit f0c26da

Please sign in to comment.