✨ Auth preferences
This commit is contained in:
		| @@ -477,5 +477,9 @@ | ||||
|   "agedTheme": "Old school style theme", | ||||
|   "agedThemeDesc": "Downgrade the global theme to Material Design 2. Unexpected issues may occur. For experimental use only.", | ||||
|   "appBackgroundImage": "Global background image", | ||||
|   "appBackgroundImageDesc": "The global background image will be displayed on all pages" | ||||
|   "appBackgroundImageDesc": "The global background image will be displayed on all pages", | ||||
|   "authPreferences": "Auth preferences", | ||||
|   "authPreferencesDesc": "Set the security behavior of your account", | ||||
|   "authMaximumAuthSteps": "Maximum authentication steps", | ||||
|   "authMaximumAuthStepsDesc": "The maximum number of authentication steps when logging in, higher value is more secure, lower value is more convenient; default is 2" | ||||
| } | ||||
|   | ||||
| @@ -473,5 +473,9 @@ | ||||
|   "agedTheme": "过时主题", | ||||
|   "agedThemeDesc": "将全局主题降级为 Material Design 2,可能发生意料之外的问题,仅供实验使用", | ||||
|   "appBackgroundImage": "全局背景图片", | ||||
|   "appBackgroundImageDesc": "全局背景图片将会在所有页面中展示" | ||||
|   "appBackgroundImageDesc": "全局背景图片将会在所有页面中展示", | ||||
|   "authPreferences": "安全偏好设置", | ||||
|   "authPreferencesDesc": "调整账号的安全行为模式", | ||||
|   "authMaximumAuthSteps": "最大认证步数", | ||||
|   "authMaximumAuthStepsDesc": "登陆时最多的验证步数,值越高则越安全,反之则会相对方便;默认设置为 2" | ||||
| } | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import 'package:solian/screens/about.dart'; | ||||
| import 'package:solian/screens/account.dart'; | ||||
| import 'package:solian/screens/account/friend.dart'; | ||||
| import 'package:solian/screens/account/preferences/notifications.dart'; | ||||
| import 'package:solian/screens/account/preferences/security.dart'; | ||||
| import 'package:solian/screens/account/profile_edit.dart'; | ||||
| import 'package:solian/screens/account/profile_page.dart'; | ||||
| import 'package:solian/screens/auth/signin.dart'; | ||||
| @@ -264,6 +265,14 @@ abstract class AppRouter { | ||||
|           child: const NotificationPreferencesScreen(), | ||||
|         ), | ||||
|       ), | ||||
|       GoRoute( | ||||
|         path: '/account/preferences/auth', | ||||
|         name: 'authPreferences', | ||||
|         builder: (context, state) => TitleShell( | ||||
|           state: state, | ||||
|           child: const AuthPreferencesScreen(), | ||||
|         ), | ||||
|       ), | ||||
|       GoRoute( | ||||
|         path: '/account/view/:name', | ||||
|         name: 'accountProfilePage', | ||||
|   | ||||
| @@ -129,6 +129,15 @@ class _AccountScreenState extends State<AccountScreen> { | ||||
|                   AppRouter.instance.pushNamed('settings'); | ||||
|                 }, | ||||
|               ), | ||||
|               if (auth.isAuthorized.value) | ||||
|                 ListTile( | ||||
|                   leading: const Icon(Icons.lock), | ||||
|                   contentPadding: const EdgeInsets.symmetric(horizontal: 34), | ||||
|                   title: Text('authPreferences'.tr), | ||||
|                   onTap: () { | ||||
|                     AppRouter.instance.pushNamed('authPreferences'); | ||||
|                   }, | ||||
|                 ), | ||||
|               if (auth.isAuthorized.value) | ||||
|                 ListTile( | ||||
|                   contentPadding: const EdgeInsets.symmetric(horizontal: 34), | ||||
|   | ||||
| @@ -59,10 +59,10 @@ class _NotificationPreferencesScreenState | ||||
|     }); | ||||
|     if (resp.statusCode != 200) { | ||||
|       context.showErrorDialog(RequestException(resp)); | ||||
|     } else { | ||||
|       context.showSnackbar('preferencesApplied'.tr); | ||||
|     } | ||||
|  | ||||
|     context.showSnackbar('preferencesApplied'.tr); | ||||
|  | ||||
|     setState(() => _isBusy = false); | ||||
|   } | ||||
|  | ||||
|   | ||||
							
								
								
									
										118
									
								
								lib/screens/account/preferences/security.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								lib/screens/account/preferences/security.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | ||||
| import 'package:flutter_animate/flutter_animate.dart'; | ||||
| import 'package:get/get.dart'; | ||||
| import 'package:get/get_connect/http/src/exceptions/exceptions.dart'; | ||||
| import 'package:solian/exceptions/request.dart'; | ||||
| import 'package:solian/exts.dart'; | ||||
| import 'package:solian/providers/auth.dart'; | ||||
|  | ||||
| class AuthPreferencesScreen extends StatefulWidget { | ||||
|   const AuthPreferencesScreen({super.key}); | ||||
|  | ||||
|   @override | ||||
|   State<AuthPreferencesScreen> createState() => _AuthPreferencesScreenState(); | ||||
| } | ||||
|  | ||||
| class _AuthPreferencesScreenState extends State<AuthPreferencesScreen> { | ||||
|   bool _isBusy = true; | ||||
|  | ||||
|   Map<String, dynamic> _config = { | ||||
|     'maximum_auth_steps': 2, | ||||
|   }; | ||||
|  | ||||
|   Future<void> _getPreferences() async { | ||||
|     setState(() => _isBusy = true); | ||||
|  | ||||
|     final auth = Get.find<AuthProvider>(); | ||||
|     if (!auth.isAuthorized.value) throw UnauthorizedException(); | ||||
|  | ||||
|     final client = await auth.configureClient('id'); | ||||
|     final resp = await client.get('/preferences/auth'); | ||||
|     if (resp.statusCode != 200 && resp.statusCode != 404) { | ||||
|       context.showErrorDialog(RequestException(resp)); | ||||
|     } | ||||
|  | ||||
|     if (resp.statusCode == 200) { | ||||
|       _config = resp.body; | ||||
|     } | ||||
|  | ||||
|     setState(() => _isBusy = false); | ||||
|   } | ||||
|  | ||||
|   Future<void> _savePreferences() async { | ||||
|     setState(() => _isBusy = true); | ||||
|  | ||||
|     final auth = Get.find<AuthProvider>(); | ||||
|     if (!auth.isAuthorized.value) throw UnauthorizedException(); | ||||
|  | ||||
|     final client = await auth.configureClient('id'); | ||||
|     final resp = await client.put('/preferences/auth', _config); | ||||
|     if (resp.statusCode != 200) { | ||||
|       context.showErrorDialog(RequestException(resp)); | ||||
|     } else { | ||||
|       context.showSnackbar('preferencesApplied'.tr); | ||||
|     } | ||||
|  | ||||
|     setState(() => _isBusy = false); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     _getPreferences(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Column( | ||||
|       children: [ | ||||
|         if (_isBusy) const LinearProgressIndicator().animate().scaleX(), | ||||
|         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( | ||||
|             children: [ | ||||
|               ListTile( | ||||
|                 title: Text('authMaximumAuthSteps'.tr), | ||||
|                 subtitle: Text('authMaximumAuthStepsDesc'.tr), | ||||
|                 contentPadding: const EdgeInsets.symmetric(horizontal: 24), | ||||
|                 trailing: SizedBox( | ||||
|                   width: 60, | ||||
|                   child: _isBusy | ||||
|                       ? null | ||||
|                       : TextFormField( | ||||
|                           decoration: InputDecoration( | ||||
|                             border: const OutlineInputBorder(), | ||||
|                             isDense: true, | ||||
|                           ), | ||||
|                           initialValue: | ||||
|                               _config['maximum_auth_steps']?.toString() ?? '2', | ||||
|                           keyboardType: TextInputType.number, | ||||
|                           inputFormatters: [ | ||||
|                             FilteringTextInputFormatter.digitsOnly | ||||
|                           ], | ||||
|                           onTapOutside: (_) => | ||||
|                               FocusManager.instance.primaryFocus?.unfocus(), | ||||
|                           onChanged: (value) { | ||||
|                             _config['maximum_auth_steps'] = | ||||
|                                 int.tryParse(value) ?? 2; | ||||
|                           }, | ||||
|                         ), | ||||
|                 ), | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -389,10 +389,14 @@ class _DashboardScreenState extends State<DashboardScreen> { | ||||
|                                 onUpdate: (_) { | ||||
|                                   _pullPosts(); | ||||
|                                 }, | ||||
|                                 padding: EdgeInsets.symmetric( | ||||
|                                   vertical: 8, | ||||
|                                   horizontal: 4, | ||||
|                                 ), | ||||
|                                 backgroundColor: Theme.of(context) | ||||
|                                     .colorScheme | ||||
|                                     .surfaceContainerLow, | ||||
|                               ).paddingAll(8), | ||||
|                               ), | ||||
|                             ), | ||||
|                           ), | ||||
|                         ).paddingSymmetric(horizontal: 8), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user