317 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Dart
		
	
	
	
			
		
		
	
	
			317 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Dart
		
	
	
	
| import 'package:extera_next/widgets/matrix.dart';
 | |
| import 'package:flutter/material.dart';
 | |
| 
 | |
| import 'package:extera_next/generated/l10n/l10n.dart';
 | |
| import 'package:matrix/matrix.dart';
 | |
| 
 | |
| import 'package:extera_next/widgets/permission_slider_dialog.dart';
 | |
| import 'adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
 | |
| import 'adaptive_dialogs/show_text_input_dialog.dart';
 | |
| import 'adaptive_dialogs/user_dialog.dart';
 | |
| import 'avatar.dart';
 | |
| import 'future_loading_dialog.dart';
 | |
| 
 | |
| void showMemberActionsPopupMenu({
 | |
|   required BuildContext context,
 | |
|   required User user,
 | |
|   void Function()? onMention,
 | |
| }) async {
 | |
|   final mx = Matrix.of(context);
 | |
|   final theme = Theme.of(context);
 | |
|   final displayname = user.calcDisplayname();
 | |
|   final isMe = user.room.client.userID == user.id;
 | |
| 
 | |
|   final overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
 | |
| 
 | |
|   final button = context.findRenderObject() as RenderBox;
 | |
| 
 | |
|   final position = RelativeRect.fromRect(
 | |
|     Rect.fromPoints(
 | |
|       button.localToGlobal(const Offset(0, -65), ancestor: overlay),
 | |
|       button.localToGlobal(
 | |
|         button.size.bottomRight(Offset.zero) + const Offset(-50, 0),
 | |
|         ancestor: overlay,
 | |
|       ),
 | |
|     ),
 | |
|     Offset.zero & overlay.size,
 | |
|   );
 | |
| 
 | |
|   final action = await showMenu<_MemberActions>(
 | |
|     context: context,
 | |
|     position: position,
 | |
|     items: <PopupMenuEntry<_MemberActions>>[
 | |
|       PopupMenuItem(
 | |
|         value: _MemberActions.info,
 | |
|         child: Padding(
 | |
|           padding: const EdgeInsets.symmetric(vertical: 8.0),
 | |
|           child: Center(
 | |
|             child: Column(
 | |
|               mainAxisSize: MainAxisSize.min,
 | |
|               children: [
 | |
|                 Avatar(
 | |
|                   name: displayname,
 | |
|                   mxContent: user.avatarUrl,
 | |
|                   presenceUserId: user.id,
 | |
|                   presenceBackgroundColor: theme.colorScheme.surfaceContainer,
 | |
|                 ),
 | |
|                 const SizedBox(height: 8),
 | |
|                 Text(
 | |
|                   displayname,
 | |
|                   textAlign: TextAlign.center,
 | |
|                   style: theme.textTheme.labelLarge,
 | |
|                   maxLines: 1,
 | |
|                   overflow: TextOverflow.ellipsis,
 | |
|                 ),
 | |
|                 Text(
 | |
|                   user.id,
 | |
|                   textAlign: TextAlign.center,
 | |
|                   style: const TextStyle(fontSize: 10),
 | |
|                   maxLines: 1,
 | |
|                   overflow: TextOverflow.ellipsis,
 | |
|                 ),
 | |
|               ],
 | |
|             ),
 | |
|           ),
 | |
|         ),
 | |
|       ),
 | |
|       const PopupMenuDivider(),
 | |
|       if (onMention != null)
 | |
|         PopupMenuItem(
 | |
|           value: _MemberActions.mention,
 | |
|           child: Row(
 | |
|             children: [
 | |
|               const Icon(Icons.alternate_email_outlined),
 | |
|               const SizedBox(width: 18),
 | |
|               Text(L10n.of(context).mention),
 | |
|             ],
 | |
|           ),
 | |
|         ),
 | |
|       if (user.membership == Membership.knock)
 | |
|         PopupMenuItem(
 | |
|           value: _MemberActions.approve,
 | |
|           child: Row(
 | |
|             children: [
 | |
|               const Icon(Icons.how_to_reg_outlined),
 | |
|               const SizedBox(width: 18),
 | |
|               Text(L10n.of(context).approve),
 | |
|             ],
 | |
|           ),
 | |
|         ),
 | |
|       PopupMenuItem(
 | |
|         enabled: user.room.canChangePowerLevel && user.canChangeUserPowerLevel,
 | |
|         value: _MemberActions.setRole,
 | |
|         child: Row(
 | |
|           children: [
 | |
|             const Icon(Icons.admin_panel_settings_outlined),
 | |
|             const SizedBox(width: 18),
 | |
|             Column(
 | |
|               mainAxisSize: MainAxisSize.min,
 | |
|               crossAxisAlignment: CrossAxisAlignment.start,
 | |
|               children: [
 | |
|                 Text(L10n.of(context).chatPermissions),
 | |
|                 Text(
 | |
|                   user.powerLevel < 50
 | |
|                       ? L10n.of(context).userLevel(user.powerLevel)
 | |
|                       : user.powerLevel < 100
 | |
|                           ? L10n.of(context).moderatorLevel(user.powerLevel)
 | |
|                           : L10n.of(context).adminLevel(user.powerLevel),
 | |
|                   style: const TextStyle(fontSize: 10),
 | |
|                 ),
 | |
|               ],
 | |
|             ),
 | |
|           ],
 | |
|         ),
 | |
|       ),
 | |
|       if (user.canKick)
 | |
|         PopupMenuItem(
 | |
|           value: _MemberActions.kick,
 | |
|           child: Row(
 | |
|             children: [
 | |
|               Icon(
 | |
|                 Icons.person_remove_outlined,
 | |
|                 color: theme.colorScheme.onErrorContainer,
 | |
|               ),
 | |
|               const SizedBox(width: 18),
 | |
|               Text(
 | |
|                 L10n.of(context).kickFromChat,
 | |
|                 style: TextStyle(color: theme.colorScheme.onErrorContainer),
 | |
|               ),
 | |
|             ],
 | |
|           ),
 | |
|         ),
 | |
|       if (user.canBan && user.membership != Membership.ban)
 | |
|         PopupMenuItem(
 | |
|           value: _MemberActions.ban,
 | |
|           child: Row(
 | |
|             children: [
 | |
|               Icon(
 | |
|                 Icons.block_outlined,
 | |
|                 color: theme.colorScheme.onErrorContainer,
 | |
|               ),
 | |
|               const SizedBox(width: 18),
 | |
|               Text(
 | |
|                 L10n.of(context).banFromChat,
 | |
|                 style: TextStyle(color: theme.colorScheme.onErrorContainer),
 | |
|               ),
 | |
|             ],
 | |
|           ),
 | |
|         ),
 | |
|       if (user.canBan && user.membership == Membership.ban)
 | |
|         PopupMenuItem(
 | |
|           value: _MemberActions.unban,
 | |
|           child: Row(
 | |
|             children: [
 | |
|               const Icon(Icons.warning),
 | |
|               const SizedBox(width: 18),
 | |
|               Text(L10n.of(context).unbanFromChat),
 | |
|             ],
 | |
|           ),
 | |
|         ),
 | |
|       if (!isMe)
 | |
|         PopupMenuItem(
 | |
|           value: _MemberActions.report,
 | |
|           child: Row(
 | |
|             children: [
 | |
|               Icon(
 | |
|                 Icons.gavel_outlined,
 | |
|                 color: theme.colorScheme.onErrorContainer,
 | |
|               ),
 | |
|               const SizedBox(width: 18),
 | |
|               Text(
 | |
|                 L10n.of(context).reportUser,
 | |
|                 style: TextStyle(color: theme.colorScheme.onErrorContainer),
 | |
|               ),
 | |
|             ],
 | |
|           ),
 | |
|         ),
 | |
|     ],
 | |
|   );
 | |
|   if (action == null) return;
 | |
|   if (!context.mounted) return;
 | |
| 
 | |
|   switch (action) {
 | |
|     case _MemberActions.mention:
 | |
|       onMention?.call();
 | |
|       return;
 | |
|     case _MemberActions.setRole:
 | |
|       final power = await showPermissionChooser(
 | |
|         context,
 | |
|         currentLevel: user.powerLevel,
 | |
|         maxLevel: user.room.ownPowerLevel,
 | |
|       );
 | |
|       if (power == null) return;
 | |
|       if (!context.mounted) return;
 | |
|       if (power >= 100) {
 | |
|         final consent = await showOkCancelAlertDialog(
 | |
|           context: context,
 | |
|           title: L10n.of(context).areYouSure,
 | |
|           message: L10n.of(context).makeAdminDescription,
 | |
|         );
 | |
|         if (consent != OkCancelResult.ok) return;
 | |
|         if (!context.mounted) return;
 | |
|       }
 | |
|       await showFutureLoadingDialog(
 | |
|         context: context,
 | |
|         future: () => user.setPower(power),
 | |
|       );
 | |
|       return;
 | |
|     case _MemberActions.approve:
 | |
|       await showFutureLoadingDialog(
 | |
|         context: context,
 | |
|         future: () => user.room.invite(user.id),
 | |
|       );
 | |
|       return;
 | |
|     case _MemberActions.kick:
 | |
|       final reason = await showTextInputDialog(
 | |
|             context: context,
 | |
|             title: L10n.of(context).doYouWantToKick(user.displayName ?? user.id),
 | |
|             okLabel: L10n.of(context).yes,
 | |
|             cancelLabel: L10n.of(context).no,
 | |
|             message: L10n.of(context).kickUserDescription,
 | |
|             hintText: L10n.of(context).reason,
 | |
|             isDestructive: true
 | |
|           );
 | |
|       if (reason != null) {
 | |
|         await showFutureLoadingDialog(
 | |
|           context: context,
 | |
|           future: () => mx.client.kick(user.room.id, user.id, reason: reason)
 | |
|         );
 | |
|       }
 | |
|       return;
 | |
|     case _MemberActions.ban:
 | |
|       final reason = await showTextInputDialog(
 | |
|             context: context,
 | |
|             title: L10n.of(context).doYouWantToBan(user.displayName ?? user.id),
 | |
|             okLabel: L10n.of(context).yes,
 | |
|             cancelLabel: L10n.of(context).no,
 | |
|             message: L10n.of(context).banUserDescription,
 | |
|             hintText: L10n.of(context).reason,
 | |
|             isDestructive: true
 | |
|           );
 | |
|       if (reason != null) {
 | |
|         await showFutureLoadingDialog(
 | |
|           context: context,
 | |
|           future: () => mx.client.ban(user.room.id, user.id, reason: reason)
 | |
|         );
 | |
|       }
 | |
|       return;
 | |
|     case _MemberActions.report:
 | |
|       final reason = await showTextInputDialog(
 | |
|         context: context,
 | |
|         title: L10n.of(context).whyDoYouWantToReportThis,
 | |
|         okLabel: L10n.of(context).report,
 | |
|         cancelLabel: L10n.of(context).cancel,
 | |
|         hintText: L10n.of(context).reason,
 | |
|       );
 | |
|       if (reason == null || reason.isEmpty) return;
 | |
| 
 | |
|       final result = await showFutureLoadingDialog(
 | |
|         context: context,
 | |
|         future: () => user.room.client.reportUser(
 | |
|           user.id,
 | |
|           reason,
 | |
|         ),
 | |
|       );
 | |
|       if (result.error != null) return;
 | |
|       ScaffoldMessenger.of(context).showSnackBar(
 | |
|         SnackBar(content: Text(L10n.of(context).contentHasBeenReported)),
 | |
|       );
 | |
|       return;
 | |
|     case _MemberActions.info:
 | |
|       await UserDialog.show(
 | |
|         context: context,
 | |
|         profile: Profile(
 | |
|           userId: user.id,
 | |
|           displayName: user.displayName,
 | |
|           avatarUrl: user.avatarUrl,
 | |
|         ),
 | |
|       );
 | |
|       return;
 | |
|     case _MemberActions.unban:
 | |
|       if (await showOkCancelAlertDialog(
 | |
|             context: context,
 | |
|             title: L10n.of(context).areYouSure,
 | |
|             okLabel: L10n.of(context).yes,
 | |
|             cancelLabel: L10n.of(context).no,
 | |
|             message: L10n.of(context).unbanUserDescription,
 | |
|           ) ==
 | |
|           OkCancelResult.ok) {
 | |
|         await showFutureLoadingDialog(
 | |
|           context: context,
 | |
|           future: () => user.unban(),
 | |
|         );
 | |
|       }
 | |
|   }
 | |
| }
 | |
| 
 | |
| enum _MemberActions {
 | |
|   info,
 | |
|   mention,
 | |
|   setRole,
 | |
|   kick,
 | |
|   ban,
 | |
|   approve,
 | |
|   unban,
 | |
|   report,
 | |
| }
 |