diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index 36701c72..5c96ff18 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -23,7 +23,7 @@ Rectangle {
ListView {
id: chat
- cacheBuffer: 4*parent.height
+ cacheBuffer: parent.height
visible: timelineManager.timeline != null
anchors.fill: parent
@@ -65,6 +65,7 @@ Rectangle {
case MtxEvent.TextMessage: return "delegates/TextMessage.qml"
case MtxEvent.ImageMessage: return "delegates/ImageMessage.qml"
case MtxEvent.VideoMessage: return "delegates/VideoMessage.qml"
+ case MtxEvent.Redacted: return "delegates/Redacted.qml"
default: return "delegates/placeholder.qml"
}
property variant eventData: model
diff --git a/resources/qml/delegates/Redacted.qml b/resources/qml/delegates/Redacted.qml
new file mode 100644
index 00000000..53e95a83
--- /dev/null
+++ b/resources/qml/delegates/Redacted.qml
@@ -0,0 +1,15 @@
+import QtQuick 2.5
+import QtQuick.Controls 2.5
+
+Label {
+ text: qsTr("redacted")
+ color: inactiveColors.text
+ horizontalAlignment: Text.AlignHCenter
+
+ height: contentHeight * 1.2
+ width: contentWidth * 1.2
+ background: Rectangle {
+ radius: parent.height / 2
+ color: colors.dark
+ }
+}
diff --git a/resources/res.qrc b/resources/res.qrc
index 62ed53e5..0d55e70d 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -120,5 +120,6 @@
qml/delegates/TextMessage.qml
qml/delegates/NoticeMessage.qml
qml/delegates/ImageMessage.qml
+ qml/delegates/Redacted.qml
diff --git a/src/timeline2/TimelineModel.cpp b/src/timeline2/TimelineModel.cpp
index 54f03b56..f544c83c 100644
--- a/src/timeline2/TimelineModel.cpp
+++ b/src/timeline2/TimelineModel.cpp
@@ -1,5 +1,6 @@
#include "TimelineModel.h"
+#include
#include
#include
@@ -160,6 +161,12 @@ toRoomEventType(const mtx::events::Event &)
{
return qml_mtx_events::EventType::VideoMessage;
}
+
+qml_mtx_events::EventType
+toRoomEventType(const mtx::events::Event &)
+{
+ return qml_mtx_events::EventType::Redacted;
+}
// ::EventType::Type toRoomEventType(const Event &e) { return
// ::EventType::LocationMessage; }
@@ -319,19 +326,9 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
if (timeline.events.empty())
return;
- std::vector ids;
- for (const auto &e : timeline.events) {
- QString id =
- boost::apply_visitor([](const auto &e) -> QString { return eventId(e); }, e);
+ std::vector ids = internalAddEvents(timeline.events);
- if (this->events.contains(id))
- continue;
-
- this->events.insert(id, e);
- ids.push_back(id);
- }
-
- if (ids.empty)
+ if (ids.empty())
return;
beginInsertRows(QModelIndex(),
@@ -341,6 +338,52 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
endInsertRows();
}
+std::vector
+TimelineModel::internalAddEvents(
+ const std::vector &timeline)
+{
+ std::vector ids;
+ for (const auto &e : timeline) {
+ QString id =
+ boost::apply_visitor([](const auto &e) -> QString { return eventId(e); }, e);
+
+ if (this->events.contains(id))
+ continue;
+
+ if (auto redaction =
+ boost::get>(&e)) {
+ QString redacts = QString::fromStdString(redaction->redacts);
+ auto redacted = std::find(eventOrder.begin(), eventOrder.end(), redacts);
+
+ if (redacted != eventOrder.end()) {
+ auto redactedEvent = boost::apply_visitor(
+ [](const auto &ev)
+ -> mtx::events::RoomEvent {
+ mtx::events::RoomEvent
+ replacement = {};
+ replacement.event_id = ev.event_id;
+ replacement.room_id = ev.room_id;
+ replacement.sender = ev.sender;
+ replacement.origin_server_ts = ev.origin_server_ts;
+ replacement.type = ev.type;
+ return replacement;
+ },
+ e);
+ events.insert(redacts, redactedEvent);
+
+ int row = (int)std::distance(eventOrder.begin(), redacted);
+ emit dataChanged(index(row, 0), index(row, 0));
+ }
+
+ continue; // don't insert redaction into timeline
+ }
+
+ this->events.insert(id, e);
+ ids.push_back(id);
+ }
+ return ids;
+}
+
void
TimelineModel::fetchHistory()
{
@@ -373,17 +416,7 @@ TimelineModel::fetchHistory()
void
TimelineModel::addBackwardsEvents(const mtx::responses::Messages &msgs)
{
- std::vector ids;
- for (const auto &e : msgs.chunk) {
- QString id =
- boost::apply_visitor([](const auto &e) -> QString { return eventId(e); }, e);
-
- if (this->events.contains(id))
- continue;
-
- this->events.insert(id, e);
- ids.push_back(id);
- }
+ std::vector ids = internalAddEvents(msgs.chunk);
if (!ids.empty()) {
beginInsertRows(QModelIndex(), 0, static_cast(ids.size() - 1));
diff --git a/src/timeline2/TimelineModel.h b/src/timeline2/TimelineModel.h
index d63ed818..ca8d4ad6 100644
--- a/src/timeline2/TimelineModel.h
+++ b/src/timeline2/TimelineModel.h
@@ -61,6 +61,7 @@ enum EventType
NoticeMessage,
TextMessage,
VideoMessage,
+ Redacted,
UnknownMessage,
};
Q_ENUM_NS(EventType)
@@ -124,6 +125,8 @@ signals:
private:
DecryptionResult decryptEvent(
const mtx::events::EncryptedEvent &e) const;
+ std::vector internalAddEvents(
+ const std::vector &timeline);
QHash events;
std::vector eventOrder;