Create a content provider instance to get message content models from
This means that all content models will now come from the same source to remove duplication across multiple models and `chatbarcaches`. It also handily breaks the dependency on needing `MessageContentModel` for `NeochatRoom`
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <Quotient/events/roommessageevent.h>
|
#include <Quotient/events/roommessageevent.h>
|
||||||
|
|
||||||
|
#include "models/messagecontentmodel.h"
|
||||||
#include "testutils.h"
|
#include "testutils.h"
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|||||||
@@ -198,6 +198,8 @@ add_library(neochat STATIC
|
|||||||
models/commonroomsmodel.h
|
models/commonroomsmodel.h
|
||||||
models/pollanswermodel.cpp
|
models/pollanswermodel.cpp
|
||||||
models/pollanswermodel.h
|
models/pollanswermodel.h
|
||||||
|
contentprovider.cpp
|
||||||
|
contentprovider.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set_source_files_properties(qml/TextToSpeechWrapper.qml PROPERTIES
|
set_source_files_properties(qml/TextToSpeechWrapper.qml PROPERTIES
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <Quotient/roommember.h>
|
#include <Quotient/roommember.h>
|
||||||
|
|
||||||
#include "chatdocumenthandler.h"
|
#include "chatdocumenthandler.h"
|
||||||
|
#include "contentprovider.h"
|
||||||
#include "eventhandler.h"
|
#include "eventhandler.h"
|
||||||
#include "models/actionsmodel.h"
|
#include "models/actionsmodel.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
@@ -170,17 +171,13 @@ MessageContentModel *ChatBarCache::relationEventContentModel()
|
|||||||
if (m_relationId.isEmpty()) {
|
if (m_relationId.isEmpty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (m_relationContentModel != nullptr) {
|
|
||||||
return m_relationContentModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto room = dynamic_cast<NeoChatRoom *>(parent());
|
auto room = dynamic_cast<NeoChatRoom *>(parent());
|
||||||
if (room == nullptr) {
|
if (room == nullptr) {
|
||||||
qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
|
qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
m_relationContentModel = new MessageContentModel(room, m_relationId, true);
|
|
||||||
return m_relationContentModel;
|
return ContentProvider::self().contentModelForEvent(room, m_relationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChatBarCache::isThreaded() const
|
bool ChatBarCache::isThreaded() const
|
||||||
|
|||||||
86
src/contentprovider.cpp
Normal file
86
src/contentprovider.cpp
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
// 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 "contentprovider.h"
|
||||||
|
|
||||||
|
ContentProvider::ContentProvider()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentProvider &ContentProvider::self()
|
||||||
|
{
|
||||||
|
static ContentProvider instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageContentModel *ContentProvider::contentModelForEvent(NeoChatRoom *room, const QString &evtOrTxnId, bool isReply)
|
||||||
|
{
|
||||||
|
if (!room || evtOrTxnId.isEmpty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_eventContentModels.contains(evtOrTxnId)) {
|
||||||
|
m_eventContentModels.insert(evtOrTxnId, new MessageContentModel(room, evtOrTxnId, isReply));
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_eventContentModels.object(evtOrTxnId);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageContentModel *ContentProvider::contentModelForEvent(NeoChatRoom *room, const Quotient::RoomEvent *event, bool isReply)
|
||||||
|
{
|
||||||
|
if (!room) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event);
|
||||||
|
if (roomMessageEvent == nullptr) {
|
||||||
|
// If for some reason a model is there remove.
|
||||||
|
if (m_eventContentModels.contains(event->id())) {
|
||||||
|
m_eventContentModels.remove(event->id());
|
||||||
|
}
|
||||||
|
if (m_eventContentModels.contains(event->transactionId())) {
|
||||||
|
m_eventContentModels.remove(event->transactionId());
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->isStateEvent() || event->matrixType() == u"org.matrix.msc3672.beacon_info"_s) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto eventId = event->id();
|
||||||
|
const auto txnId = event->transactionId();
|
||||||
|
if (!m_eventContentModels.contains(eventId) && !m_eventContentModels.contains(txnId)) {
|
||||||
|
m_eventContentModels.insert(eventId.isEmpty() ? txnId : eventId,
|
||||||
|
new MessageContentModel(room, eventId.isEmpty() ? txnId : eventId, isReply, eventId.isEmpty()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!eventId.isEmpty() && m_eventContentModels.contains(eventId)) {
|
||||||
|
return m_eventContentModels.object(eventId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!txnId.isEmpty() && m_eventContentModels.contains(txnId)) {
|
||||||
|
if (eventId.isEmpty()) {
|
||||||
|
return m_eventContentModels.object(txnId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we now have an event ID use that as the map key instead of transaction ID.
|
||||||
|
auto txnModel = m_eventContentModels.take(txnId);
|
||||||
|
m_eventContentModels.insert(eventId, txnModel);
|
||||||
|
return m_eventContentModels.object(eventId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadModel *ContentProvider::modelForThread(NeoChatRoom *room, const QString &threadRootId)
|
||||||
|
{
|
||||||
|
if (!room || threadRootId.isEmpty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_threadModels.contains(threadRootId)) {
|
||||||
|
m_threadModels.insert(threadRootId, new ThreadModel(threadRootId, room));
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_threadModels.object(threadRootId);
|
||||||
|
}
|
||||||
67
src/contentprovider.h
Normal file
67
src/contentprovider.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// 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 <QCache>
|
||||||
|
|
||||||
|
#include "models/messagecontentmodel.h"
|
||||||
|
#include "models/threadmodel.h"
|
||||||
|
#include "neochatroom.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class ContentProvider
|
||||||
|
*
|
||||||
|
* Store and retrieve models for message content.
|
||||||
|
*/
|
||||||
|
class ContentProvider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Get the global instance of ContentProvider.
|
||||||
|
*/
|
||||||
|
static ContentProvider &self();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the content model for the given event ID.
|
||||||
|
*
|
||||||
|
* A model is created if one doesn't exist. Will return nullptr if evtOrTxnId
|
||||||
|
* is empty.
|
||||||
|
*
|
||||||
|
* @warning If a non-empty ID is given it is assumed to be a valid Quotient::RoomMessageEvent
|
||||||
|
* event ID. The caller must ensure that the ID is a real event. A model will be
|
||||||
|
* returned unconditionally.
|
||||||
|
*
|
||||||
|
* @warning Do NOT use for pending events as this function has no way to differentiate.
|
||||||
|
*/
|
||||||
|
MessageContentModel *contentModelForEvent(NeoChatRoom *room, const QString &evtOrTxnId, bool isReply = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the content model for the given event.
|
||||||
|
*
|
||||||
|
* A model is created if one doesn't exist. Will return nullptr if event is:
|
||||||
|
* - nullptr
|
||||||
|
* - not a Quotient::RoomMessageEvent (e.g a state event)
|
||||||
|
*
|
||||||
|
* @note This method is preferred to the version using just an event ID as it
|
||||||
|
* can perform some basic checks. If a copy of the event is not available,
|
||||||
|
* you may have to use the version that takes an event ID.
|
||||||
|
*
|
||||||
|
* @note This version must be used for pending events as it can differentiate.
|
||||||
|
*/
|
||||||
|
MessageContentModel *contentModelForEvent(NeoChatRoom *room, const Quotient::RoomEvent *event, bool isReply = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the thread model for the given thread root event ID.
|
||||||
|
*
|
||||||
|
* A model is created if one doesn't exist. Will return nullptr if threadRootId
|
||||||
|
* is empty.
|
||||||
|
*/
|
||||||
|
ThreadModel *modelForThread(NeoChatRoom *room, const QString &threadRootId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ContentProvider();
|
||||||
|
|
||||||
|
QCache<QString, MessageContentModel> m_eventContentModels;
|
||||||
|
QCache<QString, ThreadModel> m_threadModels;
|
||||||
|
};
|
||||||
@@ -50,6 +50,22 @@ void MessageAttached::setTimeline(QQuickItem *timeline)
|
|||||||
Q_EMIT timelineChanged();
|
Q_EMIT timelineChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MessageContentModel *MessageAttached::contentModel() const
|
||||||
|
{
|
||||||
|
return m_contentModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageAttached::setContentModel(MessageContentModel *contentModel)
|
||||||
|
{
|
||||||
|
m_explicitContentModel = true;
|
||||||
|
if (m_contentModel == contentModel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_contentModel = contentModel;
|
||||||
|
propagateMessage(this);
|
||||||
|
Q_EMIT contentModelChanged();
|
||||||
|
}
|
||||||
|
|
||||||
int MessageAttached::index() const
|
int MessageAttached::index() const
|
||||||
{
|
{
|
||||||
return m_index;
|
return m_index;
|
||||||
@@ -126,6 +142,11 @@ void MessageAttached::propagateMessage(MessageAttached *message)
|
|||||||
Q_EMIT timelineChanged();
|
Q_EMIT timelineChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!m_explicitContentModel && m_contentModel != message->contentModel()) {
|
||||||
|
m_contentModel = message->contentModel();
|
||||||
|
Q_EMIT contentModelChanged();
|
||||||
|
}
|
||||||
|
|
||||||
if (m_explicitIndex || m_index != message->index()) {
|
if (m_explicitIndex || m_index != message->index()) {
|
||||||
m_index = message->index();
|
m_index = message->index();
|
||||||
Q_EMIT indexChanged();
|
Q_EMIT indexChanged();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <QQuickAttachedPropertyPropagator>
|
#include <QQuickAttachedPropertyPropagator>
|
||||||
#include <QQuickItem>
|
#include <QQuickItem>
|
||||||
|
|
||||||
|
#include "messagecontentmodel.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
|
|
||||||
class MessageAttached : public QQuickAttachedPropertyPropagator
|
class MessageAttached : public QQuickAttachedPropertyPropagator
|
||||||
@@ -26,6 +27,11 @@ class MessageAttached : public QQuickAttachedPropertyPropagator
|
|||||||
*/
|
*/
|
||||||
Q_PROPERTY(QQuickItem *timeline READ timeline WRITE setTimeline NOTIFY timelineChanged FINAL)
|
Q_PROPERTY(QQuickItem *timeline READ timeline WRITE setTimeline NOTIFY timelineChanged FINAL)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The content model for the current message.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(MessageContentModel *contentModel READ contentModel WRITE setContentModel NOTIFY contentModelChanged FINAL)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The index of the message in the timeline
|
* @brief The index of the message in the timeline
|
||||||
*/
|
*/
|
||||||
@@ -57,6 +63,9 @@ public:
|
|||||||
QQuickItem *timeline() const;
|
QQuickItem *timeline() const;
|
||||||
void setTimeline(QQuickItem *timeline);
|
void setTimeline(QQuickItem *timeline);
|
||||||
|
|
||||||
|
MessageContentModel *contentModel() const;
|
||||||
|
void setContentModel(MessageContentModel *contentModel);
|
||||||
|
|
||||||
int index() const;
|
int index() const;
|
||||||
void setIndex(int index);
|
void setIndex(int index);
|
||||||
|
|
||||||
@@ -72,6 +81,7 @@ public:
|
|||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void roomChanged();
|
void roomChanged();
|
||||||
void timelineChanged();
|
void timelineChanged();
|
||||||
|
void contentModelChanged();
|
||||||
void indexChanged();
|
void indexChanged();
|
||||||
void maxContentWidthChanged();
|
void maxContentWidthChanged();
|
||||||
void selectedTextChanged();
|
void selectedTextChanged();
|
||||||
@@ -88,6 +98,9 @@ private:
|
|||||||
QPointer<QQuickItem> m_timeline;
|
QPointer<QQuickItem> m_timeline;
|
||||||
bool m_explicitTimeline = false;
|
bool m_explicitTimeline = false;
|
||||||
|
|
||||||
|
QPointer<MessageContentModel> m_contentModel;
|
||||||
|
bool m_explicitContentModel = false;
|
||||||
|
|
||||||
int m_index = -1;
|
int m_index = -1;
|
||||||
bool m_explicitIndex = false;
|
bool m_explicitIndex = false;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include <Quotient/events/roommessageevent.h>
|
#include <Quotient/events/roommessageevent.h>
|
||||||
#include <Quotient/room.h>
|
#include <Quotient/room.h>
|
||||||
|
|
||||||
#include "messagecontentmodel.h"
|
|
||||||
#include "messagefiltermodel.h"
|
#include "messagefiltermodel.h"
|
||||||
#include "timelinemessagemodel.h"
|
#include "timelinemessagemodel.h"
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
#include "messagecontentmodel.h"
|
#include "messagecontentmodel.h"
|
||||||
|
#include "contentprovider.h"
|
||||||
#include "eventhandler.h"
|
#include "eventhandler.h"
|
||||||
#include "messagecomponenttype.h"
|
#include "messagecomponenttype.h"
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "chatbarcache.h"
|
#include "chatbarcache.h"
|
||||||
|
#include "contentprovider.h"
|
||||||
#include "filetype.h"
|
#include "filetype.h"
|
||||||
#include "linkpreviewer.h"
|
#include "linkpreviewer.h"
|
||||||
#include "models/reactionmodel.h"
|
#include "models/reactionmodel.h"
|
||||||
@@ -767,4 +769,9 @@ void MessageContentModel::updateReactionModel()
|
|||||||
resetContent();
|
resetContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThreadModel *MessageContentModel::modelForThread(const QString &threadRootId)
|
||||||
|
{
|
||||||
|
return ContentProvider::self().modelForThread(m_room, threadRootId);
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_messagecontentmodel.cpp"
|
#include "moc_messagecontentmodel.cpp"
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
#include "models/reactionmodel.h"
|
#include "models/reactionmodel.h"
|
||||||
#include "neochatroommember.h"
|
#include "neochatroommember.h"
|
||||||
|
|
||||||
|
class ThreadModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class MessageContentModel
|
* @class MessageContentModel
|
||||||
*
|
*
|
||||||
@@ -102,6 +104,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
Q_INVOKABLE void closeLinkPreview(int row);
|
Q_INVOKABLE void closeLinkPreview(int row);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the thread model for the given thread root event ID.
|
||||||
|
*
|
||||||
|
* A model is created is one doesn't exist. Will return nullptr if threadRootId
|
||||||
|
* is empty.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE ThreadModel *modelForThread(const QString &threadRootId);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void showAuthorChanged();
|
void showAuthorChanged();
|
||||||
void eventUpdated();
|
void eventUpdated();
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
#include "enums/delegatetype.h"
|
#include "enums/delegatetype.h"
|
||||||
#include "messagecontentmodel.h"
|
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
#include "timelinemessagemodel.h"
|
#include "timelinemessagemodel.h"
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "messagemodel.h"
|
#include "messagemodel.h"
|
||||||
|
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
|
#include "threadmodel.h"
|
||||||
|
|
||||||
#include <Quotient/events/encryptedevent.h>
|
#include <Quotient/events/encryptedevent.h>
|
||||||
#include <Quotient/events/roommessageevent.h>
|
#include <Quotient/events/roommessageevent.h>
|
||||||
@@ -14,6 +15,7 @@
|
|||||||
|
|
||||||
#include <KFormat>
|
#include <KFormat>
|
||||||
|
|
||||||
|
#include "contentprovider.h"
|
||||||
#include "enums/delegatetype.h"
|
#include "enums/delegatetype.h"
|
||||||
#include "enums/messagecomponenttype.h"
|
#include "enums/messagecomponenttype.h"
|
||||||
#include "eventhandler.h"
|
#include "eventhandler.h"
|
||||||
@@ -122,14 +124,14 @@ QVariant MessageModel::data(const QModelIndex &idx, int role) const
|
|||||||
|
|
||||||
if (role == ContentModelRole) {
|
if (role == ContentModelRole) {
|
||||||
if (event->get().is<EncryptedEvent>() || event->get().is<PollStartEvent>()) {
|
if (event->get().is<EncryptedEvent>() || event->get().is<PollStartEvent>()) {
|
||||||
return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(event->get().id()));
|
return QVariant::fromValue<MessageContentModel *>(ContentProvider::self().contentModelForEvent(m_room, event->get().id()));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(&event.value().get());
|
auto roomMessageEvent = eventCast<const RoomMessageEvent>(&event.value().get());
|
||||||
if (NeoChatConfig::self()->threads() && roomMessageEvent && roomMessageEvent->isThreaded()) {
|
if (NeoChatConfig::self()->threads() && roomMessageEvent && roomMessageEvent->isThreaded()) {
|
||||||
return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(roomMessageEvent->threadRootEventId()));
|
return QVariant::fromValue<MessageContentModel *>(ContentProvider::self().contentModelForEvent(m_room, roomMessageEvent->threadRootEventId()));
|
||||||
}
|
}
|
||||||
return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(&event->get()));
|
return QVariant::fromValue<MessageContentModel *>(ContentProvider::self().contentModelForEvent(m_room, &event->get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == GenericDisplayRole) {
|
if (role == GenericDisplayRole) {
|
||||||
@@ -483,9 +485,4 @@ bool MessageModel::event(QEvent *event)
|
|||||||
return QObject::event(event);
|
return QObject::event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadModel *MessageModel::threadModelForRootId(const QString &threadRootId) const
|
|
||||||
{
|
|
||||||
return m_room->modelForThread(threadRootId);
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "moc_messagemodel.cpp"
|
#include "moc_messagemodel.cpp"
|
||||||
|
|||||||
@@ -113,11 +113,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
Q_INVOKABLE [[nodiscard]] int eventIdToRow(const QString &eventID) const;
|
Q_INVOKABLE [[nodiscard]] int eventIdToRow(const QString &eventID) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get a ThreadModel for the give thread root Matrix ID.
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE ThreadModel *threadModelForRootId(const QString &threadRootId) const;
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
/**
|
/**
|
||||||
* @brief Emitted when the room is changed.
|
* @brief Emitted when the room is changed.
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
#include "enums/delegatetype.h"
|
#include "enums/delegatetype.h"
|
||||||
#include "eventhandler.h"
|
#include "eventhandler.h"
|
||||||
#include "models/messagecontentmodel.h"
|
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
|
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
|
|
||||||
|
#include "models/messagecontentmodel.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "chatbarcache.h"
|
#include "chatbarcache.h"
|
||||||
|
#include "contentprovider.h"
|
||||||
#include "eventhandler.h"
|
#include "eventhandler.h"
|
||||||
#include "messagecomponenttype.h"
|
#include "messagecomponenttype.h"
|
||||||
#include "messagecontentmodel.h"
|
#include "messagecontentmodel.h"
|
||||||
@@ -136,9 +137,9 @@ void ThreadModel::addModels()
|
|||||||
}
|
}
|
||||||
addSourceModel(m_threadFetchModel);
|
addSourceModel(m_threadFetchModel);
|
||||||
for (auto it = m_events.crbegin(); it != m_events.crend(); ++it) {
|
for (auto it = m_events.crbegin(); it != m_events.crend(); ++it) {
|
||||||
const auto contentModel = room->contentModelForEvent(*it);
|
const auto contentModel = ContentProvider::self().contentModelForEvent(room, *it);
|
||||||
if (contentModel != nullptr) {
|
if (contentModel != nullptr) {
|
||||||
addSourceModel(room->contentModelForEvent(*it));
|
addSourceModel(ContentProvider::self().contentModelForEvent(room, *it));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addSourceModel(m_threadChatBarModel);
|
addSourceModel(m_threadChatBarModel);
|
||||||
@@ -155,7 +156,7 @@ void ThreadModel::clearModels()
|
|||||||
}
|
}
|
||||||
removeSourceModel(m_threadFetchModel);
|
removeSourceModel(m_threadFetchModel);
|
||||||
for (const auto &model : m_events) {
|
for (const auto &model : m_events) {
|
||||||
const auto contentModel = room->contentModelForEvent(model);
|
const auto contentModel = ContentProvider::self().contentModelForEvent(room, model);
|
||||||
if (sourceModels().contains(contentModel)) {
|
if (sourceModels().contains(contentModel)) {
|
||||||
removeSourceModel(contentModel);
|
removeSourceModel(contentModel);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,8 +173,6 @@ void NeoChatRoom::setVisible(bool visible)
|
|||||||
|
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
m_memberObjects.clear();
|
m_memberObjects.clear();
|
||||||
m_eventContentModels.clear();
|
|
||||||
m_threadModels.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1706,77 +1704,6 @@ NeochatRoomMember *NeoChatRoom::qmlSafeMember(const QString &memberId)
|
|||||||
return m_memberObjects[memberId].get();
|
return m_memberObjects[memberId].get();
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageContentModel *NeoChatRoom::contentModelForEvent(const QString &eventId)
|
|
||||||
{
|
|
||||||
if (eventId.isEmpty()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_eventContentModels.contains(eventId)) {
|
|
||||||
return m_eventContentModels.emplace(eventId, std::make_unique<MessageContentModel>(this, eventId)).first->second.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_eventContentModels[eventId].get();
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageContentModel *NeoChatRoom::contentModelForEvent(const Quotient::RoomEvent *event)
|
|
||||||
{
|
|
||||||
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event);
|
|
||||||
if (roomMessageEvent == nullptr) {
|
|
||||||
// If for some reason a model is there remove.
|
|
||||||
if (m_eventContentModels.contains(event->id())) {
|
|
||||||
m_eventContentModels.erase(event->id());
|
|
||||||
}
|
|
||||||
if (m_eventContentModels.contains(event->transactionId())) {
|
|
||||||
m_eventContentModels.erase(event->transactionId());
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event->isStateEvent() || event->matrixType() == u"org.matrix.msc3672.beacon_info"_s) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto eventId = event->id();
|
|
||||||
const auto txnId = event->transactionId();
|
|
||||||
if (!m_eventContentModels.contains(eventId) && !m_eventContentModels.contains(txnId)) {
|
|
||||||
return m_eventContentModels
|
|
||||||
.emplace(eventId.isEmpty() ? txnId : eventId,
|
|
||||||
std::make_unique<MessageContentModel>(this, eventId.isEmpty() ? txnId : eventId, false, eventId.isEmpty()))
|
|
||||||
.first->second.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!eventId.isEmpty() && m_eventContentModels.contains(eventId)) {
|
|
||||||
return m_eventContentModels[eventId].get();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!txnId.isEmpty() && m_eventContentModels.contains(txnId)) {
|
|
||||||
if (eventId.isEmpty()) {
|
|
||||||
return m_eventContentModels[txnId].get();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we now have an event ID use that as the map key instead of transaction ID.
|
|
||||||
auto txnModel = std::move(m_eventContentModels[txnId]);
|
|
||||||
m_eventContentModels.erase(txnId);
|
|
||||||
return m_eventContentModels.emplace(eventId, std::move(txnModel)).first->second.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadModel *NeoChatRoom::modelForThread(const QString &threadRootId)
|
|
||||||
{
|
|
||||||
if (threadRootId.isEmpty()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_threadModels.contains(threadRootId)) {
|
|
||||||
return m_threadModels.emplace(threadRootId, std::make_unique<ThreadModel>(threadRootId, this)).first->second.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_threadModels[threadRootId].get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NeoChatRoom::pinEvent(const QString &eventId)
|
void NeoChatRoom::pinEvent(const QString &eventId)
|
||||||
{
|
{
|
||||||
auto eventIds = pinnedEventIds();
|
auto eventIds = pinnedEventIds();
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
#include "enums/messagetype.h"
|
#include "enums/messagetype.h"
|
||||||
#include "enums/pushrule.h"
|
#include "enums/pushrule.h"
|
||||||
#include "events/pollevent.h"
|
#include "events/pollevent.h"
|
||||||
#include "models/messagecontentmodel.h"
|
|
||||||
#include "models/threadmodel.h"
|
|
||||||
#include "neochatroommember.h"
|
#include "neochatroommember.h"
|
||||||
#include "pollhandler.h"
|
#include "pollhandler.h"
|
||||||
|
|
||||||
@@ -560,43 +558,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
NeochatRoomMember *qmlSafeMember(const QString &memberId);
|
NeochatRoomMember *qmlSafeMember(const QString &memberId);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns the content model for the given event ID.
|
|
||||||
*
|
|
||||||
* A model is created is one doesn't exist. Will return nullptr if evtOrTxnId
|
|
||||||
* is empty.
|
|
||||||
*
|
|
||||||
* @warning If a non-empty ID is given it is assumed to be a valid Quotient::RoomMessageEvent
|
|
||||||
* event ID. The caller must ensure that the ID is a real event. A model will be
|
|
||||||
* returned unconditionally.
|
|
||||||
*
|
|
||||||
* @warning Do NOT use for pending events as this function has no way to differentiate.
|
|
||||||
*/
|
|
||||||
MessageContentModel *contentModelForEvent(const QString &evtOrTxnId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns the content model for the given event.
|
|
||||||
*
|
|
||||||
* A model is created is one doesn't exist. Will return nullptr if event is:
|
|
||||||
* - nullptr
|
|
||||||
* - not a Quotient::RoomMessageEvent (e.g a state event)
|
|
||||||
*
|
|
||||||
* @note This method is preferred to the version using just an event ID as it
|
|
||||||
* can perform some basic checks. If a copy of the event is not available,
|
|
||||||
* you may have to use the version that takes an event ID.
|
|
||||||
*
|
|
||||||
* @note This version must be used for pending events as it can differentiate.
|
|
||||||
*/
|
|
||||||
MessageContentModel *contentModelForEvent(const Quotient::RoomEvent *event);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns the thread model for the given thread root event ID.
|
|
||||||
*
|
|
||||||
* A model is created is one doesn't exist. Will return nullptr if threadRootId
|
|
||||||
* is empty.
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE ThreadModel *modelForThread(const QString &threadRootId);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Pin a message in the room.
|
* @brief Pin a message in the room.
|
||||||
* @param eventId The id of the event to pin.
|
* @param eventId The id of the event to pin.
|
||||||
@@ -646,8 +607,6 @@ private:
|
|||||||
void cleanupExtraEvent(const QString &eventId);
|
void cleanupExtraEvent(const QString &eventId);
|
||||||
|
|
||||||
std::unordered_map<QString, std::unique_ptr<NeochatRoomMember>> m_memberObjects;
|
std::unordered_map<QString, std::unique_ptr<NeochatRoomMember>> m_memberObjects;
|
||||||
std::unordered_map<QString, std::unique_ptr<MessageContentModel>> m_eventContentModels;
|
|
||||||
std::unordered_map<QString, std::unique_ptr<ThreadModel>> m_threadModels;
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void updatePushNotificationState(QString type);
|
void updatePushNotificationState(QString type);
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ TimelineDelegate {
|
|||||||
|
|
||||||
Message.room: root.room
|
Message.room: root.room
|
||||||
Message.timeline: root.ListView.view
|
Message.timeline: root.ListView.view
|
||||||
|
Message.contentModel: root.contentModel
|
||||||
Message.index: root.index
|
Message.index: root.index
|
||||||
Message.maxContentWidth: contentMaxWidth
|
Message.maxContentWidth: contentMaxWidth
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ ColumnLayout {
|
|||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: threadRepeater
|
id: threadRepeater
|
||||||
model: root.Message.room.modelForThread(root.threadRoot);
|
model: root.Message.contentModel.modelForThread(root.threadRoot);
|
||||||
|
|
||||||
delegate: BaseMessageComponentChooser {
|
delegate: BaseMessageComponentChooser {
|
||||||
onSelectedTextChanged: selectedText => {
|
onSelectedTextChanged: selectedText => {
|
||||||
|
|||||||
Reference in New Issue
Block a user