Skip to content

Commit

Permalink
feat: Allow to remove a product not found from the carousel (#4126)
Browse files Browse the repository at this point in the history
* Allow to remove a product not found from the carousel

* Add an analytics event when the user removes an unknown product
  • Loading branch information
g123k authored Jun 12, 2023
1 parent abab934 commit 74c00ae
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ 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/generic_lib/design_constants.dart';
import 'package:smooth_app/cards/product_cards/smooth_product_base_card.dart';
import 'package:smooth_app/helpers/extension_on_text_helper.dart';
import 'package:smooth_app/helpers/product_cards_helper.dart';

Expand Down Expand Up @@ -129,23 +129,10 @@ class _ProductTitleCardTrailing extends StatelessWidget {
final Product product = context.read<Product>();

if (removable && !selectable) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);

return Align(
alignment: Alignment.centerRight,
child: InkWell(
customBorder: const CircleBorder(),
onTap: () => onRemove?.call(context),
child: Tooltip(
message: appLocalizations.product_card_remove_product_tooltip,
child: const Padding(
padding: EdgeInsets.all(SMALL_SPACE),
child: Icon(
Icons.clear_rounded,
size: DEFAULT_ICON_SIZE,
),
),
),
child: ProductCardCloseButton(
onRemove: onRemove,
),
);
} else {
Expand All @@ -157,5 +144,3 @@ class _ProductTitleCardTrailing extends StatelessWidget {
}
}
}

typedef OnRemoveCallback = void Function(BuildContext context);
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_card.dart';

/// A common Widget for carrousel item cards.
Expand Down Expand Up @@ -47,3 +49,35 @@ class SmoothProductBaseCard extends StatelessWidget {
);
}
}

/// A simple button to express we can remove a card
class ProductCardCloseButton extends StatelessWidget {
const ProductCardCloseButton({
this.onRemove,
super.key,
});

final OnRemoveCallback? onRemove;

@override
Widget build(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);

return InkWell(
customBorder: const CircleBorder(),
onTap: () => onRemove?.call(context),
child: Tooltip(
message: appLocalizations.product_card_remove_product_tooltip,
child: const Padding(
padding: EdgeInsets.all(SMALL_SPACE),
child: Icon(
Icons.clear_rounded,
size: DEFAULT_ICON_SIZE,
),
),
),
);
}
}

typedef OnRemoveCallback = void Function(BuildContext context);
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:smooth_app/cards/product_cards/smooth_product_base_card.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/helpers/analytics_helper.dart';
import 'package:smooth_app/pages/product/add_new_product_page.dart';

class SmoothProductCardNotFound extends StatelessWidget {
SmoothProductCardNotFound({
required this.barcode,
this.callback,
this.onAddProduct,
this.onRemoveProduct,
}) : assert(barcode.isNotEmpty);

final Future<void> Function()? callback;
final Future<void> Function()? onAddProduct;
final OnRemoveCallback? onRemoveProduct;
final String barcode;

@override
Expand All @@ -21,57 +24,72 @@ class SmoothProductCardNotFound extends StatelessWidget {

return SmoothProductBaseCard(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: textTheme.headlineSmall,
children: <InlineSpan>[
TextSpan(
text: appLocalizations.missing_product,
style: textTheme.displayMedium,
),
const WidgetSpan(
alignment: PlaceholderAlignment.belowBaseline,
baseline: TextBaseline.alphabetic,
child: SizedBox(
height: LARGE_SPACE,
Align(
alignment: AlignmentDirectional.topEnd,
child: ProductCardCloseButton(onRemove: (BuildContext context) {
AnalyticsHelper.trackEvent(
AnalyticsEvent.ignoreProductNotFound,
barcode: barcode,
);

onRemoveProduct?.call(context);
}),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: textTheme.headlineSmall,
children: <InlineSpan>[
TextSpan(
text: appLocalizations.missing_product,
style: textTheme.displayMedium,
),
const WidgetSpan(
alignment: PlaceholderAlignment.belowBaseline,
baseline: TextBaseline.alphabetic,
child: SizedBox(
height: LARGE_SPACE,
),
),
TextSpan(
text: '\n${appLocalizations.add_product_take_photos}\n',
style: textTheme.bodyMedium,
),
TextSpan(
text: '(${appLocalizations.barcode_barcode(barcode)})',
style: textTheme.bodyMedium,
),
],
),
),
TextSpan(
text: '\n${appLocalizations.add_product_take_photos}\n',
style: textTheme.bodyMedium,
),
TextSpan(
text: '(${appLocalizations.barcode_barcode(barcode)})',
style: textTheme.bodyMedium,
Padding(
padding: const EdgeInsetsDirectional.only(top: LARGE_SPACE),
child: SmoothLargeButtonWithIcon(
text: appLocalizations.add_product_information_button_label,
icon: Icons.add,
padding: const EdgeInsets.symmetric(vertical: LARGE_SPACE),
onPressed: () async {
await Navigator.push<void>(
context,
MaterialPageRoute<void>(
builder: (BuildContext context) =>
AddNewProductPage(barcode: barcode),
),
);

await onAddProduct?.call();
},
),
),
],
),
),
Padding(
padding: const EdgeInsetsDirectional.only(top: LARGE_SPACE),
child: SmoothLargeButtonWithIcon(
text: appLocalizations.add_product_information_button_label,
icon: Icons.add,
padding: const EdgeInsets.symmetric(vertical: LARGE_SPACE),
onPressed: () async {
await Navigator.push<void>(
context,
MaterialPageRoute<void>(
builder: (BuildContext context) =>
AddNewProductPage(barcode: barcode),
),
);

if (callback != null) {
await callback!();
}
},
),
),
],
),
);
Expand Down
4 changes: 4 additions & 0 deletions packages/smooth_app/lib/helpers/analytics_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ enum AnalyticsEvent {
tag: 'could not find product',
category: AnalyticsCategory.couldNotFindProduct,
),
ignoreProductNotFound(
tag: 'ignore product',
category: AnalyticsCategory.couldNotFindProduct,
),
openProductEditPage(
tag: 'opened product edit page',
category: AnalyticsCategory.productEdit,
Expand Down
3 changes: 2 additions & 1 deletion packages/smooth_app/lib/widgets/smooth_product_carousel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,11 @@ class _SmoothProductCarouselState extends State<SmoothProductCarousel> {
case ScannedProductState.NOT_FOUND:
return SmoothProductCardNotFound(
barcode: barcode,
callback: () async {
onAddProduct: () async {
await _model.refresh();
setState(() {});
},
onRemoveProduct: (_) => _model.removeBarcode(barcode),
);
case ScannedProductState.THANKS:
return const SmoothProductCardThanks();
Expand Down

0 comments on commit 74c00ae

Please sign in to comment.