diff --git a/TODO.md b/TODO.md index fc176a1f..37e6a199 100644 --- a/TODO.md +++ b/TODO.md @@ -48,7 +48,7 @@ - Make clicking on user/room mentions open relevant UI instead of matrix.to URL in browser -- Make rooms fully manageable within Mirage: settings, permissions, kick, etc +- Make rooms fully manageable within Mirage: settings, permissions, unban - Labeled text area component, use it for room creation/settings topic - Linkify URLs in topic text areas @@ -185,7 +185,6 @@ - `RoomMessageMedia` and `RoomAvatarEvent` info attributes - Handle `m.room.aliases` events -- Support "Empty room (was ...)" after peer left - Left room events after client reboot - Previewing room without joining diff --git a/src/backend/matrix_client.py b/src/backend/matrix_client.py index 55d7bef9..77610ab3 100644 --- a/src/backend/matrix_client.py +++ b/src/backend/matrix_client.py @@ -698,7 +698,8 @@ class MatrixClient(nio.AsyncClient): if room_id in self.fully_loaded_rooms or \ room_id in self.invited_rooms or \ - room_id in self.cleared_events_rooms: + room_id in self.cleared_events_rooms or \ + self.models[self.user_id, "rooms"][room_id].left: return False await self.first_sync_done.wait() diff --git a/src/gui/Pages/Chat/RoomPane/MemberDelegate.qml b/src/gui/Pages/Chat/RoomPane/MemberDelegate.qml index 8ac1e5b8..d3cd3733 100644 --- a/src/gui/Pages/Chat/RoomPane/MemberDelegate.qml +++ b/src/gui/Pages/Chat/RoomPane/MemberDelegate.qml @@ -4,6 +4,7 @@ import QtQuick 2.12 import Clipboard 0.1 import "../../../Base" import "../../../Base/HTile" +import "../../../Popups" HTileDelegate { id: member @@ -59,14 +60,17 @@ HTileDelegate { text: model.invited ? qsTr("Disinvite") : qsTr("Kick") enabled: false - popup: "Popups/KickPopup.qml" + popup: "Popups/RemoveMemberPopup.qml" popupParent: chat properties: ({ userId: chat.userId, roomId: chat.roomId, targetUserId: model.id, targetDisplayName: model.display_name, - targetIsInvited: model.invited, + operation: + model.invited ? + RemoveMemberPopup.Operation.Disinvite : + RemoveMemberPopup.Operation.Kick, }) Component.onCompleted: py.callClientCoro( @@ -76,6 +80,30 @@ HTileDelegate { can => { enabled = can }, ) } + + HMenuItemPopupSpawner { + icon.name: "room-ban" + icon.color: theme.colors.negativeBackground + text: qsTr("Ban") + enabled: false + + popup: "Popups/RemoveMemberPopup.qml" + popupParent: chat + properties: ({ + userId: chat.userId, + roomId: chat.roomId, + targetUserId: model.id, + targetDisplayName: model.display_name, + operation: RemoveMemberPopup.Operation.Ban, + }) + + Component.onCompleted: py.callClientCoro( + chat.userId, + "can_ban", + [chat.roomId, model.id], + can => { enabled = can }, + ) + } } diff --git a/src/gui/Popups/KickPopup.qml b/src/gui/Popups/RemoveMemberPopup.qml similarity index 51% rename from src/gui/Popups/KickPopup.qml rename to src/gui/Popups/RemoveMemberPopup.qml index 26b7c720..1297584a 100644 --- a/src/gui/Popups/KickPopup.qml +++ b/src/gui/Popups/RemoveMemberPopup.qml @@ -7,25 +7,39 @@ import "../Base" BoxPopup { summary.textFormat: Text.StyledText summary.text: - targetIsInvited ? - qsTr("Withdraw %1's invitation?").arg(coloredTarget) : - qsTr("Kick %1 out of the room?").arg(coloredTarget) + operation === RemoveMemberPopup.Operation.Disinvite ? + qsTr("Disinvite %1 from the room?").arg(coloredTarget) : - okText: qsTr("Kick") + operation === RemoveMemberPopup.Operation.Kick ? + qsTr("Kick %1 out of the room?").arg(coloredTarget) : + + qsTr("Ban %1 from the room?").arg(coloredTarget) + + okText: + operation === RemoveMemberPopup.Operation.Disinvite ? + qsTr("Disinvite") : + + operation === RemoveMemberPopup.Operation.Kick ? + qsTr("Kick") : + + qsTr("Ban") onOpened: reasonField.field.forceActiveFocus() onOk: py.callClientCoro( userId, - "room_kick", + operation === RemoveMemberPopup.Operation.Ban ? + "room_ban" : "room_kick", [roomId, targetUserId, reasonField.field.text || null], ) + enum Operation { Disinvite, Kick, Ban } + property string userId property string roomId property string targetUserId property string targetDisplayName - property bool targetIsInvited: false + property int operation readonly property string coloredTarget: utils.coloredNameHtml(targetDisplayName, targetUserId) diff --git a/src/icons/thin/room-ban.svg b/src/icons/thin/room-ban.svg new file mode 100644 index 00000000..a817918b --- /dev/null +++ b/src/icons/thin/room-ban.svg @@ -0,0 +1,3 @@ + + +