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> &;
|