Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: #2244 - new "add categories" button + major product provider refactoring #2276

Merged
merged 1 commit into from
Jun 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/smooth_app/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -241,4 +241,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: e1ffd3daa5042cd516081f94f61b857c0deb822d

COCOAPODS: 1.11.3
COCOAPODS: 1.11.2
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:openfoodfacts/model/Product.dart';

/// Provider that reflects all the user changes on [Product]s.
class UpToDateProductProvider with ChangeNotifier {
final Map<String, Product> _map = <String, Product>{};

Product? get(final Product product) => _map[product.barcode!];

void set(
final Product product, {
final bool notify = true,
}) {
_map[product.barcode!] = product;
if (notify) {
notifyListeners();
}
}
}
185 changes: 76 additions & 109 deletions packages/smooth_app/lib/knowledge_panel/knowledge_panels_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,124 +14,51 @@ import 'package:smooth_app/pages/product/edit_ingredients_page.dart';
import 'package:smooth_app/pages/product/nutrition_page_loaded.dart';
import 'package:smooth_app/pages/product/ordered_nutrients_cache.dart';

/// Builds "knowledge panels" panels.
///
/// Panels display large data like all health data or environment data.
/// This is to be used publicly in the app
class KnowledgePanelsBuilder {
const KnowledgePanelsBuilder({
this.setState,
this.refreshProductCallback,
/// "Knowledge Panel" widget.
class KnowledgePanelWidget extends StatelessWidget {
const KnowledgePanelWidget({
required this.panelElement,
required this.knowledgePanels,
this.product,
});

/// Would for instance refresh the product page.
final VoidCallback? setState;
final KnowledgePanelElement panelElement;
final KnowledgePanels knowledgePanels;
final Product? product;

/// Callback to refresh the product when necessary.
final Function(BuildContext)? refreshProductCallback;

/// Builds all panels.
///
/// Typical use case: product page.
List<Widget> buildAll(
KnowledgePanels knowledgePanels, {
final Product? product,
final BuildContext? context,
}) {
final List<Widget> rootPanelWidgets = <Widget>[];
if (knowledgePanels.panelIdToPanelMap['root'] == null) {
return rootPanelWidgets;
}
if (knowledgePanels.panelIdToPanelMap['root']!.elements == null) {
return rootPanelWidgets;
}
for (final KnowledgePanelElement panelElement
in knowledgePanels.panelIdToPanelMap['root']!.elements!) {
if (panelElement.elementType != KnowledgePanelElementType.PANEL) {
continue;
}
rootPanelWidgets.add(
_buildPanel(
panelElement,
knowledgePanels,
context: context,
product: product,
),
);
}
return rootPanelWidgets;
}

/// Builds a single panel, if available.
///
/// Typical use case so far: onboarding, where we focus on one panel only.
Widget? buildSingle(
final KnowledgePanels knowledgePanels,
final String panelId, {
final BuildContext? context,
}) {
if (knowledgePanels.panelIdToPanelMap['root'] == null) {
return null;
}
if (knowledgePanels.panelIdToPanelMap['root']!.elements == null) {
return null;
}
for (final KnowledgePanelElement panelElement
in knowledgePanels.panelIdToPanelMap['root']!.elements!) {
if (panelElement.elementType != KnowledgePanelElementType.PANEL) {
continue;
}
if (panelId != panelElement.panelElement!.panelId) {
continue;
}
return _buildPanel(
panelElement,
knowledgePanels,
context: context,
);
}
return null;
}

Widget _buildPanel(
final KnowledgePanelElement panelElement,
final KnowledgePanels knowledgePanels, {
final Product? product,
final BuildContext? context,
}) {
@override
Widget build(BuildContext context) {
final String panelId = panelElement.panelElement!.panelId;
final KnowledgePanel rootPanel =
knowledgePanels.panelIdToPanelMap[panelId]!;
// [knowledgePanelElementWidgets] are a set of widgets inside the root panel.
final List<Widget> knowledgePanelElementWidgets = <Widget>[];
if (context != null) {
knowledgePanelElementWidgets.add(
Padding(
padding: const EdgeInsets.symmetric(vertical: VERY_SMALL_SPACE),
child: Text(
rootPanel.titleElement!.title,
style: Theme.of(context).textTheme.headline3,
),
final List<Widget> children = <Widget>[];
children.add(
Padding(
padding: const EdgeInsets.symmetric(vertical: VERY_SMALL_SPACE),
child: Text(
rootPanel.titleElement!.title,
style: Theme.of(context).textTheme.headline3,
),
);
}
),
);
for (final KnowledgePanelElement knowledgePanelElement
in rootPanel.elements ?? <KnowledgePanelElement>[]) {
knowledgePanelElementWidgets.add(
children.add(
KnowledgePanelElementCard(
knowledgePanelElement: knowledgePanelElement,
allPanels: knowledgePanels,
),
);
}
if (product != null && context != null) {
if (product != null) {
if (panelId == 'health_card') {
final bool nutritionAddOrUpdate = product.statesTags
final bool nutritionAddOrUpdate = product!.statesTags
?.contains('en:nutrition-facts-to-be-completed') ??
false;
final AppLocalizations appLocalizations = AppLocalizations.of(context);
if (nutritionAddOrUpdate) {
knowledgePanelElementWidgets.add(
children.add(
addPanelButton(
nutritionAddOrUpdate
? appLocalizations.score_add_missing_nutrition_facts
Expand All @@ -144,19 +71,15 @@ class KnowledgePanelsBuilder {
return;
}
//ignore: use_build_context_synchronously
final Product? refreshedProduct = await Navigator.push<Product>(
await Navigator.push<Product>(
context,
MaterialPageRoute<Product>(
builder: (BuildContext context) => NutritionPageLoaded(
product,
product!,
cache.orderedNutrients,
),
),
);
if (refreshedProduct != null) {
setState?.call();
}
// TODO(monsieurtanuki): refresh the data if changed
},
),
);
Expand All @@ -167,20 +90,19 @@ class KnowledgePanelsBuilder {
.getFlag(UserPreferencesDevMode
.userPreferencesFlagEditIngredients) ??
false;
if ((product.ingredientsText == null ||
product.ingredientsText!.isEmpty) &&
if ((product!.ingredientsText == null ||
product!.ingredientsText!.isEmpty) &&
needEditIngredients) {
// When the flag is removed, this should be the following:
// if (product.statesTags?.contains('en:ingredients-to-be-completed') ?? false) {
knowledgePanelElementWidgets.add(
children.add(
addPanelButton(
appLocalizations.score_add_missing_ingredients,
onPressed: () async => Navigator.push<bool>(
context,
MaterialPageRoute<bool>(
builder: (BuildContext context) => EditIngredientsPage(
product: product,
refreshProductCallback: refreshProductCallback,
product: product!,
),
),
),
Expand All @@ -191,7 +113,52 @@ class KnowledgePanelsBuilder {
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: knowledgePanelElementWidgets,
children: children,
);
}

/// Returns all the panel elements, in option only the one matching [panelId].
static List<KnowledgePanelElement> getPanelElements(
final KnowledgePanels knowledgePanels, {
final String? panelId,
}) {
final List<KnowledgePanelElement> result = <KnowledgePanelElement>[];
if (knowledgePanels.panelIdToPanelMap['root'] == null) {
return result;
}
if (knowledgePanels.panelIdToPanelMap['root']!.elements == null) {
return result;
}
for (final KnowledgePanelElement panelElement
in knowledgePanels.panelIdToPanelMap['root']!.elements!) {
if (panelElement.elementType != KnowledgePanelElementType.PANEL) {
continue;
}
// no filter
if (panelId == null) {
result.add(panelElement);
} else {
if (panelId == panelElement.panelElement!.panelId) {
result.add(panelElement);
return result;
}
}
}
return result;
}

/// Returns the unique panel element that matches [panelId], or `null`.
static KnowledgePanelElement? getPanelElement(
final KnowledgePanels knowledgePanels,
final String panelId,
) {
final List<KnowledgePanelElement> elements = getPanelElements(
knowledgePanels,
panelId: panelId,
);
if (elements.length != 1) {
return null;
}
return elements.first;
}
}
4 changes: 4 additions & 0 deletions packages/smooth_app/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:provider/provider.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:smooth_app/data_models/continuous_scan_model.dart';
import 'package:smooth_app/data_models/product_preferences.dart';
import 'package:smooth_app/data_models/up_to_date_product_provider.dart';
import 'package:smooth_app/data_models/user_management_provider.dart';
import 'package:smooth_app/data_models/user_preferences.dart';
import 'package:smooth_app/database/dao_string.dart';
Expand Down Expand Up @@ -76,6 +77,8 @@ late ProductPreferences _productPreferences;
late LocalDatabase _localDatabase;
late ThemeProvider _themeProvider;
final ContinuousScanModel _continuousScanModel = ContinuousScanModel();
final UpToDateProductProvider _upToDateProductProvider =
UpToDateProductProvider();
bool _init1done = false;

// Had to split init in 2 methods, for test/screenshots reasons.
Expand Down Expand Up @@ -178,6 +181,7 @@ class _SmoothAppState extends State<SmoothApp> {
provide<UserManagementProvider>(_userManagementProvider),
provide<ContinuousScanModel>(_continuousScanModel),
provide<SmoothAppDataImporter>(_appDataImporter),
provide<UpToDateProductProvider>(_upToDateProductProvider),
],
builder: _buildApp,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,13 @@ class _KnowledgePanelPageTemplateState
if (snapshot.connectionState != ConnectionState.done) {
return const Center(child: CircularProgressIndicator());
}
final Widget knowledgePanelWidget =
const KnowledgePanelsBuilder().buildSingle(
_knowledgePanels,
widget.panelId,
context: context,
)!;
final Widget knowledgePanelWidget = KnowledgePanelWidget(
panelElement: KnowledgePanelWidget.getPanelElement(
_knowledgePanels,
widget.panelId,
)!,
knowledgePanels: _knowledgePanels,
);
return Container(
color: widget.backgroundColor,
child: SafeArea(
Expand Down
Loading