summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--io.github.NhekoReborn.Nheko.json2
-rw-r--r--resources/qml/TimelineRow.qml14
-rw-r--r--src/EventAccessors.cpp20
-rw-r--r--src/EventAccessors.h3
-rw-r--r--src/timeline/EventStore.cpp14
-rw-r--r--src/timeline/TimelineModel.cpp39
-rw-r--r--src/timeline/TimelineModel.h17
8 files changed, 99 insertions, 12 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cf2b5959..577cbffc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -356,7 +356,7 @@ if(USE_BUNDLED_MTXCLIENT)
 	FetchContent_Declare(
 		MatrixClient
 		GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
-		GIT_TAG        70fa15de3ec84cf0c0ab6250f2e5e62f34a6d05b
+		GIT_TAG        31e300546eb63ea25b0b879fb255beee6022da03
 		)
 	set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "")
 	set(BUILD_LIB_TESTS OFF CACHE INTERNAL "")
diff --git a/io.github.NhekoReborn.Nheko.json b/io.github.NhekoReborn.Nheko.json
index 98ab9629..f498dd5a 100644
--- a/io.github.NhekoReborn.Nheko.json
+++ b/io.github.NhekoReborn.Nheko.json
@@ -220,7 +220,7 @@
       "name": "mtxclient",
       "sources": [
         {
-          "commit": "70fa15de3ec84cf0c0ab6250f2e5e62f34a6d05b",
+          "commit": "31e300546eb63ea25b0b879fb255beee6022da03",
           "type": "git",
           "url": "https://github.com/Nheko-Reborn/mtxclient.git"
         }
diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml
index 95a025cf..e4dc267b 100644
--- a/resources/qml/TimelineRow.qml
+++ b/resources/qml/TimelineRow.qml
@@ -85,6 +85,20 @@ Item {
             width: 16
         }
 
+        ImageButton {
+            id: editButton
+
+            visible: (Settings.buttonsInTimeline && model.isEditable) || model.isEdited 
+            Layout.alignment: Qt.AlignRight | Qt.AlignTop
+            Layout.preferredHeight: 16
+            width: 16
+            hoverEnabled: true
+            image: ":/icons/icons/ui/edit.png"
+            ToolTip.visible: hovered
+            ToolTip.text: model.isEditable ? qsTr("Edit") : qsTr("Edited")
+            onClicked: if (model.isEditable) chat.model.editAction(model.id)
+        }
+
         EmojiButton {
             id: reactButton
 
diff --git a/src/EventAccessors.cpp b/src/EventAccessors.cpp
index 212c2970..e6bc61b0 100644
--- a/src/EventAccessors.cpp
+++ b/src/EventAccessors.cpp
@@ -34,6 +34,20 @@ struct detector<Default, std::void_t<Op<Args...>>, Op, Args...>
 template<template<class...> class Op, class... Args>
 using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;
 
+struct IsStateEvent
+{
+        template<class T>
+        bool operator()(const mtx::events::StateEvent<T> &)
+        {
+                return true;
+        }
+        template<class T>
+        bool operator()(const mtx::events::Event<T> &)
+        {
+                return false;
+        }
+};
+
 struct EventMsgType
 {
         template<class E>
@@ -476,3 +490,9 @@ mtx::accessors::serialize_event(const mtx::events::collections::TimelineEvents &
 {
         return std::visit([](const auto &e) { return nlohmann::json(e); }, event);
 }
+
+bool
+mtx::accessors::is_state_event(const mtx::events::collections::TimelineEvents &event)
+{
+        return std::visit(IsStateEvent{}, event);
+}
diff --git a/src/EventAccessors.h b/src/EventAccessors.h
index 95e5df24..7bf695fc 100644
--- a/src/EventAccessors.h
+++ b/src/EventAccessors.h
@@ -17,6 +17,9 @@ room_id(const mtx::events::collections::TimelineEvents &event);
 std::string
 sender(const mtx::events::collections::TimelineEvents &event);
 
+bool
+is_state_event(const mtx::events::collections::TimelineEvents &event);
+
 QDateTime
 origin_server_ts(const mtx::events::collections::TimelineEvents &event);
 
diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp
index e5a66e19..94d43a83 100644
--- a/src/timeline/EventStore.cpp
+++ b/src/timeline/EventStore.cpp
@@ -774,15 +774,17 @@ EventStore::get(std::string_view id, std::string_view related_to, bool decrypt,
         if (id.empty())
                 return nullptr;
 
-        std::string id_ = std::string(id);
+        IdIndex index{room_id_, std::string(id)};
         if (resolve_edits) {
-                auto edits_ = edits(id_);
-                if (!edits_.empty())
-                        id_ = mtx::accessors::event_id(edits_.back());
+                auto edits_ = edits(index.id);
+                if (!edits_.empty()) {
+                        index.id = mtx::accessors::event_id(edits_.back());
+                        auto event_ptr =
+                          new mtx::events::collections::TimelineEvents(std::move(edits_.back()));
+                        events_by_id_.insert(index, event_ptr);
+                }
         }
 
-        IdIndex index{room_id_, id_};
-
         auto event_ptr = events_by_id_.object(index);
         if (!event_ptr) {
                 auto event = cache::client()->getEvent(room_id_, index.id);
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index c47194f5..dd4f8696 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -288,6 +288,8 @@ TimelineModel::roleNames() const
           {ProportionalHeight, "proportionalHeight"},
           {Id, "id"},
           {State, "state"},
+          {IsEdited, "isEdited"},
+          {IsEditable, "isEditable"},
           {IsEncrypted, "isEncrypted"},
           {IsRoomEncrypted, "isRoomEncrypted"},
           {ReplyTo, "replyTo"},
@@ -409,8 +411,12 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
 
                 return QVariant(prop > 0 ? prop : 1.);
         }
-        case Id:
-                return QVariant(QString::fromStdString(event_id(event)));
+        case Id: {
+                if (auto replaces = relations(event).replaces())
+                        return QVariant(QString::fromStdString(replaces.value()));
+                else
+                        return QVariant(QString::fromStdString(event_id(event)));
+        }
         case State: {
                 auto id             = QString::fromStdString(event_id(event));
                 auto containsOthers = [](const auto &vec) {
@@ -430,6 +436,11 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
                 else
                         return qml_mtx_events::Received;
         }
+        case IsEdited:
+                return QVariant(relations(event).replaces().has_value());
+        case IsEditable:
+                return QVariant(!is_state_event(event) && mtx::accessors::sender(event) ==
+                                                            http::client()->user_id().to_string());
         case IsEncrypted: {
                 auto id              = event_id(event);
                 auto encrypted_event = events.get(id, id, false);
@@ -444,7 +455,7 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
         case ReplyTo:
                 return QVariant(QString::fromStdString(relations(event).reply_to().value_or("")));
         case Reactions: {
-                auto id = event_id(event);
+                auto id = relations(event).replaces().value_or(event_id(event));
                 return QVariant::fromValue(events.reactions(id));
         }
         case RoomId:
@@ -813,6 +824,12 @@ TimelineModel::replyAction(QString id)
         setReply(id);
 }
 
+void
+TimelineModel::editAction(QString id)
+{
+        setEdit(id);
+}
+
 RelatedInfo
 TimelineModel::relatedInfo(QString id)
 {
@@ -1501,6 +1518,22 @@ TimelineModel::formatMemberEvent(QString id)
         return rendered;
 }
 
+void
+TimelineModel::setEdit(QString newEdit)
+{
+        if (edit_ != newEdit) {
+                edit_ = newEdit;
+                emit editChanged(edit_);
+
+                auto ev = events.get(newEdit.toStdString(), "");
+                if (ev) {
+                        setReply(QString::fromStdString(
+                          mtx::accessors::relations(*ev).reply_to().value_or("")));
+                        // input()->setText(mtx::accessors::body(*ev));
+                }
+        }
+}
+
 QString
 TimelineModel::roomName() const
 {
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 51b8049e..463d8705 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -145,6 +145,7 @@ class TimelineModel : public QAbstractListModel
         Q_PROPERTY(std::vector<QString> typingUsers READ typingUsers WRITE updateTypingUsers NOTIFY
                      typingUsersChanged)
         Q_PROPERTY(QString reply READ reply WRITE setReply NOTIFY replyChanged RESET resetReply)
+        Q_PROPERTY(QString edit READ edit WRITE setEdit NOTIFY editChanged RESET resetEdit)
         Q_PROPERTY(
           bool paginationInProgress READ paginationInProgress NOTIFY paginationInProgressChanged)
         Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged)
@@ -181,6 +182,8 @@ public:
                 ProportionalHeight,
                 Id,
                 State,
+                IsEdited,
+                IsEditable,
                 IsEncrypted,
                 IsRoomEncrypted,
                 ReplyTo,
@@ -213,6 +216,7 @@ public:
         Q_INVOKABLE void viewRawMessage(QString id) const;
         Q_INVOKABLE void viewDecryptedRawMessage(QString id) const;
         Q_INVOKABLE void openUserProfile(QString userid, bool global = false);
+        Q_INVOKABLE void editAction(QString id);
         Q_INVOKABLE void replyAction(QString id);
         Q_INVOKABLE void readReceiptsAction(QString id) const;
         Q_INVOKABLE void redactEvent(QString id);
@@ -268,6 +272,16 @@ public slots:
                         emit replyChanged(reply_);
                 }
         }
+        QString edit() const { return edit_; }
+        void setEdit(QString newEdit);
+        void resetEdit()
+        {
+                if (!edit_.isEmpty()) {
+                        edit_ = "";
+                        emit editChanged(edit_);
+                        resetReply();
+                }
+        }
         void setDecryptDescription(bool decrypt) { decryptDescription = decrypt; }
         void clearTimeline() { events.clearTimeline(); }
         void receivedSessionKey(const std::string &session_key)
@@ -292,6 +306,7 @@ signals:
         void newEncryptedImage(mtx::crypto::EncryptedFile encryptionInfo);
         void typingUsersChanged(std::vector<QString> users);
         void replyChanged(QString reply);
+        void editChanged(QString reply);
         void paginationInProgressChanged(const bool);
         void newCallEvent(const mtx::events::collections::TimelineEvents &event);
 
@@ -322,7 +337,7 @@ private:
         bool m_paginationInProgress = false;
 
         QString currentId;
-        QString reply_;
+        QString reply_, edit_;
         std::vector<QString> typingUsers_;
 
         TimelineViewManager *manager_;