mirror of
https://github.com/krille-chan/fluffychat
synced 2024-09-10 10:25:11 +00:00
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:
commit
68d2417be4
19 changed files with 333 additions and 360 deletions
|
@ -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);
|
||||
|
|
|
@ -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<Chat> {
|
|||
}
|
||||
|
||||
void sendStickerAction() async {
|
||||
final sticker = await showModalBottomSheet<ImagePackImageContent>(
|
||||
final sticker = await showAdaptiveBottomSheet<ImagePackImageContent>(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
builder: (c) => StickerPickerDialog(room: room!),
|
||||
);
|
||||
if (sticker == null) return;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<HomeserverPicker> {
|
|||
}
|
||||
}
|
||||
|
||||
void showServerInfo(HomeserverBenchmarkResult server) => showModalBottomSheet(
|
||||
void showServerInfo(HomeserverBenchmarkResult server) =>
|
||||
showAdaptiveBottomSheet(
|
||||
context: context,
|
||||
builder: (_) => HomeserverBottomSheet(
|
||||
homeserver: server,
|
||||
|
|
|
@ -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<void> show(BuildContext context) => showModalBottomSheet(
|
||||
Future<void> show(BuildContext context) => showAdaptiveBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => this,
|
||||
useRootNavigator: false,
|
||||
isDismissible: false,
|
||||
);
|
||||
|
||||
|
|
|
@ -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<NewPrivateChat> {
|
|||
return;
|
||||
}
|
||||
}
|
||||
await showModalBottomSheet(
|
||||
await showAdaptiveBottomSheet(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
//useSafeArea: false,
|
||||
builder: (_) => const QrScannerModal(),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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<StoryPage> {
|
|||
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<StoryPage> {
|
|||
|
||||
void displaySeenByUsers() async {
|
||||
_modalOpened = true;
|
||||
await showModalBottomSheet(
|
||||
await showAdaptiveBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
|
|
|
@ -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<UserBottomSheet> {
|
||||
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<UserBottomSheet> {
|
|||
) ==
|
||||
OkCancelResult.ok);
|
||||
switch (action) {
|
||||
case 'report':
|
||||
case UserBottomSheetAction.report:
|
||||
final event = widget.user;
|
||||
final score = await showConfirmationDialog<int>(
|
||||
context: context,
|
||||
|
@ -82,11 +93,11 @@ class UserBottomSheetController extends State<UserBottomSheet> {
|
|||
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<UserBottomSheet> {
|
|||
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<UserBottomSheet> {
|
|||
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<UserBottomSheet> {
|
|||
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<UserBottomSheet> {
|
|||
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<UserBottomSheet> {
|
|||
.toSegments(['rooms', roomIdResult.result!]);
|
||||
Navigator.of(context, rootNavigator: false).pop();
|
||||
break;
|
||||
case 'ignore':
|
||||
case UserBottomSheetAction.ignore:
|
||||
if (await askConfirmation()) {
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
|
|
|
@ -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),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
28
lib/utils/adaptive_bottom_sheet.dart
Normal file
28
lib/utils/adaptive_bottom_sheet.dart
Normal 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),
|
||||
),
|
||||
),
|
||||
);
|
|
@ -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,
|
||||
|
|
|
@ -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<ChatSettingsPopupMenu> {
|
|||
context: context,
|
||||
builder: (context) => CupertinoWidgetsBottomSheet(room: widget.room),
|
||||
)
|
||||
: showModalBottomSheet(
|
||||
: showAdaptiveBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => WidgetsBottomSheet(room: widget.room),
|
||||
);
|
||||
|
|
|
@ -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<Profile>(
|
||||
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<Profile>(
|
||||
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),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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<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(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
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<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(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue