diff --git a/src/app/qml/NeochatMaximizeComponent.qml b/src/app/qml/NeochatMaximizeComponent.qml index ecf5c234d..b465cabaa 100644 --- a/src/app/qml/NeochatMaximizeComponent.qml +++ b/src/app/qml/NeochatMaximizeComponent.qml @@ -154,7 +154,10 @@ Components.AlbumMaximizeComponent { onOpened: forceActiveFocus() - onItemRightClicked: RoomManager.viewEventMenu(root.currentEventId, root.currentRoom) + onItemRightClicked: { + const event = root.currentRoom.findEvent(root.currentEventId); + RoomManager.viewEventMenu(event, root.currentRoom) + } onSaveItem: { var dialog = saveAsDialog.createObject(QQC2.Overlay.overlay) as Dialogs.FileDialog; diff --git a/src/app/roommanager.cpp b/src/app/roommanager.cpp index 0845b6a16..feaf3b90f 100644 --- a/src/app/roommanager.cpp +++ b/src/app/roommanager.cpp @@ -282,26 +282,20 @@ void RoomManager::viewEventSource(const QString &eventId) Q_EMIT showEventSource(eventId); } -void RoomManager::viewEventMenu(const QString &eventId, NeoChatRoom *room, const QString &selectedText, const QString &hoveredLink) +void RoomManager::viewEventMenu(const RoomEvent *event, NeoChatRoom *room, const QString &selectedText, const QString &hoveredLink) { - if (eventId.isEmpty()) { - qWarning() << "Tried to open event menu with empty event id"; + if (!event) { + qWarning() << "Tried to open event menu with empty event"; return; } - const auto it = room->findInTimeline(eventId); - if (it == room->historyEdge()) { - // This is probably a pending event - return; - } - const auto &event = **it; - Q_EMIT showDelegateMenu(eventId, - room->qmlSafeMember(event.senderId()), - MessageComponentType::typeForEvent(event), - EventHandler::plainBody(room, &event), - EventHandler::richBody(room, &event), - EventHandler::mediaInfo(room, &event)["mimeType"_L1].toString(), - room->fileTransferInfo(eventId), + Q_EMIT showDelegateMenu(event->id(), + room->qmlSafeMember(event->senderId()), + MessageComponentType::typeForEvent(*event), + EventHandler::plainBody(room, event), + EventHandler::richBody(room, event), + EventHandler::mediaInfo(room, event)["mimeType"_L1].toString(), + room->fileTransferInfo(event->id()), selectedText, hoveredLink); } diff --git a/src/app/roommanager.h b/src/app/roommanager.h index e8feff45d..e2b8fac3d 100644 --- a/src/app/roommanager.h +++ b/src/app/roommanager.h @@ -233,7 +233,7 @@ public: /** * @brief Show a context menu for the given event. */ - Q_INVOKABLE void viewEventMenu(const QString &eventId, NeoChatRoom *room, const QString &selectedText = {}, const QString &hoveredLink = {}); + Q_INVOKABLE void viewEventMenu(const RoomEvent *event, NeoChatRoom *room, const QString &selectedText = {}, const QString &hoveredLink = {}); /** * @brief Set a URL to be loaded as the initial room. diff --git a/src/libneochat/neochatroom.cpp b/src/libneochat/neochatroom.cpp index 74b82329b..7a0a7a76c 100644 --- a/src/libneochat/neochatroom.cpp +++ b/src/libneochat/neochatroom.cpp @@ -1694,6 +1694,11 @@ std::pair NeoChatRoom::getEvent(const QString return std::make_pair(extraIt != m_extraEvents.end() ? extraIt->get() : nullptr, false); } +const RoomEvent *NeoChatRoom::findEvent(const QString &eventId) const +{ + return getEvent(eventId).first; +} + const RoomEvent *NeoChatRoom::getReplyForEvent(const RoomEvent &event) const { #if Quotient_VERSION_MINOR > 9 diff --git a/src/libneochat/neochatroom.h b/src/libneochat/neochatroom.h index 720306acd..39894713f 100644 --- a/src/libneochat/neochatroom.h +++ b/src/libneochat/neochatroom.h @@ -538,6 +538,13 @@ public: */ std::pair getEvent(const QString &eventId) const; + /** + * @brief Returns the event object with the given ID if available. + * + * This function works identically to getEvent, except this is usable from QML. + */ + Q_INVOKABLE const Quotient::RoomEvent *findEvent(const QString &eventId) const; + /** * @brief Returns the event that is being replied to. This includes events that were manually loaded using NeoChatRoom::loadReply. */ diff --git a/src/messagecontent/CodeComponent.qml b/src/messagecontent/CodeComponent.qml index 73e448240..0c2be60a2 100644 --- a/src/messagecontent/CodeComponent.qml +++ b/src/messagecontent/CodeComponent.qml @@ -130,7 +130,10 @@ QQC2.Control { TapHandler { acceptedDevices: PointerDevice.TouchScreen - onLongPressed: RoomManager.viewEventMenu(root.eventId, root.Message.room, root.Message.selectedText, root.Message.hoveredLink); + onLongPressed: { + const event = root.Message.room.findEvent(root.eventId); + RoomManager.viewEventMenu(event, root.Message.room, root.Message.selectedText, root.Message.hoveredLink); + } } background: null diff --git a/src/messagecontent/QuoteComponent.qml b/src/messagecontent/QuoteComponent.qml index c4c3271a6..26a69542d 100644 --- a/src/messagecontent/QuoteComponent.qml +++ b/src/messagecontent/QuoteComponent.qml @@ -66,7 +66,10 @@ QQC2.Control { enabled: !quoteText.hoveredLink acceptedDevices: PointerDevice.TouchScreen acceptedButtons: Qt.LeftButton - onLongPressed: RoomManager.viewEventMenu(root.eventId, root.Message.room, root.Message.selectedText, root.Message.hoveredLink); + onLongPressed: { + const event = root.Message.room.findEvent(root.eventId); + RoomManager.viewEventMenu(event, root.Message.room, root.Message.selectedText, root.Message.hoveredLink); + } } } diff --git a/src/messagecontent/StateComponent.qml b/src/messagecontent/StateComponent.qml index d35757c56..34d789592 100644 --- a/src/messagecontent/StateComponent.qml +++ b/src/messagecontent/StateComponent.qml @@ -86,11 +86,12 @@ RowLayout { QtObject { id: _private - function showMessageMenu() { + function showMessageMenu(): void { if (!NeoChatConfig.developerTools) { return; } - RoomManager.viewEventMenu(root.modelData.eventId, root.Message.room, root.author, "", ""); + const event = root.Message.room.findEvent(root.modelData.eventId); + RoomManager.viewEventMenu(event, root.Message.room, root.author, "", ""); } } } diff --git a/src/messagecontent/TextComponent.qml b/src/messagecontent/TextComponent.qml index d1a7fdc8f..26bf3532b 100644 --- a/src/messagecontent/TextComponent.qml +++ b/src/messagecontent/TextComponent.qml @@ -97,12 +97,18 @@ TextEdit { enabled: !root.hoveredLink acceptedButtons: Qt.LeftButton acceptedDevices: PointerDevice.TouchScreen - onLongPressed: RoomManager.viewEventMenu(root.eventId, root.Message.room, root.Message.selectedText, root.Message.hoveredLink); + onLongPressed: { + const event = root.Message.room.findEvent(root.eventId); + RoomManager.viewEventMenu(event, root.Message.room, root.Message.selectedText, root.Message.hoveredLink); + } } TapHandler { acceptedButtons: Qt.RightButton acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus gesturePolicy: TapHandler.WithinBounds - onTapped: RoomManager.viewEventMenu(root.eventId, root.Message.room, root.Message.selectedText, root.Message.hoveredLink); + onTapped: { + const event = root.Message.room.findEvent(root.eventId); + RoomManager.viewEventMenu(event, root.Message.room, root.Message.selectedText, root.Message.hoveredLink); + } } } diff --git a/src/timeline/HiddenDelegate.qml b/src/timeline/HiddenDelegate.qml index 1365658a5..501aa2e02 100644 --- a/src/timeline/HiddenDelegate.qml +++ b/src/timeline/HiddenDelegate.qml @@ -92,8 +92,10 @@ TimelineDelegate { QtObject { id: _private - function showMessageMenu() { - RoomManager.viewEventMenu(root.eventId, root.room, ""); + + function showMessageMenu(): void { + let event = root.Message.room.findEvent(root.eventId); + RoomManager.viewEventMenu(event, root.room, ""); } } } diff --git a/src/timeline/MessageDelegate.qml b/src/timeline/MessageDelegate.qml index 4a2c5d680..ebe07bb4c 100644 --- a/src/timeline/MessageDelegate.qml +++ b/src/timeline/MessageDelegate.qml @@ -232,8 +232,9 @@ MessageDelegateBase { QtObject { id: _private - function showMessageMenu() { - RoomManager.viewEventMenu(root.eventId, root.room, root.Message.selectedText, root.Message.hoveredLink); + function showMessageMenu(): void { + let event = root.ListView.view.model.findEvent(root.eventId); + RoomManager.viewEventMenu(event, root.room, root.Message.selectedText, root.Message.hoveredLink); } } } diff --git a/src/timeline/models/messagefiltermodel.cpp b/src/timeline/models/messagefiltermodel.cpp index a19d8fdcb..6ae5a5b6b 100644 --- a/src/timeline/models/messagefiltermodel.cpp +++ b/src/timeline/models/messagefiltermodel.cpp @@ -152,6 +152,23 @@ QModelIndex MessageFilterModel::indexForEventId(const QString &eventId) const return mapFromSource(eventIndex); } +const Quotient::RoomEvent *MessageFilterModel::findEvent(const QString &eventId) const +{ + // Check if sourceModel is a message model. + auto messageModel = dynamic_cast(sourceModel()); + // See if it's a timeline model. + if (!messageModel) { + if (const auto timelineModel = dynamic_cast(sourceModel())) { + messageModel = timelineModel->timelineMessageModel(); + if (!messageModel) { + return nullptr; + } + } + } + + return messageModel->findEvent(eventId); +} + bool MessageFilterModel::showAuthor(QModelIndex index) const { for (auto r = index.row() + 1; r < rowCount(); ++r) { diff --git a/src/timeline/models/messagefiltermodel.h b/src/timeline/models/messagefiltermodel.h index b049bec6f..cb8314398 100644 --- a/src/timeline/models/messagefiltermodel.h +++ b/src/timeline/models/messagefiltermodel.h @@ -67,9 +67,13 @@ public: /** * @brief Get the QModelIndex the given event ID in the model. */ - Q_INVOKABLE QModelIndex indexforEventId(const QString &eventId) const; Q_INVOKABLE QModelIndex indexForEventId(const QString &eventId) const; + /** + * @brief Finds the event of the given event ID in the model, returning nullptr if no matches were found. + */ + Q_INVOKABLE const Quotient::RoomEvent *findEvent(const QString &eventId) const; + static void setShowAllEvents(bool enabled); static void setShowDeletedMessages(bool enabled); diff --git a/src/timeline/models/messagemodel.cpp b/src/timeline/models/messagemodel.cpp index d6149a7ac..29067b2b8 100644 --- a/src/timeline/models/messagemodel.cpp +++ b/src/timeline/models/messagemodel.cpp @@ -351,16 +351,21 @@ QHash MessageModel::roleNames() const QModelIndex MessageModel::indexForEventId(const QString &eventId) const { - if (m_room == nullptr) { - return {}; - } - - const auto it = m_room->findInTimeline(eventId); - if (it == m_room->historyEdge()) { + const auto matches = match(index(0, 0), EventIdRole, eventId); + if (matches.isEmpty()) { qWarning() << "Trying to find non-existent event:" << eventId; return {}; } - return index(it - m_room->messageEvents().rbegin() + timelineServerIndex()); + return matches.constFirst(); +} + +const RoomEvent *MessageModel::findEvent(const QString &eventId) const +{ + const auto index = indexForEventId(eventId); + if (const auto event = getEventForIndex(index)) { + return &event.value().get(); + } + return nullptr; } void MessageModel::fullEventRefresh(int row) diff --git a/src/timeline/models/messagemodel.h b/src/timeline/models/messagemodel.h index f2cf92a71..687845fd6 100644 --- a/src/timeline/models/messagemodel.h +++ b/src/timeline/models/messagemodel.h @@ -115,9 +115,13 @@ public: /** * @brief Get the QModelIndex of the given event ID in the model, returning an invalid QModelIndex if no matches were found. */ - Q_INVOKABLE QModelIndex indexforEventId(const QString &eventId) const; Q_INVOKABLE QModelIndex indexForEventId(const QString &eventId) const; + /** + * @brief Finds the event of the given event ID in the model, returning nullptr if no matches were found. + */ + Q_INVOKABLE const Quotient::RoomEvent *findEvent(const QString &eventId) const; + static void setHiddenFilter(std::function hiddenFilter); static void setThreadsEnabled(bool enableThreads);