Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F116494
D5.1733015740.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
8 KB
Referenced Files
None
Subscribers
None
D5.1733015740.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 30, 5:15 PM (17 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41759
Default Alt Text
D5.1733015740.diff (8 KB)
Attached To
Mode
D5: Display edited version of events
Attached
Detach File
Event Timeline
Log In to Comment