From e11195f4fc4839d227914cbf178c85923c5b084d Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 14 Apr 2024 11:48:51 +0200 Subject: [PATCH 01/18] 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, From e43a4ad65f1d4eadd5196e8fde4eb332b0010d64 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 14 Apr 2024 12:00:43 +0200 Subject: [PATCH 02/18] design: Adjust chat settings design --- assets/l10n/intl_en.arb | 6 +++- .../settings_chat/settings_chat_view.dart | 28 ++++++++++++------- .../filtered_timeline_extension.dart | 12 -------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index acf074308..7b8344441 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -929,7 +929,9 @@ "type": "text", "placeholders": {} }, - "hideUnknownEvents": "Hide unknown events", + "hideRedactedMessages": "Hide redacted messages", + "hideRedactedMessagesBody": "If someone redacts a message, this message won't be visible in the chat anymore.", + "hideInvalidOrUnknownMessageFormats": "Hide invalid or unknown message formats", "@hideUnknownEvents": { "type": "text", "placeholders": {} @@ -1384,6 +1386,8 @@ "type": "text", "placeholders": {} }, + "hideMemberChangesInPublicChats": "Hide member changes in public chats", + "hideMemberChangesInPublicChatsBody": "Do not show in the chat timeline if someone joins or leaves a public chat to improve readability.", "overview": "Overview", "notifyMeFor": "Notify me for", "passwordRecoverySettings": "Password recovery settings", diff --git a/lib/pages/settings_chat/settings_chat_view.dart b/lib/pages/settings_chat/settings_chat_view.dart index 70c7938fa..17b0f2700 100644 --- a/lib/pages/settings_chat/settings_chat_view.dart +++ b/lib/pages/settings_chat/settings_chat_view.dart @@ -31,7 +31,10 @@ class SettingsChatView extends StatelessWidget { trailing: const Icon(Icons.chevron_right_outlined), leading: const Icon(Icons.emoji_emotions_outlined), ), - const Divider(), + Divider( + height: 1, + color: Theme.of(context).dividerColor, + ), SettingsSwitchListTile.adaptive( title: L10n.of(context)!.formattedMessages, subtitle: L10n.of(context)!.formattedMessagesDescription, @@ -40,23 +43,25 @@ class SettingsChatView extends StatelessWidget { defaultValue: AppConfig.renderHtml, ), SettingsSwitchListTile.adaptive( - title: L10n.of(context)!.hideRedactedEvents, + title: L10n.of(context)!.hideMemberChangesInPublicChats, + subtitle: L10n.of(context)!.hideMemberChangesInPublicChatsBody, + onChanged: (b) => AppConfig.hideUnimportantStateEvents = b, + storeKey: SettingKeys.hideUnimportantStateEvents, + defaultValue: AppConfig.hideUnimportantStateEvents, + ), + SettingsSwitchListTile.adaptive( + title: L10n.of(context)!.hideRedactedMessages, + subtitle: L10n.of(context)!.hideRedactedMessagesBody, onChanged: (b) => AppConfig.hideRedactedEvents = b, storeKey: SettingKeys.hideRedactedEvents, defaultValue: AppConfig.hideRedactedEvents, ), SettingsSwitchListTile.adaptive( - title: L10n.of(context)!.hideUnknownEvents, + title: L10n.of(context)!.hideInvalidOrUnknownMessageFormats, onChanged: (b) => AppConfig.hideUnknownEvents = b, storeKey: SettingKeys.hideUnknownEvents, defaultValue: AppConfig.hideUnknownEvents, ), - SettingsSwitchListTile.adaptive( - title: L10n.of(context)!.hideUnimportantStateEvents, - onChanged: (b) => AppConfig.hideUnimportantStateEvents = b, - storeKey: SettingKeys.hideUnimportantStateEvents, - defaultValue: AppConfig.hideUnimportantStateEvents, - ), if (PlatformInfos.isMobile) SettingsSwitchListTile.adaptive( title: L10n.of(context)!.autoplayImages, @@ -64,7 +69,10 @@ class SettingsChatView extends StatelessWidget { storeKey: SettingKeys.autoplayImages, defaultValue: AppConfig.autoplayImages, ), - const Divider(), + Divider( + height: 1, + color: Theme.of(context).dividerColor, + ), SettingsSwitchListTile.adaptive( title: L10n.of(context)!.sendOnEnter, onChanged: (b) => AppConfig.sendOnEnter = b, diff --git a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart index 7ae2bf0c5..bd1cd8e46 100644 --- a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart +++ b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart @@ -18,10 +18,6 @@ extension IsStateExtension on Event { (!AppConfig.hideUnknownEvents || isEventTypeKnown) && // remove state events that we don't want to render (isState || !AppConfig.hideAllStateEvents) && - // hide unimportant state events - (!AppConfig.hideUnimportantStateEvents || - !isState || - importantStateEvents.contains(type)) && // hide simple join/leave member events in public rooms (!AppConfig.hideUnimportantStateEvents || type != EventTypes.RoomMember || @@ -29,14 +25,6 @@ extension IsStateExtension on Event { content.tryGet('membership') == 'ban' || stateKey != senderId); - static const Set importantStateEvents = { - EventTypes.Encryption, - EventTypes.RoomCreate, - EventTypes.RoomMember, - EventTypes.RoomTombstone, - EventTypes.CallInvite, - }; - bool get isState => !{ EventTypes.Message, EventTypes.Sticker, From 13661138235825bae88fc14fb4558862f3fc5761 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 14 Apr 2024 12:39:37 +0200 Subject: [PATCH 03/18] design: Follow up chat settings design --- assets/l10n/intl_en.arb | 3 ++ lib/pages/chat_details/chat_details_view.dart | 3 +- .../settings_chat/settings_chat_view.dart | 31 ++++++++++--------- .../settings_emotes/settings_emotes_view.dart | 2 +- .../settings_security_view.dart | 2 +- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 7b8344441..6060a21e2 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -131,6 +131,7 @@ "type": "text", "placeholders": {} }, + "appLockDescription": "Lock the app when not using with a pin code", "archive": "Archive", "@archive": { "type": "text", @@ -759,6 +760,8 @@ "type": "text", "placeholders": {} }, + "customEmojisAndStickers": "Custom emojis and stickers", + "customEmojisAndStickersBody": "Add or share custom emojis or stickers which can be used in any chat.", "emoteShortcode": "Emote shortcode", "@emoteShortcode": { "type": "text", diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index dc32e1845..127a98507 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -293,7 +293,8 @@ class ChatDetailsView extends StatelessWidget { Icons.insert_emoticon_outlined, ), ), - title: Text(L10n.of(context)!.emoteSettings), + title: + Text(L10n.of(context)!.customEmojisAndStickers), subtitle: Text(L10n.of(context)!.setCustomEmotes), onTap: controller.goToEmoteSettings, trailing: const Icon(Icons.chevron_right_outlined), diff --git a/lib/pages/settings_chat/settings_chat_view.dart b/lib/pages/settings_chat/settings_chat_view.dart index 17b0f2700..601c8ad00 100644 --- a/lib/pages/settings_chat/settings_chat_view.dart +++ b/lib/pages/settings_chat/settings_chat_view.dart @@ -25,16 +25,6 @@ class SettingsChatView extends StatelessWidget { child: MaxWidthBody( child: Column( children: [ - ListTile( - title: Text(L10n.of(context)!.emoteSettings), - onTap: () => context.go('/rooms/settings/chat/emotes'), - trailing: const Icon(Icons.chevron_right_outlined), - leading: const Icon(Icons.emoji_emotions_outlined), - ), - Divider( - height: 1, - color: Theme.of(context).dividerColor, - ), SettingsSwitchListTile.adaptive( title: L10n.of(context)!.formattedMessages, subtitle: L10n.of(context)!.formattedMessagesDescription, @@ -69,16 +59,29 @@ class SettingsChatView extends StatelessWidget { storeKey: SettingKeys.autoplayImages, defaultValue: AppConfig.autoplayImages, ), - Divider( - height: 1, - color: Theme.of(context).dividerColor, - ), SettingsSwitchListTile.adaptive( title: L10n.of(context)!.sendOnEnter, onChanged: (b) => AppConfig.sendOnEnter = b, storeKey: SettingKeys.sendOnEnter, defaultValue: AppConfig.sendOnEnter ?? !PlatformInfos.isMobile, ), + Divider( + height: 1, + color: Theme.of(context).dividerColor, + ), + ListTile( + title: Text(L10n.of(context)!.customEmojisAndStickers), + subtitle: Text(L10n.of(context)!.customEmojisAndStickersBody), + onTap: () => context.go('/rooms/settings/chat/emotes'), + trailing: const Padding( + padding: EdgeInsets.all(16.0), + child: Icon(Icons.emoji_emotions_outlined), + ), + ), + Divider( + height: 1, + color: Theme.of(context).dividerColor, + ), SettingsSwitchListTile.adaptive( title: L10n.of(context)!.experimentalVideoCalls, onChanged: (b) { diff --git a/lib/pages/settings_emotes/settings_emotes_view.dart b/lib/pages/settings_emotes/settings_emotes_view.dart index acb93788e..7e72cd464 100644 --- a/lib/pages/settings_emotes/settings_emotes_view.dart +++ b/lib/pages/settings_emotes/settings_emotes_view.dart @@ -24,7 +24,7 @@ class EmotesSettingsView extends StatelessWidget { return Scaffold( appBar: AppBar( leading: const Center(child: BackButton()), - title: Text(L10n.of(context)!.emoteSettings), + title: Text(L10n.of(context)!.customEmojisAndStickers), actions: [ PopupMenuButton( onSelected: (value) { diff --git a/lib/pages/settings_security/settings_security_view.dart b/lib/pages/settings_security/settings_security_view.dart index 1201a145c..0e3210a83 100644 --- a/lib/pages/settings_security/settings_security_view.dart +++ b/lib/pages/settings_security/settings_security_view.dart @@ -80,9 +80,9 @@ class SettingsSecurityView extends StatelessWidget { 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), + subtitle: Text(L10n.of(context)!.appLockDescription), onTap: controller.setAppLockAction, ), }, From cbdea137725e4f887788fcdb0808bda3e2a9147c Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 14 Apr 2024 13:06:30 +0200 Subject: [PATCH 04/18] design: Follow up settings design --- assets/l10n/intl_en.arb | 1 + .../settings_chat/settings_chat_view.dart | 20 ++++++++++++- .../settings_ignore_list.dart | 17 +++++++++-- .../settings_ignore_list_view.dart | 16 ++++++---- .../settings_security_view.dart | 30 +++---------------- 5 files changed, 49 insertions(+), 35 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 6060a21e2..32ce9160e 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -760,6 +760,7 @@ "type": "text", "placeholders": {} }, + "calls": "Calls", "customEmojisAndStickers": "Custom emojis and stickers", "customEmojisAndStickersBody": "Add or share custom emojis or stickers which can be used in any chat.", "emoteShortcode": "Emote shortcode", diff --git a/lib/pages/settings_chat/settings_chat_view.dart b/lib/pages/settings_chat/settings_chat_view.dart index 601c8ad00..1b7722b38 100644 --- a/lib/pages/settings_chat/settings_chat_view.dart +++ b/lib/pages/settings_chat/settings_chat_view.dart @@ -69,19 +69,37 @@ class SettingsChatView extends StatelessWidget { height: 1, color: Theme.of(context).dividerColor, ), + ListTile( + title: Text( + L10n.of(context)!.customEmojisAndStickers, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), + ), + ), ListTile( title: Text(L10n.of(context)!.customEmojisAndStickers), subtitle: Text(L10n.of(context)!.customEmojisAndStickersBody), onTap: () => context.go('/rooms/settings/chat/emotes'), trailing: const Padding( padding: EdgeInsets.all(16.0), - child: Icon(Icons.emoji_emotions_outlined), + child: Icon(Icons.chevron_right_outlined), ), ), Divider( height: 1, color: Theme.of(context).dividerColor, ), + ListTile( + title: Text( + L10n.of(context)!.calls, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), + ), + ), SettingsSwitchListTile.adaptive( title: L10n.of(context)!.experimentalVideoCalls, onChanged: (b) { diff --git a/lib/pages/settings_ignore_list/settings_ignore_list.dart b/lib/pages/settings_ignore_list/settings_ignore_list.dart index 0b27cee3b..d64be058b 100644 --- a/lib/pages/settings_ignore_list/settings_ignore_list.dart +++ b/lib/pages/settings_ignore_list/settings_ignore_list.dart @@ -1,6 +1,8 @@ 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 '../../widgets/matrix.dart'; import 'settings_ignore_list_view.dart'; @@ -25,9 +27,20 @@ class SettingsIgnoreListController extends State { } } + String? errorText; + void ignoreUser(BuildContext context) { - if (controller.text.isEmpty) return; - final userId = '@${controller.text}'; + final userId = controller.text.trim(); + if (userId.isEmpty) return; + if (!userId.isValidMatrixId || userId.sigil != '@') { + setState(() { + errorText = L10n.of(context)!.invalidInput; + }); + return; + } + setState(() { + errorText = null; + }); showFutureLoadingDialog( context: context, diff --git a/lib/pages/settings_ignore_list/settings_ignore_list_view.dart b/lib/pages/settings_ignore_list/settings_ignore_list_view.dart index 79c21eb28..edd26312c 100644 --- a/lib/pages/settings_ignore_list/settings_ignore_list_view.dart +++ b/lib/pages/settings_ignore_list/settings_ignore_list_view.dart @@ -38,13 +38,13 @@ class SettingsIgnoreListView extends StatelessWidget { textInputAction: TextInputAction.done, onSubmitted: (_) => controller.ignoreUser(context), decoration: InputDecoration( - border: const OutlineInputBorder(), - hintText: 'bad_guy:domain.abc', - prefixText: '@', + errorText: controller.errorText, + hintText: '@bad_guy:domain.abc', + floatingLabelBehavior: FloatingLabelBehavior.always, labelText: L10n.of(context)!.blockUsername, suffixIcon: IconButton( tooltip: L10n.of(context)!.block, - icon: const Icon(Icons.done_outlined), + icon: const Icon(Icons.send_outlined), onPressed: () => controller.ignoreUser(context), ), ), @@ -57,7 +57,9 @@ class SettingsIgnoreListView extends StatelessWidget { ], ), ), - const Divider(height: 1), + Divider( + color: Theme.of(context).dividerColor, + ), Expanded( child: StreamBuilder( stream: client.onAccountData.stream @@ -76,9 +78,11 @@ class SettingsIgnoreListView extends StatelessWidget { title: Text( s.data?.displayName ?? client.ignoredUsers[i], ), + subtitle: + Text(s.data?.userId ?? client.ignoredUsers[i]), trailing: IconButton( tooltip: L10n.of(context)!.delete, - icon: const Icon(Icons.delete_forever_outlined), + icon: const Icon(Icons.delete_outlined), onPressed: () => showFutureLoadingDialog( context: context, future: () => diff --git a/lib/pages/settings_security/settings_security_view.dart b/lib/pages/settings_security/settings_security_view.dart index 0e3210a83..ab197a910 100644 --- a/lib/pages/settings_security/settings_security_view.dart +++ b/lib/pages/settings_security/settings_security_view.dart @@ -6,7 +6,6 @@ import 'package:go_router/go_router.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/utils/beautify_string_extension.dart'; -import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -107,35 +106,14 @@ class SettingsSecurityView extends StatelessWidget { 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'), + trailing: const Icon(Icons.chevron_right_outlined), + title: Text(L10n.of(context)!.changePassword), + onTap: () => + context.go('/rooms/settings/security/password'), ), ListTile( iconColor: Colors.orange, From c300220773e09596c692b5e29e156b505e0ec330 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 14 Apr 2024 15:49:46 +0200 Subject: [PATCH 05/18] design: New chat access settings --- assets/l10n/intl_en.arb | 19 ++ lib/config/routes.dart | 12 + .../chat_access_settings_controller.dart | 216 ++++++++++++++++++ .../chat_access_settings_page.dart | 172 ++++++++++++++ lib/pages/chat_details/chat_details.dart | 197 ---------------- lib/pages/chat_details/chat_details_view.dart | 82 +------ .../chat_details/participant_list_item.dart | 17 +- .../chat_permissions_settings.dart | 39 ---- .../chat_permissions_settings_view.dart | 31 +-- .../user_bottom_sheet_view.dart | 53 +++++ lib/widgets/public_room_bottom_sheet.dart | 16 +- 11 files changed, 499 insertions(+), 355 deletions(-) create mode 100644 lib/pages/chat_access_settings/chat_access_settings_controller.dart create mode 100644 lib/pages/chat_access_settings/chat_access_settings_page.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 32ce9160e..37470efcb 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -760,6 +760,9 @@ "type": "text", "placeholders": {} }, + "globalChatId": "Global chat ID", + "accessAndVisibility": "Access and visibility", + "accessAndVisibilityDescription": "Who is allowed to join this chat and how the chat can be discovered.", "calls": "Calls", "customEmojisAndStickers": "Custom emojis and stickers", "customEmojisAndStickersBody": "Add or share custom emojis or stickers which can be used in any chat.", @@ -2276,6 +2279,14 @@ "user": {} } }, + "userWouldLikeToChangeTheChat": "{user} would like to join the chat.", + "@userWouldLikeToChangeTheChat": { + "placeholders": { + "user": {} + } + }, + "noPublicLinkHasBeenCreatedYet": "No public link has been created yet", + "knock": "Knock", "users": "Users", "@users": {}, "unlockOldMessages": "Unlock old messages", @@ -2450,6 +2461,14 @@ "query": {} } }, + "knocking": "Knocking", + "chatCanBeDiscoveredViaSearchOnServer": "Chat can be discovered via the search on {server}", + "@chatCanBeDiscoveredViaSearchOnServer": { + "type": "text", + "placeholders": { + "server": {} + } + }, "searchChatsRooms": "Search for #chats, @users...", "@searchChatsRooms": {}, "nothingFound": "Nothing found...", diff --git a/lib/config/routes.dart b/lib/config/routes.dart index a685bb789..e7efe9d29 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -7,6 +7,7 @@ import 'package:go_router/go_router.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/archive/archive.dart'; import 'package:fluffychat/pages/chat/chat.dart'; +import 'package:fluffychat/pages/chat_access_settings/chat_access_settings_controller.dart'; import 'package:fluffychat/pages/chat_details/chat_details.dart'; import 'package:fluffychat/pages/chat_encryption_settings/chat_encryption_settings.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart'; @@ -344,6 +345,17 @@ abstract class AppRoutes { ), ), routes: [ + GoRoute( + path: 'access', + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + ChatAccessSettings( + roomId: state.pathParameters['roomid']!, + ), + ), + redirect: loggedOutRedirect, + ), GoRoute( path: 'members', pageBuilder: (context, state) => defaultPageBuilder( diff --git a/lib/pages/chat_access_settings/chat_access_settings_controller.dart b/lib/pages/chat_access_settings/chat_access_settings_controller.dart new file mode 100644 index 000000000..068a47f37 --- /dev/null +++ b/lib/pages/chat_access_settings/chat_access_settings_controller.dart @@ -0,0 +1,216 @@ +import 'package:flutter/material.dart' hide Visibility; + +import 'package:adaptive_dialog/adaptive_dialog.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/pages/chat_access_settings/chat_access_settings_page.dart'; +import 'package:fluffychat/utils/localized_exception_extension.dart'; +import 'package:fluffychat/widgets/matrix.dart'; + +class ChatAccessSettings extends StatefulWidget { + final String roomId; + const ChatAccessSettings({required this.roomId, super.key}); + + @override + State createState() => ChatAccessSettingsController(); +} + +class ChatAccessSettingsController extends State { + bool joinRulesLoading = false; + bool visibilityLoading = false; + bool historyVisibilityLoading = false; + bool guestAccessLoading = false; + Room get room => Matrix.of(context).client.getRoomById(widget.roomId)!; + + void setJoinRule(JoinRules? newJoinRules) async { + if (newJoinRules == null) return; + setState(() { + joinRulesLoading = true; + }); + + try { + await room.setJoinRules(newJoinRules); + } catch (e, s) { + Logs().w('Unable to change join rules', e, s); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + e.toLocalizedString(context), + ), + ), + ); + } + } finally { + if (mounted) { + setState(() { + joinRulesLoading = false; + }); + } + } + } + + void setHistoryVisibility(HistoryVisibility? historyVisibility) async { + if (historyVisibility == null) return; + setState(() { + historyVisibilityLoading = true; + }); + + try { + await room.setHistoryVisibility(historyVisibility); + } catch (e, s) { + Logs().w('Unable to change history visibility', e, s); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + e.toLocalizedString(context), + ), + ), + ); + } + } finally { + if (mounted) { + setState(() { + historyVisibilityLoading = false; + }); + } + } + } + + void setGuestAccess(GuestAccess? guestAccess) async { + if (guestAccess == null) return; + setState(() { + guestAccessLoading = true; + }); + + try { + await room.setGuestAccess(guestAccess); + } catch (e, s) { + Logs().w('Unable to change guest access', e, s); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + e.toLocalizedString(context), + ), + ), + ); + } + } finally { + if (mounted) { + setState(() { + guestAccessLoading = false; + }); + } + } + } + + void updateRoomAction() async { + final roomVersion = room + .getState(EventTypes.RoomCreate)! + .content + .tryGet('room_version'); + final capabilitiesResult = await showFutureLoadingDialog( + context: context, + future: () => room.client.getCapabilities(), + ); + final capabilities = capabilitiesResult.result; + if (capabilities == null) return; + final newVersion = await showConfirmationDialog( + context: context, + title: L10n.of(context)!.replaceRoomWithNewerVersion, + actions: capabilities.mRoomVersions!.available.entries + .where((r) => r.key != roomVersion) + .map( + (version) => AlertDialogAction( + key: version.key, + label: + '${version.key} (${version.value.toString().split('.').last})', + ), + ) + .toList(), + ); + if (newVersion == null || + OkCancelResult.cancel == + await showOkCancelAlertDialog( + useRootNavigator: false, + context: context, + okLabel: L10n.of(context)!.yes, + cancelLabel: L10n.of(context)!.cancel, + title: L10n.of(context)!.areYouSure, + message: L10n.of(context)!.roomUpgradeDescription, + isDestructiveAction: true, + )) { + return; + } + await showFutureLoadingDialog( + context: context, + future: () => room.client.upgradeRoom(room.id, newVersion), + ); + } + + void setCanonicalAlias() async { + final input = await showTextInputDialog( + context: context, + title: L10n.of(context)!.editRoomAliases, + cancelLabel: L10n.of(context)!.cancel, + okLabel: L10n.of(context)!.ok, + textFields: [ + DialogTextField( + prefixText: '#', + suffixText: room.client.userID!.domain!, + initialText: room.canonicalAlias.localpart, + ), + ], + ); + final newAliasLocalpart = input?.singleOrNull?.trim(); + if (newAliasLocalpart == null || newAliasLocalpart.isEmpty) return; + + await showFutureLoadingDialog( + context: context, + future: () => room.setCanonicalAlias( + '#$newAliasLocalpart:${room.client.userID!.domain!}', + ), + ); + } + + void setChatVisibilityOnDirectory(bool? visibility) async { + if (visibility == null) return; + setState(() { + visibilityLoading = true; + }); + + try { + await room.client.setRoomVisibilityOnDirectory( + room.id, + visibility: visibility == true ? Visibility.public : Visibility.private, + ); + setState(() {}); + } catch (e, s) { + Logs().w('Unable to change visibility', e, s); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + e.toLocalizedString(context), + ), + ), + ); + } + } finally { + if (mounted) { + setState(() { + visibilityLoading = false; + }); + } + } + } + + @override + Widget build(BuildContext context) { + return ChatAccessSettingsPageView(this); + } +} diff --git a/lib/pages/chat_access_settings/chat_access_settings_page.dart b/lib/pages/chat_access_settings/chat_access_settings_page.dart new file mode 100644 index 000000000..8c5004733 --- /dev/null +++ b/lib/pages/chat_access_settings/chat_access_settings_page.dart @@ -0,0 +1,172 @@ +import 'package:flutter/material.dart' hide Visibility; + +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:matrix/matrix.dart'; + +import 'package:fluffychat/pages/chat_access_settings/chat_access_settings_controller.dart'; +import 'package:fluffychat/utils/fluffy_share.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; +import 'package:fluffychat/widgets/layouts/max_width_body.dart'; + +class ChatAccessSettingsPageView extends StatelessWidget { + final ChatAccessSettingsController controller; + const ChatAccessSettingsPageView(this.controller, {super.key}); + + @override + Widget build(BuildContext context) { + final room = controller.room; + return Scaffold( + appBar: AppBar( + leading: const Center(child: BackButton()), + title: Text(L10n.of(context)!.accessAndVisibility), + ), + body: MaxWidthBody( + child: StreamBuilder( + stream: room.onUpdate.stream, + builder: (context, snapshot) => Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + title: Text( + L10n.of(context)!.visibilityOfTheChatHistory, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), + ), + ), + for (final historyVisibility in HistoryVisibility.values) + RadioListTile.adaptive( + title: Text( + historyVisibility + .getLocalizedString(MatrixLocals(L10n.of(context)!)), + ), + value: historyVisibility, + groupValue: room.historyVisibility, + onChanged: controller.historyVisibilityLoading || + !room.canChangeHistoryVisibility + ? null + : controller.setHistoryVisibility, + ), + Divider(color: Theme.of(context).dividerColor), + ListTile( + title: Text( + L10n.of(context)!.whoIsAllowedToJoinThisGroup, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), + ), + ), + for (final joinRule in JoinRules.values) + RadioListTile.adaptive( + title: Text( + joinRule + .getLocalizedString(MatrixLocals(L10n.of(context)!)), + ), + value: joinRule, + groupValue: room.joinRules, + onChanged: + controller.joinRulesLoading || !room.canChangeJoinRules + ? null + : controller.setJoinRule, + ), + Divider(color: Theme.of(context).dividerColor), + if ({JoinRules.public, JoinRules.knock} + .contains(room.joinRules)) ...[ + ListTile( + title: Text( + L10n.of(context)!.areGuestsAllowedToJoin, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), + ), + ), + for (final guestAccess in GuestAccess.values) + RadioListTile.adaptive( + title: Text( + guestAccess + .getLocalizedString(MatrixLocals(L10n.of(context)!)), + ), + value: guestAccess, + groupValue: room.guestAccess, + onChanged: controller.guestAccessLoading || + !room.canChangeGuestAccess + ? null + : controller.setGuestAccess, + ), + Divider(color: Theme.of(context).dividerColor), + FutureBuilder( + future: room.client.getRoomVisibilityOnDirectory(room.id), + builder: (context, snapshot) => SwitchListTile.adaptive( + value: snapshot.data == Visibility.public, + title: Text( + L10n.of(context)!.chatCanBeDiscoveredViaSearchOnServer( + room.client.userID!.domain!, + ), + ), + onChanged: controller.setChatVisibilityOnDirectory, + ), + ), + ListTile( + title: Text(L10n.of(context)!.publicLink), + subtitle: room.canonicalAlias.isEmpty + ? Text( + L10n.of(context)!.noPublicLinkHasBeenCreatedYet, + style: const TextStyle( + fontStyle: FontStyle.italic, + ), + ) + : Text( + 'https://matrix.to/#/${room.canonicalAlias}', + style: TextStyle( + decoration: TextDecoration.underline, + color: Theme.of(context).colorScheme.primary, + ), + ), + onTap: room.canChangeStateEvent(EventTypes.RoomCanonicalAlias) + ? controller.setCanonicalAlias + : null, + trailing: room.canonicalAlias.isEmpty + ? const Padding( + padding: EdgeInsets.symmetric(horizontal: 8.0), + child: Icon(Icons.add), + ) + : IconButton( + icon: Icon(Icons.adaptive.share_outlined), + onPressed: () => FluffyShare.share(room.id, context), + ), + ), + ], + ListTile( + title: Text(L10n.of(context)!.globalChatId), + subtitle: SelectableText(room.id), + trailing: IconButton( + icon: const Icon(Icons.copy_outlined), + onPressed: () => FluffyShare.share(room.id, context), + ), + ), + ListTile( + title: Text(L10n.of(context)!.roomVersion), + subtitle: SelectableText( + room + .getState(EventTypes.RoomCreate)! + .content + .tryGet('room_version') ?? + 'Unknown', + ), + trailing: room.canSendEvent(EventTypes.RoomTombstone) + ? IconButton( + icon: const Icon(Icons.upgrade_outlined), + onPressed: controller.updateRoomAction, + ) + : null, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/chat_details/chat_details.dart b/lib/pages/chat_details/chat_details.dart index 75614b45f..6c78adda6 100644 --- a/lib/pages/chat_details/chat_details.dart +++ b/lib/pages/chat_details/chat_details.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:collection/collection.dart'; @@ -8,7 +7,6 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:matrix/matrix.dart' as matrix; import 'package:matrix/matrix.dart'; import 'package:fluffychat/pages/chat_details/chat_details_view.dart'; @@ -71,116 +69,6 @@ class ChatDetailsController extends State { } } - void editAliases() async { - final room = Matrix.of(context).client.getRoomById(roomId!); - - final aliasesResult = await showFutureLoadingDialog( - context: context, - future: () => room!.client.getLocalAliases(room.id), - ); - - final aliases = aliasesResult.result; - - if (aliases == null) return; - final adminMode = room!.canSendEvent(EventTypes.RoomCanonicalAlias); - if (aliases.isEmpty && (room.canonicalAlias.isNotEmpty)) { - aliases.add(room.canonicalAlias); - } - if (aliases.isEmpty && adminMode) { - return setAliasAction(); - } - final select = await showConfirmationDialog( - context: context, - title: L10n.of(context)!.editRoomAliases, - actions: [ - if (adminMode) - AlertDialogAction(label: L10n.of(context)!.create, key: 'new'), - ...aliases.map((alias) => AlertDialogAction(key: alias, label: alias)), - ], - ); - if (select == null) return; - if (select == 'new') { - return setAliasAction(); - } - final option = await showConfirmationDialog( - context: context, - title: select, - actions: [ - AlertDialogAction( - label: L10n.of(context)!.copyToClipboard, - key: AliasActions.copy, - isDefaultAction: true, - ), - if (adminMode) ...{ - AlertDialogAction( - label: L10n.of(context)!.setAsCanonicalAlias, - key: AliasActions.setCanonical, - isDestructiveAction: true, - ), - AlertDialogAction( - label: L10n.of(context)!.delete, - key: AliasActions.delete, - isDestructiveAction: true, - ), - }, - ], - ); - if (option == null) return; - switch (option) { - case AliasActions.copy: - await Clipboard.setData(ClipboardData(text: select)); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context)!.copiedToClipboard)), - ); - break; - case AliasActions.delete: - await showFutureLoadingDialog( - context: context, - future: () => room.client.deleteRoomAlias(select), - ); - break; - case AliasActions.setCanonical: - await showFutureLoadingDialog( - context: context, - future: () => room.client.setRoomStateWithKey( - room.id, - EventTypes.RoomCanonicalAlias, - '', - { - 'alias': select, - }, - ), - ); - break; - } - } - - void setAliasAction() async { - final room = Matrix.of(context).client.getRoomById(roomId!)!; - final domain = room.client.userID!.domain; - - final input = await showTextInputDialog( - context: context, - title: L10n.of(context)!.setInvitationLink, - okLabel: L10n.of(context)!.ok, - cancelLabel: L10n.of(context)!.cancel, - textFields: [ - DialogTextField( - prefixText: '#', - suffixText: domain, - hintText: L10n.of(context)!.alias, - initialText: room.canonicalAlias.localpart, - ), - ], - ); - if (input == null) return; - await showFutureLoadingDialog( - context: context, - future: () => - room.client.setRoomAlias('#${input.single}:${domain!}', room.id), - ); - } - void setTopicAction() async { final room = Matrix.of(context).client.getRoomById(roomId!)!; final input = await showTextInputDialog( @@ -211,91 +99,6 @@ class ChatDetailsController extends State { } } - void setGuestAccess() async { - final room = Matrix.of(context).client.getRoomById(roomId!)!; - final currentGuestAccess = room.guestAccess; - final newGuestAccess = await showConfirmationDialog( - context: context, - title: L10n.of(context)!.areGuestsAllowedToJoin, - actions: GuestAccess.values - .map( - (guestAccess) => AlertDialogAction( - key: guestAccess, - label: guestAccess - .getLocalizedString(MatrixLocals(L10n.of(context)!)), - isDefaultAction: guestAccess == currentGuestAccess, - ), - ) - .toList(), - ); - if (newGuestAccess == null || newGuestAccess == currentGuestAccess) return; - await showFutureLoadingDialog( - context: context, - future: () => room.setGuestAccess(newGuestAccess), - ); - } - - void setHistoryVisibility() async { - final room = Matrix.of(context).client.getRoomById(roomId!)!; - final currentHistoryVisibility = room.historyVisibility; - final newHistoryVisibility = - await showConfirmationDialog( - context: context, - title: L10n.of(context)!.visibilityOfTheChatHistory, - actions: HistoryVisibility.values - .map( - (visibility) => AlertDialogAction( - key: visibility, - label: visibility - .getLocalizedString(MatrixLocals(L10n.of(context)!)), - isDefaultAction: visibility == currentHistoryVisibility, - ), - ) - .toList(), - ); - if (newHistoryVisibility == null || - newHistoryVisibility == currentHistoryVisibility) return; - await showFutureLoadingDialog( - context: context, - future: () => room.setHistoryVisibility(newHistoryVisibility), - ); - } - - void setJoinRules() async { - final room = Matrix.of(context).client.getRoomById(roomId!)!; - final currentJoinRule = room.joinRules; - final newJoinRule = await showConfirmationDialog( - context: context, - title: L10n.of(context)!.whoIsAllowedToJoinThisGroup, - actions: JoinRules.values - .map( - (joinRule) => AlertDialogAction( - key: joinRule, - label: - joinRule.getLocalizedString(MatrixLocals(L10n.of(context)!)), - isDefaultAction: joinRule == currentJoinRule, - ), - ) - .toList(), - ); - if (newJoinRule == null || newJoinRule == currentJoinRule) return; - await showFutureLoadingDialog( - context: context, - future: () async { - await room.setJoinRules(newJoinRule); - room.client.setRoomVisibilityOnDirectory( - roomId!, - visibility: { - JoinRules.public, - JoinRules.knock, - }.contains(newJoinRule) - ? matrix.Visibility.public - : matrix.Visibility.private, - ); - }, - ); - } - void goToEmoteSettings() async { final room = Matrix.of(context).client.getRoomById(roomId!)!; // okay, we need to test if there are any emote state events other than the default one diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index 127a98507..387f6e8cd 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -267,23 +267,6 @@ class ChatDetailsView extends StatelessWidget { height: 1, color: Theme.of(context).dividerColor, ), - if (room.joinRules == JoinRules.public) - ListTile( - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon(Icons.link_outlined), - ), - trailing: const Icon(Icons.chevron_right_outlined), - onTap: controller.editAliases, - title: Text(L10n.of(context)!.editRoomAliases), - subtitle: Text( - (room.canonicalAlias.isNotEmpty) - ? room.canonicalAlias - : L10n.of(context)!.none, - ), - ), ListTile( leading: CircleAvatar( backgroundColor: @@ -308,69 +291,14 @@ class ChatDetailsView extends StatelessWidget { child: const Icon(Icons.shield_outlined), ), title: Text( - L10n.of(context)!.whoIsAllowedToJoinThisGroup, - ), - trailing: room.canChangeJoinRules - ? const Icon(Icons.chevron_right_outlined) - : null, - subtitle: Text( - room.joinRules?.getLocalizedString( - MatrixLocals(L10n.of(context)!), - ) ?? - L10n.of(context)!.none, - ), - onTap: room.canChangeJoinRules - ? controller.setJoinRules - : null, - ), - if (!room.isDirectChat) - ListTile( - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon(Icons.visibility_outlined), - ), - trailing: room.canChangeHistoryVisibility - ? const Icon(Icons.chevron_right_outlined) - : null, - title: Text( - L10n.of(context)!.visibilityOfTheChatHistory, + L10n.of(context)!.accessAndVisibility, ), subtitle: Text( - room.historyVisibility?.getLocalizedString( - MatrixLocals(L10n.of(context)!), - ) ?? - L10n.of(context)!.none, + L10n.of(context)!.accessAndVisibilityDescription, ), - onTap: room.canChangeHistoryVisibility - ? controller.setHistoryVisibility - : null, - ), - if (room.joinRules == JoinRules.public) - ListTile( - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon( - Icons.person_add_alt_1_outlined, - ), - ), - trailing: room.canChangeGuestAccess - ? const Icon(Icons.chevron_right_outlined) - : null, - title: Text( - L10n.of(context)!.areGuestsAllowedToJoin, - ), - subtitle: Text( - room.guestAccess.getLocalizedString( - MatrixLocals(L10n.of(context)!), - ), - ), - onTap: room.canChangeGuestAccess - ? controller.setGuestAccess - : null, + onTap: () => context + .push('/rooms/${room.id}/details/access'), + trailing: const Icon(Icons.chevron_right_outlined), ), if (!room.isDirectChat) ListTile( diff --git a/lib/pages/chat_details/participant_list_item.dart b/lib/pages/chat_details/participant_list_item.dart index e044e38e7..661153634 100644 --- a/lib/pages/chat_details/participant_list_item.dart +++ b/lib/pages/chat_details/participant_list_item.dart @@ -14,12 +14,14 @@ class ParticipantListItem extends StatelessWidget { @override Widget build(BuildContext context) { - final membershipBatch = { - Membership.join: '', - Membership.ban: L10n.of(context)!.banned, - Membership.invite: L10n.of(context)!.invited, - Membership.leave: L10n.of(context)!.leftTheChat, + final membershipBatch = switch (user.membership) { + Membership.ban => L10n.of(context)!.banned, + Membership.invite => L10n.of(context)!.invited, + Membership.join => null, + Membership.knock => L10n.of(context)!.knocking, + Membership.leave => L10n.of(context)!.leftTheChat, }; + final permissionBatch = user.powerLevel == 100 ? L10n.of(context)!.admin : user.powerLevel >= 50 @@ -66,7 +68,7 @@ class ParticipantListItem extends StatelessWidget { ), ), ), - membershipBatch[user.membership]!.isEmpty + membershipBatch == null ? const SizedBox.shrink() : Container( padding: const EdgeInsets.all(4), @@ -75,8 +77,7 @@ class ParticipantListItem extends StatelessWidget { color: Theme.of(context).secondaryHeaderColor, borderRadius: BorderRadius.circular(8), ), - child: - Center(child: Text(membershipBatch[user.membership]!)), + child: Center(child: Text(membershipBatch)), ), ], ), diff --git a/lib/pages/chat_permissions_settings/chat_permissions_settings.dart b/lib/pages/chat_permissions_settings/chat_permissions_settings.dart index a6cdcbef0..4d3bdaa09 100644 --- a/lib/pages/chat_permissions_settings/chat_permissions_settings.dart +++ b/lib/pages/chat_permissions_settings/chat_permissions_settings.dart @@ -2,7 +2,6 @@ import 'dart:developer'; import 'package:flutter/material.dart'; -import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:go_router/go_router.dart'; @@ -71,44 +70,6 @@ class ChatPermissionsSettingsController extends State { false), ); - void updateRoomAction(Capabilities capabilities) async { - final room = Matrix.of(context).client.getRoomById(roomId!)!; - final roomVersion = room - .getState(EventTypes.RoomCreate)! - .content['room_version'] as String? ?? - '1'; - final newVersion = await showConfirmationDialog( - context: context, - title: L10n.of(context)!.replaceRoomWithNewerVersion, - actions: capabilities.mRoomVersions!.available.entries - .where((r) => r.key != roomVersion) - .map( - (version) => AlertDialogAction( - key: version.key, - label: - '${version.key} (${version.value.toString().split('.').last})', - ), - ) - .toList(), - ); - if (newVersion == null || - OkCancelResult.cancel == - await showOkCancelAlertDialog( - useRootNavigator: false, - context: context, - okLabel: L10n.of(context)!.yes, - cancelLabel: L10n.of(context)!.cancel, - title: L10n.of(context)!.areYouSure, - message: L10n.of(context)!.roomUpgradeDescription, - )) { - return; - } - await showFutureLoadingDialog( - context: context, - future: () => room.client.upgradeRoom(roomId!, newVersion), - ).then((_) => context.pop()); - } - @override Widget build(BuildContext context) => ChatPermissionsSettingsView(this); } diff --git a/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart b/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart index b88095b20..11aad87ea 100644 --- a/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart +++ b/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart @@ -54,7 +54,7 @@ class ChatPermissionsSettingsView extends StatelessWidget { entry.value, ), ), - const Divider(thickness: 1), + Divider(color: Theme.of(context).dividerColor), ListTile( title: Text( L10n.of(context)!.notifications, @@ -87,7 +87,7 @@ class ChatPermissionsSettingsView extends StatelessWidget { ); }, ), - const Divider(thickness: 1), + Divider(color: Theme.of(context).dividerColor), ListTile( title: Text( L10n.of(context)!.configureChat, @@ -109,33 +109,6 @@ class ChatPermissionsSettingsView extends StatelessWidget { category: 'events', ), ), - if (room.canSendEvent(EventTypes.RoomTombstone)) ...{ - const Divider(thickness: 1), - FutureBuilder( - future: room.client.getCapabilities(), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return const Center( - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, - ), - ); - } - final roomVersion = room - .getState(EventTypes.RoomCreate)! - .content['room_version'] as String? ?? - '1'; - - return ListTile( - title: Text( - '${L10n.of(context)!.roomVersion}: $roomVersion', - ), - onTap: () => - controller.updateRoomAction(snapshot.data!), - ); - }, - ), - }, ], ), ], diff --git a/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart b/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart index 93774a8ca..c7eb6a29b 100644 --- a/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart +++ b/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; +import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/utils/url_launcher.dart'; @@ -105,6 +107,57 @@ class UserBottomSheetView extends StatelessWidget { ), body: ListView( children: [ + if (user?.membership == Membership.knock) + Padding( + padding: const EdgeInsets.all(12.0), + child: Material( + color: Theme.of(context).colorScheme.tertiaryContainer, + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + child: ListTile( + minVerticalPadding: 16, + title: Padding( + padding: const EdgeInsets.only(bottom: 12.0), + child: Text( + L10n.of(context)! + .userWouldLikeToChangeTheChat(displayname), + ), + ), + subtitle: Row( + children: [ + TextButton.icon( + style: TextButton.styleFrom( + backgroundColor: + Theme.of(context).colorScheme.background, + foregroundColor: + Theme.of(context).colorScheme.primary, + ), + onPressed: () => showFutureLoadingDialog( + context: context, + future: () => user!.room.invite(user.id), + ), + icon: const Icon(Icons.check_outlined), + label: Text(L10n.of(context)!.accept), + ), + const SizedBox(width: 12), + TextButton.icon( + style: TextButton.styleFrom( + backgroundColor: + Theme.of(context).colorScheme.errorContainer, + foregroundColor: + Theme.of(context).colorScheme.onErrorContainer, + ), + onPressed: () => showFutureLoadingDialog( + context: context, + future: () => user!.room.kick(user.id), + ), + icon: const Icon(Icons.cancel_outlined), + label: Text(L10n.of(context)!.decline), + ), + ], + ), + ), + ), + ), Row( children: [ Padding( diff --git a/lib/widgets/public_room_bottom_sheet.dart b/lib/widgets/public_room_bottom_sheet.dart index 19932f5b9..7871bc903 100644 --- a/lib/widgets/public_room_bottom_sheet.dart +++ b/lib/widgets/public_room_bottom_sheet.dart @@ -31,22 +31,26 @@ class PublicRoomBottomSheet extends StatelessWidget { void _joinRoom(BuildContext context) async { final client = Matrix.of(outerContext).client; final chunk = this.chunk; + final knock = chunk?.joinRule == 'knock'; final result = await showFutureLoadingDialog( context: context, future: () async { if (chunk != null && client.getRoomById(chunk.roomId) != null) { return chunk.roomId; } - final roomId = chunk != null && chunk.joinRule == 'knock' + final roomId = chunk != null && knock ? await client.knockRoom(chunk.roomId) : await client.joinRoom(roomAlias ?? chunk!.roomId); - if (client.getRoomById(roomId) == null) { + if (!knock && client.getRoomById(roomId) == null) { await client.waitForRoomInSync(roomId); } return roomId; }, ); + if (knock) { + return; + } if (result.error == null) { Navigator.of(context).pop(); // don't open the room if the joined room is a space @@ -138,9 +142,11 @@ class PublicRoomBottomSheet extends StatelessWidget { child: ElevatedButton.icon( onPressed: () => _joinRoom(context), label: Text( - chunk?.roomType == 'm.space' - ? L10n.of(context)!.joinSpace - : L10n.of(context)!.joinRoom, + chunk?.joinRule == 'knock' + ? L10n.of(context)!.knock + : chunk?.roomType == 'm.space' + ? L10n.of(context)!.joinSpace + : L10n.of(context)!.joinRoom, ), icon: const Icon(Icons.login_outlined), ), From 45e72428e29e923340a1b223eca89ab313d92453 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 14 Apr 2024 16:23:39 +0200 Subject: [PATCH 06/18] chore: follow up accept decline kick --- .../user_bottom_sheet/user_bottom_sheet.dart | 20 +++++++++++++++++++ .../user_bottom_sheet_view.dart | 13 +++--------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart index e2ae9c9ff..ae6c1c92d 100644 --- a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart +++ b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart @@ -250,6 +250,26 @@ class UserBottomSheetController extends State { } } + void knockAccept() async { + final user = widget.user!; + final result = await showFutureLoadingDialog( + context: context, + future: () => user.room.invite(user.id), + ); + if (result.error != null) return; + Navigator.of(context).pop(); + } + + void knockDecline() async { + final user = widget.user!; + final result = await showFutureLoadingDialog( + context: context, + future: () => user.room.kick(user.id), + ); + if (result.error != null) return; + Navigator.of(context).pop(); + } + @override Widget build(BuildContext context) => UserBottomSheetView(this); } diff --git a/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart b/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart index c7eb6a29b..13319d0e2 100644 --- a/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart +++ b/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; -import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; @@ -111,7 +110,7 @@ class UserBottomSheetView extends StatelessWidget { Padding( padding: const EdgeInsets.all(12.0), child: Material( - color: Theme.of(context).colorScheme.tertiaryContainer, + color: Theme.of(context).colorScheme.surfaceVariant, borderRadius: BorderRadius.circular(AppConfig.borderRadius), child: ListTile( minVerticalPadding: 16, @@ -131,10 +130,7 @@ class UserBottomSheetView extends StatelessWidget { foregroundColor: Theme.of(context).colorScheme.primary, ), - onPressed: () => showFutureLoadingDialog( - context: context, - future: () => user!.room.invite(user.id), - ), + onPressed: controller.knockAccept, icon: const Icon(Icons.check_outlined), label: Text(L10n.of(context)!.accept), ), @@ -146,10 +142,7 @@ class UserBottomSheetView extends StatelessWidget { foregroundColor: Theme.of(context).colorScheme.onErrorContainer, ), - onPressed: () => showFutureLoadingDialog( - context: context, - future: () => user!.room.kick(user.id), - ), + onPressed: controller.knockDecline, icon: const Icon(Icons.cancel_outlined), label: Text(L10n.of(context)!.decline), ), From 4353c8ddd13f9ae80da5ad84ecbc0c6eeee773bd Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 14 Apr 2024 16:29:11 +0200 Subject: [PATCH 07/18] chore: Follow up knock room --- lib/widgets/public_room_bottom_sheet.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/widgets/public_room_bottom_sheet.dart b/lib/widgets/public_room_bottom_sheet.dart index 7871bc903..3a4f68f3f 100644 --- a/lib/widgets/public_room_bottom_sheet.dart +++ b/lib/widgets/public_room_bottom_sheet.dart @@ -142,7 +142,11 @@ class PublicRoomBottomSheet extends StatelessWidget { child: ElevatedButton.icon( onPressed: () => _joinRoom(context), label: Text( - chunk?.joinRule == 'knock' + chunk?.joinRule == 'knock' && + Matrix.of(context) + .client + .getRoomById(chunk!.roomId) == + null ? L10n.of(context)!.knock : chunk?.roomType == 'm.space' ? L10n.of(context)!.joinSpace From 2d5f496d3c91adc1d0005100fa57e6475d82690c Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 14 Apr 2024 16:41:07 +0200 Subject: [PATCH 08/18] chore: Follow up localize join rules --- assets/l10n/intl_en.arb | 2 ++ .../chat_access_settings_page.dart | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 37470efcb..021fdd960 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2279,6 +2279,8 @@ "user": {} } }, + "usersMustKnock": "Users must knock", + "noOneCanJoin": "No one can join", "userWouldLikeToChangeTheChat": "{user} would like to join the chat.", "@userWouldLikeToChangeTheChat": { "placeholders": { diff --git a/lib/pages/chat_access_settings/chat_access_settings_page.dart b/lib/pages/chat_access_settings/chat_access_settings_page.dart index 8c5004733..60a78c011 100644 --- a/lib/pages/chat_access_settings/chat_access_settings_page.dart +++ b/lib/pages/chat_access_settings/chat_access_settings_page.dart @@ -61,8 +61,7 @@ class ChatAccessSettingsPageView extends StatelessWidget { for (final joinRule in JoinRules.values) RadioListTile.adaptive( title: Text( - joinRule - .getLocalizedString(MatrixLocals(L10n.of(context)!)), + joinRule.localizedString(L10n.of(context)!), ), value: joinRule, groupValue: room.joinRules, @@ -170,3 +169,18 @@ class ChatAccessSettingsPageView extends StatelessWidget { ); } } + +extension JoinRulesDisplayString on JoinRules { + String localizedString(L10n l10n) { + switch (this) { + case JoinRules.public: + return l10n.anyoneCanJoin; + case JoinRules.invite: + return l10n.invitedUsersOnly; + case JoinRules.knock: + return l10n.usersMustKnock; + case JoinRules.private: + return l10n.noOneCanJoin; + } + } +} From 5c7e7319e82cd5252a231930116999ee941e0dcc Mon Sep 17 00:00:00 2001 From: Rudah Ximenes Alvarenga Date: Fri, 12 Apr 2024 22:16:59 +0000 Subject: [PATCH 09/18] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (599 of 599 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/pt_BR/ --- assets/l10n/intl_pt_BR.arb | 133 ++++++++++++++++++++++++++++++++++++- 1 file changed, 132 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_pt_BR.arb b/assets/l10n/intl_pt_BR.arb index bfbae8f48..3085a4afa 100644 --- a/assets/l10n/intl_pt_BR.arb +++ b/assets/l10n/intl_pt_BR.arb @@ -2484,5 +2484,136 @@ "searchChatsRooms": "Buscar por #conversas, @usuários...", "@searchChatsRooms": {}, "databaseMigrationBody": "Por favor, espere. Isto pode demorar um pouco.", - "@databaseMigrationBody": {} + "@databaseMigrationBody": {}, + "youInvitedToBy": "Você foi convidado através do link para:\n{alias}", + "@youInvitedToBy": { + "placeholders": { + "alias": {} + } + }, + "forwardMessageTo": "Encaminhar mensagem para {roomName}?", + "@forwardMessageTo": { + "type": "text", + "placeholders": { + "roomName": {} + } + }, + "formattedMessagesDescription": "Mostrar mensagens ricas com conteúdos tipo negrito usando markdown.", + "@formattedMessagesDescription": {}, + "verifyOtherUser": "🔐 Verificar outro usuário", + "@verifyOtherUser": {}, + "verifyOtherDevice": "🔐 Verificar outro aparelho", + "@verifyOtherDevice": {}, + "acceptedKeyVerification": "{sender} aceitou sua chave de verificação", + "@acceptedKeyVerification": { + "type": "text", + "placeholders": { + "sender": {} + } + }, + "startedKeyVerification": "{sender} iniciou a chave de verificação", + "@startedKeyVerification": { + "type": "text", + "placeholders": { + "sender": {} + } + }, + "transparent": "Transparente", + "@transparent": {}, + "databaseBuildErrorBody": "Não foi possível construir o banco de dados SQLite. O aplicativo tentará utilizar o banco de dados legado por enquanto. Por favor, reporte este erro aos desenvolvedores em {url}. A mensagem de erro é: {error}", + "@databaseBuildErrorBody": { + "type": "text", + "placeholders": { + "url": {}, + "error": {} + } + }, + "initAppError": "Ocorreu um erro enquanto o aplicativo era iniciado", + "@initAppError": {}, + "restoreSessionBody": "O aplicativo tentará agora restaurar sua sessão a partir do backup. Por favor, reporte este ao desenvolvedor em {url}. A mensagem de erro é: {error}", + "@restoreSessionBody": { + "type": "text", + "placeholders": { + "url": {}, + "error": {} + } + }, + "sendReadReceipts": "Enviar recibos de leitura", + "@sendReadReceipts": {}, + "sendTypingNotificationsDescription": "Outros participantes neste chat podem ver quando você está digitando uma nova mensagem.", + "@sendTypingNotificationsDescription": {}, + "formattedMessages": "Mensagens formatadas", + "@formattedMessages": {}, + "presenceStyle": "Presença:", + "@presenceStyle": { + "type": "text", + "placeholders": {} + }, + "presencesToggle": "Mostrar o status das mensagens de outros usuários", + "@presencesToggle": { + "type": "text", + "placeholders": {} + }, + "commandHint_ignore": "Ignorar o seguinte ID Matrix", + "@commandHint_ignore": {}, + "commandHint_unignore": "Designorar o seguinte ID Matrix", + "@commandHint_unignore": {}, + "hidePresences": "Esconder lista de status?", + "@hidePresences": {}, + "sessionLostBody": "Sua sessão foi desconectada. Por favor, reporte este ao desenvolvedor em {url}. A mensagem de erro é: {error}", + "@sessionLostBody": { + "type": "text", + "placeholders": { + "url": {}, + "error": {} + } + }, + "sendReadReceiptsDescription": "Outros participantes neste chat podem ver quando você tiver lido uma mensagem.", + "@sendReadReceiptsDescription": {}, + "verifyOtherUserDescription": "Se você verificar outro usuário, você terá certeza que você conhece com quem está conversando. 💪\n\nQuando iniciar uma verificação, você e o outro usuário receberão um popup no aplicativo. Então vocês receberão uma série de emojis ou números para comparar um com o outro.\n\nA melhor maneira de fazer este procedimento é encontrar pessoalmente ou através de um vídeochamada. 👭", + "@verifyOtherUserDescription": {}, + "requestedKeyVerification": "{sender} enviou uma chave de verificação", + "@requestedKeyVerification": { + "type": "text", + "placeholders": { + "sender": {} + } + }, + "verifyOtherDeviceDescription": "Quando você verifica outro aparelho, estes aparelhos poderão trocar chaves, aumentando sua segurança. 💪\n\nQuando iniciar a verificação, um popup aparecerá no aplicativo em ambos os aparelhos. Então você verá uma série de emojis ou números que você terá que comparar um com o outro.\n\nÉ melhor fazer esse procedimento com ambos os aparelhos em mãos antes de começar a verificação. 🤳", + "@verifyOtherDeviceDescription": {}, + "canceledKeyVerification": "{sender} cancelou sua chave de verificação", + "@canceledKeyVerification": { + "type": "text", + "placeholders": { + "sender": {} + } + }, + "isReadyForKeyVerification": "{sender} está pronto para a chave de verificação", + "@isReadyForKeyVerification": { + "type": "text", + "placeholders": { + "sender": {} + } + }, + "completedKeyVerification": "{sender} completou a chave de verificação", + "@completedKeyVerification": { + "type": "text", + "placeholders": { + "sender": {} + } + }, + "stickers": "Stickers", + "@stickers": {}, + "discover": "Descubra", + "@discover": {}, + "incomingMessages": "Mensagens recebidas", + "@incomingMessages": {}, + "unreadChatsInApp": "{appname}: {unread} mensagens não lidas", + "@unreadChatsInApp": { + "type": "text", + "placeholders": { + "appname": {}, + "unread": {} + } + } } From 9516469d2813b4cec77ff7d44b20ae93b468f51d Mon Sep 17 00:00:00 2001 From: Rex_sa Date: Sun, 14 Apr 2024 04:57:59 +0000 Subject: [PATCH 10/18] Translated using Weblate (Arabic) Currently translated at 100.0% (600 of 600 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ar/ --- assets/l10n/intl_ar.arb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_ar.arb b/assets/l10n/intl_ar.arb index 59d320458..57d7a4252 100644 --- a/assets/l10n/intl_ar.arb +++ b/assets/l10n/intl_ar.arb @@ -2616,5 +2616,7 @@ "appname": {}, "unread": {} } - } + }, + "noDatabaseEncryption": "تشفير قاعدة البيانات غير مدعوم على هذا النظام الأساسي", + "@noDatabaseEncryption": {} } From 3864fab771e31c58dd73b12501c40558566b1666 Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 14 Apr 2024 14:35:30 +0000 Subject: [PATCH 11/18] Translated using Weblate (German) Currently translated at 100.0% (600 of 600 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/ --- assets/l10n/intl_de.arb | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_de.arb b/assets/l10n/intl_de.arb index 13bf1c48a..147b9b95d 100644 --- a/assets/l10n/intl_de.arb +++ b/assets/l10n/intl_de.arb @@ -2586,5 +2586,37 @@ } }, "verifyOtherDeviceDescription": "Wenn Sie ein anderes Gerät verifizieren, können diese Geräteschlüssel austauschen, was Ihre Sicherheit insgesamt erhöht. 💪 Wenn Sie eine Verifizierung starten, erscheint ein Pop-up in der App auf beiden Geräten. Dort sehen Sie dann eine Reihe von Emojis oder Zahlen, die Sie miteinander vergleichen müssen. Am besten hältst du beide Geräte bereit, bevor du die Verifizierung startest. 🤳", - "@verifyOtherDeviceDescription": {} + "@verifyOtherDeviceDescription": {}, + "presenceStyle": "Statusmeldungen:", + "@presenceStyle": { + "type": "text", + "placeholders": {} + }, + "presencesToggle": "Status-Nachrichten anderer Benutzer anzeigen", + "@presencesToggle": { + "type": "text", + "placeholders": {} + }, + "incomingMessages": "Eingehende Nachrichten", + "@incomingMessages": {}, + "commandHint_unignore": "Angegebene Matrix-ID nicht mehr ignorieren", + "@commandHint_unignore": {}, + "commandHint_ignore": "Angegebene Matrix-ID ignorieren", + "@commandHint_ignore": {}, + "noDatabaseEncryption": "Datenbankverschlüsselung wird auf dieser Plattform nicht unterstützt", + "@noDatabaseEncryption": {}, + "hidePresences": "Status-Liste verbergen?", + "@hidePresences": {}, + "stickers": "Sticker", + "@stickers": {}, + "discover": "Entdecken", + "@discover": {}, + "unreadChatsInApp": "{appname}: {unread} ungelesene Chats", + "@unreadChatsInApp": { + "type": "text", + "placeholders": { + "appname": {}, + "unread": {} + } + } } From d0bcdf4b1eba3804350e81451b8a92938958ecda Mon Sep 17 00:00:00 2001 From: xabirequejo Date: Sun, 14 Apr 2024 10:43:54 +0000 Subject: [PATCH 12/18] Translated using Weblate (Basque) Currently translated at 100.0% (600 of 600 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/eu/ --- assets/l10n/intl_eu.arb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_eu.arb b/assets/l10n/intl_eu.arb index fc29b0f12..f584d2ac2 100644 --- a/assets/l10n/intl_eu.arb +++ b/assets/l10n/intl_eu.arb @@ -2616,5 +2616,7 @@ "appname": {}, "unread": {} } - } + }, + "noDatabaseEncryption": "Plataforma honetan ezin da datu-basea zifratu", + "@noDatabaseEncryption": {} } From df2f42d68e4d4400e241f46ad0d20eb34a7f425f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?jos=C3=A9=20m?= Date: Sun, 14 Apr 2024 06:19:32 +0000 Subject: [PATCH 13/18] Translated using Weblate (Galician) Currently translated at 100.0% (600 of 600 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/gl/ --- assets/l10n/intl_gl.arb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_gl.arb b/assets/l10n/intl_gl.arb index 0231b9dce..5073a3a9a 100644 --- a/assets/l10n/intl_gl.arb +++ b/assets/l10n/intl_gl.arb @@ -2616,5 +2616,7 @@ "appname": {}, "unread": {} } - } + }, + "noDatabaseEncryption": "Nesta plataforma non temos soporte para cifrar a base de datos", + "@noDatabaseEncryption": {} } From a1109e21b5c739f2f4a942b1475d48e18dc40e33 Mon Sep 17 00:00:00 2001 From: Linerly Date: Sun, 14 Apr 2024 04:49:35 +0000 Subject: [PATCH 14/18] Translated using Weblate (Indonesian) Currently translated at 100.0% (600 of 600 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/id/ --- assets/l10n/intl_id.arb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_id.arb b/assets/l10n/intl_id.arb index e4eaf6d8c..055814c47 100644 --- a/assets/l10n/intl_id.arb +++ b/assets/l10n/intl_id.arb @@ -2615,5 +2615,7 @@ "appname": {}, "unread": {} } - } + }, + "noDatabaseEncryption": "Enkripsi basis data tidak didukung di platform ini", + "@noDatabaseEncryption": {} } From ccd4f705f9aba374ff94f59aadc5ee55cb255017 Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 14 Apr 2024 14:43:09 +0000 Subject: [PATCH 15/18] Translated using Weblate (German) Currently translated at 100.0% (620 of 620 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/ --- assets/l10n/intl_de.arb | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/assets/l10n/intl_de.arb b/assets/l10n/intl_de.arb index 147b9b95d..edea03a7f 100644 --- a/assets/l10n/intl_de.arb +++ b/assets/l10n/intl_de.arb @@ -2618,5 +2618,59 @@ "appname": {}, "unread": {} } + }, + "customEmojisAndStickersBody": "Eigene Emojis oder Sticker zur Nutzung im Chat hinzufügen oder teilen.", + "@customEmojisAndStickersBody": {}, + "globalChatId": "Globale Chat-ID", + "@globalChatId": {}, + "accessAndVisibility": "Zugang und Sichtbarkeit", + "@accessAndVisibility": {}, + "hideMemberChangesInPublicChats": "Mitglieder-Änderungen in öffentlichen Chats ausblenden", + "@hideMemberChangesInPublicChats": {}, + "accessAndVisibilityDescription": "Wer darf dem Chat beitreten und wie kann der Chat gefunden werden.", + "@accessAndVisibilityDescription": {}, + "hideMemberChangesInPublicChatsBody": "Zeige keine Beitritt- oder Verlassen-Ereignisse von Mitgliedern in der Timeline an, um die Lesbarkeit in öffentlichen Chats zu verbessern.", + "@hideMemberChangesInPublicChatsBody": {}, + "userWouldLikeToChangeTheChat": "{user} würde dem Chat gerne beitreten.", + "@userWouldLikeToChangeTheChat": { + "placeholders": { + "user": {} + } + }, + "noPublicLinkHasBeenCreatedYet": "Es wurde noch kein öffentlicher Link erstellt", + "@noPublicLinkHasBeenCreatedYet": {}, + "chatCanBeDiscoveredViaSearchOnServer": "Chat kann über die Suche auf {server} gefunden werden", + "@chatCanBeDiscoveredViaSearchOnServer": { + "type": "text", + "placeholders": { + "server": {} + } + }, + "appLockDescription": "App mit einer PIN sperren, wenn sie nicht verwendet wird", + "@appLockDescription": {}, + "calls": "Anrufe", + "@calls": {}, + "customEmojisAndStickers": "Eigene Emojis und Sticker", + "@customEmojisAndStickers": {}, + "hideRedactedMessages": "Geschwärzte Nachrichten verstecken", + "@hideRedactedMessages": {}, + "hideRedactedMessagesBody": "Wenn jemand eine Nachricht schwärzt/löscht, dann wird diese Nachricht im Chat nicht mehr sichtbar sein.", + "@hideRedactedMessagesBody": {}, + "hideInvalidOrUnknownMessageFormats": "Ungültige und unbekannte Nachrichten-Formate ausblenden", + "@hideInvalidOrUnknownMessageFormats": {}, + "overview": "Übersicht", + "@overview": {}, + "notifyMeFor": "Benachrichtige mich für", + "@notifyMeFor": {}, + "passwordRecoverySettings": "Passwort-Wiederherstellungs-Einstellungen", + "@passwordRecoverySettings": {}, + "knock": "Anklopfen", + "@knock": {}, + "knocking": "Klopft", + "@knocking": {}, + "thereAreCountUsersBlocked": "Im Augenblick werden {count} Benutzer blockiert.", + "@thereAreCountUsersBlocked": { + "type": "text", + "count": {} } } From 109136a8df254c1d465b5db8616d65c243b36ea3 Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 14 Apr 2024 14:47:52 +0000 Subject: [PATCH 16/18] Translated using Weblate (German) Currently translated at 100.0% (622 of 622 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/ --- assets/l10n/intl_de.arb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_de.arb b/assets/l10n/intl_de.arb index edea03a7f..221886f84 100644 --- a/assets/l10n/intl_de.arb +++ b/assets/l10n/intl_de.arb @@ -2672,5 +2672,9 @@ "@thereAreCountUsersBlocked": { "type": "text", "count": {} - } + }, + "usersMustKnock": "Benutzer müssen anklopfen", + "@usersMustKnock": {}, + "noOneCanJoin": "Niemand kann beitreten", + "@noOneCanJoin": {} } From d691f4641901365f216d4a359005793ddba7f0ca Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 14 Apr 2024 17:05:14 +0200 Subject: [PATCH 17/18] chore: Update user has knocked localization with emoji --- assets/l10n/intl_en.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 021fdd960..b9aaa894f 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2273,7 +2273,7 @@ "user": {} } }, - "hasKnocked": "{user} has knocked", + "hasKnocked": "🚪 {user} has knocked", "@hasKnocked": { "placeholders": { "user": {} From 2095e3ebad29f37e9e30c39d00e15a1605e0a19f Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 14 Apr 2024 20:36:38 +0200 Subject: [PATCH 18/18] chore: Follow up share alias --- .../chat_access_settings/chat_access_settings_page.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pages/chat_access_settings/chat_access_settings_page.dart b/lib/pages/chat_access_settings/chat_access_settings_page.dart index 60a78c011..776fcd48b 100644 --- a/lib/pages/chat_access_settings/chat_access_settings_page.dart +++ b/lib/pages/chat_access_settings/chat_access_settings_page.dart @@ -134,7 +134,10 @@ class ChatAccessSettingsPageView extends StatelessWidget { ) : IconButton( icon: Icon(Icons.adaptive.share_outlined), - onPressed: () => FluffyShare.share(room.id, context), + onPressed: () => FluffyShare.share( + 'https://matrix.to/#/${room.canonicalAlias}', + context, + ), ), ), ],