From dd91cb91d0ac9ce4073c6e24b9ff7ca1961505d6 Mon Sep 17 00:00:00 2001 From: Tobias Fella Date: Fri, 18 Feb 2022 16:11:51 +0100 Subject: [PATCH] Load replied-to message when it isn't in the timeline already --- .../Component/Timeline/TimelineContainer.qml | 6 +++ src/messageeventmodel.cpp | 47 +++++++++++++++---- src/messageeventmodel.h | 5 ++ 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/imports/NeoChat/Component/Timeline/TimelineContainer.qml b/imports/NeoChat/Component/Timeline/TimelineContainer.qml index d9efaefd5..a5fd4f2d2 100644 --- a/imports/NeoChat/Component/Timeline/TimelineContainer.qml +++ b/imports/NeoChat/Component/Timeline/TimelineContainer.qml @@ -27,6 +27,12 @@ QQC2.ItemDelegate { signal openExternally() signal replyClicked(string eventID) + Component.onCompleted: { + if (model.isReply && model.reply === undefined) { + messageEventModel.loadReply(sortedMessageEventModel.mapToSource(sortedMessageEventModel.index(model.index, 0))) + } + } + topPadding: 0 bottomPadding: 0 background: null diff --git a/src/messageeventmodel.cpp b/src/messageeventmodel.cpp index d940631e8..b005f47b9 100644 --- a/src/messageeventmodel.cpp +++ b/src/messageeventmodel.cpp @@ -5,6 +5,7 @@ #include "neochatconfig.h" #include +#include #include #include #include @@ -41,7 +42,9 @@ QHash MessageEventModel::roleNames() const roles[FileMimetypeIcon] = "fileMimetypeIcon"; roles[AnnotationRole] = "annotation"; roles[EventResolvedTypeRole] = "eventResolvedType"; + roles[IsReplyRole] = "isReply"; roles[ReplyRole] = "reply"; + roles[ReplyIdRole] = "replyId"; roles[UserMarkerRole] = "userMarker"; roles[ShowAuthorRole] = "showAuthor"; roles[ShowSectionRole] = "showSection"; @@ -642,19 +645,35 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const return variantList; } + if (role == IsReplyRole) { + return !evt.contentJson()["m.relates_to"].toObject()["m.in_reply_to"].toObject()["event_id"].toString().isEmpty(); + } + + if (role == ReplyIdRole) { + return evt.contentJson()["m.relates_to"].toObject()["m.in_reply_to"].toObject()["event_id"].toString(); + } + if (role == ReplyRole) { const QString &replyEventId = evt.contentJson()["m.relates_to"].toObject()["m.in_reply_to"].toObject()["event_id"].toString(); if (replyEventId.isEmpty()) { return {}; }; const auto replyIt = m_currentRoom->findInTimeline(replyEventId); - if (replyIt == m_currentRoom->historyEdge()) { + const RoomEvent *replyPtr = replyIt != m_currentRoom->historyEdge() ? &**replyIt : nullptr; + if (!replyPtr) { + for (const auto &e : m_extraEvents) { + if (e->id() == replyEventId) { + replyPtr = e.get(); + break; + } + } + } + if (!replyPtr) { return {}; - }; - const auto &replyEvt = **replyIt; + } QString type; - if (auto e = eventCast(&replyEvt)) { + if (auto e = eventCast(replyPtr)) { switch (e->msgtype()) { case MessageEventType::Emote: type = "emote"; @@ -679,29 +698,29 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const type = "message"; } - } else if (is(replyEvt)) { + } else if (is(*replyPtr)) { type = "sticker"; } else { type = "other"; } QVariant content; - if (auto e = eventCast(&replyEvt)) { + if (auto e = eventCast(replyPtr)) { // Cannot use e.contentJson() here because some // EventContent classes inject values into the copy of the // content JSON stored in EventContent::Base content = e->hasFileContent() ? QVariant::fromValue(e->content()->originalJson) : QVariant(); }; - if (auto e = eventCast(&replyEvt)) { + if (auto e = eventCast(replyPtr)) { content = QVariant::fromValue(e->image().originalJson); } return QVariantMap{{"eventId", replyEventId}, - {"display", m_currentRoom->eventToString(replyEvt, Qt::RichText)}, + {"display", m_currentRoom->eventToString(*replyPtr, Qt::RichText)}, {"content", content}, {"type", type}, - {"author", userAtEvent(static_cast(m_currentRoom->user(replyEvt.senderId())), m_currentRoom, evt)}}; + {"author", userAtEvent(static_cast(m_currentRoom->user(replyPtr->senderId())), m_currentRoom, evt)}}; } if (role == ShowAuthorRole) { @@ -851,3 +870,13 @@ QVariant MessageEventModel::getLatestMessageFromIndex(const int baseline) } return replyResponse; } + +void MessageEventModel::loadReply(const QModelIndex &index) +{ + auto job = m_currentRoom->connection()->callApi(m_currentRoom->id(), data(index, ReplyIdRole).toString()); + QPersistentModelIndex persistentIndex(index); + connect(job, &BaseJob::success, this, [this, job, persistentIndex] { + m_extraEvents.push_back(fromJson>(job->jsonData())); + Q_EMIT dataChanged(persistentIndex, persistentIndex, {ReplyRole}); + }); +} diff --git a/src/messageeventmodel.h b/src/messageeventmodel.h index d3169ddb9..efd9ff566 100644 --- a/src/messageeventmodel.h +++ b/src/messageeventmodel.h @@ -33,7 +33,9 @@ public: MimeTypeRole, FileMimetypeIcon, + IsReplyRole, ReplyRole, + ReplyIdRole, ShowAuthorRole, ShowSectionRole, @@ -64,6 +66,7 @@ public: Q_INVOKABLE [[nodiscard]] int eventIDToIndex(const QString &eventID) const; Q_INVOKABLE [[nodiscard]] QVariant getLastLocalUserMessageEventId(); Q_INVOKABLE [[nodiscard]] QVariant getLatestMessageFromIndex(const int baseline); + Q_INVOKABLE void loadReply(const QModelIndex &row); private Q_SLOTS: int refreshEvent(const QString &eventId); @@ -88,6 +91,8 @@ private: int refreshEventRoles(const QString &eventId, const QVector &roles = {}); void moveReadMarker(const QString &toEventId); + std::vector> m_extraEvents; + Q_SIGNALS: void roomChanged(); void fancyEffectsReasonFound(const QString &fancyEffect);