commit
7beaf868ef
@ -249,6 +249,7 @@ set(SRC_FILES
|
|||||||
src/emoji/Provider.cpp
|
src/emoji/Provider.cpp
|
||||||
|
|
||||||
# Timeline
|
# Timeline
|
||||||
|
src/timeline/ReactionsModel.cpp
|
||||||
src/timeline/TimelineViewManager.cpp
|
src/timeline/TimelineViewManager.cpp
|
||||||
src/timeline/TimelineModel.cpp
|
src/timeline/TimelineModel.cpp
|
||||||
src/timeline/DelegateChooser.cpp
|
src/timeline/DelegateChooser.cpp
|
||||||
@ -335,7 +336,7 @@ if(USE_BUNDLED_MTXCLIENT)
|
|||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
MatrixClient
|
MatrixClient
|
||||||
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
|
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
|
||||||
GIT_TAG v0.3.0
|
GIT_TAG 1893cd6171c40c250ca64d388c082789452340a8
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(MatrixClient)
|
FetchContent_MakeAvailable(MatrixClient)
|
||||||
else()
|
else()
|
||||||
@ -451,6 +452,7 @@ qt5_wrap_cpp(MOC_HEADERS
|
|||||||
src/emoji/PickButton.h
|
src/emoji/PickButton.h
|
||||||
|
|
||||||
# Timeline
|
# Timeline
|
||||||
|
src/timeline/ReactionsModel.h
|
||||||
src/timeline/TimelineViewManager.h
|
src/timeline/TimelineViewManager.h
|
||||||
src/timeline/TimelineModel.h
|
src/timeline/TimelineModel.h
|
||||||
src/timeline/DelegateChooser.h
|
src/timeline/DelegateChooser.h
|
||||||
|
@ -146,9 +146,9 @@
|
|||||||
"name": "mtxclient",
|
"name": "mtxclient",
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"sha256": "0c2930b5861d93bab9a6515adca74ebaa78984119705d9b4372a9deb275dd30c",
|
"sha256": "a8c0239b7157fe8eadae8b06cd6c4e3531dcc61fc5a7f52dbb3c85106f70e3a5",
|
||||||
"type": "archive",
|
"type": "archive",
|
||||||
"url": "https://github.com/Nheko-Reborn/mtxclient/archive/v0.3.0.tar.gz"
|
"url": "https://github.com/Nheko-Reborn/mtxclient/archive/1893cd6171c40c250ca64d388c082789452340a8.tar.gz"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -19,7 +19,7 @@ Rectangle {
|
|||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
visible: img.status != Image.Ready
|
visible: img.status != Image.Ready
|
||||||
color: colors.brightText
|
color: colors.text
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
@ -43,5 +43,5 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
color: colors.dark
|
color: colors.base
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,11 @@
|
|||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
|
||||||
Button {
|
AbstractButton {
|
||||||
property string image: undefined
|
property string image: undefined
|
||||||
|
|
||||||
id: button
|
id: button
|
||||||
|
|
||||||
flat: true
|
|
||||||
|
|
||||||
// disable background, because we don't want a border on hover
|
|
||||||
background: Item {
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: buttonImg
|
id: buttonImg
|
||||||
// Workaround, can't get icon.source working for now...
|
// Workaround, can't get icon.source working for now...
|
||||||
|
76
resources/qml/Reactions.qml
Normal file
76
resources/qml/Reactions.qml
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import QtQuick 2.6
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
|
||||||
|
Flow {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
property alias reactions: repeater.model
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: repeater
|
||||||
|
|
||||||
|
AbstractButton {
|
||||||
|
id: reaction
|
||||||
|
text: model.key
|
||||||
|
hoverEnabled: true
|
||||||
|
implicitWidth: contentItem.childrenRect.width + contentItem.leftPadding*2
|
||||||
|
implicitHeight: contentItem.childrenRect.height
|
||||||
|
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.text: model.users
|
||||||
|
|
||||||
|
|
||||||
|
contentItem: Row {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: reactionText.implicitHeight/4
|
||||||
|
leftPadding: reactionText.implicitHeight / 2
|
||||||
|
rightPadding: reactionText.implicitHeight / 2
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: textMetrics
|
||||||
|
font.family: settings.emoji_font_family
|
||||||
|
elide: Text.ElideRight
|
||||||
|
elideWidth: 150
|
||||||
|
text: reaction.text
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.baseline: reactionCounter.baseline
|
||||||
|
id: reactionText
|
||||||
|
text: textMetrics.elidedText + (textMetrics.elidedText == textMetrics.text ? "" : "…")
|
||||||
|
font.family: settings.emoji_font_family
|
||||||
|
color: reaction.hovered ? colors.highlight : colors.text
|
||||||
|
maximumLineCount: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: divider
|
||||||
|
height: reactionCounter.implicitHeight * 1.4
|
||||||
|
width: 1
|
||||||
|
color: reaction.hovered ? colors.highlight : colors.text
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.verticalCenter: divider.verticalCenter
|
||||||
|
id: reactionCounter
|
||||||
|
text: model.counter
|
||||||
|
font: reaction.font
|
||||||
|
color: reaction.hovered ? colors.highlight : colors.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
implicitWidth: reaction.implicitWidth
|
||||||
|
implicitHeight: reaction.implicitHeight
|
||||||
|
border.color: (reaction.hovered || model.selfReacted )? colors.highlight : colors.text
|
||||||
|
color: colors.base
|
||||||
|
border.width: 1
|
||||||
|
radius: reaction.height / 2.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -52,6 +52,10 @@ MouseArea {
|
|||||||
|
|
||||||
modelData: model
|
modelData: model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reactions {
|
||||||
|
reactions: model.reactions
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusIndicator {
|
StatusIndicator {
|
||||||
|
@ -25,6 +25,7 @@ Page {
|
|||||||
id: settings
|
id: settings
|
||||||
category: "user"
|
category: "user"
|
||||||
property bool avatar_circles: true
|
property bool avatar_circles: true
|
||||||
|
property string emoji_font_family: "default"
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings {
|
Settings {
|
||||||
@ -133,6 +134,21 @@ Page {
|
|||||||
sequence: StandardKey.MoveToNextPage
|
sequence: StandardKey.MoveToNextPage
|
||||||
onActivated: { chat.contentY = chat.contentY + chat.height / 2; chat.returnToBounds(); }
|
onActivated: { chat.contentY = chat.contentY + chat.height / 2; chat.returnToBounds(); }
|
||||||
}
|
}
|
||||||
|
Shortcut {
|
||||||
|
sequence: StandardKey.Cancel
|
||||||
|
onActivated: chat.model.reply = undefined
|
||||||
|
}
|
||||||
|
Shortcut {
|
||||||
|
sequence: "Alt+Up"
|
||||||
|
onActivated: chat.model.reply = chat.model.indexToId(chat.model.reply? chat.model.idToIndex(chat.model.reply) + 1 : 0)
|
||||||
|
}
|
||||||
|
Shortcut {
|
||||||
|
sequence: "Alt+Down"
|
||||||
|
onActivated: {
|
||||||
|
var idx = chat.model.reply? chat.model.idToIndex(chat.model.reply) - 1 : -1
|
||||||
|
chat.model.reply = idx >= 0 ? chat.model.indexToId(idx) : undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
ScrollBar.vertical: ScrollBar {
|
||||||
id: scrollbar
|
id: scrollbar
|
||||||
@ -210,7 +226,7 @@ Page {
|
|||||||
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
|
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
|
||||||
visible: section.includes(" ")
|
visible: section.includes(" ")
|
||||||
text: chat.model.formatDateSeparator(modelData.timestamp)
|
text: chat.model.formatDateSeparator(modelData.timestamp)
|
||||||
color: colors.brightText
|
color: colors.text
|
||||||
|
|
||||||
height: fontMetrics.height * 1.4
|
height: fontMetrics.height * 1.4
|
||||||
width: contentWidth * 1.2
|
width: contentWidth * 1.2
|
||||||
@ -218,7 +234,7 @@ Page {
|
|||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
radius: parent.height / 2
|
radius: parent.height / 2
|
||||||
color: colors.dark
|
color: colors.base
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Row {
|
Row {
|
||||||
|
@ -15,7 +15,7 @@ Item {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
preventStealing: true
|
preventStealing: true
|
||||||
onClicked: chat.positionViewAtIndex(chat.model.idToIndex(timelineManager.replyingEvent), ListView.Contain)
|
onClicked: chat.positionViewAtIndex(chat.model.idToIndex(modelData.id), ListView.Contain)
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,8 +117,9 @@
|
|||||||
<file>qml/MatrixText.qml</file>
|
<file>qml/MatrixText.qml</file>
|
||||||
<file>qml/StatusIndicator.qml</file>
|
<file>qml/StatusIndicator.qml</file>
|
||||||
<file>qml/EncryptionIndicator.qml</file>
|
<file>qml/EncryptionIndicator.qml</file>
|
||||||
<file>qml/TimelineRow.qml</file>
|
<file>qml/Reactions.qml</file>
|
||||||
<file>qml/ScrollHelper.qml</file>
|
<file>qml/ScrollHelper.qml</file>
|
||||||
|
<file>qml/TimelineRow.qml</file>
|
||||||
<file>qml/delegates/MessageDelegate.qml</file>
|
<file>qml/delegates/MessageDelegate.qml</file>
|
||||||
<file>qml/delegates/TextMessage.qml</file>
|
<file>qml/delegates/TextMessage.qml</file>
|
||||||
<file>qml/delegates/NoticeMessage.qml</file>
|
<file>qml/delegates/NoticeMessage.qml</file>
|
||||||
|
@ -98,15 +98,15 @@ UserMentionsWidget {
|
|||||||
qproperty-highlightedTitleColor: palette(highlighted-text);
|
qproperty-highlightedTitleColor: palette(highlighted-text);
|
||||||
qproperty-highlightedSubtitleColor: palette(highlighted-text);
|
qproperty-highlightedSubtitleColor: palette(highlighted-text);
|
||||||
|
|
||||||
qproperty-hoverTitleColor: palette(highlightedtext);
|
qproperty-hoverTitleColor: palette(dark);
|
||||||
qproperty-hoverSubtitleColor: palette(highlightedtext);
|
qproperty-hoverSubtitleColor: palette(dark);
|
||||||
|
|
||||||
qproperty-btnColor: palette(dark);
|
qproperty-btnColor: palette(dark);
|
||||||
qproperty-btnTextColor: palette(bright-text);
|
qproperty-btnTextColor: palette(bright-text);
|
||||||
|
|
||||||
qproperty-timestampColor: palette(text);
|
qproperty-timestampColor: palette(text);
|
||||||
qproperty-highlightedTimestampColor: palette(highlighted-text);
|
qproperty-highlightedTimestampColor: palette(highlighted-text);
|
||||||
qproperty-hoverTimestampColor: palette(highlighted-text);
|
qproperty-hoverTimestampColor: palette(dark);
|
||||||
|
|
||||||
qproperty-bubbleBgColor: palette(base);
|
qproperty-bubbleBgColor: palette(base);
|
||||||
qproperty-bubbleFgColor: palette(text);
|
qproperty-bubbleFgColor: palette(text);
|
||||||
|
@ -164,8 +164,8 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
|
|||||||
using namespace mtx::events;
|
using namespace mtx::events;
|
||||||
|
|
||||||
// relations shouldn't be encrypted...
|
// relations shouldn't be encrypted...
|
||||||
mtx::common::RelatesTo relation;
|
mtx::common::ReplyRelatesTo relation;
|
||||||
if (body["content"].count("m.relates_to") != 0) {
|
if (body["content"]["m.relates_to"].contains("m.in_reply_to")) {
|
||||||
relation = body["content"]["m.relates_to"];
|
relation = body["content"]["m.relates_to"];
|
||||||
body["content"].erase("m.relates_to");
|
body["content"].erase("m.relates_to");
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ UserSettings::applyTheme()
|
|||||||
/*mid*/ QColor(110, 110, 110),
|
/*mid*/ QColor(110, 110, 110),
|
||||||
/*text*/ QColor("#333"),
|
/*text*/ QColor("#333"),
|
||||||
/*bright_text*/ QColor("#333"),
|
/*bright_text*/ QColor("#333"),
|
||||||
/*base*/ QColor("white"),
|
/*base*/ QColor("#eee"),
|
||||||
/*window*/ QColor("white"));
|
/*window*/ QColor("white"));
|
||||||
lightActive.setColor(QPalette::Highlight, QColor("#38a3d8"));
|
lightActive.setColor(QPalette::Highlight, QColor("#38a3d8"));
|
||||||
lightActive.setColor(QPalette::ToolTipBase, lightActive.base().color());
|
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) {
|
switch (e.type) {
|
||||||
case EventType::RoomKeyRequest:
|
case EventType::RoomKeyRequest:
|
||||||
return qml_mtx_events::EventType::KeyRequest;
|
return qml_mtx_events::EventType::KeyRequest;
|
||||||
|
case EventType::Reaction:
|
||||||
|
return qml_mtx_events::EventType::Reaction;
|
||||||
case EventType::RoomAliases:
|
case EventType::RoomAliases:
|
||||||
return qml_mtx_events::EventType::Aliases;
|
return qml_mtx_events::EventType::Aliases;
|
||||||
case EventType::RoomAvatar:
|
case EventType::RoomAvatar:
|
||||||
@ -223,6 +225,7 @@ TimelineModel::roleNames() const
|
|||||||
{State, "state"},
|
{State, "state"},
|
||||||
{IsEncrypted, "isEncrypted"},
|
{IsEncrypted, "isEncrypted"},
|
||||||
{ReplyTo, "replyTo"},
|
{ReplyTo, "replyTo"},
|
||||||
|
{Reactions, "reactions"},
|
||||||
{RoomId, "roomId"},
|
{RoomId, "roomId"},
|
||||||
{RoomName, "roomName"},
|
{RoomName, "roomName"},
|
||||||
{RoomTopic, "roomTopic"},
|
{RoomTopic, "roomTopic"},
|
||||||
@ -345,6 +348,11 @@ TimelineModel::data(const QString &id, int role) const
|
|||||||
}
|
}
|
||||||
case ReplyTo:
|
case ReplyTo:
|
||||||
return QVariant(QString::fromStdString(in_reply_to_event(event)));
|
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:
|
case RoomId:
|
||||||
return QVariant(QString::fromStdString(room_id(event)));
|
return QVariant(QString::fromStdString(room_id(event)));
|
||||||
case RoomName:
|
case RoomName:
|
||||||
@ -471,7 +479,6 @@ TimelineModel::fetchMore(const QModelIndex &)
|
|||||||
mtx::errors::to_string(err->matrix_error.errcode),
|
mtx::errors::to_string(err->matrix_error.errcode),
|
||||||
err->matrix_error.error,
|
err->matrix_error.error,
|
||||||
err->parse_error);
|
err->parse_error);
|
||||||
emit oldMessagesRetrieved(std::move(res));
|
|
||||||
setPaginationInProgress(false);
|
setPaginationInProgress(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -609,6 +616,18 @@ TimelineModel::internalAddEvents(
|
|||||||
QString redacts = QString::fromStdString(redaction->redacts);
|
QString redacts = QString::fromStdString(redaction->redacts);
|
||||||
auto redacted = std::find(eventOrder.begin(), eventOrder.end(), 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()) {
|
if (redacted != eventOrder.end()) {
|
||||||
auto redactedEvent = std::visit(
|
auto redactedEvent = std::visit(
|
||||||
[](const auto &ev)
|
[](const auto &ev)
|
||||||
@ -632,6 +651,18 @@ TimelineModel::internalAddEvents(
|
|||||||
continue; // don't insert redaction into timeline
|
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 =
|
if (auto event =
|
||||||
std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(&e)) {
|
std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(&e)) {
|
||||||
auto e_ = decryptEvent(*event).event;
|
auto e_ = decryptEvent(*event).event;
|
||||||
@ -707,6 +738,11 @@ TimelineModel::addBackwardsEvents(const mtx::responses::Messages &msgs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
prev_batch_token_ = QString::fromStdString(msgs.end);
|
prev_batch_token_ = QString::fromStdString(msgs.end);
|
||||||
|
|
||||||
|
if (ids.empty() && !msgs.chunk.empty()) {
|
||||||
|
// no visible events fetched, prevent loading from stopping
|
||||||
|
fetchMore(QModelIndex());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
QString
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <mtxclient/http/errors.hpp>
|
#include <mtxclient/http/errors.hpp>
|
||||||
|
|
||||||
#include "CacheCryptoStructs.h"
|
#include "CacheCryptoStructs.h"
|
||||||
|
#include "ReactionsModel.h"
|
||||||
|
|
||||||
namespace mtx::http {
|
namespace mtx::http {
|
||||||
using RequestErr = const std::optional<mtx::http::ClientError> &;
|
using RequestErr = const std::optional<mtx::http::ClientError> &;
|
||||||
@ -29,6 +30,8 @@ enum EventType
|
|||||||
Unsupported,
|
Unsupported,
|
||||||
/// m.room_key_request
|
/// m.room_key_request
|
||||||
KeyRequest,
|
KeyRequest,
|
||||||
|
/// m.reaction,
|
||||||
|
Reaction,
|
||||||
/// m.room.aliases
|
/// m.room.aliases
|
||||||
Aliases,
|
Aliases,
|
||||||
/// m.room.avatar
|
/// m.room.avatar
|
||||||
@ -155,6 +158,7 @@ public:
|
|||||||
State,
|
State,
|
||||||
IsEncrypted,
|
IsEncrypted,
|
||||||
ReplyTo,
|
ReplyTo,
|
||||||
|
Reactions,
|
||||||
RoomId,
|
RoomId,
|
||||||
RoomName,
|
RoomName,
|
||||||
RoomTopic,
|
RoomTopic,
|
||||||
@ -271,6 +275,7 @@ private:
|
|||||||
QSet<QString> read;
|
QSet<QString> read;
|
||||||
QList<QString> pending;
|
QList<QString> pending;
|
||||||
std::vector<QString> eventOrder;
|
std::vector<QString> eventOrder;
|
||||||
|
std::map<QString, ReactionsModel> reactions;
|
||||||
|
|
||||||
QString room_id_;
|
QString room_id_;
|
||||||
QString prev_batch_token_;
|
QString prev_batch_token_;
|
||||||
|
Loading…
Reference in New Issue
Block a user