Move the reaction delegate into the bubble
Move the reaction delegate into the bubble so it can be instantiated by the Content model. This aims to make sure we only instantiate it when needed rather than for every event. You can now hover the event to show the ReactionComponent with a button to add a reaction if none are currently present Added bonus ReactionModel no longer needs an event pointer, the event ID is enough to get reaction from the room so things are less likely to blow up.
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "messagecontentmodel.h"
|
||||
#include "eventhandler.h"
|
||||
#include "messagecomponenttype.h"
|
||||
#include "neochatconfig.h"
|
||||
|
||||
#include <QImageReader>
|
||||
@@ -27,6 +28,7 @@
|
||||
#include "chatbarcache.h"
|
||||
#include "filetype.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "models/reactionmodel.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "texthandler.h"
|
||||
@@ -152,12 +154,18 @@ void MessageContentModel::initializeModel()
|
||||
updateReplyModel();
|
||||
resetModel();
|
||||
});
|
||||
connect(m_room, &Room::updatedEvent, this, [this](const QString &eventId) {
|
||||
if (eventId == m_eventId) {
|
||||
updateReactionModel();
|
||||
}
|
||||
});
|
||||
|
||||
initializeEvent();
|
||||
if (m_currentState == Available || m_currentState == Pending) {
|
||||
updateReplyModel();
|
||||
}
|
||||
resetModel();
|
||||
updateReactionModel();
|
||||
}
|
||||
|
||||
void MessageContentModel::initializeEvent()
|
||||
@@ -340,6 +348,10 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
||||
if (role == ReplyContentModelRole) {
|
||||
return QVariant::fromValue<MessageContentModel *>(m_replyModel);
|
||||
}
|
||||
if (role == ReactionModelRole) {
|
||||
return QVariant::fromValue<ReactionModel *>(m_reactionModel);
|
||||
;
|
||||
}
|
||||
if (role == ThreadRootRole) {
|
||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(event.first);
|
||||
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
|
||||
@@ -400,6 +412,7 @@ QHash<int, QByteArray> MessageContentModel::roleNamesStatic()
|
||||
roles[MessageContentModel::ReplyEventIdRole] = "replyEventId";
|
||||
roles[MessageContentModel::ReplyAuthorRole] = "replyAuthor";
|
||||
roles[MessageContentModel::ReplyContentModelRole] = "replyContentModel";
|
||||
roles[MessageContentModel::ReactionModelRole] = "reactionModel";
|
||||
roles[MessageContentModel::ThreadRootRole] = "threadRoot";
|
||||
roles[MessageContentModel::LinkPreviewerRole] = "linkPreviewer";
|
||||
roles[MessageContentModel::ChatBarCacheRole] = "chatBarCache";
|
||||
@@ -480,6 +493,10 @@ QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEdi
|
||||
newComponents = addLinkPreviews(newComponents);
|
||||
}
|
||||
|
||||
if ((m_reactionModel && m_reactionModel->rowCount() > 0)) {
|
||||
newComponents += MessageComponent{MessageComponentType::Reaction, QString(), {}};
|
||||
}
|
||||
|
||||
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
|
||||
if (roomMessageEvent && (roomMessageEvent->isThreaded() || m_room->threads().contains(roomMessageEvent->id()))
|
||||
&& roomMessageEvent->id() == roomMessageEvent->threadRootEventId()) {
|
||||
@@ -731,4 +748,25 @@ void MessageContentModel::updateItineraryModel()
|
||||
}
|
||||
}
|
||||
|
||||
void MessageContentModel::updateReactionModel()
|
||||
{
|
||||
if (m_reactionModel != nullptr && m_reactionModel->rowCount() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_reactionModel == nullptr) {
|
||||
m_reactionModel = new ReactionModel(this, m_eventId, m_room);
|
||||
connect(m_reactionModel, &ReactionModel::reactionsUpdated, this, &MessageContentModel::updateReactionModel);
|
||||
}
|
||||
|
||||
if (m_reactionModel->rowCount() <= 0) {
|
||||
m_reactionModel->disconnect(this);
|
||||
delete m_reactionModel;
|
||||
m_reactionModel = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
resetContent();
|
||||
}
|
||||
|
||||
#include "moc_messagecontentmodel.cpp"
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/events/roomevent.h>
|
||||
#include <Quotient/room.h>
|
||||
|
||||
#include "enums/messagecomponenttype.h"
|
||||
#include "itinerarymodel.h"
|
||||
#include "messagecomponent.h"
|
||||
#include "models/reactionmodel.h"
|
||||
#include "neochatroommember.h"
|
||||
|
||||
/**
|
||||
@@ -57,6 +57,8 @@ public:
|
||||
ReplyAuthorRole, /**< The author of the event that was replied to. */
|
||||
ReplyContentModelRole, /**< The MessageContentModel for the reply event. */
|
||||
|
||||
ReactionModelRole, /**< Reaction model for this event. */
|
||||
|
||||
ThreadRootRole, /**< The thread root event ID for the event. */
|
||||
|
||||
LinkPreviewerRole, /**< The link preview details. */
|
||||
@@ -125,6 +127,7 @@ private:
|
||||
QPointer<MessageContentModel> m_replyModel;
|
||||
void updateReplyModel();
|
||||
|
||||
ReactionModel *m_reactionModel = nullptr;
|
||||
ItineraryModel *m_itineraryModel = nullptr;
|
||||
|
||||
QList<MessageComponent> componentsForType(MessageComponentType::Type type);
|
||||
@@ -135,4 +138,6 @@ private:
|
||||
|
||||
void updateItineraryModel();
|
||||
bool m_emptyItinerary = false;
|
||||
|
||||
void updateReactionModel();
|
||||
};
|
||||
|
||||
@@ -258,18 +258,6 @@ QVariant MessageModel::data(const QModelIndex &idx, int role) const
|
||||
return m_readMarkerModels.contains(event.value().get().id());
|
||||
}
|
||||
|
||||
if (role == ReactionRole) {
|
||||
if (m_reactionModels.contains(event.value().get().id())) {
|
||||
return QVariant::fromValue<ReactionModel *>(m_reactionModels[event.value().get().id()].data());
|
||||
} else {
|
||||
return QVariantList();
|
||||
}
|
||||
}
|
||||
|
||||
if (role == ShowReactionsRole) {
|
||||
return m_reactionModels.contains(event.value().get().id());
|
||||
}
|
||||
|
||||
if (role == VerifiedRole) {
|
||||
if (event.value().get().originalEvent()) {
|
||||
auto encrypted = dynamic_cast<const EncryptedEvent *>(event.value().get().originalEvent());
|
||||
@@ -323,8 +311,6 @@ QHash<int, QByteArray> MessageModel::roleNames() const
|
||||
roles[ShowSectionRole] = "showSection";
|
||||
roles[ReadMarkersRole] = "readMarkers";
|
||||
roles[ShowReadMarkersRole] = "showReadMarkers";
|
||||
roles[ReactionRole] = "reaction";
|
||||
roles[ShowReactionsRole] = "showReactions";
|
||||
roles[VerifiedRole] = "verified";
|
||||
roles[AuthorDisplayNameRole] = "authorDisplayName";
|
||||
roles[IsRedactedRole] = "isRedacted";
|
||||
@@ -454,31 +440,6 @@ void MessageModel::createEventObjects(const Quotient::RoomEvent *event, bool isP
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto roomEvent = eventCast<const RoomMessageEvent>(event)) {
|
||||
// ReactionModel handles updates to add and remove reactions, we only need to
|
||||
// handle adding and removing whole models here.
|
||||
if (m_reactionModels.contains(eventId)) {
|
||||
// If a model already exists but now has no reactions remove it
|
||||
if (m_reactionModels[eventId]->rowCount() <= 0) {
|
||||
m_reactionModels.remove(eventId);
|
||||
if (!resetting) {
|
||||
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (m_room->relatedEvents(*event, Quotient::EventRelation::AnnotationType).count() > 0) {
|
||||
// If a model doesn't exist and there are reactions add it.
|
||||
auto reactionModel = QSharedPointer<ReactionModel>(new ReactionModel(roomEvent, m_room));
|
||||
if (reactionModel->rowCount() > 0) {
|
||||
m_reactionModels[eventId] = reactionModel;
|
||||
if (!resetting) {
|
||||
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageModel::clearModel()
|
||||
@@ -504,7 +465,6 @@ void MessageModel::clearModel()
|
||||
|
||||
void MessageModel::clearEventObjects()
|
||||
{
|
||||
m_reactionModels.clear();
|
||||
m_readMarkerModels.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -77,8 +77,6 @@ public:
|
||||
|
||||
ReadMarkersRole, /**< The first 5 other users at the event for read marker tracking. */
|
||||
ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */
|
||||
ReactionRole, /**< List model for this event. */
|
||||
ShowReactionsRole, /**< Whether there are any reactions to be shown. */
|
||||
|
||||
VerifiedRole, /**< Whether an encrypted message is sent in a verified session. */
|
||||
AuthorDisplayNameRole, /**< The displayname for the event's sender; for name change events, the old displayname. */
|
||||
@@ -155,7 +153,6 @@ private:
|
||||
bool movingEvent = false;
|
||||
|
||||
QMap<QString, QSharedPointer<ReadMarkerModel>> m_readMarkerModels;
|
||||
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
|
||||
|
||||
void createEventObjects(const Quotient::RoomEvent *event, bool isPending = false);
|
||||
};
|
||||
|
||||
@@ -9,22 +9,27 @@
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include "neochatroom.h"
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
ReactionModel::ReactionModel(const Quotient::RoomMessageEvent *event, NeoChatRoom *room)
|
||||
: QAbstractListModel(nullptr)
|
||||
ReactionModel::ReactionModel(MessageContentModel *parent, const QString &eventId, NeoChatRoom *room)
|
||||
: QAbstractListModel(parent)
|
||||
, m_room(room)
|
||||
, m_event(event)
|
||||
, m_eventId(eventId)
|
||||
{
|
||||
if (m_event != nullptr && m_room != nullptr) {
|
||||
connect(m_room, &NeoChatRoom::updatedEvent, this, [this](const QString &eventId) {
|
||||
if (m_event && m_event->id() == eventId) {
|
||||
updateReactions();
|
||||
}
|
||||
});
|
||||
Q_ASSERT(parent);
|
||||
Q_ASSERT(parent != nullptr);
|
||||
Q_ASSERT(!eventId.isEmpty());
|
||||
Q_ASSERT(room != nullptr);
|
||||
|
||||
updateReactions();
|
||||
}
|
||||
connect(m_room, &NeoChatRoom::updatedEvent, this, [this](const QString &eventId) {
|
||||
if (m_eventId == eventId) {
|
||||
updateReactions();
|
||||
}
|
||||
});
|
||||
|
||||
updateReactions();
|
||||
}
|
||||
|
||||
QVariant ReactionModel::data(const QModelIndex &index, int role) const
|
||||
@@ -99,12 +104,16 @@ int ReactionModel::rowCount(const QModelIndex &parent) const
|
||||
|
||||
void ReactionModel::updateReactions()
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
|
||||
m_reactions.clear();
|
||||
m_shortcodes.clear();
|
||||
|
||||
const auto &annotations = m_room->relatedEvents(*m_event, Quotient::EventRelation::AnnotationType);
|
||||
const auto &annotations = m_room->relatedEvents(m_eventId, Quotient::EventRelation::AnnotationType);
|
||||
if (annotations.isEmpty()) {
|
||||
endResetModel();
|
||||
return;
|
||||
|
||||
@@ -3,11 +3,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "neochatroom.h"
|
||||
#include <QAbstractListModel>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/events/reactionevent.h>
|
||||
#include <Quotient/roommember.h>
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
class RoomMessageEvent;
|
||||
}
|
||||
|
||||
class MessageContentModel;
|
||||
class NeoChatRoom;
|
||||
|
||||
/**
|
||||
* @class ReactionModel
|
||||
*
|
||||
@@ -38,7 +47,7 @@ public:
|
||||
HasLocalMember, /**< Whether the local member is in the list of authors. */
|
||||
};
|
||||
|
||||
explicit ReactionModel(const Quotient::RoomMessageEvent *event, NeoChatRoom *room);
|
||||
explicit ReactionModel(MessageContentModel *parent, const QString &eventId, NeoChatRoom *room);
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
@@ -61,9 +70,15 @@ public:
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* @brief The reactions in the model have been updated.
|
||||
*/
|
||||
void reactionsUpdated();
|
||||
|
||||
private:
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
const Quotient::RoomMessageEvent *m_event;
|
||||
QString m_eventId;
|
||||
QList<Reaction> m_reactions;
|
||||
QMap<QString, QString> m_shortcodes;
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "messagecontentmodel.h"
|
||||
|
||||
class NeoChatRoom;
|
||||
class ReactionModel;
|
||||
|
||||
/**
|
||||
* @class ThreadFetchModel
|
||||
@@ -172,8 +171,6 @@ private:
|
||||
ThreadFetchModel *m_threadFetchModel;
|
||||
ThreadChatBarModel *m_threadChatBarModel;
|
||||
|
||||
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
|
||||
|
||||
QPointer<Quotient::GetRelatingEventsWithRelTypeJob> m_currentJob = nullptr;
|
||||
std::optional<QString> m_nextBatch = QString();
|
||||
bool m_addingPending = false;
|
||||
|
||||
@@ -32,42 +32,6 @@ class TimelineMessageModel : public MessageModel
|
||||
QML_ELEMENT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*/
|
||||
enum EventRoles {
|
||||
DelegateTypeRole = Qt::UserRole + 1, /**< The delegate type of the message. */
|
||||
EventIdRole, /**< The matrix event ID of the event. */
|
||||
TimeRole, /**< The timestamp for when the event was sent (as a QDateTime). */
|
||||
SectionRole, /**< The date of the event as a string. */
|
||||
AuthorRole, /**< The author of the event. */
|
||||
HighlightRole, /**< Whether the event should be highlighted. */
|
||||
SpecialMarksRole, /**< Whether the event is hidden or not. */
|
||||
ProgressInfoRole, /**< Progress info when downloading files. */
|
||||
GenericDisplayRole, /**< A generic string based upon the message type. */
|
||||
MediaInfoRole, /**< The media info for the event. */
|
||||
|
||||
ContentModelRole, /**< The MessageContentModel for the event. */
|
||||
|
||||
IsThreadedRole, /**< Whether the message is in a thread. */
|
||||
ThreadRootRole, /**< The Matrix ID of the thread root message, if any . */
|
||||
|
||||
ShowSectionRole, /**< Whether the section header should be shown. */
|
||||
|
||||
ReadMarkersRole, /**< The first 5 other users at the event for read marker tracking. */
|
||||
ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */
|
||||
ReactionRole, /**< List model for this event. */
|
||||
ShowReactionsRole, /**< Whether there are any reactions to be shown. */
|
||||
|
||||
VerifiedRole, /**< Whether an encrypted message is sent in a verified session. */
|
||||
AuthorDisplayNameRole, /**< The displayname for the event's sender; for name change events, the old displayname. */
|
||||
IsRedactedRole, /**< Whether an event has been deleted. */
|
||||
IsPendingRole, /**< Whether an event is waiting to be accepted by the server. */
|
||||
IsEditableRole, /**< Whether the event can be edited by the user. */
|
||||
LastRole, // Keep this last
|
||||
};
|
||||
Q_ENUM(EventRoles)
|
||||
|
||||
explicit TimelineMessageModel(QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user