diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 15d1c38bb..ca168c34d 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -102,6 +102,7 @@ "no": "No", "email": "Email", "support": "Support", + "advanced": "Advanced", "settings": "Settings", "copied": "Copied", "pleaseTryAgain": "Please try again", @@ -326,5 +327,7 @@ "signInToBackup": "Sign in to backup your codes", "singIn": "Sign in", "sigInBackupReminder": "Please export your codes to ensure that you have a backup you can restore from.", - "offlineModeWarning": "You have chosen to proceed without backups. Please take manual backups to make sure your codes are safe." + "offlineModeWarning": "You have chosen to proceed without backups. Please take manual backups to make sure your codes are safe.", + "showLargeIcons": "Show large icons", + "focusOnSearchBar": "Focus search on app start" } diff --git a/lib/services/preference_service.dart b/lib/services/preference_service.dart index f1c6c1c9a..57bfbefc6 100644 --- a/lib/services/preference_service.dart +++ b/lib/services/preference_service.dart @@ -11,6 +11,7 @@ class PreferenceService { static const kHasShownCoachMarkKey = "has_shown_coach_mark"; static const kShouldShowLargeIconsKey = "should_show_large_icons"; + static const kShouldAutoFocusOnSearchBar = "should_auto_focus_on_search_bar"; Future init() async { _prefs = await SharedPreferences.getInstance(); @@ -40,4 +41,17 @@ class PreferenceService { await _prefs.setBool(kShouldShowLargeIconsKey, value); Bus.instance.fire(IconsChangedEvent()); } + + bool shouldAutoFocusOnSearchBar() { + if (_prefs.containsKey(kShouldAutoFocusOnSearchBar)) { + return _prefs.getBool(kShouldAutoFocusOnSearchBar)!; + } else { + return false; + } + } + + Future setAutoFocusOnSearchBar(bool value) async { + await _prefs.setBool(kShouldAutoFocusOnSearchBar, value); + Bus.instance.fire(IconsChangedEvent()); + } } diff --git a/lib/ui/code_widget.dart b/lib/ui/code_widget.dart index 29cfe0005..d2a32ee4e 100644 --- a/lib/ui/code_widget.dart +++ b/lib/ui/code_widget.dart @@ -16,9 +16,7 @@ import 'package:ente_auth/utils/toast_util.dart'; import 'package:ente_auth/utils/totp_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; -import 'package:local_hero/local_hero.dart'; import 'package:logging/logging.dart'; -import 'package:uuid/uuid.dart'; class CodeWidget extends StatefulWidget { final Code code; @@ -37,7 +35,6 @@ class _CodeWidgetState extends State { bool _isInitialized = false; late bool hasConfiguredAccount; late bool _shouldShowLargeIcon; - final String _key = const Uuid().v4(); @override void initState() { @@ -152,116 +149,109 @@ class _CodeWidgetState extends State { } Widget _getCardContents(AppLocalizations l10n) { - return LocalHeroScope( - duration: const Duration(milliseconds: 200), - curve: Curves.easeInOut, - child: SizedBox( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (widget.code.type == Type.totp) - CodeTimerProgress( - period: widget.code.period, - ), - const SizedBox( - height: 16, + return SizedBox( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (widget.code.type == Type.totp) + CodeTimerProgress( + period: widget.code.period, ), - Row( - children: [ - _shouldShowLargeIcon ? _getIcon() : const SizedBox.shrink(), - Expanded( - child: Column( - children: [ - _getTopRow(), - const SizedBox(height: 4), - _getBottomRow(l10n), - ], - ), + const SizedBox( + height: 16, + ), + Row( + children: [ + _shouldShowLargeIcon ? _getIcon() : const SizedBox.shrink(), + Expanded( + child: Column( + children: [ + _getTopRow(), + const SizedBox(height: 4), + _getBottomRow(l10n), + ], ), - ], - ), - const SizedBox( - height: 20, - ), - ], - ), + ), + ], + ), + const SizedBox( + height: 20, + ), + ], ), ); } Widget _getBottomRow(AppLocalizations l10n) { - return LocalHero( - tag: _key + "_bottom_row", - child: Container( - padding: const EdgeInsets.only(left: 16, right: 16), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Expanded( - child: ValueListenableBuilder( - valueListenable: _currentCode, - builder: (context, value, child) { - return Material( - type: MaterialType.transparency, - child: Text( - _getFormattedCode(value), - style: const TextStyle(fontSize: 24), - ), - ); - }, - ), + return Container( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: ValueListenableBuilder( + valueListenable: _currentCode, + builder: (context, value, child) { + return Material( + type: MaterialType.transparency, + child: Text( + _getFormattedCode(value), + style: const TextStyle(fontSize: 24), + ), + ); + }, ), - widget.code.type == Type.totp - ? GestureDetector( - onTap: () { - _copyNextToClipboard(); - }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - l10n.nextTotpTitle, - style: Theme.of(context).textTheme.bodySmall, - ), - ValueListenableBuilder( - valueListenable: _nextCode, - builder: (context, value, child) { - return Material( - type: MaterialType.transparency, - child: Text( - _getFormattedCode(value), - style: const TextStyle( - fontSize: 18, - color: Colors.grey, - ), - ), - ); - }, - ), - ], - ), - ) - : Column( + ), + widget.code.type == Type.totp + ? GestureDetector( + onTap: () { + _copyNextToClipboard(); + }, + child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( l10n.nextTotpTitle, style: Theme.of(context).textTheme.bodySmall, ), - InkWell( - onTap: _onNextHotpTapped, - child: const Icon( - Icons.forward_outlined, - size: 32, - color: Colors.grey, - ), + ValueListenableBuilder( + valueListenable: _nextCode, + builder: (context, value, child) { + return Material( + type: MaterialType.transparency, + child: Text( + _getFormattedCode(value), + style: const TextStyle( + fontSize: 18, + color: Colors.grey, + ), + ), + ); + }, ), ], ), - ], - ), + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + l10n.nextTotpTitle, + style: Theme.of(context).textTheme.bodySmall, + ), + InkWell( + onTap: _onNextHotpTapped, + child: const Icon( + Icons.forward_outlined, + size: 32, + color: Colors.grey, + ), + ), + ], + ), + ], ), ); } @@ -273,25 +263,22 @@ class _CodeWidgetState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocalHero( - tag: _key + "_top_row", - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - safeDecode(widget.code.issuer).trim(), - style: Theme.of(context).textTheme.titleLarge, - ), - const SizedBox(height: 2), - Text( - safeDecode(widget.code.account).trim(), - style: Theme.of(context).textTheme.bodySmall?.copyWith( - fontSize: 12, - color: Colors.grey, - ), - ), - ], - ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + safeDecode(widget.code.issuer).trim(), + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 2), + Text( + safeDecode(widget.code.account).trim(), + style: Theme.of(context).textTheme.bodySmall?.copyWith( + fontSize: 12, + color: Colors.grey, + ), + ), + ], ), Row( mainAxisAlignment: MainAxisAlignment.end, @@ -314,21 +301,13 @@ class _CodeWidgetState extends State { } Widget _getIcon() { - return LocalHero( - tag: _key, - child: Padding( - padding: _shouldShowLargeIcon - ? const EdgeInsets.only(left: 16) - : const EdgeInsets.all(0), - child: GestureDetector( - onTap: () { - PreferenceService.instance.setShowLargeIcons(!_shouldShowLargeIcon); - }, - child: IconUtils.instance.getIcon( - safeDecode(widget.code.issuer).trim(), - width: _shouldShowLargeIcon ? 42 : 24, - ), - ), + return Padding( + padding: _shouldShowLargeIcon + ? const EdgeInsets.only(left: 16) + : const EdgeInsets.all(0), + child: IconUtils.instance.getIcon( + safeDecode(widget.code.issuer).trim(), + width: _shouldShowLargeIcon ? 42 : 24, ), ); } diff --git a/lib/ui/home_page.dart b/lib/ui/home_page.dart index f7f07c21e..41b03a182 100644 --- a/lib/ui/home_page.dart +++ b/lib/ui/home_page.dart @@ -74,6 +74,7 @@ class _HomePageState extends State { _iconsChangedEvent = Bus.instance.on().listen((event) { setState(() {}); }); + _showSearchBox = PreferenceService.instance.shouldAutoFocusOnSearchBar(); } void _loadCodes() { @@ -177,7 +178,7 @@ class _HomePageState extends State { resizeToAvoidBottomInset: false, appBar: AppBar( title: !_showSearchBox - ? const Text('ente Authenticator') + ? const Text('ente Auth') : TextField( autofocus: _searchText.isEmpty, controller: _textController, @@ -188,6 +189,7 @@ class _HomePageState extends State { decoration: InputDecoration( hintText: l10n.searchHint, border: InputBorder.none, + focusedBorder: InputBorder.none, ), ), actions: [ diff --git a/lib/ui/settings/advanced_section_widget.dart b/lib/ui/settings/advanced_section_widget.dart new file mode 100644 index 000000000..d7f54e41b --- /dev/null +++ b/lib/ui/settings/advanced_section_widget.dart @@ -0,0 +1,83 @@ +import 'package:ente_auth/core/logging/super_logging.dart'; +import 'package:ente_auth/l10n/l10n.dart'; +import 'package:ente_auth/services/preference_service.dart'; +import 'package:ente_auth/ui/components/captioned_text_widget.dart'; +import 'package:ente_auth/ui/components/expandable_menu_item_widget.dart'; +import 'package:ente_auth/ui/components/menu_item_widget.dart'; +import 'package:ente_auth/ui/components/toggle_switch_widget.dart'; +import 'package:ente_auth/ui/settings/common_settings.dart'; +import 'package:flutter/material.dart'; + +class AdvancedSectionWidget extends StatefulWidget { + const AdvancedSectionWidget({Key? key}) : super(key: key); + + @override + State createState() => _AdvancedSectionWidgetState(); +} + +class _AdvancedSectionWidgetState extends State { + @override + Widget build(BuildContext context) { + final l10n = context.l10n; + return ExpandableMenuItemWidget( + title: l10n.advanced, + selectionOptionsWidget: _getSectionOptions(context), + leadingIcon: Icons.graphic_eq, + ); + } + + Widget _getSectionOptions(BuildContext context) { + final l10n = context.l10n; + return Column( + children: [ + sectionOptionSpacing, + MenuItemWidget( + captionedTextWidget: CaptionedTextWidget( + title: l10n.showLargeIcons, + ), + trailingWidget: ToggleSwitchWidget( + value: () => PreferenceService.instance.shouldShowLargeIcons(), + onChanged: () async { + await PreferenceService.instance.setShowLargeIcons( + !PreferenceService.instance.shouldShowLargeIcons(), + ); + setState(() {}); + }, + ), + ), + sectionOptionSpacing, + MenuItemWidget( + captionedTextWidget: CaptionedTextWidget( + title: l10n.focusOnSearchBar, + ), + trailingWidget: ToggleSwitchWidget( + value: () => + PreferenceService.instance.shouldAutoFocusOnSearchBar(), + onChanged: () async { + await PreferenceService.instance.setAutoFocusOnSearchBar( + !PreferenceService.instance.shouldAutoFocusOnSearchBar(), + ); + setState(() {}); + }, + ), + ), + sectionOptionSpacing, + MenuItemWidget( + captionedTextWidget: CaptionedTextWidget( + title: l10n.crashAndErrorReporting, + ), + trailingWidget: ToggleSwitchWidget( + value: () => SuperLogging.shouldReportErrors(), + onChanged: () async { + await SuperLogging.setShouldReportErrors( + !SuperLogging.shouldReportErrors(), + ); + setState(() {}); + }, + ), + ), + sectionOptionSpacing, + ], + ); + } +} diff --git a/lib/ui/settings/support_section_widget.dart b/lib/ui/settings/support_section_widget.dart index 05ef9e992..263fe823f 100644 --- a/lib/ui/settings/support_section_widget.dart +++ b/lib/ui/settings/support_section_widget.dart @@ -1,11 +1,9 @@ import 'package:ente_auth/core/constants.dart'; -import 'package:ente_auth/core/logging/super_logging.dart'; import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/theme/ente_theme.dart'; import 'package:ente_auth/ui/components/captioned_text_widget.dart'; import 'package:ente_auth/ui/components/expandable_menu_item_widget.dart'; import 'package:ente_auth/ui/components/menu_item_widget.dart'; -import 'package:ente_auth/ui/components/toggle_switch_widget.dart'; import 'package:ente_auth/ui/settings/common_settings.dart'; import 'package:ente_auth/ui/settings/faq.dart'; import 'package:ente_auth/utils/email_util.dart'; @@ -35,7 +33,6 @@ class _SupportSectionWidgetState extends State { return Column( children: [ sectionOptionSpacing, - MenuItemWidget( captionedTextWidget: CaptionedTextWidget( title: l10n.faq, @@ -98,21 +95,6 @@ class _SupportSectionWidgetState extends State { }, ), sectionOptionSpacing, - MenuItemWidget( - captionedTextWidget: CaptionedTextWidget( - title: l10n.crashAndErrorReporting, - ), - trailingWidget: ToggleSwitchWidget( - value: () => SuperLogging.shouldReportErrors(), - onChanged: () async { - await SuperLogging.setShouldReportErrors( - !SuperLogging.shouldReportErrors(), - ); - setState(() {}); - }, - ), - ), - sectionOptionSpacing, ], ); } diff --git a/lib/ui/settings_page.dart b/lib/ui/settings_page.dart index e40695e3c..0dbedaeec 100644 --- a/lib/ui/settings_page.dart +++ b/lib/ui/settings_page.dart @@ -14,6 +14,7 @@ import 'package:ente_auth/ui/settings/account_section_widget.dart'; import 'package:ente_auth/ui/settings/app_version_widget.dart'; import 'package:ente_auth/ui/settings/data/data_section_widget.dart'; import 'package:ente_auth/ui/settings/data/export_widget.dart'; +import 'package:ente_auth/ui/settings/advanced_section_widget.dart'; import 'package:ente_auth/ui/settings/security_section_widget.dart'; import 'package:ente_auth/ui/settings/social_section_widget.dart'; import 'package:ente_auth/ui/settings/support_dev_widget.dart'; @@ -125,6 +126,8 @@ class SettingsPage extends StatelessWidget { } contents.addAll([ + const AdvancedSectionWidget(), + sectionSpacing, const SupportSectionWidget(), sectionSpacing, const SocialSectionWidget(), diff --git a/pubspec.lock b/pubspec.lock index ef62acde9..79b0ae47f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -783,14 +783,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" - local_hero: - dependency: "direct main" - description: - name: local_hero - sha256: "2dd2904c46d786dbc6f7179ba863e04f2be1fd603c530501a336a07744b60c7b" - url: "https://pub.dev" - source: hosted - version: "0.2.0" logging: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 4f5eb5309..e177206b2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: ente_auth description: ente two-factor authenticator -version: 2.0.1+201 +version: 2.0.2+202 publish_to: none environment: @@ -51,7 +51,6 @@ dependencies: intl: ^0.18.0 json_annotation: ^4.5.0 local_auth: ^2.1.3 - local_hero: ^0.2.0 logging: ^1.0.1 modal_bottom_sheet: ^3.0.0-pre move_to_background: ^1.0.2