design: Adjust settings design

This commit is contained in:
krille-chan 2024-04-14 11:48:51 +02:00
parent 23f4c64e50
commit e11195f4fc
No known key found for this signature in database
14 changed files with 241 additions and 140 deletions

View file

@ -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": {}
}
}

View file

@ -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(

View file

@ -386,7 +386,10 @@ class BootstrapDialogState extends State<BootstrapDialog> {
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),

View file

@ -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(

View file

@ -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(

View file

@ -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),
),

View file

@ -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),

View file

@ -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,
),
],
),
),

View file

@ -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<SettingsNotifications> {
}
}
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 {

View file

@ -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,

View file

@ -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(

View file

@ -58,6 +58,7 @@ class SettingsSecurityController extends State<SettingsSecurity> {
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<SettingsSecurity> {
: 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<SettingsSecurity> {
title: L10n.of(context)!.pleaseEnterYourPassword,
okLabel: L10n.of(context)!.ok,
cancelLabel: L10n.of(context)!.cancel,
isDestructiveAction: true,
textFields: [
const DialogTextField(
obscureText: true,

View file

@ -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,
),
],
);
},

View file

@ -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,