Skip to content

Commit

Permalink
The access to Robotoff from the Product page is only via the banner (…
Browse files Browse the repository at this point in the history
…= the button is removed) (#5940)
  • Loading branch information
g123k authored Nov 27, 2024
1 parent 4750ff0 commit 538e14b
Show file tree
Hide file tree
Showing 7 changed files with 19 additions and 200 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,6 @@ class _UserPreferencesMigrationV2 extends UserPreferencesMigration {
int newVersion,
) async {
/// With version == null and 1, [_TAG_USER_GROUP] is missing
if (preferences._sharedPreferences
.getInt(UserPreferences._TAG_USER_GROUP) ==
null) {
await preferences._sharedPreferences.setInt(
UserPreferences._TAG_USER_GROUP,
math.Random().nextInt(10),
);
}
}

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ class UserPreferences extends ChangeNotifier {
static const String _TAG_CRASH_REPORTS = 'crash_reports';
static const String _TAG_PRICES_FEEDBACK_FORM = 'prices_feedback_form';
static const String _TAG_EXCLUDED_ATTRIBUTE_IDS = 'excluded_attributes';
static const String _TAG_USER_GROUP = '_user_group';
static const String _TAG_UNIQUE_RANDOM = '_unique_random';
static const String _TAG_LAZY_COUNT_PREFIX = '_lazy_count_prefix';
static const String _TAG_LATEST_PRODUCT_TYPE = '_latest_product_type';
Expand Down Expand Up @@ -212,9 +211,6 @@ class UserPreferences extends ChangeNotifier {
bool get userTracking =>
_sharedPreferences.getBool(_TAG_USER_TRACKING) ?? false;

/// A random int between 0 and 10 (a naive implementation to allow A/B testing)
int get userGroup => _sharedPreferences.getInt(_TAG_USER_GROUP)!;

/// Returns a huge random value that will be computed just once.
Future<int> getUniqueRandom() async {
const String tag = _TAG_UNIQUE_RANDOM;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ class _HelperState extends State<_Helper> {
isRemovable: false,
isSettingVisible: false,
isProductEditable: false,
showQuestionsBanner: false,
isPictureVisible: false,
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/data_models/preferences/user_preferences.dart';
import 'package:smooth_app/helpers/analytics_helper.dart';
import 'package:smooth_app/helpers/global_vars.dart';
import 'package:smooth_app/query/product_query.dart';
Expand Down Expand Up @@ -41,11 +39,6 @@ class _UserPreferencesDebugInfoState extends State<UserPreferencesDebugInfo> {

// TODO(m123): Add sentry id https://github.com/getsentry/sentry-dart/issues/1205
Future<void> loadAsyncData() async {
infos.putIfAbsent(
'User group',
() => context.read<UserPreferences>().userGroup,
);

final BaseDeviceInfo deviceInfo = await DeviceInfoPlugin().deviceInfo;

if (deviceInfo is AndroidDeviceInfo) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ class ProductPageState extends State<ProductPage>
with TraceableClientMixin, UpToDateMixin {
final ScrollController _scrollController = ScrollController();
late ProductPreferences _productPreferences;
late ProductQuestionsLayout questionsLayout;
bool _keepRobotoffQuestionsAlive = true;

double bottomPadding = 0.0;
Expand All @@ -70,7 +69,6 @@ class ProductPageState extends State<ProductPage>
final LocalDatabase localDatabase = context.read<LocalDatabase>();
initUpToDate(widget.product, localDatabase);
DaoProductLastAccess(localDatabase).put(barcode);
questionsLayout = getUserQuestionsLayout(context.read<UserPreferences>());
WidgetsBinding.instance.addPostFrameCallback((_) {
_updateLocalDatabaseWithProductHistory(context);
});
Expand Down Expand Up @@ -127,23 +125,21 @@ class ProductPageState extends State<ProductPage>
backButtonType: widget.backButton,
),
),
if (questionsLayout == ProductQuestionsLayout.banner)
Positioned(
left: 0.0,
right: 0.0,
bottom: 0.0,
child: MeasureSize(
onChange: (Size size) {
if (size.height != bottomPadding) {
setState(() => bottomPadding = size.height);
}
},
child: ProductQuestionsWidget(
upToDateProduct,
layout: ProductQuestionsLayout.banner,
),
Positioned(
left: 0.0,
right: 0.0,
bottom: 0.0,
child: MeasureSize(
onChange: (Size size) {
if (size.height != bottomPadding) {
setState(() => bottomPadding = size.height);
}
},
child: ProductQuestionsWidget(
upToDateProduct,
),
),
),
],
),
bottomNavigationBar: const ProductFooter(),
Expand Down Expand Up @@ -200,7 +196,6 @@ class ProductPageState extends State<ProductPage>
_productPreferences,
heroTag: widget.heroTag,
isFullVersion: true,
showQuestionsBanner: true,
),
),
),
Expand Down
153 changes: 6 additions & 147 deletions packages/smooth_app/lib/pages/product/product_questions_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:provider/provider.dart';
import 'package:shimmer/shimmer.dart';
import 'package:smooth_app/data_models/preferences/user_preferences.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/duration_constants.dart';
Expand All @@ -15,13 +14,9 @@ import 'package:smooth_app/query/product_questions_query.dart';
import 'package:smooth_app/themes/theme_provider.dart';

class ProductQuestionsWidget extends StatefulWidget {
const ProductQuestionsWidget(
this.product, {
this.layout = ProductQuestionsLayout.button,
});
const ProductQuestionsWidget(this.product);

final Product product;
final ProductQuestionsLayout layout;

@override
State<ProductQuestionsWidget> createState() => _ProductQuestionsWidgetState();
Expand Down Expand Up @@ -72,16 +67,10 @@ class _ProductQuestionsWidgetState extends State<ProductQuestionsWidget>
// Mandatory to call with an [AutomaticKeepAliveClientMixin]
super.build(context);

return switch (widget.layout) {
ProductQuestionsLayout.button => _ProductQuestionButton(
state: _state,
openQuestionsCallback: _openQuestions,
),
ProductQuestionsLayout.banner => _ProductQuestionBanner(
state: _state,
openQuestionsCallback: _openQuestions,
),
};
return _ProductQuestionBanner(
state: _state,
openQuestionsCallback: _openQuestions,
);
}

Future<void> _openQuestions() async {
Expand Down Expand Up @@ -124,10 +113,7 @@ class _ProductQuestionsWidgetState extends State<ProductQuestionsWidget>

void _trackEvent(AnalyticsEvent event) => AnalyticsHelper.trackProductEvent(
event,
eventValue: switch (widget.layout) {
ProductQuestionsLayout.button => 0,
ProductQuestionsLayout.banner => 1,
},
eventValue: 1,
product: widget.product,
);

Expand Down Expand Up @@ -176,133 +162,6 @@ class _ProductQuestionsWidgetState extends State<ProductQuestionsWidget>
bool get wantKeepAlive => _keepWidgetAlive;
}

/// A naive implementation to have a half of the user base using a button and
/// the other half, the banner
ProductQuestionsLayout getUserQuestionsLayout(UserPreferences preferences) {
return preferences.userGroup.isEven
? ProductQuestionsLayout.button
: ProductQuestionsLayout.banner;
}

enum ProductQuestionsLayout {
button,
banner,
}

class _ProductQuestionButton extends StatelessWidget {
const _ProductQuestionButton({
required this.state,
required this.openQuestionsCallback,
});

final _ProductQuestionsState state;
final VoidCallback openQuestionsCallback;

@override
Widget build(BuildContext context) {
return AnimatedCrossFade(
crossFadeState: state is _ProductQuestionsWithoutQuestions
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
duration: SmoothAnimationsDuration.long,
firstChild: EMPTY_WIDGET,
secondChild: Builder(builder: (BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
final Widget child = _buildContent(context, appLocalizations);

// We need to differentiate with / without a Shimmer, because
// [Shimmer] doesn't support [Ink]
final Color backgroundColor = Theme.of(context).colorScheme.primary;

if (state is _ProductQuestionsWithQuestions) {
return Semantics(
value: appLocalizations.tap_to_answer_hint,
button: true,
excludeSemantics: true,
child: InkWell(
borderRadius: ANGULAR_BORDER_RADIUS,
onTap: openQuestionsCallback,
child: Ink(
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: ANGULAR_BORDER_RADIUS,
),
padding: const EdgeInsetsDirectional.all(
SMALL_SPACE,
),
child: child,
),
),
);
} else {
return Semantics(
value: appLocalizations.robotoff_questions_loading_hint,
excludeSemantics: true,
child: Shimmer.fromColors(
baseColor: backgroundColor,
highlightColor: WHITE_COLOR.withOpacity(0.5),
period: SmoothAnimationsDuration.long * 2,
child: Container(
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: ANGULAR_BORDER_RADIUS,
),
padding: const EdgeInsetsDirectional.all(
SMALL_SPACE,
),
child: child,
),
),
);
}
}),
);
}

Widget _buildContent(
BuildContext context,
AppLocalizations appLocalizations,
) {
final bool isDarkMode = Theme.of(context).brightness == Brightness.dark;

return SizedBox(
width: double.infinity,
child: Column(
children: <Widget>[
Row(
children: <Widget>[
const _ProductQuestionIcon(),
Expanded(
child: RichText(
text: TextSpan(
text: '${appLocalizations.tap_to_answer}\n',
style:
Theme.of(context).primaryTextTheme.bodyLarge!.copyWith(
color: isDarkMode ? Colors.black : WHITE_COLOR,
height: 1.5,
),
children: <TextSpan>[
TextSpan(
text: appLocalizations.contribute_to_get_rewards,
style: Theme.of(context)
.primaryTextTheme
.bodyMedium!
.copyWith(
color: isDarkMode ? Colors.black : WHITE_COLOR,
),
),
],
),
),
),
],
),
],
),
);
}
}

class _ProductQuestionBanner extends StatelessWidget {
const _ProductQuestionBanner({
required this.state,
Expand Down
15 changes: 0 additions & 15 deletions packages/smooth_app/lib/pages/product/summary_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@ import 'package:smooth_app/helpers/product_cards_helper.dart';
import 'package:smooth_app/helpers/ui_helpers.dart';
import 'package:smooth_app/knowledge_panel/knowledge_panels/knowledge_panel_page.dart';
import 'package:smooth_app/knowledge_panel/knowledge_panels_builder.dart';
import 'package:smooth_app/pages/hunger_games/question_card.dart';
import 'package:smooth_app/pages/product/hideable_container.dart';
import 'package:smooth_app/pages/product/product_field_editor.dart';
import 'package:smooth_app/pages/product/product_incomplete_card.dart';
import 'package:smooth_app/pages/product/product_questions_widget.dart';
import 'package:smooth_app/pages/product/summary_attribute_group.dart';
import 'package:smooth_app/resources/app_icons.dart' as icons;
import 'package:smooth_app/themes/smooth_theme.dart';
Expand All @@ -42,7 +40,6 @@ class SummaryCard extends StatefulWidget {
this._product,
this._productPreferences, {
this.isFullVersion = false,
this.showQuestionsBanner = false,
this.isRemovable = true,
this.isSettingVisible = true,
this.isProductEditable = true,
Expand All @@ -64,9 +61,6 @@ class SummaryCard extends StatefulWidget {
/// Buttons should only be visible in full mode
final bool isFullVersion;

/// If true, show the [QuestionCard] if there are questions for the product.
final bool showQuestionsBanner;

/// If true, there will be a button to remove the product from the carousel.
final bool isRemovable;

Expand Down Expand Up @@ -103,13 +97,11 @@ class SummaryCard extends StatefulWidget {
class _SummaryCardState extends State<SummaryCard> with UpToDateMixin {
// For some reason, special case for "label" attributes
final Set<String> _attributesToExcludeIfStatusIsUnknown = <String>{};
late ProductQuestionsLayout _questionsLayout;

@override
void initState() {
super.initState();
initUpToDate(widget._product, context.read<LocalDatabase>());
_questionsLayout = getUserQuestionsLayout(context.read<UserPreferences>());
if (ProductIncompleteCard.isProductIncomplete(upToDateProduct)) {
AnalyticsHelper.trackProductEvent(
AnalyticsEvent.showFastTrackProductEditCard,
Expand Down Expand Up @@ -327,13 +319,6 @@ class _SummaryCardState extends State<SummaryCard> with UpToDateMixin {
if (ProductIncompleteCard.isProductIncomplete(upToDateProduct))
ProductIncompleteCard(product: upToDateProduct),
..._getAttributes(scoreAttributes),
if (widget.isFullVersion &&
widget.showQuestionsBanner &&
_questionsLayout == ProductQuestionsLayout.button)
ProductQuestionsWidget(
upToDateProduct,
layout: ProductQuestionsLayout.button,
),
attributesContainer,
...summaryCardButtons,
],
Expand Down

0 comments on commit 538e14b

Please sign in to comment.