Correctly handle reactions from channels.

This commit is contained in:
John Preston 2023-04-28 20:16:22 +04:00
parent 29d0c8c2ec
commit 918af601cf
3 changed files with 89 additions and 43 deletions

View File

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h" #include "main/main_session.h"
#include "main/main_account.h" #include "main/main_account.h"
#include "main/main_app_config.h" #include "main/main_app_config.h"
#include "main/session/send_as_peers.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_histories.h" #include "data/data_histories.h"
@ -82,6 +83,30 @@ constexpr auto kTopReactionsLimit = 14;
: config->get<int>("reactions_user_max_default", 1); : config->get<int>("reactions_user_max_default", 1);
} }
bool IsMyRecent(
const MTPDmessagePeerReaction &data,
const ReactionId &id,
not_null<PeerData*> peer,
const base::flat_map<
ReactionId,
std::vector<RecentReaction>> &recent,
bool ignoreChosen) {
if (peer->id == peer->session().userPeerId()) {
return true;
} else if (!ignoreChosen) {
return data.is_my();
}
const auto j = recent.find(id);
if (j == end(recent)) {
return false;
}
const auto k = ranges::find(
j->second,
peer,
&RecentReaction::peer);
return (k != end(j->second)) && k->my;
}
} // namespace } // namespace
PossibleItemReactionsRef LookupPossibleReactions( PossibleItemReactionsRef LookupPossibleReactions(
@ -953,7 +978,6 @@ void MessageReactions::add(const ReactionId &id, bool addToRecent) {
Expects(!id.empty()); Expects(!id.empty());
const auto history = _item->history(); const auto history = _item->history();
const auto self = history->session().user();
const auto myLimit = SentReactionsLimit(_item); const auto myLimit = SentReactionsLimit(_item);
if (ranges::contains(chosen(), id)) { if (ranges::contains(chosen(), id)) {
return; return;
@ -973,7 +997,7 @@ void MessageReactions::add(const ReactionId &id, bool addToRecent) {
_recent.erase(j); _recent.erase(j);
} else { } else {
j->second.erase( j->second.erase(
ranges::remove(j->second, self, &RecentReaction::peer), ranges::remove(j->second, true, &RecentReaction::my),
end(j->second)); end(j->second));
if (j->second.empty()) { if (j->second.empty()) {
_recent.erase(j); _recent.erase(j);
@ -982,9 +1006,14 @@ void MessageReactions::add(const ReactionId &id, bool addToRecent) {
} }
return removed; return removed;
}), end(_list)); }), end(_list));
if (_item->canViewReactions() || history->peer->isUser()) { const auto peer = history->peer;
if (_item->canViewReactions() || peer->isUser()) {
auto &list = _recent[id]; auto &list = _recent[id];
list.insert(begin(list), RecentReaction{ self }); const auto from = peer->session().sendAsPeers().resolveChosen(peer);
list.insert(begin(list), RecentReaction{
.peer = from,
.my = true,
});
} }
const auto i = ranges::find(_list, id, &MessageReaction::id); const auto i = ranges::find(_list, id, &MessageReaction::id);
if (i != end(_list)) { if (i != end(_list)) {
@ -1018,13 +1047,16 @@ void MessageReactions::remove(const ReactionId &id) {
_list.erase(i); _list.erase(i);
} }
if (j != end(_recent)) { if (j != end(_recent)) {
if (removed) {
j->second.clear();
_recent.erase(j);
} else {
j->second.erase( j->second.erase(
ranges::remove(j->second, self, &RecentReaction::peer), ranges::remove(j->second, true, &RecentReaction::my),
end(j->second)); end(j->second));
if (j->second.empty()) { if (j->second.empty()) {
_recent.erase(j); _recent.erase(j);
} else { }
Assert(!removed);
} }
} }
auto &owner = history->owner(); auto &owner = history->owner();
@ -1034,7 +1066,8 @@ void MessageReactions::remove(const ReactionId &id) {
bool MessageReactions::checkIfChanged( bool MessageReactions::checkIfChanged(
const QVector<MTPReactionCount> &list, const QVector<MTPReactionCount> &list,
const QVector<MTPMessagePeerReaction> &recent) const { const QVector<MTPMessagePeerReaction> &recent,
bool min) const {
auto &owner = _item->history()->owner(); auto &owner = _item->history()->owner();
if (owner.reactions().sending(_item)) { if (owner.reactions().sending(_item)) {
// We'll apply non-stale data from the request response. // We'll apply non-stale data from the request response.
@ -1066,13 +1099,18 @@ bool MessageReactions::checkIfChanged(
for (const auto &reaction : recent) { for (const auto &reaction : recent) {
reaction.match([&](const MTPDmessagePeerReaction &data) { reaction.match([&](const MTPDmessagePeerReaction &data) {
const auto id = ReactionFromMTP(data.vreaction()); const auto id = ReactionFromMTP(data.vreaction());
if (ranges::contains(_list, id, &MessageReaction::id)) { if (!ranges::contains(_list, id, &MessageReaction::id)) {
parsed[id].push_back(RecentReaction{ return;
.peer = owner.peer(peerFromMTP(data.vpeer_id())), }
const auto peerId = peerFromMTP(data.vpeer_id());
const auto peer = owner.peer(peerId);
const auto my = IsMyRecent(data, id, peer, _recent, min);
parsed[id].push_back({
.peer = peer,
.unread = data.is_unread(), .unread = data.is_unread(),
.big = data.is_big(), .big = data.is_big(),
.my = my,
}); });
}
}); });
} }
return !ranges::equal(_recent, parsed, []( return !ranges::equal(_recent, parsed, [](
@ -1081,7 +1119,7 @@ bool MessageReactions::checkIfChanged(
return ranges::equal(a.second, b.second, []( return ranges::equal(a.second, b.second, [](
const RecentReaction &a, const RecentReaction &a,
const RecentReaction &b) { const RecentReaction &b) {
return (a.peer == b.peer) && (a.big == b.big); return (a.peer == b.peer) && (a.big == b.big) && (a.my == b.my);
}); });
}); });
} }
@ -1089,7 +1127,7 @@ bool MessageReactions::checkIfChanged(
bool MessageReactions::change( bool MessageReactions::change(
const QVector<MTPReactionCount> &list, const QVector<MTPReactionCount> &list,
const QVector<MTPMessagePeerReaction> &recent, const QVector<MTPMessagePeerReaction> &recent,
bool ignoreChosen) { bool min) {
auto &owner = _item->history()->owner(); auto &owner = _item->history()->owner();
if (owner.reactions().sending(_item)) { if (owner.reactions().sending(_item)) {
// We'll apply non-stale data from the request response. // We'll apply non-stale data from the request response.
@ -1102,7 +1140,7 @@ bool MessageReactions::change(
count.match([&](const MTPDreactionCount &data) { count.match([&](const MTPDreactionCount &data) {
const auto id = ReactionFromMTP(data.vreaction()); const auto id = ReactionFromMTP(data.vreaction());
const auto &chosen = data.vchosen_order(); const auto &chosen = data.vchosen_order();
if (!ignoreChosen && chosen) { if (!min && chosen) {
order[id] = chosen->v; order[id] = chosen->v;
} }
const auto i = ranges::find(_list, id, &MessageReaction::id); const auto i = ranges::find(_list, id, &MessageReaction::id);
@ -1112,10 +1150,10 @@ bool MessageReactions::change(
_list.push_back({ _list.push_back({
.id = id, .id = id,
.count = nowCount, .count = nowCount,
.my = (!ignoreChosen && chosen) .my = (!min && chosen)
}); });
} else { } else {
const auto nowMy = ignoreChosen ? i->my : chosen.has_value(); const auto nowMy = min ? i->my : chosen.has_value();
if (i->count != nowCount || i->my != nowMy) { if (i->count != nowCount || i->my != nowMy) {
i->count = nowCount; i->count = nowCount;
i->my = nowMy; i->my = nowMy;
@ -1125,13 +1163,13 @@ bool MessageReactions::change(
existing.emplace(id); existing.emplace(id);
}); });
} }
if (!ignoreChosen && !order.empty()) { if (!min && !order.empty()) {
const auto min = std::numeric_limits<int>::min(); const auto minimal = std::numeric_limits<int>::min();
const auto proj = [&](const MessageReaction &reaction) { const auto proj = [&](const MessageReaction &reaction) {
return reaction.my ? order[reaction.id] : min; return reaction.my ? order[reaction.id] : minimal;
}; };
const auto correctOrder = [&] { const auto correctOrder = [&] {
auto previousOrder = min; auto previousOrder = minimal;
for (const auto &reaction : _list) { for (const auto &reaction : _list) {
const auto nowOrder = proj(reaction); const auto nowOrder = proj(reaction);
if (nowOrder < previousOrder) { if (nowOrder < previousOrder) {
@ -1156,21 +1194,28 @@ bool MessageReactions::change(
} }
} }
} }
const auto selfId = owner.session().userPeerId();
auto parsed = base::flat_map<ReactionId, std::vector<RecentReaction>>(); auto parsed = base::flat_map<ReactionId, std::vector<RecentReaction>>();
for (const auto &reaction : recent) { for (const auto &reaction : recent) {
reaction.match([&](const MTPDmessagePeerReaction &data) { reaction.match([&](const MTPDmessagePeerReaction &data) {
const auto id = ReactionFromMTP(data.vreaction()); const auto id = ReactionFromMTP(data.vreaction());
const auto i = ranges::find(_list, id, &MessageReaction::id); const auto i = ranges::find(_list, id, &MessageReaction::id);
if (i != end(_list)) { if (i == end(_list)) {
return;
}
auto &list = parsed[id]; auto &list = parsed[id];
if (list.size() < i->count) { if (list.size() >= i->count) {
list.push_back(RecentReaction{ return;
.peer = owner.peer(peerFromMTP(data.vpeer_id())), }
const auto peerId = peerFromMTP(data.vpeer_id());
const auto peer = owner.peer(peerId);
const auto my = IsMyRecent(data, id, peer, _recent, min);
list.push_back({
.peer = peer,
.unread = data.is_unread(), .unread = data.is_unread(),
.big = data.is_big(), .big = data.is_big(),
.my = my,
}); });
}
}
}); });
} }
if (_recent != parsed) { if (_recent != parsed) {

View File

@ -225,14 +225,14 @@ struct RecentReaction {
not_null<PeerData*> peer; not_null<PeerData*> peer;
bool unread = false; bool unread = false;
bool big = false; bool big = false;
bool my = false;
inline friend constexpr bool operator==( friend inline auto operator<=>(
const RecentReaction &a, const RecentReaction &a,
const RecentReaction &b) noexcept { const RecentReaction &b) = default;
return (a.peer.get() == b.peer.get()) friend inline bool operator==(
&& (a.unread == b.unread) const RecentReaction &a,
&& (a.big == b.big); const RecentReaction &b) = default;
}
}; };
class MessageReactions final { class MessageReactions final {
@ -247,7 +247,8 @@ public:
bool ignoreChosen); bool ignoreChosen);
[[nodiscard]] bool checkIfChanged( [[nodiscard]] bool checkIfChanged(
const QVector<MTPReactionCount> &list, const QVector<MTPReactionCount> &list,
const QVector<MTPMessagePeerReaction> &recent) const; const QVector<MTPMessagePeerReaction> &recent,
bool ignoreChosen) const;
[[nodiscard]] const std::vector<MessageReaction> &list() const; [[nodiscard]] const std::vector<MessageReaction> &list() const;
[[nodiscard]] auto recent() const [[nodiscard]] auto recent() const
-> const base::flat_map<ReactionId, std::vector<RecentReaction>> &; -> const base::flat_map<ReactionId, std::vector<RecentReaction>> &;

View File

@ -3199,7 +3199,7 @@ bool HistoryItem::changeReactions(const MTPMessageReactions *reactions) {
const auto &recent = data.vrecent_reactions().value_or_empty(); const auto &recent = data.vrecent_reactions().value_or_empty();
if (min && hasUnreadReaction()) { if (min && hasUnreadReaction()) {
// We can't update reactions from min if we have unread. // We can't update reactions from min if we have unread.
if (_reactions->checkIfChanged(list, recent)) { if (_reactions->checkIfChanged(list, recent, min)) {
updateReactionsUnknown(); updateReactionsUnknown();
} }
return false; return false;