diff --git a/src/messagecontent/models/reactionmodel.cpp b/src/messagecontent/models/reactionmodel.cpp index 2152f8dda..99ee6a1c1 100644 --- a/src/messagecontent/models/reactionmodel.cpp +++ b/src/messagecontent/models/reactionmodel.cpp @@ -25,6 +25,7 @@ ReactionModel::ReactionModel(MessageContentModel *parent, const QString &eventId Q_ASSERT(room != nullptr); connect(m_room, &NeoChatRoom::updatedEvent, this, [this](const QString &eventId) { + m_queuedEvents.clear(); if (m_eventId == eventId) { updateReactions(); } @@ -115,36 +116,44 @@ void ReactionModel::updateReactions() m_shortcodes.clear(); const auto &annotations = m_room->relatedEvents(m_eventId, Quotient::EventRelation::AnnotationType); - if (annotations.isEmpty()) { + auto &pendingEvents = m_queuedEvents; + if (annotations.isEmpty() && pendingEvents.empty()) { endResetModel(); return; - }; + } QMap reactions = {}; + + const auto addReaction = [this, &reactions](const Quotient::ReactionEvent *e) { + reactions[e->key()].append(e->senderId()); + if (e->contentJson()[QStringLiteral("shortcode")].toString().length()) { + m_shortcodes[e->key()] = e->contentJson()[QStringLiteral("shortcode")].toString().toHtmlEscaped(); + } + }; + for (const auto &a : annotations) { if (a->isRedacted()) { // Just in case? continue; } if (const auto &e = eventCast(a)) { - reactions[e->key()].append(e->senderId()); - if (e->contentJson()["shortcode"_L1].toString().length()) { - m_shortcodes[e->key()] = e->contentJson()["shortcode"_L1].toString().toHtmlEscaped(); - } + addReaction(e); } } + for (const auto &e : pendingEvents) { + if (e->isRedacted()) { // Just in case? + continue; + } + addReaction(e); + } + if (reactions.isEmpty()) { endResetModel(); return; } auto i = reactions.constBegin(); while (i != reactions.constEnd()) { - QStringList members; - for (const auto &member : i.value()) { - members.append(member); - } - - m_reactions.append(ReactionModel::Reaction{i.key(), members}); + m_reactions.append(ReactionModel::Reaction{i.key(), i.value()}); ++i; } @@ -161,6 +170,12 @@ QHash ReactionModel::roleNames() const }; } +void ReactionModel::queueReaction(const Quotient::ReactionEvent *event) +{ + m_queuedEvents.push_back(event); + updateReactions(); +} + QString ReactionModel::reactionText(QString text) const { text = text.toHtmlEscaped(); diff --git a/src/messagecontent/models/reactionmodel.h b/src/messagecontent/models/reactionmodel.h index f65a28b87..3eb7cc614 100644 --- a/src/messagecontent/models/reactionmodel.h +++ b/src/messagecontent/models/reactionmodel.h @@ -70,6 +70,15 @@ public: */ [[nodiscard]] QHash roleNames() const override; + /** + * @brief Puts a ReactionEvent into the pending queue. This reaction should be pulled from the pending queue. + * + * This queue is cleared once the message is updated. + * + * @param event The ReactionEvent to add. + */ + void queueReaction(const Quotient::ReactionEvent *event); + Q_SIGNALS: /** * @brief The reactions in the model have been updated. @@ -81,6 +90,7 @@ private: QString m_eventId; QList m_reactions; QMap m_shortcodes; + QList m_queuedEvents; void updateReactions(); QString reactionText(QString text) const; diff --git a/src/timeline/models/messagemodel.cpp b/src/timeline/models/messagemodel.cpp index 8bbf3f002..68d618f47 100644 --- a/src/timeline/models/messagemodel.cpp +++ b/src/timeline/models/messagemodel.cpp @@ -428,7 +428,7 @@ void MessageModel::refreshLastUserEvents(int baseTimelineRow) } } -void MessageModel::createEventObjects(const Quotient::RoomEvent *event) +void MessageModel::createEventObjects(const Quotient::RoomEvent *event, bool pending) { if (event == nullptr) { return; @@ -469,6 +469,28 @@ void MessageModel::createEventObjects(const Quotient::RoomEvent *event) } } } + + if (pending) { + if (const auto reactionEvent = eventCast(event)) { + auto targetEvent = m_currentRoom->getEvent(reactionEvent->eventId()); + if (!targetEvent) { + return; + } + + if (const auto roomEvent = eventCast(targetEvent)) { + if (m_reactionModels.contains(targetEvent->id())) { + m_reactionModels[targetEvent->id()]->queueReaction(reactionEvent); + } else { + auto reactionModel = QSharedPointer(new ReactionModel(roomEvent, m_currentRoom)); + m_reactionModels[targetEvent->id()] = reactionModel; + reactionModel->queueReaction(reactionEvent); + if (!resetting) { + refreshEventRoles(targetEvent->id(), {ReactionRole, ShowReactionsRole}); + } + } + } + } + } } void MessageModel::moveReadMarker(const QString &toEventId) diff --git a/src/timeline/models/messagemodel.h b/src/timeline/models/messagemodel.h index 00fa5c930..b0fe97249 100644 --- a/src/timeline/models/messagemodel.h +++ b/src/timeline/models/messagemodel.h @@ -192,7 +192,7 @@ protected: private: QMap> m_readMarkerModels; - void createEventObjects(const Quotient::RoomEvent *event); + void createEventObjects(const Quotient::RoomEvent *event, bool pending); static std::function m_hiddenFilter; static bool m_threadsEnabled;