Skip to content

Commit

Permalink
feat: 4223 - comparison page for 3 random products on dev mode (#4444)
Browse files Browse the repository at this point in the history
* feat: 4223 - comparison page for 3 random products on dev mode

New file:
* `compare_products3_page.dart`: Test page about comparing 3 products. Work in progress.

Impacted files:
* `attributes_card_helper.dart`: refactored introducing two new reusable methods
* `nutrition_container.dart`: refactored introducing one reusable getter
* `product_cards_helper.dart`: refactored introducing one new reusable method
* `user_preferences_dev_mode.dart`: added an item computing 3 random products and showing the product comparison page

* feat: 4223 - stupid lint check part 4

* feat: 4223 - access to boosted comparison from list page via dev mode

Impacted files:
* `product_list_page.dart`: slightly refactored the app bar text in selection mode, in order to have several possible actions; replaced the FAB with an explicit (?) "multi-select" button; added a "boosted comparison" icon button when 2 or 3 products are selected; added a "ranking" icon button
* `user_preferences_dev_mode.dart`: added a dev mode flag for "boosted comparison" (sic)

* feat: 4223 - minor warning fix

* feat: 4223 - now displays item actions in a "more..." menu

New file:
* `product_list_item_popup_items.dart`: Popup menu items for the product list page, for selected items.

Impacted files:
* `product_list_page.dart`: now displays item actions in a "more..." menu
* `product_list_popup_items.dart`: minor refactoring
  • Loading branch information
monsieurtanuki committed Aug 27, 2023
1 parent 5bac720 commit d4bdca2
Show file tree
Hide file tree
Showing 8 changed files with 658 additions and 152 deletions.
40 changes: 29 additions & 11 deletions packages/smooth_app/lib/helpers/attributes_card_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,35 @@ enum AttributeEvaluation {
}
}

Widget getAttributeDisplayIcon(final Attribute attribute) => Padding(
padding: const EdgeInsetsDirectional.only(end: VERY_SMALL_SPACE),
child: _attributeMatchComparison(
attribute,
const Icon(CupertinoIcons.question, color: RED_COLOR),
const Icon(Icons.lens, color: RED_COLOR),
const Icon(Icons.lens, color: LIGHT_ORANGE_COLOR),
const Icon(Icons.lens, color: LIGHT_ORANGE_COLOR),
const Icon(Icons.lens, color: LIGHT_GREEN_COLOR),
const Icon(Icons.lens, color: LIGHT_GREEN_COLOR),
),
Widget getAttributeDisplayIcon(final Attribute attribute) {
final Color color = getAttributeDisplayBackgroundColor(attribute);
final IconData iconData = getAttributeDisplayIconData(attribute);
return Padding(
padding: const EdgeInsetsDirectional.only(end: VERY_SMALL_SPACE),
child: Icon(iconData, color: color),
);
}

Color getAttributeDisplayBackgroundColor(final Attribute attribute) =>
_attributeMatchComparison<Color>(
attribute,
RED_COLOR,
RED_COLOR,
LIGHT_ORANGE_COLOR,
LIGHT_ORANGE_COLOR,
LIGHT_GREEN_COLOR,
LIGHT_GREEN_COLOR,
);

IconData getAttributeDisplayIconData(final Attribute attribute) =>
_attributeMatchComparison<IconData>(
attribute,
CupertinoIcons.question,
Icons.lens,
Icons.lens,
Icons.lens,
Icons.lens,
Icons.lens,
);

bool isMatchAvailable(Attribute attribute) {
Expand Down
30 changes: 25 additions & 5 deletions packages/smooth_app/lib/helpers/product_cards_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,24 @@ List<Attribute> getMandatoryAttributes(
final List<String> attributeGroupOrder,
final Set<String> attributesToExcludeIfStatusIsUnknown,
final ProductPreferences preferences,
) {
) =>
getSortedAttributes(
product,
attributeGroupOrder,
attributesToExcludeIfStatusIsUnknown,
preferences,
PreferenceImportance.ID_MANDATORY,
);

/// Returns the attributes, ordered by importance desc and attribute group order
List<Attribute> getSortedAttributes(
final Product product,
final List<String> attributeGroupOrder,
final Set<String> attributesToExcludeIfStatusIsUnknown,
final ProductPreferences preferences,
final String importance, {
final bool excludeMainScoreAttributes = true,
}) {
final List<Attribute> result = <Attribute>[];
if (product.attributeGroups == null) {
return result;
Expand All @@ -106,9 +123,10 @@ List<Attribute> getMandatoryAttributes(
for (final AttributeGroup attributeGroup in product.attributeGroups!) {
mandatoryAttributesByGroup[attributeGroup.id!] = getFilteredAttributes(
attributeGroup,
PreferenceImportance.ID_MANDATORY,
importance,
attributesToExcludeIfStatusIsUnknown,
preferences,
excludeMainScoreAttributes: excludeMainScoreAttributes,
);
}

Expand All @@ -131,15 +149,17 @@ List<Attribute> getFilteredAttributes(
final AttributeGroup attributeGroup,
final String importance,
final Set<String> attributesToExcludeIfStatusIsUnknown,
final ProductPreferences preferences,
) {
final ProductPreferences preferences, {
final bool excludeMainScoreAttributes = true,
}) {
final List<Attribute> result = <Attribute>[];
if (attributeGroup.attributes == null) {
return result;
}
for (final Attribute attribute in attributeGroup.attributes!) {
final String attributeId = attribute.id!;
if (SCORE_ATTRIBUTE_IDS.contains(attributeId)) {
if (excludeMainScoreAttributes &&
SCORE_ATTRIBUTE_IDS.contains(attributeId)) {
continue;
}
if (attributeGroup.id == AttributeGroup.ATTRIBUTE_GROUP_LABELS) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class UserPreferencesDevMode extends AbstractUserPreferences {
static const String userPreferencesFlagProd = '__devWorkingOnProd';
static const String userPreferencesTestEnvHost = '__testEnvHost';
static const String userPreferencesFlagEditIngredients = '__editIngredients';
static const String userPreferencesFlagBoostedComparison =
'__boostedComparison';
static const String userPreferencesEnumScanMode = '__scanMode';
static const String userPreferencesAppLanguageCode = '__appLanguage';
static const String userPreferencesFlagAccessibilityNoColor =
Expand Down Expand Up @@ -338,6 +340,17 @@ class UserPreferencesDevMode extends AbstractUserPreferences {
ProductQuery.setLanguage(context, userPreferences);
},
),
SwitchListTile(
title: const Text('Side by side comparison for 2 or 3 products'),
value:
userPreferences.getFlag(userPreferencesFlagBoostedComparison) ??
false,
onChanged: (bool value) async {
await userPreferences.setFlag(
userPreferencesFlagBoostedComparison, value);
_showSuccessMessage();
},
),
ListTile(
title: const Text('Debugging information'),
onTap: () async => Navigator.of(context).push(MaterialPageRoute<void>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:smooth_app/data_models/product_list.dart';
import 'package:smooth_app/database/dao_product.dart';
import 'package:smooth_app/database/dao_product_list.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart';
import 'package:smooth_app/pages/personalized_ranking_page.dart';
import 'package:smooth_app/pages/product/compare_products3_page.dart';
import 'package:smooth_app/pages/product/ordered_nutrients_cache.dart';

/// Popup menu item entries for the product list page, for selected items.
enum ProductListItemPopupMenuEntry {
compareSideBySide,
rank,
delete,
}

/// Popup menu items for the product list page, for selected items.
abstract class ProductListItemPopupItem {
/// Title of the popup menu item.
String getTitle(final AppLocalizations appLocalizations);

/// IconData of the popup menu item.
IconData getIconData();

/// Action of the popup menu item.
///
/// Returns true if the caller must refresh (setState) (e.g. after deleting).
Future<bool> doSomething({
required final ProductList productList,
required final LocalDatabase localDatabase,
required final BuildContext context,
required final Iterable<String> selectedBarcodes,
});

/// Returns the popup menu item.
PopupMenuItem<ProductListItemPopupItem> getMenuItem(
final AppLocalizations appLocalizations,
final bool enabled,
) =>
PopupMenuItem<ProductListItemPopupItem>(
value: this,
enabled: enabled,
child: ListTile(
leading: Icon(getIconData()),
title: Text(getTitle(appLocalizations)),
),
);
}

/// Popup menu item for the product list page: compare side by side selected items.
class ProductListItemPopupSideBySide extends ProductListItemPopupItem {
@override
String getTitle(final AppLocalizations appLocalizations) =>
'Compare side by side';

@override
IconData getIconData() => Icons.difference_outlined;

@override
Future<bool> doSomething({
required final ProductList productList,
required final LocalDatabase localDatabase,
required final BuildContext context,
required final Iterable<String> selectedBarcodes,
}) async {
final OrderedNutrientsCache? cache =
await OrderedNutrientsCache.getCache(context);
if (context.mounted) {
if (cache == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).nutrition_cache_loading_error,
),
),
);
return false;
}
final DaoProduct daoProduct = DaoProduct(localDatabase);
final List<Product> list = <Product>[];
for (final String barcode in selectedBarcodes) {
list.add((await daoProduct.get(barcode))!);
}
if (context.mounted) {
await Navigator.push<void>(
context,
MaterialPageRoute<void>(
builder: (BuildContext context) => CompareProducts3Page(
products: list,
orderedNutrientsCache: cache,
),
),
);
}
}
return false;
}
}

/// Popup menu item for the product list page: rank selected items.
class ProductListItemPopupRank extends ProductListItemPopupItem {
@override
String getTitle(final AppLocalizations appLocalizations) =>
appLocalizations.compare_products_mode;

@override
IconData getIconData() => Icons.compare_arrows;

@override
Future<bool> doSomething({
required final ProductList productList,
required final LocalDatabase localDatabase,
required final BuildContext context,
required final Iterable<String> selectedBarcodes,
}) async {
await Navigator.push<void>(
context,
MaterialPageRoute<void>(
builder: (_) => PersonalizedRankingPage(
barcodes: selectedBarcodes.toList(),
title: AppLocalizations.of(context).product_list_your_ranking,
),
),
);
return false;
}
}

/// Popup menu item for the product list page: delete selected items.
class ProductListItemPopupDelete extends ProductListItemPopupItem {
@override
String getTitle(final AppLocalizations appLocalizations) =>
'Delete selected items'; //appLocalizations.action_delete_list;

@override
IconData getIconData() => Icons.delete;

@override
Future<bool> doSomething({
required final ProductList productList,
required final LocalDatabase localDatabase,
required final BuildContext context,
required final Iterable<String> selectedBarcodes,
}) async {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
final DaoProductList daoProductList = DaoProductList(localDatabase);
final bool? letsDoIt = await showDialog<bool>(
context: context,
builder: (BuildContext context) => SmoothAlertDialog(
body: Container(
padding: const EdgeInsetsDirectional.only(start: SMALL_SPACE),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(appLocalizations.alert_clear_selected_user_list),
const SizedBox(height: SMALL_SPACE),
Text(appLocalizations.confirm_clear_selected_user_list),
],
),
),
positiveAction: SmoothActionButton(
onPressed: () async => Navigator.of(context).pop(true),
text: appLocalizations.yes,
),
negativeAction: SmoothActionButton(
onPressed: () => Navigator.of(context).pop(false),
text: appLocalizations.no,
),
),
);
if (letsDoIt != true) {
return false;
}
await daoProductList.bulkSet(
productList,
selectedBarcodes.toList(growable: false),
include: false,
);
await daoProductList.get(productList);
return true;
}
}
Loading

0 comments on commit d4bdca2

Please sign in to comment.