diff --git a/Telegram/SourceFiles/data/data_message_reactions.cpp b/Telegram/SourceFiles/data/data_message_reactions.cpp index a05a32ae0..80c8a7d1a 100644 --- a/Telegram/SourceFiles/data/data_message_reactions.cpp +++ b/Telegram/SourceFiles/data/data_message_reactions.cpp @@ -517,6 +517,60 @@ void MessageReactions::remove() { add(QString()); } +bool MessageReactions::checkIfChanged( + const QVector &list, + const QVector &recent) const { + auto &owner = _item->history()->owner(); + if (owner.reactions().sending(_item)) { + // We'll apply non-stale data from the request response. + return false; + } + auto existing = base::flat_set(); + for (const auto &count : list) { + const auto changed = count.match([&](const MTPDreactionCount &data) { + const auto reaction = qs(data.vreaction()); + const auto nowCount = data.vcount().v; + const auto i = _list.find(reaction); + const auto wasCount = (i != end(_list)) ? i->second : 0; + if (wasCount != nowCount) { + return true; + } + existing.emplace(reaction); + return false; + }); + if (changed) { + return true; + } + } + for (const auto &[reaction, count] : _list) { + if (!existing.contains(reaction)) { + return true; + } + } + auto parsed = base::flat_map>(); + for (const auto &reaction : recent) { + reaction.match([&](const MTPDmessagePeerReaction &data) { + const auto emoji = qs(data.vreaction()); + if (_list.contains(emoji)) { + parsed[emoji].push_back(RecentReaction{ + .peer = owner.peer(peerFromMTP(data.vpeer_id())), + .unread = data.is_unread(), + .big = data.is_big(), + }); + } + }); + } + return !ranges::equal(_recent, parsed, []( + const auto &a, + const auto &b) { + return ranges::equal(a.second, b.second, []( + const RecentReaction &a, + const RecentReaction &b) { + return (a.peer == b.peer) && (a.big == b.big); + }); + }); +} + void MessageReactions::set( const QVector &list, const QVector &recent, @@ -531,10 +585,13 @@ void MessageReactions::set( for (const auto &count : list) { count.match([&](const MTPDreactionCount &data) { const auto reaction = qs(data.vreaction()); - if (data.is_chosen() && !ignoreChosen) { - if (_chosen != reaction) { + if (!ignoreChosen) { + if (data.is_chosen() && _chosen != reaction) { _chosen = reaction; changed = true; + } else if (!data.is_chosen() && _chosen == reaction) { + _chosen = QString(); + changed = true; } } const auto nowCount = data.vcount().v; diff --git a/Telegram/SourceFiles/data/data_message_reactions.h b/Telegram/SourceFiles/data/data_message_reactions.h index 44a6776e7..2ab784c91 100644 --- a/Telegram/SourceFiles/data/data_message_reactions.h +++ b/Telegram/SourceFiles/data/data_message_reactions.h @@ -149,6 +149,9 @@ public: const QVector &list, const QVector &recent, bool ignoreChosen); + [[nodiscard]] bool checkIfChanged( + const QVector &list, + const QVector &recent) const; [[nodiscard]] const base::flat_map &list() const; [[nodiscard]] auto recent() const -> const base::flat_map> &; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index cc19f2a1d..e82d5aff6 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -850,12 +850,8 @@ void HistoryItem::updateReactions(const MTPMessageReactions *reactions) { const auto toContact = toUser && toUser->isContact(); const auto hadUnread = hasUnreadReaction(); setReactions(reactions); - const auto unreadReaction = _reactions - ? _reactions->findUnread() - : QString(); - const auto hasUnread = !unreadReaction.isEmpty(); + const auto hasUnread = hasUnreadReaction(); if (hasUnread && !hadUnread) { - _flags |= MessageFlag::HasUnreadReaction; addToUnreadThings(HistoryUnreadThings::AddType::New); if (toContact) { const auto notification = ItemNotification{ @@ -894,16 +890,29 @@ void HistoryItem::setReactions(const MTPMessageReactions *reactions) { if (data.vresults().v.isEmpty()) { if (_reactions) { _reactions = nullptr; + if (hasUnreadReaction()) { + markReactionsRead(); + } history()->owner().notifyItemDataChange(this); } return; } else if (!_reactions) { _reactions = std::make_unique(this); } - _reactions->set( - data.vresults().v, - data.vrecent_reactions().value_or_empty(), - data.is_min()); + const auto min = data.is_min(); + const auto &list = data.vresults().v; + const auto &recent = data.vrecent_reactions().value_or_empty(); + if (min && hasUnreadReaction()) { + // We can't update reactions from min if we have unread. + if (_reactions->checkIfChanged(list, recent)) { + updateReactionsUnknown(); + } + } else { + _reactions->set(list, recent, min); + if (!_reactions->findUnread().isEmpty()) { + _flags |= MessageFlag::HasUnreadReaction; + } + } }); }