summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Olm.cpp4
-rw-r--r--src/UserSettingsPage.cpp2
-rw-r--r--src/timeline/ReactionsModel.cpp98
-rw-r--r--src/timeline/ReactionsModel.h41
-rw-r--r--src/timeline/TimelineModel.cpp38
-rw-r--r--src/timeline/TimelineModel.h5
6 files changed, 184 insertions, 4 deletions
diff --git a/src/Olm.cpp b/src/Olm.cpp

index c8e4c13c..8ea39566 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp
@@ -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"); } diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp
index 54bce52c..e19aa876 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp
@@ -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()); diff --git a/src/timeline/ReactionsModel.cpp b/src/timeline/ReactionsModel.cpp new file mode 100644
index 00000000..2e249819 --- /dev/null +++ b/src/timeline/ReactionsModel.cpp
@@ -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++; + } +} diff --git a/src/timeline/ReactionsModel.h b/src/timeline/ReactionsModel.h new file mode 100644
index 00000000..5f61cd42 --- /dev/null +++ b/src/timeline/ReactionsModel.h
@@ -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; +}; diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index df2051e6..388a5842 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp
@@ -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 diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index cc63eca2..a737aac7 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h
@@ -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_;