From 5212d7ce4d66fd1465e851aee788ca9f4f6537b8 Mon Sep 17 00:00:00 2001 From: Malin Errenst Date: Thu, 2 Mar 2023 10:57:52 +0100 Subject: [PATCH] refactor: Added and applied require_trailing_commas linter rule --- analysis_options.yaml | 1 + integration_test/app_test.dart | 21 +- .../extensions/default_flows.dart | 3 +- lib/config/app_config.dart | 5 +- lib/config/routes.dart | 96 +-- lib/pages/add_story/add_story.dart | 17 +- lib/pages/add_story/invite_story_page.dart | 57 +- lib/pages/archive/archive_view.dart | 15 +- lib/pages/bootstrap/bootstrap_dialog.dart | 136 ++-- lib/pages/chat/add_widget_tile.dart | 3 +- lib/pages/chat/add_widget_tile_view.dart | 7 +- lib/pages/chat/chat.dart | 255 ++++--- lib/pages/chat/chat_event_list.dart | 22 +- lib/pages/chat/chat_input_row.dart | 31 +- lib/pages/chat/chat_view.dart | 51 +- lib/pages/chat/edit_widgets_dialog.dart | 21 +- lib/pages/chat/encryption_button.dart | 58 +- lib/pages/chat/event_info_dialog.dart | 3 +- lib/pages/chat/events/audio_player.dart | 23 +- lib/pages/chat/events/html_message.dart | 49 +- lib/pages/chat/events/message.dart | 95 ++- lib/pages/chat/events/message_content.dart | 150 ++-- lib/pages/chat/events/message_reactions.dart | 100 +-- lib/pages/chat/events/reply_content.dart | 31 +- lib/pages/chat/events/state_message.dart | 33 +- .../events/verification_request_content.dart | 34 +- lib/pages/chat/events/video_player.dart | 25 +- lib/pages/chat/input_bar.dart | 68 +- lib/pages/chat/pinned_events.dart | 178 ++--- lib/pages/chat/reactions_picker.dart | 97 +-- lib/pages/chat/recording_dialog.dart | 7 +- lib/pages/chat/reply_display.dart | 53 +- lib/pages/chat/send_file_dialog.dart | 52 +- lib/pages/chat/sticker_picker_dialog.dart | 25 +- lib/pages/chat_details/chat_details.dart | 14 +- lib/pages/chat_details/chat_details_view.dart | 694 +++++++++--------- .../chat_details/participant_list_item.dart | 11 +- .../chat_encryption_settings_view.dart | 323 ++++---- lib/pages/chat_list/chat_list.dart | 112 +-- lib/pages/chat_list/chat_list_body.dart | 453 ++++++------ lib/pages/chat_list/chat_list_header.dart | 24 +- lib/pages/chat_list/chat_list_item.dart | 29 +- lib/pages/chat_list/chat_list_view.dart | 96 +-- .../chat_list/client_chooser_button.dart | 49 +- lib/pages/chat_list/navi_rail_item.dart | 23 +- lib/pages/chat_list/search_title.dart | 16 +- lib/pages/chat_list/space_view.dart | 335 ++++----- .../chat_permissions_settings.dart | 21 +- .../chat_permissions_settings_view.dart | 59 +- lib/pages/connect/connect_page.dart | 13 +- lib/pages/connect/connect_page_view.dart | 13 +- lib/pages/connect/sso_button.dart | 3 +- .../device_settings/device_settings_view.dart | 9 +- .../user_device_list_item.dart | 5 +- lib/pages/dialer/dialer.dart | 260 ++++--- lib/pages/dialer/pip/pip_view.dart | 8 +- .../homeserver_bottom_sheet.dart | 90 +-- .../homeserver_picker/homeserver_picker.dart | 38 +- .../homeserver_picker_view.dart | 20 +- lib/pages/image_viewer/image_viewer_view.dart | 13 +- .../invitation_selection.dart | 30 +- .../invitation_selection_view.dart | 7 +- .../key_verification_dialog.dart | 149 ++-- lib/pages/login/login.dart | 26 +- lib/pages/login/login_view.dart | 222 +++--- lib/pages/new_group/new_group_view.dart | 7 +- lib/pages/new_space/new_space_view.dart | 7 +- lib/pages/settings/settings_view.dart | 170 ++--- lib/pages/settings_3pid/settings_3pid.dart | 11 +- .../settings_3pid/settings_3pid_view.dart | 18 +- .../settings_emotes/settings_emotes.dart | 19 +- .../settings_emotes/settings_emotes_view.dart | 21 +- .../settings_ignore_list_view.dart | 52 +- .../settings_multiple_emotes_view.dart | 41 +- .../settings_notifications_view.dart | 146 ++-- .../settings_security/settings_security.dart | 15 +- .../settings_stories/settings_stories.dart | 30 +- .../settings_stories_view.dart | 7 +- .../settings_style/settings_style_view.dart | 38 +- lib/pages/story/story_page.dart | 102 +-- lib/pages/story/story_view.dart | 14 +- .../user_bottom_sheet/user_bottom_sheet.dart | 61 +- lib/utils/account_bundles.dart | 10 +- lib/utils/background_push.dart | 28 +- lib/utils/client_manager.dart | 22 +- lib/utils/custom_image_resizer.dart | 8 +- lib/utils/date_time_extension.dart | 15 +- lib/utils/fluffy_share.dart | 3 +- .../client_stories_extension.dart | 41 +- .../flutter_hive_collections_database.dart | 9 +- .../ios_badge_client_extension.dart | 5 +- .../matrix_sdk_extensions/matrix_locals.dart | 8 +- lib/utils/push_helper.dart | 7 +- lib/utils/room_status_extension.dart | 16 +- lib/utils/stream_extension.dart | 8 +- lib/utils/uia_request_manager.dart | 3 +- lib/utils/update_checker_no_store.dart | 3 +- lib/utils/url_launcher.dart | 32 +- lib/utils/voip/callkeep_manager.dart | 52 +- lib/utils/voip_plugin.dart | 37 +- lib/widgets/chat_settings_popup_menu.dart | 18 +- lib/widgets/content_banner.dart | 20 +- lib/widgets/layouts/loading_view.dart | 6 +- .../local_notifications_extension.dart | 20 +- lib/widgets/lock_screen.dart | 3 +- lib/widgets/log_view.dart | 10 +- lib/widgets/matrix.dart | 76 +- lib/widgets/permission_slider_dialog.dart | 6 +- lib/widgets/public_room_bottom_sheet.dart | 112 +-- lib/widgets/theme_builder.dart | 11 +- lib/widgets/unread_rooms_badge.dart | 63 +- test/command_hint_test.dart | 9 +- 112 files changed, 3509 insertions(+), 2898 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 2c5e3c25..34a01078 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -8,6 +8,7 @@ linter: - prefer_final_locals - prefer_final_in_for_each - sort_pub_dependencies + - require_trailing_commas analyzer: errors: diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart index f4056bb3..90f42307 100644 --- a/integration_test/app_test.dart +++ b/integration_test/app_test.dart @@ -144,13 +144,15 @@ void main() { await tester.waitFor( find.descendant( - of: find.byType(InvitationSelectionView), - matching: find.byType(TextField)), + of: find.byType(InvitationSelectionView), + matching: find.byType(TextField), + ), ); await tester.enterText( find.descendant( - of: find.byType(InvitationSelectionView), - matching: find.byType(TextField)), + of: find.byType(InvitationSelectionView), + matching: find.byType(TextField), + ), Users.user2.name, ); @@ -160,14 +162,17 @@ void main() { await Future.delayed(const Duration(milliseconds: 1000)); await tester.pumpAndSettle(); - await tester.tap(find - .descendant( + await tester.tap( + find + .descendant( of: find.descendant( of: find.byType(InvitationSelectionView), matching: find.byType(ListTile), ), - matching: find.text(Users.user2.name)) - .last); + matching: find.text(Users.user2.name), + ) + .last, + ); await tester.pumpAndSettle(); await tester.waitFor(find.maybeUppercaseText('Yes')); diff --git a/integration_test/extensions/default_flows.dart b/integration_test/extensions/default_flows.dart index aa435078..66099690 100644 --- a/integration_test/extensions/default_flows.dart +++ b/integration_test/extensions/default_flows.dart @@ -144,7 +144,8 @@ extension DefaultFlowExtensions on WidgetTester { do { if (DateTime.now().isAfter(end)) { throw Exception( - 'Timed out waiting for HomeserverPicker or ChatListViewBody'); + 'Timed out waiting for HomeserverPicker or ChatListViewBody', + ); } await pumpAndSettle(); diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index bfe4c449..9ce74b6f 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -70,8 +70,9 @@ abstract class AppConfig { colorSchemeSeed = Color(json['chat_color']); } catch (e) { Logs().w( - 'Invalid color in config.json! Please make sure to define the color in this format: "0xffdd0000"', - e); + 'Invalid color in config.json! Please make sure to define the color in this format: "0xffdd0000"', + e, + ); } } if (json['application_name'] is String) { diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 8d6ec936..a98036bc 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -70,21 +70,25 @@ class AppRoutes { widget: const ChatDetails(), stackedRoutes: _chatDetailsRoutes, ), - VWidget(path: ':roomid', widget: const Chat(), stackedRoutes: [ - VWidget( - path: 'encryption', - widget: const ChatEncryptionSettings(), - ), - VWidget( - path: 'invite', - widget: const InvitationSelection(), - ), - VWidget( - path: 'details', - widget: const ChatDetails(), - stackedRoutes: _chatDetailsRoutes, - ), - ]), + VWidget( + path: ':roomid', + widget: const Chat(), + stackedRoutes: [ + VWidget( + path: 'encryption', + widget: const ChatEncryptionSettings(), + ), + VWidget( + path: 'invite', + widget: const InvitationSelection(), + ), + VWidget( + path: 'details', + widget: const ChatDetails(), + stackedRoutes: _chatDetailsRoutes, + ), + ], + ), VWidget( path: '/settings', widget: const Settings(), @@ -263,21 +267,22 @@ class AppRoutes { buildTransition: _fadeTransition, ), VWidget( - path: 'connect', - widget: const ConnectPage(), - buildTransition: _fadeTransition, - stackedRoutes: [ - VWidget( - path: 'login', - widget: const Login(), - buildTransition: _fadeTransition, - ), - VWidget( - path: 'signup', - widget: const SignupPage(), - buildTransition: _fadeTransition, - ), - ]), + path: 'connect', + widget: const ConnectPage(), + buildTransition: _fadeTransition, + stackedRoutes: [ + VWidget( + path: 'login', + widget: const Login(), + buildTransition: _fadeTransition, + ), + VWidget( + path: 'signup', + widget: const SignupPage(), + buildTransition: _fadeTransition, + ), + ], + ), VWidget( path: 'logs', widget: const LogViewer(), @@ -354,21 +359,22 @@ class AppRoutes { buildTransition: _fadeTransition, ), VWidget( - path: 'connect', - widget: const ConnectPage(), - buildTransition: _fadeTransition, - stackedRoutes: [ - VWidget( - path: 'login', - widget: const Login(), - buildTransition: _fadeTransition, - ), - VWidget( - path: 'signup', - widget: const SignupPage(), - buildTransition: _fadeTransition, - ), - ]), + path: 'connect', + widget: const ConnectPage(), + buildTransition: _fadeTransition, + stackedRoutes: [ + VWidget( + path: 'login', + widget: const Login(), + buildTransition: _fadeTransition, + ), + VWidget( + path: 'signup', + widget: const SignupPage(), + buildTransition: _fadeTransition, + ), + ], + ), ], ), VWidget( diff --git a/lib/pages/add_story/add_story.dart b/lib/pages/add_story/add_story.dart index a09c6f7b..5b5d5392 100644 --- a/lib/pages/add_story/add_story.dart +++ b/lib/pages/add_story/add_story.dart @@ -88,14 +88,15 @@ class AddStoryController extends State { ); if (picked == null) return; final matrixFile = await showFutureLoadingDialog( - context: context, - future: () async { - final bytes = await picked.readAsBytes(); - return MatrixImageFile( - bytes: bytes, - name: picked.name, - ); - }); + context: context, + future: () async { + final bytes = await picked.readAsBytes(); + return MatrixImageFile( + bytes: bytes, + name: picked.name, + ); + }, + ); setState(() { image = matrixFile.result; diff --git a/lib/pages/add_story/invite_story_page.dart b/lib/pages/add_story/invite_story_page.dart index 2b397774..c28af441 100644 --- a/lib/pages/add_story/invite_story_page.dart +++ b/lib/pages/add_story/invite_story_page.dart @@ -92,34 +92,39 @@ class InviteStoryPageState extends State { const Divider(height: 1), Expanded( child: FutureBuilder>( - future: loadContacts, - builder: (context, snapshot) { - final contacts = snapshot.data; - if (contacts == null) { - final error = snapshot.error; - if (error != null) { - return Center( - child: Text(error.toLocalizedString(context))); - } - return const Center( - child: CircularProgressIndicator.adaptive()); + future: loadContacts, + builder: (context, snapshot) { + final contacts = snapshot.data; + if (contacts == null) { + final error = snapshot.error; + if (error != null) { + return Center( + child: Text(error.toLocalizedString(context)), + ); } - _undecided = contacts.map((u) => u.id).toSet(); - return ListView.builder( - itemCount: contacts.length, - itemBuilder: (context, i) => SwitchListTile.adaptive( - value: _invite.contains(contacts[i].id), - onChanged: (b) => setState(() => b - ? _invite.add(contacts[i].id) - : _invite.remove(contacts[i].id)), - secondary: Avatar( - mxContent: contacts[i].avatarUrl, - name: contacts[i].calcDisplayname(), - ), - title: Text(contacts[i].calcDisplayname()), - ), + return const Center( + child: CircularProgressIndicator.adaptive(), ); - }), + } + _undecided = contacts.map((u) => u.id).toSet(); + return ListView.builder( + itemCount: contacts.length, + itemBuilder: (context, i) => SwitchListTile.adaptive( + value: _invite.contains(contacts[i].id), + onChanged: (b) => setState( + () => b + ? _invite.add(contacts[i].id) + : _invite.remove(contacts[i].id), + ), + secondary: Avatar( + mxContent: contacts[i].avatarUrl, + name: contacts[i].calcDisplayname(), + ), + title: Text(contacts[i].calcDisplayname()), + ), + ); + }, + ), ), ], ), diff --git a/lib/pages/archive/archive_view.dart b/lib/pages/archive/archive_view.dart index c19f7459..7740d8ba 100644 --- a/lib/pages/archive/archive_view.dart +++ b/lib/pages/archive/archive_view.dart @@ -36,19 +36,22 @@ class ArchiveView extends StatelessWidget { builder: (BuildContext context) { if (snapshot.hasError) { return Center( - child: Text( - L10n.of(context)!.oopsSomethingWentWrong, - textAlign: TextAlign.center, - )); + child: Text( + L10n.of(context)!.oopsSomethingWentWrong, + textAlign: TextAlign.center, + ), + ); } if (!snapshot.hasData) { return const Center( - child: CircularProgressIndicator.adaptive(strokeWidth: 2)); + child: CircularProgressIndicator.adaptive(strokeWidth: 2), + ); } else { archive = snapshot.data; if (archive == null || archive!.isEmpty) { return const Center( - child: Icon(Icons.archive_outlined, size: 80)); + child: Icon(Icons.archive_outlined, size: 80), + ); } return ListView.builder( itemCount: archive!.length, diff --git a/lib/pages/bootstrap/bootstrap_dialog.dart b/lib/pages/bootstrap/bootstrap_dialog.dart index 20b5aa21..3ef8bcfd 100644 --- a/lib/pages/bootstrap/bootstrap_dialog.dart +++ b/lib/pages/bootstrap/bootstrap_dialog.dart @@ -246,7 +246,8 @@ class BootstrapDialogState extends State { body: Center( child: ConstrainedBox( constraints: const BoxConstraints( - maxWidth: FluffyThemes.columnWidth * 1.5), + maxWidth: FluffyThemes.columnWidth * 1.5, + ), child: ListView( padding: const EdgeInsets.all(16.0), children: [ @@ -258,7 +259,8 @@ class BootstrapDialogState extends State { color: Theme.of(context).colorScheme.primary, ), subtitle: Text( - L10n.of(context)!.pleaseEnterRecoveryKeyDescription), + L10n.of(context)!.pleaseEnterRecoveryKeyDescription, + ), ), const Divider(height: 32), TextField( @@ -274,64 +276,68 @@ class BootstrapDialogState extends State { decoration: InputDecoration( contentPadding: const EdgeInsets.all(16), hintStyle: TextStyle( - fontFamily: Theme.of(context) - .textTheme - .bodyLarge - ?.fontFamily), + fontFamily: + Theme.of(context).textTheme.bodyLarge?.fontFamily, + ), hintText: L10n.of(context)!.recoveryKey, errorText: _recoveryKeyInputError, ), ), const SizedBox(height: 16), ElevatedButton.icon( - style: ElevatedButton.styleFrom( - foregroundColor: - Theme.of(context).colorScheme.onPrimary, - backgroundColor: Theme.of(context).primaryColor, - ), - icon: _recoveryKeyInputLoading - ? const CircularProgressIndicator.adaptive() - : const Icon(Icons.lock_open_outlined), - label: Text(L10n.of(context)!.unlockOldMessages), - onPressed: _recoveryKeyInputLoading - ? null - : () async { - setState(() { - _recoveryKeyInputError = null; - _recoveryKeyInputLoading = true; - }); - try { - final key = - _recoveryKeyTextEditingController.text; - await bootstrap.newSsssKey!.unlock( - keyOrPassphrase: key, - ); - Logs().d('SSSS unlocked'); - await bootstrap - .client.encryption!.crossSigning - .selfSign( - keyOrPassphrase: key, - ); - Logs().d('Successful elfsigned'); - await bootstrap.openExistingSsss(); - } catch (e, s) { - Logs().w('Unable to unlock SSSS', e, s); - setState(() => _recoveryKeyInputError = - L10n.of(context)!.oopsSomethingWentWrong); - } finally { - setState( - () => _recoveryKeyInputLoading = false); - } - }), - const SizedBox(height: 16), - Row(children: [ - const Expanded(child: Divider()), - Padding( - padding: const EdgeInsets.all(12.0), - child: Text(L10n.of(context)!.or), + style: ElevatedButton.styleFrom( + foregroundColor: + Theme.of(context).colorScheme.onPrimary, + backgroundColor: Theme.of(context).primaryColor, ), - const Expanded(child: Divider()), - ]), + icon: _recoveryKeyInputLoading + ? const CircularProgressIndicator.adaptive() + : const Icon(Icons.lock_open_outlined), + label: Text(L10n.of(context)!.unlockOldMessages), + onPressed: _recoveryKeyInputLoading + ? null + : () async { + setState(() { + _recoveryKeyInputError = null; + _recoveryKeyInputLoading = true; + }); + try { + final key = + _recoveryKeyTextEditingController.text; + await bootstrap.newSsssKey!.unlock( + keyOrPassphrase: key, + ); + Logs().d('SSSS unlocked'); + await bootstrap.client.encryption!.crossSigning + .selfSign( + keyOrPassphrase: key, + ); + Logs().d('Successful elfsigned'); + await bootstrap.openExistingSsss(); + } catch (e, s) { + Logs().w('Unable to unlock SSSS', e, s); + setState( + () => _recoveryKeyInputError = + L10n.of(context)!.oopsSomethingWentWrong, + ); + } finally { + setState( + () => _recoveryKeyInputLoading = false, + ); + } + }, + ), + const SizedBox(height: 16), + Row( + children: [ + const Expanded(child: Divider()), + Padding( + padding: const EdgeInsets.all(12.0), + child: Text(L10n.of(context)!.or), + ), + const Expanded(child: Divider()), + ], + ), const SizedBox(height: 16), ElevatedButton.icon( icon: const Icon(Icons.cast_connected_outlined), @@ -408,11 +414,13 @@ class BootstrapDialogState extends State { case BootstrapState.error: titleText = L10n.of(context)!.oopsSomethingWentWrong; body = const Icon(Icons.error_outline, color: Colors.red, size: 40); - buttons.add(AdaptiveFlatButton( - label: L10n.of(context)!.close, - onPressed: () => - Navigator.of(context, rootNavigator: false).pop(false), - )); + buttons.add( + AdaptiveFlatButton( + label: L10n.of(context)!.close, + onPressed: () => + Navigator.of(context, rootNavigator: false).pop(false), + ), + ); break; case BootstrapState.done: titleText = L10n.of(context)!.everythingReady; @@ -423,11 +431,13 @@ class BootstrapDialogState extends State { Text(L10n.of(context)!.yourChatBackupHasBeenSetUp), ], ); - buttons.add(AdaptiveFlatButton( - label: L10n.of(context)!.close, - onPressed: () => - Navigator.of(context, rootNavigator: false).pop(false), - )); + buttons.add( + AdaptiveFlatButton( + label: L10n.of(context)!.close, + onPressed: () => + Navigator.of(context, rootNavigator: false).pop(false), + ), + ); break; } } diff --git a/lib/pages/chat/add_widget_tile.dart b/lib/pages/chat/add_widget_tile.dart index 1034083b..89ad8976 100644 --- a/lib/pages/chat/add_widget_tile.dart +++ b/lib/pages/chat/add_widget_tile.dart @@ -75,7 +75,8 @@ class AddWidgetTileState extends State { Navigator.of(context).pop(); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context)!.errorAddingWidget))); + SnackBar(content: Text(L10n.of(context)!.errorAddingWidget)), + ); } } diff --git a/lib/pages/chat/add_widget_tile_view.dart b/lib/pages/chat/add_widget_tile_view.dart index 0f98b7cc..23118e72 100644 --- a/lib/pages/chat/add_widget_tile_view.dart +++ b/lib/pages/chat/add_widget_tile_view.dart @@ -26,12 +26,15 @@ class AddWidgetTileView extends StatelessWidget { 'm.jitsi': Text(L10n.of(context)!.widgetJitsi), 'm.video': Text(L10n.of(context)!.widgetVideo), 'm.custom': Text(L10n.of(context)!.widgetCustom), - }.map((key, value) => MapEntry( + }.map( + (key, value) => MapEntry( key, Padding( padding: const EdgeInsets.symmetric(horizontal: 4.0), child: value, - ))), + ), + ), + ), onValueChanged: controller.setWidgetType, ), Padding( diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 6c74475b..7a250419 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -82,10 +82,12 @@ class ChatController extends State { final matrixFiles = []; for (var i = 0; i < bytesList.result!.length; i++) { - matrixFiles.add(MatrixFile( - bytes: bytesList.result![i], - name: details.files[i].name, - ).detectFileType); + matrixFiles.add( + MatrixFile( + bytes: bytesList.result![i], + name: details.files[i].name, + ).detectFileType, + ); } await showDialog( @@ -139,18 +141,20 @@ class ChatController extends State { final userId = room?.directChatMatrixID; if (room == null || userId == null) { throw Exception( - 'Try to recreate a room with is not a DM room. This should not be possible from the UI!'); + 'Try to recreate a room with is not a DM room. This should not be possible from the UI!', + ); } final success = await showFutureLoadingDialog( - context: context, - future: () async { - final client = room.client; - final waitForSync = client.onSync.stream - .firstWhere((s) => s.rooms?.leave?.containsKey(room.id) ?? false); - await room.leave(); - await waitForSync; - return await client.startDirectChat(userId); - }); + context: context, + future: () async { + final client = room.client; + final waitForSync = client.onSync.stream + .firstWhere((s) => s.rooms?.leave?.containsKey(room.id) ?? false); + await room.leave(); + await waitForSync; + return await client.startDirectChat(userId); + }, + ); final roomId = success.result; if (roomId == null) return; VRouter.of(context).toSegments(['rooms', roomId]); @@ -160,7 +164,8 @@ class ChatController extends State { final room = this.room; if (room == null) { throw Exception( - 'Leave room button clicked while room is null. This should not be possible from the UI!'); + 'Leave room button clicked while room is null. This should not be possible from the UI!', + ); } final success = await showFutureLoadingDialog( context: context, @@ -327,10 +332,12 @@ class ChatController extends State { } // ignore: unawaited_futures - room!.sendTextEvent(sendController.text, - inReplyTo: replyEvent, - editEventId: editEvent?.eventId, - parseCommands: parseCommands); + room!.sendTextEvent( + sendController.text, + inReplyTo: replyEvent, + editEventId: editEvent?.eventId, + parseCommands: parseCommands, + ); sendController.value = TextEditingValue( text: pendingText, selection: const TextSelection.collapsed(offset: 0), @@ -354,10 +361,12 @@ class ChatController extends State { useRootNavigator: false, builder: (c) => SendFileDialog( files: result - .map((xfile) => MatrixFile( - bytes: xfile.toUint8List(), - name: xfile.fileName!, - ).detectFileType) + .map( + (xfile) => MatrixFile( + bytes: xfile.toUint8List(), + name: xfile.fileName!, + ).detectFileType, + ) .toList(), room: room!, ), @@ -375,10 +384,12 @@ class ChatController extends State { useRootNavigator: false, builder: (c) => SendFileDialog( files: result - .map((xfile) => MatrixFile( - bytes: xfile.toUint8List(), - name: xfile.fileName!, - ).detectFileType) + .map( + (xfile) => MatrixFile( + bytes: xfile.toUint8List(), + name: xfile.fileName!, + ).detectFileType, + ) .toList(), room: room!, ), @@ -537,8 +548,9 @@ class ChatController extends State { for (final event in selectedEvents) { if (copyString.isNotEmpty) copyString += '\n\n'; copyString += event.getDisplayEvent(timeline!).calcLocalizedBodyFallback( - MatrixLocals(L10n.of(context)!), - withSenderNamePrefix: true); + MatrixLocals(L10n.of(context)!), + withSenderNamePrefix: true, + ); } return copyString; } @@ -554,33 +566,35 @@ class ChatController extends State { void reportEventAction() async { final event = selectedEvents.single; final score = await showConfirmationDialog( - context: context, - title: L10n.of(context)!.reportMessage, - message: L10n.of(context)!.howOffensiveIsThisContent, - cancelLabel: L10n.of(context)!.cancel, - okLabel: L10n.of(context)!.ok, - actions: [ - AlertDialogAction( - key: -100, - label: L10n.of(context)!.extremeOffensive, - ), - AlertDialogAction( - key: -50, - label: L10n.of(context)!.offensive, - ), - AlertDialogAction( - key: 0, - label: L10n.of(context)!.inoffensive, - ), - ]); + context: context, + title: L10n.of(context)!.reportMessage, + message: L10n.of(context)!.howOffensiveIsThisContent, + cancelLabel: L10n.of(context)!.cancel, + okLabel: L10n.of(context)!.ok, + actions: [ + AlertDialogAction( + key: -100, + label: L10n.of(context)!.extremeOffensive, + ), + AlertDialogAction( + key: -50, + label: L10n.of(context)!.offensive, + ), + AlertDialogAction( + key: 0, + label: L10n.of(context)!.inoffensive, + ), + ], + ); if (score == null) return; final reason = await showTextInputDialog( - useRootNavigator: false, - context: context, - title: L10n.of(context)!.whyDoYouWantToReportThis, - okLabel: L10n.of(context)!.ok, - cancelLabel: L10n.of(context)!.cancel, - textFields: [DialogTextField(hintText: L10n.of(context)!.reason)]); + useRootNavigator: false, + context: context, + title: L10n.of(context)!.whyDoYouWantToReportThis, + okLabel: L10n.of(context)!.ok, + cancelLabel: L10n.of(context)!.cancel, + textFields: [DialogTextField(hintText: L10n.of(context)!.reason)], + ); if (reason == null || reason.single.isEmpty) return; final result = await showFutureLoadingDialog( context: context, @@ -597,7 +611,8 @@ class ChatController extends State { selectedEvents.clear(); }); ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context)!.contentHasBeenReported))); + SnackBar(content: Text(L10n.of(context)!.contentHasBeenReported)), + ); } void redactEventsAction() async { @@ -612,25 +627,27 @@ class ChatController extends State { if (!confirmed) return; for (final event in selectedEvents) { await showFutureLoadingDialog( - context: context, - future: () async { - if (event.status.isSent) { - if (event.canRedact) { - await event.redactEvent(); - } else { - final client = currentRoomBundle.firstWhere( - (cl) => selectedEvents.first.senderId == cl!.userID, - orElse: () => null); - if (client == null) { - return; - } - final room = client.getRoomById(roomId!)!; - await Event.fromJson(event.toJson(), room).redactEvent(); - } + context: context, + future: () async { + if (event.status.isSent) { + if (event.canRedact) { + await event.redactEvent(); } else { - await event.remove(); + final client = currentRoomBundle.firstWhere( + (cl) => selectedEvents.first.senderId == cl!.userID, + orElse: () => null, + ); + if (client == null) { + return; + } + final room = client.getRoomById(roomId!)!; + await Event.fromJson(event.toJson(), room).redactEvent(); } - }); + } else { + await event.remove(); + } + }, + ); } setState(() { showEmojiPicker = false; @@ -706,41 +723,42 @@ class ChatController extends State { // event id not found...maybe we can fetch it? // the try...finally is here to start and close the loading dialog reliably await showFutureLoadingDialog( - context: context, - future: () async { - // okay, we first have to fetch if the event is in the room + context: context, + future: () async { + // okay, we first have to fetch if the event is in the room + try { + final event = await timeline!.getEventById(eventId); + if (event == null) { + // event is null...meaning something is off + return; + } + } catch (err) { + if (err is MatrixException && err.errcode == 'M_NOT_FOUND') { + // event wasn't found, as the server gave a 404 or something + return; + } + rethrow; + } + // okay, we know that the event *is* in the room + while (eventIndex == -1) { + if (!canLoadMore) { + // we can't load any more events but still haven't found ours yet...better stop here + return; + } try { - final event = await timeline!.getEventById(eventId); - if (event == null) { - // event is null...meaning something is off - return; - } + await timeline!.requestHistory(historyCount: _loadHistoryCount); } catch (err) { - if (err is MatrixException && err.errcode == 'M_NOT_FOUND') { - // event wasn't found, as the server gave a 404 or something + if (err is TimeoutException) { + // loading the history timed out...so let's do nothing return; } rethrow; } - // okay, we know that the event *is* in the room - while (eventIndex == -1) { - if (!canLoadMore) { - // we can't load any more events but still haven't found ours yet...better stop here - return; - } - try { - await timeline!.requestHistory(historyCount: _loadHistoryCount); - } catch (err) { - if (err is TimeoutException) { - // loading the history timed out...so let's do nothing - return; - } - rethrow; - } - eventIndex = - timeline!.events.indexWhere((e) => e.eventId == eventId); - } - }); + eventIndex = + timeline!.events.indexWhere((e) => e.eventId == eventId); + } + }, + ); } if (!mounted) { return; @@ -811,7 +829,8 @@ class ChatController extends State { sendController ..text = sendController.text.characters.skipLast(1).toString() ..selection = TextSelection.fromPosition( - TextPosition(offset: sendController.text.length)); + TextPosition(offset: sendController.text.length), + ); break; } } @@ -846,8 +865,9 @@ class ChatController extends State { void editSelectedEventAction() { final client = currentRoomBundle.firstWhere( - (cl) => selectedEvents.first.senderId == cl!.userID, - orElse: () => null); + (cl) => selectedEvents.first.senderId == cl!.userID, + orElse: () => null, + ); if (client == null) { return; } @@ -855,10 +875,12 @@ class ChatController extends State { setState(() { pendingText = sendController.text; editEvent = selectedEvents.first; - inputText = sendController.text = editEvent! - .getDisplayEvent(timeline!) - .calcLocalizedBodyFallback(MatrixLocals(L10n.of(context)!), - withSenderNamePrefix: false, hideReply: true); + inputText = sendController.text = + editEvent!.getDisplayEvent(timeline!).calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)!), + withSenderNamePrefix: false, + hideReply: true, + ); selectedEvents.clear(); }); inputFocus.requestFocus(); @@ -881,10 +903,12 @@ class ChatController extends State { } final result = await showFutureLoadingDialog( context: context, - future: () => room!.client.joinRoom(room! - .getState(EventTypes.RoomTombstone)! - .parsedTombstoneContent - .replacementRoom), + future: () => room!.client.joinRoom( + room! + .getState(EventTypes.RoomTombstone)! + .parsedTombstoneContent + .replacementRoom, + ), ); await showFutureLoadingDialog( context: context, @@ -1077,9 +1101,10 @@ class ChatController extends State { if (callType == null) return; final success = await showFutureLoadingDialog( - context: context, - future: () => - Matrix.of(context).voipPlugin!.voip.requestTurnServerCredentials()); + context: context, + future: () => + Matrix.of(context).voipPlugin!.voip.requestTurnServerCredentials(), + ); if (success.result != null) { final voipPlugin = Matrix.of(context).voipPlugin; try { diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index b3cbc57c..199ca16d 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -86,19 +86,20 @@ class ChatEventList extends StatelessWidget { index: i - 1, controller: controller.scrollController, child: event.isVisibleInGui - ? Message(event, + ? Message( + event, onSwipe: (direction) => controller.replyAction(replyTo: event), onInfoTab: controller.showEventInfo, onAvatarTab: (Event event) => showAdaptiveBottomSheet( - context: context, - builder: (c) => UserBottomSheet( - user: event.senderFromMemoryOrFallback, - outerContext: context, - onMention: () => controller.sendController.text += - '${event.senderFromMemoryOrFallback.mention} ', - ), - ), + context: context, + builder: (c) => UserBottomSheet( + user: event.senderFromMemoryOrFallback, + outerContext: context, + onMention: () => controller.sendController.text += + '${event.senderFromMemoryOrFallback.mention} ', + ), + ), onSelect: controller.onSelectMessage, scrollToEventId: (String eventId) => controller.scrollToEventId(eventId), @@ -108,7 +109,8 @@ class ChatEventList extends StatelessWidget { timeline: controller.timeline!, nextEvent: i < controller.timeline!.events.length ? controller.timeline!.events[i] - : null) + : null, + ) : Container(), ); }, diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index d509e8a9..ec3aed36 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -300,23 +300,24 @@ class _ChatAccountPicker extends StatelessWidget { builder: (context, snapshot) => PopupMenuButton( onSelected: _popupMenuButtonSelected, itemBuilder: (BuildContext context) => clients - .map((client) => PopupMenuItem( - value: client!.userID, - child: FutureBuilder( - future: client.fetchOwnProfile(), - builder: (context, snapshot) => ListTile( - leading: Avatar( - mxContent: snapshot.data?.avatarUrl, - name: snapshot.data?.displayName ?? - client.userID!.localpart, - size: 20, - ), - title: - Text(snapshot.data?.displayName ?? client.userID!), - contentPadding: const EdgeInsets.all(0), + .map( + (client) => PopupMenuItem( + value: client!.userID, + child: FutureBuilder( + future: client.fetchOwnProfile(), + builder: (context, snapshot) => ListTile( + leading: Avatar( + mxContent: snapshot.data?.avatarUrl, + name: snapshot.data?.displayName ?? + client.userID!.localpart, + size: 20, ), + title: Text(snapshot.data?.displayName ?? client.userID!), + contentPadding: const EdgeInsets.all(0), ), - )) + ), + ), + ) .toList(), child: Avatar( mxContent: snapshot.data?.avatarUrl, diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 032cfd16..0d79cb30 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -49,11 +49,12 @@ class ChatView extends StatelessWidget { if (controller.canSaveSelectedEvent) // Use builder context to correctly position the share dialog on iPad Builder( - builder: (context) => IconButton( - icon: Icon(Icons.adaptive.share), - tooltip: L10n.of(context)!.share, - onPressed: () => controller.saveSelectedEvent(context), - )), + builder: (context) => IconButton( + icon: Icon(Icons.adaptive.share), + tooltip: L10n.of(context)!.share, + onPressed: () => controller.saveSelectedEvent(context), + ), + ), if (controller.canRedactSelectedEvents) IconButton( icon: const Icon(Icons.delete_outlined), @@ -155,7 +156,9 @@ class ChatView extends StatelessWidget { if (controller.room!.membership == Membership.invite) { showFutureLoadingDialog( - context: context, future: () => controller.room!.join()); + context: context, + future: () => controller.room!.join(), + ); } final bottomSheetPadding = FluffyThemes.isColumnMode(context) ? 16.0 : 8.0; final colorScheme = Theme.of(context).colorScheme; @@ -249,21 +252,23 @@ class ChatView extends StatelessWidget { PinnedEvents(controller), Expanded( child: GestureDetector( - onTap: controller.clearSingleSelectedEvent, - child: Builder( - builder: (context) { - if (controller.timeline == null) { - return const Center( - child: CircularProgressIndicator - .adaptive(strokeWidth: 2), - ); - } - - return ChatEventList( - controller: controller, + onTap: controller.clearSingleSelectedEvent, + child: Builder( + builder: (context) { + if (controller.timeline == null) { + return const Center( + child: + CircularProgressIndicator.adaptive( + strokeWidth: 2,), ); - }, - )), + } + + return ChatEventList( + controller: controller, + ); + }, + ), + ), ), if (controller.room!.canSendDefaultMessages && controller.room!.membership == Membership.join) @@ -274,7 +279,8 @@ class ChatView extends StatelessWidget { right: bottomSheetPadding, ), constraints: const BoxConstraints( - maxWidth: FluffyThemes.columnWidth * 2.5), + maxWidth: FluffyThemes.columnWidth * 2.5, + ), alignment: Alignment.center, child: Material( borderRadius: const BorderRadius.only( @@ -324,7 +330,8 @@ class ChatView extends StatelessWidget { onPressed: controller.recreateChat, label: Text( - L10n.of(context)!.reopenChat), + L10n.of(context)!.reopenChat, + ), ), ], ) diff --git a/lib/pages/chat/edit_widgets_dialog.dart b/lib/pages/chat/edit_widgets_dialog.dart index 8d351913..7d9579a1 100644 --- a/lib/pages/chat/edit_widgets_dialog.dart +++ b/lib/pages/chat/edit_widgets_dialog.dart @@ -15,15 +15,18 @@ class EditWidgetsDialog extends StatelessWidget { return SimpleDialog( title: Text(L10n.of(context)!.editWidgets), children: [ - ...room.widgets.map((e) => ListTile( - title: Text(e.name ?? e.type), - leading: IconButton( - onPressed: () { - room.deleteWidget(e.id!); - Navigator.of(context).pop(); - }, - icon: const Icon(Icons.delete)), - )), + ...room.widgets.map( + (e) => ListTile( + title: Text(e.name ?? e.type), + leading: IconButton( + onPressed: () { + room.deleteWidget(e.id!); + Navigator.of(context).pop(); + }, + icon: const Icon(Icons.delete), + ), + ), + ), AddWidgetTile(room: room), ], ); diff --git a/lib/pages/chat/encryption_button.dart b/lib/pages/chat/encryption_button.dart index a7ad5742..d05091f0 100644 --- a/lib/pages/chat/encryption_button.dart +++ b/lib/pages/chat/encryption_button.dart @@ -13,34 +13,34 @@ class EncryptionButton extends StatelessWidget { @override Widget build(BuildContext context) { return StreamBuilder( - stream: Matrix.of(context) - .client - .onSync - .stream - .where((s) => s.deviceLists != null), - builder: (context, snapshot) { - return FutureBuilder( - future: room.calcEncryptionHealthState(), - builder: (BuildContext context, snapshot) => IconButton( - tooltip: room.encrypted - ? L10n.of(context)!.encrypted - : L10n.of(context)!.encryptionNotEnabled, - icon: Icon( - room.encrypted - ? Icons.lock_outlined - : Icons.lock_open_outlined, - size: 20, - color: room.joinRules != JoinRules.public && - !room.encrypted - ? Colors.red - : room.joinRules != JoinRules.public && - snapshot.data == - EncryptionHealthState.unverifiedDevices - ? Colors.orange - : null), - onPressed: () => VRouter.of(context) - .toSegments(['rooms', room.id, 'encryption']), - )); - }); + stream: Matrix.of(context) + .client + .onSync + .stream + .where((s) => s.deviceLists != null), + builder: (context, snapshot) { + return FutureBuilder( + future: room.calcEncryptionHealthState(), + builder: (BuildContext context, snapshot) => IconButton( + tooltip: room.encrypted + ? L10n.of(context)!.encrypted + : L10n.of(context)!.encryptionNotEnabled, + icon: Icon( + room.encrypted ? Icons.lock_outlined : Icons.lock_open_outlined, + size: 20, + color: room.joinRules != JoinRules.public && !room.encrypted + ? Colors.red + : room.joinRules != JoinRules.public && + snapshot.data == + EncryptionHealthState.unverifiedDevices + ? Colors.orange + : null, + ), + onPressed: () => VRouter.of(context) + .toSegments(['rooms', room.id, 'encryption']), + ), + ); + }, + ); } } diff --git a/lib/pages/chat/event_info_dialog.dart b/lib/pages/chat/event_info_dialog.dart index 9dd92b17..5ac29c80 100644 --- a/lib/pages/chat/event_info_dialog.dart +++ b/lib/pages/chat/event_info_dialog.dart @@ -54,7 +54,8 @@ class EventInfoDialog extends StatelessWidget { ), title: Text(L10n.of(context)!.sender), subtitle: Text( - '${event.senderFromMemoryOrFallback.calcDisplayname()} [${event.senderId}]'), + '${event.senderFromMemoryOrFallback.calcDisplayname()} [${event.senderId}]', + ), ), ListTile( title: Text(L10n.of(context)!.time), diff --git a/lib/pages/chat/events/audio_player.dart b/lib/pages/chat/events/audio_player.dart index ee2e3e08..68934022 100644 --- a/lib/pages/chat/events/audio_player.dart +++ b/lib/pages/chat/events/audio_player.dart @@ -69,7 +69,8 @@ class AudioPlayerState extends State { if (!kIsWeb) { final tempDir = await getTemporaryDirectory(); final fileName = Uri.encodeComponent( - widget.event.attachmentOrThumbnailMxcUrl()!.pathSegments.last); + widget.event.attachmentOrThumbnailMxcUrl()!.pathSegments.last, + ); file = File('${tempDir.path}/${fileName}_${matrixFile.name}'); await file.writeAsBytes(matrixFile.bytes); } @@ -224,23 +225,27 @@ class AudioPlayerState extends State { for (var i = 0; i < AudioPlayerWidget.wavesCount; i++) Expanded( child: InkWell( - onTap: () => audioPlayer?.seek(Duration( + onTap: () => audioPlayer?.seek( + Duration( milliseconds: (maxPosition / AudioPlayerWidget.wavesCount) .round() * - i)), + i, + ), + ), child: Container( height: 32, alignment: Alignment.center, child: Opacity( opacity: currentPosition > i ? 1 : 0.5, child: Container( - margin: const EdgeInsets.symmetric(horizontal: 1), - decoration: BoxDecoration( - color: widget.color, - borderRadius: BorderRadius.circular(64), - ), - height: 32 * (waveform[i] / 1024)), + margin: const EdgeInsets.symmetric(horizontal: 1), + decoration: BoxDecoration( + color: widget.color, + borderRadius: BorderRadius.circular(64), + ), + height: 32 * (waveform[i] / 1024), + ), ), ), ), diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index 29344e49..fe9b274a 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -38,9 +38,14 @@ class HtmlMessage extends StatelessWidget { // miss-matching tags, and this way we actually correctly identify what we want to strip and, well, // strip it. final renderHtml = html.replaceAll( - RegExp('.*', - caseSensitive: false, multiLine: false, dotAll: true), - ''); + RegExp( + '.*', + caseSensitive: false, + multiLine: false, + dotAll: true, + ), + '', + ); // there is no need to pre-validate the html, as we validate it while rendering @@ -61,8 +66,12 @@ class HtmlMessage extends StatelessWidget { maxLines: maxLines, onLinkTap: (url) => UrlLauncher(context, url).launchUrl(), onPillTap: (url) => UrlLauncher(context, url).launchUrl(), - getMxcUrl: (String mxc, double? width, double? height, - {bool? animated = false}) { + getMxcUrl: ( + String mxc, + double? width, + double? height, { + bool? animated = false, + }) { final ratio = MediaQuery.of(context).devicePixelRatio; return Uri.parse(mxc) .getThumbnail( @@ -75,19 +84,23 @@ class HtmlMessage extends StatelessWidget { .toString(); }, onImageTap: (String mxc) => showDialog( - context: Matrix.of(context).navigatorContext, - useRootNavigator: false, - builder: (_) => ImageViewer(Event( - type: EventTypes.Message, - content: { - 'body': mxc, - 'url': mxc, - 'msgtype': MessageTypes.Image, - }, - senderId: room.client.userID!, - originServerTs: DateTime.now(), - eventId: 'fake_event', - room: room))), + context: Matrix.of(context).navigatorContext, + useRootNavigator: false, + builder: (_) => ImageViewer( + Event( + type: EventTypes.Message, + content: { + 'body': mxc, + 'url': mxc, + 'msgtype': MessageTypes.Image, + }, + senderId: room.client.userID!, + originServerTs: DateTime.now(), + eventId: 'fake_event', + room: room, + ), + ), + ), setCodeLanguage: (String key, String value) async { await matrix.store.setItem('${SettingKeys.codeLanguage}.$key', value); }, diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 4f21fd6a..685163af 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -27,18 +27,19 @@ class Message extends StatelessWidget { final bool selected; final Timeline timeline; - const Message(this.event, - {this.nextEvent, - this.longPressSelect = false, - this.onSelect, - this.onInfoTab, - this.onAvatarTab, - this.scrollToEventId, - required this.onSwipe, - this.selected = false, - required this.timeline, - Key? key}) - : super(key: key); + const Message( + this.event, { + this.nextEvent, + this.longPressSelect = false, + this.onSelect, + this.onInfoTab, + this.onAvatarTab, + this.scrollToEventId, + required this.onSwipe, + this.selected = false, + required this.timeline, + Key? key, + }) : super(key: key); /// Indicates wheither the user may use a mouse instead /// of touchscreen. @@ -126,13 +127,15 @@ class Message extends StatelessWidget { height: 16 * AppConfig.bubbleSizeFactor, child: event.status == EventStatus.sending ? const CircularProgressIndicator.adaptive( - strokeWidth: 2) + strokeWidth: 2, + ) : event.status == EventStatus.error ? const Icon(Icons.error, color: Colors.red) : null, ), ), - )) + ), + ) : FutureBuilder( future: event.fetchSenderUser(), builder: (context, snapshot) { @@ -142,7 +145,8 @@ class Message extends StatelessWidget { name: user.calcDisplayname(), onTap: () => onAvatarTab!(event), ); - }), + }, + ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -171,7 +175,8 @@ class Message extends StatelessWidget { : displayname.lightColorText), ), ); - }), + }, + ), ), Container( alignment: alignment, @@ -198,7 +203,8 @@ class Message extends StatelessWidget { ? EdgeInsets.zero : EdgeInsets.all(16 * AppConfig.bubbleSizeFactor), constraints: const BoxConstraints( - maxWidth: FluffyThemes.columnWidth * 1.5), + maxWidth: FluffyThemes.columnWidth * 1.5, + ), child: Stack( children: [ Column( @@ -233,11 +239,14 @@ class Message extends StatelessWidget { child: AbsorbPointer( child: Container( margin: EdgeInsets.symmetric( - vertical: 4.0 * - AppConfig.bubbleSizeFactor), - child: ReplyContent(replyEvent, - ownMessage: ownMessage, - timeline: timeline), + vertical: + 4.0 * AppConfig.bubbleSizeFactor, + ), + child: ReplyContent( + replyEvent, + ownMessage: ownMessage, + timeline: timeline, + ), ), ), ); @@ -249,10 +258,13 @@ class Message extends StatelessWidget { onInfoTab: onInfoTab, ), if (event.hasAggregatedEvents( - timeline, RelationshipTypes.edit)) + timeline, + RelationshipTypes.edit, + )) Padding( padding: EdgeInsets.only( - top: 4.0 * AppConfig.bubbleSizeFactor), + top: 4.0 * AppConfig.bubbleSizeFactor, + ), child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -301,26 +313,29 @@ class Message extends StatelessWidget { Padding( padding: displayTime ? EdgeInsets.symmetric( - vertical: 8.0 * AppConfig.bubbleSizeFactor) + vertical: 8.0 * AppConfig.bubbleSizeFactor, + ) : EdgeInsets.zero, child: Center( - child: Material( - color: displayTime - ? Theme.of(context).colorScheme.background - : Theme.of(context) - .colorScheme - .background - .withOpacity(0.33), - borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), - clipBehavior: Clip.antiAlias, - child: Padding( - padding: const EdgeInsets.all(6.0), - child: Text( - event.originServerTs.localizedTime(context), - style: TextStyle(fontSize: 14 * AppConfig.fontSizeFactor), + child: Material( + color: displayTime + ? Theme.of(context).colorScheme.background + : Theme.of(context) + .colorScheme + .background + .withOpacity(0.33), + borderRadius: + BorderRadius.circular(AppConfig.borderRadius / 2), + clipBehavior: Clip.antiAlias, + child: Padding( + padding: const EdgeInsets.all(6.0), + child: Text( + event.originServerTs.localizedTime(context), + style: TextStyle(fontSize: 14 * AppConfig.fontSizeFactor), + ), ), ), - )), + ), ), row, if (event.hasAggregatedEvents(timeline, RelationshipTypes.reaction)) diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 29f32bbe..de5722eb 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -27,21 +27,27 @@ class MessageContent extends StatelessWidget { final Color textColor; final void Function(Event)? onInfoTab; - const MessageContent(this.event, - {this.onInfoTab, Key? key, required this.textColor}) - : super(key: key); + const MessageContent( + this.event, { + this.onInfoTab, + Key? key, + required this.textColor, + }) : super(key: key); void _verifyOrRequestKey(BuildContext context) async { final l10n = L10n.of(context)!; if (event.content['can_request_session'] != true) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( content: Text( - event.type == EventTypes.Encrypted - ? l10n.needPantalaimonWarning - : event.calcLocalizedBodyFallback( - MatrixLocals(l10n), - ), - ))); + event.type == EventTypes.Encrypted + ? l10n.needPantalaimonWarning + : event.calcLocalizedBodyFallback( + MatrixLocals(l10n), + ), + ), + ), + ); return; } final client = Matrix.of(context).client; @@ -213,73 +219,83 @@ class MessageContent extends StatelessWidget { default: if (event.redacted) { return FutureBuilder( - future: event.redactedBecause?.fetchSenderUser(), - builder: (context, snapshot) { - return _ButtonContent( - label: L10n.of(context)!.redactedAnEvent(snapshot.data - ?.calcDisplayname() ?? - event.senderFromMemoryOrFallback.calcDisplayname()), - icon: const Icon(Icons.delete_outlined), - textColor: buttonTextColor, - onPressed: () => onInfoTab!(event), - ); - }); + future: event.redactedBecause?.fetchSenderUser(), + builder: (context, snapshot) { + return _ButtonContent( + label: L10n.of(context)!.redactedAnEvent( + snapshot.data?.calcDisplayname() ?? + event.senderFromMemoryOrFallback.calcDisplayname(), + ), + icon: const Icon(Icons.delete_outlined), + textColor: buttonTextColor, + onPressed: () => onInfoTab!(event), + ); + }, + ); } final bigEmotes = event.onlyEmotes && event.numberEmotes > 0 && event.numberEmotes <= 10; return FutureBuilder( - future: event.calcLocalizedBody(MatrixLocals(L10n.of(context)!), - hideReply: true), - builder: (context, snapshot) { - return LinkText( - text: snapshot.data ?? - event.calcLocalizedBodyFallback( - MatrixLocals(L10n.of(context)!), - hideReply: true), - textStyle: TextStyle( - color: textColor, - fontSize: bigEmotes ? fontSize * 3 : fontSize, - decoration: - event.redacted ? TextDecoration.lineThrough : null, - ), - linkStyle: TextStyle( - color: textColor.withAlpha(150), - fontSize: bigEmotes ? fontSize * 3 : fontSize, - decoration: TextDecoration.underline, - decorationColor: textColor.withAlpha(150), - ), - onLinkTap: (url) => UrlLauncher(context, url).launchUrl(), - ); - }); + future: event.calcLocalizedBody( + MatrixLocals(L10n.of(context)!), + hideReply: true, + ), + builder: (context, snapshot) { + return LinkText( + text: snapshot.data ?? + event.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)!), + hideReply: true, + ), + textStyle: TextStyle( + color: textColor, + fontSize: bigEmotes ? fontSize * 3 : fontSize, + decoration: + event.redacted ? TextDecoration.lineThrough : null, + ), + linkStyle: TextStyle( + color: textColor.withAlpha(150), + fontSize: bigEmotes ? fontSize * 3 : fontSize, + decoration: TextDecoration.underline, + decorationColor: textColor.withAlpha(150), + ), + onLinkTap: (url) => UrlLauncher(context, url).launchUrl(), + ); + }, + ); } case EventTypes.CallInvite: return FutureBuilder( - future: event.fetchSenderUser(), - builder: (context, snapshot) { - return _ButtonContent( - label: L10n.of(context)!.startedACall( - snapshot.data?.calcDisplayname() ?? - event.senderFromMemoryOrFallback.calcDisplayname()), - icon: const Icon(Icons.phone_outlined), - textColor: buttonTextColor, - onPressed: () => onInfoTab!(event), - ); - }); + future: event.fetchSenderUser(), + builder: (context, snapshot) { + return _ButtonContent( + label: L10n.of(context)!.startedACall( + snapshot.data?.calcDisplayname() ?? + event.senderFromMemoryOrFallback.calcDisplayname(), + ), + icon: const Icon(Icons.phone_outlined), + textColor: buttonTextColor, + onPressed: () => onInfoTab!(event), + ); + }, + ); default: return FutureBuilder( - future: event.fetchSenderUser(), - builder: (context, snapshot) { - return _ButtonContent( - label: L10n.of(context)!.userSentUnknownEvent( - snapshot.data?.calcDisplayname() ?? - event.senderFromMemoryOrFallback.calcDisplayname(), - event.type), - icon: const Icon(Icons.info_outlined), - textColor: buttonTextColor, - onPressed: () => onInfoTab!(event), - ); - }); + future: event.fetchSenderUser(), + builder: (context, snapshot) { + return _ButtonContent( + label: L10n.of(context)!.userSentUnknownEvent( + snapshot.data?.calcDisplayname() ?? + event.senderFromMemoryOrFallback.calcDisplayname(), + event.type, + ), + icon: const Icon(Icons.info_outlined), + textColor: buttonTextColor, + onPressed: () => onInfoTab!(event), + ); + }, + ); } } } diff --git a/lib/pages/chat/events/message_reactions.dart b/lib/pages/chat/events/message_reactions.dart index 6f8c206c..3545aa86 100644 --- a/lib/pages/chat/events/message_reactions.dart +++ b/lib/pages/chat/events/message_reactions.dart @@ -46,45 +46,51 @@ class MessageReactions extends StatelessWidget { final reactionList = reactionMap.values.toList(); reactionList.sort((a, b) => b.count - a.count > 0 ? 1 : -1); - return Wrap(spacing: 4.0, runSpacing: 4.0, children: [ - ...reactionList - .map( - (r) => _Reaction( - reactionKey: r.key, - count: r.count, - reacted: r.reacted, - onTap: () { - if (r.reacted) { - final evt = allReactionEvents.firstWhereOrNull((e) => - e.senderId == e.room.client.userID && - e.content['m.relates_to']['key'] == r.key); - if (evt != null) { - showFutureLoadingDialog( - context: context, - future: () => evt.redactEvent(), + return Wrap( + spacing: 4.0, + runSpacing: 4.0, + children: [ + ...reactionList + .map( + (r) => _Reaction( + reactionKey: r.key, + count: r.count, + reacted: r.reacted, + onTap: () { + if (r.reacted) { + final evt = allReactionEvents.firstWhereOrNull( + (e) => + e.senderId == e.room.client.userID && + e.content['m.relates_to']['key'] == r.key, ); + if (evt != null) { + showFutureLoadingDialog( + context: context, + future: () => evt.redactEvent(), + ); + } + } else { + event.room.sendReaction(event.eventId, r.key!); } - } else { - event.room.sendReaction(event.eventId, r.key!); - } - }, - onLongPress: () async => await _AdaptableReactorsDialog( - client: client, - reactionEntry: r, - ).show(context), + }, + onLongPress: () async => await _AdaptableReactorsDialog( + client: client, + reactionEntry: r, + ).show(context), + ), + ) + .toList(), + if (allReactionEvents.any((e) => e.status.isSending)) + const SizedBox( + width: 28, + height: 28, + child: Padding( + padding: EdgeInsets.all(4.0), + child: CircularProgressIndicator.adaptive(strokeWidth: 1), ), - ) - .toList(), - if (allReactionEvents.any((e) => e.status.isSending)) - const SizedBox( - width: 28, - height: 28, - child: Padding( - padding: EdgeInsets.all(4.0), - child: CircularProgressIndicator.adaptive(strokeWidth: 1), ), - ), - ]); + ], + ); } } @@ -121,11 +127,13 @@ class _Reaction extends StatelessWidget { height: fontSize, ), const SizedBox(width: 4), - Text(count.toString(), - style: TextStyle( - color: textColor, - fontSize: DefaultTextStyle.of(context).style.fontSize, - )), + Text( + count.toString(), + style: TextStyle( + color: textColor, + fontSize: DefaultTextStyle.of(context).style.fontSize, + ), + ), ], ); } else { @@ -133,11 +141,13 @@ class _Reaction extends StatelessWidget { if (renderKey.length > 10) { renderKey = renderKey.getRange(0, 9) + Characters('…'); } - content = Text('$renderKey $count', - style: TextStyle( - color: textColor, - fontSize: DefaultTextStyle.of(context).style.fontSize, - )); + content = Text( + '$renderKey $count', + style: TextStyle( + color: textColor, + fontSize: DefaultTextStyle.of(context).style.fontSize, + ), + ); } return InkWell( onTap: () => onTap != null ? onTap!() : null, diff --git a/lib/pages/chat/events/reply_content.dart b/lib/pages/chat/events/reply_content.dart index 56833f67..26c47218 100644 --- a/lib/pages/chat/events/reply_content.dart +++ b/lib/pages/chat/events/reply_content.dart @@ -84,21 +84,22 @@ class ReplyContent extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ FutureBuilder( - future: displayEvent.fetchSenderUser(), - builder: (context, snapshot) { - return Text( - '${snapshot.data?.calcDisplayname() ?? displayEvent.senderFromMemoryOrFallback.calcDisplayname()}:', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontWeight: FontWeight.bold, - color: ownMessage - ? Theme.of(context).colorScheme.onPrimary - : Theme.of(context).colorScheme.onBackground, - fontSize: fontSize, - ), - ); - }), + future: displayEvent.fetchSenderUser(), + builder: (context, snapshot) { + return Text( + '${snapshot.data?.calcDisplayname() ?? displayEvent.senderFromMemoryOrFallback.calcDisplayname()}:', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontWeight: FontWeight.bold, + color: ownMessage + ? Theme.of(context).colorScheme.onPrimary + : Theme.of(context).colorScheme.onBackground, + fontSize: fontSize, + ), + ); + }, + ), replyBody, ], ), diff --git a/lib/pages/chat/events/state_message.dart b/lib/pages/chat/events/state_message.dart index 35b34cf5..d60aa814 100644 --- a/lib/pages/chat/events/state_message.dart +++ b/lib/pages/chat/events/state_message.dart @@ -25,22 +25,23 @@ class StateMessage extends StatelessWidget { borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), ), child: FutureBuilder( - future: event.calcLocalizedBody(MatrixLocals(L10n.of(context)!)), - builder: (context, snapshot) { - return Text( - snapshot.data ?? - event.calcLocalizedBodyFallback( - MatrixLocals(L10n.of(context)!), - ), - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14 * AppConfig.fontSizeFactor, - color: Theme.of(context).colorScheme.onSecondaryContainer, - decoration: - event.redacted ? TextDecoration.lineThrough : null, - ), - ); - }), + future: event.calcLocalizedBody(MatrixLocals(L10n.of(context)!)), + builder: (context, snapshot) { + return Text( + snapshot.data ?? + event.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)!), + ), + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14 * AppConfig.fontSizeFactor, + color: Theme.of(context).colorScheme.onSecondaryContainer, + decoration: + event.redacted ? TextDecoration.lineThrough : null, + ), + ); + }, + ), ), ), ); diff --git a/lib/pages/chat/events/verification_request_content.dart b/lib/pages/chat/events/verification_request_content.dart index dc15b532..257b9f31 100644 --- a/lib/pages/chat/events/verification_request_content.dart +++ b/lib/pages/chat/events/verification_request_content.dart @@ -9,9 +9,11 @@ class VerificationRequestContent extends StatelessWidget { final Event event; final Timeline timeline; - const VerificationRequestContent( - {required this.event, required this.timeline, Key? key}) - : super(key: key); + const VerificationRequestContent({ + required this.event, + required this.timeline, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -43,18 +45,22 @@ class VerificationRequestContent extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Icon(Icons.lock_outlined, - color: canceled - ? Colors.red - : (fullyDone ? Colors.green : Colors.grey)), + Icon( + Icons.lock_outlined, + color: canceled + ? Colors.red + : (fullyDone ? Colors.green : Colors.grey), + ), const SizedBox(width: 8), - Text(canceled - ? 'Error ${cancel.first.content.tryGet('code')}: ${cancel.first.content.tryGet('reason')}' - : (fullyDone - ? L10n.of(context)!.verifySuccess - : (started - ? L10n.of(context)!.loadingPleaseWait - : L10n.of(context)!.newVerificationRequest))) + Text( + canceled + ? 'Error ${cancel.first.content.tryGet('code')}: ${cancel.first.content.tryGet('reason')}' + : (fullyDone + ? L10n.of(context)!.verifySuccess + : (started + ? L10n.of(context)!.loadingPleaseWait + : L10n.of(context)!.newVerificationRequest)), + ) ], ), ), diff --git a/lib/pages/chat/events/video_player.dart b/lib/pages/chat/events/video_player.dart index ad6fb075..8d31e5a7 100644 --- a/lib/pages/chat/events/video_player.dart +++ b/lib/pages/chat/events/video_player.dart @@ -39,7 +39,8 @@ class EventVideoPlayerState extends State { } else { final tempDir = await getTemporaryDirectory(); final fileName = Uri.encodeComponent( - widget.event.attachmentOrThumbnailMxcUrl()!.pathSegments.last); + widget.event.attachmentOrThumbnailMxcUrl()!.pathSegments.last, + ); final file = File('${tempDir.path}/${fileName}_${videoFile.name}'); if (await file.exists() == false) { await file.writeAsBytes(videoFile.bytes); @@ -62,13 +63,17 @@ class EventVideoPlayerState extends State { ); } } on MatrixConnectionException catch (e) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(e.toLocalizedString(context)), - )); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(e.toLocalizedString(context)), + ), + ); } catch (e, s) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(e.toLocalizedString(context)), - )); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(e.toLocalizedString(context)), + ), + ); Logs().w('Error while playing video', e, s); } finally { // Workaround for Chewie needs time to get the aspectRatio @@ -120,14 +125,16 @@ class EventVideoPlayerState extends State { width: 24, height: 24, child: CircularProgressIndicator.adaptive( - strokeWidth: 2), + strokeWidth: 2, + ), ) : const Icon(Icons.download_outlined), label: Text( _isDownloading ? L10n.of(context)!.loadingPleaseWait : L10n.of(context)!.videoWithSize( - widget.event.sizeString ?? '?MB'), + widget.event.sizeString ?? '?MB', + ), ), onPressed: _isDownloading ? null : _downloadAction, ), diff --git a/lib/pages/chat/input_bar.dart b/lib/pages/chat/input_bar.dart index 0f5b181c..bba5fe00 100644 --- a/lib/pages/chat/input_bar.dart +++ b/lib/pages/chat/input_bar.dart @@ -117,8 +117,10 @@ class InputBar extends StatelessWidget { } // aside of emote packs, also propose normal (tm) unicode emojis final matchingUnicodeEmojis = Emoji.all() - .where((element) => [element.name, ...element.keywords] - .any((element) => element.toLowerCase().contains(emoteSearch))) + .where( + (element) => [element.name, ...element.keywords] + .any((element) => element.toLowerCase().contains(emoteSearch)), + ) .toList(); // sort by the index of the search term in the name in order to have // best matches first @@ -186,12 +188,14 @@ class InputBar extends StatelessWidget { .toLowerCase() .contains(roomSearch)) || (state.content['alt_aliases'] is List && - state.content['alt_aliases'].any((l) => - l is String && - l - .split(':')[0] - .toLowerCase() - .contains(roomSearch))))) || + state.content['alt_aliases'].any( + (l) => + l is String && + l + .split(':')[0] + .toLowerCase() + .contains(roomSearch), + )))) || (r.name.toLowerCase().contains(roomSearch))) { ret.add({ 'type': 'room', @@ -226,8 +230,10 @@ class InputBar extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('/$command', - style: const TextStyle(fontFamily: 'monospace')), + Text( + '/$command', + style: const TextStyle(fontFamily: 'monospace'), + ), Text( hint, maxLines: 1, @@ -273,8 +279,8 @@ class InputBar extends StatelessWidget { child: suggestion['pack_avatar_url'] != null ? Avatar( mxContent: Uri.tryParse( - suggestion.tryGet('pack_avatar_url') ?? - ''), + suggestion.tryGet('pack_avatar_url') ?? '', + ), name: suggestion.tryGet('pack_display_name'), size: size * 0.9, client: client, @@ -397,23 +403,27 @@ class InputBar extends StatelessWidget { actions: !useShortCuts ? {} : { - NewLineIntent: CallbackAction(onInvoke: (i) { - final val = controller!.value; - final selection = val.selection.start; - final messageWithoutNewLine = - '${controller!.text.substring(0, val.selection.start)}\n${controller!.text.substring(val.selection.end)}'; - controller!.value = TextEditingValue( - text: messageWithoutNewLine, - selection: TextSelection.fromPosition( - TextPosition(offset: selection + 1), - ), - ); - return null; - }), - SubmitLineIntent: CallbackAction(onInvoke: (i) { - onSubmitted!(controller!.text); - return null; - }), + NewLineIntent: CallbackAction( + onInvoke: (i) { + final val = controller!.value; + final selection = val.selection.start; + final messageWithoutNewLine = + '${controller!.text.substring(0, val.selection.start)}\n${controller!.text.substring(val.selection.end)}'; + controller!.value = TextEditingValue( + text: messageWithoutNewLine, + selection: TextSelection.fromPosition( + TextPosition(offset: selection + 1), + ), + ); + return null; + }, + ), + SubmitLineIntent: CallbackAction( + onInvoke: (i) { + onSubmitted!(controller!.text); + return null; + }, + ), }, child: TypeAheadField>( direction: AxisDirection.up, diff --git a/lib/pages/chat/pinned_events.dart b/lib/pages/chat/pinned_events.dart index e6e0ec78..ba780876 100644 --- a/lib/pages/chat/pinned_events.dart +++ b/lib/pages/chat/pinned_events.dart @@ -18,23 +18,28 @@ class PinnedEvents extends StatelessWidget { const PinnedEvents(this.controller, {Key? key}) : super(key: key); Future _displayPinnedEventsDialog( - BuildContext context, List events) async { + BuildContext context, + List events, + ) async { final eventId = events.length == 1 ? events.single?.eventId : await showConfirmationDialog( context: context, title: L10n.of(context)!.pinMessage, actions: events - .map((event) => AlertDialogAction( - key: event?.eventId ?? '', - label: event?.calcLocalizedBodyFallback( - MatrixLocals(L10n.of(context)!), - withSenderNamePrefix: true, - hideReply: true, - ) ?? - 'UNKNOWN', - )) - .toList()); + .map( + (event) => AlertDialogAction( + key: event?.eventId ?? '', + label: event?.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)!), + withSenderNamePrefix: true, + hideReply: true, + ) ?? + 'UNKNOWN', + ), + ) + .toList(), + ); if (eventId != null) controller.scrollToEventId(eventId); } @@ -54,87 +59,86 @@ class PinnedEvents extends StatelessWidget { return completer; }); return FutureBuilder>( - future: Future.wait(completers.map((e) => e.future).toList()), - builder: (context, snapshot) { - final pinnedEvents = snapshot.data; - final event = (pinnedEvents != null && pinnedEvents.isNotEmpty) - ? snapshot.data?.last - : null; + future: Future.wait(completers.map((e) => e.future).toList()), + builder: (context, snapshot) { + final pinnedEvents = snapshot.data; + final event = (pinnedEvents != null && pinnedEvents.isNotEmpty) + ? snapshot.data?.last + : null; - if (event == null || pinnedEvents == null) { - return Container(); - } + if (event == null || pinnedEvents == null) { + return Container(); + } - final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor; - return Material( - color: Theme.of(context).colorScheme.surfaceVariant, - child: InkWell( - onTap: () => _displayPinnedEventsDialog( - context, - pinnedEvents, - ), - child: Row( - children: [ - IconButton( - splashRadius: 20, - iconSize: 20, - color: Theme.of(context).colorScheme.onSurfaceVariant, - icon: const Icon(Icons.push_pin), - tooltip: L10n.of(context)!.unpin, - onPressed: controller.room - ?.canSendEvent(EventTypes.RoomPinnedEvents) ?? - false - ? () => controller.unpinEvent(event.eventId) - : null, - ), - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 4.0), - child: FutureBuilder( - future: event.calcLocalizedBody( - MatrixLocals(L10n.of(context)!), - withSenderNamePrefix: true, - hideReply: true, + final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor; + return Material( + color: Theme.of(context).colorScheme.surfaceVariant, + child: InkWell( + onTap: () => _displayPinnedEventsDialog( + context, + pinnedEvents, + ), + child: Row( + children: [ + IconButton( + splashRadius: 20, + iconSize: 20, + color: Theme.of(context).colorScheme.onSurfaceVariant, + icon: const Icon(Icons.push_pin), + tooltip: L10n.of(context)!.unpin, + onPressed: controller.room + ?.canSendEvent(EventTypes.RoomPinnedEvents) ?? + false + ? () => controller.unpinEvent(event.eventId) + : null, + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: FutureBuilder( + future: event.calcLocalizedBody( + MatrixLocals(L10n.of(context)!), + withSenderNamePrefix: true, + hideReply: true, + ), + builder: (context, snapshot) { + return LinkText( + text: snapshot.data ?? + event.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)!), + withSenderNamePrefix: true, + hideReply: true, + ), + maxLines: 2, + textStyle: TextStyle( + color: + Theme.of(context).colorScheme.onSurfaceVariant, + overflow: TextOverflow.ellipsis, + fontSize: fontSize, + decoration: event.redacted + ? TextDecoration.lineThrough + : null, ), - builder: (context, snapshot) { - return LinkText( - text: snapshot.data ?? - event.calcLocalizedBodyFallback( - MatrixLocals(L10n.of(context)!), - withSenderNamePrefix: true, - hideReply: true, - ), - maxLines: 2, - textStyle: TextStyle( - color: Theme.of(context) - .colorScheme - .onSurfaceVariant, - overflow: TextOverflow.ellipsis, - fontSize: fontSize, - decoration: event.redacted - ? TextDecoration.lineThrough - : null, - ), - linkStyle: TextStyle( - color: Theme.of(context) - .colorScheme - .onSurfaceVariant, - fontSize: fontSize, - decoration: TextDecoration.underline, - decorationColor: Theme.of(context) - .colorScheme - .onSurfaceVariant, - ), - onLinkTap: (url) => - UrlLauncher(context, url).launchUrl(), - ); - }), + linkStyle: TextStyle( + color: + Theme.of(context).colorScheme.onSurfaceVariant, + fontSize: fontSize, + decoration: TextDecoration.underline, + decorationColor: + Theme.of(context).colorScheme.onSurfaceVariant, + ), + onLinkTap: (url) => + UrlLauncher(context, url).launchUrl(), + ); + }, ), ), - ], - ), + ), + ], ), - ); - }); + ), + ); + }, + ); } } diff --git a/lib/pages/chat/reactions_picker.dart b/lib/pages/chat/reactions_picker.dart index 53f8cfe7..37678ce3 100644 --- a/lib/pages/chat/reactions_picker.dart +++ b/lib/pages/chat/reactions_picker.dart @@ -26,37 +26,45 @@ class ReactionsPicker extends StatelessWidget { height: (display) ? 56 : 0, child: Material( color: Colors.transparent, - child: Builder(builder: (context) { - if (!display) { - return Container(); - } - final proposals = proposeEmojis( + child: Builder( + builder: (context) { + if (!display) { + return Container(); + } + final proposals = proposeEmojis( controller.selectedEvents.first.plaintextBody, number: 25, - languageCodes: EmojiProposalLanguageCodes.values.toSet()); - final emojis = proposals.isNotEmpty - ? proposals.map((e) => e.char).toList() - : List.from(AppEmojis.emojis); - final allReactionEvents = controller.selectedEvents.first - .aggregatedEvents( - controller.timeline!, RelationshipTypes.reaction) - .where((event) => - event.senderId == event.room.client.userID && - event.type == 'm.reaction'); + languageCodes: EmojiProposalLanguageCodes.values.toSet(), + ); + final emojis = proposals.isNotEmpty + ? proposals.map((e) => e.char).toList() + : List.from(AppEmojis.emojis); + final allReactionEvents = controller.selectedEvents.first + .aggregatedEvents( + controller.timeline!, + RelationshipTypes.reaction, + ) + .where( + (event) => + event.senderId == event.room.client.userID && + event.type == 'm.reaction', + ); - for (final event in allReactionEvents) { - try { - emojis.remove(event.content['m.relates_to']['key']); - } catch (_) {} - } - return Row(children: [ - Expanded( - child: Container( + for (final event in allReactionEvents) { + try { + emojis.remove(event.content['m.relates_to']['key']); + } catch (_) {} + } + return Row( + children: [ + Expanded( + child: Container( decoration: BoxDecoration( - color: Theme.of(context).secondaryHeaderColor, - borderRadius: const BorderRadius.only( - bottomRight: - Radius.circular(AppConfig.borderRadius))), + color: Theme.of(context).secondaryHeaderColor, + borderRadius: const BorderRadius.only( + bottomRight: Radius.circular(AppConfig.borderRadius), + ), + ), padding: const EdgeInsets.only(right: 1), child: ListView.builder( scrollDirection: Axis.horizontal, @@ -74,23 +82,28 @@ class ReactionsPicker extends StatelessWidget { ), ), ), - ))), - InkWell( - borderRadius: BorderRadius.circular(8), - child: Container( - margin: const EdgeInsets.symmetric(horizontal: 8), - width: 36, - height: 56, - decoration: BoxDecoration( - color: Theme.of(context).secondaryHeaderColor, - shape: BoxShape.circle, + ), ), - child: const Icon(Icons.add_outlined), ), - onTap: () => - controller.pickEmojiReactionAction(allReactionEvents)) - ]); - }), + InkWell( + borderRadius: BorderRadius.circular(8), + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 8), + width: 36, + height: 56, + decoration: BoxDecoration( + color: Theme.of(context).secondaryHeaderColor, + shape: BoxShape.circle, + ), + child: const Icon(Icons.add_outlined), + ), + onTap: () => + controller.pickEmojiReactionAction(allReactionEvents), + ) + ], + ); + }, + ), ), ); } diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart index d556535c..317ab791 100644 --- a/lib/pages/chat/recording_dialog.dart +++ b/lib/pages/chat/recording_dialog.dart @@ -130,7 +130,8 @@ class RecordingDialogState extends State { .take(26) .toList() .reversed - .map((amplitude) => Container( + .map( + (amplitude) => Container( margin: const EdgeInsets.only(left: 2), width: 4, decoration: BoxDecoration( @@ -138,7 +139,9 @@ class RecordingDialogState extends State { borderRadius: BorderRadius.circular(AppConfig.borderRadius), ), - height: maxDecibalWidth * (amplitude / 100))) + height: maxDecibalWidth * (amplitude / 100), + ), + ) .toList(), ), ), diff --git a/lib/pages/chat/reply_display.dart b/lib/pages/chat/reply_display.dart index 0e4caec6..2f78d467 100644 --- a/lib/pages/chat/reply_display.dart +++ b/lib/pages/chat/reply_display.dart @@ -33,10 +33,14 @@ class ReplyDisplay extends StatelessWidget { ), Expanded( child: controller.replyEvent != null - ? ReplyContent(controller.replyEvent!, - timeline: controller.timeline!) - : _EditContent(controller.editEvent - ?.getDisplayEvent(controller.timeline!)), + ? ReplyContent( + controller.replyEvent!, + timeline: controller.timeline!, + ) + : _EditContent( + controller.editEvent + ?.getDisplayEvent(controller.timeline!), + ), ), ], ), @@ -64,26 +68,27 @@ class _EditContent extends StatelessWidget { ), Container(width: 15.0), FutureBuilder( - future: event.calcLocalizedBody( - MatrixLocals(L10n.of(context)!), - withSenderNamePrefix: false, - hideReply: true, - ), - builder: (context, snapshot) { - return Text( - snapshot.data ?? - event.calcLocalizedBodyFallback( - MatrixLocals(L10n.of(context)!), - withSenderNamePrefix: false, - hideReply: true, - ), - overflow: TextOverflow.ellipsis, - maxLines: 1, - style: TextStyle( - color: Theme.of(context).textTheme.bodyMedium!.color, - ), - ); - }), + future: event.calcLocalizedBody( + MatrixLocals(L10n.of(context)!), + withSenderNamePrefix: false, + hideReply: true, + ), + builder: (context, snapshot) { + return Text( + snapshot.data ?? + event.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)!), + withSenderNamePrefix: false, + hideReply: true, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: TextStyle( + color: Theme.of(context).textTheme.bodyMedium!.color, + ), + ); + }, + ), ], ); } diff --git a/lib/pages/chat/send_file_dialog.dart b/lib/pages/chat/send_file_dialog.dart index e7f2ace8..dff80548 100644 --- a/lib/pages/chat/send_file_dialog.dart +++ b/lib/pages/chat/send_file_dialog.dart @@ -33,11 +33,12 @@ class SendFileDialogState extends State { MatrixImageFile? thumbnail; if (file is MatrixVideoFile && file.bytes.length > minSizeToCompress) { await showFutureLoadingDialog( - context: context, - future: () async { - file = await file.resizeVideo(); - thumbnail = await file.getVideoThumbnail(); - }); + context: context, + future: () async { + file = await file.resizeVideo(); + thumbnail = await file.getVideoThumbnail(); + }, + ); } final scaffoldMessenger = ScaffoldMessenger.of(context); widget.room @@ -79,26 +80,29 @@ class SendFileDialogState extends State { } Widget contentWidget; if (allFilesAreImages) { - contentWidget = Column(mainAxisSize: MainAxisSize.min, children: [ - Flexible( - child: Image.memory( - widget.files.first.bytes, - fit: BoxFit.contain, + contentWidget = Column( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Image.memory( + widget.files.first.bytes, + fit: BoxFit.contain, + ), ), - ), - Row( - children: [ - Checkbox( - value: origImage, - onChanged: (v) => setState(() => origImage = v ?? false), - ), - InkWell( - onTap: () => setState(() => origImage = !origImage), - child: Text('${L10n.of(context)!.sendOriginal} ($sizeString)'), - ), - ], - ) - ]); + Row( + children: [ + Checkbox( + value: origImage, + onChanged: (v) => setState(() => origImage = v ?? false), + ), + InkWell( + onTap: () => setState(() => origImage = !origImage), + child: Text('${L10n.of(context)!.sendOriginal} ($sizeString)'), + ), + ], + ) + ], + ); } else { contentWidget = Text('$fileName ($sizeString)'); } diff --git a/lib/pages/chat/sticker_picker_dialog.dart b/lib/pages/chat/sticker_picker_dialog.dart index e8ed706f..33b66a09 100644 --- a/lib/pages/chat/sticker_picker_dialog.dart +++ b/lib/pages/chat/sticker_picker_dialog.dart @@ -28,12 +28,13 @@ class StickerPickerDialogState extends State { final pack = stickerPacks[packSlugs[packIndex]]!; final filteredImagePackImageEntried = pack.images.entries.toList(); if (searchFilter?.isNotEmpty ?? false) { - filteredImagePackImageEntried.removeWhere((e) => - !(e.key.toLowerCase().contains(searchFilter!.toLowerCase()) || - (e.value.body - ?.toLowerCase() - .contains(searchFilter!.toLowerCase()) ?? - false))); + filteredImagePackImageEntried.removeWhere( + (e) => !(e.key.toLowerCase().contains(searchFilter!.toLowerCase()) || + (e.value.body + ?.toLowerCase() + .contains(searchFilter!.toLowerCase()) ?? + false)), + ); } final imageKeys = filteredImagePackImageEntried.map((e) => e.key).toList(); @@ -57,7 +58,8 @@ class StickerPickerDialogState extends State { GridView.builder( itemCount: imageKeys.length, gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 100), + maxCrossAxisExtent: 100, + ), shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemBuilder: (BuildContext context, int imageIndex) { @@ -127,10 +129,11 @@ class StickerPickerDialogState extends State { ), ), SliverList( - delegate: SliverChildBuilderDelegate( - packBuilder, - childCount: packSlugs.length, - )), + delegate: SliverChildBuilderDelegate( + packBuilder, + childCount: packSlugs.length, + ), + ), ], ), ), diff --git a/lib/pages/chat_details/chat_details.dart b/lib/pages/chat_details/chat_details.dart index 1678441c..9dcbca92 100644 --- a/lib/pages/chat_details/chat_details.dart +++ b/lib/pages/chat_details/chat_details.dart @@ -58,7 +58,8 @@ class ChatDetailsController extends State { ); if (success.error == null) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context)!.displaynameHasBeenChanged))); + SnackBar(content: Text(L10n.of(context)!.displaynameHasBeenChanged)), + ); } } @@ -212,8 +213,11 @@ class ChatDetailsController extends State { future: () => room.setDescription(input.single), ); if (success.error == null) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(L10n.of(context)!.groupDescriptionHasBeenChanged))); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(L10n.of(context)!.groupDescriptionHasBeenChanged), + ), + ); } } @@ -325,7 +329,9 @@ class ChatDetailsController extends State { void requestMoreMembersAction() async { final room = Matrix.of(context).client.getRoomById(roomId!); final participants = await showFutureLoadingDialog( - context: context, future: () => room!.requestParticipants()); + context: context, + future: () => room!.requestParticipants(), + ); if (participants.error == null) { setState(() => members = participants.result); } diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index 62bd2936..91af25e7 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -43,364 +43,400 @@ class ChatDetailsView extends StatelessWidget { controller.members!.length < actualMembersCount; final iconColor = Theme.of(context).textTheme.bodyLarge!.color; return StreamBuilder( - stream: room.onUpdate.stream, - builder: (context, snapshot) { - return Scaffold( - body: NestedScrollView( - headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) => [ - SliverAppBar( - leading: IconButton( - icon: const Icon(Icons.close_outlined), - onPressed: () => - VRouter.of(context).path.startsWith('/spaces/') - ? VRouter.of(context).pop() - : VRouter.of(context) - .toSegments(['rooms', controller.roomId!]), - ), - elevation: Theme.of(context).appBarTheme.elevation, - expandedHeight: 300.0, - floating: true, - pinned: true, - actions: [ - if (room.canonicalAlias.isNotEmpty) - IconButton( - tooltip: L10n.of(context)!.share, - icon: Icon(Icons.adaptive.share_outlined), - onPressed: () => FluffyShare.share( - AppConfig.inviteLinkPrefix + room.canonicalAlias, - context), + stream: room.onUpdate.stream, + builder: (context, snapshot) { + return Scaffold( + body: NestedScrollView( + headerSliverBuilder: + (BuildContext context, bool innerBoxIsScrolled) => [ + SliverAppBar( + leading: IconButton( + icon: const Icon(Icons.close_outlined), + onPressed: () => + VRouter.of(context).path.startsWith('/spaces/') + ? VRouter.of(context).pop() + : VRouter.of(context) + .toSegments(['rooms', controller.roomId!]), + ), + elevation: Theme.of(context).appBarTheme.elevation, + expandedHeight: 300.0, + floating: true, + pinned: true, + actions: [ + if (room.canonicalAlias.isNotEmpty) + IconButton( + tooltip: L10n.of(context)!.share, + icon: Icon(Icons.adaptive.share_outlined), + onPressed: () => FluffyShare.share( + AppConfig.inviteLinkPrefix + room.canonicalAlias, + context, ), - ChatSettingsPopupMenu(room, false) - ], - title: Text( - room.getLocalizedDisplayname( - MatrixLocals(L10n.of(context)!)), - ), - backgroundColor: - Theme.of(context).appBarTheme.backgroundColor, - flexibleSpace: FlexibleSpaceBar( - background: ContentBanner( - mxContent: room.avatar, - onEdit: room.canSendEvent('m.room.avatar') - ? controller.setAvatarAction - : null, - defaultIcon: Icons.group_outlined, ), + ChatSettingsPopupMenu(room, false) + ], + title: Text( + room.getLocalizedDisplayname( + MatrixLocals(L10n.of(context)!), ), ), - ], - body: MaxWidthBody( - child: ListView.builder( - itemCount: controller.members!.length + - 1 + - (canRequestMoreMembers ? 1 : 0), - itemBuilder: (BuildContext context, int i) => i == 0 - ? Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ListTile( - onTap: room.canSendEvent(EventTypes.RoomTopic) - ? controller.setTopicAction - : null, - trailing: room.canSendEvent(EventTypes.RoomTopic) - ? Icon( - Icons.edit_outlined, - color: Theme.of(context) - .colorScheme - .onBackground, - ) - : null, - title: Text( - L10n.of(context)!.groupDescription, - style: TextStyle( - color: - Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.bold, - ), - ), - ), - if (room.topic.isNotEmpty) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0), - child: LinkText( - text: room.topic.isEmpty - ? L10n.of(context)!.addGroupDescription - : room.topic, - linkStyle: - const TextStyle(color: Colors.blueAccent), - textStyle: TextStyle( - fontSize: 14, + backgroundColor: Theme.of(context).appBarTheme.backgroundColor, + flexibleSpace: FlexibleSpaceBar( + background: ContentBanner( + mxContent: room.avatar, + onEdit: room.canSendEvent('m.room.avatar') + ? controller.setAvatarAction + : null, + defaultIcon: Icons.group_outlined, + ), + ), + ), + ], + body: MaxWidthBody( + child: ListView.builder( + itemCount: controller.members!.length + + 1 + + (canRequestMoreMembers ? 1 : 0), + itemBuilder: (BuildContext context, int i) => i == 0 + ? Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ListTile( + onTap: room.canSendEvent(EventTypes.RoomTopic) + ? controller.setTopicAction + : null, + trailing: room.canSendEvent(EventTypes.RoomTopic) + ? Icon( + Icons.edit_outlined, color: Theme.of(context) - .textTheme - .bodyMedium! - .color, - decorationColor: Theme.of(context) - .textTheme - .bodyMedium! - .color, - ), - onLinkTap: (url) => - UrlLauncher(context, url).launchUrl(), - ), + .colorScheme + .onBackground, + ) + : null, + title: Text( + L10n.of(context)!.groupDescription, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, ), - const SizedBox(height: 8), - const Divider(height: 1), - ListTile( - title: Text( - L10n.of(context)!.settings, - style: TextStyle( - color: - Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.bold, - ), - ), - trailing: Icon(controller.displaySettings - ? Icons.keyboard_arrow_down_outlined - : Icons.keyboard_arrow_right_outlined), - onTap: controller.toggleDisplaySettings, ), - if (controller.displaySettings) ...[ - if (room.canSendEvent('m.room.name')) - ListTile( + ), + if (room.topic.isNotEmpty) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + ), + child: LinkText( + text: room.topic.isEmpty + ? L10n.of(context)!.addGroupDescription + : room.topic, + linkStyle: + const TextStyle(color: Colors.blueAccent), + textStyle: TextStyle( + fontSize: 14, + color: Theme.of(context) + .textTheme + .bodyMedium! + .color, + decorationColor: Theme.of(context) + .textTheme + .bodyMedium! + .color, + ), + onLinkTap: (url) => + UrlLauncher(context, url).launchUrl(), + ), + ), + const SizedBox(height: 8), + const Divider(height: 1), + ListTile( + title: Text( + L10n.of(context)!.settings, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), + ), + trailing: Icon( + controller.displaySettings + ? Icons.keyboard_arrow_down_outlined + : Icons.keyboard_arrow_right_outlined, + ), + onTap: controller.toggleDisplaySettings, + ), + if (controller.displaySettings) ...[ + if (room.canSendEvent('m.room.name')) + ListTile( + leading: CircleAvatar( + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: iconColor, + child: const Icon( + Icons.people_outline_outlined, + ), + ), + title: Text( + L10n.of(context)!.changeTheNameOfTheGroup, + ), + subtitle: Text( + room.getLocalizedDisplayname( + MatrixLocals(L10n.of(context)!), + ), + ), + onTap: controller.setDisplaynameAction, + ), + if (room.joinRules == JoinRules.public) + ListTile( + leading: CircleAvatar( + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: iconColor, + child: const Icon(Icons.link_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: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: iconColor, + child: const Icon( + Icons.insert_emoticon_outlined, + ), + ), + title: Text(L10n.of(context)!.emoteSettings), + subtitle: Text(L10n.of(context)!.setCustomEmotes), + onTap: controller.goToEmoteSettings, + ), + PopupMenuButton( + onSelected: controller.setJoinRulesAction, + itemBuilder: (BuildContext context) => + >[ + if (room.canChangeJoinRules) + PopupMenuItem( + value: JoinRules.public, + child: Text( + JoinRules.public.getLocalizedString( + MatrixLocals(L10n.of(context)!), + ), + ), + ), + if (room.canChangeJoinRules) + PopupMenuItem( + value: JoinRules.invite, + child: Text( + JoinRules.invite.getLocalizedString( + MatrixLocals(L10n.of(context)!), + ), + ), + ), + ], + child: ListTile( + leading: CircleAvatar( + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: iconColor, + child: const Icon(Icons.shield_outlined), + ), + title: Text( + L10n.of(context)!.whoIsAllowedToJoinThisGroup, + ), + subtitle: Text( + room.joinRules?.getLocalizedString( + MatrixLocals(L10n.of(context)!), + ) ?? + L10n.of(context)!.none, + ), + ), + ), + PopupMenuButton( + onSelected: controller.setHistoryVisibilityAction, + itemBuilder: (BuildContext context) => + >[ + if (room.canChangeHistoryVisibility) + PopupMenuItem( + value: HistoryVisibility.invited, + child: Text( + HistoryVisibility.invited + .getLocalizedString( + MatrixLocals(L10n.of(context)!), + ), + ), + ), + if (room.canChangeHistoryVisibility) + PopupMenuItem( + value: HistoryVisibility.joined, + child: Text( + HistoryVisibility.joined + .getLocalizedString( + MatrixLocals(L10n.of(context)!), + ), + ), + ), + if (room.canChangeHistoryVisibility) + PopupMenuItem( + value: HistoryVisibility.shared, + child: Text( + HistoryVisibility.shared + .getLocalizedString( + MatrixLocals(L10n.of(context)!), + ), + ), + ), + if (room.canChangeHistoryVisibility) + PopupMenuItem( + value: HistoryVisibility.worldReadable, + child: Text( + HistoryVisibility.worldReadable + .getLocalizedString( + MatrixLocals(L10n.of(context)!), + ), + ), + ), + ], + child: ListTile( + leading: CircleAvatar( + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: iconColor, + child: const Icon(Icons.visibility_outlined), + ), + title: Text( + L10n.of(context)!.visibilityOfTheChatHistory, + ), + subtitle: Text( + room.historyVisibility?.getLocalizedString( + MatrixLocals(L10n.of(context)!), + ) ?? + L10n.of(context)!.none, + ), + ), + ), + if (room.joinRules == JoinRules.public) + PopupMenuButton( + onSelected: controller.setGuestAccessAction, + itemBuilder: (BuildContext context) => + >[ + if (room.canChangeGuestAccess) + PopupMenuItem( + value: GuestAccess.canJoin, + child: Text( + GuestAccess.canJoin.getLocalizedString( + MatrixLocals( + L10n.of(context)!, + ), + ), + ), + ), + if (room.canChangeGuestAccess) + PopupMenuItem( + value: GuestAccess.forbidden, + child: Text( + GuestAccess.forbidden + .getLocalizedString( + MatrixLocals( + L10n.of(context)!, + ), + ), + ), + ), + ], + child: ListTile( leading: CircleAvatar( backgroundColor: Theme.of(context) .scaffoldBackgroundColor, foregroundColor: iconColor, child: const Icon( - Icons.people_outline_outlined), + Icons.person_add_alt_1_outlined, + ), ), - title: Text(L10n.of(context)! - .changeTheNameOfTheGroup), - subtitle: Text(room.getLocalizedDisplayname( - MatrixLocals(L10n.of(context)!))), - onTap: controller.setDisplaynameAction, - ), - if (room.joinRules == JoinRules.public) - ListTile( - leading: CircleAvatar( - backgroundColor: Theme.of(context) - .scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon(Icons.link_outlined), + title: Text( + L10n.of(context)!.areGuestsAllowedToJoin, ), - onTap: controller.editAliases, - title: - Text(L10n.of(context)!.editRoomAliases), subtitle: Text( - (room.canonicalAlias.isNotEmpty) - ? room.canonicalAlias - : L10n.of(context)!.none), - ), - ListTile( - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon( - Icons.insert_emoticon_outlined), - ), - title: Text(L10n.of(context)!.emoteSettings), - subtitle: - Text(L10n.of(context)!.setCustomEmotes), - onTap: controller.goToEmoteSettings, - ), - PopupMenuButton( - onSelected: controller.setJoinRulesAction, - itemBuilder: (BuildContext context) => - >[ - if (room.canChangeJoinRules) - PopupMenuItem( - value: JoinRules.public, - child: Text(JoinRules.public - .getLocalizedString( - MatrixLocals(L10n.of(context)!))), - ), - if (room.canChangeJoinRules) - PopupMenuItem( - value: JoinRules.invite, - child: Text(JoinRules.invite - .getLocalizedString( - MatrixLocals(L10n.of(context)!))), - ), - ], - child: ListTile( - leading: CircleAvatar( - backgroundColor: Theme.of(context) - .scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon(Icons.shield_outlined)), - title: Text(L10n.of(context)! - .whoIsAllowedToJoinThisGroup), - subtitle: Text( - room.joinRules?.getLocalizedString( - MatrixLocals(L10n.of(context)!)) ?? - L10n.of(context)!.none, - ), - ), - ), - PopupMenuButton( - onSelected: - controller.setHistoryVisibilityAction, - itemBuilder: (BuildContext context) => - >[ - if (room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.invited, - child: Text(HistoryVisibility.invited - .getLocalizedString( - MatrixLocals(L10n.of(context)!))), - ), - if (room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.joined, - child: Text(HistoryVisibility.joined - .getLocalizedString( - MatrixLocals(L10n.of(context)!))), - ), - if (room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.shared, - child: Text(HistoryVisibility.shared - .getLocalizedString( - MatrixLocals(L10n.of(context)!))), - ), - if (room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.worldReadable, - child: Text(HistoryVisibility - .worldReadable - .getLocalizedString( - MatrixLocals(L10n.of(context)!))), - ), - ], - child: ListTile( - leading: CircleAvatar( - backgroundColor: Theme.of(context) - .scaffoldBackgroundColor, - foregroundColor: iconColor, - child: - const Icon(Icons.visibility_outlined), - ), - title: Text(L10n.of(context)! - .visibilityOfTheChatHistory), - subtitle: Text( - room.historyVisibility?.getLocalizedString( - MatrixLocals(L10n.of(context)!)) ?? - L10n.of(context)!.none, - ), - ), - ), - if (room.joinRules == JoinRules.public) - PopupMenuButton( - onSelected: controller.setGuestAccessAction, - itemBuilder: (BuildContext context) => - >[ - if (room.canChangeGuestAccess) - PopupMenuItem( - value: GuestAccess.canJoin, - child: Text( - GuestAccess.canJoin - .getLocalizedString(MatrixLocals( - L10n.of(context)!)), - ), - ), - if (room.canChangeGuestAccess) - PopupMenuItem( - value: GuestAccess.forbidden, - child: Text( - GuestAccess.forbidden - .getLocalizedString(MatrixLocals( - L10n.of(context)!)), - ), - ), - ], - child: ListTile( - leading: CircleAvatar( - backgroundColor: Theme.of(context) - .scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon( - Icons.person_add_alt_1_outlined), - ), - title: Text(L10n.of(context)! - .areGuestsAllowedToJoin), - subtitle: Text( - room.guestAccess.getLocalizedString( - MatrixLocals(L10n.of(context)!)), + room.guestAccess.getLocalizedString( + MatrixLocals(L10n.of(context)!), ), ), ), - ListTile( - title: - Text(L10n.of(context)!.editChatPermissions), - subtitle: Text( - L10n.of(context)!.whoCanPerformWhichAction), - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon( - Icons.edit_attributes_outlined), - ), - onTap: () => - VRouter.of(context).to('permissions'), ), - ], - const Divider(height: 1), ListTile( - title: Text( - actualMembersCount > 1 - ? L10n.of(context)!.countParticipants( - actualMembersCount.toString()) - : L10n.of(context)!.emptyChat, - style: TextStyle( - color: - Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.bold, - ), + title: + Text(L10n.of(context)!.editChatPermissions), + subtitle: Text( + L10n.of(context)!.whoCanPerformWhichAction, ), - ), - room.canInvite - ? ListTile( - title: - Text(L10n.of(context)!.inviteContact), - leading: CircleAvatar( - backgroundColor: - Theme.of(context).primaryColor, - foregroundColor: Colors.white, - radius: Avatar.defaultSize / 2, - child: const Icon(Icons.add_outlined), - ), - onTap: () => - VRouter.of(context).to('invite'), - ) - : Container(), - ], - ) - : i < controller.members!.length + 1 - ? ParticipantListItem(controller.members![i - 1]) - : ListTile( - title: Text(L10n.of(context)! - .loadCountMoreParticipants( - (actualMembersCount - - controller.members!.length) - .toString())), leading: CircleAvatar( backgroundColor: Theme.of(context).scaffoldBackgroundColor, + foregroundColor: iconColor, child: const Icon( - Icons.refresh, - color: Colors.grey, + Icons.edit_attributes_outlined, ), ), - onTap: controller.requestMoreMembersAction, + onTap: () => + VRouter.of(context).to('permissions'), ), - ), + ], + const Divider(height: 1), + ListTile( + title: Text( + actualMembersCount > 1 + ? L10n.of(context)!.countParticipants( + actualMembersCount.toString(), + ) + : L10n.of(context)!.emptyChat, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), + ), + ), + room.canInvite + ? ListTile( + title: Text(L10n.of(context)!.inviteContact), + leading: CircleAvatar( + backgroundColor: + Theme.of(context).primaryColor, + foregroundColor: Colors.white, + radius: Avatar.defaultSize / 2, + child: const Icon(Icons.add_outlined), + ), + onTap: () => VRouter.of(context).to('invite'), + ) + : Container(), + ], + ) + : i < controller.members!.length + 1 + ? ParticipantListItem(controller.members![i - 1]) + : ListTile( + title: Text( + L10n.of(context)!.loadCountMoreParticipants( + (actualMembersCount - + controller.members!.length) + .toString(), + ), + ), + leading: CircleAvatar( + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + child: const Icon( + Icons.refresh, + color: Colors.grey, + ), + ), + onTap: controller.requestMoreMembersAction, + ), ), ), - ); - }); + ), + ); + }, + ); } } diff --git a/lib/pages/chat_details/participant_list_item.dart b/lib/pages/chat_details/participant_list_item.dart index 17880d8a..31f3789c 100644 --- a/lib/pages/chat_details/participant_list_item.dart +++ b/lib/pages/chat_details/participant_list_item.dart @@ -47,11 +47,12 @@ class ParticipantListItem extends StatelessWidget { ), margin: const EdgeInsets.symmetric(horizontal: 8), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primaryContainer, - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: Theme.of(context).colorScheme.primary, - )), + color: Theme.of(context).colorScheme.primaryContainer, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: Theme.of(context).colorScheme.primary, + ), + ), child: Text( permissionBatch, style: TextStyle( diff --git a/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart b/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart index cfbbbad2..a6ca7808 100644 --- a/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart +++ b/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart @@ -19,179 +19,180 @@ class ChatEncryptionSettingsView extends StatelessWidget { Widget build(BuildContext context) { final room = controller.room; return StreamBuilder( - stream: room.client.onSync.stream.where( - (s) => s.rooms?.join?[room.id] != null || s.deviceLists != null), - builder: (context, _) => Scaffold( - appBar: AppBar( - leading: IconButton( - icon: const Icon(Icons.close_outlined), - onPressed: () => VRouter.of(context) - .toSegments(['rooms', controller.roomId!]), - ), - title: Text(L10n.of(context)!.endToEndEncryption), - actions: [ - TextButton( - onPressed: () => - launchUrlString(AppConfig.encryptionTutorial), - child: Text(L10n.of(context)!.help), - ), - ], + stream: room.client.onSync.stream.where( + (s) => s.rooms?.join?[room.id] != null || s.deviceLists != null, + ), + builder: (context, _) => Scaffold( + appBar: AppBar( + leading: IconButton( + icon: const Icon(Icons.close_outlined), + onPressed: () => + VRouter.of(context).toSegments(['rooms', controller.roomId!]), + ), + title: Text(L10n.of(context)!.endToEndEncryption), + actions: [ + TextButton( + onPressed: () => launchUrlString(AppConfig.encryptionTutorial), + child: Text(L10n.of(context)!.help), + ), + ], + ), + body: ListView( + children: [ + SwitchListTile( + secondary: CircleAvatar( + foregroundColor: + Theme.of(context).colorScheme.onPrimaryContainer, + backgroundColor: Theme.of(context).colorScheme.primaryContainer, + child: const Icon(Icons.lock_outlined), ), - body: ListView( - children: [ - SwitchListTile( - secondary: CircleAvatar( - foregroundColor: - Theme.of(context).colorScheme.onPrimaryContainer, - backgroundColor: - Theme.of(context).colorScheme.primaryContainer, - child: const Icon(Icons.lock_outlined)), - title: Text(L10n.of(context)!.encryptThisChat), - value: room.encrypted, - onChanged: controller.enableEncryption, + title: Text(L10n.of(context)!.encryptThisChat), + value: room.encrypted, + onChanged: controller.enableEncryption, + ), + Center( + child: Image.asset( + 'assets/encryption.png', + width: 212, + ), + ), + const Divider(height: 1), + if (room.isDirectChat) + Padding( + padding: const EdgeInsets.all(16.0), + child: SizedBox( + width: double.infinity, + child: ElevatedButton.icon( + onPressed: controller.startVerification, + icon: const Icon(Icons.verified_outlined), + label: Text(L10n.of(context)!.verifyStart), ), - Center( - child: Image.asset( - 'assets/encryption.png', - width: 212, - ), + ), + ), + if (room.encrypted) ...[ + const SizedBox(height: 16), + ListTile( + title: Text( + L10n.of(context)!.deviceKeys, + style: const TextStyle( + fontWeight: FontWeight.bold, ), - const Divider(height: 1), - if (room.isDirectChat) - Padding( - padding: const EdgeInsets.all(16.0), - child: SizedBox( - width: double.infinity, - child: ElevatedButton.icon( - onPressed: controller.startVerification, - icon: const Icon(Icons.verified_outlined), - label: Text(L10n.of(context)!.verifyStart), + ), + ), + StreamBuilder( + stream: room.onUpdate.stream, + builder: (context, snapshot) => FutureBuilder>( + future: room.getUserDeviceKeys(), + builder: (BuildContext context, snapshot) { + if (snapshot.hasError) { + return Center( + child: Text( + '${L10n.of(context)!.oopsSomethingWentWrong}: ${snapshot.error}', ), - ), - ), - if (room.encrypted) ...[ - const SizedBox(height: 16), - ListTile( - title: Text( - L10n.of(context)!.deviceKeys, - style: const TextStyle( - fontWeight: FontWeight.bold, + ); + } + if (!snapshot.hasData) { + return const Center( + child: CircularProgressIndicator.adaptive( + strokeWidth: 2, ), - ), - ), - StreamBuilder( - stream: room.onUpdate.stream, - builder: (context, snapshot) => FutureBuilder< - List>( - future: room.getUserDeviceKeys(), - builder: (BuildContext context, snapshot) { - if (snapshot.hasError) { - return Center( - child: Text( - '${L10n.of(context)!.oopsSomethingWentWrong}: ${snapshot.error}'), - ); - } - if (!snapshot.hasData) { - return const Center( - child: CircularProgressIndicator.adaptive( - strokeWidth: 2)); - } - final deviceKeys = snapshot.data!; - return ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: deviceKeys.length, - itemBuilder: (BuildContext context, int i) => - SwitchListTile( - value: !deviceKeys[i].blocked, - activeColor: deviceKeys[i].verified - ? Colors.green - : Colors.orange, - onChanged: (_) => - controller.toggleDeviceKey(deviceKeys[i]), - title: Row( - children: [ - Icon( - deviceKeys[i].verified - ? Icons.verified_outlined - : deviceKeys[i].blocked - ? Icons.block_outlined - : Icons.info_outlined, - color: deviceKeys[i].verified - ? Colors.green - : deviceKeys[i].blocked - ? Colors.red - : Colors.orange, - size: 20, - ), - const SizedBox(width: 4), - Text( - deviceKeys[i].deviceId ?? - L10n.of(context)!.unknownDevice, - ), - const SizedBox(width: 4), - Flexible( - fit: FlexFit.loose, - child: Material( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius), - side: BorderSide( - color: Theme.of(context) - .colorScheme - .primary, - ), - ), - color: Theme.of(context) - .colorScheme - .primaryContainer, - child: Padding( - padding: const EdgeInsets.all(4.0), - child: Text( - deviceKeys[i].userId, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - color: Theme.of(context) - .colorScheme - .primary, - fontSize: 12, - fontStyle: FontStyle.italic, - ), - ), - ), - ), - ), - ], - ), - subtitle: Text( - deviceKeys[i].ed25519Key?.beautified ?? - L10n.of(context)! - .unknownEncryptionAlgorithm, - style: TextStyle( - fontFamily: 'RobotoMono', + ); + } + final deviceKeys = snapshot.data!; + return ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: deviceKeys.length, + itemBuilder: (BuildContext context, int i) => + SwitchListTile( + value: !deviceKeys[i].blocked, + activeColor: deviceKeys[i].verified + ? Colors.green + : Colors.orange, + onChanged: (_) => + controller.toggleDeviceKey(deviceKeys[i]), + title: Row( + children: [ + Icon( + deviceKeys[i].verified + ? Icons.verified_outlined + : deviceKeys[i].blocked + ? Icons.block_outlined + : Icons.info_outlined, + color: deviceKeys[i].verified + ? Colors.green + : deviceKeys[i].blocked + ? Colors.red + : Colors.orange, + size: 20, + ), + const SizedBox(width: 4), + Text( + deviceKeys[i].deviceId ?? + L10n.of(context)!.unknownDevice, + ), + const SizedBox(width: 4), + Flexible( + fit: FlexFit.loose, + child: Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), + side: BorderSide( color: - Theme.of(context).colorScheme.secondary, + Theme.of(context).colorScheme.primary, + ), + ), + color: Theme.of(context) + .colorScheme + .primaryContainer, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: Text( + deviceKeys[i].userId, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: + Theme.of(context).colorScheme.primary, + fontSize: 12, + fontStyle: FontStyle.italic, + ), ), ), ), - ); - }), - ), - ] else - Padding( - padding: const EdgeInsets.all(16.0), - child: Center( - child: Text( - L10n.of(context)!.encryptionNotEnabled, - style: const TextStyle( - fontStyle: FontStyle.italic, + ), + ], + ), + subtitle: Text( + deviceKeys[i].ed25519Key?.beautified ?? + L10n.of(context)!.unknownEncryptionAlgorithm, + style: TextStyle( + fontFamily: 'RobotoMono', + color: Theme.of(context).colorScheme.secondary, ), ), ), - ), - ], + ); + }, + ), ), - )); + ] else + Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: Text( + L10n.of(context)!.encryptionNotEnabled, + style: const TextStyle( + fontStyle: FontStyle.italic, + ), + ), + ), + ), + ], + ), + ), + ); } } diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 126bc7c5..1aa57706 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -164,19 +164,21 @@ class ChatListController extends State void setServer() async { final newServer = await showTextInputDialog( - useRootNavigator: false, - title: L10n.of(context)!.changeTheHomeserver, - context: context, - okLabel: L10n.of(context)!.ok, - cancelLabel: L10n.of(context)!.cancel, - textFields: [ - DialogTextField( - prefixText: 'https://', - hintText: Matrix.of(context).client.homeserver?.host, - initialText: searchServer, - keyboardType: TextInputType.url, - autocorrect: false) - ]); + useRootNavigator: false, + title: L10n.of(context)!.changeTheHomeserver, + context: context, + okLabel: L10n.of(context)!.ok, + cancelLabel: L10n.of(context)!.cancel, + textFields: [ + DialogTextField( + prefixText: 'https://', + hintText: Matrix.of(context).client.homeserver?.host, + initialText: searchServer, + keyboardType: TextInputType.url, + autocorrect: false, + ) + ], + ); if (newServer == null) return; Store().setItem(_serverStoreNamespace, newServer.single); setState(() { @@ -382,9 +384,11 @@ class ChatListController extends State } void toggleSelection(String roomId) { - setState(() => selectedRoomIds.contains(roomId) - ? selectedRoomIds.remove(roomId) - : selectedRoomIds.add(roomId)); + setState( + () => selectedRoomIds.contains(roomId) + ? selectedRoomIds.remove(roomId) + : selectedRoomIds.add(roomId), + ); } Future toggleUnread() async { @@ -456,16 +460,17 @@ class ChatListController extends State void setStatus() async { final input = await showTextInputDialog( - useRootNavigator: false, - context: context, - title: L10n.of(context)!.setStatus, - okLabel: L10n.of(context)!.ok, - cancelLabel: L10n.of(context)!.cancel, - textFields: [ - DialogTextField( - hintText: L10n.of(context)!.statusExampleMessage, - ), - ]); + useRootNavigator: false, + context: context, + title: L10n.of(context)!.setStatus, + okLabel: L10n.of(context)!.ok, + cancelLabel: L10n.of(context)!.cancel, + textFields: [ + DialogTextField( + hintText: L10n.of(context)!.statusExampleMessage, + ), + ], + ); if (input == null) return; await showFutureLoadingDialog( context: context, @@ -491,22 +496,23 @@ class ChatListController extends State Future addToSpace() async { final selectedSpace = await showConfirmationDialog( - context: context, - title: L10n.of(context)!.addToSpace, - message: L10n.of(context)!.addToSpaceDescription, - fullyCapitalizedForMaterial: false, - actions: Matrix.of(context) - .client - .rooms - .where((r) => r.isSpace) - .map( - (space) => AlertDialogAction( - key: space.id, - label: space - .getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)), - ), - ) - .toList()); + context: context, + title: L10n.of(context)!.addToSpace, + message: L10n.of(context)!.addToSpaceDescription, + fullyCapitalizedForMaterial: false, + actions: Matrix.of(context) + .client + .rooms + .where((r) => r.isSpace) + .map( + (space) => AlertDialogAction( + key: space.id, + label: space + .getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)), + ), + ) + .toList(), + ); if (selectedSpace == null) return; final result = await showFutureLoadingDialog( context: context, @@ -532,14 +538,19 @@ class ChatListController extends State } bool get anySelectedRoomNotMarkedUnread => selectedRoomIds.any( - (roomId) => !Matrix.of(context).client.getRoomById(roomId)!.markedUnread); + (roomId) => + !Matrix.of(context).client.getRoomById(roomId)!.markedUnread, + ); bool get anySelectedRoomNotFavorite => selectedRoomIds.any( - (roomId) => !Matrix.of(context).client.getRoomById(roomId)!.isFavourite); + (roomId) => !Matrix.of(context).client.getRoomById(roomId)!.isFavourite, + ); - bool get anySelectedRoomNotMuted => selectedRoomIds.any((roomId) => - Matrix.of(context).client.getRoomById(roomId)!.pushRuleState == - PushRuleState.notify); + bool get anySelectedRoomNotMuted => selectedRoomIds.any( + (roomId) => + Matrix.of(context).client.getRoomById(roomId)!.pushRuleState == + PushRuleState.notify, + ); bool waitForFirstSync = false; @@ -624,9 +635,10 @@ class ChatListController extends State switch (action) { case EditBundleAction.addToBundle: final bundle = await showTextInputDialog( - context: context, - title: l10n.bundleName, - textFields: [DialogTextField(hintText: l10n.bundleName)]); + context: context, + title: l10n.bundleName, + textFields: [DialogTextField(hintText: l10n.bundleName)], + ); if (bundle == null || bundle.isEmpty || bundle.single.isEmpty) return; await showFutureLoadingDialog( context: context, diff --git a/lib/pages/chat_list/chat_list_body.dart b/lib/pages/chat_list/chat_list_body.dart index 4e8b6f70..f5e018ec 100644 --- a/lib/pages/chat_list/chat_list_body.dart +++ b/lib/pages/chat_list/chat_list_body.dart @@ -46,247 +46,248 @@ class ChatListViewBody extends StatelessWidget { ); }, child: StreamBuilder( - key: ValueKey(client.userID.toString() + + key: ValueKey( + client.userID.toString() + controller.activeFilter.toString() + - controller.activeSpaceId.toString()), - stream: client.onSync.stream - .where((s) => s.hasRoomUpdate) - .rateLimit(const Duration(seconds: 1)), - builder: (context, _) { - if (controller.activeFilter == ActiveFilter.spaces && - !controller.isSearchMode) { - return SpaceView( - controller, - scrollController: controller.scrollController, - key: Key(controller.activeSpaceId ?? 'Spaces'), - ); - } - if (controller.waitForFirstSync && client.prevBatch != null) { - final rooms = controller.filteredRooms; - final displayStoriesHeader = { - ActiveFilter.allChats, - ActiveFilter.messages, - }.contains(controller.activeFilter) && - client.storiesRooms.isNotEmpty; - return ListView.builder( - controller: controller.scrollController, - // add +1 space below in order to properly scroll below the spaces bar - itemCount: rooms.length + 1, - itemBuilder: (BuildContext context, int i) { - if (i == 0) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (controller.isSearchMode) ...[ - SearchTitle( - title: L10n.of(context)!.publicRooms, - icon: const Icon(Icons.explore_outlined), - ), - AnimatedContainer( - clipBehavior: Clip.hardEdge, - decoration: const BoxDecoration(), - height: roomSearchResult == null || - roomSearchResult.chunk.isEmpty - ? 0 - : 106, - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - child: roomSearchResult == null - ? null - : ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: roomSearchResult.chunk.length, - itemBuilder: (context, i) => _SearchItem( - title: roomSearchResult.chunk[i].name ?? - roomSearchResult.chunk[i] - .canonicalAlias?.localpart ?? - L10n.of(context)!.group, - avatar: - roomSearchResult.chunk[i].avatarUrl, - onPressed: () => showAdaptiveBottomSheet( - context: context, - builder: (c) => PublicRoomBottomSheet( - roomAlias: roomSearchResult - .chunk[i].canonicalAlias ?? - roomSearchResult.chunk[i].roomId, - outerContext: context, - chunk: roomSearchResult.chunk[i], - ), - ), - ), - ), - ), - SearchTitle( - title: L10n.of(context)!.users, - icon: const Icon(Icons.group_outlined), - ), - AnimatedContainer( - clipBehavior: Clip.hardEdge, - decoration: const BoxDecoration(), - height: userSearchResult == null || - userSearchResult.results.isEmpty - ? 0 - : 106, - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - child: userSearchResult == null - ? null - : ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: userSearchResult.results.length, - itemBuilder: (context, i) => _SearchItem( - title: userSearchResult - .results[i].displayName ?? - userSearchResult - .results[i].userId.localpart ?? - L10n.of(context)!.unknownDevice, - avatar: - userSearchResult.results[i].avatarUrl, - onPressed: () => showAdaptiveBottomSheet( - context: context, - builder: (c) => ProfileBottomSheet( - userId: userSearchResult - .results[i].userId, - outerContext: context, - ), - ), - ), - ), - ), - SearchTitle( - title: L10n.of(context)!.stories, - icon: const Icon(Icons.camera_alt_outlined), - ), - ], - if (displayStoriesHeader) - StoriesHeader( - key: const Key('stories_header'), - filter: controller.searchController.text, - ), - const ConnectionStatusHeader(), + controller.activeSpaceId.toString(), + ), + stream: client.onSync.stream + .where((s) => s.hasRoomUpdate) + .rateLimit(const Duration(seconds: 1)), + builder: (context, _) { + if (controller.activeFilter == ActiveFilter.spaces && + !controller.isSearchMode) { + return SpaceView( + controller, + scrollController: controller.scrollController, + key: Key(controller.activeSpaceId ?? 'Spaces'), + ); + } + if (controller.waitForFirstSync && client.prevBatch != null) { + final rooms = controller.filteredRooms; + final displayStoriesHeader = { + ActiveFilter.allChats, + ActiveFilter.messages, + }.contains(controller.activeFilter) && + client.storiesRooms.isNotEmpty; + return ListView.builder( + controller: controller.scrollController, + // add +1 space below in order to properly scroll below the spaces bar + itemCount: rooms.length + 1, + itemBuilder: (BuildContext context, int i) { + if (i == 0) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (controller.isSearchMode) ...[ + SearchTitle( + title: L10n.of(context)!.publicRooms, + icon: const Icon(Icons.explore_outlined), + ), AnimatedContainer( - height: controller.isTorBrowser ? 64 : 0, - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, clipBehavior: Clip.hardEdge, decoration: const BoxDecoration(), - child: Material( - color: Theme.of(context).colorScheme.surface, - child: ListTile( - leading: const Icon(Icons.vpn_key), - title: Text(L10n.of(context)!.dehydrateTor), - subtitle: - Text(L10n.of(context)!.dehydrateTorLong), - trailing: - const Icon(Icons.chevron_right_outlined), - onTap: controller.dehydrate, - ), - ), - ), - if (controller.isSearchMode) - SearchTitle( - title: L10n.of(context)!.chats, - icon: const Icon(Icons.chat_outlined), - ), - if (rooms.isEmpty && !controller.isSearchMode) - Padding( - padding: const EdgeInsets.all(32.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.asset( - 'assets/start_chat.png', - height: 256, + height: roomSearchResult == null || + roomSearchResult.chunk.isEmpty + ? 0 + : 106, + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + child: roomSearchResult == null + ? null + : ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: roomSearchResult.chunk.length, + itemBuilder: (context, i) => _SearchItem( + title: roomSearchResult.chunk[i].name ?? + roomSearchResult.chunk[i].canonicalAlias + ?.localpart ?? + L10n.of(context)!.group, + avatar: roomSearchResult.chunk[i].avatarUrl, + onPressed: () => showAdaptiveBottomSheet( + context: context, + builder: (c) => PublicRoomBottomSheet( + roomAlias: roomSearchResult + .chunk[i].canonicalAlias ?? + roomSearchResult.chunk[i].roomId, + outerContext: context, + chunk: roomSearchResult.chunk[i], + ), + ), + ), ), - const Divider(height: 1), - ], - ), - ), + ), + SearchTitle( + title: L10n.of(context)!.users, + icon: const Icon(Icons.group_outlined), + ), + AnimatedContainer( + clipBehavior: Clip.hardEdge, + decoration: const BoxDecoration(), + height: userSearchResult == null || + userSearchResult.results.isEmpty + ? 0 + : 106, + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + child: userSearchResult == null + ? null + : ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: userSearchResult.results.length, + itemBuilder: (context, i) => _SearchItem( + title: userSearchResult + .results[i].displayName ?? + userSearchResult + .results[i].userId.localpart ?? + L10n.of(context)!.unknownDevice, + avatar: + userSearchResult.results[i].avatarUrl, + onPressed: () => showAdaptiveBottomSheet( + context: context, + builder: (c) => ProfileBottomSheet( + userId: + userSearchResult.results[i].userId, + outerContext: context, + ), + ), + ), + ), + ), + SearchTitle( + title: L10n.of(context)!.stories, + icon: const Icon(Icons.camera_alt_outlined), + ), ], - ); - } - i--; - if (!rooms[i] - .getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)) - .toLowerCase() - .contains( - controller.searchController.text.toLowerCase())) { - return Container(); - } - return ChatListItem( - rooms[i], - key: Key('chat_list_item_${rooms[i].id}'), - selected: controller.selectedRoomIds.contains(rooms[i].id), - onTap: controller.selectMode == SelectMode.select - ? () => controller.toggleSelection(rooms[i].id) - : null, - onLongPress: () => controller.toggleSelection(rooms[i].id), - activeChat: controller.activeChat == rooms[i].id, - ); - }, - ); - } - const dummyChatCount = 5; - final titleColor = - Theme.of(context).textTheme.bodyLarge!.color!.withAlpha(100); - final subtitleColor = - Theme.of(context).textTheme.bodyLarge!.color!.withAlpha(50); - return ListView.builder( - key: const Key('dummychats'), - itemCount: dummyChatCount, - itemBuilder: (context, i) => Opacity( - opacity: (dummyChatCount - i) / dummyChatCount, - child: ListTile( - leading: CircleAvatar( - backgroundColor: titleColor, - child: CircularProgressIndicator( - strokeWidth: 1, - color: Theme.of(context).textTheme.bodyLarge!.color, - ), - ), - title: Row( - children: [ - Expanded( - child: Container( - height: 14, - decoration: BoxDecoration( - color: titleColor, - borderRadius: BorderRadius.circular(3), + if (displayStoriesHeader) + StoriesHeader( + key: const Key('stories_header'), + filter: controller.searchController.text, + ), + const ConnectionStatusHeader(), + AnimatedContainer( + height: controller.isTorBrowser ? 64 : 0, + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + clipBehavior: Clip.hardEdge, + decoration: const BoxDecoration(), + child: Material( + color: Theme.of(context).colorScheme.surface, + child: ListTile( + leading: const Icon(Icons.vpn_key), + title: Text(L10n.of(context)!.dehydrateTor), + subtitle: Text(L10n.of(context)!.dehydrateTorLong), + trailing: const Icon(Icons.chevron_right_outlined), + onTap: controller.dehydrate, ), ), ), - const SizedBox(width: 36), - Container( - height: 14, - width: 14, - decoration: BoxDecoration( - color: subtitleColor, - borderRadius: BorderRadius.circular(14), + if (controller.isSearchMode) + SearchTitle( + title: L10n.of(context)!.chats, + icon: const Icon(Icons.chat_outlined), ), - ), - const SizedBox(width: 12), - Container( - height: 14, - width: 14, - decoration: BoxDecoration( - color: subtitleColor, - borderRadius: BorderRadius.circular(14), + if (rooms.isEmpty && !controller.isSearchMode) + Padding( + padding: const EdgeInsets.all(32.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + 'assets/start_chat.png', + height: 256, + ), + const Divider(height: 1), + ], + ), ), - ), ], - ), - subtitle: Container( - decoration: BoxDecoration( - color: subtitleColor, - borderRadius: BorderRadius.circular(3), - ), - height: 12, - margin: const EdgeInsets.only(right: 22), + ); + } + i--; + if (!rooms[i] + .getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)) + .toLowerCase() + .contains( + controller.searchController.text.toLowerCase(), + )) { + return Container(); + } + return ChatListItem( + rooms[i], + key: Key('chat_list_item_${rooms[i].id}'), + selected: controller.selectedRoomIds.contains(rooms[i].id), + onTap: controller.selectMode == SelectMode.select + ? () => controller.toggleSelection(rooms[i].id) + : null, + onLongPress: () => controller.toggleSelection(rooms[i].id), + activeChat: controller.activeChat == rooms[i].id, + ); + }, + ); + } + const dummyChatCount = 5; + final titleColor = + Theme.of(context).textTheme.bodyLarge!.color!.withAlpha(100); + final subtitleColor = + Theme.of(context).textTheme.bodyLarge!.color!.withAlpha(50); + return ListView.builder( + key: const Key('dummychats'), + itemCount: dummyChatCount, + itemBuilder: (context, i) => Opacity( + opacity: (dummyChatCount - i) / dummyChatCount, + child: ListTile( + leading: CircleAvatar( + backgroundColor: titleColor, + child: CircularProgressIndicator( + strokeWidth: 1, + color: Theme.of(context).textTheme.bodyLarge!.color, ), ), + title: Row( + children: [ + Expanded( + child: Container( + height: 14, + decoration: BoxDecoration( + color: titleColor, + borderRadius: BorderRadius.circular(3), + ), + ), + ), + const SizedBox(width: 36), + Container( + height: 14, + width: 14, + decoration: BoxDecoration( + color: subtitleColor, + borderRadius: BorderRadius.circular(14), + ), + ), + const SizedBox(width: 12), + Container( + height: 14, + width: 14, + decoration: BoxDecoration( + color: subtitleColor, + borderRadius: BorderRadius.circular(14), + ), + ), + ], + ), + subtitle: Container( + decoration: BoxDecoration( + color: subtitleColor, + borderRadius: BorderRadius.circular(3), + ), + height: 12, + margin: const EdgeInsets.only(right: 22), + ), ), - ); - }), + ), + ); + }, + ), ); } } diff --git a/lib/pages/chat_list/chat_list_header.dart b/lib/pages/chat_list/chat_list_header.dart index 96cb883e..91c58b97 100644 --- a/lib/pages/chat_list/chat_list_header.dart +++ b/lib/pages/chat_list/chat_list_header.dart @@ -120,22 +120,28 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget { ), IconButton( tooltip: L10n.of(context)!.toggleUnread, - icon: Icon(controller.anySelectedRoomNotMarkedUnread - ? Icons.mark_chat_read_outlined - : Icons.mark_chat_unread_outlined), + icon: Icon( + controller.anySelectedRoomNotMarkedUnread + ? Icons.mark_chat_read_outlined + : Icons.mark_chat_unread_outlined, + ), onPressed: controller.toggleUnread, ), IconButton( tooltip: L10n.of(context)!.toggleFavorite, - icon: Icon(controller.anySelectedRoomNotFavorite - ? Icons.push_pin_outlined - : Icons.push_pin), + icon: Icon( + controller.anySelectedRoomNotFavorite + ? Icons.push_pin_outlined + : Icons.push_pin, + ), onPressed: controller.toggleFavouriteRoom, ), IconButton( - icon: Icon(controller.anySelectedRoomNotMuted - ? Icons.notifications_off_outlined - : Icons.notifications_outlined), + icon: Icon( + controller.anySelectedRoomNotMuted + ? Icons.notifications_off_outlined + : Icons.notifications_outlined, + ), tooltip: L10n.of(context)!.toggleMuted, onPressed: controller.toggleMuted, ), diff --git a/lib/pages/chat_list/chat_list_item.dart b/lib/pages/chat_list/chat_list_item.dart index 94919381..3d0b8eab 100644 --- a/lib/pages/chat_list/chat_list_item.dart +++ b/lib/pages/chat_list/chat_list_item.dart @@ -38,15 +38,16 @@ class ChatListItem extends StatelessWidget { if (activeChat) return; if (room.membership == Membership.invite) { final joinResult = await showFutureLoadingDialog( - context: context, - future: () async { - final waitForRoom = room.client.waitForRoomInSync( - room.id, - join: true, - ); - await room.join(); - await waitForRoom; - }); + context: context, + future: () async { + final waitForRoom = room.client.waitForRoomInSync( + room.id, + join: true, + ); + await room.join(); + await waitForRoom; + }, + ); if (joinResult.error != null) return; } @@ -107,7 +108,9 @@ class ChatListItem extends StatelessWidget { ); if (confirmed == OkCancelResult.cancel) return; await showFutureLoadingDialog( - context: context, future: () => room.leave()); + context: context, + future: () => room.leave(), + ); return; } } @@ -183,7 +186,8 @@ class ChatListItem extends StatelessWidget { if (room.isFavourite) Padding( padding: EdgeInsets.only( - right: room.notificationCount > 0 ? 4.0 : 0.0), + right: room.notificationCount > 0 ? 4.0 : 0.0, + ), child: Icon( Icons.push_pin, size: 16, @@ -282,7 +286,8 @@ class ChatListItem extends StatelessWidget { : null, ), ); - }), + }, + ), ), const SizedBox(width: 8), AnimatedContainer( diff --git a/lib/pages/chat_list/chat_list_view.dart b/lib/pages/chat_list/chat_list_view.dart index 6bd24a49..50262d13 100644 --- a/lib/pages/chat_list/chat_list_view.dart +++ b/lib/pages/chat_list/chat_list_view.dart @@ -109,56 +109,60 @@ class ChatListView extends StatelessWidget { children: [ if (FluffyThemes.isColumnMode(context) && FluffyThemes.getDisplayNavigationRail(context)) ...[ - Builder(builder: (context) { - final allSpaces = client.rooms.where((room) => room.isSpace); - final rootSpaces = allSpaces - .where( - (space) => !allSpaces.any( - (parentSpace) => parentSpace.spaceChildren - .any((child) => child.roomId == space.id), - ), - ) - .toList(); - final destinations = getNavigationDestinations(context); + Builder( + builder: (context) { + final allSpaces = + client.rooms.where((room) => room.isSpace); + final rootSpaces = allSpaces + .where( + (space) => !allSpaces.any( + (parentSpace) => parentSpace.spaceChildren + .any((child) => child.roomId == space.id), + ), + ) + .toList(); + final destinations = getNavigationDestinations(context); - return SizedBox( - width: 64, - child: ListView.builder( - scrollDirection: Axis.vertical, - itemCount: rootSpaces.length + destinations.length, - itemBuilder: (context, i) { - if (i < destinations.length) { + return SizedBox( + width: 64, + child: ListView.builder( + scrollDirection: Axis.vertical, + itemCount: rootSpaces.length + destinations.length, + itemBuilder: (context, i) { + if (i < destinations.length) { + return NaviRailItem( + isSelected: i == controller.selectedIndex, + onTap: () => controller.onDestinationSelected(i), + icon: destinations[i].icon, + selectedIcon: destinations[i].selectedIcon, + toolTip: destinations[i].label, + ); + } + i -= destinations.length; + final isSelected = + controller.activeFilter == ActiveFilter.spaces && + rootSpaces[i].id == controller.activeSpaceId; return NaviRailItem( - isSelected: i == controller.selectedIndex, - onTap: () => controller.onDestinationSelected(i), - icon: destinations[i].icon, - selectedIcon: destinations[i].selectedIcon, - toolTip: destinations[i].label, - ); - } - i -= destinations.length; - final isSelected = - controller.activeFilter == ActiveFilter.spaces && - rootSpaces[i].id == controller.activeSpaceId; - return NaviRailItem( - toolTip: rootSpaces[i].getLocalizedDisplayname( - MatrixLocals(L10n.of(context)!)), - isSelected: isSelected, - onTap: () => - controller.setActiveSpace(rootSpaces[i].id), - icon: Avatar( - mxContent: rootSpaces[i].avatar, - name: rootSpaces[i].getLocalizedDisplayname( + toolTip: rootSpaces[i].getLocalizedDisplayname( MatrixLocals(L10n.of(context)!), ), - size: 32, - fontSize: 12, - ), - ); - }, - ), - ); - }), + isSelected: isSelected, + onTap: () => + controller.setActiveSpace(rootSpaces[i].id), + icon: Avatar( + mxContent: rootSpaces[i].avatar, + name: rootSpaces[i].getLocalizedDisplayname( + MatrixLocals(L10n.of(context)!), + ), + size: 32, + fontSize: 12, + ), + ); + }, + ), + ); + }, + ), Container( color: Theme.of(context).dividerColor, width: 1, diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart index 28b4669a..392d73cc 100644 --- a/lib/pages/chat_list/client_chooser_button.dart +++ b/lib/pages/chat_list/client_chooser_button.dart @@ -20,11 +20,13 @@ class ClientChooserButton extends StatelessWidget { List> _bundleMenuItems(BuildContext context) { final matrix = Matrix.of(context); final bundles = matrix.accountBundles.keys.toList() - ..sort((a, b) => a!.isValidMatrixId == b!.isValidMatrixId - ? 0 - : a.isValidMatrixId && !b.isValidMatrixId - ? -1 - : 1); + ..sort( + (a, b) => a!.isValidMatrixId == b!.isValidMatrixId + ? 0 + : a.isValidMatrixId && !b.isValidMatrixId + ? -1 + : 1, + ); return >[ PopupMenuItem( value: SettingsAction.newStory, @@ -142,7 +144,9 @@ class ClientChooserButton extends StatelessWidget { IconButton( icon: const Icon(Icons.edit_outlined), onPressed: () => controller.editBundlesForAccount( - client.userID, bundle), + client.userID, + bundle, + ), ), ], ), @@ -270,9 +274,12 @@ class ClientChooserButton extends StatelessWidget { break; case SettingsAction.invite: FluffyShare.share( - L10n.of(context)!.inviteText(Matrix.of(context).client.userID!, - 'https://matrix.to/#/${Matrix.of(context).client.userID}?client=im.fluffychat'), - context); + L10n.of(context)!.inviteText( + Matrix.of(context).client.userID!, + 'https://matrix.to/#/${Matrix.of(context).client.userID}?client=im.fluffychat', + ), + context, + ); break; case SettingsAction.settings: VRouter.of(context).to('/settings'); @@ -290,11 +297,13 @@ class ClientChooserButton extends StatelessWidget { BuildContext context, ) { final bundles = matrix.accountBundles.keys.toList() - ..sort((a, b) => a!.isValidMatrixId == b!.isValidMatrixId - ? 0 - : a.isValidMatrixId && !b.isValidMatrixId - ? -1 - : 1); + ..sort( + (a, b) => a!.isValidMatrixId == b!.isValidMatrixId + ? 0 + : a.isValidMatrixId && !b.isValidMatrixId + ? -1 + : 1, + ); // beginning from end if negative if (index < 0) { int clientCount = 0; @@ -320,11 +329,13 @@ class ClientChooserButton extends StatelessWidget { int index = 0; final bundles = matrix.accountBundles.keys.toList() - ..sort((a, b) => a!.isValidMatrixId == b!.isValidMatrixId - ? 0 - : a.isValidMatrixId && !b.isValidMatrixId - ? -1 - : 1); + ..sort( + (a, b) => a!.isValidMatrixId == b!.isValidMatrixId + ? 0 + : a.isValidMatrixId && !b.isValidMatrixId + ? -1 + : 1, + ); for (final bundleName in bundles) { final bundle = matrix.accountBundles[bundleName]; if (bundle == null) return null; diff --git a/lib/pages/chat_list/navi_rail_item.dart b/lib/pages/chat_list/navi_rail_item.dart index ed8f5e16..0c078393 100644 --- a/lib/pages/chat_list/navi_rail_item.dart +++ b/lib/pages/chat_list/navi_rail_item.dart @@ -48,17 +48,18 @@ class NaviRailItem extends StatelessWidget { onPressed: onTap, tooltip: toolTip, icon: Material( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - color: isSelected - ? Theme.of(context).colorScheme.primaryContainer - : Theme.of(context).colorScheme.background, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 8.0, - ), - child: isSelected ? selectedIcon ?? icon : icon, - )), + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + color: isSelected + ? Theme.of(context).colorScheme.primaryContainer + : Theme.of(context).colorScheme.background, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 8.0, + ), + child: isSelected ? selectedIcon ?? icon : icon, + ), + ), ), ), ], diff --git a/lib/pages/chat_list/search_title.dart b/lib/pages/chat_list/search_title.dart index 0aceb431..3ed0e64d 100644 --- a/lib/pages/chat_list/search_title.dart +++ b/lib/pages/chat_list/search_title.dart @@ -45,13 +45,15 @@ class SearchTitle extends StatelessWidget { children: [ icon, const SizedBox(width: 16), - Text(title, - textAlign: TextAlign.left, - style: TextStyle( - color: Theme.of(context).colorScheme.onSurface, - fontSize: 12, - fontWeight: FontWeight.bold, - )), + Text( + title, + textAlign: TextAlign.left, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), if (trailing != null) Expanded( child: Align( diff --git a/lib/pages/chat_list/space_view.dart b/lib/pages/chat_list/space_view.dart index 41fc483f..132075a2 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -53,11 +53,14 @@ class _SpaceViewState extends State { final result = await showFutureLoadingDialog( context: context, future: () async { - await client.joinRoom(spaceChild.roomId, - serverName: space?.spaceChildren - .firstWhereOrNull( - (child) => child.roomId == spaceChild.roomId) - ?.via); + await client.joinRoom( + spaceChild.roomId, + serverName: space?.spaceChildren + .firstWhereOrNull( + (child) => child.roomId == spaceChild.roomId, + ) + ?.via, + ); if (client.getRoomById(spaceChild.roomId) == null) { // Wait for room actually appears in sync await client.waitForRoomInSync(spaceChild.roomId, join: true); @@ -78,8 +81,10 @@ class _SpaceViewState extends State { VRouter.of(context).toSegments(['rooms', spaceChild.roomId]); } - void _onSpaceChildContextMenu( - [SpaceRoomsChunk? spaceChild, Room? room]) async { + void _onSpaceChildContextMenu([ + SpaceRoomsChunk? spaceChild, + Room? room, + ]) async { final client = Matrix.of(context).client; final activeSpaceId = widget.controller.activeSpaceId; final activeSpace = @@ -169,8 +174,10 @@ class _SpaceViewState extends State { maxLines: 1, overflow: TextOverflow.ellipsis, ), - subtitle: Text(L10n.of(context)! - .numChats(rootSpace.spaceChildren.length.toString())), + subtitle: Text( + L10n.of(context)! + .numChats(rootSpace.spaceChildren.length.toString()), + ), onTap: () => widget.controller.setActiveSpace(rootSpace.id), onLongPress: () => _onSpaceChildContextMenu(null, rootSpace), trailing: const Icon(Icons.chevron_right_outlined), @@ -180,166 +187,166 @@ class _SpaceViewState extends State { ); } return FutureBuilder( - future: getFuture(activeSpaceId), - builder: (context, snapshot) { - final response = snapshot.data; - final error = snapshot.error; - if (error != null) { - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: Text(error.toLocalizedString(context)), - ), - IconButton( - onPressed: _refresh, - icon: const Icon(Icons.refresh_outlined), - ) - ], - ); - } - if (response == null) { - return const Center(child: CircularProgressIndicator.adaptive()); - } - final parentSpace = allSpaces.firstWhereOrNull((space) => space - .spaceChildren - .any((child) => child.roomId == activeSpaceId)); - final spaceChildren = response.rooms; - final canLoadMore = response.nextBatch != null; - return VWidgetGuard( - onSystemPop: (redirector) async { - if (parentSpace != null) { - widget.controller.setActiveSpace(parentSpace.id); - redirector.stopRedirection(); - return; + future: getFuture(activeSpaceId), + builder: (context, snapshot) { + final response = snapshot.data; + final error = snapshot.error; + if (error != null) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Text(error.toLocalizedString(context)), + ), + IconButton( + onPressed: _refresh, + icon: const Icon(Icons.refresh_outlined), + ) + ], + ); + } + if (response == null) { + return const Center(child: CircularProgressIndicator.adaptive()); + } + final parentSpace = allSpaces.firstWhereOrNull( + (space) => + space.spaceChildren.any((child) => child.roomId == activeSpaceId), + ); + final spaceChildren = response.rooms; + final canLoadMore = response.nextBatch != null; + return VWidgetGuard( + onSystemPop: (redirector) async { + if (parentSpace != null) { + widget.controller.setActiveSpace(parentSpace.id); + redirector.stopRedirection(); + return; + } + }, + child: ListView.builder( + itemCount: spaceChildren.length + 1 + (canLoadMore ? 1 : 0), + controller: widget.scrollController, + itemBuilder: (context, i) { + if (i == 0) { + return ListTile( + leading: BackButton( + onPressed: () => + widget.controller.setActiveSpace(parentSpace?.id), + ), + title: Text( + parentSpace == null + ? L10n.of(context)!.allSpaces + : parentSpace.getLocalizedDisplayname( + MatrixLocals(L10n.of(context)!), + ), + ), + trailing: IconButton( + icon: snapshot.connectionState != ConnectionState.done + ? const CircularProgressIndicator.adaptive() + : const Icon(Icons.refresh_outlined), + onPressed: snapshot.connectionState != ConnectionState.done + ? null + : _refresh, + ), + ); } - }, - child: ListView.builder( - itemCount: spaceChildren.length + 1 + (canLoadMore ? 1 : 0), - controller: widget.scrollController, - itemBuilder: (context, i) { - if (i == 0) { - return ListTile( - leading: BackButton( - onPressed: () => - widget.controller.setActiveSpace(parentSpace?.id), - ), - title: Text(parentSpace == null - ? L10n.of(context)!.allSpaces - : parentSpace.getLocalizedDisplayname( - MatrixLocals(L10n.of(context)!), - )), - trailing: IconButton( - icon: snapshot.connectionState != ConnectionState.done - ? const CircularProgressIndicator.adaptive() - : const Icon(Icons.refresh_outlined), - onPressed: - snapshot.connectionState != ConnectionState.done - ? null - : _refresh, - ), - ); - } - i--; - if (canLoadMore && i == spaceChildren.length) { - return ListTile( - title: Text(L10n.of(context)!.loadMore), - trailing: const Icon(Icons.chevron_right_outlined), - onTap: () { - prevBatch = response.nextBatch; - _refresh(); - }, - ); - } - final spaceChild = spaceChildren[i]; - final room = client.getRoomById(spaceChild.roomId); - if (room != null && !room.isSpace) { - return ChatListItem( - room, - onLongPress: () => - _onSpaceChildContextMenu(spaceChild, room), - activeChat: widget.controller.activeChat == room.id, - ); - } - final isSpace = spaceChild.roomType == 'm.space'; - final topic = spaceChild.topic?.isEmpty ?? true - ? null - : spaceChild.topic; - if (spaceChild.roomId == activeSpaceId) { - return SearchTitle( - title: spaceChild.name ?? - spaceChild.canonicalAlias ?? - 'Space', - icon: Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: Avatar( - size: 24, - mxContent: spaceChild.avatarUrl, - name: spaceChild.name, - fontSize: 9, - ), - ), - color: Theme.of(context) - .colorScheme - .secondaryContainer - .withAlpha(128), - trailing: const Padding( - padding: EdgeInsets.symmetric(horizontal: 16.0), - child: Icon(Icons.edit_outlined), - ), - onTap: () => _onJoinSpaceChild(spaceChild), - ); - } - return ListTile( - leading: Avatar( + i--; + if (canLoadMore && i == spaceChildren.length) { + return ListTile( + title: Text(L10n.of(context)!.loadMore), + trailing: const Icon(Icons.chevron_right_outlined), + onTap: () { + prevBatch = response.nextBatch; + _refresh(); + }, + ); + } + final spaceChild = spaceChildren[i]; + final room = client.getRoomById(spaceChild.roomId); + if (room != null && !room.isSpace) { + return ChatListItem( + room, + onLongPress: () => _onSpaceChildContextMenu(spaceChild, room), + activeChat: widget.controller.activeChat == room.id, + ); + } + final isSpace = spaceChild.roomType == 'm.space'; + final topic = + spaceChild.topic?.isEmpty ?? true ? null : spaceChild.topic; + if (spaceChild.roomId == activeSpaceId) { + return SearchTitle( + title: + spaceChild.name ?? spaceChild.canonicalAlias ?? 'Space', + icon: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: Avatar( + size: 24, mxContent: spaceChild.avatarUrl, name: spaceChild.name, + fontSize: 9, ), - title: Row( - children: [ - Expanded( - child: Text( - spaceChild.name ?? - spaceChild.canonicalAlias ?? - L10n.of(context)!.chat, - maxLines: 1, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - ), - if (!isSpace) ...[ - const Icon( - Icons.people_outline, - size: 16, - ), - const SizedBox(width: 4), - Text( - spaceChild.numJoinedMembers.toString(), - style: const TextStyle(fontSize: 14), - ), - ], - ], + ), + color: Theme.of(context) + .colorScheme + .secondaryContainer + .withAlpha(128), + trailing: const Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: Icon(Icons.edit_outlined), + ), + onTap: () => _onJoinSpaceChild(spaceChild), + ); + } + return ListTile( + leading: Avatar( + mxContent: spaceChild.avatarUrl, + name: spaceChild.name, + ), + title: Row( + children: [ + Expanded( + child: Text( + spaceChild.name ?? + spaceChild.canonicalAlias ?? + L10n.of(context)!.chat, + maxLines: 1, + style: const TextStyle(fontWeight: FontWeight.bold), + ), ), - onTap: () => _onJoinSpaceChild(spaceChild), - onLongPress: () => - _onSpaceChildContextMenu(spaceChild, room), - subtitle: Text( - topic ?? - (isSpace - ? L10n.of(context)!.enterSpace - : L10n.of(context)!.enterRoom), - maxLines: 1, - style: TextStyle( - color: Theme.of(context).colorScheme.onBackground), - ), - trailing: isSpace - ? const Icon(Icons.chevron_right_outlined) - : null, - ); - }), - ); - }); + if (!isSpace) ...[ + const Icon( + Icons.people_outline, + size: 16, + ), + const SizedBox(width: 4), + Text( + spaceChild.numJoinedMembers.toString(), + style: const TextStyle(fontSize: 14), + ), + ], + ], + ), + onTap: () => _onJoinSpaceChild(spaceChild), + onLongPress: () => _onSpaceChildContextMenu(spaceChild, room), + subtitle: Text( + topic ?? + (isSpace + ? L10n.of(context)!.enterSpace + : L10n.of(context)!.enterRoom), + maxLines: 1, + style: TextStyle( + color: Theme.of(context).colorScheme.onBackground, + ), + ), + trailing: + isSpace ? const Icon(Icons.chevron_right_outlined) : null, + ); + }, + ), + ); + }, + ); } } diff --git a/lib/pages/chat_permissions_settings/chat_permissions_settings.dart b/lib/pages/chat_permissions_settings/chat_permissions_settings.dart index 32fbb9d4..f591d35f 100644 --- a/lib/pages/chat_permissions_settings/chat_permissions_settings.dart +++ b/lib/pages/chat_permissions_settings/chat_permissions_settings.dart @@ -22,12 +22,17 @@ class ChatPermissionsSettings extends StatefulWidget { class ChatPermissionsSettingsController extends State { String? get roomId => VRouter.of(context).pathParameters['roomid']; - void editPowerLevel(BuildContext context, String key, int currentLevel, - {String? category}) async { + void editPowerLevel( + BuildContext context, + String key, + int currentLevel, { + String? category, + }) async { final room = Matrix.of(context).client.getRoomById(roomId!)!; if (!room.canSendEvent(EventTypes.RoomPowerLevels)) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context)!.noPermission))); + SnackBar(content: Text(L10n.of(context)!.noPermission)), + ); return; } final newLevel = await showPermissionChooser( @@ -36,7 +41,8 @@ class ChatPermissionsSettingsController extends State { ); if (newLevel == null) return; final content = Map.from( - room.getState(EventTypes.RoomPowerLevels)!.content); + room.getState(EventTypes.RoomPowerLevels)!.content, + ); if (category != null) { if (!content.containsKey(category)) { content[category] = {}; @@ -74,10 +80,13 @@ class ChatPermissionsSettingsController extends State { title: L10n.of(context)!.replaceRoomWithNewerVersion, actions: capabilities.mRoomVersions!.available.entries .where((r) => r.key != roomVersion) - .map((version) => AlertDialogAction( + .map( + (version) => AlertDialogAction( key: version.key, label: - '${version.key} (${version.value.toString().split('.').last})')) + '${version.key} (${version.value.toString().split('.').last})', + ), + ) .toList(), ); if (newVersion == null || 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 645d2358..9fc69bb5 100644 --- a/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart +++ b/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart @@ -41,7 +41,8 @@ class ChatPermissionsSettingsView extends StatelessWidget { return Center(child: Text(L10n.of(context)!.noRoomsFound)); } final powerLevelsContent = Map.from( - room.getState(EventTypes.RoomPowerLevels)!.content); + room.getState(EventTypes.RoomPowerLevels)!.content, + ); final powerLevels = Map.from(powerLevelsContent) ..removeWhere((k, v) => v is! int); final eventsPowerLevels = @@ -57,7 +58,10 @@ class ChatPermissionsSettingsView extends StatelessWidget { permissionKey: entry.key, permission: entry.value, onTap: () => controller.editPowerLevel( - context, entry.key, entry.value), + context, + entry.key, + entry.value, + ), ), const Divider(thickness: 1), ListTile( @@ -69,21 +73,26 @@ class ChatPermissionsSettingsView extends StatelessWidget { ), ), ), - Builder(builder: (context) { - const key = 'rooms'; - final int value = powerLevelsContent - .containsKey('notifications') - ? powerLevelsContent['notifications']['rooms'] ?? 0 - : 0; - return PermissionsListTile( - permissionKey: key, - permission: value, - category: 'notifications', - onTap: () => controller.editPowerLevel( - context, key, value, - category: 'notifications'), - ); - }), + Builder( + builder: (context) { + const key = 'rooms'; + final int value = powerLevelsContent + .containsKey('notifications') + ? powerLevelsContent['notifications']['rooms'] ?? 0 + : 0; + return PermissionsListTile( + permissionKey: key, + permission: value, + category: 'notifications', + onTap: () => controller.editPowerLevel( + context, + key, + value, + category: 'notifications', + ), + ); + }, + ), const Divider(thickness: 1), ListTile( title: Text( @@ -100,8 +109,11 @@ class ChatPermissionsSettingsView extends StatelessWidget { category: 'events', permission: entry.value, onTap: () => controller.editPowerLevel( - context, entry.key, entry.value, - category: 'events'), + context, + entry.key, + entry.value, + category: 'events', + ), ), if (room.canSendEvent(EventTypes.RoomTombstone)) ...{ const Divider(thickness: 1), @@ -110,8 +122,10 @@ class ChatPermissionsSettingsView extends StatelessWidget { builder: (context, snapshot) { if (!snapshot.hasData) { return const Center( - child: CircularProgressIndicator.adaptive( - strokeWidth: 2)); + child: CircularProgressIndicator.adaptive( + strokeWidth: 2, + ), + ); } final String roomVersion = room .getState(EventTypes.RoomCreate)! @@ -120,7 +134,8 @@ class ChatPermissionsSettingsView extends StatelessWidget { return ListTile( title: Text( - '${L10n.of(context)!.roomVersion}: $roomVersion'), + '${L10n.of(context)!.roomVersion}: $roomVersion', + ), onTap: () => controller.updateRoomAction(snapshot.data!), ); diff --git a/lib/pages/connect/connect_page.dart b/lib/pages/connect/connect_page.dart index 1e34e269..d950f681 100644 --- a/lib/pages/connect/connect_page.dart +++ b/lib/pages/connect/connect_page.dart @@ -118,8 +118,9 @@ class ConnectPageController extends State { List? get identityProviders { final loginTypes = _rawLoginTypes; if (loginTypes == null) return null; - final rawProviders = loginTypes.tryGetList('flows')!.singleWhere((flow) => - flow['type'] == AuthenticationTypes.sso)['identity_providers']; + final rawProviders = loginTypes.tryGetList('flows')!.singleWhere( + (flow) => flow['type'] == AuthenticationTypes.sso, + )['identity_providers']; final list = (rawProviders as List) .map((json) => IdentityProvider.fromJson(json)) .toList(); @@ -163,9 +164,11 @@ class ConnectPageController extends State { RequestType.GET, '/client/r0/login', ) - .then((loginTypes) => setState(() { - _rawLoginTypes = loginTypes; - })); + .then( + (loginTypes) => setState(() { + _rawLoginTypes = loginTypes; + }), + ); } } diff --git a/lib/pages/connect/connect_page_view.dart b/lib/pages/connect/connect_page_view.dart index 6e549def..fe21c1f6 100644 --- a/lib/pages/connect/connect_page_view.dart +++ b/lib/pages/connect/connect_page_view.dart @@ -174,17 +174,20 @@ class ConnectPageView extends StatelessWidget { ) : Image.network( Uri.parse(identityProviders.single.icon!) - .getDownloadLink(Matrix.of(context) - .getLoginClient()) + .getDownloadLink( + Matrix.of(context).getLoginClient(), + ) .toString(), width: 32, height: 32, ), onPressed: () => controller .ssoLoginAction(identityProviders.single.id!), - label: Text(identityProviders.single.name ?? - identityProviders.single.brand ?? - L10n.of(context)!.loginWithOneClick), + label: Text( + identityProviders.single.name ?? + identityProviders.single.brand ?? + L10n.of(context)!.loginWithOneClick, + ), ), ) : Wrap( diff --git a/lib/pages/connect/sso_button.dart b/lib/pages/connect/sso_button.dart index b0ec604b..f8fe1e16 100644 --- a/lib/pages/connect/sso_button.dart +++ b/lib/pages/connect/sso_button.dart @@ -37,7 +37,8 @@ class SsoButton extends StatelessWidget { : Image.network( Uri.parse(identityProvider.icon!) .getDownloadLink( - Matrix.of(context).getLoginClient()) + Matrix.of(context).getLoginClient(), + ) .toString(), width: 32, height: 32, diff --git a/lib/pages/device_settings/device_settings_view.dart b/lib/pages/device_settings/device_settings_view.dart index 8b8f467d..2c1a1839 100644 --- a/lib/pages/device_settings/device_settings_view.dart +++ b/lib/pages/device_settings/device_settings_view.dart @@ -35,7 +35,8 @@ class DevicesSettingsView extends StatelessWidget { } if (!snapshot.hasData || controller.devices == null) { return const Center( - child: CircularProgressIndicator.adaptive(strokeWidth: 2)); + child: CircularProgressIndicator.adaptive(strokeWidth: 2), + ); } return ListView.builder( itemCount: controller.notThisDevice.length + 1, @@ -63,12 +64,14 @@ class DevicesSettingsView extends StatelessWidget { ), trailing: controller.loadingDeletingDevices ? const CircularProgressIndicator.adaptive( - strokeWidth: 2) + strokeWidth: 2, + ) : const Icon(Icons.delete_outline), onTap: controller.loadingDeletingDevices ? null : () => controller.removeDevicesAction( - controller.notThisDevice), + controller.notThisDevice, + ), ) else Center( diff --git a/lib/pages/device_settings/user_device_list_item.dart b/lib/pages/device_settings/user_device_list_item.dart index 52cc6bf1..887b3b44 100644 --- a/lib/pages/device_settings/user_device_list_item.dart +++ b/lib/pages/device_settings/user_device_list_item.dart @@ -135,8 +135,9 @@ class UserDeviceListItem extends StatelessWidget { ), subtitle: Text( L10n.of(context)!.lastActiveAgo( - DateTime.fromMillisecondsSinceEpoch(userDevice.lastSeenTs ?? 0) - .localizedTimeShort(context)), + DateTime.fromMillisecondsSinceEpoch(userDevice.lastSeenTs ?? 0) + .localizedTimeShort(context), + ), style: const TextStyle(fontWeight: FontWeight.w300), ), ); diff --git a/lib/pages/dialer/dialer.dart b/lib/pages/dialer/dialer.dart index 6c11aecf..2891fc54 100644 --- a/lib/pages/dialer/dialer.dart +++ b/lib/pages/dialer/dialer.dart @@ -36,9 +36,12 @@ import 'package:fluffychat/widgets/avatar.dart'; import 'pip/pip_view.dart'; class _StreamView extends StatelessWidget { - const _StreamView(this.wrappedStream, - {Key? key, this.mainView = false, required this.matrixClient}) - : super(key: key); + const _StreamView( + this.wrappedStream, { + Key? key, + this.mainView = false, + required this.matrixClient, + }) : super(key: key); final WrappedMediaStream wrappedStream; final Client matrixClient; @@ -67,43 +70,48 @@ class _StreamView extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - decoration: const BoxDecoration( - color: Colors.black54, - ), - child: Stack( - alignment: Alignment.center, - children: [ - if (videoMuted) - Container( - color: Colors.transparent, - ), - if (!videoMuted) - RTCVideoView( - // yes, it must explicitly be casted even though I do not feel - // comfortable with it... - wrappedStream.renderer as RTCVideoRenderer, - mirror: mirrored, - objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain, - ), - if (videoMuted) - Positioned( - child: Avatar( + decoration: const BoxDecoration( + color: Colors.black54, + ), + child: Stack( + alignment: Alignment.center, + children: [ + if (videoMuted) + Container( + color: Colors.transparent, + ), + if (!videoMuted) + RTCVideoView( + // yes, it must explicitly be casted even though I do not feel + // comfortable with it... + wrappedStream.renderer as RTCVideoRenderer, + mirror: mirrored, + objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain, + ), + if (videoMuted) + Positioned( + child: Avatar( mxContent: avatarUrl, name: displayName, size: mainView ? 96 : 48, client: matrixClient, // textSize: mainView ? 36 : 24, // matrixClient: matrixClient, - )), - if (!isScreenSharing) - Positioned( - left: 4.0, - bottom: 4.0, - child: Icon(audioMuted ? Icons.mic_off : Icons.mic, - color: Colors.white, size: 18.0), - ) - ], - )); + ), + ), + if (!isScreenSharing) + Positioned( + left: 4.0, + bottom: 4.0, + child: Icon( + audioMuted ? Icons.mic_off : Icons.mic, + color: Colors.white, + size: 18.0, + ), + ) + ], + ), + ); } } @@ -114,14 +122,14 @@ class Calling extends StatefulWidget { final CallSession call; final Client client; - const Calling( - {required this.context, - required this.call, - required this.client, - required this.callId, - this.onClear, - Key? key}) - : super(key: key); + const Calling({ + required this.context, + required this.call, + required this.client, + required this.callId, + this.onClear, + Key? key, + }) : super(key: key); @override MyCallingPage createState() => MyCallingPage(); @@ -206,7 +214,8 @@ class MyCallingPage extends State { event == CallEvent.kRemoteHoldUnhold) { setState(() {}); Logs().i( - 'Call hold event: local ${call.localHold}, remote ${call.remoteOnHold}'); + 'Call hold event: local ${call.localHold}, remote ${call.remoteOnHold}', + ); } }); _state = call.state; @@ -239,7 +248,9 @@ class MyCallingPage extends State { void _resizeLocalVideo(Orientation orientation) { final shortSide = min( - MediaQuery.of(context).size.width, MediaQuery.of(context).size.height); + MediaQuery.of(context).size.width, + MediaQuery.of(context).size.height, + ); _localVideoMargin = remoteStream != null ? const EdgeInsets.only(top: 20.0, right: 20.0) : EdgeInsets.zero; @@ -304,8 +315,9 @@ class MyCallingPage extends State { foregroundTaskOptions: const ForegroundTaskOptions(), ); FlutterForegroundTask.startService( - notificationTitle: L10n.of(context)!.screenSharingTitle, - notificationText: L10n.of(context)!.screenSharingDetail); + notificationTitle: L10n.of(context)!.screenSharingTitle, + notificationText: L10n.of(context)!.screenSharingDetail, + ); } else { FlutterForegroundTask.stopService(); } @@ -331,7 +343,8 @@ class MyCallingPage extends State { void _switchCamera() async { if (call.localUserMediaStream != null) { await Helper.switchCamera( - call.localUserMediaStream!.stream!.getVideoTracks()[0]); + call.localUserMediaStream!.stream!.getVideoTracks()[0], + ); if (PlatformInfos.isMobile) { call.facingMode == 'user' ? call.facingMode = 'environment' @@ -473,22 +486,27 @@ class MyCallingPage extends State { } else if (call.remoteOnHold) { title = 'You held the call.'; } - stackWidgets.add(Center( - child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( - Icons.pause, - size: 48.0, - color: Colors.white, + stackWidgets.add( + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.pause, + size: 48.0, + color: Colors.white, + ), + Text( + title, + style: const TextStyle( + color: Colors.white, + fontSize: 24.0, + ), + ) + ], ), - Text( - title, - style: const TextStyle( - color: Colors.white, - fontSize: 24.0, - ), - ) - ]), - )); + ), + ); return stackWidgets; } @@ -502,10 +520,15 @@ class MyCallingPage extends State { } if (primaryStream != null) { - stackWidgets.add(Center( - child: _StreamView(primaryStream, - mainView: true, matrixClient: widget.client), - )); + stackWidgets.add( + Center( + child: _StreamView( + primaryStream, + mainView: true, + matrixClient: widget.client, + ), + ), + ); } if (isFloating || !connected) { @@ -522,47 +545,58 @@ class MyCallingPage extends State { if (call.remoteScreenSharingStream != null) { final remoteUserMediaStream = call.remoteUserMediaStream; - secondaryStreamViews.add(SizedBox( - width: _localVideoWidth, - height: _localVideoHeight, - child: _StreamView(remoteUserMediaStream!, matrixClient: widget.client), - )); + secondaryStreamViews.add( + SizedBox( + width: _localVideoWidth, + height: _localVideoHeight, + child: + _StreamView(remoteUserMediaStream!, matrixClient: widget.client), + ), + ); secondaryStreamViews.add(const SizedBox(height: 10)); } final localStream = call.localUserMediaStream ?? call.localScreenSharingStream; if (localStream != null && !isFloating) { - secondaryStreamViews.add(SizedBox( - width: _localVideoWidth, - height: _localVideoHeight, - child: _StreamView(localStream, matrixClient: widget.client), - )); + secondaryStreamViews.add( + SizedBox( + width: _localVideoWidth, + height: _localVideoHeight, + child: _StreamView(localStream, matrixClient: widget.client), + ), + ); secondaryStreamViews.add(const SizedBox(height: 10)); } if (call.localScreenSharingStream != null && !isFloating) { - secondaryStreamViews.add(SizedBox( - width: _localVideoWidth, - height: _localVideoHeight, - child: _StreamView(call.remoteUserMediaStream!, - matrixClient: widget.client), - )); + secondaryStreamViews.add( + SizedBox( + width: _localVideoWidth, + height: _localVideoHeight, + child: _StreamView( + call.remoteUserMediaStream!, + matrixClient: widget.client, + ), + ), + ); secondaryStreamViews.add(const SizedBox(height: 10)); } if (secondaryStreamViews.isNotEmpty) { - stackWidgets.add(Container( - padding: const EdgeInsets.fromLTRB(0, 20, 0, 120), - alignment: Alignment.bottomRight, - child: Container( - width: _localVideoWidth, - margin: _localVideoMargin, - child: Column( - children: secondaryStreamViews, + stackWidgets.add( + Container( + padding: const EdgeInsets.fromLTRB(0, 20, 0, 120), + alignment: Alignment.bottomRight, + child: Container( + width: _localVideoWidth, + margin: _localVideoMargin, + child: Column( + children: secondaryStreamViews, + ), ), ), - )); + ); } return stackWidgets; @@ -570,27 +604,31 @@ class MyCallingPage extends State { @override Widget build(BuildContext context) { - return PIPView(builder: (context, isFloating) { - return Scaffold( + return PIPView( + builder: (context, isFloating) { + return Scaffold( resizeToAvoidBottomInset: !isFloating, floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, floatingActionButton: SizedBox( - width: 320.0, - height: 150.0, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: _buildActionButtons(isFloating))), + width: 320.0, + height: 150.0, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: _buildActionButtons(isFloating), + ), + ), body: OrientationBuilder( - builder: (BuildContext context, Orientation orientation) { - return Container( + builder: (BuildContext context, Orientation orientation) { + return Container( decoration: const BoxDecoration( color: Colors.black87, ), - child: Stack(children: [ - ..._buildContent(orientation, isFloating), - if (!isFloating) - Positioned( + child: Stack( + children: [ + ..._buildContent(orientation, isFloating), + if (!isFloating) + Positioned( top: 24.0, left: 24.0, child: IconButton( @@ -599,9 +637,15 @@ class MyCallingPage extends State { onPressed: () { PIPView.of(context)?.setFloating(true); }, - )) - ])); - })); - }); + ), + ) + ], + ), + ); + }, + ), + ); + }, + ); } } diff --git a/lib/pages/dialer/pip/pip_view.dart b/lib/pages/dialer/pip/pip_view.dart index 95fd195a..74008aaf 100644 --- a/lib/pages/dialer/pip/pip_view.dart +++ b/lib/pages/dialer/pip/pip_view.dart @@ -198,9 +198,11 @@ class PIPViewState extends State with TickerProviderStateMixin { : Tween( begin: _dragOffset, end: calculatedOffset, - ).transform(_dragAnimationController.isAnimating - ? dragAnimationValue - : toggleFloatingAnimationValue); + ).transform( + _dragAnimationController.isAnimating + ? dragAnimationValue + : toggleFloatingAnimationValue, + ); final borderRadius = Tween( begin: 0, end: 10, diff --git a/lib/pages/homeserver_picker/homeserver_bottom_sheet.dart b/lib/pages/homeserver_picker/homeserver_bottom_sheet.dart index c1750184..d7cd534b 100644 --- a/lib/pages/homeserver_picker/homeserver_bottom_sheet.dart +++ b/lib/pages/homeserver_picker/homeserver_bottom_sheet.dart @@ -21,52 +21,54 @@ class HomeserverBottomSheet extends StatelessWidget { appBar: AppBar( title: Text(homeserver.homeserver.baseUrl.host), ), - body: ListView(children: [ - if (description != null && description.isNotEmpty) + body: ListView( + children: [ + if (description != null && description.isNotEmpty) + ListTile( + leading: const Icon(Icons.info_outlined), + title: Text(description), + ), + if (jurisdiction != null && jurisdiction.isNotEmpty) + ListTile( + leading: const Icon(Icons.location_city_outlined), + title: Text(jurisdiction), + ), + if (homeserverSoftware != null && homeserverSoftware.isNotEmpty) + ListTile( + leading: const Icon(Icons.domain_outlined), + title: Text(homeserverSoftware), + ), ListTile( - leading: const Icon(Icons.info_outlined), - title: Text(description), + onTap: () => + launchUrlString(homeserver.homeserver.baseUrl.toString()), + leading: const Icon(Icons.link_outlined), + title: Text(homeserver.homeserver.baseUrl.toString()), ), - if (jurisdiction != null && jurisdiction.isNotEmpty) - ListTile( - leading: const Icon(Icons.location_city_outlined), - title: Text(jurisdiction), - ), - if (homeserverSoftware != null && homeserverSoftware.isNotEmpty) - ListTile( - leading: const Icon(Icons.domain_outlined), - title: Text(homeserverSoftware), - ), - ListTile( - onTap: () => - launchUrlString(homeserver.homeserver.baseUrl.toString()), - leading: const Icon(Icons.link_outlined), - title: Text(homeserver.homeserver.baseUrl.toString()), - ), - if (registration != null) - ListTile( - onTap: () => launchUrlString(registration.toString()), - leading: const Icon(Icons.person_add_outlined), - title: Text(registration.toString()), - ), - if (rules != null) - ListTile( - onTap: () => launchUrlString(rules.toString()), - leading: const Icon(Icons.visibility_outlined), - title: Text(rules.toString()), - ), - if (privacy != null) - ListTile( - onTap: () => launchUrlString(privacy.toString()), - leading: const Icon(Icons.shield_outlined), - title: Text(privacy.toString()), - ), - if (responseTime != null) - ListTile( - leading: const Icon(Icons.timer_outlined), - title: Text('${responseTime.inMilliseconds}ms'), - ), - ]), + if (registration != null) + ListTile( + onTap: () => launchUrlString(registration.toString()), + leading: const Icon(Icons.person_add_outlined), + title: Text(registration.toString()), + ), + if (rules != null) + ListTile( + onTap: () => launchUrlString(rules.toString()), + leading: const Icon(Icons.visibility_outlined), + title: Text(rules.toString()), + ), + if (privacy != null) + ListTile( + onTap: () => launchUrlString(privacy.toString()), + leading: const Icon(Icons.shield_outlined), + title: Text(privacy.toString()), + ), + if (responseTime != null) + ListTile( + leading: const Icon(Icons.timer_outlined), + title: Text('${responseTime.inMilliseconds}ms'), + ), + ], + ), ); } } diff --git a/lib/pages/homeserver_picker/homeserver_picker.dart b/lib/pages/homeserver_picker/homeserver_picker.dart index 7e8f47a4..90bd682b 100644 --- a/lib/pages/homeserver_picker/homeserver_picker.dart +++ b/lib/pages/homeserver_picker/homeserver_picker.dart @@ -51,10 +51,11 @@ class HomeserverPickerController extends State { Hive.openBox('test').then((value) => null).catchError( (e, s) async { await showOkAlertDialog( - context: context, - title: L10n.of(context)!.indexedDbErrorTitle, - message: L10n.of(context)!.indexedDbErrorLong, - onWillPop: () async => false); + context: context, + title: L10n.of(context)!.indexedDbErrorTitle, + message: L10n.of(context)!.indexedDbErrorLong, + onWillPop: () async => false, + ); _checkTorBrowser(); }, ); @@ -85,9 +86,11 @@ class HomeserverPickerController extends State { }); List get filteredHomeservers => benchmarkResults! - .where((element) => - element.homeserver.baseUrl.host.contains(searchTerm) || - (element.homeserver.description?.contains(searchTerm) ?? false)) + .where( + (element) => + element.homeserver.baseUrl.host.contains(searchTerm) || + (element.homeserver.description?.contains(searchTerm) ?? false), + ) .toList(); void _loadHomeserverList() async { @@ -186,15 +189,16 @@ class HomeserverPickerController extends State { final file = await FilePickerCross.importFromStorage(); if (file.fileName == null) return; await showFutureLoadingDialog( - context: context, - future: () async { - try { - final client = Matrix.of(context).getLoginClient(); - await client.importDump(file.toString()); - Matrix.of(context).initMatrix(); - } catch (e, s) { - Logs().e('Future error:', e, s); - } - }); + context: context, + future: () async { + try { + final client = Matrix.of(context).getLoginClient(); + await client.importDump(file.toString()); + Matrix.of(context).initMatrix(); + } catch (e, s) { + Logs().e('Future error:', e, s); + } + }, + ); } } diff --git a/lib/pages/homeserver_picker/homeserver_picker_view.dart b/lib/pages/homeserver_picker/homeserver_picker_view.dart index 5542a623..47d79d6c 100644 --- a/lib/pages/homeserver_picker/homeserver_picker_view.dart +++ b/lib/pages/homeserver_picker/homeserver_picker_view.dart @@ -65,10 +65,11 @@ class HomeserverPickerView extends StatelessWidget { child: benchmarkResults == null ? const Center( child: Padding( - padding: EdgeInsets.all(12.0), - child: - CircularProgressIndicator.adaptive(), - )) + padding: EdgeInsets.all(12.0), + child: CircularProgressIndicator + .adaptive(), + ), + ) : Column( children: controller.filteredHomeservers .map( @@ -82,19 +83,20 @@ class HomeserverPickerView extends StatelessWidget { .showServerInfo(server), ), onTap: () => controller.setServer( - server - .homeserver.baseUrl.host), + server.homeserver.baseUrl.host, + ), title: Text( server.homeserver.baseUrl.host, style: const TextStyle( - color: Colors.black), + color: Colors.black, + ), ), subtitle: Text( server.homeserver.description ?? '', style: TextStyle( - color: - Colors.grey.shade700), + color: Colors.grey.shade700, + ), ), ), ) diff --git a/lib/pages/image_viewer/image_viewer_view.dart b/lib/pages/image_viewer/image_viewer_view.dart index c0970446..0bfdff2f 100644 --- a/lib/pages/image_viewer/image_viewer_view.dart +++ b/lib/pages/image_viewer/image_viewer_view.dart @@ -42,12 +42,13 @@ class ImageViewerView extends StatelessWidget { if (PlatformInfos.isMobile) // Use builder context to correctly position the share dialog on iPad Builder( - builder: (context) => IconButton( - onPressed: () => controller.shareFileAction(context), - tooltip: L10n.of(context)!.share, - color: Colors.white, - icon: Icon(Icons.adaptive.share_outlined), - )) + builder: (context) => IconButton( + onPressed: () => controller.shareFileAction(context), + tooltip: L10n.of(context)!.share, + color: Colors.white, + icon: Icon(Icons.adaptive.share_outlined), + ), + ) ], ), body: InteractiveViewer( diff --git a/lib/pages/invitation_selection/invitation_selection.dart b/lib/pages/invitation_selection/invitation_selection.dart index efdde84a..418ff878 100644 --- a/lib/pages/invitation_selection/invitation_selection.dart +++ b/lib/pages/invitation_selection/invitation_selection.dart @@ -71,8 +71,11 @@ class InvitationSelectionController extends State { future: () => room.invite(id), ); if (success.error == null) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(L10n.of(context)!.contactHasBeenInvitedToTheGroup))); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(L10n.of(context)!.contactHasBeenInvitedToTheGroup), + ), + ); } } @@ -99,7 +102,8 @@ class InvitationSelectionController extends State { response = await matrix.client.searchUserDirectory(text, limit: 10); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text((e).toLocalizedString(context)))); + SnackBar(content: Text((e).toLocalizedString(context))), + ); return; } finally { setState(() => loading = false); @@ -108,19 +112,25 @@ class InvitationSelectionController extends State { foundProfiles = List.from(response.results); if (text.isValidMatrixId && foundProfiles.indexWhere((profile) => text == profile.userId) == -1) { - setState(() => foundProfiles = [ - Profile.fromJson({'user_id': text}), - ]); + setState( + () => foundProfiles = [ + Profile.fromJson({'user_id': text}), + ], + ); } final participants = Matrix.of(context) .client .getRoomById(roomId!)! .getParticipants() - .where((user) => - [Membership.join, Membership.invite].contains(user.membership)) + .where( + (user) => + [Membership.join, Membership.invite].contains(user.membership), + ) .toList(); - foundProfiles.removeWhere((profile) => - participants.indexWhere((u) => u.id == profile.userId) != -1); + foundProfiles.removeWhere( + (profile) => + participants.indexWhere((u) => u.id == profile.userId) != -1, + ); }); } diff --git a/lib/pages/invitation_selection/invitation_selection_view.dart b/lib/pages/invitation_selection/invitation_selection_view.dart index d5288b64..0c5a111b 100644 --- a/lib/pages/invitation_selection/invitation_selection_view.dart +++ b/lib/pages/invitation_selection/invitation_selection_view.dart @@ -75,7 +75,9 @@ class InvitationSelectionView extends StatelessWidget { ), subtitle: Text(controller.foundProfiles[i].userId), onTap: () => controller.inviteAction( - context, controller.foundProfiles[i].userId), + context, + controller.foundProfiles[i].userId, + ), ), ) : FutureBuilder>( @@ -106,7 +108,8 @@ class InvitationSelectionView extends StatelessWidget { maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( - color: Theme.of(context).colorScheme.secondary), + color: Theme.of(context).colorScheme.secondary, + ), ), onTap: () => controller.inviteAction(context, contacts[i].id), diff --git a/lib/pages/key_verification/key_verification_dialog.dart b/lib/pages/key_verification/key_verification_dialog.dart index 94cb9f28..51939736 100644 --- a/lib/pages/key_verification/key_verification_dialog.dart +++ b/lib/pages/key_verification/key_verification_dialog.dart @@ -70,19 +70,20 @@ class KeyVerificationPageState extends State { if (input.isEmpty) return; final valid = await showFutureLoadingDialog( - context: context, - future: () async { - // make sure the loading spinner shows before we test the keys - await Future.delayed(const Duration(milliseconds: 100)); - var valid = false; - try { - await widget.request.openSSSS(keyOrPassphrase: input); - valid = true; - } catch (_) { - valid = false; - } - return valid; - }); + context: context, + future: () async { + // make sure the loading spinner shows before we test the keys + await Future.delayed(const Duration(milliseconds: 100)); + var valid = false; + try { + await widget.request.openSSSS(keyOrPassphrase: input); + valid = true; + } catch (_) { + valid = false; + } + return valid; + }, + ); if (valid.error != null) { await showOkAlertDialog( useRootNavigator: false, @@ -117,8 +118,10 @@ class KeyVerificationPageState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - Text(L10n.of(context)!.askSSSSSign, - style: const TextStyle(fontSize: 20)), + Text( + L10n.of(context)!.askSSSSSign, + style: const TextStyle(fontSize: 20), + ), Container(height: 10), TextField( controller: textEditingController, @@ -141,18 +144,22 @@ class KeyVerificationPageState extends State { ], ), ); - buttons.add(TextButton( - child: Text( - L10n.of(context)!.submit, + buttons.add( + TextButton( + child: Text( + L10n.of(context)!.submit, + ), + onPressed: () => checkInput(textEditingController.text), ), - onPressed: () => checkInput(textEditingController.text), - )); - buttons.add(TextButton( - child: Text( - L10n.of(context)!.skip, + ); + buttons.add( + TextButton( + child: Text( + L10n.of(context)!.skip, + ), + onPressed: () => widget.request.openSSSS(skip: true), ), - onPressed: () => widget.request.openSSSS(skip: true), - )); + ); break; case KeyVerificationState.askAccept: title = Text(L10n.of(context)!.newVerificationRequest); @@ -171,19 +178,23 @@ class KeyVerificationPageState extends State { ) ], ); - buttons.add(TextButton.icon( - icon: const Icon(Icons.close), - style: TextButton.styleFrom(foregroundColor: Colors.red), - label: Text(L10n.of(context)!.reject), - onPressed: () => widget.request - .rejectVerification() - .then((_) => Navigator.of(context, rootNavigator: false).pop()), - )); - buttons.add(TextButton.icon( - icon: const Icon(Icons.check), - label: Text(L10n.of(context)!.accept), - onPressed: () => widget.request.acceptVerification(), - )); + buttons.add( + TextButton.icon( + icon: const Icon(Icons.close), + style: TextButton.styleFrom(foregroundColor: Colors.red), + label: Text(L10n.of(context)!.reject), + onPressed: () => widget.request + .rejectVerification() + .then((_) => Navigator.of(context, rootNavigator: false).pop()), + ), + ); + buttons.add( + TextButton.icon( + icon: const Icon(Icons.check), + label: Text(L10n.of(context)!.accept), + onPressed: () => widget.request.acceptVerification(), + ), + ); break; case KeyVerificationState.waitingAccept: body = Center( @@ -245,19 +256,23 @@ class KeyVerificationPageState extends State { ), ], ); - buttons.add(TextButton.icon( - icon: const Icon(Icons.close), - style: TextButton.styleFrom( - foregroundColor: Colors.red, + buttons.add( + TextButton.icon( + icon: const Icon(Icons.close), + style: TextButton.styleFrom( + foregroundColor: Colors.red, + ), + label: Text(L10n.of(context)!.theyDontMatch), + onPressed: () => widget.request.rejectSas(), ), - label: Text(L10n.of(context)!.theyDontMatch), - onPressed: () => widget.request.rejectSas(), - )); - buttons.add(TextButton.icon( - icon: const Icon(Icons.check_outlined), - label: Text(L10n.of(context)!.theyMatch), - onPressed: () => widget.request.acceptSas(), - )); + ); + buttons.add( + TextButton.icon( + icon: const Icon(Icons.check_outlined), + label: Text(L10n.of(context)!.theyMatch), + onPressed: () => widget.request.acceptSas(), + ), + ); break; case KeyVerificationState.waitingSas: final acceptText = widget.request.sasTypes.contains('emoji') @@ -279,8 +294,11 @@ class KeyVerificationPageState extends State { body = Column( mainAxisSize: MainAxisSize.min, children: [ - const Icon(Icons.check_circle_outlined, - color: Colors.green, size: 128.0), + const Icon( + Icons.check_circle_outlined, + color: Colors.green, + size: 128.0, + ), const SizedBox(height: 10), Text( L10n.of(context)!.verifySuccess, @@ -288,12 +306,14 @@ class KeyVerificationPageState extends State { ), ], ); - buttons.add(TextButton( - child: Text( - L10n.of(context)!.close, + buttons.add( + TextButton( + child: Text( + L10n.of(context)!.close, + ), + onPressed: () => Navigator.of(context, rootNavigator: false).pop(), ), - onPressed: () => Navigator.of(context, rootNavigator: false).pop(), - )); + ); break; case KeyVerificationState.error: body = Column( @@ -307,12 +327,14 @@ class KeyVerificationPageState extends State { ), ], ); - buttons.add(TextButton( - child: Text( - L10n.of(context)!.close, + buttons.add( + TextButton( + child: Text( + L10n.of(context)!.close, + ), + onPressed: () => Navigator.of(context, rootNavigator: false).pop(), ), - onPressed: () => Navigator.of(context, rootNavigator: false).pop(), - )); + ); break; } return Scaffold( @@ -350,7 +372,8 @@ class _Emoji extends StatelessWidget { return emoji.name; } final translations = Map.from( - sasEmoji[emoji.number]['translated_descriptions']); + sasEmoji[emoji.number]['translated_descriptions'], + ); translations['en'] = emoji.name; for (final locale in window.locales) { final wantLocaleParts = locale.toString().split('_'); diff --git a/lib/pages/login/login.dart b/lib/pages/login/login.dart index b63d3fa9..d4d5d1f2 100644 --- a/lib/pages/login/login.dart +++ b/lib/pages/login/login.dart @@ -67,15 +67,17 @@ class LoginController extends State { } else { identifier = AuthenticationUserIdentifier(user: username); } - await matrix.getLoginClient().login(LoginType.mLoginPassword, - identifier: identifier, - // To stay compatible with older server versions - // ignore: deprecated_member_use - user: identifier.type == AuthenticationIdentifierTypes.userId - ? username - : null, - password: passwordController.text, - initialDeviceDisplayName: PlatformInfos.clientName); + await matrix.getLoginClient().login( + LoginType.mLoginPassword, + identifier: identifier, + // To stay compatible with older server versions + // ignore: deprecated_member_use + user: identifier.type == AuthenticationIdentifierTypes.userId + ? username + : null, + password: passwordController.text, + initialDeviceDisplayName: PlatformInfos.clientName, + ); } on MatrixException catch (exception) { setState(() => passwordError = exception.errorMessage); return setState(() => loading = false); @@ -121,7 +123,8 @@ class LoginController extends State { Matrix.of(context).getLoginClient().homeserver = oldHomeserver; // okay, the server we checked does not appear to be a matrix server Logs().v( - '$newDomain is not running a homeserver, asking to use $oldHomeserver'); + '$newDomain is not running a homeserver, asking to use $oldHomeserver', + ); final dialogResult = await showOkCancelAlertDialog( context: context, useRootNavigator: false, @@ -230,7 +233,8 @@ class LoginController extends State { ); if (success.error == null) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context)!.passwordHasBeenChanged))); + SnackBar(content: Text(L10n.of(context)!.passwordHasBeenChanged)), + ); usernameController.text = input.single; passwordController.text = password.single; login(); diff --git a/lib/pages/login/login_view.dart b/lib/pages/login/login_view.dart index 1644b84d..0b660f78 100644 --- a/lib/pages/login/login_view.dart +++ b/lib/pages/login/login_view.dart @@ -19,125 +19,131 @@ class LoginView extends StatelessWidget { automaticallyImplyLeading: !controller.loading, centerTitle: true, title: Text( - L10n.of(context)!.logInTo(Matrix.of(context) - .getLoginClient() - .homeserver - .toString() - .replaceFirst('https://', '')), + L10n.of(context)!.logInTo( + Matrix.of(context) + .getLoginClient() + .homeserver + .toString() + .replaceFirst('https://', ''), + ), ), ), - body: Builder(builder: (context) { - return AutofillGroup( - child: ListView( - children: [ - Padding( - padding: const EdgeInsets.all(12.0), - child: TextField( - readOnly: controller.loading, - autocorrect: false, - autofocus: true, - onChanged: controller.checkWellKnownWithCoolDown, - controller: controller.usernameController, - textInputAction: TextInputAction.next, - keyboardType: TextInputType.emailAddress, - autofillHints: - controller.loading ? null : [AutofillHints.username], - decoration: InputDecoration( - prefixIcon: const Icon(Icons.account_box_outlined), - errorText: controller.usernameError, - errorStyle: const TextStyle(color: Colors.orange), - hintText: L10n.of(context)!.emailOrUsername, - ), - ), - ), - Padding( - padding: const EdgeInsets.all(12.0), - child: TextField( - readOnly: controller.loading, - autocorrect: false, - autofillHints: - controller.loading ? null : [AutofillHints.password], - controller: controller.passwordController, - textInputAction: TextInputAction.go, - obscureText: !controller.showPassword, - onSubmitted: (_) => controller.login(), - decoration: InputDecoration( - prefixIcon: const Icon(Icons.lock_outlined), - errorText: controller.passwordError, - errorStyle: const TextStyle(color: Colors.orange), - suffixIcon: IconButton( - onPressed: controller.toggleShowPassword, - icon: Icon( - controller.showPassword - ? Icons.visibility_off_outlined - : Icons.visibility_outlined, - color: Colors.black, - ), + body: Builder( + builder: (context) { + return AutofillGroup( + child: ListView( + children: [ + Padding( + padding: const EdgeInsets.all(12.0), + child: TextField( + readOnly: controller.loading, + autocorrect: false, + autofocus: true, + onChanged: controller.checkWellKnownWithCoolDown, + controller: controller.usernameController, + textInputAction: TextInputAction.next, + keyboardType: TextInputType.emailAddress, + autofillHints: + controller.loading ? null : [AutofillHints.username], + decoration: InputDecoration( + prefixIcon: const Icon(Icons.account_box_outlined), + errorText: controller.usernameError, + errorStyle: const TextStyle(color: Colors.orange), + hintText: L10n.of(context)!.emailOrUsername, ), - hintText: L10n.of(context)!.password, ), ), - ), - Hero( - tag: 'signinButton', - child: Padding( + Padding( + padding: const EdgeInsets.all(12.0), + child: TextField( + readOnly: controller.loading, + autocorrect: false, + autofillHints: + controller.loading ? null : [AutofillHints.password], + controller: controller.passwordController, + textInputAction: TextInputAction.go, + obscureText: !controller.showPassword, + onSubmitted: (_) => controller.login(), + decoration: InputDecoration( + prefixIcon: const Icon(Icons.lock_outlined), + errorText: controller.passwordError, + errorStyle: const TextStyle(color: Colors.orange), + suffixIcon: IconButton( + onPressed: controller.toggleShowPassword, + icon: Icon( + controller.showPassword + ? Icons.visibility_off_outlined + : Icons.visibility_outlined, + color: Colors.black, + ), + ), + hintText: L10n.of(context)!.password, + ), + ), + ), + Hero( + tag: 'signinButton', + child: Padding( + padding: const EdgeInsets.all(12.0), + child: ElevatedButton.icon( + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of(context).colorScheme.primary, + foregroundColor: + Theme.of(context).colorScheme.onPrimary, + ), + onPressed: controller.loading ? null : controller.login, + icon: const Icon(Icons.login_outlined), + label: controller.loading + ? const LinearProgressIndicator() + : Text(L10n.of(context)!.login), + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Row( + children: [ + Expanded( + child: Divider( + thickness: 1, + color: Theme.of(context).dividerColor, + ), + ), + Padding( + padding: const EdgeInsets.all(12.0), + child: Text( + L10n.of(context)!.or, + style: const TextStyle(fontSize: 18), + ), + ), + Expanded( + child: Divider( + thickness: 1, + color: Theme.of(context).dividerColor, + ), + ), + ], + ), + ), + Padding( padding: const EdgeInsets.all(12.0), child: ElevatedButton.icon( + onPressed: controller.loading + ? () {} + : controller.passwordForgotten, style: ElevatedButton.styleFrom( - backgroundColor: Theme.of(context).colorScheme.primary, - foregroundColor: Theme.of(context).colorScheme.onPrimary, + foregroundColor: Theme.of(context).colorScheme.error, + backgroundColor: Theme.of(context).colorScheme.onError, ), - onPressed: controller.loading ? null : controller.login, - icon: const Icon(Icons.login_outlined), - label: controller.loading - ? const LinearProgressIndicator() - : Text(L10n.of(context)!.login), + icon: const Icon(Icons.safety_check_outlined), + label: Text(L10n.of(context)!.passwordForgotten), ), ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Row( - children: [ - Expanded( - child: Divider( - thickness: 1, - color: Theme.of(context).dividerColor, - ), - ), - Padding( - padding: const EdgeInsets.all(12.0), - child: Text( - L10n.of(context)!.or, - style: const TextStyle(fontSize: 18), - ), - ), - Expanded( - child: Divider( - thickness: 1, - color: Theme.of(context).dividerColor, - ), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.all(12.0), - child: ElevatedButton.icon( - onPressed: - controller.loading ? () {} : controller.passwordForgotten, - style: ElevatedButton.styleFrom( - foregroundColor: Theme.of(context).colorScheme.error, - backgroundColor: Theme.of(context).colorScheme.onError, - ), - icon: const Icon(Icons.safety_check_outlined), - label: Text(L10n.of(context)!.passwordForgotten), - ), - ), - ], - ), - ); - }), + ], + ), + ); + }, + ), ); } } diff --git a/lib/pages/new_group/new_group_view.dart b/lib/pages/new_group/new_group_view.dart index 69b27355..7aec730a 100644 --- a/lib/pages/new_group/new_group_view.dart +++ b/lib/pages/new_group/new_group_view.dart @@ -29,9 +29,10 @@ class NewGroupView extends StatelessWidget { textInputAction: TextInputAction.go, onSubmitted: controller.submitAction, decoration: InputDecoration( - labelText: L10n.of(context)!.optionalGroupName, - prefixIcon: const Icon(Icons.people_outlined), - hintText: L10n.of(context)!.enterAGroupName), + labelText: L10n.of(context)!.optionalGroupName, + prefixIcon: const Icon(Icons.people_outlined), + hintText: L10n.of(context)!.enterAGroupName, + ), ), ), SwitchListTile.adaptive( diff --git a/lib/pages/new_space/new_space_view.dart b/lib/pages/new_space/new_space_view.dart index 750302ea..ce4f967c 100644 --- a/lib/pages/new_space/new_space_view.dart +++ b/lib/pages/new_space/new_space_view.dart @@ -29,9 +29,10 @@ class NewSpaceView extends StatelessWidget { textInputAction: TextInputAction.go, onSubmitted: controller.submitAction, decoration: InputDecoration( - labelText: L10n.of(context)!.spaceName, - prefixIcon: const Icon(Icons.people_outlined), - hintText: L10n.of(context)!.enterASpacepName), + labelText: L10n.of(context)!.spaceName, + prefixIcon: const Icon(Icons.people_outlined), + hintText: L10n.of(context)!.enterASpacepName, + ), ), ), SwitchListTile.adaptive( diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart index af2903f5..575a8783 100644 --- a/lib/pages/settings/settings_view.dart +++ b/lib/pages/settings/settings_view.dart @@ -40,98 +40,100 @@ class SettingsView extends StatelessWidget { key: const Key('SettingsListViewContent'), children: [ FutureBuilder( - future: controller.profileFuture, - builder: (context, snapshot) { - final profile = snapshot.data; - final mxid = Matrix.of(context).client.userID ?? - L10n.of(context)!.user; - final displayname = - profile?.displayName ?? mxid.localpart ?? mxid; - return Row( - children: [ - Padding( - padding: const EdgeInsets.all(32.0), - child: Stack( - children: [ - Material( - elevation: Theme.of(context) - .appBarTheme - .scrolledUnderElevation ?? - 4, - shadowColor: - Theme.of(context).appBarTheme.shadowColor, - shape: RoundedRectangleBorder( - side: BorderSide( - color: Theme.of(context).dividerColor, - ), - borderRadius: BorderRadius.circular( - Avatar.defaultSize * 2.5), + future: controller.profileFuture, + builder: (context, snapshot) { + final profile = snapshot.data; + final mxid = + Matrix.of(context).client.userID ?? L10n.of(context)!.user; + final displayname = + profile?.displayName ?? mxid.localpart ?? mxid; + return Row( + children: [ + Padding( + padding: const EdgeInsets.all(32.0), + child: Stack( + children: [ + Material( + elevation: Theme.of(context) + .appBarTheme + .scrolledUnderElevation ?? + 4, + shadowColor: + Theme.of(context).appBarTheme.shadowColor, + shape: RoundedRectangleBorder( + side: BorderSide( + color: Theme.of(context).dividerColor, ), - child: Avatar( - mxContent: profile?.avatarUrl, - name: displayname, - size: Avatar.defaultSize * 2.5, - fontSize: 18 * 2.5, + borderRadius: BorderRadius.circular( + Avatar.defaultSize * 2.5, ), ), - if (profile != null) - Positioned( - bottom: 0, - right: 0, - child: FloatingActionButton.small( - onPressed: controller.setAvatarAction, - heroTag: null, - child: const Icon(Icons.camera_alt_outlined), - ), + child: Avatar( + mxContent: profile?.avatarUrl, + name: displayname, + size: Avatar.defaultSize * 2.5, + fontSize: 18 * 2.5, + ), + ), + if (profile != null) + Positioned( + bottom: 0, + right: 0, + child: FloatingActionButton.small( + onPressed: controller.setAvatarAction, + heroTag: null, + child: const Icon(Icons.camera_alt_outlined), ), - ], - ), + ), + ], ), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextButton.icon( - onPressed: controller.setDisplaynameAction, - icon: const Icon( - Icons.edit_outlined, - size: 16, - ), - style: TextButton.styleFrom( - foregroundColor: - Theme.of(context).colorScheme.onBackground, - ), - label: Text( - displayname, - maxLines: 1, - overflow: TextOverflow.ellipsis, - // style: const TextStyle(fontSize: 18), - ), + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextButton.icon( + onPressed: controller.setDisplaynameAction, + icon: const Icon( + Icons.edit_outlined, + size: 16, ), - TextButton.icon( - onPressed: () => FluffyShare.share(mxid, context), - icon: const Icon( - Icons.copy_outlined, - size: 14, - ), - style: TextButton.styleFrom( - foregroundColor: - Theme.of(context).colorScheme.secondary, - ), - label: Text( - mxid, - maxLines: 1, - overflow: TextOverflow.ellipsis, - // style: const TextStyle(fontSize: 12), - ), + style: TextButton.styleFrom( + foregroundColor: + Theme.of(context).colorScheme.onBackground, ), - ], - ), + label: Text( + displayname, + maxLines: 1, + overflow: TextOverflow.ellipsis, + // style: const TextStyle(fontSize: 18), + ), + ), + TextButton.icon( + onPressed: () => FluffyShare.share(mxid, context), + icon: const Icon( + Icons.copy_outlined, + size: 14, + ), + style: TextButton.styleFrom( + foregroundColor: + Theme.of(context).colorScheme.secondary, + ), + label: Text( + mxid, + maxLines: 1, + overflow: TextOverflow.ellipsis, + // style: const TextStyle(fontSize: 12), + ), + ), + ], ), - ], - ); - }), + ), + ], + ); + }, + ), const Divider(thickness: 1), if (showChatBackupBanner == null) ListTile( diff --git a/lib/pages/settings_3pid/settings_3pid.dart b/lib/pages/settings_3pid/settings_3pid.dart index 56a7a8d0..ff325693 100644 --- a/lib/pages/settings_3pid/settings_3pid.dart +++ b/lib/pages/settings_3pid/settings_3pid.dart @@ -79,11 +79,12 @@ class Settings3PidController extends State { return; } final success = await showFutureLoadingDialog( - context: context, - future: () => Matrix.of(context).client.delete3pidFromAccount( - identifier.address, - identifier.medium, - )); + context: context, + future: () => Matrix.of(context).client.delete3pidFromAccount( + identifier.address, + identifier.medium, + ), + ); if (success.error != null) return; setState(() => request = null); } diff --git a/lib/pages/settings_3pid/settings_3pid_view.dart b/lib/pages/settings_3pid/settings_3pid_view.dart index 07ed7a4c..e4cc3166 100644 --- a/lib/pages/settings_3pid/settings_3pid_view.dart +++ b/lib/pages/settings_3pid/settings_3pid_view.dart @@ -30,8 +30,10 @@ class Settings3PidView extends StatelessWidget { body: MaxWidthBody( child: FutureBuilder?>( future: controller.request, - builder: (BuildContext context, - AsyncSnapshot?> snapshot) { + builder: ( + BuildContext context, + AsyncSnapshot?> snapshot, + ) { if (snapshot.hasError) { return Center( child: Text( @@ -42,7 +44,8 @@ class Settings3PidView extends StatelessWidget { } if (!snapshot.hasData) { return const Center( - child: CircularProgressIndicator.adaptive(strokeWidth: 2)); + child: CircularProgressIndicator.adaptive(strokeWidth: 2), + ); } final identifier = snapshot.data!; return Column( @@ -71,10 +74,11 @@ class Settings3PidView extends StatelessWidget { itemCount: identifier.length, itemBuilder: (BuildContext context, int i) => ListTile( leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: Colors.grey, - child: Icon(identifier[i].iconData)), + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: Colors.grey, + child: Icon(identifier[i].iconData), + ), title: Text(identifier[i].address), trailing: IconButton( tooltip: L10n.of(context)!.delete, diff --git a/lib/pages/settings_emotes/settings_emotes.dart b/lib/pages/settings_emotes/settings_emotes.dart index 49a2aa5f..41e145d2 100644 --- a/lib/pages/settings_emotes/settings_emotes.dart +++ b/lib/pages/settings_emotes/settings_emotes.dart @@ -60,13 +60,20 @@ class EmotesSettingsController extends State { await showFutureLoadingDialog( context: context, future: () => client.setRoomStateWithKey( - room!.id, 'im.ponies.room_emotes', stateKey ?? '', pack!.toJson()), + room!.id, + 'im.ponies.room_emotes', + stateKey ?? '', + pack!.toJson(), + ), ); } else { await showFutureLoadingDialog( context: context, future: () => client.setAccountData( - client.userID!, 'im.ponies.user_emotes', pack!.toJson()), + client.userID!, + 'im.ponies.user_emotes', + pack!.toJson(), + ), ); } } @@ -95,7 +102,10 @@ class EmotesSettingsController extends State { await showFutureLoadingDialog( context: context, future: () => client.setAccountData( - client.userID!, 'im.ponies.emote_rooms', content), + client.userID!, + 'im.ponies.emote_rooms', + content, + ), ); setState(() {}); } @@ -197,7 +207,8 @@ class EmotesSettingsController extends State { } void imagePickerAction( - ValueNotifier controller) async { + ValueNotifier controller, + ) async { final result = await FilePickerCross.importFromStorage(type: FileTypeCross.image); if (result.fileName == null) return; diff --git a/lib/pages/settings_emotes/settings_emotes_view.dart b/lib/pages/settings_emotes/settings_emotes_view.dart index 43997fab..88d4cda1 100644 --- a/lib/pages/settings_emotes/settings_emotes_view.dart +++ b/lib/pages/settings_emotes/settings_emotes_view.dart @@ -140,16 +140,17 @@ class EmotesSettingsView extends StatelessWidget { actions: !useShortCuts ? {} : { - SubmitLineIntent: - CallbackAction(onInvoke: (i) { - controller.submitImageAction( - imageCode, - textEditingController.text, - image, - textEditingController, - ); - return null; - }), + SubmitLineIntent: CallbackAction( + onInvoke: (i) { + controller.submitImageAction( + imageCode, + textEditingController.text, + image, + textEditingController, + ); + return null; + }, + ), }, child: TextField( readOnly: controller.readonly, 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 05d97f0d..bd2a9769 100644 --- a/lib/pages/settings_ignore_list/settings_ignore_list_view.dart +++ b/lib/pages/settings_ignore_list/settings_ignore_list_view.dart @@ -58,34 +58,36 @@ class SettingsIgnoreListView extends StatelessWidget { const Divider(height: 1), Expanded( child: StreamBuilder( - stream: client.onAccountData.stream - .where((a) => a.type == 'm.ignored_user_list'), - builder: (context, snapshot) { - return ListView.builder( - itemCount: client.ignoredUsers.length, - itemBuilder: (c, i) => FutureBuilder( - future: - client.getProfileFromUserId(client.ignoredUsers[i]), - builder: (c, s) => ListTile( - leading: Avatar( - mxContent: s.data?.avatarUrl ?? Uri.parse(''), - name: s.data?.displayName ?? client.ignoredUsers[i], - ), - title: Text( - s.data?.displayName ?? client.ignoredUsers[i]), - trailing: IconButton( - tooltip: L10n.of(context)!.delete, - icon: const Icon(Icons.delete_forever_outlined), - onPressed: () => showFutureLoadingDialog( - context: context, - future: () => - client.unignoreUser(client.ignoredUsers[i]), - ), + stream: client.onAccountData.stream + .where((a) => a.type == 'm.ignored_user_list'), + builder: (context, snapshot) { + return ListView.builder( + itemCount: client.ignoredUsers.length, + itemBuilder: (c, i) => FutureBuilder( + future: + client.getProfileFromUserId(client.ignoredUsers[i]), + builder: (c, s) => ListTile( + leading: Avatar( + mxContent: s.data?.avatarUrl ?? Uri.parse(''), + name: s.data?.displayName ?? client.ignoredUsers[i], + ), + title: Text( + s.data?.displayName ?? client.ignoredUsers[i], + ), + trailing: IconButton( + tooltip: L10n.of(context)!.delete, + icon: const Icon(Icons.delete_forever_outlined), + onPressed: () => showFutureLoadingDialog( + context: context, + future: () => + client.unignoreUser(client.ignoredUsers[i]), ), ), ), - ); - }), + ), + ); + }, + ), ), ], ), diff --git a/lib/pages/settings_multiple_emotes/settings_multiple_emotes_view.dart b/lib/pages/settings_multiple_emotes/settings_multiple_emotes_view.dart index 45652f45..6a61b8df 100644 --- a/lib/pages/settings_multiple_emotes/settings_multiple_emotes_view.dart +++ b/lib/pages/settings_multiple_emotes/settings_multiple_emotes_view.dart @@ -32,27 +32,28 @@ class MultipleEmotesSettingsView extends StatelessWidget { final keys = packs.keys.toList(); keys.sort(); return ListView.separated( - separatorBuilder: (BuildContext context, int i) => Container(), - itemCount: keys.length, - itemBuilder: (BuildContext context, int i) { - final event = packs[keys[i]]; - String? packName = - keys[i].isNotEmpty ? keys[i] : 'Default Pack'; - if (event != null && event.content['pack'] is Map) { - if (event.content['pack']['displayname'] is String) { - packName = event.content['pack']['displayname']; - } else if (event.content['pack']['name'] is String) { - packName = event.content['pack']['name']; - } + separatorBuilder: (BuildContext context, int i) => Container(), + itemCount: keys.length, + itemBuilder: (BuildContext context, int i) { + final event = packs[keys[i]]; + String? packName = keys[i].isNotEmpty ? keys[i] : 'Default Pack'; + if (event != null && event.content['pack'] is Map) { + if (event.content['pack']['displayname'] is String) { + packName = event.content['pack']['displayname']; + } else if (event.content['pack']['name'] is String) { + packName = event.content['pack']['name']; } - return ListTile( - title: Text(packName!), - onTap: () async { - VRouter.of(context).toSegments( - ['rooms', room.id, 'details', 'emotes', keys[i]]); - }, - ); - }); + } + return ListTile( + title: Text(packName!), + onTap: () async { + VRouter.of(context).toSegments( + ['rooms', room.id, 'details', 'emotes', keys[i]], + ); + }, + ); + }, + ); }, ), ); diff --git a/lib/pages/settings_notifications/settings_notifications_view.dart b/lib/pages/settings_notifications/settings_notifications_view.dart index 919e1f66..57160da4 100644 --- a/lib/pages/settings_notifications/settings_notifications_view.dart +++ b/lib/pages/settings_notifications/settings_notifications_view.dart @@ -25,90 +25,94 @@ class SettingsNotificationsView extends StatelessWidget { body: MaxWidthBody( withScrolling: true, child: StreamBuilder( - stream: Matrix.of(context) - .client - .onAccountData - .stream - .where((event) => event.type == 'm.push_rules'), - builder: (BuildContext context, _) { - return Column( - children: [ - SwitchListTile.adaptive( - value: !Matrix.of(context).client.allPushNotificationsMuted, - title: Text( - L10n.of(context)!.notificationsEnabledForThisAccount), - onChanged: (_) => showFutureLoadingDialog( - context: context, - future: () => - Matrix.of(context).client.setMuteAllPushNotifications( - !Matrix.of(context) - .client - .allPushNotificationsMuted, - ), - ), + stream: Matrix.of(context) + .client + .onAccountData + .stream + .where((event) => event.type == 'm.push_rules'), + builder: (BuildContext context, _) { + return Column( + children: [ + SwitchListTile.adaptive( + value: !Matrix.of(context).client.allPushNotificationsMuted, + title: Text( + L10n.of(context)!.notificationsEnabledForThisAccount, ), - if (!Matrix.of(context).client.allPushNotificationsMuted) ...{ - const Divider(thickness: 1), - ListTile( - title: Text( - L10n.of(context)!.pushRules, - style: TextStyle( - color: Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.bold, + onChanged: (_) => showFutureLoadingDialog( + context: context, + future: () => Matrix.of(context) + .client + .setMuteAllPushNotifications( + !Matrix.of(context).client.allPushNotificationsMuted, ), - ), - ), - for (var item in NotificationSettingsItem.items) - SwitchListTile.adaptive( - value: controller.getNotificationSetting(item) ?? true, - title: Text(item.title(context)), - onChanged: (bool enabled) => - controller.setNotificationSetting(item, enabled), - ), - }, + ), + ), + if (!Matrix.of(context).client.allPushNotificationsMuted) ...{ const Divider(thickness: 1), ListTile( title: Text( - L10n.of(context)!.devices, + L10n.of(context)!.pushRules, style: TextStyle( color: Theme.of(context).colorScheme.secondary, fontWeight: FontWeight.bold, ), ), ), - FutureBuilder?>( - future: controller.pusherFuture ??= - Matrix.of(context).client.getPushers(), - builder: (context, snapshot) { - if (snapshot.hasError) { - Center( - child: Text( - snapshot.error!.toLocalizedString(context), - ), - ); - } - if (snapshot.connectionState != ConnectionState.done) { - const Center( - child: CircularProgressIndicator.adaptive( - strokeWidth: 2)); - } - final pushers = snapshot.data ?? []; - return ListView.builder( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: pushers.length, - itemBuilder: (_, i) => ListTile( - title: Text( - '${pushers[i].appDisplayName} - ${pushers[i].appId}'), - subtitle: Text(pushers[i].data.url.toString()), - onTap: () => controller.onPusherTap(pushers[i]), + for (var item in NotificationSettingsItem.items) + SwitchListTile.adaptive( + value: controller.getNotificationSetting(item) ?? true, + title: Text(item.title(context)), + onChanged: (bool enabled) => + controller.setNotificationSetting(item, enabled), + ), + }, + const Divider(thickness: 1), + ListTile( + title: Text( + L10n.of(context)!.devices, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.bold, + ), + ), + ), + FutureBuilder?>( + future: controller.pusherFuture ??= + Matrix.of(context).client.getPushers(), + builder: (context, snapshot) { + if (snapshot.hasError) { + Center( + child: Text( + snapshot.error!.toLocalizedString(context), ), ); - }, - ), - ], - ); - }), + } + if (snapshot.connectionState != ConnectionState.done) { + const Center( + child: CircularProgressIndicator.adaptive( + strokeWidth: 2, + ), + ); + } + final pushers = snapshot.data ?? []; + return ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: pushers.length, + itemBuilder: (_, i) => ListTile( + title: Text( + '${pushers[i].appDisplayName} - ${pushers[i].appId}', + ), + subtitle: Text(pushers[i].data.url.toString()), + onTap: () => controller.onPusherTap(pushers[i]), + ), + ); + }, + ), + ], + ); + }, + ), ), ); } diff --git a/lib/pages/settings_security/settings_security.dart b/lib/pages/settings_security/settings_security.dart index 265e534f..cc951b17 100644 --- a/lib/pages/settings_security/settings_security.dart +++ b/lib/pages/settings_security/settings_security.dart @@ -56,7 +56,8 @@ class SettingsSecurityController extends State { ); if (success.error == null) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context)!.passwordHasBeenChanged))); + SnackBar(content: Text(L10n.of(context)!.passwordHasBeenChanged)), + ); } } @@ -151,7 +152,8 @@ class SettingsSecurityController extends State { auth: AuthenticationPassword( password: input.single, identifier: AuthenticationUserIdentifier( - user: Matrix.of(context).client.userID!), + user: Matrix.of(context).client.userID!, + ), ), ), ); @@ -181,10 +183,11 @@ class SettingsSecurityController extends State { try { final export = await Matrix.of(context).client.exportDump(); final filePickerCross = FilePickerCross( - Uint8List.fromList(const Utf8Codec().encode(export!)), - path: - '/fluffychat-export-${DateFormat(DateFormat.YEAR_MONTH_DAY).format(DateTime.now())}.fluffybackup', - fileExtension: 'fluffybackup'); + Uint8List.fromList(const Utf8Codec().encode(export!)), + path: + '/fluffychat-export-${DateFormat(DateFormat.YEAR_MONTH_DAY).format(DateTime.now())}.fluffybackup', + fileExtension: 'fluffybackup', + ); await filePickerCross.exportToStorage( subject: L10n.of(context)!.dehydrateShare, ); diff --git a/lib/pages/settings_stories/settings_stories.dart b/lib/pages/settings_stories/settings_stories.dart index 6459349a..802c07d0 100644 --- a/lib/pages/settings_stories/settings_stories.dart +++ b/lib/pages/settings_stories/settings_stories.dart @@ -32,14 +32,15 @@ class SettingsStoriesController extends State { final blockList = room.client.storiesBlockList; blockList.add(user.id); await showFutureLoadingDialog( - context: context, - future: () async { - await user.kick(); - await room.client.setStoriesBlockList(blockList.toSet().toList()); - setState(() { - users[user] = false; - }); + context: context, + future: () async { + await user.kick(); + await room.client.setStoriesBlockList(blockList.toSet().toList()); + setState(() { + users[user] = false; }); + }, + ); return; } @@ -47,14 +48,15 @@ class SettingsStoriesController extends State { final blockList = room.client.storiesBlockList; blockList.remove(user.id); await showFutureLoadingDialog( - context: context, - future: () async { - await room.client.setStoriesBlockList(blockList); - await room.invite(user.id); - setState(() { - users[user] = true; - }); + context: context, + future: () async { + await room.client.setStoriesBlockList(blockList); + await room.invite(user.id); + setState(() { + users[user] = true; }); + }, + ); return; } diff --git a/lib/pages/settings_stories/settings_stories_view.dart b/lib/pages/settings_stories/settings_stories_view.dart index d502373c..31524eef 100644 --- a/lib/pages/settings_stories/settings_stories_view.dart +++ b/lib/pages/settings_stories/settings_stories_view.dart @@ -38,9 +38,10 @@ class SettingsStoriesView extends StatelessWidget { } if (snapshot.connectionState != ConnectionState.done) { return const Center( - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, - )); + child: CircularProgressIndicator.adaptive( + strokeWidth: 2, + ), + ); } return ListView.builder( itemCount: controller.users.length, diff --git a/lib/pages/settings_style/settings_style_view.dart b/lib/pages/settings_style/settings_style_view.dart index bc8adc43..4b5e67e2 100644 --- a/lib/pages/settings_style/settings_style_view.dart +++ b/lib/pages/settings_style/settings_style_view.dart @@ -59,16 +59,18 @@ class SettingsStyleView extends StatelessWidget { borderRadius: BorderRadius.circular(colorPickerSize), child: SizedBox( - width: colorPickerSize, - height: colorPickerSize, - child: controller.currentColor == color - ? const Center( - child: Icon( + width: colorPickerSize, + height: colorPickerSize, + child: controller.currentColor == color + ? const Center( + child: Icon( Icons.check, size: 16, color: Colors.white, - )) - : null), + ), + ) + : null, + ), ), ), ), @@ -118,16 +120,18 @@ class SettingsStyleView extends StatelessWidget { ), onTap: controller.deleteWallpaperAction, ), - Builder(builder: (context) { - return ListTile( - title: Text(L10n.of(context)!.changeWallpaper), - trailing: Icon( - Icons.photo_outlined, - color: Theme.of(context).textTheme.bodyLarge?.color, - ), - onTap: controller.setWallpaperAction, - ); - }), + Builder( + builder: (context) { + return ListTile( + title: Text(L10n.of(context)!.changeWallpaper), + trailing: Icon( + Icons.photo_outlined, + color: Theme.of(context).textTheme.bodyLarge?.color, + ), + onTap: controller.setWallpaperAction, + ); + }, + ), const Divider(height: 1), ListTile( title: Text( diff --git a/lib/pages/story/story_page.dart b/lib/pages/story/story_page.dart index 6d728118..2db5c5b7 100644 --- a/lib/pages/story/story_page.dart +++ b/lib/pages/story/story_page.dart @@ -46,10 +46,11 @@ class StoryPageController extends State { Timeline? timeline; Event? get currentEvent => index < events.length ? events[index] : null; - StoryThemeData get storyThemeData => - StoryThemeData.fromJson(currentEvent?.content - .tryGetMap(StoryThemeData.contentKey) ?? - {}); + StoryThemeData get storyThemeData => StoryThemeData.fromJson( + currentEvent?.content + .tryGetMap(StoryThemeData.contentKey) ?? + {}, + ); bool replyLoading = false; bool _modalOpened = false; @@ -83,8 +84,9 @@ class StoryPageController extends State { final client = Matrix.of(context).client; final roomId = await client.startDirectChat(currentEvent.senderId); var replyText = L10n.of(context)!.storyFrom( - currentEvent.originServerTs.localizedTime(context), - currentEvent.content.tryGet('body') ?? ''); + currentEvent.originServerTs.localizedTime(context), + currentEvent.content.tryGet('body') ?? '', + ); replyText = replyText.split('\n').map((line) => '> $line').join('\n'); message = '$replyText\n\n$message'; await client.getRoomById(roomId)!.sendTextEvent(message); @@ -307,33 +309,35 @@ class StoryPageController extends State { final event = currentEvent; if (event == null) return; final score = await showConfirmationDialog( - context: context, - title: L10n.of(context)!.reportMessage, - message: L10n.of(context)!.howOffensiveIsThisContent, - cancelLabel: L10n.of(context)!.cancel, - okLabel: L10n.of(context)!.ok, - actions: [ - AlertDialogAction( - key: -100, - label: L10n.of(context)!.extremeOffensive, - ), - AlertDialogAction( - key: -50, - label: L10n.of(context)!.offensive, - ), - AlertDialogAction( - key: 0, - label: L10n.of(context)!.inoffensive, - ), - ]); + context: context, + title: L10n.of(context)!.reportMessage, + message: L10n.of(context)!.howOffensiveIsThisContent, + cancelLabel: L10n.of(context)!.cancel, + okLabel: L10n.of(context)!.ok, + actions: [ + AlertDialogAction( + key: -100, + label: L10n.of(context)!.extremeOffensive, + ), + AlertDialogAction( + key: -50, + label: L10n.of(context)!.offensive, + ), + AlertDialogAction( + key: 0, + label: L10n.of(context)!.inoffensive, + ), + ], + ); if (score == null) return; final reason = await showTextInputDialog( - useRootNavigator: false, - context: context, - title: L10n.of(context)!.whyDoYouWantToReportThis, - okLabel: L10n.of(context)!.ok, - cancelLabel: L10n.of(context)!.cancel, - textFields: [DialogTextField(hintText: L10n.of(context)!.reason)]); + useRootNavigator: false, + context: context, + title: L10n.of(context)!.whyDoYouWantToReportThis, + okLabel: L10n.of(context)!.ok, + cancelLabel: L10n.of(context)!.cancel, + textFields: [DialogTextField(hintText: L10n.of(context)!.reason)], + ); if (reason == null || reason.single.isEmpty) return; final result = await showFutureLoadingDialog( context: context, @@ -352,7 +356,9 @@ class StoryPageController extends State { } Future downloadAndDecryptAttachment( - Event event, bool getThumbnail) async { + Event event, + bool getThumbnail, + ) async { return _fileCache[event.eventId] ??= event.downloadAndDecryptAttachment(getThumbnail: getThumbnail); } @@ -400,10 +406,12 @@ class StoryPageController extends State { final timeline = this.timeline = await room.getTimeline(); timeline.requestKeys(); var events = timeline.events - .where((e) => - e.type == EventTypes.Message && - !e.redacted && - e.status == EventStatus.synced) + .where( + (e) => + e.type == EventTypes.Message && + !e.redacted && + e.status == EventStatus.synced, + ) .toList(); final hasOutdatedEvents = events.removeOutdatedEvents(); @@ -432,12 +440,16 @@ class StoryPageController extends State { // Preload images and videos events - .where((event) => {MessageTypes.Image, MessageTypes.Video} - .contains(event.messageType)) - .forEach((event) => downloadAndDecryptAttachment( + .where( + (event) => {MessageTypes.Image, MessageTypes.Video} + .contains(event.messageType), + ) + .forEach( + (event) => downloadAndDecryptAttachment( event, - event.messageType == MessageTypes.Video && - PlatformInfos.isMobile)); + event.messageType == MessageTypes.Video && PlatformInfos.isMobile, + ), + ); // Reverse list this.events.clear(); @@ -502,9 +514,11 @@ class StoryPageController extends State { extension on List { bool removeOutdatedEvents() { - final outdatedIndex = indexWhere((event) => - DateTime.now().difference(event.originServerTs).inHours > - ClientStoriesExtension.lifeTimeInHours); + final outdatedIndex = indexWhere( + (event) => + DateTime.now().difference(event.originServerTs).inHours > + ClientStoriesExtension.lifeTimeInHours, + ); if (outdatedIndex != -1) { removeRange(outdatedIndex, length); return true; diff --git a/lib/pages/story/story_view.dart b/lib/pages/story/story_view.dart index 18725384..bc7315fe 100644 --- a/lib/pages/story/story_view.dart +++ b/lib/pages/story/story_view.dart @@ -144,9 +144,10 @@ class StoryView extends StatelessWidget { final events = controller.events; if (snapshot.connectionState != ConnectionState.done) { return const Center( - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, - )); + child: CircularProgressIndicator.adaptive( + strokeWidth: 2, + ), + ); } if (events.isEmpty) { return Center( @@ -218,7 +219,9 @@ class StoryView extends StatelessWidget { !PlatformInfos.isMobile)) FutureBuilder( future: controller.downloadAndDecryptAttachment( - event, event.messageType == MessageTypes.Video), + event, + event.messageType == MessageTypes.Video, + ), builder: (context, snapshot) { final matrixFile = snapshot.data; if (matrixFile == null) { @@ -364,7 +367,8 @@ class StoryView extends StatelessWidget { height: 16, child: Center( child: CircularProgressIndicator.adaptive( - strokeWidth: 2), + strokeWidth: 2, + ), ), ) : IconButton( diff --git a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart index daf4791e..1be9b1fc 100644 --- a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart +++ b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart @@ -52,33 +52,35 @@ class UserBottomSheetController extends State { case UserBottomSheetAction.report: final event = widget.user; final score = await showConfirmationDialog( - context: context, - title: L10n.of(context)!.reportUser, - message: L10n.of(context)!.howOffensiveIsThisContent, - cancelLabel: L10n.of(context)!.cancel, - okLabel: L10n.of(context)!.ok, - actions: [ - AlertDialogAction( - key: -100, - label: L10n.of(context)!.extremeOffensive, - ), - AlertDialogAction( - key: -50, - label: L10n.of(context)!.offensive, - ), - AlertDialogAction( - key: 0, - label: L10n.of(context)!.inoffensive, - ), - ]); + context: context, + title: L10n.of(context)!.reportUser, + message: L10n.of(context)!.howOffensiveIsThisContent, + cancelLabel: L10n.of(context)!.cancel, + okLabel: L10n.of(context)!.ok, + actions: [ + AlertDialogAction( + key: -100, + label: L10n.of(context)!.extremeOffensive, + ), + AlertDialogAction( + key: -50, + label: L10n.of(context)!.offensive, + ), + AlertDialogAction( + key: 0, + label: L10n.of(context)!.inoffensive, + ), + ], + ); if (score == null) return; final reason = await showTextInputDialog( - useRootNavigator: false, - context: context, - title: L10n.of(context)!.whyDoYouWantToReportThis, - okLabel: L10n.of(context)!.ok, - cancelLabel: L10n.of(context)!.cancel, - textFields: [DialogTextField(hintText: L10n.of(context)!.reason)]); + useRootNavigator: false, + context: context, + title: L10n.of(context)!.whyDoYouWantToReportThis, + okLabel: L10n.of(context)!.ok, + cancelLabel: L10n.of(context)!.cancel, + textFields: [DialogTextField(hintText: L10n.of(context)!.reason)], + ); if (reason == null || reason.single.isEmpty) return; final result = await showFutureLoadingDialog( context: context, @@ -91,7 +93,8 @@ class UserBottomSheetController extends State { ); if (result.error != null) return; ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context)!.contentHasBeenReported))); + SnackBar(content: Text(L10n.of(context)!.contentHasBeenReported)), + ); break; case UserBottomSheetAction.mention: Navigator.of(context, rootNavigator: false).pop(); @@ -151,9 +154,9 @@ class UserBottomSheetController extends State { case UserBottomSheetAction.ignore: if (await askConfirmation()) { await showFutureLoadingDialog( - context: context, - future: () => - Matrix.of(context).client.ignoreUser(widget.user.id)); + context: context, + future: () => Matrix.of(context).client.ignoreUser(widget.user.id), + ); } } } diff --git a/lib/utils/account_bundles.dart b/lib/utils/account_bundles.dart index f45f9dc7..8bd296e0 100644 --- a/lib/utils/account_bundles.dart +++ b/lib/utils/account_bundles.dart @@ -55,10 +55,12 @@ extension AccountBundlesExtension on Client { } ret ??= []; if (ret.isEmpty) { - ret.add(AccountBundle( - name: userID, - priority: 0, - )); + ret.add( + AccountBundle( + name: userID, + priority: 0, + ), + ); } return ret; } diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart index 49d8178b..5f5b7618 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -78,7 +78,8 @@ class BackgroundPush { firebase?.setListeners( onMessage: (message) => pushHelper( PushNotification.fromJson( - Map.from(message['data'] ?? message)), + Map.from(message['data'] ?? message), + ), client: client, l10n: l10n, activeRoomId: router?.currentState?.pathParameters['roomid'], @@ -331,7 +332,8 @@ class BackgroundPush { } } catch (e) { Logs().i( - '[Push] No self-hosted unified push gateway present: $newEndpoint'); + '[Push] No self-hosted unified push gateway present: $newEndpoint', + ); } Logs().i('[Push] UnifiedPush using endpoint $endpoint'); final oldTokens = {}; @@ -366,7 +368,8 @@ class BackgroundPush { Future _onUpMessage(Uint8List message, String i) async { upAction = true; final data = Map.from( - json.decode(utf8.decode(message))['notification']); + json.decode(utf8.decode(message))['notification'], + ); // UP may strip the devices list data['devices'] ??= []; await pushHelper( @@ -382,8 +385,11 @@ class BackgroundPush { /// IDs we map the [roomId] to a number and store this number. late Map idMap; Future _loadIdMap() async { - idMap = Map.from(json.decode( - (await store.getItem(SettingKeys.notificationCurrentIds)) ?? '{}')); + idMap = Map.from( + json.decode( + (await store.getItem(SettingKeys.notificationCurrentIds)) ?? '{}', + ), + ); } Future mapRoomIdToInt(String roomId) async { @@ -441,7 +447,8 @@ class BackgroundPush { if (syncErrored) { try { Logs().v( - '[Push] failed to sync for fallback push, fetching notifications endpoint...'); + '[Push] failed to sync for fallback push, fetching notifications endpoint...', + ); final notifications = await client.getNotifications(limit: 20); final notificationRooms = notifications.notifications.map((n) => n.roomId).toSet(); @@ -450,8 +457,9 @@ class BackgroundPush { .map((r) => r.id); } catch (e) { Logs().v( - '[Push] failed to fetch pending notifications for clearing push, falling back...', - e); + '[Push] failed to fetch pending notifications for clearing push, falling back...', + e, + ); emptyRooms = client.rooms .where((r) => r.notificationCount == 0) .map((r) => r.id); @@ -474,7 +482,9 @@ class BackgroundPush { } if (changed) { await store.setItem( - SettingKeys.notificationCurrentIds, json.encode(idMap)); + SettingKeys.notificationCurrentIds, + json.encode(idMap), + ); } } finally { _clearingPushLock = false; diff --git a/lib/utils/client_manager.dart b/lib/utils/client_manager.dart index d68f3fb3..43be8c6a 100644 --- a/lib/utils/client_manager.dart +++ b/lib/utils/client_manager.dart @@ -39,19 +39,25 @@ abstract class ClientManager { } final clients = clientNames.map(createClient).toList(); if (initialize) { - await Future.wait(clients.map((client) => client - .init( - waitForFirstSync: false, - waitUntilLoadCompletedLoaded: false, - ) - .catchError( - (e, s) => Logs().e('Unable to initialize client', e, s)))); + await Future.wait( + clients.map( + (client) => client + .init( + waitForFirstSync: false, + waitUntilLoadCompletedLoaded: false, + ) + .catchError( + (e, s) => Logs().e('Unable to initialize client', e, s), + ), + ), + ); } if (clients.length > 1 && clients.any((c) => !c.isLogged())) { final loggedOutClients = clients.where((c) => !c.isLogged()).toList(); for (final client in loggedOutClients) { Logs().w( - 'Multi account is enabled but client ${client.userID} is not logged in. Removing...'); + 'Multi account is enabled but client ${client.userID} is not logged in. Removing...', + ); clientNames.remove(client.clientName); clients.remove(client); } diff --git a/lib/utils/custom_image_resizer.dart b/lib/utils/custom_image_resizer.dart index 677d7e8e..48b803a9 100644 --- a/lib/utils/custom_image_resizer.dart +++ b/lib/utils/custom_image_resizer.dart @@ -5,7 +5,8 @@ import 'package:matrix/matrix.dart'; import 'package:native_imaging/native_imaging.dart' as native; Future customImageResizer( - MatrixImageFileResizeArguments arguments) async { + MatrixImageFileResizeArguments arguments, +) async { await native.init(); late native.Image nativeImg; @@ -21,7 +22,10 @@ Future customImageResizer( return null; } final rgba = Uint8List.view( - rgbaData.buffer, rgbaData.offsetInBytes, rgbaData.lengthInBytes); + rgbaData.buffer, + rgbaData.offsetInBytes, + rgbaData.lengthInBytes, + ); final width = dartFrame.image.width; final height = dartFrame.image.height; diff --git a/lib/utils/date_time_extension.dart b/lib/utils/date_time_extension.dart index e37b4f0e..dcc61a62 100644 --- a/lib/utils/date_time_extension.dart +++ b/lib/utils/date_time_extension.dart @@ -77,10 +77,15 @@ extension DateTimeExtension on DateTime { } } else if (sameYear) { return L10n.of(context)!.dateWithoutYear( - month.toString().padLeft(2, '0'), day.toString().padLeft(2, '0')); + month.toString().padLeft(2, '0'), + day.toString().padLeft(2, '0'), + ); } - return L10n.of(context)!.dateWithYear(year.toString(), - month.toString().padLeft(2, '0'), day.toString().padLeft(2, '0')); + return L10n.of(context)!.dateWithYear( + year.toString(), + month.toString().padLeft(2, '0'), + day.toString().padLeft(2, '0'), + ); } /// If the DateTime is today, this returns [localizedTimeOfDay()], if not it also @@ -95,7 +100,9 @@ extension DateTimeExtension on DateTime { if (sameDay) return localizedTimeOfDay(context); return L10n.of(context)!.dateAndTimeOfDay( - localizedTimeShort(context), localizedTimeOfDay(context)); + localizedTimeShort(context), + localizedTimeOfDay(context), + ); } static String _z(int i) => i < 10 ? '0${i.toString()}' : i.toString(); diff --git a/lib/utils/fluffy_share.dart b/lib/utils/fluffy_share.dart index 630803b3..b5f1321b 100644 --- a/lib/utils/fluffy_share.dart +++ b/lib/utils/fluffy_share.dart @@ -19,7 +19,8 @@ abstract class FluffyShare { ClipboardData(text: text), ); ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context)!.copiedToClipboard))); + SnackBar(content: Text(L10n.of(context)!.copiedToClipboard)), + ); return; } } diff --git a/lib/utils/matrix_sdk_extensions/client_stories_extension.dart b/lib/utils/matrix_sdk_extensions/client_stories_extension.dart index 281ab323..413ef31c 100644 --- a/lib/utils/matrix_sdk_extensions/client_stories_extension.dart +++ b/lib/utils/matrix_sdk_extensions/client_stories_extension.dart @@ -15,8 +15,10 @@ extension ClientStoriesExtension on Client { List get contacts => rooms .where((room) => room.isDirectChat) - .map((room) => - room.unsafeGetUserFromMemoryOrFallback(room.directChatMatrixID!)) + .map( + (room) => + room.unsafeGetUserFromMemoryOrFallback(room.directChatMatrixID!), + ) .toList(); List get storiesRooms => @@ -78,23 +80,30 @@ extension ClientStoriesExtension on Client { } Future getStoriesRoom(BuildContext context) async { - final candidates = rooms.where((room) => - room.getState(EventTypes.RoomCreate)?.content.tryGet('type') == - storiesRoomType && - room.ownPowerLevel >= 100); + final candidates = rooms.where( + (room) => + room + .getState(EventTypes.RoomCreate) + ?.content + .tryGet('type') == + storiesRoomType && + room.ownPowerLevel >= 100, + ); if (candidates.isEmpty) return null; if (candidates.length == 1) return candidates.single; return await showModalActionSheet( - context: context, - actions: candidates - .map( - (room) => SheetAction( - label: room.getLocalizedDisplayname( - MatrixLocals(L10n.of(context)!), - ), - key: room), - ) - .toList()); + context: context, + actions: candidates + .map( + (room) => SheetAction( + label: room.getLocalizedDisplayname( + MatrixLocals(L10n.of(context)!), + ), + key: room, + ), + ) + .toList(), + ); } } diff --git a/lib/utils/matrix_sdk_extensions/flutter_hive_collections_database.dart b/lib/utils/matrix_sdk_extensions/flutter_hive_collections_database.dart index b902231f..932763c5 100644 --- a/lib/utils/matrix_sdk_extensions/flutter_hive_collections_database.dart +++ b/lib/utils/matrix_sdk_extensions/flutter_hive_collections_database.dart @@ -23,7 +23,8 @@ class FlutterHiveCollectionsDatabase extends HiveCollectionsDatabase { static const String _cipherStorageKey = 'hive_encryption_key'; static Future databaseBuilder( - Client client) async { + Client client, + ) async { Logs().d('Open Hive...'); HiveAesCipher? hiverCipher; try { @@ -96,9 +97,9 @@ class FlutterHiveCollectionsDatabase extends HiveCollectionsDatabase { } } // do not destroy your stable FluffyChat in debug mode - directory = Directory(directory.uri - .resolve(kDebugMode ? 'hive_debug' : 'hive') - .toFilePath()); + directory = Directory( + directory.uri.resolve(kDebugMode ? 'hive_debug' : 'hive').toFilePath(), + ); directory.create(recursive: true); path = directory.path; } diff --git a/lib/utils/matrix_sdk_extensions/ios_badge_client_extension.dart b/lib/utils/matrix_sdk_extensions/ios_badge_client_extension.dart index 6c2e4d12..bea7713d 100644 --- a/lib/utils/matrix_sdk_extensions/ios_badge_client_extension.dart +++ b/lib/utils/matrix_sdk_extensions/ios_badge_client_extension.dart @@ -8,8 +8,9 @@ extension IosBadgeClientExtension on Client { void updateIosBadge() { if (PlatformInfos.isIOS) { // Workaround for iOS not clearing notifications with fcm_shared_isolate - if (!rooms.any((r) => - r.membership == Membership.invite || (r.notificationCount > 0))) { + if (!rooms.any( + (r) => r.membership == Membership.invite || (r.notificationCount > 0), + )) { // ignore: unawaited_futures FlutterLocalNotificationsPlugin().cancelAll(); FlutterAppBadger.removeBadge(); diff --git a/lib/utils/matrix_sdk_extensions/matrix_locals.dart b/lib/utils/matrix_sdk_extensions/matrix_locals.dart index a90e1e13..fdf2030d 100644 --- a/lib/utils/matrix_sdk_extensions/matrix_locals.dart +++ b/lib/utils/matrix_sdk_extensions/matrix_locals.dart @@ -62,7 +62,9 @@ class MatrixLocals extends MatrixLocalizations { @override String changedTheGuestAccessRulesTo( - String senderName, String localizedString) { + String senderName, + String localizedString, + ) { return l10n.changedTheGuestAccessRulesTo(senderName, localizedString); } @@ -73,7 +75,9 @@ class MatrixLocals extends MatrixLocalizations { @override String changedTheHistoryVisibilityTo( - String senderName, String localizedString) { + String senderName, + String localizedString, + ) { return l10n.changedTheHistoryVisibilityTo(senderName, localizedString); } diff --git a/lib/utils/push_helper.dart b/lib/utils/push_helper.dart index fb1fbe63..80b4132d 100644 --- a/lib/utils/push_helper.dart +++ b/lib/utils/push_helper.dart @@ -111,7 +111,9 @@ Future _tryPushHelper( await flutterLocalNotificationsPlugin.cancelAll(); final store = await SharedPreferences.getInstance(); await store.setString( - SettingKeys.notificationCurrentIds, json.encode({})); + SettingKeys.notificationCurrentIds, + json.encode({}), + ); } } return; @@ -237,7 +239,8 @@ Future _tryPushHelper( Future mapRoomIdToInt(String roomId) async { final store = await SharedPreferences.getInstance(); final idMap = Map.from( - jsonDecode(store.getString(SettingKeys.notificationCurrentIds) ?? '{}')); + jsonDecode(store.getString(SettingKeys.notificationCurrentIds) ?? '{}'), + ); int? currentInt; try { currentInt = idMap[roomId]; diff --git a/lib/utils/room_status_extension.dart b/lib/utils/room_status_extension.dart index 14b6080f..3b4e623f 100644 --- a/lib/utils/room_status_extension.dart +++ b/lib/utils/room_status_extension.dart @@ -54,12 +54,14 @@ extension RoomStatusExtension on Room { } } else if (typingUsers.length == 2) { typingText = L10n.of(context)!.userAndUserAreTyping( - typingUsers.first.calcDisplayname(), - typingUsers[1].calcDisplayname()); + typingUsers.first.calcDisplayname(), + typingUsers[1].calcDisplayname(), + ); } else if (typingUsers.length > 2) { typingText = L10n.of(context)!.userAndOthersAreTyping( - typingUsers.first.calcDisplayname(), - (typingUsers.length - 1).toString()); + typingUsers.first.calcDisplayname(), + (typingUsers.length - 1).toString(), + ); } return typingText; } @@ -76,8 +78,10 @@ extension RoomStatusExtension on Room { break; } } - lastReceipts.removeWhere((user) => - user.id == client.userID || user.id == timeline.events.first.senderId); + lastReceipts.removeWhere( + (user) => + user.id == client.userID || user.id == timeline.events.first.senderId, + ); return lastReceipts.toList(); } } diff --git a/lib/utils/stream_extension.dart b/lib/utils/stream_extension.dart index 685b78f6..66e840fc 100644 --- a/lib/utils/stream_extension.dart +++ b/lib/utils/stream_extension.dart @@ -33,9 +33,11 @@ extension StreamExtension on Stream { gotMessage = true; } }; - final subscription = listen((_) => onMessage?.call(), - onDone: () => controller.close(), - onError: (e, s) => controller.addError(e, s)); + final subscription = listen( + (_) => onMessage?.call(), + onDone: () => controller.close(), + onError: (e, s) => controller.addError(e, s), + ); // add proper cleanup to the subscription and the controller, to not memory leak controller.onCancel = () { subscription.cancel(); diff --git a/lib/utils/uia_request_manager.dart b/lib/utils/uia_request_manager.dart index 5d108134..5c95844a 100644 --- a/lib/utils/uia_request_manager.dart +++ b/lib/utils/uia_request_manager.dart @@ -82,7 +82,8 @@ extension UiaRequestManager on MatrixState { ); default: final url = Uri.parse( - '${client.homeserver}/_matrix/client/r0/auth/$stage/fallback/web?session=${uiaRequest.session}'); + '${client.homeserver}/_matrix/client/r0/auth/$stage/fallback/web?session=${uiaRequest.session}', + ); launchUrlString(url.toString()); if (OkCancelResult.ok == await showOkCancelAlertDialog( diff --git a/lib/utils/update_checker_no_store.dart b/lib/utils/update_checker_no_store.dart index 1a3250d7..d41bc07f 100644 --- a/lib/utils/update_checker_no_store.dart +++ b/lib/utils/update_checker_no_store.dart @@ -20,7 +20,8 @@ class UpdateCheckerNoStore { static const gitLabHost = 'gitlab.com'; static Uri get tagsUri => Uri.parse( - 'https://$gitLabHost/projects/$gitLabProjectId/repository/tags'); + 'https://$gitLabHost/projects/$gitLabProjectId/repository/tags', + ); final BuildContext context; diff --git a/lib/utils/url_launcher.dart b/lib/utils/url_launcher.dart index 7e42e8e5..5329d32f 100644 --- a/lib/utils/url_launcher.dart +++ b/lib/utils/url_launcher.dart @@ -33,7 +33,8 @@ class UrlLauncher { if (uri == null) { // we can't open this thing ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context)!.cantOpenUri(url!)))); + SnackBar(content: Text(L10n.of(context)!.cantOpenUri(url!))), + ); return; } if (!{'https', 'http'}.contains(uri.scheme)) { @@ -61,7 +62,8 @@ class UrlLauncher { // transmute geo URIs on desktop to openstreetmap links, as those usually can't handle // geo URIs launchUrlString( - 'https://www.openstreetmap.org/?mlat=${latlong.first}&mlon=${latlong.last}#map=16/${latlong.first}/${latlong.last}'); + 'https://www.openstreetmap.org/?mlat=${latlong.first}&mlon=${latlong.last}#map=16/${latlong.first}/${latlong.last}', + ); } return; } @@ -71,7 +73,8 @@ class UrlLauncher { } if (uri.host.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context)!.cantOpenUri(url!)))); + SnackBar(content: Text(L10n.of(context)!.cantOpenUri(url!))), + ); return; } // okay, we have either an http or an https URI. @@ -86,8 +89,10 @@ class UrlLauncher { }).join('.'); // Force LaunchMode.externalApplication, otherwise url_launcher will default // to opening links in a webview on mobile platforms. - launchUrlString(uri.replace(host: newHost).toString(), - mode: LaunchMode.externalApplication); + launchUrlString( + uri.replace(host: newHost).toString(), + mode: LaunchMode.externalApplication, + ); } void openMatrixToUrl() async { @@ -142,8 +147,10 @@ class UrlLauncher { } // we have the room, so....just open it if (event != null) { - VRouter.of(context).toSegments(['rooms', room.id], - queryParameters: {'event': event}); + VRouter.of(context).toSegments( + ['rooms', room.id], + queryParameters: {'event': event}, + ); } else { VRouter.of(context).toSegments(['rooms', room.id]); } @@ -175,11 +182,14 @@ class UrlLauncher { if (response.error != null) return; // wait for two seconds so that it probably came down /sync await showFutureLoadingDialog( - context: context, - future: () => Future.delayed(const Duration(seconds: 2))); + context: context, + future: () => Future.delayed(const Duration(seconds: 2)), + ); if (event != null) { - VRouter.of(context).toSegments(['rooms', response.result!], - queryParameters: {'event': event}); + VRouter.of(context).toSegments( + ['rooms', response.result!], + queryParameters: {'event': event}, + ); } else { VRouter.of(context).toSegments(['rooms', response.result!]); } diff --git a/lib/utils/voip/callkeep_manager.dart b/lib/utils/voip/callkeep_manager.dart index 6df4a81d..6de7cb6e 100644 --- a/lib/utils/voip/callkeep_manager.dart +++ b/lib/utils/voip/callkeep_manager.dart @@ -111,14 +111,15 @@ class CallKeepManager { Future showCallkitIncoming(CallSession call) async { if (!setupDone) { await _callKeep.setup( - null, - { - 'ios': { - 'appName': appName, - }, - 'android': alertOptions, + null, + { + 'ios': { + 'appName': appName, }, - backgroundMode: true); + 'android': alertOptions, + }, + backgroundMode: true, + ); } setupDone = true; await displayIncomingCall(call); @@ -131,7 +132,8 @@ class CallKeepManager { (event) { if (event == CallEvent.kLocalHoldUnhold) { Logs().i( - 'Call hold event: local ${call.localHold}, remote ${call.remoteOnHold}'); + 'Call hold event: local ${call.localHold}, remote ${call.remoteOnHold}', + ); } }, ); @@ -169,10 +171,14 @@ class CallKeepManager { _callKeep.on(CallKeepPerformAnswerCallAction(), answerCall); _callKeep.on(CallKeepDidPerformDTMFAction(), didPerformDTMFAction); _callKeep.on( - CallKeepDidReceiveStartCallAction(), didReceiveStartCallAction); + CallKeepDidReceiveStartCallAction(), + didReceiveStartCallAction, + ); _callKeep.on(CallKeepDidToggleHoldAction(), didToggleHoldCallAction); _callKeep.on( - CallKeepDidPerformSetMutedCallAction(), didPerformSetMutedCallAction); + CallKeepDidPerformSetMutedCallAction(), + didPerformSetMutedCallAction, + ); _callKeep.on(CallKeepPerformEndCallAction(), endCall); _callKeep.on(CallKeepPushKitToken(), onPushKitToken); _callKeep.on(CallKeepDidDisplayIncomingCall(), didDisplayIncomingCall); @@ -209,11 +215,17 @@ class CallKeepManager { Future updateDisplay(String callUUID) async { // Workaround because Android doesn't display well displayName, se we have to switch ... if (isIOS) { - await _callKeep.updateDisplay(callUUID, - displayName: 'New Name', handle: callUUID); + await _callKeep.updateDisplay( + callUUID, + displayName: 'New Name', + handle: callUUID, + ); } else { - await _callKeep.updateDisplay(callUUID, - displayName: callUUID, handle: 'New Name'); + await _callKeep.updateDisplay( + callUUID, + displayName: callUUID, + handle: 'New Name', + ); } } @@ -250,7 +262,8 @@ class CallKeepManager { const Divider(), ListTile( onTap: () => FlutterForegroundTask.openSystemAlertWindowSettings( - forceOpen: true), + forceOpen: true, + ), title: Text(L10n.of(context)!.appearOnTop), subtitle: Text(L10n.of(context)!.appearOnTopDetails), trailing: const Icon(Icons.file_upload_rounded), @@ -310,7 +323,8 @@ class CallKeepManager { } Future didReceiveStartCallAction( - CallKeepDidReceiveStartCallAction event) async { + CallKeepDidReceiveStartCallAction event, + ) async { if (event.handle == null) { // @TODO: sometime we receive `didReceiveStartCallAction` with handle` undefined` return; @@ -328,7 +342,8 @@ class CallKeepManager { } Future didPerformSetMutedCallAction( - CallKeepDidPerformSetMutedCallAction event) async { + CallKeepDidPerformSetMutedCallAction event, + ) async { final keeper = calls[event.callUUID]; if (event.muted!) { keeper!.call.setMicrophoneMuted(true); @@ -339,7 +354,8 @@ class CallKeepManager { } Future didToggleHoldCallAction( - CallKeepDidToggleHoldAction event) async { + CallKeepDidToggleHoldAction event, + ) async { final keeper = calls[event.callUUID]; if (event.hold!) { keeper!.call.setRemoteOnHold(true); diff --git a/lib/utils/voip_plugin.dart b/lib/utils/voip_plugin.dart index 1f996856..65ad9370 100644 --- a/lib/utils/voip_plugin.dart +++ b/lib/utils/voip_plugin.dart @@ -82,14 +82,15 @@ class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate { } else { overlayEntry = OverlayEntry( builder: (_) => Calling( - context: context, - client: client, - callId: callId, - call: call, - onClear: () { - overlayEntry?.remove(); - overlayEntry = null; - }), + context: context, + client: client, + callId: callId, + call: call, + onClear: () { + overlayEntry?.remove(); + overlayEntry = null; + }, + ), ); Overlay.of(context).insert(overlayEntry!); } @@ -103,8 +104,9 @@ class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate { @override Future createPeerConnection( - Map configuration, - [Map constraints = const {}]) => + Map configuration, [ + Map constraints = const {}, + ]) => webrtc_impl.createPeerConnection(configuration, constraints); @override @@ -150,7 +152,9 @@ class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate { try { final wasForeground = await FlutterForegroundTask.isAppOnForeground; await Store().setItem( - 'wasForeground', wasForeground == true ? 'true' : 'false'); + 'wasForeground', + wasForeground == true ? 'true' : 'false', + ); FlutterForegroundTask.setOnLockScreenVisibility(true); FlutterForegroundTask.wakeUpScreen(); FlutterForegroundTask.launchApp(); @@ -162,10 +166,13 @@ class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate { try { if (!hasCallingAccount) { ScaffoldMessenger.of(FluffyChatApp.routerKey.currentContext!) - .showSnackBar(const SnackBar( - content: Text( - 'No calling accounts found (used for native calls UI)', - ))); + .showSnackBar( + const SnackBar( + content: Text( + 'No calling accounts found (used for native calls UI)', + ), + ), + ); } } catch (e) { Logs().e('failed to show snackbar'); diff --git a/lib/widgets/chat_settings_popup_menu.dart b/lib/widgets/chat_settings_popup_menu.dart index 1e5f876f..1abcc6db 100644 --- a/lib/widgets/chat_settings_popup_menu.dart +++ b/lib/widgets/chat_settings_popup_menu.dart @@ -150,7 +150,9 @@ class ChatSettingsPopupMenuState extends State { ); if (confirmed == OkCancelResult.ok) { final success = await showFutureLoadingDialog( - context: context, future: () => widget.room.leave()); + context: context, + future: () => widget.room.leave(), + ); if (success.error == null) { VRouter.of(context).to('/rooms'); } @@ -158,15 +160,17 @@ class ChatSettingsPopupMenuState extends State { break; case 'mute': await showFutureLoadingDialog( - context: context, - future: () => widget.room - .setPushRuleState(PushRuleState.mentionsOnly)); + context: context, + future: () => + widget.room.setPushRuleState(PushRuleState.mentionsOnly), + ); break; case 'unmute': await showFutureLoadingDialog( - context: context, - future: () => - widget.room.setPushRuleState(PushRuleState.notify)); + context: context, + future: () => + widget.room.setPushRuleState(PushRuleState.notify), + ); break; case 'details': _showChatDetails(); diff --git a/lib/widgets/content_banner.dart b/lib/widgets/content_banner.dart index e18b4683..72a89a5a 100644 --- a/lib/widgets/content_banner.dart +++ b/lib/widgets/content_banner.dart @@ -13,16 +13,16 @@ class ContentBanner extends StatelessWidget { final double opacity; final WidgetBuilder? placeholder; - const ContentBanner( - {this.mxContent, - this.height = 400, - this.defaultIcon = Icons.account_circle_outlined, - this.onEdit, - this.client, - this.opacity = 0.75, - this.placeholder, - Key? key}) - : super(key: key); + const ContentBanner({ + this.mxContent, + this.height = 400, + this.defaultIcon = Icons.account_circle_outlined, + this.onEdit, + this.client, + this.opacity = 0.75, + this.placeholder, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { diff --git a/lib/widgets/layouts/loading_view.dart b/lib/widgets/layouts/loading_view.dart index d45fe53f..3675cde8 100644 --- a/lib/widgets/layouts/loading_view.dart +++ b/lib/widgets/layouts/loading_view.dart @@ -16,8 +16,10 @@ class LoadingView extends StatelessWidget { (_) async { await UpdateCheckerNoStore(context).checkUpdate(); VRouter.of(context).to( - Matrix.of(context).widget.clients.any((client) => - client.onLoginStateChanged.value == LoginState.loggedIn) + Matrix.of(context).widget.clients.any( + (client) => + client.onLoginStateChanged.value == LoginState.loggedIn, + ) ? '/rooms' : '/home', queryParameters: VRouter.of(context).queryParameters, diff --git a/lib/widgets/local_notifications_extension.dart b/lib/widgets/local_notifications_extension.dart index fb6b071f..ffe32771 100644 --- a/lib/widgets/local_notifications_extension.dart +++ b/lib/widgets/local_notifications_extension.dart @@ -44,12 +44,17 @@ extension LocalNotificationsExtension on MatrixState { removeMarkdown: true, ); final icon = event.senderFromMemoryOrFallback.avatarUrl?.getThumbnail( - client, - width: 64, - height: 64, - method: ThumbnailMethod.crop) ?? - room.avatar?.getThumbnail(client, - width: 64, height: 64, method: ThumbnailMethod.crop); + client, + width: 64, + height: 64, + method: ThumbnailMethod.crop, + ) ?? + room.avatar?.getThumbnail( + client, + width: 64, + height: 64, + method: ThumbnailMethod.crop, + ); if (kIsWeb) { html.AudioElement() ..src = @@ -73,7 +78,8 @@ extension LocalNotificationsExtension on MatrixState { final avatarDirectory = await Directory('${tempDirectory.path}/notiavatars/').create(); appIconFile = File( - '${avatarDirectory.path}/${Uri.encodeComponent(appIconUrl.toString())}'); + '${avatarDirectory.path}/${Uri.encodeComponent(appIconUrl.toString())}', + ); if (await appIconFile.exists() == false) { final response = await http.get(appIconUrl); await appIconFile.writeAsBytes(response.bodyBytes); diff --git a/lib/widgets/lock_screen.dart b/lib/widgets/lock_screen.dart index 9b03dc0f..2b5473f2 100644 --- a/lib/widgets/lock_screen.dart +++ b/lib/widgets/lock_screen.dart @@ -72,7 +72,8 @@ class LockScreenState extends State { await ([TargetPlatform.linux] .contains(Theme.of(context).platform) ? SharedPreferences.getInstance().then( - (prefs) => prefs.getString(SettingKeys.appLockKey)) + (prefs) => prefs.getString(SettingKeys.appLockKey), + ) : const FlutterSecureStorage() .read(key: SettingKeys.appLockKey))) { AppLock.of(context)!.didUnlock(); diff --git a/lib/widgets/log_view.dart b/lib/widgets/log_view.dart index d87f15f8..8e62f7b8 100644 --- a/lib/widgets/log_view.dart +++ b/lib/widgets/log_view.dart @@ -34,10 +34,12 @@ class LogViewerState extends State { ), PopupMenuButton( itemBuilder: (context) => Level.values - .map((level) => PopupMenuItem( - value: level, - child: Text(level.toString()), - )) + .map( + (level) => PopupMenuItem( + value: level, + child: Text(level.toString()), + ), + ) .toList(), onSelected: (Level level) => setState(() => logLevel = level), ), diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index a5d3fa33..43016b79 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -135,18 +135,22 @@ class MatrixState extends State with WidgetsBindingObserver { continue; } resBundles[bundle.name] ??= []; - resBundles[bundle.name]!.add(_AccountBundleWithClient( - client: widget.clients[i], - bundle: bundle, - )); + resBundles[bundle.name]!.add( + _AccountBundleWithClient( + client: widget.clients[i], + bundle: bundle, + ), + ); } } for (final b in resBundles.values) { - b.sort((a, b) => a.bundle!.priority == null - ? 1 - : b.bundle!.priority == null - ? -1 - : a.bundle!.priority!.compareTo(b.bundle!.priority!)); + b.sort( + (a, b) => a.bundle!.priority == null + ? 1 + : b.bundle!.priority == null + ? -1 + : a.bundle!.priority!.compareTo(b.bundle!.priority!), + ); } return resBundles .map((k, v) => MapEntry(k, v.map((vv) => vv.client).toList())); @@ -161,8 +165,8 @@ class MatrixState extends State with WidgetsBindingObserver { return client; } final candidate = _loginClientCandidate ??= ClientManager.createClient( - '${AppConfig.applicationName}-${DateTime.now().millisecondsSinceEpoch}') - ..onLoginStateChanged + '${AppConfig.applicationName}-${DateTime.now().millisecondsSinceEpoch}', + )..onLoginStateChanged .stream .where((l) => l == LoginState.loggedIn) .first @@ -286,16 +290,20 @@ class MatrixState extends State with WidgetsBindingObserver { final c = getClientByName(name); if (c == null) { Logs().w( - 'Attempted to register subscriptions for non-existing client $name'); + 'Attempted to register subscriptions for non-existing client $name', + ); return; } onRoomKeyRequestSub[name] ??= c.onRoomKeyRequest.stream.listen((RoomKeyRequest request) async { - if (widget.clients.any(((cl) => - cl.userID == request.requestingDevice.userId && - cl.identityKey == request.requestingDevice.curve25519Key))) { + if (widget.clients.any( + ((cl) => + cl.userID == request.requestingDevice.userId && + cl.identityKey == request.requestingDevice.curve25519Key), + )) { Logs().i( - '[Key Request] Request is from one of our own clients, forwarding the key...'); + '[Key Request] Request is from one of our own clients, forwarding the key...', + ); await request.forwardKey(); } }); @@ -344,11 +352,13 @@ class MatrixState extends State with WidgetsBindingObserver { c.onSync.stream.first.then((s) { html.Notification.requestPermission(); onNotification[name] ??= c.onEvent.stream - .where((e) => - e.type == EventUpdateType.timeline && - [EventTypes.Message, EventTypes.Sticker, EventTypes.Encrypted] - .contains(e.content['type']) && - e.content['sender'] != c.userID) + .where( + (e) => + e.type == EventUpdateType.timeline && + [EventTypes.Message, EventTypes.Sticker, EventTypes.Encrypted] + .contains(e.content['type']) && + e.content['sender'] != c.userID, + ) .listen(showLocalNotification); }); } @@ -448,25 +458,31 @@ class MatrixState extends State with WidgetsBindingObserver { wallpaper = file; } }); - store.getItem(SettingKeys.fontSizeFactor).then((value) => - AppConfig.fontSizeFactor = - double.tryParse(value ?? '') ?? AppConfig.fontSizeFactor); - store.getItem(SettingKeys.bubbleSizeFactor).then((value) => - AppConfig.bubbleSizeFactor = - double.tryParse(value ?? '') ?? AppConfig.bubbleSizeFactor); + store.getItem(SettingKeys.fontSizeFactor).then( + (value) => AppConfig.fontSizeFactor = + double.tryParse(value ?? '') ?? AppConfig.fontSizeFactor, + ); + store.getItem(SettingKeys.bubbleSizeFactor).then( + (value) => AppConfig.bubbleSizeFactor = + double.tryParse(value ?? '') ?? AppConfig.bubbleSizeFactor, + ); store .getItemBool(SettingKeys.renderHtml, AppConfig.renderHtml) .then((value) => AppConfig.renderHtml = value); store .getItemBool( - SettingKeys.hideRedactedEvents, AppConfig.hideRedactedEvents) + SettingKeys.hideRedactedEvents, + AppConfig.hideRedactedEvents, + ) .then((value) => AppConfig.hideRedactedEvents = value); store .getItemBool(SettingKeys.hideUnknownEvents, AppConfig.hideUnknownEvents) .then((value) => AppConfig.hideUnknownEvents = value); store - .getItemBool(SettingKeys.showDirectChatsInSpaces, - AppConfig.showDirectChatsInSpaces) + .getItemBool( + SettingKeys.showDirectChatsInSpaces, + AppConfig.showDirectChatsInSpaces, + ) .then((value) => AppConfig.showDirectChatsInSpaces = value); store .getItemBool(SettingKeys.separateChatTypes, AppConfig.separateChatTypes) diff --git a/lib/widgets/permission_slider_dialog.dart b/lib/widgets/permission_slider_dialog.dart index bc7dafa1..9aca4ff2 100644 --- a/lib/widgets/permission_slider_dialog.dart +++ b/lib/widgets/permission_slider_dialog.dart @@ -27,8 +27,10 @@ extension on PermissionLevel { } } -Future showPermissionChooser(BuildContext context, - {int currentLevel = 0}) async { +Future showPermissionChooser( + BuildContext context, { + int currentLevel = 0, +}) async { final permissionLevel = await showConfirmationDialog( context: context, title: L10n.of(context)!.setPermissionsLevel, diff --git a/lib/widgets/public_room_bottom_sheet.dart b/lib/widgets/public_room_bottom_sheet.dart index cfb3f734..e47077be 100644 --- a/lib/widgets/public_room_bottom_sheet.dart +++ b/lib/widgets/public_room_bottom_sheet.dart @@ -38,7 +38,8 @@ class PublicRoomBottomSheet extends StatelessWidget { if (result.error == null) { if (client.getRoomById(result.result!) == null) { await client.onSync.stream.firstWhere( - (sync) => sync.rooms?.join?.containsKey(result.result) ?? false); + (sync) => sync.rooms?.join?.containsKey(result.result) ?? false, + ); } // don't open the room if the joined room is a space if (!client.getRoomById(result.result!)!.isSpace) { @@ -93,66 +94,69 @@ class PublicRoomBottomSheet extends StatelessWidget { ], ), body: FutureBuilder( - future: _search(context), - builder: (context, snapshot) { - final profile = snapshot.data; - return ListView( - padding: EdgeInsets.zero, - children: [ - if (profile == null) - Container( - height: 156, - alignment: Alignment.center, - color: Theme.of(context).secondaryHeaderColor, - child: snapshot.hasError - ? Text(snapshot.error!.toLocalizedString(context)) - : const CircularProgressIndicator.adaptive( - strokeWidth: 2), - ) - else - Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Avatar( - mxContent: profile.avatarUrl, - name: profile.name ?? roomAlias, - size: Avatar.defaultSize * 3, - fontSize: 36, - ), + future: _search(context), + builder: (context, snapshot) { + final profile = snapshot.data; + return ListView( + padding: EdgeInsets.zero, + children: [ + if (profile == null) + Container( + height: 156, + alignment: Alignment.center, + color: Theme.of(context).secondaryHeaderColor, + child: snapshot.hasError + ? Text(snapshot.error!.toLocalizedString(context)) + : const CircularProgressIndicator.adaptive( + strokeWidth: 2, + ), + ) + else + Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Avatar( + mxContent: profile.avatarUrl, + name: profile.name ?? roomAlias, + size: Avatar.defaultSize * 3, + fontSize: 36, ), ), - ListTile( - title: Text(profile?.name ?? + ), + ListTile( + title: Text( + profile?.name ?? roomAlias?.localpart ?? chunk!.roomId.localpart ?? - ''), - subtitle: Text( - '${L10n.of(context)!.participant}: ${profile?.numJoinedMembers ?? 0}', - ), - trailing: const Icon(Icons.account_box_outlined), + '', ), - if (profile?.topic?.isNotEmpty ?? false) - ListTile( - title: Text( - L10n.of(context)!.groupDescription, - style: TextStyle( - color: Theme.of(context).colorScheme.secondary, - ), - ), - subtitle: LinkText( - text: profile!.topic!, - linkStyle: const TextStyle(color: Colors.blueAccent), - textStyle: TextStyle( - fontSize: 14, - color: Theme.of(context).textTheme.bodyMedium!.color, - ), - onLinkTap: (url) => - UrlLauncher(context, url).launchUrl(), + subtitle: Text( + '${L10n.of(context)!.participant}: ${profile?.numJoinedMembers ?? 0}', + ), + trailing: const Icon(Icons.account_box_outlined), + ), + if (profile?.topic?.isNotEmpty ?? false) + ListTile( + title: Text( + L10n.of(context)!.groupDescription, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, ), ), - ], - ); - }), + subtitle: LinkText( + text: profile!.topic!, + linkStyle: const TextStyle(color: Colors.blueAccent), + textStyle: TextStyle( + fontSize: 14, + color: Theme.of(context).textTheme.bodyMedium!.color, + ), + onLinkTap: (url) => UrlLauncher(context, url).launchUrl(), + ), + ), + ], + ); + }, + ), ), ); } diff --git a/lib/widgets/theme_builder.dart b/lib/widgets/theme_builder.dart index c7e96636..b1ca4aa4 100644 --- a/lib/widgets/theme_builder.dart +++ b/lib/widgets/theme_builder.dart @@ -91,11 +91,12 @@ class ThemeController extends State { return Provider( create: (_) => this, child: DynamicColorBuilder( - builder: (light, _) => widget.builder( - context, - themeMode, - primaryColor ?? light?.primary, - )), + builder: (light, _) => widget.builder( + context, + themeMode, + primaryColor ?? light?.primary, + ), + ), ); } } diff --git a/lib/widgets/unread_rooms_badge.dart b/lib/widgets/unread_rooms_badge.dart index 3c8fc6c3..6ebde3c9 100644 --- a/lib/widgets/unread_rooms_badge.dart +++ b/lib/widgets/unread_rooms_badge.dart @@ -20,38 +20,39 @@ class UnreadRoomsBadge extends StatelessWidget { @override Widget build(BuildContext context) { return StreamBuilder( - stream: Matrix.of(context) + stream: Matrix.of(context) + .client + .onSync + .stream + .where((syncUpdate) => syncUpdate.hasRoomUpdate), + builder: (context, _) { + final unreadCount = Matrix.of(context) .client - .onSync - .stream - .where((syncUpdate) => syncUpdate.hasRoomUpdate), - builder: (context, _) { - final unreadCount = Matrix.of(context) - .client - .rooms - .where(filter) - .where((r) => (r.isUnread || r.membership == Membership.invite)) - .length; - return b.Badge( - alignment: Alignment.bottomRight, - badgeContent: Text( - unreadCount.toString(), - style: TextStyle( - color: Theme.of(context).colorScheme.onPrimary, - fontSize: 12, - ), + .rooms + .where(filter) + .where((r) => (r.isUnread || r.membership == Membership.invite)) + .length; + return b.Badge( + alignment: Alignment.bottomRight, + badgeContent: Text( + unreadCount.toString(), + style: TextStyle( + color: Theme.of(context).colorScheme.onPrimary, + fontSize: 12, ), - showBadge: unreadCount != 0, - animationType: b.BadgeAnimationType.scale, - badgeColor: Theme.of(context).colorScheme.primary, - position: badgePosition, - elevation: 4, - borderSide: BorderSide( - color: Theme.of(context).colorScheme.background, - width: 2, - ), - child: child, - ); - }); + ), + showBadge: unreadCount != 0, + animationType: b.BadgeAnimationType.scale, + badgeColor: Theme.of(context).colorScheme.primary, + position: badgePosition, + elevation: 4, + borderSide: BorderSide( + color: Theme.of(context).colorScheme.background, + width: 2, + ), + child: child, + ); + }, + ); } } diff --git a/test/command_hint_test.dart b/test/command_hint_test.dart index e9877f8f..e56f0605 100644 --- a/test/command_hint_test.dart +++ b/test/command_hint_test.dart @@ -15,8 +15,11 @@ void main() async { final commands = (await prepareTestClient()).commands.keys; final missing = commands.where((c) => !translated.contains(c)).toList(); - expect(0, missing.length, - reason: - 'missing hints for $missing\nAdding hints? See scripts/generate_command_hints_glue.sh'); + expect( + 0, + missing.length, + reason: + 'missing hints for $missing\nAdding hints? See scripts/generate_command_hints_glue.sh', + ); }); }