summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNicolas Werner <nicolas.werner@hotmail.de>2020-07-19 12:22:54 +0200
committerNicolas Werner <nicolas.werner@hotmail.de>2020-07-19 12:22:54 +0200
commit6f2bc908badc207754ff55d543d41d9e2b847c97 (patch)
tree179012d3d482aa539462bf203651d3df04022df4 /src
parentClose cursor we don't need and where we overwrite the contents (diff)
downloadnheko-6f2bc908badc207754ff55d543d41d9e2b847c97.tar.xz
Fix reaction display
Diffstat (limited to 'src')
-rw-r--r--src/Cache.cpp35
-rw-r--r--src/Cache_p.h3
-rw-r--r--src/timeline/EventStore.cpp70
-rw-r--r--src/timeline/EventStore.h5
-rw-r--r--src/timeline/Reaction.cpp1
-rw-r--r--src/timeline/Reaction.h24
-rw-r--r--src/timeline/ReactionsModel.cpp98
-rw-r--r--src/timeline/ReactionsModel.h41
-rw-r--r--src/timeline/TimelineModel.cpp3
-rw-r--r--src/timeline/TimelineModel.h1
10 files changed, 140 insertions, 141 deletions
diff --git a/src/Cache.cpp b/src/Cache.cpp
index 9464a546..0307bee1 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -1353,6 +1353,37 @@ Cache::storeEvent(const std::string &room_id,
         txn.commit();
 }
 
+std::vector<std::string>
+Cache::relatedEvents(const std::string &room_id, const std::string &event_id)
+{
+        auto txn         = lmdb::txn::begin(env_, nullptr, MDB_RDONLY);
+        auto relationsDb = getRelationsDb(txn, room_id);
+
+        std::vector<std::string> related_ids;
+
+        auto related_cursor  = lmdb::cursor::open(txn, relationsDb);
+        lmdb::val related_to = event_id, related_event;
+        bool first           = true;
+
+        try {
+                if (!related_cursor.get(related_to, related_event, MDB_SET))
+                        return {};
+
+                while (related_cursor.get(
+                  related_to, related_event, first ? MDB_FIRST_DUP : MDB_NEXT_DUP)) {
+                        first = false;
+                        if (event_id != std::string_view(related_to.data(), related_to.size()))
+                                break;
+
+                        related_ids.emplace_back(related_event.data(), related_event.size());
+                }
+        } catch (const lmdb::error &e) {
+                nhlog::db()->error("related events error: {}", e.what());
+        }
+
+        return related_ids;
+}
+
 QMap<QString, RoomInfo>
 Cache::roomInfo(bool withInvites)
 {
@@ -2354,6 +2385,10 @@ Cache::saveOldMessages(const std::string &room_id, const mtx::responses::Message
 
         std::string event_id_val;
         for (const auto &e : res.chunk) {
+                if (std::holds_alternative<
+                      mtx::events::RedactionEvent<mtx::events::msg::Redaction>>(e))
+                        continue;
+
                 auto event         = mtx::accessors::serialize_event(e);
                 event_id_val       = event["event_id"].get<std::string>();
                 lmdb::val event_id = event_id_val;
diff --git a/src/Cache_p.h b/src/Cache_p.h
index 88308e45..61d91b0c 100644
--- a/src/Cache_p.h
+++ b/src/Cache_p.h
@@ -188,6 +188,9 @@ public:
         void storeEvent(const std::string &room_id,
                         const std::string &event_id,
                         const mtx::events::collections::TimelineEvent &event);
+        std::vector<std::string> relatedEvents(const std::string &room_id,
+                                               const std::string &event_id);
+
         struct TimelineRange
         {
                 uint64_t first, last;
diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp
index 80e8d474..0bd7a97e 100644
--- a/src/timeline/EventStore.cpp
+++ b/src/timeline/EventStore.cpp
@@ -3,12 +3,15 @@
 #include <QThread>
 #include <QTimer>
 
+#include "Cache.h"
 #include "Cache_p.h"
 #include "EventAccessors.h"
 #include "Logging.h"
 #include "MatrixClient.h"
 #include "Olm.h"
 
+Q_DECLARE_METATYPE(Reaction)
+
 QCache<EventStore::IdIndex, mtx::events::collections::TimelineEvents> EventStore::decryptedEvents_{
   1000};
 QCache<EventStore::IdIndex, mtx::events::collections::TimelineEvents> EventStore::events_by_id_{
@@ -18,6 +21,9 @@ QCache<EventStore::Index, mtx::events::collections::TimelineEvents> EventStore::
 EventStore::EventStore(std::string room_id, QObject *)
   : room_id_(std::move(room_id))
 {
+        static auto reactionType = qRegisterMetaType<Reaction>();
+        (void)reactionType;
+
         auto range = cache::client()->getTimelineRange(room_id_);
 
         if (range) {
@@ -223,6 +229,70 @@ EventStore::handleSync(const mtx::responses::Timeline &events)
         }
 }
 
+QVariantList
+EventStore::reactions(const std::string &event_id)
+{
+        auto event_ids = cache::client()->relatedEvents(room_id_, event_id);
+
+        struct TempReaction
+        {
+                int count = 0;
+                std::vector<std::string> users;
+                std::string reactedBySelf;
+        };
+        std::map<std::string, TempReaction> aggregation;
+        std::vector<Reaction> reactions;
+
+        auto self = http::client()->user_id().to_string();
+        for (const auto &id : event_ids) {
+                auto related_event = event(id, event_id);
+                if (!related_event)
+                        continue;
+
+                if (auto reaction = std::get_if<mtx::events::RoomEvent<mtx::events::msg::Reaction>>(
+                      related_event)) {
+                        auto &agg = aggregation[reaction->content.relates_to.key];
+
+                        if (agg.count == 0) {
+                                Reaction temp{};
+                                temp.key_ =
+                                  QString::fromStdString(reaction->content.relates_to.key);
+                                reactions.push_back(temp);
+                        }
+
+                        agg.count++;
+                        agg.users.push_back(cache::displayName(room_id_, reaction->sender));
+                        if (reaction->sender == self)
+                                agg.reactedBySelf = reaction->event_id;
+                }
+        }
+
+        QVariantList temp;
+        for (auto &reaction : reactions) {
+                const auto &agg            = aggregation[reaction.key_.toStdString()];
+                reaction.count_            = agg.count;
+                reaction.selfReactedEvent_ = QString::fromStdString(agg.reactedBySelf);
+
+                bool first = true;
+                for (const auto &user : agg.users) {
+                        if (first)
+                                first = false;
+                        else
+                                reaction.users_ += ", ";
+
+                        reaction.users_ += QString::fromStdString(user);
+                }
+
+                nhlog::db()->debug("key: {}, count: {}, users: {}",
+                                   reaction.key_.toStdString(),
+                                   reaction.count_,
+                                   reaction.users_.toStdString());
+                temp.append(QVariant::fromValue(reaction));
+        }
+
+        return temp;
+}
+
 mtx::events::collections::TimelineEvents *
 EventStore::event(int idx, bool decrypt)
 {
diff --git a/src/timeline/EventStore.h b/src/timeline/EventStore.h
index 3a78cba8..5a792040 100644
--- a/src/timeline/EventStore.h
+++ b/src/timeline/EventStore.h
@@ -5,12 +5,15 @@
 
 #include <QCache>
 #include <QObject>
+#include <QVariant>
 #include <qhashfunctions.h>
 
 #include <mtx/events/collections.hpp>
 #include <mtx/responses/messages.hpp>
 #include <mtx/responses/sync.hpp>
 
+#include "Reaction.h"
+
 class EventStore : public QObject
 {
         Q_OBJECT
@@ -65,6 +68,8 @@ public:
         // always returns a proper event as long as the idx is valid
         mtx::events::collections::TimelineEvents *event(int idx, bool decrypt = true);
 
+        QVariantList reactions(const std::string &event_id);
+
         int size() const
         {
                 return last != std::numeric_limits<uint64_t>::max()
diff --git a/src/timeline/Reaction.cpp b/src/timeline/Reaction.cpp
new file mode 100644
index 00000000..343c4649
--- /dev/null
+++ b/src/timeline/Reaction.cpp
@@ -0,0 +1 @@
+#include "Reaction.h"
diff --git a/src/timeline/Reaction.h b/src/timeline/Reaction.h
new file mode 100644
index 00000000..5f122e0a
--- /dev/null
+++ b/src/timeline/Reaction.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <QObject>
+#include <QString>
+
+struct Reaction
+{
+        Q_GADGET
+        Q_PROPERTY(QString key READ key)
+        Q_PROPERTY(QString users READ users)
+        Q_PROPERTY(QString selfReactedEvent READ selfReactedEvent)
+        Q_PROPERTY(int count READ count)
+
+public:
+        QString key() const { return key_; }
+        QString users() const { return users_; }
+        QString selfReactedEvent() const { return selfReactedEvent_; }
+        int count() const { return count_; }
+
+        QString key_;
+        QString users_;
+        QString selfReactedEvent_;
+        int count_;
+};
diff --git a/src/timeline/ReactionsModel.cpp b/src/timeline/ReactionsModel.cpp
deleted file mode 100644
index 1200e2ba..00000000
--- a/src/timeline/ReactionsModel.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-#include "ReactionsModel.h"
-
-#include <Cache.h>
-#include <MatrixClient.h>
-
-QHash<int, QByteArray>
-ReactionsModel::roleNames() const
-{
-        return {
-          {Key, "key"},
-          {Count, "counter"},
-          {Users, "users"},
-          {SelfReactedEvent, "selfReactedEvent"},
-        };
-}
-
-int
-ReactionsModel::rowCount(const QModelIndex &) const
-{
-        return static_cast<int>(reactions.size());
-}
-
-QVariant
-ReactionsModel::data(const QModelIndex &index, int role) const
-{
-        const int i = index.row();
-        if (i < 0 || i >= static_cast<int>(reactions.size()))
-                return {};
-
-        switch (role) {
-        case Key:
-                return QString::fromStdString(reactions[i].key);
-        case Count:
-                return static_cast<int>(reactions[i].reactions.size());
-        case Users: {
-                QString users;
-                bool first = true;
-                for (const auto &reaction : reactions[i].reactions) {
-                        if (!first)
-                                users += ", ";
-                        else
-                                first = false;
-                        users += QString::fromStdString(
-                          cache::displayName(room_id_, reaction.second.sender));
-                }
-                return users;
-        }
-        case SelfReactedEvent:
-                for (const auto &reaction : reactions[i].reactions)
-                        if (reaction.second.sender == http::client()->user_id().to_string())
-                                return QString::fromStdString(reaction.second.event_id);
-                return QStringLiteral("");
-        default:
-                return {};
-        }
-}
-
-void
-ReactionsModel::addReaction(const std::string &room_id,
-                            const mtx::events::RoomEvent<mtx::events::msg::Reaction> &reaction)
-{
-        room_id_ = room_id;
-
-        int idx = 0;
-        for (auto &storedReactions : reactions) {
-                if (storedReactions.key == reaction.content.relates_to.key) {
-                        storedReactions.reactions[reaction.event_id] = reaction;
-                        emit dataChanged(index(idx, 0), index(idx, 0));
-                        return;
-                }
-                idx++;
-        }
-
-        beginInsertRows(QModelIndex(), idx, idx);
-        reactions.push_back(
-          KeyReaction{reaction.content.relates_to.key, {{reaction.event_id, reaction}}});
-        endInsertRows();
-}
-
-void
-ReactionsModel::removeReaction(const mtx::events::RoomEvent<mtx::events::msg::Reaction> &reaction)
-{
-        int idx = 0;
-        for (auto &storedReactions : reactions) {
-                if (storedReactions.key == reaction.content.relates_to.key) {
-                        storedReactions.reactions.erase(reaction.event_id);
-
-                        if (storedReactions.reactions.size() == 0) {
-                                beginRemoveRows(QModelIndex(), idx, idx);
-                                reactions.erase(reactions.begin() + idx);
-                                endRemoveRows();
-                        } else
-                                emit dataChanged(index(idx, 0), index(idx, 0));
-                        return;
-                }
-                idx++;
-        }
-}
diff --git a/src/timeline/ReactionsModel.h b/src/timeline/ReactionsModel.h
deleted file mode 100644
index c839afc8..00000000
--- a/src/timeline/ReactionsModel.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#pragma once
-
-#include <QAbstractListModel>
-#include <QHash>
-
-#include <utility>
-#include <vector>
-
-#include <mtx/events/collections.hpp>
-
-class ReactionsModel : public QAbstractListModel
-{
-        Q_OBJECT
-public:
-        explicit ReactionsModel(QObject *parent = nullptr) { Q_UNUSED(parent); }
-        enum Roles
-        {
-                Key,
-                Count,
-                Users,
-                SelfReactedEvent,
-        };
-
-        QHash<int, QByteArray> roleNames() const override;
-        int rowCount(const QModelIndex &parent = QModelIndex()) const override;
-        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
-
-public slots:
-        void addReaction(const std::string &room_id,
-                         const mtx::events::RoomEvent<mtx::events::msg::Reaction> &reaction);
-        void removeReaction(const mtx::events::RoomEvent<mtx::events::msg::Reaction> &reaction);
-
-private:
-        struct KeyReaction
-        {
-                std::string key;
-                std::map<std::string, mtx::events::RoomEvent<mtx::events::msg::Reaction>> reactions;
-        };
-        std::string room_id_;
-        std::vector<KeyReaction> reactions;
-};
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 470e3988..85d2eb4e 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -366,7 +366,8 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
         case ReplyTo:
                 return QVariant(QString::fromStdString(in_reply_to_event(event)));
         case Reactions: {
-                return {};
+                auto id = event_id(event);
+                return QVariant::fromValue(events.reactions(id));
         }
         case RoomId:
                 return QVariant(room_id_);
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 9f9717df..cbe88fd2 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -10,7 +10,6 @@
 
 #include "CacheCryptoStructs.h"
 #include "EventStore.h"
-#include "ReactionsModel.h"
 
 namespace mtx::http {
 using RequestErr = const std::optional<mtx::http::ClientError> &;