✨ Auth preference screen
This commit is contained in:
		| @@ -859,5 +859,12 @@ | ||||
|   "notificationTopicPostSubscription": "Post Subscriptions", | ||||
|   "notificationTopicMessaging": "New Messages", | ||||
|   "notificationTopicMessagingCall": "Incoming Calls", | ||||
|   "notificationTopicGeneral": "General" | ||||
|   "notificationTopicGeneral": "General", | ||||
|   "authMaximumAuthSteps": "Maximum Authenticate Steps", | ||||
|   "authMaximumAuthStepsDescription": { | ||||
|     "one": "Maximum ask for {} step authenticate", | ||||
|     "other": "Maximum ask for {} steps authenticate" | ||||
|   }, | ||||
|   "authAlwaysRisky": "Always Risky", | ||||
|   "authAlwaysRiskyDescription": "Always ask for the highest steps count of authentication when logging in." | ||||
| } | ||||
|   | ||||
| @@ -857,5 +857,12 @@ | ||||
|   "notificationTopicPostSubscription": "帖子订阅", | ||||
|   "notificationTopicMessaging": "消息", | ||||
|   "notificationTopicMessagingCall": "通话", | ||||
|   "notificationTopicGeneral": "杂项" | ||||
|   "notificationTopicGeneral": "杂项", | ||||
|   "authMaximumAuthSteps": "最大验证步骤", | ||||
|   "authMaximumAuthStepsDescription": { | ||||
|     "one": "登入时最多要求 {} 步验证", | ||||
|     "other": "登入时最多要求 {} 步验证" | ||||
|   }, | ||||
|   "authAlwaysRisky": "总是风险", | ||||
|   "authAlwaysRiskyDescription": "在登入时始终按最高标准要求验证。" | ||||
| } | ||||
|   | ||||
| @@ -846,5 +846,23 @@ | ||||
|   "translated": "已翻譯", | ||||
|   "settingsAutoTranslate": "自動翻譯", | ||||
|   "settingsAutoTranslateDescription": "在查看帖子、消息時自動翻譯文本。", | ||||
|   "trayMenuHide": "隱藏" | ||||
|   "trayMenuHide": "隱藏", | ||||
|   "accountSettingsNotify": "通知設置", | ||||
|   "accountSettingsNotifyDescription": "調整你所收到的通知種類。", | ||||
|   "accountSettingsSecurity": "安全設置", | ||||
|   "accountSettingsSecurityDescription": "調整你的帳户安全設置。", | ||||
|   "save": "保存", | ||||
|   "notificationTopicPostFeedback": "帖子數據反饋", | ||||
|   "notificationTopicPostReply": "帖子回覆", | ||||
|   "notificationTopicPostSubscription": "帖子訂閲", | ||||
|   "notificationTopicMessaging": "消息", | ||||
|   "notificationTopicMessagingCall": "通話", | ||||
|   "notificationTopicGeneral": "雜項", | ||||
|   "authMaximumAuthSteps": "最大驗證步驟", | ||||
|   "authMaximumAuthStepsDescription": { | ||||
|     "one": "登入時最多要求 {} 步驗證", | ||||
|     "other": "登入時最多要求 {} 步驗證" | ||||
|   }, | ||||
|   "authAlwaysRisky": "總是風險", | ||||
|   "authAlwaysRiskyDescription": "在登入時始終按最高標準要求驗證。" | ||||
| } | ||||
|   | ||||
| @@ -846,5 +846,23 @@ | ||||
|   "translated": "已翻譯", | ||||
|   "settingsAutoTranslate": "自動翻譯", | ||||
|   "settingsAutoTranslateDescription": "在查看帖子、消息時自動翻譯文本。", | ||||
|   "trayMenuHide": "隱藏" | ||||
|   "trayMenuHide": "隱藏", | ||||
|   "accountSettingsNotify": "通知設置", | ||||
|   "accountSettingsNotifyDescription": "調整你所收到的通知種類。", | ||||
|   "accountSettingsSecurity": "安全設置", | ||||
|   "accountSettingsSecurityDescription": "調整你的帳戶安全設置。", | ||||
|   "save": "保存", | ||||
|   "notificationTopicPostFeedback": "帖子數據反饋", | ||||
|   "notificationTopicPostReply": "帖子回覆", | ||||
|   "notificationTopicPostSubscription": "帖子訂閱", | ||||
|   "notificationTopicMessaging": "消息", | ||||
|   "notificationTopicMessagingCall": "通話", | ||||
|   "notificationTopicGeneral": "雜項", | ||||
|   "authMaximumAuthSteps": "最大驗證步驟", | ||||
|   "authMaximumAuthStepsDescription": { | ||||
|     "one": "登入時最多要求 {} 步驗證", | ||||
|     "other": "登入時最多要求 {} 步驗證" | ||||
|   }, | ||||
|   "authAlwaysRisky": "總是風險", | ||||
|   "authAlwaysRiskyDescription": "在登入時始終按最高標準要求驗證。" | ||||
| } | ||||
|   | ||||
| @@ -10,6 +10,7 @@ import 'package:surface/screens/account/contact_methods.dart'; | ||||
| import 'package:surface/screens/account/factor_settings.dart'; | ||||
| import 'package:surface/screens/account/keypairs.dart'; | ||||
| import 'package:surface/screens/account/prefs/notify.dart'; | ||||
| import 'package:surface/screens/account/prefs/security.dart'; | ||||
| import 'package:surface/screens/account/profile_page.dart'; | ||||
| import 'package:surface/screens/account/profile_edit.dart'; | ||||
| import 'package:surface/screens/account/publishers/publisher_edit.dart'; | ||||
| @@ -168,6 +169,11 @@ final _appRoutes = [ | ||||
|             name: 'accountSettingsNotify', | ||||
|             builder: (context, state) => const AccountNotifyPrefsScreen(), | ||||
|           ), | ||||
|           GoRoute( | ||||
|             path: '/auth', | ||||
|             name: 'accountSettingsSecurity', | ||||
|             builder: (context, state) => const AccountSecurityPrefsScreen(), | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|       GoRoute( | ||||
|   | ||||
| @@ -107,6 +107,16 @@ class AccountSettingsScreen extends StatelessWidget { | ||||
|                 GoRouter.of(context).pushNamed('accountSettingsNotify'); | ||||
|               }, | ||||
|             ), | ||||
|             ListTile( | ||||
|               title: Text('accountSettingsSecurity').tr(), | ||||
|               subtitle: Text('accountSettingsSecurityDescription').tr(), | ||||
|               contentPadding: const EdgeInsets.symmetric(horizontal: 24), | ||||
|               leading: const Icon(Symbols.shield), | ||||
|               trailing: const Icon(Symbols.chevron_right), | ||||
|               onTap: () { | ||||
|                 GoRouter.of(context).pushNamed('accountSettingsSecurity'); | ||||
|               }, | ||||
|             ), | ||||
|             ListTile( | ||||
|               title: Text('accountProfileEdit').tr(), | ||||
|               subtitle: Text('accountProfileEditSubtitle').tr(), | ||||
|   | ||||
							
								
								
									
										147
									
								
								lib/screens/account/prefs/security.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								lib/screens/account/prefs/security.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:material_symbols_icons/symbols.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:surface/providers/sn_network.dart'; | ||||
| import 'package:surface/widgets/dialog.dart'; | ||||
| import 'package:surface/widgets/loading_indicator.dart'; | ||||
| import 'package:surface/widgets/navigation/app_scaffold.dart'; | ||||
|  | ||||
| class AccountSecurityPrefsScreen extends StatefulWidget { | ||||
|   const AccountSecurityPrefsScreen({super.key}); | ||||
|  | ||||
|   @override | ||||
|   State<AccountSecurityPrefsScreen> createState() => | ||||
|       _AccountSecurityPrefsScreenState(); | ||||
| } | ||||
|  | ||||
| class _AccountSecurityPrefsScreenState | ||||
|     extends State<AccountSecurityPrefsScreen> { | ||||
|   bool _isBusy = true; | ||||
|  | ||||
|   Map<String, dynamic> _config = { | ||||
|     'maximum_auth_steps': 2, | ||||
|     'always_risky': false, | ||||
|   }; | ||||
|  | ||||
|   Future<void> _getPreferences() async { | ||||
|     setState(() => _isBusy = true); | ||||
|  | ||||
|     final sn = context.read<SnNetworkProvider>(); | ||||
|  | ||||
|     try { | ||||
|       final resp = await sn.client.get('/cgi/id/preferences/auth'); | ||||
|       _config = resp.data['config'] | ||||
|           .map((k, v) => MapEntry(k, v as bool)) | ||||
|           .cast<String, bool>(); | ||||
|     } finally { | ||||
|       setState(() => _isBusy = false); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future<void> _savePreferences() async { | ||||
|     setState(() => _isBusy = true); | ||||
|  | ||||
|     final sn = context.read<SnNetworkProvider>(); | ||||
|  | ||||
|     try { | ||||
|       await sn.client.put( | ||||
|         '/cgi/id/preferences/auth', | ||||
|         data: { | ||||
|           'config': _config, | ||||
|         }, | ||||
|       ); | ||||
|       if (!mounted) return; | ||||
|       context.showSnackbar('accountSettingsApplied'.tr()); | ||||
|     } catch (err) { | ||||
|       if (!mounted) return; | ||||
|       context.showErrorDialog(err); | ||||
|     } finally { | ||||
|       setState(() => _isBusy = false); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     _getPreferences(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return AppScaffold( | ||||
|       appBar: AppBar( | ||||
|         leading: const PageBackButton(), | ||||
|         title: Text('accountSettingsSecurity').tr(), | ||||
|       ), | ||||
|       body: Column( | ||||
|         children: [ | ||||
|           LoadingIndicator(isActive: _isBusy), | ||||
|           ListTile( | ||||
|             tileColor: Theme.of(context).colorScheme.surfaceContainer, | ||||
|             contentPadding: const EdgeInsets.symmetric(horizontal: 24), | ||||
|             leading: const Icon(Icons.save), | ||||
|             title: Text('save').tr(), | ||||
|             enabled: !_isBusy, | ||||
|             onTap: () { | ||||
|               _savePreferences(); | ||||
|             }, | ||||
|           ), | ||||
|           Expanded( | ||||
|             child: ListView( | ||||
|               padding: EdgeInsets.zero, | ||||
|               children: [ | ||||
|                 ListTile( | ||||
|                   title: Text('authMaximumAuthSteps').tr(), | ||||
|                   subtitle: Text('authMaximumAuthStepsDescription') | ||||
|                       .plural(_config['maximum_auth_steps'] ?? 2), | ||||
|                   contentPadding: const EdgeInsets.symmetric(horizontal: 24), | ||||
|                   trailing: Row( | ||||
|                     mainAxisSize: MainAxisSize.min, | ||||
|                     children: [ | ||||
|                       IconButton( | ||||
|                         padding: EdgeInsets.zero, | ||||
|                         visualDensity: const VisualDensity( | ||||
|                           horizontal: -4, | ||||
|                           vertical: -4, | ||||
|                         ), | ||||
|                         icon: const Icon(Symbols.remove), | ||||
|                         onPressed: () { | ||||
|                           if (_config['maximum_auth_steps'] > 1) { | ||||
|                             setState(() => _config['maximum_auth_steps']--); | ||||
|                           } | ||||
|                         }, | ||||
|                       ), | ||||
|                       IconButton( | ||||
|                         padding: EdgeInsets.zero, | ||||
|                         visualDensity: const VisualDensity( | ||||
|                           horizontal: -4, | ||||
|                           vertical: -4, | ||||
|                         ), | ||||
|                         icon: const Icon(Symbols.add), | ||||
|                         onPressed: () { | ||||
|                           if (_config['maximum_auth_steps'] < 99) { | ||||
|                             setState(() => _config['maximum_auth_steps']++); | ||||
|                           } | ||||
|                         }, | ||||
|                       ), | ||||
|                     ], | ||||
|                   ), | ||||
|                 ), | ||||
|                 CheckboxListTile( | ||||
|                   title: Text('authAlwaysRisky').tr(), | ||||
|                   subtitle: Text('authAlwaysRiskyDescription').tr(), | ||||
|                   contentPadding: const EdgeInsets.symmetric(horizontal: 24), | ||||
|                   value: _config['always_risky'] ?? false, | ||||
|                   onChanged: (value) { | ||||
|                     setState(() => _config['always_risky'] = value); | ||||
|                   }, | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user