diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index fa06a944a..334de740b 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -3126,5 +3126,12 @@ "theProcessWasCanceled": "The process was canceled.", "join": "Join", "searchOrEnterHomeserverAddress": "Search or enter homeserver address", - "matrixId": "Matrix ID" + "matrixId": "Matrix ID", + "setPowerLevel": "Set power level", + "makeModerator": "Make moderator", + "makeAdmin": "Make admin", + "removeModeratorRights": "Remove moderator rights", + "removeAdminRights": "Remove admin rights", + "powerLevel": "Power level", + "setPowerLevelDescription": "Power levels define what a member is allowed to do in this room and usually range between 0 and 100." } diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 2daa40654..3906354c1 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -210,6 +210,7 @@ class Message extends StatelessWidget { singleSelected && event.room.canSendDefaultMessages; final enterThread = this.enterThread; + final sender = event.senderFromMemoryOrFallback; return Center( child: Swipeable( @@ -358,9 +359,7 @@ class Message extends StatelessWidget { FutureBuilder( future: event.fetchSenderUser(), builder: (context, snapshot) { - final user = - snapshot.data ?? - event.senderFromMemoryOrFallback; + final user = snapshot.data ?? sender; return Avatar( mxContent: user.avatarUrl, name: user.calcDisplayname(), @@ -392,52 +391,78 @@ class Message extends StatelessWidget { ownMessage || event.room.isDirectChat ? const SizedBox(height: 12) - : FutureBuilder( - future: event - .fetchSenderUser(), - builder: (context, snapshot) { - final displayname = - snapshot.data - ?.calcDisplayname() ?? - event - .senderFromMemoryOrFallback - .calcDisplayname(); - return Text( - displayname, - style: TextStyle( - fontSize: 11, - fontWeight: - FontWeight.bold, - color: - (theme.brightness == - Brightness - .light - ? displayname - .color - : displayname - .lightColorText), - shadows: - !wallpaperMode - ? null - : [ - const Shadow( - offset: - Offset( - 0.0, - 0.0, - ), - blurRadius: - 3, - color: Colors - .black, - ), - ], + : Row( + children: [ + if (sender.powerLevel >= + 50) + Padding( + padding: + const EdgeInsets.only( + right: 2.0, + ), + child: Icon( + sender.powerLevel >= + 100 + ? Icons + .admin_panel_settings + : Icons + .add_moderator_outlined, + size: 14, + color: theme + .colorScheme + .onPrimaryContainer, + ), ), - maxLines: 1, - overflow: TextOverflow - .ellipsis, - ); - }, + Expanded( + child: FutureBuilder( + future: event + .fetchSenderUser(), + builder: (context, snapshot) { + final displayname = + snapshot.data + ?.calcDisplayname() ?? + sender + .calcDisplayname(); + return Text( + displayname, + style: TextStyle( + fontSize: 11, + fontWeight: + FontWeight + .bold, + color: + (theme.brightness == + Brightness + .light + ? displayname + .color + : displayname + .lightColorText), + shadows: + !wallpaperMode + ? null + : [ + const Shadow( + offset: Offset( + 0.0, + 0.0, + ), + blurRadius: + 3, + color: + Colors.black, + ), + ], + ), + maxLines: 1, + overflow: + TextOverflow + .ellipsis, + ); + }, + ), + ), + ], ), ), Container( diff --git a/lib/widgets/member_actions_popup_menu_button.dart b/lib/widgets/member_actions_popup_menu_button.dart index 55eea2cb3..395038859 100644 --- a/lib/widgets/member_actions_popup_menu_button.dart +++ b/lib/widgets/member_actions_popup_menu_button.dart @@ -43,13 +43,7 @@ Future showMemberActionsPopupMenu({ child: Row( spacing: 12.0, children: [ - Avatar( - name: displayname, - size: 30, - mxContent: user.avatarUrl, - presenceUserId: user.id, - presenceBackgroundColor: theme.colorScheme.surfaceContainer, - ), + Avatar(name: displayname, size: 30, mxContent: user.avatarUrl), ConstrainedBox( constraints: const BoxConstraints(maxWidth: 200), child: Text( @@ -83,31 +77,71 @@ Future showMemberActionsPopupMenu({ ], ), ), - 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: .min, - crossAxisAlignment: .start, + if (user.canChangeUserPowerLevel) ...[ + if (user.powerLevel < 100) + PopupMenuItem( + value: _MemberActions.makeAdmin, + child: Row( 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), - ), + const Icon(Icons.admin_panel_settings_outlined), + const SizedBox(width: 18), + Text(L10n.of(context).makeAdmin), ], ), - ], + ), + if (user.powerLevel < 50) + PopupMenuItem( + value: _MemberActions.makeModerator, + child: Row( + children: [ + const Icon(Icons.add_moderator_outlined), + const SizedBox(width: 18), + Text(L10n.of(context).makeModerator), + ], + ), + ), + if (user.powerLevel >= 100) + PopupMenuItem( + value: _MemberActions.removeAdmin, + child: Row( + children: [ + const Icon(Icons.remove_moderator_outlined), + const SizedBox(width: 18), + Text(L10n.of(context).removeAdminRights), + ], + ), + ) + else if (user.powerLevel >= 50) + PopupMenuItem( + value: _MemberActions.removeModerator, + child: Row( + children: [ + const Icon(Icons.remove_moderator_outlined), + const SizedBox(width: 18), + Text(L10n.of(context).removeModeratorRights), + ], + ), + ), + ], + if (user.canChangeUserPowerLevel || + !{0, 50, 100}.contains(user.powerLevel)) + PopupMenuItem( + value: _MemberActions.setPowerLevel, + enabled: user.canChangeUserPowerLevel, + child: Row( + children: [ + const Icon(Icons.manage_accounts_outlined), + const SizedBox(width: 18), + Text( + user.canChangeUserPowerLevel + ? L10n.of(context).setPowerLevel + : L10n.of(context).powerLevel, + ), + if (!{0, 50, 100}.contains(user.powerLevel)) + Text(' (${user.powerLevel})'), + ], + ), ), - ), if (user.canKick) PopupMenuItem( value: _MemberActions.kick, @@ -179,7 +213,7 @@ Future showMemberActionsPopupMenu({ case _MemberActions.mention: onMention?.call(); return; - case _MemberActions.setRole: + case _MemberActions.setPowerLevel: final power = await showPermissionChooser( context, currentLevel: user.powerLevel, @@ -280,13 +314,48 @@ Future showMemberActionsPopupMenu({ future: () => user.unban(), ); } + case _MemberActions.makeAdmin: + if (user.room.ownPowerLevel <= 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(100), + ); + case _MemberActions.makeModerator: + await showFutureLoadingDialog( + context: context, + future: () => user.setPower(50), + ); + case _MemberActions.removeAdmin: + case _MemberActions.removeModerator: + final defaultUserLevel = + user.room + .getState(EventTypes.RoomPowerLevels) + ?.content + .tryGet('users_default') ?? + 0; + await showFutureLoadingDialog( + context: context, + future: () => user.setPower(defaultUserLevel), + ); } } enum _MemberActions { info, mention, - setRole, + setPowerLevel, + makeAdmin, + makeModerator, + removeAdmin, + removeModerator, kick, ban, approve, diff --git a/lib/widgets/permission_slider_dialog.dart b/lib/widgets/permission_slider_dialog.dart index dd613eedd..1a0f2556d 100644 --- a/lib/widgets/permission_slider_dialog.dart +++ b/lib/widgets/permission_slider_dialog.dart @@ -9,7 +9,7 @@ Future showPermissionChooser( int currentLevel = 0, int maxLevel = 100, }) async { - final controller = TextEditingController(); + final controller = TextEditingController(text: currentLevel.toString()); final error = ValueNotifier(null); return await showAdaptiveDialog( context: context, @@ -22,7 +22,7 @@ Future showPermissionChooser( crossAxisAlignment: .stretch, spacing: 12.0, children: [ - Text(L10n.of(context).setPermissionsLevelDescription), + Text(L10n.of(context).setPowerLevelDescription), ValueListenableBuilder( valueListenable: error, builder: (context, errorText, _) => DialogTextField( @@ -38,8 +38,6 @@ Future showPermissionChooser( ), actions: [ AdaptiveDialogAction( - bigButtons: true, - borderRadius: AdaptiveDialogAction.topRadius, onPressed: () { final level = int.tryParse(controller.text.trim()); if (level == null) { @@ -52,31 +50,12 @@ Future showPermissionChooser( } Navigator.of(context).pop(level); }, - child: Text(L10n.of(context).setCustomPermissionLevel), + child: Text(L10n.of(context).setPowerLevel), + ), + AdaptiveDialogAction( + onPressed: () => Navigator.of(context).pop(null), + child: Text(L10n.of(context).cancel), ), - if (maxLevel >= 100 && currentLevel != 100) - AdaptiveDialogAction( - borderRadius: AdaptiveDialogAction.centerRadius, - bigButtons: true, - onPressed: () => Navigator.of(context).pop(100), - child: Text(L10n.of(context).admin), - ), - if (maxLevel >= 50 && currentLevel != 50) - AdaptiveDialogAction( - borderRadius: maxLevel != 0 - ? AdaptiveDialogAction.centerRadius - : AdaptiveDialogAction.bottomRadius, - bigButtons: true, - onPressed: () => Navigator.of(context).pop(50), - child: Text(L10n.of(context).moderator), - ), - if (currentLevel != 0) - AdaptiveDialogAction( - borderRadius: AdaptiveDialogAction.bottomRadius, - bigButtons: true, - onPressed: () => Navigator.of(context).pop(0), - child: Text(L10n.of(context).normalUser), - ), ], ), );