From fbb68686eab3162ded4d63a056d1fcc73d4f1339 Mon Sep 17 00:00:00 2001 From: Krille Date: Sat, 7 Jan 2023 10:29:34 +0100 Subject: [PATCH] style: New modal bottom sheets --- integration_test/app_test.dart | 22 +- lib/pages/chat/chat.dart | 4 +- lib/pages/chat/chat_app_bar_title.dart | 3 +- lib/pages/chat/chat_event_list.dart | 3 +- lib/pages/chat/event_info_dialog.dart | 3 +- lib/pages/chat/events/message_content.dart | 3 +- .../chat_details/participant_list_item.dart | 3 +- lib/pages/chat_list/chat_list_body.dart | 5 +- .../homeserver_picker/homeserver_picker.dart | 4 +- .../key_verification_dialog.dart | 4 +- .../new_private_chat/new_private_chat.dart | 5 +- lib/pages/story/story_page.dart | 5 +- .../user_bottom_sheet/user_bottom_sheet.dart | 29 +- .../user_bottom_sheet_view.dart | 257 ++++++++---------- lib/utils/adaptive_bottom_sheet.dart | 28 ++ lib/utils/url_launcher.dart | 5 +- lib/widgets/chat_settings_popup_menu.dart | 3 +- lib/widgets/profile_bottom_sheet.dart | 132 ++++----- lib/widgets/public_room_bottom_sheet.dart | 175 ++++++------ 19 files changed, 333 insertions(+), 360 deletions(-) create mode 100644 lib/utils/adaptive_bottom_sheet.dart diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart index 0ebfb57c..912e44cc 100644 --- a/integration_test/app_test.dart +++ b/integration_test/app_test.dart @@ -61,12 +61,14 @@ void main() { await tester.pumpAndSettle(); await tester.scrollUntilVisible( - find.text('Chats'), + find.text('Chats').first, 500, - scrollable: find.descendant( - of: find.byType(ChatListViewBody), - matching: find.byType(Scrollable), - ), + scrollable: find + .descendant( + of: find.byType(ChatListViewBody), + matching: find.byType(Scrollable), + ) + .first, ); await tester.pumpAndSettle(); await tester.tap(find.text('Chats')); @@ -77,10 +79,12 @@ void main() { await tester.scrollUntilVisible( find.text(Users.user2.name).first, 500, - scrollable: find.descendant( - of: find.byType(ChatListViewBody), - matching: find.byType(Scrollable), - ), + scrollable: find + .descendant( + of: find.byType(ChatListViewBody), + matching: find.byType(Scrollable), + ) + .first, ); await tester.pumpAndSettle(); await tester.tap(find.text(Users.user2.name).first); diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 00c82fba..5bf0adf8 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -22,6 +22,7 @@ import 'package:vrouter/vrouter.dart'; import 'package:fluffychat/pages/chat/chat_view.dart'; import 'package:fluffychat/pages/chat/event_info_dialog.dart'; import 'package:fluffychat/pages/chat/recording_dialog.dart'; +import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/ios_badge_client_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; @@ -422,9 +423,8 @@ class ChatController extends State { } void sendStickerAction() async { - final sticker = await showModalBottomSheet( + final sticker = await showAdaptiveBottomSheet( context: context, - useRootNavigator: false, builder: (c) => StickerPickerDialog(room: room!), ); if (sticker == null) return; diff --git a/lib/pages/chat/chat_app_bar_title.dart b/lib/pages/chat/chat_app_bar_title.dart index ba6dc187..5905a49b 100644 --- a/lib/pages/chat/chat_app_bar_title.dart +++ b/lib/pages/chat/chat_app_bar_title.dart @@ -5,6 +5,7 @@ import 'package:vrouter/vrouter.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart'; +import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/widgets/avatar.dart'; @@ -26,7 +27,7 @@ class ChatAppBarTitle extends StatelessWidget { splashColor: Colors.transparent, highlightColor: Colors.transparent, onTap: directChatMatrixID != null - ? () => showModalBottomSheet( + ? () => showAdaptiveBottomSheet( context: context, builder: (c) => UserBottomSheet( user: room diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 15eeb79f..b3cbc57c 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -10,6 +10,7 @@ import 'package:fluffychat/pages/chat/events/message.dart'; import 'package:fluffychat/pages/chat/seen_by_row.dart'; import 'package:fluffychat/pages/chat/typing_indicators.dart'; import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart'; +import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/filtered_timeline_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -89,7 +90,7 @@ class ChatEventList extends StatelessWidget { onSwipe: (direction) => controller.replyAction(replyTo: event), onInfoTab: controller.showEventInfo, - onAvatarTab: (Event event) => showModalBottomSheet( + onAvatarTab: (Event event) => showAdaptiveBottomSheet( context: context, builder: (c) => UserBottomSheet( user: event.senderFromMemoryOrFallback, diff --git a/lib/pages/chat/event_info_dialog.dart b/lib/pages/chat/event_info_dialog.dart index 16862bf0..9dd92b17 100644 --- a/lib/pages/chat/event_info_dialog.dart +++ b/lib/pages/chat/event_info_dialog.dart @@ -6,11 +6,12 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; extension EventInfoDialogExtension on Event { - void showInfoDialog(BuildContext context) => showModalBottomSheet( + void showInfoDialog(BuildContext context) => showAdaptiveBottomSheet( context: context, builder: (context) => EventInfoDialog(l10n: L10n.of(context)!, event: this), diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 873a2575..be84a00b 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -5,6 +5,7 @@ import 'package:matrix/matrix.dart'; import 'package:matrix_link_text/link_text.dart'; import 'package:fluffychat/pages/chat/events/video_player.dart'; +import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/widgets/avatar.dart'; @@ -52,7 +53,7 @@ class MessageContent extends StatelessWidget { } event.requestKey(); final sender = event.senderFromMemoryOrFallback; - await showModalBottomSheet( + await showAdaptiveBottomSheet( context: context, builder: (context) => Scaffold( appBar: AppBar( diff --git a/lib/pages/chat_details/participant_list_item.dart b/lib/pages/chat_details/participant_list_item.dart index 20499b1d..5a7dd593 100644 --- a/lib/pages/chat_details/participant_list_item.dart +++ b/lib/pages/chat_details/participant_list_item.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import '../../widgets/avatar.dart'; import '../user_bottom_sheet/user_bottom_sheet.dart'; @@ -28,7 +29,7 @@ class ParticipantListItem extends StatelessWidget { return Opacity( opacity: user.membership == Membership.join ? 1 : 0.5, child: ListTile( - onTap: () => showModalBottomSheet( + onTap: () => showAdaptiveBottomSheet( context: context, builder: (c) => UserBottomSheet( user: user, diff --git a/lib/pages/chat_list/chat_list_body.dart b/lib/pages/chat_list/chat_list_body.dart index 8a070351..6e1e2aaf 100644 --- a/lib/pages/chat_list/chat_list_body.dart +++ b/lib/pages/chat_list/chat_list_body.dart @@ -9,6 +9,7 @@ import 'package:fluffychat/pages/chat_list/chat_list_item.dart'; import 'package:fluffychat/pages/chat_list/search_title.dart'; import 'package:fluffychat/pages/chat_list/space_view.dart'; import 'package:fluffychat/pages/chat_list/stories_header.dart'; +import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/client_stories_extension.dart'; import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; @@ -101,7 +102,7 @@ class ChatListViewBody extends StatelessWidget { L10n.of(context)!.group, avatar: roomSearchResult.chunk[i].avatarUrl, - onPressed: () => showModalBottomSheet( + onPressed: () => showAdaptiveBottomSheet( context: context, builder: (c) => PublicRoomBottomSheet( roomAlias: roomSearchResult @@ -140,7 +141,7 @@ class ChatListViewBody extends StatelessWidget { L10n.of(context)!.unknownDevice, avatar: userSearchResult.results[i].avatarUrl, - onPressed: () => showModalBottomSheet( + onPressed: () => showAdaptiveBottomSheet( context: context, builder: (c) => ProfileBottomSheet( userId: userSearchResult diff --git a/lib/pages/homeserver_picker/homeserver_picker.dart b/lib/pages/homeserver_picker/homeserver_picker.dart index 2c322c59..7e8f47a4 100644 --- a/lib/pages/homeserver_picker/homeserver_picker.dart +++ b/lib/pages/homeserver_picker/homeserver_picker.dart @@ -15,6 +15,7 @@ import 'package:vrouter/vrouter.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/homeserver_picker/homeserver_bottom_sheet.dart'; import 'package:fluffychat/pages/homeserver_picker/homeserver_picker_view.dart'; +import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/widgets/matrix.dart'; import '../../utils/localized_exception_extension.dart'; @@ -71,7 +72,8 @@ class HomeserverPickerController extends State { } } - void showServerInfo(HomeserverBenchmarkResult server) => showModalBottomSheet( + void showServerInfo(HomeserverBenchmarkResult server) => + showAdaptiveBottomSheet( context: context, builder: (_) => HomeserverBottomSheet( homeserver: server, diff --git a/lib/pages/key_verification/key_verification_dialog.dart b/lib/pages/key_verification/key_verification_dialog.dart index be617fe7..880283e8 100644 --- a/lib/pages/key_verification/key_verification_dialog.dart +++ b/lib/pages/key_verification/key_verification_dialog.dart @@ -10,13 +10,13 @@ import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/widgets/avatar.dart'; class KeyVerificationDialog extends StatefulWidget { - Future show(BuildContext context) => showModalBottomSheet( + Future show(BuildContext context) => showAdaptiveBottomSheet( context: context, builder: (context) => this, - useRootNavigator: false, isDismissible: false, ); diff --git a/lib/pages/new_private_chat/new_private_chat.dart b/lib/pages/new_private_chat/new_private_chat.dart index c40d0092..59cd8bd4 100644 --- a/lib/pages/new_private_chat/new_private_chat.dart +++ b/lib/pages/new_private_chat/new_private_chat.dart @@ -7,6 +7,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/pages/new_private_chat/new_private_chat_view.dart'; import 'package:fluffychat/pages/new_private_chat/qr_scanner_modal.dart'; +import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/url_launcher.dart'; @@ -75,10 +76,8 @@ class NewPrivateChatController extends State { return; } } - await showModalBottomSheet( + await showAdaptiveBottomSheet( context: context, - useRootNavigator: false, - //useSafeArea: false, builder: (_) => const QrScannerModal(), ); } diff --git a/lib/pages/story/story_page.dart b/lib/pages/story/story_page.dart index 6c553c2b..6d728118 100644 --- a/lib/pages/story/story_page.dart +++ b/lib/pages/story/story_page.dart @@ -13,6 +13,7 @@ import 'package:video_player/video_player.dart'; import 'package:vrouter/vrouter.dart'; import 'package:fluffychat/pages/story/story_view.dart'; +import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/client_stories_extension.dart'; @@ -58,7 +59,7 @@ class StoryPageController extends State { void replyEmojiAction() async { if (replyLoading) return; _modalOpened = true; - await showModalBottomSheet( + await showAdaptiveBottomSheet( context: context, builder: (context) => EmojiPicker( onEmojiSelected: (c, e) { @@ -123,7 +124,7 @@ class StoryPageController extends State { void displaySeenByUsers() async { _modalOpened = true; - await showModalBottomSheet( + await showAdaptiveBottomSheet( context: context, builder: (context) => Scaffold( appBar: AppBar( diff --git a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart index 187c141d..daf4791e 100644 --- a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart +++ b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart @@ -10,6 +10,17 @@ import 'package:fluffychat/widgets/permission_slider_dialog.dart'; import '../../widgets/matrix.dart'; import 'user_bottom_sheet_view.dart'; +enum UserBottomSheetAction { + report, + mention, + ban, + kick, + unban, + permission, + message, + ignore, +} + class UserBottomSheet extends StatefulWidget { final User user; final Function? onMention; @@ -27,7 +38,7 @@ class UserBottomSheet extends StatefulWidget { } class UserBottomSheetController extends State { - void participantAction(String action) async { + void participantAction(UserBottomSheetAction action) async { // ignore: prefer_function_declarations_over_variables final Function askConfirmation = () async => (await showOkCancelAlertDialog( useRootNavigator: false, @@ -38,7 +49,7 @@ class UserBottomSheetController extends State { ) == OkCancelResult.ok); switch (action) { - case 'report': + case UserBottomSheetAction.report: final event = widget.user; final score = await showConfirmationDialog( context: context, @@ -82,11 +93,11 @@ class UserBottomSheetController extends State { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(L10n.of(context)!.contentHasBeenReported))); break; - case 'mention': + case UserBottomSheetAction.mention: Navigator.of(context, rootNavigator: false).pop(); widget.onMention!(); break; - case 'ban': + case UserBottomSheetAction.ban: if (await askConfirmation()) { await showFutureLoadingDialog( context: context, @@ -95,7 +106,7 @@ class UserBottomSheetController extends State { Navigator.of(context, rootNavigator: false).pop(); } break; - case 'unban': + case UserBottomSheetAction.unban: if (await askConfirmation()) { await showFutureLoadingDialog( context: context, @@ -104,7 +115,7 @@ class UserBottomSheetController extends State { Navigator.of(context, rootNavigator: false).pop(); } break; - case 'kick': + case UserBottomSheetAction.kick: if (await askConfirmation()) { await showFutureLoadingDialog( context: context, @@ -113,7 +124,7 @@ class UserBottomSheetController extends State { Navigator.of(context, rootNavigator: false).pop(); } break; - case 'permission': + case UserBottomSheetAction.permission: final newPermission = await showPermissionChooser( context, currentLevel: widget.user.powerLevel, @@ -127,7 +138,7 @@ class UserBottomSheetController extends State { Navigator.of(context, rootNavigator: false).pop(); } break; - case 'message': + case UserBottomSheetAction.message: final roomIdResult = await showFutureLoadingDialog( context: context, future: () => widget.user.startDirectChat(), @@ -137,7 +148,7 @@ class UserBottomSheetController extends State { .toSegments(['rooms', roomIdResult.result!]); Navigator.of(context, rootNavigator: false).pop(); break; - case 'ignore': + case UserBottomSheetAction.ignore: if (await askConfirmation()) { await showFutureLoadingDialog( context: context, diff --git a/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart b/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart index ba1bd2fc..d3adf29a 100644 --- a/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart +++ b/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart @@ -1,15 +1,11 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; +import 'package:fluffychat/widgets/avatar.dart'; import '../../utils/matrix_sdk_extensions/presence_extension.dart'; -import '../../widgets/content_banner.dart'; -import '../../widgets/m2_popup_menu_button.dart'; import '../../widgets/matrix.dart'; import 'user_bottom_sheet.dart'; @@ -23,155 +19,114 @@ class UserBottomSheetView extends StatelessWidget { final user = controller.widget.user; final client = Matrix.of(context).client; final presence = client.presences[user.id]; - return Center( - child: SizedBox( - width: min( - MediaQuery.of(context).size.width, FluffyThemes.columnWidth * 1.5), - child: Material( - elevation: 4, - child: SafeArea( - child: Scaffold( - extendBodyBehindAppBar: true, - appBar: AppBar( - elevation: 0, - backgroundColor: - Theme.of(context).scaffoldBackgroundColor.withOpacity(0.5), - leading: IconButton( - icon: const Icon(Icons.arrow_downward_outlined), - onPressed: Navigator.of(context, rootNavigator: false).pop, - tooltip: L10n.of(context)!.close, - ), - title: Text(user.calcDisplayname()), - actions: [ - if (user.id != client.userID) - M2PopupMenuButton( - itemBuilder: (_) => [ - if (controller.widget.onMention != null) - PopupMenuItem( - value: 'mention', - child: _TextWithIcon( - L10n.of(context)!.mention, - Icons.alternate_email_outlined, - ), - ), - if (user.id != client.userID && !user.room.isDirectChat) - PopupMenuItem( - value: 'message', - child: _TextWithIcon( - L10n.of(context)!.sendAMessage, - Icons.send_outlined, - ), - ), - if (user.canChangePowerLevel) - PopupMenuItem( - value: 'permission', - child: _TextWithIcon( - L10n.of(context)!.setPermissionsLevel, - Icons.edit_attributes_outlined, - ), - ), - if (user.canKick) - PopupMenuItem( - value: 'kick', - child: _TextWithIcon( - L10n.of(context)!.kickFromChat, - Icons.exit_to_app_outlined, - ), - ), - if (user.canBan && user.membership != Membership.ban) - PopupMenuItem( - value: 'ban', - child: _TextWithIcon( - L10n.of(context)!.banFromChat, - Icons.warning_sharp, - ), - ) - else if (user.canBan && - user.membership == Membership.ban) - PopupMenuItem( - value: 'unban', - child: _TextWithIcon( - L10n.of(context)!.unbanFromChat, - Icons.warning_outlined, - ), - ), - if (!client.ignoredUsers.contains(user.id)) - PopupMenuItem( - value: 'ignore', - child: _TextWithIcon( - L10n.of(context)!.ignore, - Icons.block, - ), - ), - PopupMenuItem( - value: 'report', - child: _TextWithIcon( - L10n.of(context)!.reportUser, - Icons.shield_outlined, - ), - ), - ], - onSelected: controller.participantAction, - ), - ], - ), - body: Column( - children: [ - Expanded( - child: ContentBanner( - mxContent: user.avatarUrl, - defaultIcon: Icons.account_circle_outlined, - client: client, - ), - ), - ListTile( - title: Text(L10n.of(context)!.username), - subtitle: Text(user.id), - trailing: Icon(Icons.adaptive.share_outlined), - onTap: () => FluffyShare.share( - user.id, - context, - ), - ), - if (presence != null) - ListTile( - title: Text(presence.getLocalizedStatusMessage(context)), - subtitle: - Text(presence.getLocalizedLastActiveAgo(context)), - trailing: Icon(Icons.circle, - color: presence.presence == PresenceType.online - ? Colors.green - : Colors.grey), - ), - ], - ), - ), + return SafeArea( + child: Scaffold( + appBar: AppBar( + leading: CloseButton( + onPressed: Navigator.of(context, rootNavigator: false).pop, ), + title: Text(user.calcDisplayname()), + actions: [ + if (user.id != client.userID) + Padding( + padding: const EdgeInsets.all(8.0), + child: OutlinedButton.icon( + onPressed: () => controller + .participantAction(UserBottomSheetAction.message), + icon: const Icon(Icons.chat_outlined), + label: Text(L10n.of(context)!.newChat), + ), + ), + ], + ), + body: ListView( + children: [ + Row( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Avatar( + mxContent: user.avatarUrl, + name: user.calcDisplayname(), + size: Avatar.defaultSize * 2, + fontSize: 24, + ), + ), + Expanded( + child: ListTile( + contentPadding: const EdgeInsets.only(right: 16.0), + title: Text(user.id), + subtitle: presence == null + ? null + : Text(presence.getLocalizedLastActiveAgo(context)), + trailing: IconButton( + icon: Icon(Icons.adaptive.share), + onPressed: () => FluffyShare.share( + user.id, + context, + ), + ), + ), + ), + ], + ), + if (controller.widget.onMention != null) + ListTile( + trailing: const Icon(Icons.alternate_email_outlined), + title: Text(L10n.of(context)!.mention), + onTap: () => + controller.participantAction(UserBottomSheetAction.mention), + ), + if (user.canChangePowerLevel) + ListTile( + title: Text(L10n.of(context)!.setPermissionsLevel), + trailing: const Icon(Icons.edit_attributes_outlined), + onTap: () => controller + .participantAction(UserBottomSheetAction.permission), + ), + if (user.canKick) + ListTile( + title: Text(L10n.of(context)!.kickFromChat), + trailing: const Icon(Icons.exit_to_app_outlined), + onTap: () => + controller.participantAction(UserBottomSheetAction.kick), + ), + if (user.canBan && user.membership != Membership.ban) + ListTile( + title: Text(L10n.of(context)!.banFromChat), + trailing: const Icon(Icons.warning_sharp), + onTap: () => + controller.participantAction(UserBottomSheetAction.ban), + ) + else if (user.canBan && user.membership == Membership.ban) + ListTile( + title: Text(L10n.of(context)!.unbanFromChat), + trailing: const Icon(Icons.warning_outlined), + onTap: () => + controller.participantAction(UserBottomSheetAction.unban), + ), + if (user.id != client.userID && + !client.ignoredUsers.contains(user.id)) + ListTile( + textColor: Theme.of(context).colorScheme.onErrorContainer, + iconColor: Theme.of(context).colorScheme.onErrorContainer, + title: Text(L10n.of(context)!.ignore), + trailing: const Icon(Icons.block), + onTap: () => + controller.participantAction(UserBottomSheetAction.ignore), + ), + if (user.id != client.userID) + ListTile( + textColor: Theme.of(context).colorScheme.error, + iconColor: Theme.of(context).colorScheme.error, + title: Text(L10n.of(context)!.reportUser), + trailing: const Icon(Icons.shield_outlined), + onTap: () => + controller.participantAction(UserBottomSheetAction.report), + ), + ], ), ), ); } } - -class _TextWithIcon extends StatelessWidget { - final String text; - final IconData iconData; - - const _TextWithIcon( - this.text, - this.iconData, { - Key? key, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(iconData), - const SizedBox(width: 16), - Text(text), - ], - ); - } -} diff --git a/lib/utils/adaptive_bottom_sheet.dart b/lib/utils/adaptive_bottom_sheet.dart new file mode 100644 index 00000000..c075b0ee --- /dev/null +++ b/lib/utils/adaptive_bottom_sheet.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; + +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/utils/platform_infos.dart'; + +Future showAdaptiveBottomSheet({ + required BuildContext context, + required Widget Function(BuildContext) builder, + bool isDismissible = true, +}) => + showModalBottomSheet( + context: context, + builder: builder, + useRootNavigator: !PlatformInfos.isMobile, + isDismissible: isDismissible, + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height - 128, + maxWidth: FluffyThemes.columnWidth * 1.5, + ), + clipBehavior: Clip.hardEdge, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(AppConfig.borderRadius), + topRight: Radius.circular(AppConfig.borderRadius), + ), + ), + ); diff --git a/lib/utils/url_launcher.dart b/lib/utils/url_launcher.dart index cc64da35..47fd8889 100644 --- a/lib/utils/url_launcher.dart +++ b/lib/utils/url_launcher.dart @@ -10,6 +10,7 @@ import 'package:url_launcher/url_launcher.dart'; import 'package:vrouter/vrouter.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/profile_bottom_sheet.dart'; import 'package:fluffychat/widgets/public_room_bottom_sheet.dart'; @@ -145,7 +146,7 @@ class UrlLauncher { } return; } else { - await showModalBottomSheet( + await showAdaptiveBottomSheet( context: context, builder: (c) => PublicRoomBottomSheet( roomAlias: identityParts.primaryIdentifier, @@ -182,7 +183,7 @@ class UrlLauncher { } } } else if (identityParts.primaryIdentifier.sigil == '@') { - await showModalBottomSheet( + await showAdaptiveBottomSheet( context: context, builder: (c) => ProfileBottomSheet( userId: identityParts.primaryIdentifier, diff --git a/lib/widgets/chat_settings_popup_menu.dart b/lib/widgets/chat_settings_popup_menu.dart index a38bcc9b..84095588 100644 --- a/lib/widgets/chat_settings_popup_menu.dart +++ b/lib/widgets/chat_settings_popup_menu.dart @@ -14,6 +14,7 @@ import 'package:vrouter/vrouter.dart'; import 'package:fluffychat/pages/chat/cupertino_widgets_bottom_sheet.dart'; import 'package:fluffychat/pages/chat/edit_widgets_dialog.dart'; import 'package:fluffychat/pages/chat/widgets_bottom_sheet.dart'; +import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'm2_popup_menu_button.dart'; import 'matrix.dart'; @@ -185,7 +186,7 @@ class ChatSettingsPopupMenuState extends State { context: context, builder: (context) => CupertinoWidgetsBottomSheet(room: widget.room), ) - : showModalBottomSheet( + : showAdaptiveBottomSheet( context: context, builder: (context) => WidgetsBottomSheet(room: widget.room), ); diff --git a/lib/widgets/profile_bottom_sheet.dart b/lib/widgets/profile_bottom_sheet.dart index 53e57dd9..dffa71c0 100644 --- a/lib/widgets/profile_bottom_sheet.dart +++ b/lib/widgets/profile_bottom_sheet.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -7,10 +5,8 @@ import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:matrix/matrix.dart'; import 'package:vrouter/vrouter.dart'; -import 'package:fluffychat/config/themes.dart'; -import 'package:fluffychat/widgets/content_banner.dart'; +import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import '../utils/localized_exception_extension.dart'; class ProfileBottomSheet extends StatelessWidget { final String userId; @@ -37,79 +33,65 @@ class ProfileBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { - return Center( - child: SizedBox( - width: min( - MediaQuery.of(context).size.width, FluffyThemes.columnWidth * 1.5), - child: Material( - elevation: 4, - child: SafeArea( - child: Scaffold( - extendBodyBehindAppBar: true, - appBar: AppBar( - elevation: 0, - backgroundColor: - Theme.of(context).scaffoldBackgroundColor.withOpacity(0.5), - leading: IconButton( - icon: const Icon(Icons.arrow_downward_outlined), - onPressed: Navigator.of(context, rootNavigator: false).pop, - tooltip: L10n.of(context)!.close, + return SafeArea( + child: FutureBuilder( + future: Matrix.of(context).client.getProfileFromUserId(userId), + builder: (context, snapshot) { + final profile = snapshot.data; + return Scaffold( + appBar: AppBar( + leading: CloseButton( + onPressed: Navigator.of(context, rootNavigator: false).pop, + ), + title: ListTile( + contentPadding: const EdgeInsets.only(right: 16.0), + title: Text( + profile?.displayName ?? userId.localpart ?? userId, + style: const TextStyle(fontSize: 18), + ), + subtitle: Text( + userId, + style: const TextStyle(fontSize: 12), ), ), - body: FutureBuilder( - future: - Matrix.of(context).client.getProfileFromUserId(userId), - builder: (context, snapshot) { - final profile = snapshot.data; - - return Column( - children: [ - Expanded( - child: profile == null - ? Container( - alignment: Alignment.center, - color: Theme.of(context).secondaryHeaderColor, - child: snapshot.hasError - ? Text(snapshot.error! - .toLocalizedString(context)) - : const CircularProgressIndicator - .adaptive(strokeWidth: 2), - ) - : ContentBanner( - mxContent: profile.avatarUrl, - defaultIcon: Icons.account_circle_outlined, - client: Matrix.of(context).client, - placeholder: (context) => Center( - child: Text( - userId.localpart ?? userId, - style: - Theme.of(context).textTheme.headline3, - ), - ), - ), - ), - ListTile( - title: Text( - profile?.displayName ?? userId.localpart ?? ''), - subtitle: Text(userId), - trailing: const Icon(Icons.account_box_outlined), - ), - Container( - width: double.infinity, - padding: const EdgeInsets.all(12), - child: ElevatedButton.icon( - onPressed: () => _startDirectChat(context), - label: Text(L10n.of(context)!.newChat), - icon: const Icon(Icons.send_outlined), - ), - ), - const SizedBox(height: 8), - ], - ); - }), + actions: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: OutlinedButton.icon( + onPressed: () => _startDirectChat(context), + icon: Icon(Icons.adaptive.share_outlined), + label: Text(L10n.of(context)!.share), + ), + ), + ], ), - ), - ), + body: ListView( + children: [ + Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Avatar( + mxContent: profile?.avatarUrl, + name: profile?.displayName ?? userId, + size: Avatar.defaultSize * 3, + fontSize: 36, + ), + ), + ), + Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + child: FloatingActionButton.extended( + onPressed: () => _startDirectChat(context), + label: Text(L10n.of(context)!.newChat), + icon: const Icon(Icons.send_outlined), + ), + ), + const SizedBox(height: 8), + ], + ), + ); + }, ), ); } diff --git a/lib/widgets/public_room_bottom_sheet.dart b/lib/widgets/public_room_bottom_sheet.dart index f97bdb90..bb6e098e 100644 --- a/lib/widgets/public_room_bottom_sheet.dart +++ b/lib/widgets/public_room_bottom_sheet.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -8,7 +6,6 @@ import 'package:matrix/matrix.dart'; import 'package:matrix_link_text/link_text.dart'; import 'package:vrouter/vrouter.dart'; -import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/widgets/content_banner.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -72,101 +69,87 @@ class PublicRoomBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { final roomAlias = this.roomAlias; - return Center( - child: SizedBox( - width: min( - MediaQuery.of(context).size.width, FluffyThemes.columnWidth * 1.5), - child: Material( - elevation: 4, - child: SafeArea( - child: Scaffold( - extendBodyBehindAppBar: true, - appBar: AppBar( - elevation: 0, - titleSpacing: 0, - backgroundColor: - Theme.of(context).scaffoldBackgroundColor.withOpacity(0.5), - title: Text( - roomAlias ?? chunk!.name ?? chunk!.roomId, - overflow: TextOverflow.fade, - ), - leading: IconButton( - icon: const Icon(Icons.arrow_downward_outlined), - onPressed: Navigator.of(context, rootNavigator: false).pop, - tooltip: L10n.of(context)!.close, - ), - actions: [ - TextButton.icon( - onPressed: () => _joinRoom(context), - label: Text(L10n.of(context)!.joinRoom), - icon: const Icon(Icons.login_outlined), - ), - ], - ), - 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 - ContentBanner( - mxContent: profile.avatarUrl, - height: 156, - defaultIcon: Icons.group_outlined, - client: Matrix.of(context).client, - ), - 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 - .bodyText2! - .color, - ), - onLinkTap: (url) => - UrlLauncher(context, url).launchUrl(), - ), - ), - ], - ); - }), - ), + return SafeArea( + child: Scaffold( + extendBodyBehindAppBar: true, + appBar: AppBar( + elevation: 0, + titleSpacing: 0, + backgroundColor: + Theme.of(context).scaffoldBackgroundColor.withOpacity(0.5), + title: Text( + roomAlias ?? chunk!.name ?? chunk!.roomId, + overflow: TextOverflow.fade, ), + leading: IconButton( + icon: const Icon(Icons.arrow_downward_outlined), + onPressed: Navigator.of(context, rootNavigator: false).pop, + tooltip: L10n.of(context)!.close, + ), + actions: [ + TextButton.icon( + onPressed: () => _joinRoom(context), + label: Text(L10n.of(context)!.joinRoom), + icon: const Icon(Icons.login_outlined), + ), + ], ), + 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 + ContentBanner( + mxContent: profile.avatarUrl, + height: 156, + defaultIcon: Icons.group_outlined, + client: Matrix.of(context).client, + ), + 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.bodyText2!.color, + ), + onLinkTap: (url) => + UrlLauncher(context, url).launchUrl(), + ), + ), + ], + ); + }), ), ); }