From 00dc9db07e8d61881c76bee251c58658c5fd28df Mon Sep 17 00:00:00 2001 From: Edouard Marquez Date: Sat, 18 Jan 2025 00:25:07 +0100 Subject: [PATCH 1/3] Info about the photo in the banner --- .../bottom_sheets/smooth_bottom_sheet.dart | 43 ++- packages/smooth_app/lib/l10n/app_en.arb | 20 ++ .../pages/image/product_image_other_page.dart | 11 +- .../product_image_gallery_details_banner.dart | 284 ++++++++---------- .../product_image_gallery_photo_row.dart | 1 - .../lib/pages/product/owner_field_info.dart | 8 +- 6 files changed, 193 insertions(+), 174 deletions(-) diff --git a/packages/smooth_app/lib/generic_lib/bottom_sheets/smooth_bottom_sheet.dart b/packages/smooth_app/lib/generic_lib/bottom_sheets/smooth_bottom_sheet.dart index e4c7f01456e8..8bfc76c9854c 100644 --- a/packages/smooth_app/lib/generic_lib/bottom_sheets/smooth_bottom_sheet.dart +++ b/packages/smooth_app/lib/generic_lib/bottom_sheets/smooth_bottom_sheet.dart @@ -111,10 +111,13 @@ Future showSmoothListOfChoicesModalSheet({ List? suffixIcons, Color? suffixIconTint, EdgeInsetsGeometry? padding, + EdgeInsetsGeometry? contentPadding, EdgeInsetsGeometry? dividerPadding, TextStyle? textStyle, Color? headerBackgroundColor, + Color? footerBackgroundColor, Color? prefixIndicatorColor, + double footerSpace = 0.0, bool safeArea = false, }) { assert(labels.length == values.length); @@ -142,10 +145,11 @@ Future showSmoothListOfChoicesModalSheet({ labels.elementAt(i), style: textStyle ?? const TextStyle(fontWeight: FontWeight.w500), ), - contentPadding: EdgeInsetsDirectional.only( - start: LARGE_SPACE, - end: addEndArrowToItems ? 17.0 : LARGE_SPACE, - ), + contentPadding: contentPadding ?? + EdgeInsetsDirectional.only( + start: LARGE_SPACE, + end: addEndArrowToItems ? 17.0 : LARGE_SPACE, + ), trailing: (suffixIcons != null ? IconTheme.merge( data: IconThemeData(color: suffixIconTint), @@ -170,15 +174,34 @@ Future showSmoothListOfChoicesModalSheet({ } } - if (footer != null) { - items.add(footer); + double bottomPadding = MediaQuery.paddingOf(context).bottom; + + if (safeArea && bottomPadding == 0.0) { + bottomPadding = MediaQuery.viewPaddingOf(context).bottom; } - final double paddingHeight = MediaQuery.paddingOf(context).bottom; - items.add(SizedBox(height: paddingHeight)); + if (footer != null) { + if (footerSpace > 0.0) { + items.add(SizedBox(height: footerSpace)); + } + + Widget footerChild = Column( + children: [ + footer, + SizedBox(height: bottomPadding), + ], + ); + + if (footerBackgroundColor != null) { + footerChild = ColoredBox( + color: footerBackgroundColor, + child: footerChild, + ); + } - if (safeArea && paddingHeight == 0.0) { - items.add(SizedBox(height: MediaQuery.viewPaddingOf(context).bottom)); + items.add(footerChild); + } else { + items.add(SizedBox(height: bottomPadding)); } return showSmoothModalSheet( diff --git a/packages/smooth_app/lib/l10n/app_en.arb b/packages/smooth_app/lib/l10n/app_en.arb index 4bbd74f2ec64..73d429d33426 100644 --- a/packages/smooth_app/lib/l10n/app_en.arb +++ b/packages/smooth_app/lib/l10n/app_en.arb @@ -538,6 +538,10 @@ "imageType": {} } }, + "outdated_image_short_label": "may be outdated", + "@outdated_image_short_label": { + "description": "A label for outdated images" + }, "ingredients": "Ingredients", "@ingredients": {}, "ingredients_editing_instructions": "Keep the original order. Indicate the percentage when specified. Separate with a comma or hyphen and use parentheses for ingredients of an ingredient.", @@ -851,6 +855,22 @@ "@product_image_action_choose_existing_photo": { "description": "Replace the existing picture with one from the product's photos" }, + "product_image_details_label": "Information about the photo", + "@product_image_details_label": { + "description": "Label for the photo details" + }, + "product_image_details_from_producer": "From the producer", + "@product_image_details_from_producer": { + "description": "Text to indicate that the image was taken by the producer" + }, + "product_image_details_date": "Date", + "@product_image_details_date": { + "description": "Text to indicate the date of the image" + }, + "product_image_details_date_unknown": "Unknown", + "@product_image_details_date": { + "description": "Text to indicate the date of the image is unknown" + }, "homepage_main_card_logo_description": "Welcome to Open Food Facts", "@homepage_main_card_logo_description": { "description": "Description for accessibility of the Open Food Facts logo on the homepage" diff --git a/packages/smooth_app/lib/pages/image/product_image_other_page.dart b/packages/smooth_app/lib/pages/image/product_image_other_page.dart index 913dfcbf1f8e..62d5cc9d1f63 100644 --- a/packages/smooth_app/lib/pages/image/product_image_other_page.dart +++ b/packages/smooth_app/lib/pages/image/product_image_other_page.dart @@ -398,6 +398,7 @@ class _ProductImageDetailsButton extends StatelessWidget { ), subtitle: Text(image.contributor ?? '-'), ), + const Divider(), ListTile( title: Text( appLocalizations.photo_viewer_details_date_title), @@ -405,6 +406,7 @@ class _ProductImageDetailsButton extends StatelessWidget { ? DateFormat.yMMMMEEEEd().format(image.uploaded!) : '-'), ), + const Divider(), ListTile( title: Text( appLocalizations.photo_viewer_details_size_title), @@ -418,7 +420,8 @@ class _ProductImageDetailsButton extends StatelessWidget { : '-', ), ), - if (url.isNotEmpty) + if (url.isNotEmpty) ...[ + const Divider(), ListTile( title: Text(appLocalizations .photo_viewer_details_url_title), @@ -427,9 +430,11 @@ class _ProductImageDetailsButton extends StatelessWidget { onTap: () { LaunchUrlHelper.launchURL(url); }, - ), + ) + ], SizedBox( - height: MediaQuery.viewPaddingOf(context).bottom), + height: MediaQuery.viewPaddingOf(context).bottom, + ), ], ), ); diff --git a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_details_banner.dart b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_details_banner.dart index 60137beef9d0..c5e7b3e26fbb 100644 --- a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_details_banner.dart +++ b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_details_banner.dart @@ -38,11 +38,17 @@ Future<_PhotoRowActions?> _showPhotoBanner({ const Icon(Icons.perm_media_rounded), const Icon(Icons.image_search_rounded), ], + contentPadding: const EdgeInsetsDirectional.symmetric( + horizontal: LARGE_SPACE, + ), addEndArrowToItems: true, + footerBackgroundColor: lightTheme ? extension.primaryLight : null, + footerSpace: VERY_SMALL_SPACE, footer: _PhotoRowBanner( children: [ _PhotoRowDate(transientFile: transientFile), - _PhotoRowLockedStatus( + const Divider(color: Colors.white), + _PhotoRowContributor( product: product, imageField: imageField, language: language, @@ -61,14 +67,50 @@ class _PhotoRowBanner extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: EdgeInsetsDirectional.only( - top: MEDIUM_SPACE, - bottom: !(Platform.isIOS || Platform.isMacOS) ? 0.0 : VERY_SMALL_SPACE, + final SmoothColorsThemeExtension extension = + context.extension(); + final bool lightTheme = context.lightTheme(); + + return ListTileTheme.merge( + titleTextStyle: TextStyle( + inherit: true, + fontSize: 16.0, + fontWeight: FontWeight.w500, + color: lightTheme ? Colors.black : Colors.white, + fontFamily: 'OpenSans', + ), + leadingAndTrailingTextStyle: TextStyle( + fontSize: 16.0, + fontWeight: FontWeight.w500, + color: lightTheme ? Colors.black : Colors.white, + fontFamily: 'OpenSans', + ), + contentPadding: const EdgeInsetsDirectional.only( + start: BALANCED_SPACE, + end: LARGE_SPACE, ), child: Column( mainAxisSize: MainAxisSize.min, - children: children, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: double.infinity, + color: extension.primaryDark, + padding: const EdgeInsetsDirectional.symmetric( + horizontal: MEDIUM_SPACE, + vertical: MEDIUM_SPACE, + ), + child: Text( + AppLocalizations.of(context).product_image_details_label, + style: const TextStyle( + color: Colors.white, + fontSize: 17.0, + fontWeight: FontWeight.bold, + ), + ), + ), + ...children, + ], ), ); } @@ -80,6 +122,42 @@ enum _PhotoRowActions { selectFromProductPhotos, } +class _PhotoRowContributor extends StatelessWidget { + const _PhotoRowContributor({ + required this.product, + required this.imageField, + required this.language, + }); + + final Product product; + final ImageField imageField; + final OpenFoodFactsLanguage language; + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final SmoothColorsThemeExtension extension = + context.extension(); + + final bool isLocked = product.isImageLocked(imageField, language) == true; + + return ListTile( + leading: _PhotoRowDetailsIcon( + color: extension.primaryDark, + icon: const OwnerFieldIcon( + color: Colors.white, + size: 19.0, + ), + padding: const EdgeInsetsDirectional.only(bottom: 1.0, end: 1.0), + ), + title: Text(appLocalizations.product_image_details_from_producer), + trailing: Text( + isLocked ? appLocalizations.yes : appLocalizations.no, + ), + ); + } +} + /// The date of the photo (used in the modal sheet) class _PhotoRowDate extends StatelessWidget { const _PhotoRowDate({ @@ -90,46 +168,33 @@ class _PhotoRowDate extends StatelessWidget { @override Widget build(BuildContext context) { - if (!transientFile.isImageAvailable() || - transientFile.uploadedDate == null) { - return EMPTY_WIDGET; - } - + final AppLocalizations appLocalizations = AppLocalizations.of(context); final SmoothColorsThemeExtension extension = context.extension(); - final bool outdated = transientFile.expired; - final AppLocalizations appLocalizations = AppLocalizations.of(context); + final bool outdated = transientFile.expired; - return _PhotoRowInfo( - icon: outdated ? _outdatedIcon : _successIcon, - iconBackgroundColor: outdated ? extension.warning : extension.success, - text: Padding( - /// Padding required by the use of [RichText] - padding: const EdgeInsetsDirectional.only(bottom: 2.75), - child: RichText( - text: TextSpan( - children: [ - TextSpan( - text: '${appLocalizations.date}${appLocalizations.sep}: ', - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - TextSpan( - text: DateFormat.yMd(ProductQuery.getLocaleString()) - .format(transientFile.uploadedDate!), - ), - ], - style: DefaultTextStyle.of(context).style.merge( - const TextStyle( - fontSize: 15.0, - fontWeight: FontWeight.w500, - color: Colors.white, - ), - ), + return ListTile( + leading: _PhotoRowDetailsIcon( + color: outdated ? extension.warning : extension.primaryDark, + icon: outdated ? _outdatedIcon : _successIcon, + ), + title: Text(appLocalizations.date), + trailing: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + transientFile.uploadedDate != null + ? DateFormat.yMd().format(transientFile.uploadedDate!) + : appLocalizations.product_image_details_date_unknown, ), - ), + if (outdated) + Text( + '(${appLocalizations.outdated_image_short_label})', + style: const TextStyle(fontSize: 15.0), + ), + ], ), ); } @@ -140,144 +205,45 @@ class _PhotoRowDate extends StatelessWidget { start: 1.5, ), child: icons.Outdated( - color: Colors.white, size: 19.0, + color: Colors.white, ), ); Widget get _successIcon => const Padding( - padding: EdgeInsetsDirectional.only( - bottom: 0.5, - start: 0.5, - ), + padding: EdgeInsetsDirectional.only(bottom: 0.5), child: icons.Clock( - color: Colors.white, size: 19.0, + color: Colors.white, ), ); } -/// If the photo is locked by the owner (used in the modal sheet) -class _PhotoRowLockedStatus extends StatelessWidget { - const _PhotoRowLockedStatus({ - required this.product, - required this.imageField, - required this.language, - }); - - final Product product; - final ImageField imageField; - final OpenFoodFactsLanguage language; - - @override - Widget build(BuildContext context) { - if (product.isImageLocked(imageField, language) != true) { - return EMPTY_WIDGET; - } - - final SmoothColorsThemeExtension extension = - context.extension(); - - final AppLocalizations appLocalizations = AppLocalizations.of(context); - - return Padding( - padding: const EdgeInsetsDirectional.only(top: SMALL_SPACE), - child: _PhotoRowInfo( - icon: const IconTheme( - data: IconThemeData( - size: 19.0, - color: Colors.white, - ), - child: Padding( - padding: EdgeInsetsDirectional.only(bottom: 2.0), - child: OwnerFieldIcon(), - ), - ), - iconBackgroundColor: extension.warning, - text: Text( - appLocalizations.owner_field_image, - style: const TextStyle( - fontWeight: FontWeight.w600, - color: Colors.white, - ), - ), - wrapTextInExpanded: true, - ), - ); - } -} - -/// Show an info in the modal sheet -class _PhotoRowInfo extends StatelessWidget { - const _PhotoRowInfo({ +class _PhotoRowDetailsIcon extends StatelessWidget { + const _PhotoRowDetailsIcon({ required this.icon, - required this.iconBackgroundColor, - required this.text, - this.wrapTextInExpanded = false, + required this.color, + this.padding, }); final Widget icon; - final Color iconBackgroundColor; - final Widget text; - final bool wrapTextInExpanded; + final Color color; + final EdgeInsetsGeometry? padding; @override Widget build(BuildContext context) { - final SmoothColorsThemeExtension extension = - context.extension(); - - return Padding( - padding: const EdgeInsetsDirectional.symmetric(horizontal: SMALL_SPACE), - child: DecoratedBox( - decoration: BoxDecoration( - color: extension.primaryDark, - borderRadius: BorderRadius.all( - Radius.circular(MediaQuery.of(context).size.height), - ), - ), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: 47.5, - maxWidth: MediaQuery.sizeOf(context).width * 0.95, - ), - child: IntrinsicHeight( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox.square( - dimension: 47.5, - child: DecoratedBox( - decoration: BoxDecoration( - color: iconBackgroundColor, - shape: BoxShape.circle, - ), - child: Center(child: icon), - ), - ), - _textWidget, - ], - ), - ), - ), + return DecoratedBox( + decoration: BoxDecoration( + color: color, + shape: BoxShape.circle, ), - ); - } - - Widget get _textWidget { - final Widget textWidget = Padding( - padding: const EdgeInsetsDirectional.only( - start: MEDIUM_SPACE, - end: VERY_LARGE_SPACE, + child: SizedBox.square( + dimension: 35.0, + child: Padding( + padding: padding ?? const EdgeInsetsDirectional.all(SMALL_SPACE), + child: icon, + ), ), - child: text, ); - - if (wrapTextInExpanded) { - return Expanded( - child: textWidget, - ); - } - - return textWidget; } } diff --git a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart index a75b2d0ae025..73ec064d625a 100644 --- a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart +++ b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart @@ -18,7 +18,6 @@ import 'package:smooth_app/pages/product/gallery_view/product_image_gallery_view import 'package:smooth_app/pages/product/owner_field_info.dart'; import 'package:smooth_app/pages/product/product_image_server_button.dart'; import 'package:smooth_app/pages/product/product_image_swipeable_view.dart'; -import 'package:smooth_app/query/product_query.dart'; import 'package:smooth_app/resources/app_animations.dart'; import 'package:smooth_app/resources/app_icons.dart' as icons; import 'package:smooth_app/themes/smooth_theme.dart'; diff --git a/packages/smooth_app/lib/pages/product/owner_field_info.dart b/packages/smooth_app/lib/pages/product/owner_field_info.dart index 55a2b3c9d15f..eb6511d9e4ca 100644 --- a/packages/smooth_app/lib/pages/product/owner_field_info.dart +++ b/packages/smooth_app/lib/pages/product/owner_field_info.dart @@ -37,14 +37,20 @@ class OwnerFieldBanner extends StatelessWidget { /// Standard icon about "owner fields". class OwnerFieldIcon extends StatelessWidget { - const OwnerFieldIcon({this.size, super.key}); + const OwnerFieldIcon({ + this.size, + this.color, + super.key, + }); final double? size; + final Color? color; @override Widget build(BuildContext context) => Icon( _ownerFieldIconData, size: size, + color: color, semanticLabel: AppLocalizations.of(context).owner_field_info_title, ); } From b15960ddbabdfdba25a6bbf8474b4a5551bfd95e Mon Sep 17 00:00:00 2001 From: Edouard Marquez Date: Sat, 18 Jan 2025 22:35:11 +0100 Subject: [PATCH 2/3] Contributor's name + expandable container --- packages/smooth_app/lib/l10n/app_en.arb | 8 + .../product_image_gallery_details_banner.dart | 220 +++++++++++++----- .../product_image_gallery_photo_row.dart | 1 + 3 files changed, 175 insertions(+), 54 deletions(-) diff --git a/packages/smooth_app/lib/l10n/app_en.arb b/packages/smooth_app/lib/l10n/app_en.arb index 73d429d33426..5ae3131eec45 100644 --- a/packages/smooth_app/lib/l10n/app_en.arb +++ b/packages/smooth_app/lib/l10n/app_en.arb @@ -863,6 +863,14 @@ "@product_image_details_from_producer": { "description": "Text to indicate that the image was taken by the producer" }, + "product_image_details_contributor": "Contributor", + "@product_image_details_contributor": { + "description": "The name of the contributor who uploaded the image" + }, + "product_image_details_contributor_producer": "Contributor (producer)", + "@product_image_details_contributor": { + "description": "The name of the contributor (and also the owner field) who uploaded the image" + }, "product_image_details_date": "Date", "@product_image_details_date": { "description": "Text to indicate the date of the image" diff --git a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_details_banner.dart b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_details_banner.dart index c5e7b3e26fbb..dcf704435b86 100644 --- a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_details_banner.dart +++ b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_details_banner.dart @@ -44,26 +44,59 @@ Future<_PhotoRowActions?> _showPhotoBanner({ addEndArrowToItems: true, footerBackgroundColor: lightTheme ? extension.primaryLight : null, footerSpace: VERY_SMALL_SPACE, - footer: _PhotoRowBanner( - children: [ - _PhotoRowDate(transientFile: transientFile), - const Divider(color: Colors.white), - _PhotoRowContributor( - product: product, - imageField: imageField, - language: language, - ), - ], - ), + footer: transientFile.isImageAvailable() + ? _PhotoRowBanner( + product: product, + imageField: imageField, + language: language, + transientFile: transientFile, + ) + : null, ); return action; } -class _PhotoRowBanner extends StatelessWidget { - const _PhotoRowBanner({required this.children}); +enum _PhotoRowActions { + takePicture, + selectFromGallery, + selectFromProductPhotos, +} + +class _PhotoRowBanner extends StatefulWidget { + const _PhotoRowBanner({ + required this.product, + required this.imageField, + required this.language, + required this.transientFile, + }); - final List children; + final Product product; + final ImageField imageField; + final OpenFoodFactsLanguage language; + final TransientFile transientFile; + + @override + State<_PhotoRowBanner> createState() => _PhotoRowBannerState(); +} + +class _PhotoRowBannerState extends State<_PhotoRowBanner> { + late bool _expanded; + late final bool _dateInitiallyVisible; + late final bool _contributorInitiallyVisible; + + @override + void initState() { + super.initState(); + + _dateInitiallyVisible = _PhotoRowDate.isVisible(widget.transientFile); + _contributorInitiallyVisible = _PhotoRowContributor.isVisible( + widget.product, + widget.imageField, + widget.language, + ); + _expanded = _dateInitiallyVisible && _contributorInitiallyVisible; + } @override Widget build(BuildContext context) { @@ -93,35 +126,69 @@ class _PhotoRowBanner extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - width: double.infinity, - color: extension.primaryDark, - padding: const EdgeInsetsDirectional.symmetric( - horizontal: MEDIUM_SPACE, - vertical: MEDIUM_SPACE, - ), - child: Text( - AppLocalizations.of(context).product_image_details_label, - style: const TextStyle( - color: Colors.white, - fontSize: 17.0, - fontWeight: FontWeight.bold, + Material( + type: MaterialType.transparency, + child: InkWell( + onTap: !_expanded + ? () => setState(() => _expanded = !_expanded) + : null, + child: Ink( + width: double.infinity, + color: extension.primaryDark, + padding: const EdgeInsetsDirectional.symmetric( + horizontal: MEDIUM_SPACE, + vertical: MEDIUM_SPACE, + ), + child: Row( + children: [ + Expanded( + child: Text( + AppLocalizations.of(context) + .product_image_details_label, + style: const TextStyle( + color: Colors.white, + fontSize: 17.0, + fontWeight: FontWeight.bold, + ), + ), + ), + if (!_expanded) + Padding( + padding: const EdgeInsetsDirectional.only( + end: 9.0, + ), + child: Semantics( + value: MaterialLocalizations.of(context) + .expandedIconTapHint, + excludeSemantics: true, + child: const icons.Chevron.down( + size: 18.0, + color: Colors.white, + ), + ), + ) + ], + ), ), ), ), - ...children, + if (_expanded || _dateInitiallyVisible) + _PhotoRowDate( + transientFile: widget.transientFile, + ), + if (_expanded) const Divider(color: Colors.white), + if (_expanded || _contributorInitiallyVisible) + _PhotoRowContributor( + product: widget.product, + imageField: widget.imageField, + language: widget.language, + ), ], ), ); } } -enum _PhotoRowActions { - takePicture, - selectFromGallery, - selectFromProductPhotos, -} - class _PhotoRowContributor extends StatelessWidget { const _PhotoRowContributor({ required this.product, @@ -133,29 +200,75 @@ class _PhotoRowContributor extends StatelessWidget { final ImageField imageField; final OpenFoodFactsLanguage language; + static bool isVisible(Product product, ImageField imageField, + OpenFoodFactsLanguage language) => + product.isImageLocked(imageField, language) == true; + @override Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); final SmoothColorsThemeExtension extension = context.extension(); - final bool isLocked = product.isImageLocked(imageField, language) == true; + final bool isLocked = isVisible(product, imageField, language); + final String? contributor = _contributor; + + final String title; + final String value; + final Widget icon; + final EdgeInsetsGeometry padding; + + if (contributor?.isNotEmpty == true) { + title = isLocked + ? appLocalizations.product_image_details_contributor_producer + : appLocalizations.product_image_details_contributor; + value = contributor!; + } else { + title = appLocalizations.product_image_details_from_producer; + value = isLocked ? appLocalizations.yes : appLocalizations.no; + } + + if (isLocked) { + icon = const OwnerFieldIcon(size: 19.0); + padding = const EdgeInsetsDirectional.only(bottom: 1.0, end: 1.0); + } else { + icon = const Icon(Icons.person); + padding = EdgeInsets.zero; + } return ListTile( leading: _PhotoRowDetailsIcon( color: extension.primaryDark, - icon: const OwnerFieldIcon( - color: Colors.white, - size: 19.0, - ), - padding: const EdgeInsetsDirectional.only(bottom: 1.0, end: 1.0), - ), - title: Text(appLocalizations.product_image_details_from_producer), - trailing: Text( - isLocked ? appLocalizations.yes : appLocalizations.no, + icon: icon, + padding: padding, ), + title: Text(title), + trailing: Text(value), ); } + + String? get _contributor { + if (product.images == null) { + return null; + } + + for (final ProductImage productImage in product.images!) { + if (productImage.field == imageField && + productImage.language == language) { + if (productImage.contributor != null) { + /// Always null in my tests + return productImage.contributor; + } + + /// Let's try to find by the image id + return product.images!.firstWhereOrNull((ProductImage img) { + return productImage.imgid == img.imgid; + })?.contributor; + } + } + + return null; + } } /// The date of the photo (used in the modal sheet) @@ -166,13 +279,15 @@ class _PhotoRowDate extends StatelessWidget { final TransientFile transientFile; + static bool isVisible(TransientFile transientFile) => transientFile.expired; + @override Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); final SmoothColorsThemeExtension extension = context.extension(); - final bool outdated = transientFile.expired; + final bool outdated = isVisible(transientFile); return ListTile( leading: _PhotoRowDetailsIcon( @@ -204,18 +319,12 @@ class _PhotoRowDate extends StatelessWidget { bottom: 1.5, start: 1.5, ), - child: icons.Outdated( - size: 19.0, - color: Colors.white, - ), + child: icons.Outdated(size: 19.0), ); Widget get _successIcon => const Padding( padding: EdgeInsetsDirectional.only(bottom: 0.5), - child: icons.Clock( - size: 19.0, - color: Colors.white, - ), + child: icons.Clock(size: 19.0), ); } @@ -241,7 +350,10 @@ class _PhotoRowDetailsIcon extends StatelessWidget { dimension: 35.0, child: Padding( padding: padding ?? const EdgeInsetsDirectional.all(SMALL_SPACE), - child: icon, + child: IconTheme.merge( + data: const IconThemeData(color: Colors.white), + child: icon, + ), ), ), ); diff --git a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart index 73ec064d625a..a62ebe17464e 100644 --- a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart +++ b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_photo_row.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:auto_size_text/auto_size_text.dart'; +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:intl/intl.dart'; From 99534a7c2f4e91406cb13b87df88a8dd2aab1d2f Mon Sep 17 00:00:00 2001 From: Edouard Marquez Date: Sat, 18 Jan 2025 22:37:50 +0100 Subject: [PATCH 3/3] Recenter outdated icon --- .../gallery_view/product_image_gallery_details_banner.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_details_banner.dart b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_details_banner.dart index dcf704435b86..498dacb9e7ed 100644 --- a/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_details_banner.dart +++ b/packages/smooth_app/lib/pages/product/gallery_view/product_image_gallery_details_banner.dart @@ -293,6 +293,12 @@ class _PhotoRowDate extends StatelessWidget { leading: _PhotoRowDetailsIcon( color: outdated ? extension.warning : extension.primaryDark, icon: outdated ? _outdatedIcon : _successIcon, + padding: outdated + ? const EdgeInsetsDirectional.only( + top: 0.5, + end: 1.0, + ) + : null, ), title: Text(appLocalizations.date), trailing: Column(