From 11fd4f88ec61c587fe873cb0d4abb7d7f723a6bf Mon Sep 17 00:00:00 2001 From: James Graham Date: Sat, 27 Jul 2024 08:46:56 +0000 Subject: [PATCH] Create NeochatRoomMember as a shim for RoomMember The intention is that NeochatRoomMember can be created passed to QML and then be fully managed by it. It effectively just grabs the current RoomMember, calls the correct function then discards it so that we don't end up trying to access an already deleted state event. --- autotests/eventhandlertest.cpp | 28 ----- src/CMakeLists.txt | 2 + src/eventhandler.cpp | 14 --- src/eventhandler.h | 17 +-- src/foreigntypes.h | 6 -- src/models/messagecontentmodel.cpp | 34 ++++-- src/models/messagecontentmodel.h | 5 + src/models/messageeventmodel.cpp | 50 +++++---- src/models/messageeventmodel.h | 2 + src/models/messagefiltermodel.cpp | 2 + src/models/searchmodel.cpp | 13 ++- src/models/searchmodel.h | 4 + src/neochatroom.cpp | 5 + src/neochatroommember.cpp | 166 +++++++++++++++++++++++++++++ src/neochatroommember.h | 83 +++++++++++++++ src/qml/CodeMaximizeComponent.qml | 2 +- src/roommanager.cpp | 14 +-- src/roommanager.h | 11 +- src/timeline/CodeComponent.qml | 2 +- src/timeline/HiddenDelegate.qml | 4 +- src/timeline/MessageDelegate.qml | 4 +- src/timeline/StateComponent.qml | 7 +- 22 files changed, 363 insertions(+), 112 deletions(-) create mode 100644 src/neochatroommember.cpp create mode 100644 src/neochatroommember.h diff --git a/autotests/eventhandlertest.cpp b/autotests/eventhandlertest.cpp index 0b62c7c01..b288b4a54 100644 --- a/autotests/eventhandlertest.cpp +++ b/autotests/eventhandlertest.cpp @@ -36,8 +36,6 @@ private Q_SLOTS: void eventId(); void nullEventId(); - void author(); - void nullAuthor(); void authorDisplayName(); void nullAuthorDisplayName(); void singleLineSidplayName(); @@ -96,32 +94,6 @@ void EventHandlerTest::nullEventId() QCOMPARE(noEventHandler.getId(), QString()); } -void EventHandlerTest::author() -{ - auto event = room->messageEvents().at(0).get(); - auto author = room->member(event->senderId()); - EventHandler eventHandler(room, event); - - auto eventHandlerAuthor = eventHandler.getAuthor(); - - QCOMPARE(eventHandlerAuthor.isLocalMember(), author.id() == room->localMember().id()); - QCOMPARE(eventHandlerAuthor.id(), author.id()); - QCOMPARE(eventHandlerAuthor.displayName(), author.displayName()); - QCOMPARE(eventHandlerAuthor.avatarUrl(), author.avatarUrl()); - QCOMPARE(eventHandlerAuthor.avatarMediaId(), author.avatarMediaId()); - QCOMPARE(eventHandlerAuthor.color(), author.color()); -} - -void EventHandlerTest::nullAuthor() -{ - QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_room set to nullptr."); - QCOMPARE(emptyHandler.getAuthor(), RoomMember()); - - EventHandler noEventHandler(room, nullptr); - QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_event set to nullptr. Returning empty user."); - QCOMPARE(noEventHandler.getAuthor(), RoomMember()); -} - void EventHandlerTest::authorDisplayName() { EventHandler eventHandler(room, room->messageEvents().at(1).get()); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4a15d7bcb..0b9420d04 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -190,6 +190,8 @@ add_library(neochat STATIC threepidbindhelper.h models/readmarkermodel.cpp models/readmarkermodel.h + neochatroommember.cpp + neochatroommember.h ) set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES diff --git a/src/eventhandler.cpp b/src/eventhandler.cpp index 237c57382..6b4b65a38 100644 --- a/src/eventhandler.cpp +++ b/src/eventhandler.cpp @@ -61,20 +61,6 @@ MessageComponentType::Type EventHandler::messageComponentType() const return MessageComponentType::typeForEvent(*m_event); } -Quotient::RoomMember EventHandler::getAuthor(bool isPending) const -{ - if (m_room == nullptr) { - qCWarning(EventHandling) << "getAuthor called with m_room set to nullptr."; - return {}; - } - if (m_event == nullptr) { - qCWarning(EventHandling) << "getAuthor called with m_event set to nullptr. Returning empty user."; - return {}; - } - - return isPending ? m_room->localMember() : m_room->member(m_event->senderId()); -} - QString EventHandler::getAuthorDisplayName(bool isPending) const { if (m_room == nullptr) { diff --git a/src/eventhandler.h b/src/eventhandler.h index ca4c2895d..673710239 100644 --- a/src/eventhandler.h +++ b/src/eventhandler.h @@ -53,25 +53,10 @@ public: */ MessageComponentType::Type messageComponentType() const; - /** - * @brief Get the author of the event in context of the room. - * - * An empty Quotient::RoomMember will be returned if the EventHandler hasn't had - * the room or event initialised. - * - * @param isPending if the event is pending, i.e. has not been confirmed by - * the server. - * - * @return a Quotient::RoomMember object for the user. - * - * @sa Quotient::RoomMember - */ - Quotient::RoomMember getAuthor(bool isPending = false) const; - /** * @brief Get the display name of the event author. * - * This method is separate from getAuthor() and special in that it will return + * This method is special in that it will return * the old display name of the author if the current event is one that caused it * to change. This allows for scenarios where the UI wishes to notify that a * user's display name has changed and what it changed from. diff --git a/src/foreigntypes.h b/src/foreigntypes.h index b55333686..02f6e71ea 100644 --- a/src/foreigntypes.h +++ b/src/foreigntypes.h @@ -38,9 +38,3 @@ struct ForeignSSSSHandler { QML_FOREIGN(Quotient::SSSSHandler) QML_NAMED_ELEMENT(SSSSHandler) }; - -struct RoomMemberForeign { - Q_GADGET - QML_FOREIGN(Quotient::RoomMember) - QML_NAMED_ELEMENT(RoomMember) -}; diff --git a/src/models/messagecontentmodel.cpp b/src/models/messagecontentmodel.cpp index 1238ddb45..4b7c15ef1 100644 --- a/src/models/messagecontentmodel.cpp +++ b/src/models/messagecontentmodel.cpp @@ -3,6 +3,7 @@ #include "messagecontentmodel.h" #include "neochatconfig.h" +#include "neochatroommember.h" #include @@ -35,10 +36,10 @@ MessageContentModel::MessageContentModel(NeoChatRoom *room, const Quotient::Room , m_room(room) , m_eventId(event != nullptr ? event->id() : QString()) , m_eventSenderId(event != nullptr ? event->senderId() : QString()) - , m_event(loadEvent(event->fullJson())) , m_isPending(isPending) , m_isReply(isReply) { + intiializeEvent(event); initializeModel(); } @@ -81,8 +82,7 @@ void MessageContentModel::initializeModel() if (m_eventId == serverEvent->id()) { beginResetModel(); m_isPending = false; - m_event = loadEvent(serverEvent->fullJson()); - Q_EMIT eventUpdated(); + intiializeEvent(serverEvent); endResetModel(); } } @@ -91,8 +91,7 @@ void MessageContentModel::initializeModel() if (m_room != nullptr && m_event != nullptr) { if (m_eventId == newEvent->id()) { beginResetModel(); - m_event = loadEvent(newEvent->fullJson()); - Q_EMIT eventUpdated(); + intiializeEvent(newEvent); endResetModel(); } } @@ -154,6 +153,29 @@ void MessageContentModel::initializeModel() resetModel(); } +void MessageContentModel::intiializeEvent(const QString &eventId) +{ + const auto newEvent = m_room->getEvent(eventId); + if (newEvent != nullptr) { + intiializeEvent(newEvent); + } +} + +void MessageContentModel::intiializeEvent(const Quotient::RoomEvent *event) +{ + m_event = loadEvent(event->fullJson()); + auto senderId = event->senderId(); + // A pending event might not have a sender ID set yet but in that case it must + // be the local member. + if (senderId.isEmpty()) { + senderId = m_room->localMember().id(); + } + if (m_eventSenderObject == nullptr) { + m_eventSenderObject = std::unique_ptr(new NeochatRoomMember(m_room, senderId)); + } + Q_EMIT eventUpdated(); +} + bool MessageContentModel::showAuthor() const { return m_showAuthor; @@ -239,7 +261,7 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const return eventHandler.getTimeString(false, QLocale::ShortFormat, m_isPending, lastUpdated); } if (role == AuthorRole) { - return QVariant::fromValue(eventHandler.getAuthor(m_isPending)); + return QVariant::fromValue(m_eventSenderObject.get()); } if (role == MediaInfoRole) { return eventHandler.getMediaInfo(); diff --git a/src/models/messagecontentmodel.h b/src/models/messagecontentmodel.h index 127bffc30..1e086a72b 100644 --- a/src/models/messagecontentmodel.h +++ b/src/models/messagecontentmodel.h @@ -6,11 +6,13 @@ #include #include +#include #include #include "enums/messagecomponenttype.h" #include "eventhandler.h" #include "itinerarymodel.h" +#include "neochatroommember.h" struct MessageComponent { MessageComponentType::Type type = MessageComponentType::Other; @@ -115,6 +117,7 @@ private: QPointer m_room; QString m_eventId; QString m_eventSenderId; + std::unique_ptr m_eventSenderObject = nullptr; Quotient::RoomEventPtr m_event; bool m_isPending; @@ -122,6 +125,8 @@ private: bool m_isReply; void initializeModel(); + void intiializeEvent(const QString &eventId); + void intiializeEvent(const Quotient::RoomEvent *event); QList m_components; void resetModel(); diff --git a/src/models/messageeventmodel.cpp b/src/models/messageeventmodel.cpp index 26eaf1b5a..2ed17187a 100644 --- a/src/models/messageeventmodel.cpp +++ b/src/models/messageeventmodel.cpp @@ -26,6 +26,8 @@ #include "messagecontentmodel.h" #include "models/messagefiltermodel.h" #include "models/reactionmodel.h" +#include "neochatroom.h" +#include "neochatroommember.h" #include "readmarkermodel.h" #include "texthandler.h" @@ -90,6 +92,10 @@ void MessageEventModel::setRoom(NeoChatRoom *room) m_currentRoom->disconnect(this); m_currentRoom = nullptr; endResetModel(); + + // Don't clear the member objects until the model has been fully reset and all + // refs cleared. + m_memberObjects.clear(); } beginResetModel(); @@ -150,8 +156,9 @@ void MessageEventModel::setRoom(NeoChatRoom *room) refreshLastUserEvents(i); } }); - connect(m_currentRoom, &Room::pendingEventAboutToAdd, this, [this] { + connect(m_currentRoom, &Room::pendingEventAboutToAdd, this, [this](Quotient::RoomEvent *event) { m_initialized = true; + createEventObjects(event); beginInsertRows({}, 0, 0); }); connect(m_currentRoom, &Room::pendingEventAdded, this, &MessageEventModel::endInsertRows); @@ -214,22 +221,6 @@ void MessageEventModel::setRoom(NeoChatRoom *room) beginResetModel(); endResetModel(); }); - connect(m_currentRoom, &Room::memberNameUpdated, this, [this](RoomMember member) { - for (auto it = m_currentRoom->messageEvents().rbegin(); it != m_currentRoom->messageEvents().rend(); ++it) { - auto event = it->event(); - if (event->senderId() == member.id()) { - refreshEventRoles(event->id(), {AuthorRole}); - } - } - }); - connect(m_currentRoom, &Room::memberAvatarUpdated, this, [this](RoomMember member) { - for (auto it = m_currentRoom->messageEvents().rbegin(); it != m_currentRoom->messageEvents().rend(); ++it) { - auto event = it->event(); - if (event->senderId() == member.id()) { - refreshEventRoles(event->id(), {AuthorRole}); - } - } - }); qCDebug(MessageEvent) << "Connected to room" << room->id() << "as" << room->localMember().id(); } else { @@ -397,6 +388,8 @@ void MessageEventModel::fetchMore(const QModelIndex &parent) } } +static NeochatRoomMember *emptyNeochatRoomMember = new NeochatRoomMember; + QVariant MessageEventModel::data(const QModelIndex &idx, int role) const { if (!checkIndex(idx, QAbstractItemModel::CheckIndexOption::IndexIsValid)) { @@ -469,7 +462,18 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const } if (role == AuthorRole) { - return QVariant::fromValue(eventHandler.getAuthor(isPending)); + QString mId; + if (isPending) { + mId = m_currentRoom->localMember().id(); + } else { + mId = evt.senderId(); + } + + if (!m_memberObjects.contains(mId)) { + return QVariant::fromValue(emptyNeochatRoomMember); + } + + return QVariant::fromValue(m_memberObjects.at(mId).get()); } if (role == HighlightRole) { @@ -619,6 +623,16 @@ void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event) } auto eventId = event->id(); + auto senderId = event->senderId(); + // A pending event might not have a sender ID set yet but in that case it must + // be the local member. + if (senderId.isEmpty()) { + senderId = m_currentRoom->localMember().id(); + } + + if (!m_memberObjects.contains(senderId)) { + m_memberObjects[senderId] = std::unique_ptr(new NeochatRoomMember(m_currentRoom, senderId)); + } // ReadMarkerModel handles updates to add and remove markers, we only need to // handle adding and removing whole models here. diff --git a/src/models/messageeventmodel.h b/src/models/messageeventmodel.h index cdf7cb685..ef895f4ee 100644 --- a/src/models/messageeventmodel.h +++ b/src/models/messageeventmodel.h @@ -9,6 +9,7 @@ #include "linkpreviewer.h" #include "neochatroom.h" +#include "neochatroommember.h" #include "pollhandler.h" #include "readmarkermodel.h" @@ -115,6 +116,7 @@ private: bool movingEvent = false; KFormat m_format; + std::map> m_memberObjects; QMap> m_readMarkerModels; QMap> m_reactionModels; diff --git a/src/models/messagefiltermodel.cpp b/src/models/messagefiltermodel.cpp index e5f025abc..6286ab922 100644 --- a/src/models/messagefiltermodel.cpp +++ b/src/models/messagefiltermodel.cpp @@ -4,11 +4,13 @@ #include "messagefiltermodel.h" #include +#include #include "enums/delegatetype.h" #include "messagecontentmodel.h" #include "messageeventmodel.h" #include "neochatconfig.h" +#include "neochatroommember.h" #include "timelinemodel.h" using namespace Quotient; diff --git a/src/models/searchmodel.cpp b/src/models/searchmodel.cpp index 4a8dc2031..bbd5243dd 100644 --- a/src/models/searchmodel.cpp +++ b/src/models/searchmodel.cpp @@ -7,6 +7,7 @@ #include "eventhandler.h" #include "models/messagecontentmodel.h" #include "neochatroom.h" +#include "neochatroommember.h" #include @@ -66,7 +67,17 @@ void SearchModel::search() m_job = job; connect(job, &BaseJob::finished, this, [this, job] { beginResetModel(); + m_memberObjects.clear(); m_result = job->searchCategories().roomEvents; + + if (m_result.has_value()) { + for (const auto &result : m_result.value().results) { + if (!m_memberObjects.contains(result.result->senderId())) { + m_memberObjects[result.result->senderId()] = std::unique_ptr(new NeochatRoomMember(m_room, result.result->senderId())); + } + } + } + endResetModel(); setSearching(false); m_job = nullptr; @@ -83,7 +94,7 @@ QVariant SearchModel::data(const QModelIndex &index, int role) const switch (role) { case AuthorRole: - return QVariant::fromValue(eventHandler.getAuthor()); + return QVariant::fromValue(m_memberObjects.at(event.senderId()).get()); case ShowSectionRole: if (row == 0) { return true; diff --git a/src/models/searchmodel.h b/src/models/searchmodel.h index 79477741c..f1d5944a6 100644 --- a/src/models/searchmodel.h +++ b/src/models/searchmodel.h @@ -9,6 +9,8 @@ #include +#include "neochatroommember.h" + namespace Quotient { class Connection; @@ -123,4 +125,6 @@ private: std::optional m_result = std::nullopt; Quotient::SearchJob *m_job = nullptr; bool m_searching = false; + + std::map> m_memberObjects; }; diff --git a/src/neochatroom.cpp b/src/neochatroom.cpp index 569c6358e..378a2222b 100644 --- a/src/neochatroom.cpp +++ b/src/neochatroom.cpp @@ -1767,6 +1767,11 @@ const RoomEvent *NeoChatRoom::getEvent(const QString &eventId) const return timelineIt->get(); } + const auto pendingIt = findPendingEvent(eventId); + if (pendingIt != pendingEvents().end()) { + return pendingIt->event(); + } + auto extraIt = std::find_if(m_extraEvents.begin(), m_extraEvents.end(), [eventId](const Quotient::event_ptr_tt &event) { return event->id() == eventId; }); diff --git a/src/neochatroommember.cpp b/src/neochatroommember.cpp new file mode 100644 index 000000000..7b88aadaf --- /dev/null +++ b/src/neochatroommember.cpp @@ -0,0 +1,166 @@ +// SPDX-FileCopyrightText: 2024 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +#include "neochatroommember.h" + +#include "neochatroom.h" + +NeochatRoomMember::NeochatRoomMember(NeoChatRoom *room, const QString &memberId) + : m_room(room) + , m_memberId(memberId) +{ + Q_ASSERT(!m_memberId.isEmpty()); + + if (m_room != nullptr) { + connect(m_room, &NeoChatRoom::memberNameUpdated, this, [this](Quotient::RoomMember member) { + if (member.id() == m_memberId) { + Q_EMIT displayNameUpdated(); + } + }); + connect(m_room, &NeoChatRoom::memberAvatarUpdated, this, [this](Quotient::RoomMember member) { + if (member.id() == m_memberId) { + Q_EMIT avatarUpdated(); + } + }); + } +} + +QString NeochatRoomMember::id() const +{ + return m_memberId; +} + +Quotient::Uri NeochatRoomMember::uri() const +{ + if (m_room == nullptr || m_memberId.isEmpty()) { + return {}; + } + + return m_room->member(m_memberId).uri(); +} + +bool NeochatRoomMember::isLocalMember() const +{ + if (m_room == nullptr || m_memberId.isEmpty()) { + return false; + } + + return m_room->member(m_memberId).isLocalMember(); +} + +Quotient::Membership NeochatRoomMember::membershipState() const +{ + if (m_room == nullptr || m_memberId.isEmpty()) { + return Quotient::Membership::Leave; + } + + return m_room->member(m_memberId).membershipState(); +} + +QString NeochatRoomMember::name() const +{ + if (m_room == nullptr || m_memberId.isEmpty()) { + return id(); + } + + return m_room->member(m_memberId).name(); +} + +QString NeochatRoomMember::displayName() const +{ + if (m_room == nullptr || m_memberId.isEmpty()) { + return id(); + } + + return m_room->member(m_memberId).displayName(); +} + +QString NeochatRoomMember::htmlSafeDisplayName() const +{ + if (m_room == nullptr || m_memberId.isEmpty()) { + return id(); + } + + return m_room->member(m_memberId).htmlSafeDisplayName(); +} + +QString NeochatRoomMember::fullName() const +{ + if (m_room == nullptr || m_memberId.isEmpty()) { + return id(); + } + + return m_room->member(m_memberId).fullName(); +} + +QString NeochatRoomMember::htmlSafeFullName() const +{ + if (m_room == nullptr || m_memberId.isEmpty()) { + return id(); + } + + return m_room->member(m_memberId).htmlSafeFullName(); +} + +QString NeochatRoomMember::disambiguatedName() const +{ + if (m_room == nullptr || m_memberId.isEmpty()) { + return id(); + } + + return m_room->member(m_memberId).disambiguatedName(); +} + +QString NeochatRoomMember::htmlSafeDisambiguatedName() const +{ + if (m_room == nullptr || m_memberId.isEmpty()) { + return id(); + } + + return m_room->member(m_memberId).htmlSafeDisambiguatedName(); +} + +int NeochatRoomMember::hue() const +{ + if (m_room == nullptr || m_memberId.isEmpty()) { + return 0; + } + + return m_room->member(m_memberId).hue(); +} + +qreal NeochatRoomMember::hueF() const +{ + if (m_room == nullptr || m_memberId.isEmpty()) { + return 0.0; + } + + return m_room->member(m_memberId).hueF(); +} + +QColor NeochatRoomMember::color() const +{ + if (m_room == nullptr || m_memberId.isEmpty()) { + return {}; + } + + return m_room->member(m_memberId).color(); +} + +QString NeochatRoomMember::avatarMediaId() const +{ + if (m_room == nullptr || m_memberId.isEmpty()) { + return {}; + } + + return m_room->member(m_memberId).avatarMediaId(); +} + +QUrl NeochatRoomMember::avatarUrl() const +{ + if (m_room == nullptr || m_memberId.isEmpty()) { + return {}; + } + + return m_room->member(m_memberId).avatarUrl(); +} diff --git a/src/neochatroommember.h b/src/neochatroommember.h new file mode 100644 index 000000000..afb34a83a --- /dev/null +++ b/src/neochatroommember.h @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2024 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +#pragma once + +#include +#include +#include + +#include +#include + +class NeoChatRoom; + +/** + * @class NeochatRoomMember + * + * This class is a shim around RoomMember that can be safety passed to QML. + * + * Because RoomMember has an internal pointer to a RoomMemberEvent it is + * designed to be created used then quickly discarded as the stste event is changed + * every time the member updates. Passing these to QML which will hold onto them + * can lead to accessing an already deleted Quotient::RoomMemberEvent relatively easily. + * + * This class instead holds a member ID and can therefore always safely create and + * access Quotient::RoomMember objects while being used as long as needed by QML. + * + * @note This is only needed to pass to QML if only accessing in CPP RoomMmeber can + * be used safely. + * + * @note The interface is the same as Quotient::RoomMember. + * + * @sa Quotient::RoomMember + */ +class NeochatRoomMember : public QObject +{ + Q_OBJECT + QML_ELEMENT + QML_UNCREATABLE("") + + Q_PROPERTY(QString id READ id CONSTANT) + Q_PROPERTY(Quotient::Uri uri READ uri CONSTANT) + Q_PROPERTY(bool isLocalMember READ isLocalMember CONSTANT) + Q_PROPERTY(QString displayName READ displayName NOTIFY displayNameUpdated) + Q_PROPERTY(QString htmlSafeDisplayName READ htmlSafeDisplayName NOTIFY displayNameUpdated) + Q_PROPERTY(QString fullName READ fullName NOTIFY displayNameUpdated) + Q_PROPERTY(QString htmlSafeFullName READ htmlSafeFullName NOTIFY displayNameUpdated) + Q_PROPERTY(QString disambiguatedName READ disambiguatedName NOTIFY displayNameUpdated) + Q_PROPERTY(QString htmlSafeDisambiguatedName READ htmlSafeDisambiguatedName NOTIFY displayNameUpdated) + Q_PROPERTY(int hue READ hue CONSTANT) + Q_PROPERTY(qreal hueF READ hueF CONSTANT) + Q_PROPERTY(QColor color READ color CONSTANT) + Q_PROPERTY(QUrl avatarUrl READ avatarUrl NOTIFY avatarUpdated) + +public: + NeochatRoomMember() = default; + explicit NeochatRoomMember(NeoChatRoom *room, const QString &memberId); + + QString id() const; + Quotient::Uri uri() const; + bool isLocalMember() const; + Quotient::Membership membershipState() const; + QString name() const; + QString displayName() const; + QString htmlSafeDisplayName() const; + QString fullName() const; + QString htmlSafeFullName() const; + QString disambiguatedName() const; + QString htmlSafeDisambiguatedName() const; + int hue() const; + qreal hueF() const; + QColor color() const; + QString avatarMediaId() const; + QUrl avatarUrl() const; + +Q_SIGNALS: + void displayNameUpdated(); + void avatarUpdated(); + +private: + QPointer m_room; + const QString m_memberId = QString(); +}; diff --git a/src/qml/CodeMaximizeComponent.qml b/src/qml/CodeMaximizeComponent.qml index 84add4422..0b01e2b36 100644 --- a/src/qml/CodeMaximizeComponent.qml +++ b/src/qml/CodeMaximizeComponent.qml @@ -17,7 +17,7 @@ Components.AbstractMaximizeComponent { /** * @brief The message author. */ - property RoomMember author + property NeochatRoomMember author /** * @brief The timestamp of the message. diff --git a/src/roommanager.cpp b/src/roommanager.cpp index 063799f49..8bbd690cd 100644 --- a/src/roommanager.cpp +++ b/src/roommanager.cpp @@ -11,6 +11,7 @@ #include "neochatconfig.h" #include "neochatconnection.h" #include "neochatroom.h" +#include "neochatroommember.h" #include "spacehierarchycache.h" #include "urlhelper.h" @@ -184,7 +185,7 @@ void RoomManager::maximizeMedia(int index) Q_EMIT showMaximizedMedia(index); } -void RoomManager::maximizeCode(const RoomMember &author, const QDateTime &time, const QString &codeText, const QString &language) +void RoomManager::maximizeCode(NeochatRoomMember *author, const QDateTime &time, const QString &codeText, const QString &language) { if (codeText.isEmpty()) { return; @@ -202,14 +203,14 @@ void RoomManager::viewEventSource(const QString &eventId) Q_EMIT showEventSource(eventId); } -void RoomManager::viewEventMenu(const QString &eventId, NeoChatRoom *room, const QString &selectedText) +void RoomManager::viewEventMenu(const QString &eventId, NeoChatRoom *room, NeochatRoomMember *sender, const QString &selectedText) { const auto &event = **room->findInTimeline(eventId); const auto eventHandler = EventHandler(room, &event); if (eventHandler.getMediaInfo().contains("mimeType"_ls)) { Q_EMIT showFileMenu(eventId, - eventHandler.getAuthor(), + sender, eventHandler.messageComponentType(), eventHandler.getPlainBody(), eventHandler.getMediaInfo()["mimeType"_ls].toString(), @@ -217,12 +218,7 @@ void RoomManager::viewEventMenu(const QString &eventId, NeoChatRoom *room, const return; } - Q_EMIT showMessageMenu(eventId, - eventHandler.getAuthor(), - eventHandler.messageComponentType(), - eventHandler.getPlainBody(), - eventHandler.getRichBody(), - selectedText); + Q_EMIT showMessageMenu(eventId, sender, eventHandler.messageComponentType(), eventHandler.getPlainBody(), eventHandler.getRichBody(), selectedText); } bool RoomManager::hasOpenRoom() const diff --git a/src/roommanager.h b/src/roommanager.h index 07de885df..7c642c377 100644 --- a/src/roommanager.h +++ b/src/roommanager.h @@ -23,6 +23,7 @@ #include "models/sortfilterspacelistmodel.h" #include "models/timelinemodel.h" #include "models/userlistmodel.h" +#include "neochatroommember.h" class NeoChatRoom; class NeoChatConnection; @@ -216,7 +217,7 @@ public: */ Q_INVOKABLE void maximizeMedia(int index); - Q_INVOKABLE void maximizeCode(const RoomMember &author, const QDateTime &time, const QString &codeText, const QString &language); + Q_INVOKABLE void maximizeCode(NeochatRoomMember *author, const QDateTime &time, const QString &codeText, const QString &language); /** * @brief Request that any full screen overlay currently open closes. @@ -231,7 +232,7 @@ public: /** * @brief Show a context menu for the given event. */ - Q_INVOKABLE void viewEventMenu(const QString &eventId, NeoChatRoom *room, const QString &selectedText = {}); + Q_INVOKABLE void viewEventMenu(const QString &eventId, NeoChatRoom *room, NeochatRoomMember *sender, const QString &selectedText = {}); ChatDocumentHandler *chatDocumentHandler() const; void setChatDocumentHandler(ChatDocumentHandler *handler); @@ -287,7 +288,7 @@ Q_SIGNALS: /** * @brief Request a block of code is shown maximized. */ - void showMaximizedCode(const RoomMember &author, const QDateTime &time, const QString &codeText, const QString &language); + void showMaximizedCode(NeochatRoomMember *author, const QDateTime &time, const QString &codeText, const QString &language); /** * @brief Request that any full screen overlay closes. @@ -303,7 +304,7 @@ Q_SIGNALS: * @brief Request to show a menu for the given event. */ void showMessageMenu(const QString &eventId, - const Quotient::RoomMember &author, + const NeochatRoomMember *author, MessageComponentType::Type messageComponentType, const QString &plainText, const QString &htmlText, @@ -313,7 +314,7 @@ Q_SIGNALS: * @brief Request to show a menu for the given media event. */ void showFileMenu(const QString &eventId, - const Quotient::RoomMember &author, + const NeochatRoomMember *author, MessageComponentType::Type messageComponentType, const QString &plainText, const QString &mimeType, diff --git a/src/timeline/CodeComponent.qml b/src/timeline/CodeComponent.qml index 1972e6ee9..3a6882480 100644 --- a/src/timeline/CodeComponent.qml +++ b/src/timeline/CodeComponent.qml @@ -20,7 +20,7 @@ QQC2.Control { * * @sa Quotient::RoomMember */ - required property RoomMember author + required property NeochatRoomMember author /** * @brief The timestamp of the message. diff --git a/src/timeline/HiddenDelegate.qml b/src/timeline/HiddenDelegate.qml index 4f4a3a2ec..c0438f478 100644 --- a/src/timeline/HiddenDelegate.qml +++ b/src/timeline/HiddenDelegate.qml @@ -26,7 +26,7 @@ TimelineDelegate { /** * @brief The message author. */ - required property RoomMember author + required property NeochatRoomMember author width: parent?.width rightPadding: NeoChatConfig.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing @@ -88,7 +88,7 @@ TimelineDelegate { QtObject { id: _private function showMessageMenu() { - RoomManager.viewEventMenu(root.eventId, root.room, ""); + RoomManager.viewEventMenu(root.eventId, root.room, root.author, ""); } } } diff --git a/src/timeline/MessageDelegate.qml b/src/timeline/MessageDelegate.qml index f717dfc6c..f2259b665 100644 --- a/src/timeline/MessageDelegate.qml +++ b/src/timeline/MessageDelegate.qml @@ -50,7 +50,7 @@ TimelineDelegate { * * @sa Quotient::RoomMember */ - required property var author + required property NeochatRoomMember author /** * @brief The model to visualise the content of the message. @@ -365,7 +365,7 @@ TimelineDelegate { property bool showUserMessageOnRight: NeoChatConfig.showLocalMessagesOnRight && root.author.isLocalMember && !NeoChatConfig.compactLayout && !root.alwaysFillWidth function showMessageMenu() { - RoomManager.viewEventMenu(root.eventId, root.room, root.selectedText); + RoomManager.viewEventMenu(root.eventId, root.room, root.author, root.selectedText); } } } diff --git a/src/timeline/StateComponent.qml b/src/timeline/StateComponent.qml index 0aca69e86..2d3f8a4eb 100644 --- a/src/timeline/StateComponent.qml +++ b/src/timeline/StateComponent.qml @@ -48,12 +48,13 @@ RowLayout { source: root.author?.avatarUrl ?? "" name: root.author?.displayName ?? "" - color: root.author?.color ?? undefined + color: root.author?.color ?? Kirigami.Theme.highlightColor MouseArea { anchors.fill: parent + enabled: root.author cursorShape: Qt.PointingHandCursor - onClicked: RoomManager.resolveResource("https://matrix.to/#/" + root.author.id) + onClicked: RoomManager.resolveResource("https://matrix.to/#/" + root.author?.id ?? "") } } @@ -61,7 +62,7 @@ RowLayout { id: label Layout.alignment: Qt.AlignVCenter Layout.fillWidth: true - text: `${root.authorDisplayName} ${root.text}` + text: `${root.authorDisplayName} ${root.text}` wrapMode: Text.WordWrap textFormat: Text.RichText onLinkActivated: link => RoomManager.resolveResource(link)