From 1096948f0d474c515e690f07116264d20dfa3aac Mon Sep 17 00:00:00 2001 From: monsieurtanuki Date: Tue, 19 Sep 2023 08:40:25 +0200 Subject: [PATCH] feat: 4513 - new "preferences search" page from dev mode (#4640) * feat: 4513 - new "preferences search page" from dev mode Deleted files: * `user_preferences_camera_sound.dart` * `user_preferences_crash_reporting.dart` * `user_preferences_haptic_feedback.dart` * `user_preferences_send_anonymous.dart` New files: * `user_preferences_item.dart`: Item for preferences, with labels for pre-filtering and widget builder. * `user_preferences_search_page.dart`: Search page for preferences, with TextField filter. Impacted files: * `abstract_user_preferences.dart`: new `getLabels` method for filtering; children are now `UserPreferencesItem` for filtering; minor refactoring. * `user_preferences_account.dart`: children are now `UserPreferencesItem`; minor refactoring * `user_preferences_advanced_settings.dart`: added a static `UserPreferencesItem` getter * `user_preferences_attribute_group.dart`: children are now `UserPreferencesItem`; minor refactoring * `user_preferences_choose_accent_color.dart`: added a static `UserPreferencesItem` getter * `user_preferences_choose_app_theme.dart`: added a static `UserPreferencesItem` getter * `user_preferences_choose_text_color_contrast.dart`: added a static `UserPreferencesItem` getter * `user_preferences_connect.dart`: children are now `UserPreferencesItem`; minor refactoring * `user_preferences_contribute.dart`: children are now `UserPreferencesItem`; minor refactoring * `user_preferences_country_selector.dart`: added a static `UserPreferencesItem` getter * `user_preferences_dev_mode.dart`: children are now `UserPreferencesItem`; minor refactoring * `user_preferences_faq.dart`: children are now `UserPreferencesItem`; minor refactoring * `user_preferences_food.dart`: children are now `UserPreferencesItem`; minor refactoring * `user_preferences_image_source.dart`: added a static `UserPreferencesItem` getter * `user_preferences_language_selector.dart`: added a static `UserPreferencesItem` getter * `user_preferences_page.dart`: moved `getUserPreferences` to `PreferencePageType`; minor refactoring * `user_preferences_rate_us.dart`: added a static `UserPreferencesItem` getter * `user_preferences_settings.dart`: children are now `UserPreferencesItem`; minor refactoring * `user_preferences_share_with_friends.dart`: added a static `UserPreferencesItem` getter * `user_preferences_widgets.dart`: new classes `UserPreferencesItemSwitch` and `UserPreferencesItemTile` * Update packages/smooth_app/lib/pages/preferences/user_preferences_page.dart --- .../abstract_user_preferences.dart | 19 +- .../preferences/user_preferences_account.dart | 137 ++++++------ .../user_preferences_advanced_settings.dart | 14 ++ .../user_preferences_attribute_group.dart | 104 +++++++-- .../user_preferences_camera_sound.dart | 22 -- .../user_preferences_choose_accent_color.dart | 19 +- .../user_preferences_choose_app_theme.dart | 17 ++ ...references_choose_text_color_contrast.dart | 16 ++ .../preferences/user_preferences_connect.dart | 42 ++-- .../user_preferences_contribute.dart | 177 +++++++-------- .../user_preferences_country_selector.dart | 13 ++ .../user_preferences_crash_reporting.dart | 22 -- .../user_preferences_dev_mode.dart | 151 ++++++------- .../preferences/user_preferences_faq.dart | 53 ++--- .../preferences/user_preferences_food.dart | 69 +++--- .../user_preferences_haptic_feedback.dart | 22 -- .../user_preferences_image_source.dart | 16 ++ .../preferences/user_preferences_item.dart | 21 ++ .../user_preferences_language_selector.dart | 13 ++ .../preferences/user_preferences_page.dart | 172 +++++++------- .../preferences/user_preferences_rate_us.dart | 15 +- .../user_preferences_search_page.dart | 110 +++++++++ .../user_preferences_send_anonymous.dart | 22 -- .../user_preferences_settings.dart | 209 ++++++++++++------ .../user_preferences_share_with_friends.dart | 13 ++ .../preferences/user_preferences_widgets.dart | 156 +++++++------ 26 files changed, 991 insertions(+), 653 deletions(-) delete mode 100644 packages/smooth_app/lib/pages/preferences/user_preferences_camera_sound.dart delete mode 100644 packages/smooth_app/lib/pages/preferences/user_preferences_crash_reporting.dart delete mode 100644 packages/smooth_app/lib/pages/preferences/user_preferences_haptic_feedback.dart create mode 100644 packages/smooth_app/lib/pages/preferences/user_preferences_item.dart create mode 100644 packages/smooth_app/lib/pages/preferences/user_preferences_search_page.dart delete mode 100644 packages/smooth_app/lib/pages/preferences/user_preferences_send_anonymous.dart diff --git a/packages/smooth_app/lib/pages/preferences/abstract_user_preferences.dart b/packages/smooth_app/lib/pages/preferences/abstract_user_preferences.dart index b6ab289763c..b528c5a91c7 100644 --- a/packages/smooth_app/lib/pages/preferences/abstract_user_preferences.dart +++ b/packages/smooth_app/lib/pages/preferences/abstract_user_preferences.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:smooth_app/data_models/preferences/user_preferences.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_list_tile.dart'; import 'package:smooth_app/pages/preferences/user_preferences_page.dart'; import 'package:smooth_app/themes/constant_icons.dart'; @@ -23,6 +24,9 @@ abstract class AbstractUserPreferences { @protected PreferencePageType getPreferencePageType(); + /// Title of the preference page. + String getPageTitleString() => getTitleString(); + /// Title of the header, always visible. String getTitleString(); @@ -35,7 +39,18 @@ abstract class AbstractUserPreferences { /// Subtitle of the header, always visible. @protected - Widget? getSubtitle(); + String? getSubtitleString() => null; + + /// Subtitle of the header, always visible. + @protected + Widget? getSubtitle() => + getSubtitleString() == null ? null : Text(getSubtitleString()!); + + List getLabels() => [ + getPageTitleString(), + getTitleString(), + if (getSubtitleString() != null) getSubtitleString()!, + ]; Widget getOnlyHeader() => InkWell( onTap: () async => runHeaderAction(), @@ -69,7 +84,7 @@ abstract class AbstractUserPreferences { IconData getLeadingIconData(); /// Body of the content. - List getBody(); + List getChildren(); /// Returns the action when we tap on the header. @protected diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_account.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_account.dart index 36e46572e23..30db63f15bb 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_account.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_account.dart @@ -13,6 +13,7 @@ import 'package:smooth_app/helpers/launch_url_helper.dart'; import 'package:smooth_app/helpers/user_management_helper.dart'; import 'package:smooth_app/pages/preferences/abstract_user_preferences.dart'; import 'package:smooth_app/pages/preferences/account_deletion_webview.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_list_tile.dart'; import 'package:smooth_app/pages/preferences/user_preferences_page.dart'; import 'package:smooth_app/pages/product/common/product_query_page_helper.dart'; @@ -42,31 +43,31 @@ class UserPreferencesAccount extends AbstractUserPreferences { String? _getUserId() => OpenFoodAPIConfiguration.globalUser?.userId; @override - Widget getTitle() { + String getTitleString() { final String? userId = _getUserId(); - final String title; if (userId == null) { - title = appLocalizations.user_profile_title_guest; - } else if (userId.isEmail) { - title = appLocalizations.user_profile_title_id_email(userId); - } else { - title = appLocalizations.user_profile_title_id_default(userId); + return appLocalizations.user_profile_title_guest; } - - return Text( - title, - style: Theme.of(context).textTheme.displayMedium, - ); + if (userId.isEmail) { + return appLocalizations.user_profile_title_id_email(userId); + } + return appLocalizations.user_profile_title_id_default(userId); } @override - String getTitleString() => appLocalizations.myPreferences_profile_title; + String getPageTitleString() => appLocalizations.myPreferences_profile_title; @override - Widget? getSubtitle() => _isUserConnected() - ? Text(appLocalizations.myPreferences_profile_subtitle) - : Text(appLocalizations.user_profile_subtitle_guest); + String getSubtitleString() => _isUserConnected() + ? appLocalizations.myPreferences_profile_subtitle + : appLocalizations.user_profile_subtitle_guest; + + @override + List getLabels() => [ + ...super.getLabels(), + if (_getUserId() == null) appLocalizations.sign_in, + ]; @override IconData getLeadingIconData() => Icons.face; @@ -127,30 +128,33 @@ class UserPreferencesAccount extends AbstractUserPreferences { ); @override - List getBody() { + List getChildren() { if (OpenFoodAPIConfiguration.globalUser == null) { // No credentials final Size size = MediaQuery.of(context).size; - return [ - Center( - child: ElevatedButton( - onPressed: () async => _goToLoginPage(), - style: ButtonStyle( - minimumSize: MaterialStateProperty.all( - Size(size.width * 0.5, themeData.buttonTheme.height + 10), - ), - shape: MaterialStateProperty.all( - const RoundedRectangleBorder( - borderRadius: CIRCULAR_BORDER_RADIUS, + return [ + UserPreferencesItemSimple( + labels: [appLocalizations.sign_in], + builder: (_) => Center( + child: ElevatedButton( + onPressed: () async => _goToLoginPage(), + style: ButtonStyle( + minimumSize: MaterialStateProperty.all( + Size(size.width * 0.5, themeData.buttonTheme.height + 10), + ), + shape: MaterialStateProperty.all( + const RoundedRectangleBorder( + borderRadius: CIRCULAR_BORDER_RADIUS, + ), ), ), - ), - child: Text( - appLocalizations.sign_in, - style: themeData.textTheme.bodyMedium?.copyWith( - fontSize: 18.0, - fontWeight: FontWeight.bold, - color: themeData.colorScheme.onPrimary, + child: Text( + appLocalizations.sign_in, + style: themeData.textTheme.bodyMedium?.copyWith( + fontSize: 18.0, + fontWeight: FontWeight.bold, + color: themeData.colorScheme.onPrimary, + ), ), ), ), @@ -161,7 +165,7 @@ class UserPreferencesAccount extends AbstractUserPreferences { final LocalDatabase localDatabase = context.read(); // Credentials final String userId = OpenFoodAPIConfiguration.globalUser!.userId; - return [ + return [ _buildProductQueryTile( productQuery: PagedUserProductQuery( userId: userId, @@ -299,7 +303,7 @@ class UserPreferencesAccount extends AbstractUserPreferences { } } - Widget _buildProductQueryTile({ + UserPreferencesItem _buildProductQueryTile({ required final PagedProductQuery productQuery, required final String title, required final IconData iconData, @@ -320,42 +324,45 @@ class UserPreferencesAccount extends AbstractUserPreferences { myCount: myCount, ); - Widget _getListTile( + UserPreferencesItem _getListTile( final String title, final VoidCallback onTap, final IconData leading, { final Future? myCount, }) => - Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15), - ), - elevation: 5, - color: Theme.of(context).cardColor, - child: UserPreferencesListTile( - title: Text(title), - onTap: onTap, + UserPreferencesItemSimple( + labels: [title], + builder: (_) => Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), ), - leading: UserPreferencesListTile.getTintedIcon(leading, context), - trailing: myCount == null - ? null - : FutureBuilder( - future: myCount, - builder: - (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.connectionState != ConnectionState.done) { - return const SizedBox( - height: LARGE_SPACE, - width: LARGE_SPACE, - child: CircularProgressIndicator.adaptive()); - } - return snapshot.data == null - ? EMPTY_WIDGET - : Text(snapshot.data.toString()); - }, - ), + elevation: 5, + color: Theme.of(context).cardColor, + child: UserPreferencesListTile( + title: Text(title), + onTap: onTap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), + leading: UserPreferencesListTile.getTintedIcon(leading, context), + trailing: myCount == null + ? null + : FutureBuilder( + future: myCount, + builder: + (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return const SizedBox( + height: LARGE_SPACE, + width: LARGE_SPACE, + child: CircularProgressIndicator.adaptive()); + } + return snapshot.data == null + ? EMPTY_WIDGET + : Text(snapshot.data.toString()); + }, + ), + ), ), ); } diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_advanced_settings.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_advanced_settings.dart index 223bd4da68e..e8e958c168d 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_advanced_settings.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_advanced_settings.dart @@ -1,11 +1,25 @@ import 'package:app_settings/app_settings.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; class UserPreferencesAdvancedSettings extends StatelessWidget { const UserPreferencesAdvancedSettings(); + static UserPreferencesItem getUserPreferencesItem( + final BuildContext context, + ) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return UserPreferencesItemSimple( + labels: [ + appLocalizations.native_app_settings, + appLocalizations.native_app_description, + ], + builder: (_) => const UserPreferencesAdvancedSettings(), + ); + } + @override Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_attribute_group.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_attribute_group.dart index 074fbdd7ead..ecdd3f7b49d 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_attribute_group.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_attribute_group.dart @@ -5,6 +5,7 @@ import 'package:smooth_app/data_models/preferences/user_preferences.dart'; import 'package:smooth_app/data_models/product_preferences.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/pages/preferences/attribute_group_list_tile.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/widgets/attribute_button.dart'; /// Collapsed/expanded display of an attribute group for the preferences page. @@ -28,38 +29,87 @@ class UserPreferencesAttributeGroup { bool get _isCollapsed => userPreferences.activeAttributeGroup != group.id; + // TODO(monsieurtanuki): double-check if it could be used/useful + List getLabels() { + final List result = []; + if (group.name != null) { + result.add(group.name!); + } + if (group.warning != null) { + result.add(group.warning!); + } + final List excludedAttributeIds = + userPreferences.getExcludedAttributeIds(); + for (final Attribute attribute in group.attributes!) { + if (excludedAttributeIds.contains(attribute.id)) { + continue; + } + if (attribute.settingNote != null) { + result.add(attribute.settingNote!); + } + if (attribute.settingName != null) { + result.add(attribute.settingName!); + } + if (attribute.id != null) { + result.add(attribute.id!); + } + if (attribute.name != null) { + result.add(attribute.name!); + } + } + return result; + } + List getContent() { final List result = []; + for (final UserPreferencesItem item in getItems()) { + result.add(item.builder(context)); + } + return result; + } + + List getItems({bool? collapsed}) { + collapsed ??= _isCollapsed; + final List result = []; result.add( - InkWell( - onTap: () async => userPreferences.setActiveAttributeGroup(group.id!), - child: AttributeGroupListTile( - title: Text( - group.name ?? appLocalizations.unknown, - style: themeData.textTheme.titleLarge, + UserPreferencesItemSimple( + labels: [], + builder: (_) => InkWell( + onTap: () async => userPreferences.setActiveAttributeGroup(group.id!), + child: AttributeGroupListTile( + title: Text( + group.name ?? appLocalizations.unknown, + style: themeData.textTheme.titleLarge, + ), + icon: collapsed! + ? const Icon(Icons.keyboard_arrow_right) + : const Icon(Icons.keyboard_arrow_down), ), - icon: _isCollapsed - ? const Icon(Icons.keyboard_arrow_right) - : const Icon(Icons.keyboard_arrow_down), ), ), ); - if (_isCollapsed) { + if (collapsed) { return result; } if (group.warning != null) { result.add( - Container( - color: Theme.of(context).colorScheme.error, - width: double.infinity, - padding: const EdgeInsets.all(LARGE_SPACE), - margin: const EdgeInsets.all(LARGE_SPACE), - child: Text( - group.warning ?? appLocalizations.unknown, - style: TextStyle( - color: Theme.of(context).colorScheme.onError, - ), - ), + UserPreferencesItemSimple( + labels: [group.warning!], + builder: (final BuildContext context) { + final ColorScheme colorScheme = Theme.of(context).colorScheme; + return Container( + color: colorScheme.error, + width: double.infinity, + padding: const EdgeInsets.all(LARGE_SPACE), + margin: const EdgeInsets.all(LARGE_SPACE), + child: Text( + group.warning!, + style: TextStyle( + color: colorScheme.onError, + ), + ), + ); + }, ), ); } @@ -69,7 +119,17 @@ class UserPreferencesAttributeGroup { if (excludedAttributeIds.contains(attribute.id)) { continue; } - result.add(AttributeButton(attribute, productPreferences)); + result.add( + UserPreferencesItemSimple( + labels: [ + if (attribute.settingNote != null) attribute.settingNote!, + if (attribute.settingName != null) attribute.settingName!, + if (attribute.id != null) attribute.id!, + if (attribute.name != null) attribute.name!, + ], + builder: (_) => AttributeButton(attribute, productPreferences), + ), + ); } return result; } diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_camera_sound.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_camera_sound.dart deleted file mode 100644 index b4540e4f877..00000000000 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_camera_sound.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:provider/provider.dart'; -import 'package:smooth_app/data_models/preferences/user_preferences.dart'; -import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; - -class UserPreferencesCameraSound extends StatelessWidget { - const UserPreferencesCameraSound(); - - @override - Widget build(BuildContext context) { - final AppLocalizations appLocalizations = AppLocalizations.of(context); - final UserPreferences userPreferences = context.watch(); - return UserPreferencesSwitchItem( - title: appLocalizations.camera_play_sound_title, - subtitle: appLocalizations.camera_play_sound_subtitle, - value: userPreferences.playCameraSound, - onChanged: (final bool value) async => - userPreferences.setPlayCameraSound(value), - ); - } -} diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_choose_accent_color.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_choose_accent_color.dart index 981599144a0..ee45d575a55 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_choose_accent_color.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_choose_accent_color.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; import 'package:smooth_app/themes/color_provider.dart'; import 'package:smooth_app/themes/color_schemes.dart'; @@ -9,6 +10,20 @@ import 'package:smooth_app/themes/color_schemes.dart'; class UserPreferencesChooseAccentColor extends StatelessWidget { const UserPreferencesChooseAccentColor(); + static UserPreferencesItem getUserPreferencesItem( + final BuildContext context, + ) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final Map labels = _localizedNames(appLocalizations); + return UserPreferencesItemSimple( + labels: [ + appLocalizations.select_accent_color, + ...labels.values, + ], + builder: (_) => const UserPreferencesChooseAccentColor(), + ); + } + @override Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); @@ -30,7 +45,9 @@ class UserPreferencesChooseAccentColor extends StatelessWidget { ); } - Map _localizedNames(AppLocalizations appLocalizations) => + static Map _localizedNames( + AppLocalizations appLocalizations, + ) => { 'Blue': appLocalizations.color_blue, 'Cyan': appLocalizations.color_cyan, diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_choose_app_theme.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_choose_app_theme.dart index 6fe37c5b1e7..317ee899635 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_choose_app_theme.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_choose_app_theme.dart @@ -1,12 +1,29 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; import 'package:smooth_app/themes/theme_provider.dart'; class UserPreferencesChooseAppTheme extends StatelessWidget { const UserPreferencesChooseAppTheme(); + static UserPreferencesItem getUserPreferencesItem( + final BuildContext context, + ) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return UserPreferencesItemSimple( + labels: [ + appLocalizations.darkmode, + appLocalizations.darkmode_system_default, + appLocalizations.darkmode_light, + appLocalizations.darkmode_dark, + appLocalizations.theme_amoled, + ], + builder: (_) => const UserPreferencesChooseAppTheme(), + ); + } + @override Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_choose_text_color_contrast.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_choose_text_color_contrast.dart index 49d3780ca5e..af0ff071c56 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_choose_text_color_contrast.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_choose_text_color_contrast.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; import 'package:smooth_app/themes/color_schemes.dart'; import 'package:smooth_app/themes/contrast_provider.dart'; @@ -8,6 +9,21 @@ import 'package:smooth_app/themes/contrast_provider.dart'; class UserPreferencesChooseTextColorContrast extends StatelessWidget { const UserPreferencesChooseTextColorContrast(); + static UserPreferencesItem getUserPreferencesItem( + final BuildContext context, + ) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return UserPreferencesItemSimple( + labels: [ + appLocalizations.text_contrast_mode, + appLocalizations.contrast_high, + appLocalizations.contrast_medium, + appLocalizations.contrast_low, + ], + builder: (_) => const UserPreferencesChooseTextColorContrast(), + ); + } + @override Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_connect.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_connect.dart index 84d019635f6..4e61ab665c5 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_connect.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_connect.dart @@ -15,6 +15,7 @@ import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; import 'package:smooth_app/helpers/app_helper.dart'; import 'package:smooth_app/helpers/launch_url_helper.dart'; import 'package:smooth_app/pages/preferences/abstract_user_preferences.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_list_tile.dart'; import 'package:smooth_app/pages/preferences/user_preferences_page.dart'; import 'package:smooth_app/query/product_query.dart'; @@ -40,9 +41,6 @@ class UserPreferencesConnect extends AbstractUserPreferences { @override String getTitleString() => appLocalizations.connect_with_us; - @override - Widget? getSubtitle() => null; - @override IconData getLeadingIconData() => Icons.alternate_email; @@ -53,7 +51,7 @@ class UserPreferencesConnect extends AbstractUserPreferences { Color? getHeaderColor() => const Color(0xFFDDE7FF); @override - List getBody() => [ + List getChildren() => [ _getListTile( title: appLocalizations.instagram, url: appLocalizations.instagram_link, @@ -81,7 +79,7 @@ class UserPreferencesConnect extends AbstractUserPreferences { url: 'https://blog.openfoodfacts.org', leadingIconData: Icons.newspaper, ), - const Divider(), + _getDivider(), _getListTile( title: appLocalizations.support_via_forum, url: 'https://forum.openfoodfacts.org/', @@ -92,7 +90,7 @@ class UserPreferencesConnect extends AbstractUserPreferences { url: 'https://slack.openfoodfacts.org/', leadingIconData: Icons.chat, ), - const Divider(), + _getDivider(), _getListTile( title: appLocalizations.contact_title_pro_page, url: ProductQuery.replaceSubdomain( @@ -110,7 +108,7 @@ class UserPreferencesConnect extends AbstractUserPreferences { : 'producers@openfoodfacts.org', ), ), - const Divider(), + _getDivider(), _getListTile( title: appLocalizations.contact_title_press_page, url: ProductQuery.replaceSubdomain( @@ -128,7 +126,7 @@ class UserPreferencesConnect extends AbstractUserPreferences { : 'press@openfoodfacts.org', ), ), - const Divider(), + _getDivider(), _getListTile( title: appLocalizations.contact_title_newsletter, url: 'https://link.openfoodfacts.org/newsletter-en', @@ -207,22 +205,25 @@ class UserPreferencesConnect extends AbstractUserPreferences { return buffer.toString(); } - Widget _getListTile({ + UserPreferencesItem _getListTile({ required final String title, final IconData? leadingIconData, final Widget? leadingWidget, final String? url, final VoidCallback? onTap, }) => - UserPreferencesListTile( - title: Text(title), - onTap: onTap ?? () async => LaunchUrlHelper.launchURL(url!, false), - trailing: - UserPreferencesListTile.getTintedIcon(Icons.open_in_new, context), - leading: leadingIconData != null - ? UserPreferencesListTile.getTintedIcon(leadingIconData, context) - : leadingWidget, - externalLink: true, + UserPreferencesItemSimple( + labels: [title], + builder: (_) => UserPreferencesListTile( + title: Text(title), + onTap: onTap ?? () async => LaunchUrlHelper.launchURL(url!, false), + trailing: + UserPreferencesListTile.getTintedIcon(Icons.open_in_new, context), + leading: leadingIconData != null + ? UserPreferencesListTile.getTintedIcon(leadingIconData, context) + : leadingWidget, + externalLink: true, + ), ); Future _sendEmail({ @@ -302,4 +303,9 @@ class UserPreferencesConnect extends AbstractUserPreferences { ); } } + + UserPreferencesItem _getDivider() => UserPreferencesItemSimple( + labels: [], + builder: (_) => const Divider(), + ); } diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_contribute.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_contribute.dart index a8ecdcf7dfe..49b106196ab 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_contribute.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_contribute.dart @@ -13,6 +13,7 @@ import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; import 'package:smooth_app/helpers/launch_url_helper.dart'; import 'package:smooth_app/pages/hunger_games/question_page.dart'; import 'package:smooth_app/pages/preferences/abstract_user_preferences.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_list_tile.dart'; import 'package:smooth_app/pages/preferences/user_preferences_page.dart'; import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; @@ -40,9 +41,6 @@ class UserPreferencesContribute extends AbstractUserPreferences { @override String getTitleString() => appLocalizations.contribute; - @override - Widget? getSubtitle() => null; - @override IconData getLeadingIconData() => Icons.emoji_people; @@ -53,71 +51,70 @@ class UserPreferencesContribute extends AbstractUserPreferences { Color? getHeaderColor() => const Color(0xFFFFF2DF); @override - List getBody() { - return [ - _getListTile( - 'Hunger Games', - () async => _hungerGames(), - Icons.games, - ), - _getListTile( - appLocalizations.contribute_improve_header, - () async => _contribute(), - Icons.data_saver_on, - ), - _getListTile( - appLocalizations.contribute_sw_development, - () async => _develop(), - Icons.app_shortcut, - ), - _getListTile( - appLocalizations.contribute_translate_header, - () async => _translate(), - Icons.translate, - ), - _getListTile( - appLocalizations.how_to_contribute, - () async => LaunchUrlHelper.launchURL( - ProductQuery.replaceSubdomain( - 'https://world.openfoodfacts.org/contribute', + List getChildren() => [ + _getListTile( + 'Hunger Games', + () async => _hungerGames(), + Icons.games, + ), + _getListTile( + appLocalizations.contribute_improve_header, + () async => _contribute(), + Icons.data_saver_on, + ), + _getListTile( + appLocalizations.contribute_sw_development, + () async => _develop(), + Icons.app_shortcut, + ), + _getListTile( + appLocalizations.contribute_translate_header, + () async => _translate(), + Icons.translate, + ), + _getListTile( + appLocalizations.how_to_contribute, + () async => LaunchUrlHelper.launchURL( + ProductQuery.replaceSubdomain( + 'https://world.openfoodfacts.org/contribute', + ), + false, ), - false, + Icons.volunteer_activism_outlined, + externalLink: true, ), - Icons.volunteer_activism_outlined, - externalLink: true, - ), - _getListTile( - appLocalizations.contribute_join_skill_pool, - () async => LaunchUrlHelper.launchURL( - 'https://docs.google.com/forms/d/e/1FAIpQLSfGHAn5KxW7ko3_GlDfQpVGKpPAMHMbDvY2IjtxfJSXxKJQ2A/viewform?usp=sf_link', - false, + _getListTile( + appLocalizations.contribute_join_skill_pool, + () async => LaunchUrlHelper.launchURL( + 'https://docs.google.com/forms/d/e/1FAIpQLSfGHAn5KxW7ko3_GlDfQpVGKpPAMHMbDvY2IjtxfJSXxKJQ2A/viewform?usp=sf_link', + false, + ), + Icons.group, + externalLink: true, ), - Icons.group, - externalLink: true, - ), - _getListTile( - appLocalizations.contribute_share_header, - () async => _share(appLocalizations.contribute_share_content), - Icons.adaptive.share, - ), - _getListTile( - appLocalizations.contribute_donate_header, - () async => LaunchUrlHelper.launchURL( - AppLocalizations.of(context).donate_url, - false, + _getListTile( + appLocalizations.contribute_share_header, + () async => _share(appLocalizations.contribute_share_content), + Icons.adaptive.share, ), - Icons.volunteer_activism, - icon: UserPreferencesListTile.getTintedIcon(Icons.open_in_new, context), - externalLink: true, - ), - _getListTile( - appLocalizations.contributors_label, - () async => _contributors(), - Icons.emoji_people, - description: appLocalizations.contributors_description, - ), - ]; - } + _getListTile( + appLocalizations.contribute_donate_header, + () async => LaunchUrlHelper.launchURL( + AppLocalizations.of(context).donate_url, + false, + ), + Icons.volunteer_activism, + icon: + UserPreferencesListTile.getTintedIcon(Icons.open_in_new, context), + externalLink: true, + ), + _getListTile( + appLocalizations.contributors_label, + () async => _contributors(), + Icons.emoji_people, + description: appLocalizations.contributors_description, + ), + ]; Future _contribute() => showDialog( context: context, @@ -171,6 +168,7 @@ class UserPreferencesContribute extends AbstractUserPreferences { builder: (BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); + context.watch(); return SmoothAlertDialog( title: appLocalizations.contribute_sw_development, body: Column( @@ -193,7 +191,14 @@ class UserPreferencesContribute extends AbstractUserPreferences { 'https://github.com/openfoodfacts', false), ), const SizedBox(height: 10), - const _DevModeSetting(), + UserPreferencesSwitchWidget( + title: appLocalizations.contribute_develop_dev_mode_title, + subtitle: + appLocalizations.contribute_develop_dev_mode_subtitle, + value: userPreferences.devMode != 0, + onChanged: (final bool devMode) async => + userPreferences.setDevMode(devMode ? 1 : 0), + ), ], ), negativeAction: SmoothActionButton( @@ -251,7 +256,7 @@ class UserPreferencesContribute extends AbstractUserPreferences { Future _hungerGames() async => openQuestionPage(context); - Widget _getListTile( + UserPreferencesItem _getListTile( final String title, final VoidCallback onTap, final IconData leading, { @@ -268,16 +273,21 @@ class UserPreferencesContribute extends AbstractUserPreferences { ); if (description != null) { - return Semantics( - value: title, - hint: description, - button: true, - excludeSemantics: true, - child: tile, + return UserPreferencesItemSimple( + labels: [title, description], + builder: (_) => Semantics( + value: title, + hint: description, + button: true, + excludeSemantics: true, + child: tile, + ), ); - } else { - return tile; } + return UserPreferencesItemSimple( + labels: [title], + builder: (_) => tile, + ); } } @@ -379,22 +389,3 @@ class _ContributorsDialog extends StatelessWidget { ); } } - -class _DevModeSetting extends StatelessWidget { - const _DevModeSetting({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final AppLocalizations appLocalizations = AppLocalizations.of(context); - final UserPreferences userPreferences = context.watch(); - - return UserPreferencesSwitchItem( - title: appLocalizations.contribute_develop_dev_mode_title, - subtitle: appLocalizations.contribute_develop_dev_mode_subtitle, - value: userPreferences.devMode != 0, - onChanged: (final bool devMode) async { - await userPreferences.setDevMode(devMode ? 1 : 0); - }, - ); - } -} diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_country_selector.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_country_selector.dart index 9717452d791..b91e4eeec91 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_country_selector.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_country_selector.dart @@ -2,10 +2,23 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/pages/onboarding/country_selector.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; class UserPreferencesCountrySelector extends StatelessWidget { const UserPreferencesCountrySelector(); + static UserPreferencesItem getUserPreferencesItem( + final BuildContext context, + ) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return UserPreferencesItemSimple( + labels: [ + appLocalizations.country_chooser_label, + ], + builder: (_) => const UserPreferencesCountrySelector(), + ); + } + @override Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_crash_reporting.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_crash_reporting.dart deleted file mode 100644 index edc55bc336b..00000000000 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_crash_reporting.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:provider/provider.dart'; -import 'package:smooth_app/data_models/preferences/user_preferences.dart'; -import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; - -class UserPreferencesCrashReporting extends StatelessWidget { - const UserPreferencesCrashReporting(); - - @override - Widget build(BuildContext context) { - final AppLocalizations appLocalizations = AppLocalizations.of(context); - final UserPreferences userPreferences = context.watch(); - return UserPreferencesSwitchItem( - title: appLocalizations.crash_reporting_toggle_title, - subtitle: appLocalizations.crash_reporting_toggle_subtitle, - value: userPreferences.crashReports, - onChanged: (final bool value) async => - userPreferences.setCrashReports(value), - ); - } -} diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart index 051fb988a88..eafd8a8b323 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart @@ -17,7 +17,10 @@ import 'package:smooth_app/pages/offline_data_page.dart'; import 'package:smooth_app/pages/offline_tasks_page.dart'; import 'package:smooth_app/pages/preferences/abstract_user_preferences.dart'; import 'package:smooth_app/pages/preferences/user_preferences_dev_debug_info.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_page.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_search_page.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; import 'package:smooth_app/query/product_query.dart'; /// Full page display of "dev mode" for the preferences page. @@ -70,18 +73,13 @@ class UserPreferencesDevMode extends AbstractUserPreferences { ), ); - @override - Widget? getSubtitle() => null; - @override IconData getLeadingIconData() => Icons.settings; @override - List getBody() => [ - SwitchListTile( - title: Text( - appLocalizations.contribute_develop_dev_mode_title, - ), + List getChildren() => [ + UserPreferencesItemSwitch( + title: appLocalizations.contribute_develop_dev_mode_title, onChanged: (bool value) async { final NavigatorState navigator = Navigator.of(context); // resetting back to "no dev mode" @@ -93,22 +91,16 @@ class UserPreferencesDevMode extends AbstractUserPreferences { }, value: userPreferences.devMode == 1, ), - ListTile( - title: Text( - appLocalizations.dev_preferences_reset_onboarding_title, - ), - subtitle: Text( - appLocalizations.dev_preferences_reset_onboarding_subtitle, - ), + UserPreferencesItemTile( + title: appLocalizations.dev_preferences_reset_onboarding_title, + subtitle: appLocalizations.dev_preferences_reset_onboarding_subtitle, onTap: () async { await userPreferences.resetOnboarding(); _showSuccessMessage(); }, ), - ListTile( - title: Text( - appLocalizations.dev_preferences_environment_switch_title, - ), + UserPreferencesItemTile( + title: appLocalizations.dev_preferences_environment_switch_title, trailing: DropdownButton( value: OpenFoodAPIConfiguration.globalQueryType == QueryType.PROD, elevation: 16, @@ -128,21 +120,15 @@ class UserPreferencesDevMode extends AbstractUserPreferences { ], ), ), - ListTile( - title: Text( - appLocalizations.dev_preferences_test_environment_title, - ), - subtitle: Text( - appLocalizations.dev_preferences_test_environment_subtitle( - '${OpenFoodAPIConfiguration.uriScheme}://${OpenFoodAPIConfiguration.uriTestHost}/', - ), + UserPreferencesItemTile( + title: appLocalizations.dev_preferences_test_environment_title, + subtitle: appLocalizations.dev_preferences_test_environment_subtitle( + '${OpenFoodAPIConfiguration.uriScheme}://${OpenFoodAPIConfiguration.uriTestHost}/', ), onTap: () async => _changeTestEnvHost(), ), - SwitchListTile( - title: Text( - appLocalizations.dev_preferences_edit_ingredients_title, - ), + UserPreferencesItemSwitch( + title: appLocalizations.dev_preferences_edit_ingredients_title, value: userPreferences.getFlag(userPreferencesFlagEditIngredients) ?? false, onChanged: (bool value) async { @@ -151,10 +137,8 @@ class UserPreferencesDevMode extends AbstractUserPreferences { _showSuccessMessage(); }, ), - SwitchListTile( - title: const Text( - 'Accessibility: remove colors', - ), + UserPreferencesItemSwitch( + title: 'Accessibility: remove colors', value: userPreferences .getFlag(userPreferencesFlagAccessibilityNoColor) ?? false, @@ -164,10 +148,8 @@ class UserPreferencesDevMode extends AbstractUserPreferences { _showSuccessMessage(); }, ), - SwitchListTile( - title: const Text( - 'Accessibility: show emoji', - ), + UserPreferencesItemSwitch( + title: 'Accessibility: show emoji', value: userPreferences.getFlag(userPreferencesFlagAccessibilityEmoji) ?? false, @@ -177,11 +159,9 @@ class UserPreferencesDevMode extends AbstractUserPreferences { _showSuccessMessage(); }, ), - ListTile( - title: Text( - appLocalizations.dev_preferences_export_history_title, - ), - subtitle: Text(appLocalizations.clipboard_barcode_copy), + UserPreferencesItemTile( + title: appLocalizations.dev_preferences_export_history_title, + subtitle: appLocalizations.clipboard_barcode_copy, onTap: () async { final LocalDatabase localDatabase = context.read(); final Map export = @@ -245,20 +225,18 @@ class UserPreferencesDevMode extends AbstractUserPreferences { }, ), _dataImporterTile(), - ListTile( - title: Text(appLocalizations.offline_data), - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext context) => const OfflineDataPage(), - ), - ); - }, + UserPreferencesItemTile( + title: appLocalizations.offline_data, + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => const OfflineDataPage(), + ), + ), ), - ListTile( - title: Text(appLocalizations.background_task_title), - subtitle: Text(appLocalizations.background_task_subtitle), + UserPreferencesItemTile( + title: appLocalizations.background_task_title, + subtitle: appLocalizations.background_task_subtitle, trailing: const BackgroundTaskBadge( child: Icon(Icons.edit_notifications_outlined), ), @@ -269,13 +247,9 @@ class UserPreferencesDevMode extends AbstractUserPreferences { ), ), ), - ListTile( - title: Text( - appLocalizations.dev_preferences_import_history_title, - ), - subtitle: Text( - appLocalizations.dev_preferences_import_history_subtitle, - ), + UserPreferencesItemTile( + title: appLocalizations.dev_preferences_import_history_title, + subtitle: appLocalizations.dev_preferences_import_history_subtitle, onTap: () async { final LocalDatabase localDatabase = context.read(); await ProductListImportExport().importFromJSON( @@ -294,9 +268,9 @@ class UserPreferencesDevMode extends AbstractUserPreferences { localDatabase.notifyListeners(); }, ), - ListTile( - title: const Text('Add cards to scanner'), - subtitle: const Text('Adds 3 sample products to the scanner'), + UserPreferencesItemTile( + title: 'Add cards to scanner', + subtitle: 'Adds 3 sample products to the scanner', onTap: () async { final ContinuousScanModel model = context.read(); @@ -311,10 +285,8 @@ class UserPreferencesDevMode extends AbstractUserPreferences { } }, ), - SwitchListTile( - title: Text( - appLocalizations.dev_mode_hide_ecoscore_title, - ), + UserPreferencesItemSwitch( + title: appLocalizations.dev_mode_hide_ecoscore_title, value: userPreferences .getExcludedAttributeIds() .contains(Attribute.ATTRIBUTE_ECOSCORE), @@ -328,16 +300,16 @@ class UserPreferencesDevMode extends AbstractUserPreferences { await userPreferences.setExcludedAttributeIds(list); }, ), - ListTile( + UserPreferencesItemTile( // Do not translate - title: const Text('Reset app language'), + title: 'Reset app language', onTap: () async { userPreferences.setAppLanguageCode(null); ProductQuery.setLanguage(context, userPreferences); }, ), - SwitchListTile( - title: const Text('Side by side comparison for 2 or 3 products'), + UserPreferencesItemSwitch( + title: 'Side by side comparison for 2 or 3 products', value: userPreferences.getFlag(userPreferencesFlagBoostedComparison) ?? false, @@ -347,26 +319,31 @@ class UserPreferencesDevMode extends AbstractUserPreferences { _showSuccessMessage(); }, ), - ListTile( - title: const Text('Debugging information'), + UserPreferencesItemTile( + title: 'Debugging information', onTap: () async => Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext context) => const UserPreferencesDebugInfo())), - ) + ), + UserPreferencesItemTile( + title: 'Preference Search...', + onTap: () async => Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => + const UserPreferencesSearchPage(), + ), + ), + ), ]; - ListTile _dataImporterTile() { + UserPreferencesItem _dataImporterTile() { final SmoothAppDataImporterStatus status = context.read().status; - return ListTile( - title: Text( - appLocalizations.dev_preferences_migration_title, - ), - subtitle: Text( - appLocalizations.dev_preferences_migration_subtitle( - status.printableLabel(appLocalizations), - ), + return UserPreferencesItemTile( + title: appLocalizations.dev_preferences_migration_title, + subtitle: appLocalizations.dev_preferences_migration_subtitle( + status.printableLabel(appLocalizations), ), onTap: status.canInitiateMigration ? () { diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_faq.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_faq.dart index dc639c19e18..8e79aa8caef 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_faq.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_faq.dart @@ -12,6 +12,7 @@ import 'package:smooth_app/helpers/global_vars.dart'; import 'package:smooth_app/helpers/launch_url_helper.dart'; import 'package:smooth_app/helpers/user_feedback_helper.dart'; import 'package:smooth_app/pages/preferences/abstract_user_preferences.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_list_tile.dart'; import 'package:smooth_app/pages/preferences/user_preferences_page.dart'; import 'package:smooth_app/query/product_query.dart'; @@ -36,9 +37,6 @@ class UserPreferencesFaq extends AbstractUserPreferences { @override String getTitleString() => appLocalizations.faq; - @override - Widget? getSubtitle() => null; - @override IconData getLeadingIconData() => Icons.question_mark; @@ -51,7 +49,7 @@ class UserPreferencesFaq extends AbstractUserPreferences { bool get _isDark => Theme.of(context).brightness == Brightness.dark; @override - List getBody() => [ + List getChildren() => [ _getListTile( title: appLocalizations.faq, leadingIconData: Icons.question_mark, @@ -151,7 +149,7 @@ class UserPreferencesFaq extends AbstractUserPreferences { ), ]; - Widget _getListTile({ + UserPreferencesItem _getListTile({ required final String title, final IconData? leadingIconData, final String? leadingSvg, @@ -160,31 +158,34 @@ class UserPreferencesFaq extends AbstractUserPreferences { final VoidCallback? onTap, final Icon? icon, }) => - UserPreferencesListTile( - title: Text(title), - onTap: onTap ?? () async => LaunchUrlHelper.launchURL(url!, false), - trailing: icon ?? - UserPreferencesListTile.getTintedIcon(Icons.open_in_new, context), - leading: SizedBox( - width: 2 * DEFAULT_ICON_SIZE, - height: 2 * DEFAULT_ICON_SIZE, - child: Center( - child: leadingIconData != null - ? UserPreferencesListTile.getTintedIcon( - leadingIconData, context) - : leadingSvg == null - ? null - : SvgPicture.asset( - leadingSvg, - width: leadingSvgWidth ?? 2 * DEFAULT_ICON_SIZE, - package: AppHelper.APP_PACKAGE, - ), + UserPreferencesItemSimple( + labels: [title], + builder: (_) => UserPreferencesListTile( + title: Text(title), + onTap: onTap ?? () async => LaunchUrlHelper.launchURL(url!, false), + trailing: icon ?? + UserPreferencesListTile.getTintedIcon(Icons.open_in_new, context), + leading: SizedBox( + width: 2 * DEFAULT_ICON_SIZE, + height: 2 * DEFAULT_ICON_SIZE, + child: Center( + child: leadingIconData != null + ? UserPreferencesListTile.getTintedIcon( + leadingIconData, context) + : leadingSvg == null + ? null + : SvgPicture.asset( + leadingSvg, + width: leadingSvgWidth ?? 2 * DEFAULT_ICON_SIZE, + package: AppHelper.APP_PACKAGE, + ), + ), ), + externalLink: url != null, ), - externalLink: url != null, ); - Widget _getNutriListTile({ + UserPreferencesItem _getNutriListTile({ required final String title, required final String url, required final String svg, diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_food.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_food.dart index 203d3ac0053..dadbf644cb9 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_food.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_food.dart @@ -8,8 +8,10 @@ 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/preferences/abstract_user_preferences.dart'; import 'package:smooth_app/pages/preferences/user_preferences_attribute_group.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_list_tile.dart'; import 'package:smooth_app/pages/preferences/user_preferences_page.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; import 'package:smooth_app/widgets/smooth_text.dart'; /// Collapsed/expanded display of attribute groups for the preferences page. @@ -45,7 +47,7 @@ class UserPreferencesFood extends AbstractUserPreferences { String getTitleString() => appLocalizations.myPreferences_food_title; @override - Widget? getSubtitle() => Text(appLocalizations.myPreferences_food_subtitle); + String getSubtitleString() => appLocalizations.myPreferences_food_subtitle; @override IconData getLeadingIconData() => Icons.ramen_dining; @@ -57,21 +59,18 @@ class UserPreferencesFood extends AbstractUserPreferences { Color? getHeaderColor() => const Color(0xFFEBF1FF); @override - List getBody() { - final List result = [ - // we don't want this on the onboarding - UserPreferencesListTile( - leading: UserPreferencesListTile.getTintedIcon( - Icons.rotate_left, - context, + List getChildren() => [ + // we don't want this on the onboarding + UserPreferencesItemTile( + leading: UserPreferencesListTile.getTintedIcon( + Icons.rotate_left, + context, + ), + title: appLocalizations.reset_food_prefs, + onTap: () async => _confirmReset(), ), - title: Text(appLocalizations.reset_food_prefs), - onTap: () async => _confirmReset(), - ), - ]; - result.addAll(_getOnboardingBody()); - return result; - } + ..._getOnboardingBody(collapsed: false) + ]; List _reorderGroups(List groups) { final List result = []; @@ -106,25 +105,33 @@ class UserPreferencesFood extends AbstractUserPreferences { } /// Returns a slightly different version of [getContent] for the onboarding. - List getOnboardingContent() => [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: LARGE_SPACE), - child: Text( - getTitleString(), - style: themeData.textTheme.displayMedium, - ), + List getOnboardingContent() { + final List result = [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: LARGE_SPACE), + child: Text( + getTitleString(), + style: themeData.textTheme.displayMedium, ), - ..._getOnboardingBody(), - ]; + ), + ]; + for (final UserPreferencesItem item in _getOnboardingBody()) { + result.add(item.builder(context)); + } + return result; + } - List _getOnboardingBody() { + List _getOnboardingBody({final bool? collapsed}) { final List groups = _reorderGroups(productPreferences.attributeGroups!); - final List result = [ - ListTile( - title: Text( - appLocalizations.myPreferences_food_comment, - style: WellSpacedTextHelper.TEXT_STYLE_WITH_WELL_SPACED, + final List result = [ + UserPreferencesItemSimple( + labels: [appLocalizations.myPreferences_food_comment], + builder: (_) => ListTile( + title: Text( + appLocalizations.myPreferences_food_comment, + style: WellSpacedTextHelper.TEXT_STYLE_WITH_WELL_SPACED, + ), ), ), ]; @@ -137,7 +144,7 @@ class UserPreferencesFood extends AbstractUserPreferences { userPreferences: userPreferences, appLocalizations: appLocalizations, themeData: themeData, - ).getContent(), + ).getItems(collapsed: collapsed), ); } return result; diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_haptic_feedback.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_haptic_feedback.dart deleted file mode 100644 index f7f49f2c954..00000000000 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_haptic_feedback.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:provider/provider.dart'; -import 'package:smooth_app/data_models/preferences/user_preferences.dart'; -import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; - -class UserPreferencesHapticFeedback extends StatelessWidget { - const UserPreferencesHapticFeedback(); - - @override - Widget build(BuildContext context) { - final AppLocalizations appLocalizations = AppLocalizations.of(context); - final UserPreferences userPreferences = context.watch(); - return UserPreferencesSwitchItem( - title: appLocalizations.app_haptic_feedback_title, - subtitle: appLocalizations.app_haptic_feedback_subtitle, - value: userPreferences.hapticFeedbackEnabled, - onChanged: (final bool value) async => - userPreferences.setHapticFeedbackEnabled(value), - ); - } -} diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_image_source.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_image_source.dart index 602759bbbdd..a2177e25eb8 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_image_source.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_image_source.dart @@ -2,11 +2,27 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:smooth_app/data_models/preferences/user_preferences.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; class UserPreferencesImageSource extends StatelessWidget { const UserPreferencesImageSource(); + static UserPreferencesItem getUserPreferencesItem( + final BuildContext context, + ) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return UserPreferencesItemSimple( + labels: [ + appLocalizations.choose_image_source_title, + appLocalizations.user_picture_source_select, + appLocalizations.settings_app_camera, + appLocalizations.gallery_source_label, + ], + builder: (_) => const UserPreferencesImageSource(), + ); + } + @override Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_item.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_item.dart new file mode 100644 index 00000000000..f7f3f84497c --- /dev/null +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_item.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +/// Item for preferences, with labels for pre-filtering and widget builder. +abstract interface class UserPreferencesItem { + Iterable get labels; + Widget Function(BuildContext) get builder; +} + +/// Simplest implementation of a [UserPreferencesItem]. +class UserPreferencesItemSimple implements UserPreferencesItem { + const UserPreferencesItemSimple({ + required this.labels, + required this.builder, + }); + + @override + final Iterable labels; + + @override + final Widget Function(BuildContext) builder; +} diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_language_selector.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_language_selector.dart index 99fd79c6c7e..98bb9fc9959 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_language_selector.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_language_selector.dart @@ -5,11 +5,24 @@ import 'package:provider/provider.dart'; import 'package:smooth_app/data_models/preferences/user_preferences.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/widgets/language_selector.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/query/product_query.dart'; class UserPreferencesLanguageSelector extends StatelessWidget { const UserPreferencesLanguageSelector(); + static UserPreferencesItem getUserPreferencesItem( + final BuildContext context, + ) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return UserPreferencesItemSimple( + labels: [ + appLocalizations.language_picker_label, + ], + builder: (_) => const UserPreferencesLanguageSelector(), + ); + } + @override Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_page.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_page.dart index a2cd5041df0..0a49b07113a 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_page.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_page.dart @@ -18,6 +18,7 @@ import 'package:smooth_app/pages/preferences/user_preferences_contribute.dart'; import 'package:smooth_app/pages/preferences/user_preferences_dev_mode.dart'; import 'package:smooth_app/pages/preferences/user_preferences_faq.dart'; import 'package:smooth_app/pages/preferences/user_preferences_food.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_settings.dart'; import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; import 'package:smooth_app/themes/theme_provider.dart'; @@ -38,6 +39,86 @@ enum PreferencePageType { /// A tag used when opening a new screen /// eg: preferences/account final String tag; + + AbstractUserPreferences getUserPreferences({ + required final UserPreferences userPreferences, + required final BuildContext context, + }) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final ThemeProvider themeProvider = context.read(); + final ThemeData themeData = Theme.of(context); + final ProductPreferences productPreferences = + context.read(); + // TODO(monsieurtanuki): the following line is probably useless - get rid of it if possible + context.read(); + + switch (this) { + case PreferencePageType.ACCOUNT: + return UserPreferencesAccount( + context: context, + userPreferences: userPreferences, + appLocalizations: appLocalizations, + themeData: themeData, + ); + case PreferencePageType.FOOD: + return UserPreferencesFood( + productPreferences: productPreferences, + context: context, + userPreferences: userPreferences, + appLocalizations: appLocalizations, + themeData: themeData, + ); + case PreferencePageType.SETTINGS: + return UserPreferencesSettings( + themeProvider: themeProvider, + context: context, + userPreferences: userPreferences, + appLocalizations: appLocalizations, + themeData: themeData, + ); + case PreferencePageType.DEV_MODE: + return UserPreferencesDevMode( + context: context, + userPreferences: userPreferences, + appLocalizations: appLocalizations, + themeData: themeData, + ); + case PreferencePageType.CONTRIBUTE: + return UserPreferencesContribute( + context: context, + userPreferences: userPreferences, + appLocalizations: appLocalizations, + themeData: themeData, + ); + case PreferencePageType.FAQ: + return UserPreferencesFaq( + context: context, + userPreferences: userPreferences, + appLocalizations: appLocalizations, + themeData: themeData, + ); + case PreferencePageType.CONNECT: + return UserPreferencesConnect( + context: context, + userPreferences: userPreferences, + appLocalizations: appLocalizations, + themeData: themeData, + ); + } + } + + static List getPreferencePageTypes( + final UserPreferences userPreferences, + ) => + [ + PreferencePageType.ACCOUNT, + PreferencePageType.FOOD, + PreferencePageType.SETTINGS, + PreferencePageType.CONTRIBUTE, + PreferencePageType.FAQ, + PreferencePageType.CONNECT, + if (userPreferences.devMode > 0) PreferencePageType.DEV_MODE, + ]; } /// Preferences page: main or detailed. @@ -73,21 +154,13 @@ class _UserPreferencesPageState extends State final String? headerAsset; final Color? headerColor; if (widget.type == null) { - final List items = [ - PreferencePageType.ACCOUNT, - PreferencePageType.FOOD, - PreferencePageType.SETTINGS, - PreferencePageType.CONTRIBUTE, - PreferencePageType.FAQ, - PreferencePageType.CONNECT, - if (userPreferences.devMode > 0) PreferencePageType.DEV_MODE, - ]; - + final List items = + PreferencePageType.getPreferencePageTypes(userPreferences); for (final PreferencePageType type in items) { final AbstractUserPreferences abstractUserPreferences = - getUserPreferences( - type: type, + type.getUserPreferences( userPreferences: userPreferences, + context: context, ); children.add(abstractUserPreferences.getOnlyHeader()); final Widget? additionalSubtitle = @@ -104,12 +177,15 @@ class _UserPreferencesPageState extends State addDividers = true; } else { final AbstractUserPreferences abstractUserPreferences = - getUserPreferences( - type: widget.type!, + widget.type!.getUserPreferences( userPreferences: userPreferences, + context: context, ); - children.addAll(abstractUserPreferences.getBody()); + for (final UserPreferencesItem item + in abstractUserPreferences.getChildren()) { + children.add(item.builder(context)); + } appBarTitle = abstractUserPreferences.getTitleString(); addDividers = false; @@ -188,70 +264,4 @@ class _UserPreferencesPageState extends State ), ); } - - AbstractUserPreferences getUserPreferences({ - required final PreferencePageType type, - required final UserPreferences userPreferences, - }) { - final AppLocalizations appLocalizations = AppLocalizations.of(context); - final ThemeProvider themeProvider = context.watch(); - final ThemeData themeData = Theme.of(context); - final ProductPreferences productPreferences = - context.watch(); - context.watch(); - - switch (type) { - case PreferencePageType.ACCOUNT: - return UserPreferencesAccount( - context: context, - userPreferences: userPreferences, - appLocalizations: appLocalizations, - themeData: themeData, - ); - case PreferencePageType.FOOD: - return UserPreferencesFood( - productPreferences: productPreferences, - context: context, - userPreferences: userPreferences, - appLocalizations: appLocalizations, - themeData: themeData, - ); - case PreferencePageType.SETTINGS: - return UserPreferencesSettings( - themeProvider: themeProvider, - context: context, - userPreferences: userPreferences, - appLocalizations: appLocalizations, - themeData: themeData, - ); - case PreferencePageType.DEV_MODE: - return UserPreferencesDevMode( - context: context, - userPreferences: userPreferences, - appLocalizations: appLocalizations, - themeData: themeData, - ); - case PreferencePageType.CONTRIBUTE: - return UserPreferencesContribute( - context: context, - userPreferences: userPreferences, - appLocalizations: appLocalizations, - themeData: themeData, - ); - case PreferencePageType.FAQ: - return UserPreferencesFaq( - context: context, - userPreferences: userPreferences, - appLocalizations: appLocalizations, - themeData: themeData, - ); - case PreferencePageType.CONNECT: - return UserPreferencesConnect( - context: context, - userPreferences: userPreferences, - appLocalizations: appLocalizations, - themeData: themeData, - ); - } - } } diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_rate_us.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_rate_us.dart index c9502b344f9..14aedf2055e 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_rate_us.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_rate_us.dart @@ -4,12 +4,25 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/helpers/entry_points_helper.dart'; import 'package:smooth_app/helpers/global_vars.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; import 'package:smooth_app/services/smooth_services.dart'; class UserPreferencesRateUs extends StatelessWidget { const UserPreferencesRateUs(); + static UserPreferencesItem getUserPreferencesItem( + final BuildContext context, + ) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return UserPreferencesItemSimple( + labels: [ + appLocalizations.rate_app, + ], + builder: (_) => const UserPreferencesRateUs(), + ); + } + @override Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); @@ -27,8 +40,6 @@ class UserPreferencesRateUs extends StatelessWidget { try { await ApplicationStore.openAppDetails(); } on PlatformException { - final AppLocalizations appLocalizations = - AppLocalizations.of(context); final ThemeData themeData = Theme.of(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar( diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_search_page.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_search_page.dart new file mode 100644 index 00000000000..ded2608cd82 --- /dev/null +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_search_page.dart @@ -0,0 +1,110 @@ +import 'package:diacritic/diacritic.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/data_models/preferences/user_preferences.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/pages/preferences/abstract_user_preferences.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_page.dart'; + +/// Search page for preferences, with TextField filter. +class UserPreferencesSearchPage extends StatefulWidget { + const UserPreferencesSearchPage({super.key}); + + @override + State createState() => + _UserPreferencesSearchPageState(); +} + +class _UserPreferencesSearchPageState extends State { + final TextEditingController _controller = TextEditingController(); + + @override + Widget build(BuildContext context) { + final UserPreferences userPreferences = context.watch(); + final List items = _getItems(_controller.text, userPreferences); + return Scaffold( + appBar: AppBar( + title: const Text('Preferences Search'), + ), + body: SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextField( + controller: _controller, + onChanged: (_) => setState(() {}), + decoration: InputDecoration( + filled: true, + border: const OutlineInputBorder( + borderRadius: ANGULAR_BORDER_RADIUS, + borderSide: BorderSide.none, + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: SMALL_SPACE, + vertical: SMALL_SPACE, + ), + hintText: 'Looking for...', + suffix: IconButton( + icon: const Icon(Icons.clear), + onPressed: () => setState(() => _controller.text = ''), + ), + ), + ), + if (items.isNotEmpty && _controller.text.isNotEmpty) + Expanded( + child: ListView.builder( + itemCount: items.length, + itemBuilder: (final BuildContext context, final int index) => + items[index], + ), + ), + ], + ), + ), + ); + } + + List _getItems( + final String searchString, + final UserPreferences userPreferences, + ) { + final String needle = removeDiacritics(searchString.toLowerCase()); + final List result = []; + final List types = + PreferencePageType.getPreferencePageTypes(userPreferences); + for (final PreferencePageType type in types) { + final AbstractUserPreferences abstractUserPreferences = + type.getUserPreferences( + userPreferences: userPreferences, + context: context, + ); + // we find the label in the page description: we add all the page items. + if (_findLabels(needle, abstractUserPreferences.getLabels())) { + for (final UserPreferencesItem item + in abstractUserPreferences.getChildren()) { + result.add(item.builder(context)); + } + } else { + // we try to find the label in each page item. + for (final UserPreferencesItem item + in abstractUserPreferences.getChildren()) { + if (_findLabels(needle, item.labels)) { + result.add(item.builder(context)); + } + } + } + } + return result; + } + + bool _findLabels(final String needle, final Iterable labels) { + for (final String label in labels) { + if (removeDiacritics(label.toLowerCase()).contains(needle)) { + return true; + } + } + return false; + } +} diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_send_anonymous.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_send_anonymous.dart deleted file mode 100644 index fba29329641..00000000000 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_send_anonymous.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:provider/provider.dart'; -import 'package:smooth_app/data_models/preferences/user_preferences.dart'; -import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; - -class UserPreferencesSendAnonymous extends StatelessWidget { - const UserPreferencesSendAnonymous(); - - @override - Widget build(BuildContext context) { - final AppLocalizations appLocalizations = AppLocalizations.of(context); - final UserPreferences userPreferences = context.watch(); - return UserPreferencesSwitchItem( - title: appLocalizations.send_anonymous_data_toggle_title, - subtitle: appLocalizations.send_anonymous_data_toggle_subtitle, - value: userPreferences.userTracking, - onChanged: (final bool allow) async => - userPreferences.setUserTracking(allow), - ); - } -} diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_settings.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_settings.dart index 6eb0733e806..937cdc94908 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_settings.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_settings.dart @@ -2,22 +2,20 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:smooth_app/data_models/preferences/user_preferences.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/helpers/camera_helper.dart'; import 'package:smooth_app/knowledge_panel/knowledge_panels/knowledge_panel_card.dart'; import 'package:smooth_app/pages/preferences/abstract_user_preferences.dart'; import 'package:smooth_app/pages/preferences/user_preferences_advanced_settings.dart'; -import 'package:smooth_app/pages/preferences/user_preferences_camera_sound.dart'; import 'package:smooth_app/pages/preferences/user_preferences_choose_accent_color.dart'; import 'package:smooth_app/pages/preferences/user_preferences_choose_app_theme.dart'; import 'package:smooth_app/pages/preferences/user_preferences_choose_text_color_contrast.dart'; import 'package:smooth_app/pages/preferences/user_preferences_country_selector.dart'; -import 'package:smooth_app/pages/preferences/user_preferences_crash_reporting.dart'; -import 'package:smooth_app/pages/preferences/user_preferences_haptic_feedback.dart'; import 'package:smooth_app/pages/preferences/user_preferences_image_source.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_language_selector.dart'; import 'package:smooth_app/pages/preferences/user_preferences_page.dart'; import 'package:smooth_app/pages/preferences/user_preferences_rate_us.dart'; -import 'package:smooth_app/pages/preferences/user_preferences_send_anonymous.dart'; import 'package:smooth_app/pages/preferences/user_preferences_share_with_friends.dart'; import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; import 'package:smooth_app/themes/theme_provider.dart'; @@ -46,85 +44,154 @@ class UserPreferencesSettings extends AbstractUserPreferences { String getTitleString() => appLocalizations.myPreferences_settings_title; @override - Widget? getSubtitle() => - Text(appLocalizations.myPreferences_settings_subtitle); + String getSubtitleString() => + appLocalizations.myPreferences_settings_subtitle; @override IconData getLeadingIconData() => Icons.handyman; @override - List getBody() => [ - UserPreferencesTitle.firstItem( - label: appLocalizations.settings_app_app, + List getChildren() { + final UserPreferences userPreferences = context.read(); + return [ + _getTitle( + label: appLocalizations.settings_app_app, + addExtraPadding: false, + ), + UserPreferencesChooseAppTheme.getUserPreferencesItem(context), + if (themeProvider.currentTheme == THEME_AMOLED) + UserPreferencesChooseAccentColor.getUserPreferencesItem(context), + if (themeProvider.currentTheme == THEME_AMOLED) + UserPreferencesChooseTextColorContrast.getUserPreferencesItem(context), + _getDivider(), + UserPreferencesCountrySelector.getUserPreferencesItem(context), + _getDivider(), + UserPreferencesLanguageSelector.getUserPreferencesItem(context), + _getDivider(), + UserPreferencesImageSource.getUserPreferencesItem(context), + if (CameraHelper.hasACamera) + _getTitle( + label: appLocalizations.settings_app_camera, ), - const UserPreferencesChooseAppTheme(), - if (themeProvider.currentTheme == THEME_AMOLED) - const UserPreferencesChooseAccentColor(), - if (themeProvider.currentTheme == THEME_AMOLED) - const UserPreferencesChooseTextColorContrast(), - const UserPreferencesListItemDivider(), - const UserPreferencesCountrySelector(), - const UserPreferencesListItemDivider(), - const UserPreferencesLanguageSelector(), - const UserPreferencesListItemDivider(), - const UserPreferencesImageSource(), - if (CameraHelper.hasACamera) - UserPreferencesTitle( - label: appLocalizations.settings_app_camera, - ), - if (CameraHelper.hasACamera) const UserPreferencesCameraSound(), - UserPreferencesTitle( - label: appLocalizations.settings_app_products, + if (CameraHelper.hasACamera) + UserPreferencesItemSwitch( + title: appLocalizations.camera_play_sound_title, + subtitle: appLocalizations.camera_play_sound_subtitle, + value: userPreferences.playCameraSound, + onChanged: (final bool value) async => + userPreferences.setPlayCameraSound(value), ), - _ExpandPanelHelper( - title: appLocalizations.expand_nutrition_facts, - subtitle: appLocalizations.expand_nutrition_facts_body, - panelId: KnowledgePanelCard.PANEL_NUTRITION_TABLE_ID, + _getTitle( + label: appLocalizations.settings_app_products, + ), + _getExpandPanel( + title: appLocalizations.expand_nutrition_facts, + subtitle: appLocalizations.expand_nutrition_facts_body, + panelId: KnowledgePanelCard.PANEL_NUTRITION_TABLE_ID, + ), + _getDivider(), + _getExpandPanel( + title: appLocalizations.expand_ingredients, + subtitle: appLocalizations.expand_ingredients_body, + panelId: KnowledgePanelCard.PANEL_INGREDIENTS_ID, + ), + if (CameraHelper.hasACamera) + _getTitle( + label: appLocalizations.settings_app_miscellaneous, ), - const UserPreferencesListItemDivider(), - _ExpandPanelHelper( - title: appLocalizations.expand_ingredients, - subtitle: appLocalizations.expand_ingredients_body, - panelId: KnowledgePanelCard.PANEL_INGREDIENTS_ID, + if (CameraHelper.hasACamera) + UserPreferencesItemSwitch( + title: appLocalizations.app_haptic_feedback_title, + subtitle: appLocalizations.app_haptic_feedback_subtitle, + value: userPreferences.hapticFeedbackEnabled, + onChanged: (final bool value) async => + userPreferences.setHapticFeedbackEnabled(value), ), - if (CameraHelper.hasACamera) - UserPreferencesTitle( - label: appLocalizations.settings_app_miscellaneous, - ), - if (CameraHelper.hasACamera) const UserPreferencesHapticFeedback(), - UserPreferencesTitle(label: appLocalizations.settings_app_data), - const UserPreferencesCrashReporting(), - const UserPreferencesListItemDivider(), - const UserPreferencesSendAnonymous(), - const UserPreferencesListItemDivider(), - const UserPreferencesAdvancedSettings(), - const UserPreferencesListItemDivider(), - const UserPreferencesRateUs(), - const UserPreferencesShareWithFriends(), - ]; + _getTitle(label: appLocalizations.settings_app_data), + UserPreferencesItemSwitch( + title: appLocalizations.crash_reporting_toggle_title, + subtitle: appLocalizations.crash_reporting_toggle_subtitle, + value: userPreferences.crashReports, + onChanged: (final bool value) async => + userPreferences.setCrashReports(value), + ), + _getDivider(), + UserPreferencesItemSwitch( + title: appLocalizations.send_anonymous_data_toggle_title, + subtitle: appLocalizations.send_anonymous_data_toggle_subtitle, + value: userPreferences.userTracking, + onChanged: (final bool allow) async => + userPreferences.setUserTracking(allow), + ), + _getDivider(), + UserPreferencesAdvancedSettings.getUserPreferencesItem(context), + _getDivider(), + UserPreferencesRateUs.getUserPreferencesItem(context), + UserPreferencesShareWithFriends.getUserPreferencesItem(context), + ]; + } + + UserPreferencesItem _getTitle({ + required final String label, + final bool addExtraPadding = true, + }) => + UserPreferencesItemSimple( + labels: [label], + builder: (_) => _UserPreferencesTitle( + label: label, + addExtraPadding: addExtraPadding, + ), + ); + + UserPreferencesItem _getDivider() => UserPreferencesItemSimple( + labels: [], + builder: (_) => const UserPreferencesListItemDivider(), + ); + + UserPreferencesItem _getExpandPanel({ + required String title, + required String subtitle, + required String panelId, + }) => + UserPreferencesItemSimple( + labels: [title, subtitle], + builder: (_) { + final String flagTag = KnowledgePanelCard.getExpandFlagTag(panelId); + return UserPreferencesSwitchWidget( + title: title, + subtitle: subtitle, + value: userPreferences.getFlag(flagTag) ?? false, + onChanged: (final bool value) async => + userPreferences.setFlag(flagTag, value), + ); + }, + ); } -class _ExpandPanelHelper extends StatelessWidget { - const _ExpandPanelHelper({ - required this.title, - required this.subtitle, - required this.panelId, - }); +class _UserPreferencesTitle extends StatelessWidget { + const _UserPreferencesTitle({ + required this.label, + this.addExtraPadding = true, + }) : assert(label.length > 0); - final String title; - final String subtitle; - final String panelId; + final String label; + final bool addExtraPadding; @override - Widget build(BuildContext context) { - final UserPreferences userPreferences = context.watch(); - final String flagTag = KnowledgePanelCard.getExpandFlagTag(panelId); - return UserPreferencesSwitchItem( - title: title, - subtitle: subtitle, - value: userPreferences.getFlag(flagTag) ?? false, - onChanged: (final bool value) async => - userPreferences.setFlag(flagTag, value), - ); - } + Widget build(BuildContext context) => SizedBox( + width: double.infinity, + child: Padding( + padding: EdgeInsetsDirectional.only( + top: addExtraPadding ? LARGE_SPACE : LARGE_SPACE, + bottom: SMALL_SPACE, + // Horizontal = same as ListTile + start: LARGE_SPACE, + end: LARGE_SPACE, + ), + child: Text( + label, + style: Theme.of(context).textTheme.displayLarge, + ), + ), + ); } diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_share_with_friends.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_share_with_friends.dart index 060773d9ae1..c6d28ccb72c 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_share_with_friends.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_share_with_friends.dart @@ -2,11 +2,24 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:share_plus/share_plus.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; class UserPreferencesShareWithFriends extends StatelessWidget { const UserPreferencesShareWithFriends(); + static UserPreferencesItem getUserPreferencesItem( + final BuildContext context, + ) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return UserPreferencesItemSimple( + labels: [ + appLocalizations.contribute_share_header, + ], + builder: (_) => const UserPreferencesShareWithFriends(), + ); + } + @override Widget build(BuildContext context) => UserPreferenceListTile( title: AppLocalizations.of(context).contribute_share_header, diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_widgets.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_widgets.dart index 4966cc41f82..76390f6fdb1 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_widgets.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_widgets.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:smooth_app/generic_lib/bottom_sheets/smooth_bottom_sheet.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; /// A dashed line class UserPreferencesListItemDivider extends StatelessWidget { @@ -62,45 +63,105 @@ class _DashedLinePainter extends CustomPainter { bool shouldRepaint(CustomPainter oldDelegate) => false; } -class UserPreferencesSwitchItem extends StatelessWidget { - const UserPreferencesSwitchItem({ +class UserPreferencesSwitchWidget extends StatelessWidget { + const UserPreferencesSwitchWidget({ required this.title, required this.subtitle, required this.value, required this.onChanged, - Key? key, - }) : super(key: key); + }); final String title; - final String subtitle; + final String? subtitle; final bool value; - final ValueChanged? onChanged; + final ValueChanged onChanged; @override - Widget build(BuildContext context) { - return SwitchListTile.adaptive( - title: Padding( - padding: const EdgeInsetsDirectional.only( - top: SMALL_SPACE, - bottom: SMALL_SPACE, - ), - child: Text(title, style: Theme.of(context).textTheme.headlineMedium), - ), - subtitle: Padding( - padding: const EdgeInsetsDirectional.only( - bottom: SMALL_SPACE, - ), - child: Text( - subtitle, - style: const TextStyle(height: 1.5), + Widget build(BuildContext context) => SwitchListTile.adaptive( + title: Padding( + padding: const EdgeInsetsDirectional.only( + top: SMALL_SPACE, + bottom: SMALL_SPACE, + ), + child: Text(title, style: Theme.of(context).textTheme.headlineMedium), ), - ), - activeColor: Theme.of(context).primaryColor, - value: value, - onChanged: onChanged, - isThreeLine: true, - ); - } + subtitle: subtitle == null + ? null + : Padding( + padding: const EdgeInsetsDirectional.only( + bottom: SMALL_SPACE, + ), + child: Text( + subtitle!, + style: const TextStyle(height: 1.5), + ), + ), + activeColor: Theme.of(context).primaryColor, + value: value, + onChanged: onChanged, + isThreeLine: subtitle != null, + ); +} + +class UserPreferencesItemSwitch implements UserPreferencesItem { + const UserPreferencesItemSwitch({ + required this.title, + this.subtitle, + required this.value, + required this.onChanged, + }); + + final String title; + final String? subtitle; + final bool value; + final ValueChanged onChanged; + + @override + List get labels => [ + title, + if (subtitle != null) subtitle!, + ]; + + @override + Widget Function(BuildContext) get builder => + (final BuildContext context) => UserPreferencesSwitchWidget( + title: title, + subtitle: subtitle, + value: value, + onChanged: onChanged, + ); +} + +class UserPreferencesItemTile implements UserPreferencesItem { + const UserPreferencesItemTile({ + required this.title, + this.subtitle, + this.onTap, + this.leading, + this.trailing, + }); + + final String title; + final String? subtitle; + final VoidCallback? onTap; + final Widget? leading; + final Widget? trailing; + + @override + List get labels => [ + title, + if (subtitle != null) subtitle!, + ]; + + @override + Widget Function(BuildContext) get builder => + (final BuildContext context) => ListTile( + title: Text(title), + subtitle: subtitle == null ? null : Text(subtitle!), + onTap: onTap, + leading: leading, + trailing: trailing, + ); } /// A preference allowing to choose between a list of items. @@ -340,43 +401,6 @@ class _ChoiceItem extends StatelessWidget { } } -class UserPreferencesTitle extends StatelessWidget { - const UserPreferencesTitle({ - required this.label, - this.addExtraPadding = true, - Key? key, - }) : assert(label.length > 0), - super(key: key); - - const UserPreferencesTitle.firstItem({ - required String label, - Key? key, - }) : this(label: label, addExtraPadding: false, key: key); - - final String label; - final bool addExtraPadding; - - @override - Widget build(BuildContext context) { - return SizedBox( - width: double.infinity, - child: Padding( - padding: EdgeInsetsDirectional.only( - top: addExtraPadding ? LARGE_SPACE : LARGE_SPACE, - bottom: SMALL_SPACE, - // Horizontal = same as ListTile - start: LARGE_SPACE, - end: LARGE_SPACE, - ), - child: Text( - label, - style: Theme.of(context).textTheme.displayLarge, - ), - ), - ); - } -} - class UserPreferenceListTile extends StatelessWidget { const UserPreferenceListTile({ required this.title,