diff --git a/TODO.md b/TODO.md index 9e15ca46..cbfaa182 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,8 @@ # TODO +- redact local echo effect +- being able to redact local echos + ## Refactoring - Rewrite account settings using `HTabbedContainer` diff --git a/src/backend/matrix_client.py b/src/backend/matrix_client.py index a290828e..bdd216b3 100644 --- a/src/backend/matrix_client.py +++ b/src/backend/matrix_client.py @@ -885,8 +885,8 @@ class MatrixClient(nio.AsyncClient): """ return await asyncio.gather(*[ - self.room_redact(room_id, evt_id, reason) - for evt_id in event_ids + self.room_redact(room_id, ev_id, reason) + for ev_id in event_ids ]) @@ -1119,7 +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_redact_all = 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"), @@ -1190,8 +1190,8 @@ class MatrixClient(nio.AsyncClient): async def register_nio_event( self, - room: nio.MatrixRoom, - ev: nio.Event, + room: nio.MatrixRoom, + ev: nio.Event, event_id: str = "", **fields, ) -> None: diff --git a/src/backend/models/items.py b/src/backend/models/items.py index 511bb04b..3428075e 100644 --- a/src/backend/models/items.py +++ b/src/backend/models/items.py @@ -70,7 +70,7 @@ class Room(ModelItem): guests_allowed: bool = True can_invite: bool = False - can_redact: bool = False + can_redact_all: 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 24e9d34f..ea484a1d 100644 --- a/src/backend/nio_callbacks.py +++ b/src/backend/nio_callbacks.py @@ -165,9 +165,9 @@ class NioCallbacks: model = self.client.models[self.client.user_id, room.room_id, "events"] event = None - for evt in model._sorted_data: - if evt.event_id == ev.redacts: - event = evt + for existing in model._sorted_data: + if existing.event_id == ev.redacts: + event = existing break if not (event and event.event_type is not nio.RedactedEvent): @@ -187,16 +187,15 @@ class NioCallbacks: 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("_", " ") + kind = ev.source["type"] + is_message = kind == "m.room.message" + kind = kind.split(".")[-1].capitalize().replace("_", " ") - co = "%s event removed%s.%s" % ( + co = "%s%s removed%s%s" % ( kind, - f" by {ev.redacter}" if ev.redacter != ev.sender else "", - f" Reason: {ev.reason}." if ev.reason else "", + "" if is_message else " event", + f" by {ev.redacter}" if ev.redacter != ev.sender else "", + f", reason: {ev.reason}." if ev.reason else "", ) await self.client.register_nio_event( diff --git a/src/gui/Pages/Chat/Timeline/EventDelegate.qml b/src/gui/Pages/Chat/Timeline/EventDelegate.qml index baf73247..764648b8 100644 --- a/src/gui/Pages/Chat/Timeline/EventDelegate.qml +++ b/src/gui/Pages/Chat/Timeline/EventDelegate.qml @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + import QtQuick 2.12 import QtQuick.Layouts 1.12 import Clipboard 0.1 @@ -222,27 +224,31 @@ HColumnLayout { text: qsTr("Remove") enabled: properties.eventIds.length - popup: "Popups/RedactEvents.qml" + popup: "Popups/RedactPopup.qml" popupParent: chat properties: ({ userId: chat.userId, roomId: chat.roomId, + 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") : - "" + redactableEvents + .filter(ev => ev.event_type !== "RedactedEvent") + .map(ev => ev.event_id), + + onlyOwnMessageWarning: + ! chat.roomInfo.can_redact_all && + redactableEvents.length < eventList.selectedCount }) + + readonly property var redactableEvents: + ( + eventList.selectedCount ? + eventList.getSortedChecked() : + [model] + ).filter(ev => + ev.sender_id === chat.userId || + chat.roomInfo.can_redact_all + ) } HMenuItem { diff --git a/src/gui/Popups/RedactEvents.qml b/src/gui/Popups/RedactPopup.qml similarity index 81% rename from src/gui/Popups/RedactEvents.qml rename to src/gui/Popups/RedactPopup.qml index 4bd64b40..becfb131 100644 --- a/src/gui/Popups/RedactEvents.qml +++ b/src/gui/Popups/RedactPopup.qml @@ -11,6 +11,10 @@ BoxPopup { qsTr("Remove selected message?") details.color: theme.colors.warningText + details.text: + onlyOwnMessageWarning ? + qsTr("Only your messages will be removed") : + "" HLabeledTextField { id: reasonField @@ -31,4 +35,5 @@ BoxPopup { property string userId: "" property var eventIds: [] + property bool onlyOwnMessageWarning: false } diff --git a/src/gui/Utils.qml b/src/gui/Utils.qml index 5e15efa8..389eb1d3 100644 --- a/src/gui/Utils.qml +++ b/src/gui/Utils.qml @@ -202,7 +202,7 @@ QtObject { if (type === "RedactedEvent") { return qsTr( - `` + + `` + escapeHtml(ev.content) + "" ) diff --git a/src/themes/Glass.qpl b/src/themes/Glass.qpl index b4de7ccc..cc93aaee 100644 --- a/src/themes/Glass.qpl +++ b/src/themes/Glass.qpl @@ -353,6 +353,8 @@ chat: color body: colors.text color date: colors.dimText + color redactedBody: colors.halfDimText + color noticeBody: colors.halfDimText int noticeLineWidth: 1 * uiScale diff --git a/src/themes/Midnight.qpl b/src/themes/Midnight.qpl index 2c8dd7ab..e2fe3c70 100644 --- a/src/themes/Midnight.qpl +++ b/src/themes/Midnight.qpl @@ -366,6 +366,8 @@ chat: color body: colors.text color date: colors.dimText + color redactedBody: colors.halfDimText + color noticeBody: colors.halfDimText int noticeLineWidth: 1 * uiScale