From e11195f4fc4839d227914cbf178c85923c5b084d Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 14 Apr 2024 11:48:51 +0200 Subject: [PATCH] design: Adjust settings design --- assets/l10n/intl_en.arb | 10 +- lib/config/themes.dart | 3 + lib/pages/bootstrap/bootstrap_dialog.dart | 5 +- lib/pages/chat_details/chat_details_view.dart | 10 +- .../device_settings/device_settings_view.dart | 20 +- .../homeserver_picker_view.dart | 7 +- lib/pages/settings/settings_view.dart | 15 +- .../settings_chat/settings_chat_view.dart | 6 - .../settings_notifications.dart | 49 ++++- .../settings_notifications_view.dart | 40 ++-- .../settings_password_view.dart | 11 +- .../settings_security/settings_security.dart | 3 + .../settings_security_view.dart | 179 +++++++++--------- .../settings_style/settings_style_view.dart | 23 ++- 14 files changed, 241 insertions(+), 140 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index baa741ad5..acf074308 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -1384,6 +1384,9 @@ "type": "text", "placeholders": {} }, + "overview": "Overview", + "notifyMeFor": "Notify me for", + "passwordRecoverySettings": "Password recovery settings", "passwordRecovery": "Password recovery", "@passwordRecovery": { "type": "text", @@ -2602,5 +2605,10 @@ "unread": {} } }, - "noDatabaseEncryption": "Database encryption is not supported on this platform" + "noDatabaseEncryption": "Database encryption is not supported on this platform", + "thereAreCountUsersBlocked": "Right now there are {count} users blocked.", + "@thereAreCountUsersBlocked": { + "type": "text", + "count": {} + } } diff --git a/lib/config/themes.dart b/lib/config/themes.dart index 8bb7ff744..adf647233 100644 --- a/lib/config/themes.dart +++ b/lib/config/themes.dart @@ -140,6 +140,9 @@ abstract class FluffyThemes { ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( + backgroundColor: colorScheme.secondaryContainer, + foregroundColor: colorScheme.onSecondaryContainer, + elevation: 0, padding: const EdgeInsets.all(16), textStyle: const TextStyle(fontSize: 16), shape: RoundedRectangleBorder( diff --git a/lib/pages/bootstrap/bootstrap_dialog.dart b/lib/pages/bootstrap/bootstrap_dialog.dart index 8bf4f4b02..e1c4cde71 100644 --- a/lib/pages/bootstrap/bootstrap_dialog.dart +++ b/lib/pages/bootstrap/bootstrap_dialog.dart @@ -386,7 +386,10 @@ class BootstrapDialogState extends State { const SizedBox(height: 16), ElevatedButton.icon( style: ElevatedButton.styleFrom( - foregroundColor: Colors.red, + backgroundColor: + Theme.of(context).colorScheme.errorContainer, + foregroundColor: + Theme.of(context).colorScheme.onErrorContainer, ), icon: const Icon(Icons.delete_outlined), label: Text(L10n.of(context)!.recoveryKeyLost), diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index eb9bb40b5..dc32e1845 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -221,10 +221,18 @@ class ChatDetailsView extends StatelessWidget { else Padding( padding: const EdgeInsets.all(16.0), - child: OutlinedButton.icon( + child: TextButton.icon( onPressed: controller.setTopicAction, label: Text(L10n.of(context)!.setChatDescription), icon: const Icon(Icons.edit_outlined), + style: TextButton.styleFrom( + backgroundColor: Theme.of(context) + .colorScheme + .secondaryContainer, + foregroundColor: Theme.of(context) + .colorScheme + .onSecondaryContainer, + ), ), ), Padding( diff --git a/lib/pages/device_settings/device_settings_view.dart b/lib/pages/device_settings/device_settings_view.dart index 5493836ff..e107d30c9 100644 --- a/lib/pages/device_settings/device_settings_view.dart +++ b/lib/pages/device_settings/device_settings_view.dart @@ -71,11 +71,6 @@ class DevicesSettingsView extends StatelessWidget { block: controller.blockDeviceAction, unblock: controller.unblockDeviceAction, ), - const Divider( - height: 16.0, - indent: 16, - endIndent: 16, - ), ], if (controller.notThisDevice.isNotEmpty) Padding( @@ -85,17 +80,18 @@ class DevicesSettingsView extends StatelessWidget { ), child: SizedBox( width: double.infinity, - child: OutlinedButton.icon( + child: TextButton.icon( label: Text( controller.errorDeletingDevices ?? L10n.of(context)!.removeAllOtherDevices, ), - style: OutlinedButton.styleFrom( - foregroundColor: - Theme.of(context).colorScheme.error, - side: BorderSide( - color: Theme.of(context).colorScheme.error, - ), + style: TextButton.styleFrom( + foregroundColor: Theme.of(context) + .colorScheme + .onErrorContainer, + backgroundColor: Theme.of(context) + .colorScheme + .errorContainer, ), icon: controller.loadingDeletingDevices ? const CircularProgressIndicator.adaptive( diff --git a/lib/pages/homeserver_picker/homeserver_picker_view.dart b/lib/pages/homeserver_picker/homeserver_picker_view.dart index d48b626aa..eed2adac1 100644 --- a/lib/pages/homeserver_picker/homeserver_picker_view.dart +++ b/lib/pages/homeserver_picker/homeserver_picker_view.dart @@ -198,12 +198,7 @@ class _LoginButton extends StatelessWidget { width: double.infinity, child: OutlinedButton.icon( style: OutlinedButton.styleFrom( - side: BorderSide( - width: withBorder ? 1 : 0, - color: withBorder - ? Theme.of(context).colorScheme.onBackground - : Colors.transparent, - ), + side: BorderSide.none, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(99), ), diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart index 05af6fda9..c9ca5a0ff 100644 --- a/lib/pages/settings/settings_view.dart +++ b/lib/pages/settings/settings_view.dart @@ -136,7 +136,10 @@ class SettingsView extends StatelessWidget { ); }, ), - const Divider(thickness: 1), + Divider( + height: 1, + color: Theme.of(context).dividerColor, + ), if (showChatBackupBanner == null) ListTile( leading: const Icon(Icons.backup_outlined), @@ -151,7 +154,10 @@ class SettingsView extends StatelessWidget { title: Text(L10n.of(context)!.chatBackup), onChanged: controller.firstRunBootstrapAction, ), - const Divider(thickness: 1), + Divider( + height: 1, + color: Theme.of(context).dividerColor, + ), ListTile( leading: const Icon(Icons.format_paint_outlined), title: Text(L10n.of(context)!.changeTheme), @@ -182,7 +188,10 @@ class SettingsView extends StatelessWidget { onTap: () => context.go('/rooms/settings/security'), trailing: const Icon(Icons.chevron_right_outlined), ), - const Divider(thickness: 1), + Divider( + height: 1, + color: Theme.of(context).dividerColor, + ), ListTile( leading: const Icon(Icons.help_outline_outlined), title: Text(L10n.of(context)!.help), diff --git a/lib/pages/settings_chat/settings_chat_view.dart b/lib/pages/settings_chat/settings_chat_view.dart index b2d4a40ad..70c7938fa 100644 --- a/lib/pages/settings_chat/settings_chat_view.dart +++ b/lib/pages/settings_chat/settings_chat_view.dart @@ -91,12 +91,6 @@ class SettingsChatView extends StatelessWidget { child: Icon(Icons.call), ), ), - SettingsSwitchListTile.adaptive( - title: L10n.of(context)!.separateChatTypes, - onChanged: (b) => AppConfig.separateChatTypes = b, - storeKey: SettingKeys.separateChatTypes, - defaultValue: AppConfig.separateChatTypes, - ), ], ), ), diff --git a/lib/pages/settings_notifications/settings_notifications.dart b/lib/pages/settings_notifications/settings_notifications.dart index 6ab4740af..2baf14dd0 100644 --- a/lib/pages/settings_notifications/settings_notifications.dart +++ b/lib/pages/settings_notifications/settings_notifications.dart @@ -6,6 +6,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/utils/localized_exception_extension.dart'; import '../../widgets/matrix.dart'; import 'settings_notifications_view.dart'; @@ -89,16 +90,52 @@ class SettingsNotificationsController extends State { } } - void setNotificationSetting(NotificationSettingsItem item, bool enabled) { - showFutureLoadingDialog( - context: context, - future: () => Matrix.of(context).client.setPushRuleEnabled( + bool isLoading = false; + + void setNotificationSetting( + NotificationSettingsItem item, + bool enabled, + ) async { + final scaffoldMessenger = ScaffoldMessenger.of(context); + setState(() { + isLoading = true; + }); + try { + await Matrix.of(context).client.setPushRuleEnabled( 'global', item.type, item.key, enabled, - ), - ); + ); + } catch (e, s) { + Logs().w('Unable to change notification settings', e, s); + scaffoldMessenger + .showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); + } finally { + setState(() { + isLoading = false; + }); + } + } + + void onToggleMuteAllNotifications() async { + final scaffoldMessenger = ScaffoldMessenger.of(context); + setState(() { + isLoading = true; + }); + try { + await Matrix.of(context).client.setMuteAllPushNotifications( + !Matrix.of(context).client.allPushNotificationsMuted, + ); + } catch (e, s) { + Logs().w('Unable to change notification settings', e, s); + scaffoldMessenger + .showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); + } finally { + setState(() { + isLoading = false; + }); + } } void onPusherTap(Pusher pusher) async { diff --git a/lib/pages/settings_notifications/settings_notifications_view.dart b/lib/pages/settings_notifications/settings_notifications_view.dart index 64d356a80..978858a02 100644 --- a/lib/pages/settings_notifications/settings_notifications_view.dart +++ b/lib/pages/settings_notifications/settings_notifications_view.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; @@ -36,13 +35,18 @@ class SettingsNotificationsView extends StatelessWidget { title: Text( L10n.of(context)!.notificationsEnabledForThisAccount, ), - onChanged: (_) => showFutureLoadingDialog( - context: context, - future: () => Matrix.of(context) - .client - .setMuteAllPushNotifications( - !Matrix.of(context).client.allPushNotificationsMuted, - ), + onChanged: controller.isLoading + ? null + : (_) => controller.onToggleMuteAllNotifications(), + ), + Divider(color: Theme.of(context).dividerColor), + ListTile( + title: Text( + L10n.of(context)!.notifyMeFor, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), ), ), for (final item in NotificationSettingsItem.items) @@ -51,14 +55,14 @@ class SettingsNotificationsView extends StatelessWidget { ? false : controller.getNotificationSetting(item) ?? true, title: Text(item.title(context)), - onChanged: Matrix.of(context) - .client - .allPushNotificationsMuted + onChanged: controller.isLoading ? null - : (bool enabled) => - controller.setNotificationSetting(item, enabled), + : Matrix.of(context).client.allPushNotificationsMuted + ? null + : (bool enabled) => controller + .setNotificationSetting(item, enabled), ), - const Divider(), + Divider(color: Theme.of(context).dividerColor), ListTile( title: Text( L10n.of(context)!.devices, @@ -87,6 +91,14 @@ class SettingsNotificationsView extends StatelessWidget { ); } final pushers = snapshot.data ?? []; + if (pushers.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: Text(L10n.of(context)!.noOtherDevicesFound), + ), + ); + } return ListView.builder( physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, diff --git a/lib/pages/settings_password/settings_password_view.dart b/lib/pages/settings_password/settings_password_view.dart index 577077a0a..dd81fa122 100644 --- a/lib/pages/settings_password/settings_password_view.dart +++ b/lib/pages/settings_password/settings_password_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:go_router/go_router.dart'; import 'package:fluffychat/pages/settings_password/settings_password.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; @@ -12,7 +13,15 @@ class SettingsPasswordView extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text(L10n.of(context)!.changePassword)), + appBar: AppBar( + title: Text(L10n.of(context)!.changePassword), + actions: [ + TextButton( + child: Text(L10n.of(context)!.passwordRecoverySettings), + onPressed: () => context.go('/rooms/settings/security/3pid'), + ), + ], + ), body: ListTileTheme( iconColor: Theme.of(context).colorScheme.onBackground, child: MaxWidthBody( diff --git a/lib/pages/settings_security/settings_security.dart b/lib/pages/settings_security/settings_security.dart index 3f488070b..69069d693 100644 --- a/lib/pages/settings_security/settings_security.dart +++ b/lib/pages/settings_security/settings_security.dart @@ -58,6 +58,7 @@ class SettingsSecurityController extends State { message: L10n.of(context)!.deactivateAccountWarning, okLabel: L10n.of(context)!.ok, cancelLabel: L10n.of(context)!.cancel, + isDestructiveAction: true, ) == OkCancelResult.cancel) { return; @@ -74,6 +75,7 @@ class SettingsSecurityController extends State { : L10n.of(context)!.supposedMxid(supposedMxid), ), ], + isDestructiveAction: true, okLabel: L10n.of(context)!.delete, cancelLabel: L10n.of(context)!.cancel, ); @@ -86,6 +88,7 @@ class SettingsSecurityController extends State { title: L10n.of(context)!.pleaseEnterYourPassword, okLabel: L10n.of(context)!.ok, cancelLabel: L10n.of(context)!.cancel, + isDestructiveAction: true, textFields: [ const DialogTextField( obscureText: true, diff --git a/lib/pages/settings_security/settings_security_view.dart b/lib/pages/settings_security/settings_security_view.dart index a125ac1f1..1201a145c 100644 --- a/lib/pages/settings_security/settings_security_view.dart +++ b/lib/pages/settings_security/settings_security_view.dart @@ -42,95 +42,15 @@ class SettingsSecurityView extends StatelessWidget { } return Column( children: [ - if (error != null) - ListTile( - leading: const Icon( - Icons.warning_outlined, - color: Colors.orange, - ), - title: Text( - error.toLocalizedString(context), - style: const TextStyle(color: Colors.orange), - ), - ), - if (capabilities?.mChangePassword?.enabled != false || - error != null) ...[ - ListTile( - leading: const Icon(Icons.key_outlined), - trailing: error != null - ? null - : const Icon(Icons.chevron_right_outlined), - title: Text( - L10n.of(context)!.changePassword, - style: TextStyle( - decoration: - error == null ? null : TextDecoration.lineThrough, - ), - ), - onTap: error != null - ? null - : () => - context.go('/rooms/settings/security/password'), - ), - ListTile( - leading: const Icon(Icons.mail_outlined), - trailing: error != null - ? null - : const Icon(Icons.chevron_right_outlined), - title: Text( - L10n.of(context)!.passwordRecovery, - style: TextStyle( - decoration: - error == null ? null : TextDecoration.lineThrough, - ), - ), - onTap: error != null - ? null - : () => context.go('/rooms/settings/security/3pid'), - ), - ], ListTile( - leading: const Icon(Icons.block_outlined), - trailing: const Icon(Icons.chevron_right_outlined), - title: Text(L10n.of(context)!.blockedUsers), - onTap: () => - context.go('/rooms/settings/security/ignorelist'), - ), - if (Matrix.of(context).client.encryption != null) ...{ - if (PlatformInfos.isMobile) - ListTile( - leading: const Icon(Icons.lock_outlined), - trailing: const Icon(Icons.chevron_right_outlined), - title: Text(L10n.of(context)!.appLock), - onTap: controller.setAppLockAction, - ), - }, - const Divider(height: 1), - ListTile( - leading: const Icon(Icons.tap_and_play), title: Text( - L10n.of(context)!.dehydrate, - style: const TextStyle(color: Colors.red), + L10n.of(context)!.privacy, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), ), - onTap: controller.dehydrateAction, ), - ListTile( - leading: const Icon(Icons.delete_outlined), - title: Text( - L10n.of(context)!.deleteAccount, - style: const TextStyle(color: Colors.red), - ), - onTap: controller.deleteAccountAction, - ), - ListTile( - title: Text(L10n.of(context)!.yourPublicKey), - subtitle: SelectableText( - Matrix.of(context).client.fingerprintKey.beautified, - style: const TextStyle(fontFamily: 'monospace'), - ), - leading: const Icon(Icons.vpn_key_outlined), - ), - const Divider(height: 1), SettingsSwitchListTile.adaptive( title: L10n.of(context)!.sendTypingNotifications, subtitle: @@ -146,6 +66,95 @@ class SettingsSecurityView extends StatelessWidget { storeKey: SettingKeys.sendPublicReadReceipts, defaultValue: AppConfig.sendPublicReadReceipts, ), + ListTile( + trailing: const Icon(Icons.chevron_right_outlined), + title: Text(L10n.of(context)!.blockedUsers), + subtitle: Text( + L10n.of(context)!.thereAreCountUsersBlocked( + Matrix.of(context).client.ignoredUsers.length, + ), + ), + onTap: () => + context.go('/rooms/settings/security/ignorelist'), + ), + if (Matrix.of(context).client.encryption != null) ...{ + if (PlatformInfos.isMobile) + ListTile( + leading: const Icon(Icons.lock_outlined), + trailing: const Icon(Icons.chevron_right_outlined), + title: Text(L10n.of(context)!.appLock), + onTap: controller.setAppLockAction, + ), + }, + Divider( + height: 1, + color: Theme.of(context).dividerColor, + ), + ListTile( + title: Text( + L10n.of(context)!.account, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), + ), + ), + ListTile( + title: Text(L10n.of(context)!.yourPublicKey), + leading: const Icon(Icons.vpn_key_outlined), + subtitle: SelectableText( + Matrix.of(context).client.fingerprintKey.beautified, + style: const TextStyle(fontFamily: 'monospace'), + ), + ), + if (error != null) + ListTile( + leading: const Icon( + Icons.warning_outlined, + color: Colors.orange, + ), + title: Text( + error.toLocalizedString(context), + style: const TextStyle(color: Colors.orange), + ), + ), + if (capabilities?.mChangePassword?.enabled != false || + error != null) + ListTile( + leading: const Icon(Icons.password_outlined), + trailing: error != null + ? null + : const Icon(Icons.chevron_right_outlined), + title: Text( + L10n.of(context)!.changePassword, + style: TextStyle( + decoration: + error == null ? null : TextDecoration.lineThrough, + ), + ), + onTap: error != null + ? null + : () => + context.go('/rooms/settings/security/password'), + ), + ListTile( + iconColor: Colors.orange, + leading: const Icon(Icons.tap_and_play), + title: Text( + L10n.of(context)!.dehydrate, + style: const TextStyle(color: Colors.orange), + ), + onTap: controller.dehydrateAction, + ), + ListTile( + iconColor: Colors.red, + leading: const Icon(Icons.delete_outlined), + title: Text( + L10n.of(context)!.deleteAccount, + style: const TextStyle(color: Colors.red), + ), + onTap: controller.deleteAccountAction, + ), ], ); }, diff --git a/lib/pages/settings_style/settings_style_view.dart b/lib/pages/settings_style/settings_style_view.dart index ed80e8268..cf7348021 100644 --- a/lib/pages/settings_style/settings_style_view.dart +++ b/lib/pages/settings_style/settings_style_view.dart @@ -135,7 +135,10 @@ class SettingsStyleView extends StatelessWidget { ), ), const SizedBox(height: 8), - const Divider(height: 1), + Divider( + height: 1, + color: Theme.of(context).dividerColor, + ), ListTile( title: Text( L10n.of(context)!.setTheme, @@ -163,10 +166,13 @@ class SettingsStyleView extends StatelessWidget { title: Text(L10n.of(context)!.darkTheme), onChanged: controller.switchTheme, ), - const Divider(height: 1), + Divider( + height: 1, + color: Theme.of(context).dividerColor, + ), ListTile( title: Text( - L10n.of(context)!.presenceStyle, + L10n.of(context)!.overview, style: TextStyle( color: Theme.of(context).colorScheme.secondary, fontWeight: FontWeight.bold, @@ -179,7 +185,16 @@ class SettingsStyleView extends StatelessWidget { storeKey: SettingKeys.showPresences, defaultValue: AppConfig.showPresences, ), - const Divider(height: 1), + SettingsSwitchListTile.adaptive( + title: L10n.of(context)!.separateChatTypes, + onChanged: (b) => AppConfig.separateChatTypes = b, + storeKey: SettingKeys.separateChatTypes, + defaultValue: AppConfig.separateChatTypes, + ), + Divider( + height: 1, + color: Theme.of(context).dividerColor, + ), ListTile( title: Text( L10n.of(context)!.messagesStyle,