From e60a7f6dacbdfa2afb676ceb9467f41bd44c23b2 Mon Sep 17 00:00:00 2001 From: vSLG Date: Wed, 1 Apr 2020 14:33:19 -0300 Subject: [PATCH] Make "Remove" option conditional in context menu Refactor code in the backend so things can work better --- src/backend/matrix_client.py | 10 +++- src/backend/models/items.py | 1 + src/backend/nio_callbacks.py | 54 ++++++++++++------- src/gui/Pages/Chat/Timeline/EventDelegate.qml | 20 +++++-- src/gui/Popups/RedactEvents.qml | 2 + src/gui/Utils.qml | 16 +++--- 6 files changed, 69 insertions(+), 34 deletions(-) diff --git a/src/backend/matrix_client.py b/src/backend/matrix_client.py index b1a3a551..a290828e 100644 --- a/src/backend/matrix_client.py +++ b/src/backend/matrix_client.py @@ -875,9 +875,10 @@ class MatrixClient(nio.AsyncClient): return (successes, errors) + async def room_mass_redact( self, room_id: str, reason: str, *event_ids: str, - ): + ) -> List[nio.responses.RoomRedactResponse]: """Redact events from a room in parallel. Returns a list of sucessful redacts. @@ -888,6 +889,7 @@ class MatrixClient(nio.AsyncClient): for evt_id in event_ids ]) + async def generate_thumbnail( self, data: UploadData, is_svg: bool = False, ) -> Tuple[bytes, MatrixImageInfo]: @@ -1117,6 +1119,7 @@ class MatrixClient(nio.AsyncClient): guests_allowed = room.guest_access == "can_join", can_invite = levels.can_user_invite(self.user), + can_redact = levels.can_user_redact(self.user), can_send_messages = can_send_msg(), can_set_name = can_send_state("m.room.name"), can_set_topic = can_send_state("m.room.topic"), @@ -1186,7 +1189,10 @@ class MatrixClient(nio.AsyncClient): async def register_nio_event( - self, room: nio.MatrixRoom, ev: nio.Event, event_id: str = None, + self, + room: nio.MatrixRoom, + ev: nio.Event, + event_id: str = "", **fields, ) -> None: """Register a `nio.Event` as a `Event` object in our model.""" diff --git a/src/backend/models/items.py b/src/backend/models/items.py index 4157b60a..511bb04b 100644 --- a/src/backend/models/items.py +++ b/src/backend/models/items.py @@ -70,6 +70,7 @@ class Room(ModelItem): guests_allowed: bool = True can_invite: bool = False + can_redact: bool = False can_send_messages: bool = False can_set_name: bool = False can_set_topic: bool = False diff --git a/src/backend/nio_callbacks.py b/src/backend/nio_callbacks.py index ebb2c4ab..24e9d34f 100644 --- a/src/backend/nio_callbacks.py +++ b/src/backend/nio_callbacks.py @@ -165,32 +165,46 @@ class NioCallbacks: model = self.client.models[self.client.user_id, room.room_id, "events"] event = None - for event in model._sorted_data: - if event.event_id == ev.redacts: + for evt in model._sorted_data: + if evt.event_id == ev.redacts: + event = evt break - if ( - event and - issubclass(event.event_type, nio.events.room_events.RoomMessage) - ): - event.source.source["content"] = {} - event.source.source["unsigned"] = { - "redacted_by": ev.event_id, - "redacted_because": ev.source, - } + if not (event and event.event_type is not nio.RedactedEvent): + return - await self.client.register_nio_event( - room, - nio.events.room_events.RedactedEvent.from_dict( - event.source.source, - ), - event_id = event.id, - ) + event.source.source["content"] = {} + event.source.source["unsigned"] = { + "redacted_by": ev.event_id, + "redacted_because": ev.source, + } + + await self.onRedactedEvent( + room, + nio.RedactedEvent.from_dict(event.source.source), + event_id = event.id, + ) - async def onRedactedEvent(self, room, ev) -> None: + async def onRedactedEvent(self, room, ev, event_id: str = "") -> None: + # There is no way to know which kind of event was redacted in an + # encrypted room. + kind = "Message" if ev.type == "m.room.encrypted" \ + else ev.type.split(".")[-1].capitalize() \ + .replace("_", " ") + + co = "%s event removed%s.%s" % ( + kind, + f" by {ev.redacter}" if ev.redacter != ev.sender else "", + f" Reason: {ev.reason}." if ev.reason else "", + ) + await self.client.register_nio_event( - room, ev, reason=ev.reason, + room, + ev, + event_id = event_id, + reason = ev.reason or "", + content = co, ) diff --git a/src/gui/Pages/Chat/Timeline/EventDelegate.qml b/src/gui/Pages/Chat/Timeline/EventDelegate.qml index 244fc04d..baf73247 100644 --- a/src/gui/Pages/Chat/Timeline/EventDelegate.qml +++ b/src/gui/Pages/Chat/Timeline/EventDelegate.qml @@ -220,16 +220,28 @@ HColumnLayout { HMenuItemPopupSpawner { icon.name: "remove-message" text: qsTr("Remove") - enabled: ! isRedacted + enabled: properties.eventIds.length popup: "Popups/RedactEvents.qml" popupParent: chat properties: ({ userId: chat.userId, roomId: chat.roomId, - eventIds: eventList.selectedCount ? - eventList.getSortedChecked().map(ev => ev.event_id) : - [model.event_id] + eventIds: + ( + eventList.selectedCount ? + eventList.getSortedChecked() : + [model] + ).filter(ev => + ( + ev.sender_id === chat.userId || + chat.roomInfo.can_redact + ) && ! isRedacted + ).map(ev => ev.event_id), + "details.text": + (! chat.roomInfo.can_redact && eventList.selectedCount) ? + qsTr("Only your messages will be removed") : + "" }) } diff --git a/src/gui/Popups/RedactEvents.qml b/src/gui/Popups/RedactEvents.qml index bcb54c47..4bd64b40 100644 --- a/src/gui/Popups/RedactEvents.qml +++ b/src/gui/Popups/RedactEvents.qml @@ -10,6 +10,8 @@ BoxPopup { qsTr("Remove selected messages?") : qsTr("Remove selected message?") + details.color: theme.colors.warningText + HLabeledTextField { id: reasonField label.text: qsTr("Reason (optional):") diff --git a/src/gui/Utils.qml b/src/gui/Utils.qml index e3b821fe..5e15efa8 100644 --- a/src/gui/Utils.qml +++ b/src/gui/Utils.qml @@ -165,11 +165,11 @@ QtObject { function escapeHtml(text) { // Replace special HTML characters by encoded alternatives - return text.replace("&", "&") - .replace("<", "<") - .replace(">", ">") - .replace('"', """) - .replace("'", "'") + return text.replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'") } @@ -202,9 +202,9 @@ QtObject { if (type === "RedactedEvent") { return qsTr( - "Removed message" + - `${ev.reason ? ". Reason: " + ev.reason : ""}` + - "" + `` + + escapeHtml(ev.content) + + "" ) }