Merge branch 'krille/nicer-modal-bottom-sheet' into 'main'

style: New modal bottom sheets

See merge request famedly/fluffychat!1071
This commit is contained in:
Krille 2023-01-08 10:35:31 +00:00
commit 8f9e1a9142
19 changed files with 333 additions and 360 deletions

View File

@ -61,12 +61,14 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.scrollUntilVisible( await tester.scrollUntilVisible(
find.text('Chats'), find.text('Chats').first,
500, 500,
scrollable: find.descendant( scrollable: find
of: find.byType(ChatListViewBody), .descendant(
matching: find.byType(Scrollable), of: find.byType(ChatListViewBody),
), matching: find.byType(Scrollable),
)
.first,
); );
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.tap(find.text('Chats')); await tester.tap(find.text('Chats'));
@ -77,10 +79,12 @@ void main() {
await tester.scrollUntilVisible( await tester.scrollUntilVisible(
find.text(Users.user2.name).first, find.text(Users.user2.name).first,
500, 500,
scrollable: find.descendant( scrollable: find
of: find.byType(ChatListViewBody), .descendant(
matching: find.byType(Scrollable), of: find.byType(ChatListViewBody),
), matching: find.byType(Scrollable),
)
.first,
); );
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.tap(find.text(Users.user2.name).first); await tester.tap(find.text(Users.user2.name).first);

View File

@ -22,6 +22,7 @@ import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/pages/chat/chat_view.dart'; import 'package:fluffychat/pages/chat/chat_view.dart';
import 'package:fluffychat/pages/chat/event_info_dialog.dart'; import 'package:fluffychat/pages/chat/event_info_dialog.dart';
import 'package:fluffychat/pages/chat/recording_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/event_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/ios_badge_client_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/ios_badge_client_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
@ -422,9 +423,8 @@ class ChatController extends State<Chat> {
} }
void sendStickerAction() async { void sendStickerAction() async {
final sticker = await showModalBottomSheet<ImagePackImageContent>( final sticker = await showAdaptiveBottomSheet<ImagePackImageContent>(
context: context, context: context,
useRootNavigator: false,
builder: (c) => StickerPickerDialog(room: room!), builder: (c) => StickerPickerDialog(room: room!),
); );
if (sticker == null) return; if (sticker == null) return;

View File

@ -5,6 +5,7 @@ import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.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/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/avatar.dart';
@ -26,7 +27,7 @@ class ChatAppBarTitle extends StatelessWidget {
splashColor: Colors.transparent, splashColor: Colors.transparent,
highlightColor: Colors.transparent, highlightColor: Colors.transparent,
onTap: directChatMatrixID != null onTap: directChatMatrixID != null
? () => showModalBottomSheet( ? () => showAdaptiveBottomSheet(
context: context, context: context,
builder: (c) => UserBottomSheet( builder: (c) => UserBottomSheet(
user: room user: room

View File

@ -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/seen_by_row.dart';
import 'package:fluffychat/pages/chat/typing_indicators.dart'; import 'package:fluffychat/pages/chat/typing_indicators.dart';
import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.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/matrix_sdk_extensions/filtered_timeline_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
@ -89,7 +90,7 @@ class ChatEventList extends StatelessWidget {
onSwipe: (direction) => onSwipe: (direction) =>
controller.replyAction(replyTo: event), controller.replyAction(replyTo: event),
onInfoTab: controller.showEventInfo, onInfoTab: controller.showEventInfo,
onAvatarTab: (Event event) => showModalBottomSheet( onAvatarTab: (Event event) => showAdaptiveBottomSheet(
context: context, context: context,
builder: (c) => UserBottomSheet( builder: (c) => UserBottomSheet(
user: event.senderFromMemoryOrFallback, user: event.senderFromMemoryOrFallback,

View File

@ -6,11 +6,12 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.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/utils/date_time_extension.dart';
import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/avatar.dart';
extension EventInfoDialogExtension on Event { extension EventInfoDialogExtension on Event {
void showInfoDialog(BuildContext context) => showModalBottomSheet( void showInfoDialog(BuildContext context) => showAdaptiveBottomSheet(
context: context, context: context,
builder: (context) => builder: (context) =>
EventInfoDialog(l10n: L10n.of(context)!, event: this), EventInfoDialog(l10n: L10n.of(context)!, event: this),

View File

@ -5,6 +5,7 @@ import 'package:matrix/matrix.dart';
import 'package:matrix_link_text/link_text.dart'; import 'package:matrix_link_text/link_text.dart';
import 'package:fluffychat/pages/chat/events/video_player.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/date_time_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/avatar.dart';
@ -52,7 +53,7 @@ class MessageContent extends StatelessWidget {
} }
event.requestKey(); event.requestKey();
final sender = event.senderFromMemoryOrFallback; final sender = event.senderFromMemoryOrFallback;
await showModalBottomSheet( await showAdaptiveBottomSheet(
context: context, context: context,
builder: (context) => Scaffold( builder: (context) => Scaffold(
appBar: AppBar( appBar: AppBar(

View File

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
import '../../widgets/avatar.dart'; import '../../widgets/avatar.dart';
import '../user_bottom_sheet/user_bottom_sheet.dart'; import '../user_bottom_sheet/user_bottom_sheet.dart';
@ -28,7 +29,7 @@ class ParticipantListItem extends StatelessWidget {
return Opacity( return Opacity(
opacity: user.membership == Membership.join ? 1 : 0.5, opacity: user.membership == Membership.join ? 1 : 0.5,
child: ListTile( child: ListTile(
onTap: () => showModalBottomSheet( onTap: () => showAdaptiveBottomSheet(
context: context, context: context,
builder: (c) => UserBottomSheet( builder: (c) => UserBottomSheet(
user: user, user: user,

View File

@ -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/search_title.dart';
import 'package:fluffychat/pages/chat_list/space_view.dart'; import 'package:fluffychat/pages/chat_list/space_view.dart';
import 'package:fluffychat/pages/chat_list/stories_header.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/matrix_sdk_extensions/client_stories_extension.dart';
import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/utils/stream_extension.dart';
import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/avatar.dart';
@ -101,7 +102,7 @@ class ChatListViewBody extends StatelessWidget {
L10n.of(context)!.group, L10n.of(context)!.group,
avatar: avatar:
roomSearchResult.chunk[i].avatarUrl, roomSearchResult.chunk[i].avatarUrl,
onPressed: () => showModalBottomSheet( onPressed: () => showAdaptiveBottomSheet(
context: context, context: context,
builder: (c) => PublicRoomBottomSheet( builder: (c) => PublicRoomBottomSheet(
roomAlias: roomSearchResult roomAlias: roomSearchResult
@ -140,7 +141,7 @@ class ChatListViewBody extends StatelessWidget {
L10n.of(context)!.unknownDevice, L10n.of(context)!.unknownDevice,
avatar: avatar:
userSearchResult.results[i].avatarUrl, userSearchResult.results[i].avatarUrl,
onPressed: () => showModalBottomSheet( onPressed: () => showAdaptiveBottomSheet(
context: context, context: context,
builder: (c) => ProfileBottomSheet( builder: (c) => ProfileBottomSheet(
userId: userSearchResult userId: userSearchResult

View File

@ -15,6 +15,7 @@ import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/config/app_config.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_bottom_sheet.dart';
import 'package:fluffychat/pages/homeserver_picker/homeserver_picker_view.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 'package:fluffychat/widgets/matrix.dart';
import '../../utils/localized_exception_extension.dart'; import '../../utils/localized_exception_extension.dart';
@ -71,7 +72,8 @@ class HomeserverPickerController extends State<HomeserverPicker> {
} }
} }
void showServerInfo(HomeserverBenchmarkResult server) => showModalBottomSheet( void showServerInfo(HomeserverBenchmarkResult server) =>
showAdaptiveBottomSheet(
context: context, context: context,
builder: (_) => HomeserverBottomSheet( builder: (_) => HomeserverBottomSheet(
homeserver: server, homeserver: server,

View File

@ -10,13 +10,13 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:matrix/encryption.dart'; import 'package:matrix/encryption.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/avatar.dart';
class KeyVerificationDialog extends StatefulWidget { class KeyVerificationDialog extends StatefulWidget {
Future<void> show(BuildContext context) => showModalBottomSheet( Future<void> show(BuildContext context) => showAdaptiveBottomSheet(
context: context, context: context,
builder: (context) => this, builder: (context) => this,
useRootNavigator: false,
isDismissible: false, isDismissible: false,
); );

View File

@ -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/new_private_chat_view.dart';
import 'package:fluffychat/pages/new_private_chat/qr_scanner_modal.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/fluffy_share.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/utils/url_launcher.dart';
@ -75,10 +76,8 @@ class NewPrivateChatController extends State<NewPrivateChat> {
return; return;
} }
} }
await showModalBottomSheet( await showAdaptiveBottomSheet(
context: context, context: context,
useRootNavigator: false,
//useSafeArea: false,
builder: (_) => const QrScannerModal(), builder: (_) => const QrScannerModal(),
); );
} }

View File

@ -13,6 +13,7 @@ import 'package:video_player/video_player.dart';
import 'package:vrouter/vrouter.dart'; import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/pages/story/story_view.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/date_time_extension.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/client_stories_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/client_stories_extension.dart';
@ -58,7 +59,7 @@ class StoryPageController extends State<StoryPage> {
void replyEmojiAction() async { void replyEmojiAction() async {
if (replyLoading) return; if (replyLoading) return;
_modalOpened = true; _modalOpened = true;
await showModalBottomSheet( await showAdaptiveBottomSheet(
context: context, context: context,
builder: (context) => EmojiPicker( builder: (context) => EmojiPicker(
onEmojiSelected: (c, e) { onEmojiSelected: (c, e) {
@ -123,7 +124,7 @@ class StoryPageController extends State<StoryPage> {
void displaySeenByUsers() async { void displaySeenByUsers() async {
_modalOpened = true; _modalOpened = true;
await showModalBottomSheet( await showAdaptiveBottomSheet(
context: context, context: context,
builder: (context) => Scaffold( builder: (context) => Scaffold(
appBar: AppBar( appBar: AppBar(

View File

@ -10,6 +10,17 @@ import 'package:fluffychat/widgets/permission_slider_dialog.dart';
import '../../widgets/matrix.dart'; import '../../widgets/matrix.dart';
import 'user_bottom_sheet_view.dart'; import 'user_bottom_sheet_view.dart';
enum UserBottomSheetAction {
report,
mention,
ban,
kick,
unban,
permission,
message,
ignore,
}
class UserBottomSheet extends StatefulWidget { class UserBottomSheet extends StatefulWidget {
final User user; final User user;
final Function? onMention; final Function? onMention;
@ -27,7 +38,7 @@ class UserBottomSheet extends StatefulWidget {
} }
class UserBottomSheetController extends State<UserBottomSheet> { class UserBottomSheetController extends State<UserBottomSheet> {
void participantAction(String action) async { void participantAction(UserBottomSheetAction action) async {
// ignore: prefer_function_declarations_over_variables // ignore: prefer_function_declarations_over_variables
final Function askConfirmation = () async => (await showOkCancelAlertDialog( final Function askConfirmation = () async => (await showOkCancelAlertDialog(
useRootNavigator: false, useRootNavigator: false,
@ -38,7 +49,7 @@ class UserBottomSheetController extends State<UserBottomSheet> {
) == ) ==
OkCancelResult.ok); OkCancelResult.ok);
switch (action) { switch (action) {
case 'report': case UserBottomSheetAction.report:
final event = widget.user; final event = widget.user;
final score = await showConfirmationDialog<int>( final score = await showConfirmationDialog<int>(
context: context, context: context,
@ -82,11 +93,11 @@ class UserBottomSheetController extends State<UserBottomSheet> {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context)!.contentHasBeenReported))); SnackBar(content: Text(L10n.of(context)!.contentHasBeenReported)));
break; break;
case 'mention': case UserBottomSheetAction.mention:
Navigator.of(context, rootNavigator: false).pop(); Navigator.of(context, rootNavigator: false).pop();
widget.onMention!(); widget.onMention!();
break; break;
case 'ban': case UserBottomSheetAction.ban:
if (await askConfirmation()) { if (await askConfirmation()) {
await showFutureLoadingDialog( await showFutureLoadingDialog(
context: context, context: context,
@ -95,7 +106,7 @@ class UserBottomSheetController extends State<UserBottomSheet> {
Navigator.of(context, rootNavigator: false).pop(); Navigator.of(context, rootNavigator: false).pop();
} }
break; break;
case 'unban': case UserBottomSheetAction.unban:
if (await askConfirmation()) { if (await askConfirmation()) {
await showFutureLoadingDialog( await showFutureLoadingDialog(
context: context, context: context,
@ -104,7 +115,7 @@ class UserBottomSheetController extends State<UserBottomSheet> {
Navigator.of(context, rootNavigator: false).pop(); Navigator.of(context, rootNavigator: false).pop();
} }
break; break;
case 'kick': case UserBottomSheetAction.kick:
if (await askConfirmation()) { if (await askConfirmation()) {
await showFutureLoadingDialog( await showFutureLoadingDialog(
context: context, context: context,
@ -113,7 +124,7 @@ class UserBottomSheetController extends State<UserBottomSheet> {
Navigator.of(context, rootNavigator: false).pop(); Navigator.of(context, rootNavigator: false).pop();
} }
break; break;
case 'permission': case UserBottomSheetAction.permission:
final newPermission = await showPermissionChooser( final newPermission = await showPermissionChooser(
context, context,
currentLevel: widget.user.powerLevel, currentLevel: widget.user.powerLevel,
@ -127,7 +138,7 @@ class UserBottomSheetController extends State<UserBottomSheet> {
Navigator.of(context, rootNavigator: false).pop(); Navigator.of(context, rootNavigator: false).pop();
} }
break; break;
case 'message': case UserBottomSheetAction.message:
final roomIdResult = await showFutureLoadingDialog( final roomIdResult = await showFutureLoadingDialog(
context: context, context: context,
future: () => widget.user.startDirectChat(), future: () => widget.user.startDirectChat(),
@ -137,7 +148,7 @@ class UserBottomSheetController extends State<UserBottomSheet> {
.toSegments(['rooms', roomIdResult.result!]); .toSegments(['rooms', roomIdResult.result!]);
Navigator.of(context, rootNavigator: false).pop(); Navigator.of(context, rootNavigator: false).pop();
break; break;
case 'ignore': case UserBottomSheetAction.ignore:
if (await askConfirmation()) { if (await askConfirmation()) {
await showFutureLoadingDialog( await showFutureLoadingDialog(
context: context, context: context,

View File

@ -1,15 +1,11 @@
import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:fluffychat/widgets/avatar.dart';
import '../../utils/matrix_sdk_extensions/presence_extension.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 '../../widgets/matrix.dart';
import 'user_bottom_sheet.dart'; import 'user_bottom_sheet.dart';
@ -23,155 +19,114 @@ class UserBottomSheetView extends StatelessWidget {
final user = controller.widget.user; final user = controller.widget.user;
final client = Matrix.of(context).client; final client = Matrix.of(context).client;
final presence = client.presences[user.id]; final presence = client.presences[user.id];
return Center( return SafeArea(
child: SizedBox( child: Scaffold(
width: min( appBar: AppBar(
MediaQuery.of(context).size.width, FluffyThemes.columnWidth * 1.5), leading: CloseButton(
child: Material( onPressed: Navigator.of(context, rootNavigator: false).pop,
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),
),
],
),
),
), ),
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),
],
);
}
}

View File

@ -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<T?> showAdaptiveBottomSheet<T>({
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),
),
),
);

View File

@ -10,6 +10,7 @@ import 'package:url_launcher/url_launcher.dart';
import 'package:vrouter/vrouter.dart'; import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/config/app_config.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/matrix.dart';
import 'package:fluffychat/widgets/profile_bottom_sheet.dart'; import 'package:fluffychat/widgets/profile_bottom_sheet.dart';
import 'package:fluffychat/widgets/public_room_bottom_sheet.dart'; import 'package:fluffychat/widgets/public_room_bottom_sheet.dart';
@ -145,7 +146,7 @@ class UrlLauncher {
} }
return; return;
} else { } else {
await showModalBottomSheet( await showAdaptiveBottomSheet(
context: context, context: context,
builder: (c) => PublicRoomBottomSheet( builder: (c) => PublicRoomBottomSheet(
roomAlias: identityParts.primaryIdentifier, roomAlias: identityParts.primaryIdentifier,
@ -182,7 +183,7 @@ class UrlLauncher {
} }
} }
} else if (identityParts.primaryIdentifier.sigil == '@') { } else if (identityParts.primaryIdentifier.sigil == '@') {
await showModalBottomSheet( await showAdaptiveBottomSheet(
context: context, context: context,
builder: (c) => ProfileBottomSheet( builder: (c) => ProfileBottomSheet(
userId: identityParts.primaryIdentifier, userId: identityParts.primaryIdentifier,

View File

@ -14,6 +14,7 @@ import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/pages/chat/cupertino_widgets_bottom_sheet.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/edit_widgets_dialog.dart';
import 'package:fluffychat/pages/chat/widgets_bottom_sheet.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 'm2_popup_menu_button.dart';
import 'matrix.dart'; import 'matrix.dart';
@ -185,7 +186,7 @@ class ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
context: context, context: context,
builder: (context) => CupertinoWidgetsBottomSheet(room: widget.room), builder: (context) => CupertinoWidgetsBottomSheet(room: widget.room),
) )
: showModalBottomSheet( : showAdaptiveBottomSheet(
context: context, context: context,
builder: (context) => WidgetsBottomSheet(room: widget.room), builder: (context) => WidgetsBottomSheet(room: widget.room),
); );

View File

@ -1,5 +1,3 @@
import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.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:matrix/matrix.dart';
import 'package:vrouter/vrouter.dart'; import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/content_banner.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
import '../utils/localized_exception_extension.dart';
class ProfileBottomSheet extends StatelessWidget { class ProfileBottomSheet extends StatelessWidget {
final String userId; final String userId;
@ -37,79 +33,65 @@ class ProfileBottomSheet extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Center( return SafeArea(
child: SizedBox( child: FutureBuilder<Profile>(
width: min( future: Matrix.of(context).client.getProfileFromUserId(userId),
MediaQuery.of(context).size.width, FluffyThemes.columnWidth * 1.5), builder: (context, snapshot) {
child: Material( final profile = snapshot.data;
elevation: 4, return Scaffold(
child: SafeArea( appBar: AppBar(
child: Scaffold( leading: CloseButton(
extendBodyBehindAppBar: true, onPressed: Navigator.of(context, rootNavigator: false).pop,
appBar: AppBar( ),
elevation: 0, title: ListTile(
backgroundColor: contentPadding: const EdgeInsets.only(right: 16.0),
Theme.of(context).scaffoldBackgroundColor.withOpacity(0.5), title: Text(
leading: IconButton( profile?.displayName ?? userId.localpart ?? userId,
icon: const Icon(Icons.arrow_downward_outlined), style: const TextStyle(fontSize: 18),
onPressed: Navigator.of(context, rootNavigator: false).pop, ),
tooltip: L10n.of(context)!.close, subtitle: Text(
userId,
style: const TextStyle(fontSize: 12),
), ),
), ),
body: FutureBuilder<Profile>( actions: [
future: Padding(
Matrix.of(context).client.getProfileFromUserId(userId), padding: const EdgeInsets.all(8.0),
builder: (context, snapshot) { child: OutlinedButton.icon(
final profile = snapshot.data; onPressed: () => _startDirectChat(context),
icon: Icon(Icons.adaptive.share_outlined),
return Column( label: Text(L10n.of(context)!.share),
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),
],
);
}),
), ),
), 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),
],
),
);
},
), ),
); );
} }

View File

@ -1,5 +1,3 @@
import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.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:matrix_link_text/link_text.dart';
import 'package:vrouter/vrouter.dart'; import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/utils/url_launcher.dart';
import 'package:fluffychat/widgets/content_banner.dart'; import 'package:fluffychat/widgets/content_banner.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
@ -72,101 +69,87 @@ class PublicRoomBottomSheet extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final roomAlias = this.roomAlias; final roomAlias = this.roomAlias;
return Center( return SafeArea(
child: SizedBox( child: Scaffold(
width: min( extendBodyBehindAppBar: true,
MediaQuery.of(context).size.width, FluffyThemes.columnWidth * 1.5), appBar: AppBar(
child: Material( elevation: 0,
elevation: 4, titleSpacing: 0,
child: SafeArea( backgroundColor:
child: Scaffold( Theme.of(context).scaffoldBackgroundColor.withOpacity(0.5),
extendBodyBehindAppBar: true, title: Text(
appBar: AppBar( roomAlias ?? chunk!.name ?? chunk!.roomId,
elevation: 0, overflow: TextOverflow.fade,
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<PublicRoomsChunk>(
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(),
),
),
],
);
}),
),
), ),
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<PublicRoomsChunk>(
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(),
),
),
],
);
}),
), ),
); );
} }