Page MenuHomePhorge

D5.1732600971.diff
No OneTemporary

Size
8 KB
Referenced Files
None
Subscribers
None

D5.1732600971.diff

diff --git a/changelogs/84.add b/changelogs/84.add
new file mode 100644
--- /dev/null
+++ b/changelogs/84.add
@@ -0,0 +1 @@
+Display edited version of events
diff --git a/src/contents/ui/Bubble.qml b/src/contents/ui/Bubble.qml
--- a/src/contents/ui/Bubble.qml
+++ b/src/contents/ui/Bubble.qml
@@ -191,6 +191,13 @@
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.8
}
+ Label {
+ objectName: 'editedIndicator'
+ visible: !compactMode && event.isEdited
+ text: l10n.get('event-edited-indicator')
+ font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.8
+ }
+
Kazv.EventReadIndicator {
objectName: 'eventReadIndicator'
shouldShow: !compactMode && !event.isLocalEcho
diff --git a/src/contents/ui/EventView.qml b/src/contents/ui/EventView.qml
--- a/src/contents/ui/EventView.qml
+++ b/src/contents/ui/EventView.qml
@@ -202,6 +202,10 @@
if (e.isState) {
return 'state';
}
+ // ignore edits of other events
+ if (e.relationType === 'm.replace') {
+ return 'ignore';
+ }
switch (e.type) {
case 'm.room.message':
switch (e.content.msgtype) {
diff --git a/src/l10n/cmn-Hans/100-ui.ftl b/src/l10n/cmn-Hans/100-ui.ftl
--- a/src/l10n/cmn-Hans/100-ui.ftl
+++ b/src/l10n/cmn-Hans/100-ui.ftl
@@ -157,6 +157,7 @@
event-cannot-decrypt-text = (加密内容)
event-read-indicator-more = +{ $rest }
event-read-indicator-list-title = { $numUsers } 个用户已读
+event-edited-indicator = (编辑过了)
media-file-menu-option-view = 查看
media-file-menu-option-save-as = 保存为
diff --git a/src/l10n/en/100-ui.ftl b/src/l10n/en/100-ui.ftl
--- a/src/l10n/en/100-ui.ftl
+++ b/src/l10n/en/100-ui.ftl
@@ -172,6 +172,7 @@
[1] 1 user
*[other] { $numUsers } users
}
+event-edited-indicator = (edited)
media-file-menu-option-view = View
media-file-menu-option-save-as = Save as
diff --git a/src/matrix-event.hpp b/src/matrix-event.hpp
--- a/src/matrix-event.hpp
+++ b/src/matrix-event.hpp
@@ -29,6 +29,11 @@
lager::reader<std::optional<Kazv::LocalEchoDesc>> m_localEcho;
lager::reader<Kazv::Event> m_event;
std::optional<Kazv::Room> m_room;
+ lager::reader<std::string> m_eventIdStd;
+ lager::reader<std::string> m_senderStd;
+ /// the unedited content of this event
+ lager::reader<QJsonObject> m_originalContent;
+ lager::reader<Kazv::EventList> m_edits;
public:
explicit MatrixEvent(lager::reader<std::variant<Kazv::Event, Kazv::LocalEchoDesc>> event, std::optional<Kazv::Room> room = std::nullopt, QObject *parent = 0);
@@ -55,6 +60,7 @@
LAGER_QT_READER(QString, relationType);
LAGER_QT_READER(QString, relatedEventId);
LAGER_QT_READER(QString, formattedTime);
+ LAGER_QT_READER(bool, isEdited);
Q_INVOKABLE MatrixEventReaderListModel *readers() const;
diff --git a/src/matrix-event.cpp b/src/matrix-event.cpp
--- a/src/matrix-event.cpp
+++ b/src/matrix-event.cpp
@@ -38,11 +38,53 @@
, m_localEcho(event.map(getLocalEcho))
, m_event(event.map(getEvent))
, m_room(room)
- , LAGER_QT(eventId)(m_event.xform(zug::map([](Event e) { return e.id(); }) | strToQt))
- , LAGER_QT(sender)(m_event.xform(zug::map([](Event e) { return e.sender(); }) | strToQt))
+ , m_eventIdStd(m_event.map(&Event::id))
+ , m_senderStd(m_event.map(&Event::sender))
+ , m_originalContent(m_event.map([](Event e) { return e.content().get().get<QJsonObject>(); }))
+ , m_edits(m_room.has_value() ? lager::reader<EventList>(lager::with(
+ m_event,
+ m_room->relatedEvents(m_eventIdStd, "m.replace")
+ ).map([](const Event &origEvent, const EventList &edits) -> EventList {
+ // https://spec.matrix.org/v1.10/client-server-api/#event-replacements
+ if (!(
+ // (3) no state key
+ !origEvent.isState()
+ // (4) original event is not an edit
+ && origEvent.relationship().first != "m.replace"
+ )) {
+ return EventList{};
+ }
+ auto origType = origEvent.type();
+ auto origSender = origEvent.sender();
+ return intoImmer(
+ EventList{},
+ zug::filter([&origType, &origSender](const Event &ev) {
+ return
+ // (0) same room id requirement is implicit.
+ // (1) same sender
+ origSender == ev.sender()
+ // (2) same type
+ && origType == ev.type()
+ // (3) no state key
+ && !ev.isState()
+ // (4) original event is not an edit is checked above
+ // (5) replaced event content has `m.new_content`
+ && ev.content().get().contains("m.new_content");
+ }),
+ edits
+ );
+ })) : lager::make_constant(EventList{}))
+ , LAGER_QT(eventId)(m_eventIdStd.xform(strToQt))
+ , LAGER_QT(sender)(m_senderStd.xform(strToQt))
, LAGER_QT(type)(m_event.xform(zug::map([](Event e) { return e.type(); }) | strToQt))
, LAGER_QT(stateKey)(m_event.xform(zug::map([](Event e) { return e.stateKey(); }) | strToQt))
- , LAGER_QT(content)(m_event.xform(zug::map([](Event e) { return e.content().get().get<QJsonObject>(); })))
+ , LAGER_QT(content)(lager::with(m_originalContent, m_edits).map([](const QJsonObject &origContent, const EventList &edits) {
+ if (edits.empty()) {
+ return origContent;
+ } else {
+ return edits.at(edits.size() - 1).content().get().at("m.new_content").get<QJsonObject>();
+ }
+ }))
, LAGER_QT(encrypted)(m_event.xform(zug::map([](Event e) { return e.encrypted(); })))
, LAGER_QT(decrypted)(m_event.map([](Event e) { return e.decrypted(); }))
, LAGER_QT(isState)(m_event.map([](Event e) { return e.isState(); }))
@@ -91,6 +133,7 @@
QLocale::ShortFormat
);
}))
+ , LAGER_QT(isEdited)(m_edits.map([](const auto &edits) { return !edits.empty(); }))
{
}
diff --git a/src/tests/matrix-room-timeline-test.cpp b/src/tests/matrix-room-timeline-test.cpp
--- a/src/tests/matrix-room-timeline-test.cpp
+++ b/src/tests/matrix-room-timeline-test.cpp
@@ -34,6 +34,7 @@
private Q_SLOTS:
void testLocalEcho();
void testReadReceipts();
+ void testEdits();
};
void MatrixRoomTimelineTest::testLocalEcho()
@@ -91,6 +92,42 @@
}
}
+void MatrixRoomTimelineTest::testEdits()
+{
+ auto r = makeRoom(withRoomTimeline({
+ makeEvent(withEventId("$0")),
+ makeEvent(withEventId("$1") | withEventRelationship("moe.kazv.mxc.some-rel", "$0")),
+ makeEvent(withEventId("$2") | withEventContent(json{{"body", "first"}}) | withEventRelationship("m.replace", "$1")),
+ // valid
+ makeEvent(withEventId("$3") | withEventContent(json{{"m.new_content", {{"body", "second"}}}}) | withEventRelationship("m.replace", "$1")),
+ // also valid
+ makeEvent(withEventId("$4") | withEventContent(json{{"m.new_content", {{"body", "third"}}}}) | withEventRelationship("m.replace", "$1")),
+ // invalid, changed event type
+ makeEvent(withEventId("$5") | withEventContent(json{{"m.new_content", {{"body", "fourth"}}}}) | withEventRelationship("m.replace", "$1") | withEventType("moe.kazv.mxc.some-other-type")),
+ // invalid, changed event sender
+ makeEvent(withEventId("$6") | withEventContent(json{{"m.new_content", {{"body", "fifth"}}}}) | withEventRelationship("m.replace", "$1") | withEventType("moe.kazv.mxc.some-other-type") | withEventSenderId("@othersender:example.com")),
+ // invalid, has state key
+ makeEvent(withEventId("$7") | withEventContent(json{{"m.new_content", {{"body", "sixth"}}}}) | withEventRelationship("m.replace", "$1") | withEventType("moe.kazv.mxc.some-other-type") | withStateKey("")),
+ // invalid, no m.new_content
+ makeEvent(withEventId("$8") | withEventContent(json{{"body", "seventh"}}) | withEventRelationship("m.replace", "$1") | withEventType("moe.kazv.mxc.some-other-type")),
+ }));
+ auto model = SdkModel{makeClient(withRoom(r))};
+ std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
+ auto roomList = toUniquePtr(sdk->roomList());
+ auto room = toUniquePtr(roomList->room(QString::fromStdString(r.roomId)));
+ auto timeline = toUniquePtr(room->timeline());
+
+ auto event = toUniquePtr(timeline->at(7));
+ QCOMPARE(event->eventId(), "$1");
+ // the content is taken from the newest valid edit
+ qDebug() << event->content();
+ QCOMPARE(event->content(), (QJsonObject{{"body", "third"}}));
+ // the relation is taken from the original content
+ QCOMPARE(event->relationType(), "moe.kazv.mxc.some-rel");
+ QCOMPARE(event->relatedEventId(), "$0");
+ QVERIFY(event->isEdited());
+}
+
QTEST_MAIN(MatrixRoomTimelineTest)
#include "matrix-room-timeline-test.moc"

File Metadata

Mime Type
text/plain
Expires
Mon, Nov 25, 10:02 PM (12 h, 34 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40495
Default Alt Text
D5.1732600971.diff (8 KB)

Event Timeline