commit
7beaf868ef
16 changed files with 296 additions and 23 deletions
|
|
@ -164,8 +164,8 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
|
|||
using namespace mtx::events;
|
||||
|
||||
// relations shouldn't be encrypted...
|
||||
mtx::common::RelatesTo relation;
|
||||
if (body["content"].count("m.relates_to") != 0) {
|
||||
mtx::common::ReplyRelatesTo relation;
|
||||
if (body["content"]["m.relates_to"].contains("m.in_reply_to")) {
|
||||
relation = body["content"]["m.relates_to"];
|
||||
body["content"].erase("m.relates_to");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ UserSettings::applyTheme()
|
|||
/*mid*/ QColor(110, 110, 110),
|
||||
/*text*/ QColor("#333"),
|
||||
/*bright_text*/ QColor("#333"),
|
||||
/*base*/ QColor("white"),
|
||||
/*base*/ QColor("#eee"),
|
||||
/*window*/ QColor("white"));
|
||||
lightActive.setColor(QPalette::Highlight, QColor("#38a3d8"));
|
||||
lightActive.setColor(QPalette::ToolTipBase, lightActive.base().color());
|
||||
|
|
|
|||
98
src/timeline/ReactionsModel.cpp
Normal file
98
src/timeline/ReactionsModel.cpp
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#include "ReactionsModel.h"
|
||||
|
||||
#include <Cache.h>
|
||||
#include <MatrixClient.h>
|
||||
|
||||
QHash<int, QByteArray>
|
||||
ReactionsModel::roleNames() const
|
||||
{
|
||||
return {
|
||||
{Key, "key"},
|
||||
{Count, "counter"},
|
||||
{Users, "users"},
|
||||
{SelfReacted, "selfReacted"},
|
||||
};
|
||||
}
|
||||
|
||||
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 SelfReacted:
|
||||
for (const auto &reaction : reactions[i].reactions)
|
||||
if (reaction.second.sender == http::client()->user_id().to_string())
|
||||
return true;
|
||||
return false;
|
||||
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++;
|
||||
}
|
||||
}
|
||||
41
src/timeline/ReactionsModel.h
Normal file
41
src/timeline/ReactionsModel.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#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,
|
||||
SelfReacted,
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
|
@ -42,6 +42,8 @@ struct RoomEventType
|
|||
switch (e.type) {
|
||||
case EventType::RoomKeyRequest:
|
||||
return qml_mtx_events::EventType::KeyRequest;
|
||||
case EventType::Reaction:
|
||||
return qml_mtx_events::EventType::Reaction;
|
||||
case EventType::RoomAliases:
|
||||
return qml_mtx_events::EventType::Aliases;
|
||||
case EventType::RoomAvatar:
|
||||
|
|
@ -223,6 +225,7 @@ TimelineModel::roleNames() const
|
|||
{State, "state"},
|
||||
{IsEncrypted, "isEncrypted"},
|
||||
{ReplyTo, "replyTo"},
|
||||
{Reactions, "reactions"},
|
||||
{RoomId, "roomId"},
|
||||
{RoomName, "roomName"},
|
||||
{RoomTopic, "roomTopic"},
|
||||
|
|
@ -345,6 +348,11 @@ TimelineModel::data(const QString &id, int role) const
|
|||
}
|
||||
case ReplyTo:
|
||||
return QVariant(QString::fromStdString(in_reply_to_event(event)));
|
||||
case Reactions:
|
||||
if (reactions.count(id))
|
||||
return QVariant::fromValue((QObject *)&reactions.at(id));
|
||||
else
|
||||
return {};
|
||||
case RoomId:
|
||||
return QVariant(QString::fromStdString(room_id(event)));
|
||||
case RoomName:
|
||||
|
|
@ -471,7 +479,6 @@ TimelineModel::fetchMore(const QModelIndex &)
|
|||
mtx::errors::to_string(err->matrix_error.errcode),
|
||||
err->matrix_error.error,
|
||||
err->parse_error);
|
||||
emit oldMessagesRetrieved(std::move(res));
|
||||
setPaginationInProgress(false);
|
||||
return;
|
||||
}
|
||||
|
|
@ -609,6 +616,18 @@ TimelineModel::internalAddEvents(
|
|||
QString redacts = QString::fromStdString(redaction->redacts);
|
||||
auto redacted = std::find(eventOrder.begin(), eventOrder.end(), redacts);
|
||||
|
||||
auto event = events.value(redacts);
|
||||
if (auto reaction =
|
||||
std::get_if<mtx::events::RoomEvent<mtx::events::msg::Reaction>>(
|
||||
&event)) {
|
||||
QString reactedTo =
|
||||
QString::fromStdString(reaction->content.relates_to.event_id);
|
||||
reactions[reactedTo].removeReaction(*reaction);
|
||||
int idx = idToIndex(reactedTo);
|
||||
if (idx >= 0)
|
||||
emit dataChanged(index(idx, 0), index(idx, 0));
|
||||
}
|
||||
|
||||
if (redacted != eventOrder.end()) {
|
||||
auto redactedEvent = std::visit(
|
||||
[](const auto &ev)
|
||||
|
|
@ -632,6 +651,18 @@ TimelineModel::internalAddEvents(
|
|||
continue; // don't insert redaction into timeline
|
||||
}
|
||||
|
||||
if (auto reaction =
|
||||
std::get_if<mtx::events::RoomEvent<mtx::events::msg::Reaction>>(&e)) {
|
||||
QString reactedTo =
|
||||
QString::fromStdString(reaction->content.relates_to.event_id);
|
||||
events.insert(id, e);
|
||||
reactions[reactedTo].addReaction(room_id_.toStdString(), *reaction);
|
||||
int idx = idToIndex(reactedTo);
|
||||
if (idx >= 0)
|
||||
emit dataChanged(index(idx, 0), index(idx, 0));
|
||||
continue; // don't insert reaction into timeline
|
||||
}
|
||||
|
||||
if (auto event =
|
||||
std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(&e)) {
|
||||
auto e_ = decryptEvent(*event).event;
|
||||
|
|
@ -707,6 +738,11 @@ TimelineModel::addBackwardsEvents(const mtx::responses::Messages &msgs)
|
|||
}
|
||||
|
||||
prev_batch_token_ = QString::fromStdString(msgs.end);
|
||||
|
||||
if (ids.empty() && !msgs.chunk.empty()) {
|
||||
// no visible events fetched, prevent loading from stopping
|
||||
fetchMore(QModelIndex());
|
||||
}
|
||||
}
|
||||
|
||||
QString
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <mtxclient/http/errors.hpp>
|
||||
|
||||
#include "CacheCryptoStructs.h"
|
||||
#include "ReactionsModel.h"
|
||||
|
||||
namespace mtx::http {
|
||||
using RequestErr = const std::optional<mtx::http::ClientError> &;
|
||||
|
|
@ -29,6 +30,8 @@ enum EventType
|
|||
Unsupported,
|
||||
/// m.room_key_request
|
||||
KeyRequest,
|
||||
/// m.reaction,
|
||||
Reaction,
|
||||
/// m.room.aliases
|
||||
Aliases,
|
||||
/// m.room.avatar
|
||||
|
|
@ -155,6 +158,7 @@ public:
|
|||
State,
|
||||
IsEncrypted,
|
||||
ReplyTo,
|
||||
Reactions,
|
||||
RoomId,
|
||||
RoomName,
|
||||
RoomTopic,
|
||||
|
|
@ -271,6 +275,7 @@ private:
|
|||
QSet<QString> read;
|
||||
QList<QString> pending;
|
||||
std::vector<QString> eventOrder;
|
||||
std::map<QString, ReactionsModel> reactions;
|
||||
|
||||
QString room_id_;
|
||||
QString prev_batch_token_;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue