From 7ac86dc4f8ee11046345d69ab91fad98813373e6 Mon Sep 17 00:00:00 2001 From: monsieurtanuki Date: Tue, 20 Jun 2023 17:44:16 +0200 Subject: [PATCH] feat: 3843 - matomo for new product page (#4217) * feat: 3843 - matomo for new product page Impacted files: * `add_new_product_page.dart`: implemented the tracking of 6 events for matomo * `analytics_helper.dart`: added 1 category and 6 related events for the "new product page" * feat: 3843 - TraceableClientMixin --- .../lib/helpers/analytics_helper.dart | 25 ++++ .../pages/product/add_new_product_page.dart | 119 ++++++++++++++---- 2 files changed, 121 insertions(+), 23 deletions(-) diff --git a/packages/smooth_app/lib/helpers/analytics_helper.dart b/packages/smooth_app/lib/helpers/analytics_helper.dart index d7bd98786de..b30584086d1 100644 --- a/packages/smooth_app/lib/helpers/analytics_helper.dart +++ b/packages/smooth_app/lib/helpers/analytics_helper.dart @@ -16,6 +16,7 @@ enum AnalyticsCategory { share(tag: 'share'), couldNotFindProduct(tag: 'could not find product'), productEdit(tag: 'product edit'), + newProduct(tag: 'new product'), list(tag: 'list'), deepLink(tag: 'deep link'); @@ -47,6 +48,30 @@ enum AnalyticsEvent { tag: 'opened product edit page', category: AnalyticsCategory.productEdit, ), + openNewProductPage( + tag: 'opened new product page', + category: AnalyticsCategory.newProduct, + ), + categoriesNewProductPage( + tag: 'set categories on new product page', + category: AnalyticsCategory.newProduct, + ), + nutritionNewProductPage( + tag: 'set nutrition facts on new product page', + category: AnalyticsCategory.newProduct, + ), + ingredientsNewProductPage( + tag: 'set ingredients on new product page', + category: AnalyticsCategory.newProduct, + ), + imagesNewProductPage( + tag: 'set at least one image on new product page', + category: AnalyticsCategory.newProduct, + ), + closeEmptyNewProductPage( + tag: 'closed new product page without any input', + category: AnalyticsCategory.newProduct, + ), shareList(tag: 'shared a list', category: AnalyticsCategory.list), openListWeb(tag: 'open a list in wbe', category: AnalyticsCategory.list), productDeepLink( diff --git a/packages/smooth_app/lib/pages/product/add_new_product_page.dart b/packages/smooth_app/lib/pages/product/add_new_product_page.dart index e43d54c0945..39e54362ac1 100644 --- a/packages/smooth_app/lib/pages/product/add_new_product_page.dart +++ b/packages/smooth_app/lib/pages/product/add_new_product_page.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +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'; @@ -12,6 +13,7 @@ import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; import 'package:smooth_app/generic_lib/svg_icon_chip.dart'; import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; +import 'package:smooth_app/helpers/analytics_helper.dart'; import 'package:smooth_app/helpers/image_field_extension.dart'; import 'package:smooth_app/pages/image_crop_page.dart'; import 'package:smooth_app/pages/product/common/product_dialog_helper.dart'; @@ -44,7 +46,8 @@ class AddNewProductPage extends StatefulWidget { State createState() => _AddNewProductPageState(); } -class _AddNewProductPageState extends State { +class _AddNewProductPageState extends State + with TraceableClientMixin { // Just one file per main image field final Map _uploadedImages = {}; @@ -76,6 +79,17 @@ class _AddNewProductPageState extends State { bool _ecoscoreExpanded = false; + bool _trackedPopulatedCategories = false; + bool _trackedPopulatedIngredients = false; + bool _trackedPopulatedNutrition = false; + bool _trackedPopulatedImages = false; + + @override + String get traceName => 'Opened add_new_product_page'; + + @override + String get traceTitle => 'add_new_product_page'; + @override void initState() { super.initState(); @@ -91,6 +105,10 @@ class _AddNewProductPageState extends State { _localDatabase = context.read(); _localDatabase.upToDate.showInterest(barcode); _daoProductList = DaoProductList(_localDatabase); + AnalyticsHelper.trackEvent( + AnalyticsEvent.openNewProductPage, + barcode: barcode, + ); } @override @@ -130,6 +148,12 @@ class _AddNewProductPageState extends State { ), ), ); + if (leaveThePage == true) { + AnalyticsHelper.trackEvent( + AnalyticsEvent.closeEmptyNewProductPage, + barcode: barcode, + ); + } return leaveThePage ?? false; }, child: SmoothScaffold( @@ -251,7 +275,7 @@ class _AddNewProductPageState extends State { if (_ecoscoreExpanded) _buildEditorButton(context, _originEditor), if (_ecoscoreExpanded) _buildEditorButton(context, _labelEditor), if (_ecoscoreExpanded) _buildEditorButton(context, _packagingEditor), - if (_ecoscoreExpanded) _buildEditorButton(context, _ingredientsEditor), + if (_ecoscoreExpanded) _buildIngredientsButton(context), ]; } @@ -268,9 +292,8 @@ class _AddNewProductPageState extends State { style: _getSubtitleStyle(context), ), _buildCategoriesButton(context), - _buildEditorButton( + _buildIngredientsButton( context, - _ingredientsEditor, forceIconData: Icons.filter_2, disabled: !_categoryEditor.isPopulated(_product), ), @@ -340,6 +363,13 @@ class _AddNewProductPageState extends State { ); if (finalPhoto != null) { setState(() { + if (!_trackedPopulatedImages) { + _trackedPopulatedImages = true; + AnalyticsHelper.trackEvent( + AnalyticsEvent.imagesNewProductPage, + barcode: barcode, + ); + } if (imageField == ImageField.OTHER) { _otherUploadedImages.add(finalPhoto); } else { @@ -351,19 +381,30 @@ class _AddNewProductPageState extends State { done: imageFile != null, ); - Widget _buildNutritionInputButton(final BuildContext context) => _MyButton( - AppLocalizations.of(context).nutritional_facts_input_button_label, - Icons.filter_2, - // deactivated when the categories were not set beforehand - !_categoryEditor.isPopulated(_product) - ? null - : () async => NutritionPageLoaded.showNutritionPage( - product: _product, - isLoggedInMandatory: false, - widget: this, - ), - done: _nutritionFactsAdded, - ); + Widget _buildNutritionInputButton(final BuildContext context) { + if (!_trackedPopulatedNutrition) { + if (_nutritionFactsAdded) { + _trackedPopulatedNutrition = true; + AnalyticsHelper.trackEvent( + AnalyticsEvent.nutritionNewProductPage, + barcode: barcode, + ); + } + } + return _MyButton( + AppLocalizations.of(context).nutritional_facts_input_button_label, + Icons.filter_2, + // deactivated when the categories were not set beforehand + !_categoryEditor.isPopulated(_product) + ? null + : () async => NutritionPageLoaded.showNutritionPage( + product: _product, + isLoggedInMandatory: false, + widget: this, + ), + done: _nutritionFactsAdded, + ); + } Widget _buildEditorButton( final BuildContext context, @@ -386,12 +427,22 @@ class _AddNewProductPageState extends State { ); } - Widget _buildCategoriesButton(final BuildContext context) => - _buildEditorButton( - context, - _categoryEditor, - forceIconData: Icons.filter_1, - ); + Widget _buildCategoriesButton(final BuildContext context) { + if (!_trackedPopulatedCategories) { + if (_categoryEditor.isPopulated(_product)) { + _trackedPopulatedCategories = true; + AnalyticsHelper.trackEvent( + AnalyticsEvent.categoriesNewProductPage, + barcode: barcode, + ); + } + } + return _buildEditorButton( + context, + _categoryEditor, + forceIconData: Icons.filter_1, + ); + } List _getMiscRows(final BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); @@ -403,6 +454,28 @@ class _AddNewProductPageState extends State { _buildEditorButton(context, _detailsEditor), ]; } + + Widget _buildIngredientsButton( + final BuildContext context, { + final IconData? forceIconData, + final bool disabled = false, + }) { + if (!_trackedPopulatedIngredients) { + if (_ingredientsEditor.isPopulated(_product)) { + _trackedPopulatedIngredients = true; + AnalyticsHelper.trackEvent( + AnalyticsEvent.ingredientsNewProductPage, + barcode: barcode, + ); + } + } + return _buildEditorButton( + context, + _ingredientsEditor, + forceIconData: forceIconData, + disabled: disabled, + ); + } } /// Standard button.