From db1afc980e2c68366ffceb70056c575e28ad52b7 Mon Sep 17 00:00:00 2001 From: vSLG Date: Sat, 28 Mar 2020 14:24:05 -0300 Subject: [PATCH] Add RedactionEvent support on backend --- src/backend/matrix_client.py | 70 ++++++++++++++++++- src/backend/models/items.py | 1 + src/backend/models/model.py | 18 +++++ src/backend/nio_callbacks.py | 6 ++ src/gui/Pages/Chat/Timeline/EventDelegate.qml | 2 + src/gui/Utils.qml | 8 +++ 6 files changed, 103 insertions(+), 2 deletions(-) diff --git a/src/backend/matrix_client.py b/src/backend/matrix_client.py index 1875b50a..f9cc5fac 100644 --- a/src/backend/matrix_client.py +++ b/src/backend/matrix_client.py @@ -1208,6 +1208,11 @@ class MatrixClient(nio.AsyncClient): content, inline=True, room_id=room.room_id, ) + age = 0 + with suppress(Exception): + age = ev.source.get("age") + age = age or 0 + # Create Event ModelItem item = Event( @@ -1215,7 +1220,12 @@ class MatrixClient(nio.AsyncClient): event_id = ev.event_id, event_type = type(ev), source = ev, - date = datetime.fromtimestamp(ev.server_timestamp / 1000), + date = datetime.fromtimestamp( + ( + datetime.fromtimestamp(ev.server_timestamp / 1000) - + datetime.fromtimestamp(age / 1000) + ).total_seconds() + ), sender_id = ev.sender, sender_name = sender_name, sender_avatar = sender_avatar, @@ -1242,5 +1252,61 @@ class MatrixClient(nio.AsyncClient): AlertRequested() model[item.id] = item - await self.set_room_last_event(room.room_id, item) + + + async def register_redact_event( + self, room: nio.MatrixRoom, ev: nio.Event, **fields + ) -> None: + """Register a `nio.events.room_events.RedactionEvent` in our model. + + If .redacts attribute points to an existing model, it should get replaced. + Else register as a new Event. + """ + + model = self.models[self.user_id, room.room_id, "events"] + event = model.get_event_id(ev.redacts) + + if event: + sender_name, sender_avatar = \ + await self.get_member_name_avatar(room.room_id, ev.sender) + + target_id = getattr(ev, "state_key", "") or "" + + target_name, target_avatar = \ + await self.get_member_name_avatar(room.room_id, target_id) \ + if target_id else ("", "") + + content = fields.get("content", "").strip() + + if content and "inline_content" not in fields: + fields["inline_content"] = HTML.filter( + content, inline=True, room_id=room.room_id, + ) + + age = ev.source["unsigned"]["age"] + + item = Event( + id = ev.event_id, + event_id = ev.event_id, + event_type = type(ev), + source = ev, + date = datetime.fromtimestamp( + ( + datetime.fromtimestamp(ev.server_timestamp / 1000) - + datetime.fromtimestamp(age / 1000) + ).total_seconds() + ), + sender_id = ev.sender, + sender_name = sender_name, + sender_avatar = sender_avatar, + target_id = target_id, + target_name = target_name, + target_avatar = target_avatar, + links = Event.parse_links(content), + **fields, + ) + + model[event.id] = item + else: + await self.register_nio_event(room, ev, **fields) diff --git a/src/backend/models/items.py b/src/backend/models/items.py index 4157b60a..e865e263 100644 --- a/src/backend/models/items.py +++ b/src/backend/models/items.py @@ -177,6 +177,7 @@ class Event(ModelItem): content: str = "" inline_content: str = "" + redacts: str = "" reason: str = "" links: List[str] = field(default_factory=list) mentions: List[Tuple[str, str]] = field(default_factory=list) diff --git a/src/backend/models/model.py b/src/backend/models/model.py index ea53f749..22fe5882 100644 --- a/src/backend/models/model.py +++ b/src/backend/models/model.py @@ -129,3 +129,21 @@ class Model(MutableMapping): new = type(self)(sync_id=sync_id) new.update(self) return new + + + def get_event_id(self, event_id: str) -> "Model": + """Get an event from the internal dict by event_id field. + + This function exists because not every event is indexed with its + event_id field. + """ + + event = self._data.get(event_id) + if event and event.event_id == event_id: + return event + + for event in self._data.values(): + if event.event_id == event_id: + return event + + return None diff --git a/src/backend/nio_callbacks.py b/src/backend/nio_callbacks.py index be3ca300..79bc7bd6 100644 --- a/src/backend/nio_callbacks.py +++ b/src/backend/nio_callbacks.py @@ -161,6 +161,12 @@ class NioCallbacks: await self.onRoomMessageMedia(room, ev) + async def onRedactionEvent(self, room, ev) -> None: + await self.client.register_redact_event( + room, ev, redacts=ev.redacts, reason=ev.reason + ) + + async def onRoomCreateEvent(self, room, ev) -> None: co = "%1 allowed users on other matrix servers to join this room" \ if ev.federate else \ diff --git a/src/gui/Pages/Chat/Timeline/EventDelegate.qml b/src/gui/Pages/Chat/Timeline/EventDelegate.qml index 780f5671..0f121e28 100644 --- a/src/gui/Pages/Chat/Timeline/EventDelegate.qml +++ b/src/gui/Pages/Chat/Timeline/EventDelegate.qml @@ -33,6 +33,7 @@ HColumnLayout { readonly property bool smallAvatar: compact readonly property bool collapseAvatar: combine readonly property bool hideAvatar: onRight + readonly property bool isRedacted: model.event_type === "RedactionEvent" readonly property bool hideNameLine: model.event_type === "RoomMessageEmote" || @@ -221,6 +222,7 @@ HColumnLayout { HMenuItemPopupSpawner { icon.name: "remove-message" text: qsTr("Remove") + enabled: ! isRedacted popup: "Popups/RedactEvents.qml" popupParent: chat diff --git a/src/gui/Utils.qml b/src/gui/Utils.qml index 2c1edcaf..475f5d6e 100644 --- a/src/gui/Utils.qml +++ b/src/gui/Utils.qml @@ -200,6 +200,14 @@ QtObject { return qsTr(ev.content).arg(sender).arg(target) } + if (type === "RedactionEvent") { + return qsTr( + "Removed message" + + `${ev.reason ? ". Reason: " + ev.reason : ""}` + + "" + ) + } + return qsTr(ev.content).arg(sender) }