Update settings (#240)
This commit is contained in:
commit
ad3ae560bd
9 changed files with 217 additions and 160 deletions
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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<void> 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<void> setAutoFocusOnSearchBar(bool value) async {
|
||||
await _prefs.setBool(kShouldAutoFocusOnSearchBar, value);
|
||||
Bus.instance.fire(IconsChangedEvent());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<CodeWidget> {
|
|||
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<CodeWidget> {
|
|||
}
|
||||
|
||||
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<String>(
|
||||
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<String>(
|
||||
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<String>(
|
||||
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<String>(
|
||||
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<CodeWidget> {
|
|||
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<CodeWidget> {
|
|||
}
|
||||
|
||||
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,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ class _HomePageState extends State<HomePage> {
|
|||
_iconsChangedEvent = Bus.instance.on<IconsChangedEvent>().listen((event) {
|
||||
setState(() {});
|
||||
});
|
||||
_showSearchBox = PreferenceService.instance.shouldAutoFocusOnSearchBar();
|
||||
}
|
||||
|
||||
void _loadCodes() {
|
||||
|
@ -177,7 +178,7 @@ class _HomePageState extends State<HomePage> {
|
|||
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<HomePage> {
|
|||
decoration: InputDecoration(
|
||||
hintText: l10n.searchHint,
|
||||
border: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
|
|
83
lib/ui/settings/advanced_section_widget.dart
Normal file
83
lib/ui/settings/advanced_section_widget.dart
Normal file
|
@ -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<AdvancedSectionWidget> createState() => _AdvancedSectionWidgetState();
|
||||
}
|
||||
|
||||
class _AdvancedSectionWidgetState extends State<AdvancedSectionWidget> {
|
||||
@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,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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<SupportSectionWidget> {
|
|||
return Column(
|
||||
children: [
|
||||
sectionOptionSpacing,
|
||||
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: CaptionedTextWidget(
|
||||
title: l10n.faq,
|
||||
|
@ -98,21 +95,6 @@ class _SupportSectionWidgetState extends State<SupportSectionWidget> {
|
|||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: CaptionedTextWidget(
|
||||
title: l10n.crashAndErrorReporting,
|
||||
),
|
||||
trailingWidget: ToggleSwitchWidget(
|
||||
value: () => SuperLogging.shouldReportErrors(),
|
||||
onChanged: () async {
|
||||
await SuperLogging.setShouldReportErrors(
|
||||
!SuperLogging.shouldReportErrors(),
|
||||
);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue