Move the storage of MessageContentModels to the room
Move the storage of MessageContentModels to the room in the same manner as memeber objects to prevent duplication but mainly to make the system easier to maintain going forward with things like threads for example. This requires the creation of a MessageContentFilterModel as the same model may be used in multiple places, sometimes with the author showning sometimes not.
This commit is contained in:
@@ -39,14 +39,6 @@ QVariant MediaMessageFilterModel::data(const QModelIndex &index, int role) const
|
||||
const auto previousEventDay = mapToSource(this->index(index.row() + 1, 0)).data(TimelineMessageModel::TimeRole).toDateTime().toLocalTime().date();
|
||||
return day != previousEventDay;
|
||||
}
|
||||
// Catch and force the author to be shown for all rows
|
||||
if (role == TimelineMessageModel::ContentModelRole) {
|
||||
const auto model = qvariant_cast<MessageContentModel *>(mapToSource(index).data(TimelineMessageModel::ContentModelRole));
|
||||
if (model != nullptr) {
|
||||
model->setShowAuthor(true);
|
||||
}
|
||||
return QVariant::fromValue<MessageContentModel *>(model);
|
||||
}
|
||||
|
||||
QVariantMap mediaInfo = mapToSource(index).data(TimelineMessageModel::MediaInfoRole).toMap();
|
||||
|
||||
|
||||
37
src/models/messagecontentfiltermodel.cpp
Normal file
37
src/models/messagecontentfiltermodel.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
// SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "messagecontentfiltermodel.h"
|
||||
#include "enums/messagecomponenttype.h"
|
||||
#include "models/messagecontentmodel.h"
|
||||
|
||||
MessageContentFilterModel::MessageContentFilterModel(QObject *parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool MessageContentFilterModel::showAuthor() const
|
||||
{
|
||||
return m_showAuthor;
|
||||
}
|
||||
|
||||
void MessageContentFilterModel::setShowAuthor(bool showAuthor)
|
||||
{
|
||||
if (showAuthor == m_showAuthor) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_showAuthor = showAuthor;
|
||||
Q_EMIT showAuthorChanged();
|
||||
}
|
||||
|
||||
bool MessageContentFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||
{
|
||||
if (m_showAuthor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto index = sourceModel()->index(source_row, 0, source_parent);
|
||||
auto contentType = static_cast<MessageComponentType::Type>(index.data(MessageContentModel::ComponentTypeRole).toInt());
|
||||
return contentType != MessageComponentType::Author;
|
||||
}
|
||||
43
src/models/messagecontentfiltermodel.h
Normal file
43
src/models/messagecontentfiltermodel.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QQmlEngine>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
/**
|
||||
* @class MessageContentFilterModel
|
||||
*
|
||||
* This model filters a message's contents.
|
||||
*/
|
||||
class MessageContentFilterModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
/**
|
||||
* @brief Whether the author component should be shown.
|
||||
*/
|
||||
Q_PROPERTY(bool showAuthor READ showAuthor WRITE setShowAuthor NOTIFY showAuthorChanged)
|
||||
|
||||
public:
|
||||
explicit MessageContentFilterModel(QObject *parent = nullptr);
|
||||
|
||||
bool showAuthor() const;
|
||||
void setShowAuthor(bool showAuthor);
|
||||
|
||||
Q_SIGNALS:
|
||||
void showAuthorChanged();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Whether a row should be shown out or not.
|
||||
*
|
||||
* @sa QSortFilterProxyModel::filterAcceptsRow
|
||||
*/
|
||||
[[nodiscard]] bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
||||
|
||||
private:
|
||||
bool m_showAuthor = true;
|
||||
};
|
||||
@@ -233,32 +233,6 @@ NeochatRoomMember *MessageContentModel::senderObject() const
|
||||
return m_room->qmlSafeMember(eventResult.first->senderId());
|
||||
}
|
||||
|
||||
bool MessageContentModel::showAuthor() const
|
||||
{
|
||||
return m_showAuthor;
|
||||
}
|
||||
|
||||
void MessageContentModel::setShowAuthor(bool showAuthor)
|
||||
{
|
||||
if (showAuthor == m_showAuthor) {
|
||||
return;
|
||||
}
|
||||
m_showAuthor = showAuthor;
|
||||
|
||||
if (m_room->connection()->isIgnored(senderId())) {
|
||||
if (showAuthor) {
|
||||
beginInsertRows({}, 0, 0);
|
||||
m_components.prepend(MessageComponent{MessageComponentType::Author, QString(), {}});
|
||||
endInsertRows();
|
||||
} else {
|
||||
beginRemoveRows({}, 0, 0);
|
||||
m_components.remove(0, 1);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
Q_EMIT showAuthorChanged();
|
||||
}
|
||||
|
||||
static LinkPreviewer *emptyLinkPreview = new LinkPreviewer;
|
||||
|
||||
QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
||||
@@ -434,9 +408,7 @@ void MessageContentModel::resetModel()
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_showAuthor) {
|
||||
m_components += MessageComponent{MessageComponentType::Author, QString(), {}};
|
||||
}
|
||||
m_components += MessageComponent{MessageComponentType::Author, QString(), {}};
|
||||
|
||||
m_components += messageContentComponents();
|
||||
endResetModel();
|
||||
|
||||
@@ -25,11 +25,6 @@ class MessageContentModel : public QAbstractListModel
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
/**
|
||||
* @brief Whether the author component is being shown.
|
||||
*/
|
||||
Q_PROPERTY(bool showAuthor READ showAuthor WRITE setShowAuthor NOTIFY showAuthorChanged)
|
||||
|
||||
public:
|
||||
enum MessageState {
|
||||
Unknown, /**< The message state is unknown. */
|
||||
@@ -75,9 +70,6 @@ public:
|
||||
bool isPending = false,
|
||||
MessageContentModel *parent = nullptr);
|
||||
|
||||
bool showAuthor() const;
|
||||
void setShowAuthor(bool showAuthor);
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
*
|
||||
@@ -117,7 +109,6 @@ private:
|
||||
NeochatRoomMember *senderObject() const;
|
||||
|
||||
MessageState m_currentState = Unknown;
|
||||
bool m_showAuthor = true;
|
||||
bool m_isReply;
|
||||
|
||||
void initializeModel();
|
||||
|
||||
@@ -92,12 +92,8 @@ QVariant MessageFilterModel::data(const QModelIndex &index, int role) const
|
||||
return authorList(mapToSource(index).row());
|
||||
} else if (role == ExcessAuthorsRole) {
|
||||
return excessAuthors(mapToSource(index).row());
|
||||
} else if (role == TimelineMessageModel::ContentModelRole) {
|
||||
const auto model = qvariant_cast<MessageContentModel *>(mapToSource(index).data(TimelineMessageModel::ContentModelRole));
|
||||
if (model != nullptr && !showAuthor(index)) {
|
||||
model->setShowAuthor(false);
|
||||
}
|
||||
return QVariant::fromValue<MessageContentModel *>(model);
|
||||
} else if (role == ShowAuthorRole) {
|
||||
return showAuthor(index);
|
||||
}
|
||||
return QSortFilterProxyModel::data(index, role);
|
||||
}
|
||||
@@ -109,6 +105,7 @@ QHash<int, QByteArray> MessageFilterModel::roleNames() const
|
||||
roles[StateEventsRole] = "stateEvents";
|
||||
roles[AuthorListRole] = "authorList";
|
||||
roles[ExcessAuthorsRole] = "excessAuthors";
|
||||
roles[ShowAuthorRole] = "showAuthor";
|
||||
return roles;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
StateEventsRole, /**< List of state events in the aggregated state. */
|
||||
AuthorListRole, /**< List of the first 5 unique authors of the aggregated state event. */
|
||||
ExcessAuthorsRole, /**< The number of unique authors beyond the first 5. */
|
||||
ShowAuthorRole, /**< Whether the author of a message should be shown. */
|
||||
LastRole, // Keep this last
|
||||
};
|
||||
|
||||
|
||||
@@ -120,16 +120,11 @@ QVariant MessageModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
if (role == ContentModelRole) {
|
||||
QString modelId;
|
||||
if (!event->get().id().isEmpty() && m_contentModels.contains(event->get().id())) {
|
||||
modelId = event.value().get().id();
|
||||
} else if (!event.value().get().transactionId().isEmpty() && m_contentModels.contains(event.value().get().transactionId())) {
|
||||
modelId = event.value().get().transactionId();
|
||||
auto evtOrTxnId = event->get().id();
|
||||
if (evtOrTxnId.isEmpty()) {
|
||||
evtOrTxnId = event->get().transactionId();
|
||||
}
|
||||
if (!modelId.isEmpty()) {
|
||||
return QVariant::fromValue<MessageContentModel *>(m_contentModels.at(modelId).get());
|
||||
}
|
||||
return {};
|
||||
return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(evtOrTxnId));
|
||||
}
|
||||
|
||||
if (role == GenericDisplayRole) {
|
||||
@@ -430,12 +425,6 @@ void MessageModel::createEventObjects(const Quotient::RoomEvent *event, bool isP
|
||||
senderId = m_room->localMember().id();
|
||||
}
|
||||
|
||||
if (!m_contentModels.contains(eventId) && !m_contentModels.contains(event->transactionId())) {
|
||||
if (!event->isStateEvent() || event->matrixType() == u"org.matrix.msc3672.beacon_info"_s) {
|
||||
m_contentModels[eventId] = std::unique_ptr<MessageContentModel>(new MessageContentModel(m_room, eventId, false, isPending));
|
||||
}
|
||||
}
|
||||
|
||||
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event);
|
||||
if (roomMessageEvent && roomMessageEvent->isThreaded() && !m_threadModels.contains(roomMessageEvent->threadRootEventId())) {
|
||||
m_threadModels[roomMessageEvent->threadRootEventId()] = QSharedPointer<ThreadModel>(new ThreadModel(roomMessageEvent->threadRootEventId(), m_room));
|
||||
@@ -515,7 +504,6 @@ void MessageModel::clearModel()
|
||||
|
||||
void MessageModel::clearEventObjects()
|
||||
{
|
||||
m_contentModels.clear();
|
||||
m_reactionModels.clear();
|
||||
m_readMarkerModels.clear();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <QQmlEngine>
|
||||
#include <functional>
|
||||
|
||||
#include "messagecontentmodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "pollhandler.h"
|
||||
#include "readmarkermodel.h"
|
||||
@@ -153,7 +152,6 @@ private:
|
||||
bool resetting = false;
|
||||
bool movingEvent = false;
|
||||
|
||||
std::map<QString, std::unique_ptr<MessageContentModel>> m_contentModels;
|
||||
QMap<QString, QSharedPointer<ReadMarkerModel>> m_readMarkerModels;
|
||||
QMap<QString, QSharedPointer<ThreadModel>> m_threadModels;
|
||||
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
|
||||
|
||||
@@ -97,10 +97,9 @@ void ThreadModel::fetchMore(const QModelIndex &parent)
|
||||
const auto connection = room->connection();
|
||||
m_currentJob = connection->callApi<Quotient::GetRelatingEventsWithRelTypeJob>(room->id(), m_threadRootId, u"m.thread"_s, *m_nextBatch, QString(), 5);
|
||||
connect(m_currentJob, &Quotient::BaseJob::success, this, [this]() {
|
||||
const auto room = dynamic_cast<NeoChatRoom *>(QObject::parent());
|
||||
auto newEvents = m_currentJob->chunk();
|
||||
for (auto &event : newEvents) {
|
||||
m_contentModels.push_back(new MessageContentModel(room, event->id()));
|
||||
m_events.push_back(event->id());
|
||||
}
|
||||
|
||||
addModels();
|
||||
@@ -122,12 +121,11 @@ void ThreadModel::fetchMore(const QModelIndex &parent)
|
||||
|
||||
void ThreadModel::addNewEvent(const Quotient::RoomEvent *event)
|
||||
{
|
||||
const auto room = dynamic_cast<NeoChatRoom *>(QObject::parent());
|
||||
auto eventId = event->id();
|
||||
if (eventId.isEmpty()) {
|
||||
eventId = event->transactionId();
|
||||
}
|
||||
m_contentModels.push_front(new MessageContentModel(room, eventId));
|
||||
m_events.push_front(eventId);
|
||||
}
|
||||
|
||||
void ThreadModel::addModels()
|
||||
@@ -137,8 +135,15 @@ void ThreadModel::addModels()
|
||||
}
|
||||
|
||||
addSourceModel(m_threadRootContentModel.get());
|
||||
for (auto it = m_contentModels.crbegin(); it != m_contentModels.crend(); ++it) {
|
||||
addSourceModel(*it);
|
||||
const auto room = dynamic_cast<NeoChatRoom *>(QObject::parent());
|
||||
if (room == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (auto it = m_events.crbegin(); it != m_events.crend(); ++it) {
|
||||
const auto contentModel = room->contentModelForEvent(*it);
|
||||
if (contentModel != nullptr) {
|
||||
addSourceModel(room->contentModelForEvent(*it));
|
||||
}
|
||||
}
|
||||
addSourceModel(m_threadChatBarModel);
|
||||
|
||||
@@ -149,9 +154,15 @@ void ThreadModel::addModels()
|
||||
void ThreadModel::clearModels()
|
||||
{
|
||||
removeSourceModel(m_threadRootContentModel.get());
|
||||
for (const auto &model : m_contentModels) {
|
||||
if (sourceModels().contains(model)) {
|
||||
removeSourceModel(model);
|
||||
|
||||
const auto room = dynamic_cast<NeoChatRoom *>(QObject::parent());
|
||||
if (room == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (const auto &model : m_events) {
|
||||
const auto contentModel = room->contentModelForEvent(model);
|
||||
if (sourceModels().contains(contentModel)) {
|
||||
removeSourceModel(contentModel);
|
||||
}
|
||||
}
|
||||
removeSourceModel(m_threadChatBarModel);
|
||||
|
||||
@@ -122,14 +122,9 @@ private:
|
||||
|
||||
std::unique_ptr<MessageContentModel> m_threadRootContentModel;
|
||||
|
||||
std::deque<MessageContentModel *> m_contentModels;
|
||||
std::deque<QString> m_events;
|
||||
ThreadChatBarModel *m_threadChatBarModel;
|
||||
|
||||
QList<QString> m_events;
|
||||
QList<QString> m_pendingEvents;
|
||||
|
||||
std::unordered_map<QString, std::unique_ptr<Quotient::RoomEvent>> m_unloadedEvents;
|
||||
|
||||
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
|
||||
|
||||
QPointer<Quotient::GetRelatingEventsWithRelTypeJob> m_currentJob = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user