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)