Skip to content

Commit

Permalink
feat: 3941 - refactoring about up-to-date product data for StatefulWi…
Browse files Browse the repository at this point in the history
…dgets (#4262)
  • Loading branch information
monsieurtanuki authored Jul 13, 2023
1 parent 79aad85 commit 4f35708
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 291 deletions.
55 changes: 55 additions & 0 deletions packages/smooth_app/lib/data_models/up_to_date_mixin.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:smooth_app/background/background_task_manager.dart';
import 'package:smooth_app/database/local_database.dart';

/// Provides the most up-to-date local product data for a StatefulWidget.
///
/// Typically we have
/// * a product from the database (downloaded from the server)
/// * potentially pending changes to apply on top while they're being uploaded
///
/// With this mixin
/// * we get the most up-to-date local product data
/// * we re-launch the task manager if relevant
/// * we track the barcodes currently "opened" by the app
@optionalTypeArgs
mixin UpToDateMixin<T extends StatefulWidget> on State<T> {
/// To be used in the `initState` method.
void initUpToDate(
final Product initialProduct,
final LocalDatabase localDatabase,
) {
this.initialProduct = initialProduct;
_localDatabase = localDatabase;
localDatabase.upToDate.showInterest(barcode);
}

@protected
late final Product initialProduct;

late final LocalDatabase _localDatabase;

late Product _product;

@protected
String get barcode => initialProduct.barcode!;

@protected
Product get upToDateProduct => _product;

@override
void dispose() {
_localDatabase.upToDate.loseInterest(barcode);
super.dispose();
}

/// Refreshes [upToDateProduct] with the latest available local data.
///
/// To be used in the `build` method, after a call to
/// `context.watch<LocalDatabase>()`.
void refreshUpToDate() {
BackgroundTaskManager.getInstance(_localDatabase).run(); // no await
_product = _localDatabase.upToDate.getLocalUpToDate(initialProduct);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:matomo_tracker/matomo_tracker.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
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/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_card.dart';
Expand All @@ -26,29 +27,17 @@ class KnowledgePanelPage extends StatefulWidget {
}

class _KnowledgePanelPageState extends State<KnowledgePanelPage>
with TraceableClientMixin {
with TraceableClientMixin, UpToDateMixin {
@override
String get traceTitle => 'knowledge_panel_page';

@override
String get traceName => 'Opened full knowledge panel page';

late Product _product;
late final Product _initialProduct;
late final LocalDatabase _localDatabase;

@override
void initState() {
super.initState();
_initialProduct = widget.product;
_localDatabase = context.read<LocalDatabase>();
_localDatabase.upToDate.showInterest(_initialProduct.barcode!);
}

@override
void dispose() {
_localDatabase.upToDate.loseInterest(_initialProduct.barcode!);
super.dispose();
initUpToDate(widget.product, context.read<LocalDatabase>());
}

static KnowledgePanelPanelGroupElement? _groupElementOf(
Expand All @@ -63,7 +52,7 @@ class _KnowledgePanelPageState extends State<KnowledgePanelPage>
@override
Widget build(BuildContext context) {
context.watch<LocalDatabase>();
_product = _localDatabase.upToDate.getLocalUpToDate(_initialProduct);
refreshUpToDate();
return SmoothScaffold(
appBar: SmoothAppBar(
title: Text(
Expand All @@ -81,7 +70,7 @@ class _KnowledgePanelPageState extends State<KnowledgePanelPage>
),
child: KnowledgePanelExpandedCard(
panelId: widget.panelId,
product: _product,
product: upToDateProduct,
isInitiallyExpanded: true,
),
),
Expand Down Expand Up @@ -112,8 +101,10 @@ class _KnowledgePanelPageState extends State<KnowledgePanelPage>
groupElement?.title!.isNotEmpty == true) {
return groupElement!.title!;
}
final KnowledgePanel? panel =
KnowledgePanelWidget.getKnowledgePanel(_product, widget.panelId);
final KnowledgePanel? panel = KnowledgePanelWidget.getKnowledgePanel(
upToDateProduct,
widget.panelId,
);
if (panel?.titleElement?.title.isNotEmpty == true) {
return (panel?.titleElement?.title)!;
}
Expand Down
49 changes: 20 additions & 29 deletions packages/smooth_app/lib/pages/product/add_new_product_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:matomo_tracker/matomo_tracker.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/data_models/product_list.dart';
import 'package:smooth_app/data_models/up_to_date_mixin.dart';
import 'package:smooth_app/database/dao_product_list.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dart';
Expand Down Expand Up @@ -91,16 +92,13 @@ class AddNewProductPage extends StatefulWidget {
}

class _AddNewProductPageState extends State<AddNewProductPage>
with TraceableClientMixin {
with TraceableClientMixin, UpToDateMixin {
// Just one file per main image field
final Map<ImageField, File> _uploadedImages = <ImageField, File>{};

// Many possible files for "other" image field
final List<File> _otherUploadedImages = <File>[];

late Product _product;
late final Product _initialProduct;
late final LocalDatabase _localDatabase;
late DaoProductList _daoProductList;

final ProductList _history = ProductList.history();
Expand Down Expand Up @@ -145,29 +143,20 @@ class _AddNewProductPageState extends State<AddNewProductPage>
_detailsEditor,
_nutritionEditor,
];
_initialProduct = widget.product;
_localDatabase = context.read<LocalDatabase>();
_localDatabase.upToDate.showInterest(barcode);
_daoProductList = DaoProductList(_localDatabase);
final LocalDatabase localDatabase = context.read<LocalDatabase>();
initUpToDate(widget.product, localDatabase);
_daoProductList = DaoProductList(localDatabase);
AnalyticsHelper.trackEvent(
widget.events[EditProductAction.openPage]!,
barcode: barcode,
);
}

@override
void dispose() {
_localDatabase.upToDate.loseInterest(barcode);
super.dispose();
}

String get barcode => widget.product.barcode!;

@override
Widget build(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
context.watch<LocalDatabase>();
_product = _localDatabase.upToDate.getLocalUpToDate(_initialProduct);
refreshUpToDate();

_addToHistory();

Expand Down Expand Up @@ -203,7 +192,9 @@ class _AddNewProductPageState extends State<AddNewProductPage>
child: SmoothScaffold(
appBar: SmoothAppBar(
title: ListTile(
title: Text(_product.productName ?? appLocalizations.new_product),
title: Text(
upToDateProduct.productName ?? appLocalizations.new_product,
),
subtitle: Text(barcode),
),
),
Expand Down Expand Up @@ -235,8 +226,8 @@ class _AddNewProductPageState extends State<AddNewProductPage>
return;
}
if (_isPopulated) {
_product.productName = _product.productName?.trim();
_product.brands = _product.brands?.trim();
upToDateProduct.productName = upToDateProduct.productName?.trim();
upToDateProduct.brands = upToDateProduct.brands?.trim();
await _daoProductList.push(_history, barcode);
_alreadyPushedToHistory = true;
}
Expand All @@ -245,7 +236,7 @@ class _AddNewProductPageState extends State<AddNewProductPage>
/// Returns true if at least one field is populated.
bool get _isPopulated {
for (final ProductFieldEditor editor in _editors) {
if (editor.isPopulated(_product)) {
if (editor.isPopulated(upToDateProduct)) {
return true;
}
}
Expand All @@ -266,7 +257,7 @@ class _AddNewProductPageState extends State<AddNewProductPage>
);

Attribute? _getAttribute(final String tag) =>
_product.getAttributes(<String>[tag])[tag];
upToDateProduct.getAttributes(<String>[tag])[tag];

List<Widget> _getNutriscoreRows(final BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
Expand Down Expand Up @@ -340,7 +331,7 @@ class _AddNewProductPageState extends State<AddNewProductPage>
_buildIngredientsButton(
context,
forceIconData: Icons.filter_2,
disabled: !_categoryEditor.isPopulated(_product),
disabled: !_categoryEditor.isPopulated(upToDateProduct),
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
Expand Down Expand Up @@ -428,7 +419,7 @@ class _AddNewProductPageState extends State<AddNewProductPage>

Widget _buildNutritionInputButton(final BuildContext context) {
if (!_trackedPopulatedNutrition) {
if (_nutritionEditor.isPopulated(_product)) {
if (_nutritionEditor.isPopulated(upToDateProduct)) {
_trackedPopulatedNutrition = true;
AnalyticsHelper.trackEvent(
widget.events[EditProductAction.nutritionFacts]!,
Expand All @@ -440,7 +431,7 @@ class _AddNewProductPageState extends State<AddNewProductPage>
context,
_nutritionEditor,
forceIconData: Icons.filter_2,
disabled: !_categoryEditor.isPopulated(_product),
disabled: !_categoryEditor.isPopulated(upToDateProduct),
);
}

Expand All @@ -450,15 +441,15 @@ class _AddNewProductPageState extends State<AddNewProductPage>
final IconData? forceIconData,
final bool disabled = false,
}) {
final bool done = editor.isPopulated(_product);
final bool done = editor.isPopulated(upToDateProduct);
return _MyButton(
editor.getLabel(AppLocalizations.of(context)),
forceIconData ?? (done ? _doneIcon : _todoIcon),
disabled
? null
: () async => editor.edit(
context: context,
product: _product,
product: upToDateProduct,
isLoggedInMandatory: widget.isLoggedInMandatory,
),
done: done,
Expand All @@ -467,7 +458,7 @@ class _AddNewProductPageState extends State<AddNewProductPage>

Widget _buildCategoriesButton(final BuildContext context) {
if (!_trackedPopulatedCategories) {
if (_categoryEditor.isPopulated(_product)) {
if (_categoryEditor.isPopulated(upToDateProduct)) {
_trackedPopulatedCategories = true;
AnalyticsHelper.trackEvent(
widget.events[EditProductAction.category]!,
Expand Down Expand Up @@ -499,7 +490,7 @@ class _AddNewProductPageState extends State<AddNewProductPage>
final bool disabled = false,
}) {
if (!_trackedPopulatedIngredients) {
if (_ingredientsEditor.isPopulated(_product)) {
if (_ingredientsEditor.isPopulated(upToDateProduct)) {
_trackedPopulatedIngredients = true;
AnalyticsHelper.trackEvent(
widget.events[EditProductAction.ingredients]!,
Expand Down
Loading

0 comments on commit 4f35708

Please sign in to comment.