mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +03:00
Fix reaction display
This commit is contained in:
parent
d467568a65
commit
6f2bc908ba
12 changed files with 151 additions and 152 deletions
|
@ -251,7 +251,7 @@ set(SRC_FILES
|
|||
|
||||
# Timeline
|
||||
src/timeline/EventStore.cpp
|
||||
src/timeline/ReactionsModel.cpp
|
||||
src/timeline/Reaction.cpp
|
||||
src/timeline/TimelineViewManager.cpp
|
||||
src/timeline/TimelineModel.cpp
|
||||
src/timeline/DelegateChooser.cpp
|
||||
|
@ -455,7 +455,7 @@ qt5_wrap_cpp(MOC_HEADERS
|
|||
|
||||
# Timeline
|
||||
src/timeline/EventStore.h
|
||||
src/timeline/ReactionsModel.h
|
||||
src/timeline/Reaction.h
|
||||
src/timeline/TimelineViewManager.h
|
||||
src/timeline/TimelineModel.h
|
||||
src/timeline/DelegateChooser.h
|
||||
|
|
|
@ -30,11 +30,11 @@ Flow {
|
|||
implicitHeight: contentItem.childrenRect.height
|
||||
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: model.users
|
||||
ToolTip.text: modelData.users
|
||||
|
||||
onClicked: {
|
||||
console.debug("Picked " + model.key + "in response to " + reactionFlow.eventId + " in room " + reactionFlow.roomId + ". selfReactedEvent: " + model.selfReactedEvent)
|
||||
timelineManager.reactToMessage(reactionFlow.roomId, reactionFlow.eventId, model.key, model.selfReactedEvent)
|
||||
console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + " in room " + reactionFlow.roomId + ". selfReactedEvent: " + modelData.selfReactedEvent)
|
||||
timelineManager.reactToMessage(reactionFlow.roomId, reactionFlow.eventId, modelData.key, modelData.selfReactedEvent)
|
||||
}
|
||||
|
||||
|
||||
|
@ -49,13 +49,13 @@ Flow {
|
|||
font.family: settings.emojiFont
|
||||
elide: Text.ElideRight
|
||||
elideWidth: 150
|
||||
text: model.key
|
||||
text: modelData.key
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.baseline: reactionCounter.baseline
|
||||
id: reactionText
|
||||
text: textMetrics.elidedText + (textMetrics.elidedText == model.key ? "" : "…")
|
||||
text: textMetrics.elidedText + (textMetrics.elidedText == modelData.key ? "" : "…")
|
||||
font.family: settings.emojiFont
|
||||
color: reaction.hovered ? colors.highlight : colors.text
|
||||
maximumLineCount: 1
|
||||
|
@ -65,13 +65,13 @@ Flow {
|
|||
id: divider
|
||||
height: Math.floor(reactionCounter.implicitHeight * 1.4)
|
||||
width: 1
|
||||
color: (reaction.hovered || model.selfReactedEvent !== '') ? colors.highlight : colors.text
|
||||
color: (reaction.hovered || modelData.selfReactedEvent !== '') ? colors.highlight : colors.text
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: divider.verticalCenter
|
||||
id: reactionCounter
|
||||
text: model.counter
|
||||
text: modelData.count
|
||||
font: reaction.font
|
||||
color: reaction.hovered ? colors.highlight : colors.text
|
||||
}
|
||||
|
@ -82,8 +82,8 @@ Flow {
|
|||
|
||||
implicitWidth: reaction.implicitWidth
|
||||
implicitHeight: reaction.implicitHeight
|
||||
border.color: (reaction.hovered || model.selfReactedEvent !== '') ? colors.highlight : colors.text
|
||||
color: model.selfReactedEvent !== '' ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.20) : colors.base
|
||||
border.color: (reaction.hovered || modelData.selfReactedEvent !== '') ? colors.highlight : colors.text
|
||||
color: modelData.selfReactedEvent !== '' ? Qt.hsla(highlightHue, highlightSat, highlightLight, 0.20) : colors.base
|
||||
border.width: 1
|
||||
radius: reaction.height / 2.0
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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()
|
||||
|
|
1
src/timeline/Reaction.cpp
Normal file
1
src/timeline/Reaction.cpp
Normal file
|
@ -0,0 +1 @@
|
|||
#include "Reaction.h"
|
24
src/timeline/Reaction.h
Normal file
24
src/timeline/Reaction.h
Normal file
|
@ -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_;
|
||||
};
|
|
@ -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++;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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_);
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include "CacheCryptoStructs.h"
|
||||
#include "EventStore.h"
|
||||
#include "ReactionsModel.h"
|
||||
|
||||
namespace mtx::http {
|
||||
using RequestErr = const std::optional<mtx::http::ClientError> &;
|
||||
|
|
Loading…
Reference in a new issue