From 5fe2e649fb7c7ddf85beb588e9c9167562645707 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 31 Dec 2021 00:59:29 +0300 Subject: [PATCH] Attempt to fix a crash in reactions list view. --- Telegram/SourceFiles/api/api_who_reacted.cpp | 123 ++++++++++-------- .../controls/who_reacted_context_action.cpp | 2 +- 2 files changed, 73 insertions(+), 52 deletions(-) diff --git a/Telegram/SourceFiles/api/api_who_reacted.cpp b/Telegram/SourceFiles/api/api_who_reacted.cpp index fba2100114..d42bb7d386 100644 --- a/Telegram/SourceFiles/api/api_who_reacted.cpp +++ b/Telegram/SourceFiles/api/api_who_reacted.cpp @@ -31,28 +31,50 @@ namespace { constexpr auto kContextReactionsLimit = 50; +struct Peers { + std::vector list; + bool unknown = false; +}; +inline bool operator==(const Peers &a, const Peers &b) noexcept { + return (a.list == b.list) && (a.unknown == b.unknown); +} + struct PeerWithReaction { PeerId peer = 0; QString reaction; }; -bool operator==(const PeerWithReaction &a, const PeerWithReaction &b) { +inline bool operator==( + const PeerWithReaction &a, + const PeerWithReaction &b) noexcept { return (a.peer == b.peer) && (a.reaction == b.reaction); } +struct PeersWithReactions { + std::vector list; + int fullReactionsCount = 0; + bool unknown = false; +}; +inline bool operator==( + const PeersWithReactions &a, + const PeersWithReactions &b) noexcept { + return (a.fullReactionsCount == b.fullReactionsCount) + && (a.list == b.list) + && (a.unknown == b.unknown); +} + struct CachedRead { - explicit CachedRead(PeerId unknownFlag) - : list(std::vector{ unknownFlag }) { + CachedRead() + : data(Peers{ .unknown = true }) { } - rpl::variable> list; + rpl::variable data; mtpRequestId requestId = 0; }; struct CachedReacted { - explicit CachedReacted(PeerId unknownFlag) - : list( - std::vector{ PeerWithReaction{ unknownFlag } }) { + CachedReacted() + : data(PeersWithReactions{ .unknown = true }) { } - rpl::variable> list; + rpl::variable data; mtpRequestId requestId = 0; }; @@ -66,10 +88,7 @@ struct Context { if (i != end(cachedRead)) { return i->second; } - return cachedRead.emplace( - item, - CachedRead(item->history()->session().userPeerId()) - ).first->second; + return cachedRead.emplace(item, CachedRead()).first->second; } [[nodiscard]] CachedReacted &cacheReacted(not_null item) { @@ -77,10 +96,7 @@ struct Context { if (i != end(cachedReacted)) { return i->second; } - return cachedReacted.emplace( - item, - CachedReacted(item->history()->session().userPeerId()) - ).first->second; + return cachedReacted.emplace(item, CachedReacted()).first->second; } }; @@ -193,7 +209,7 @@ struct State { return Ui::WhoReadType::Seen; } -[[nodiscard]] rpl::producer> WhoReadIds( +[[nodiscard]] rpl::producer WhoReadIds( not_null item, not_null context) { auto weak = QPointer(context.get()); @@ -213,32 +229,35 @@ struct State { ).done([=](const MTPVector &result) { auto &entry = context->cacheRead(item); entry.requestId = 0; - auto peers = std::vector(); - peers.reserve(std::max(int(result.v.size()), 1)); + auto parsed = Peers(); + parsed.list.reserve(result.v.size()); for (const auto &id : result.v) { - peers.push_back(UserId(id)); + parsed.list.push_back(UserId(id)); } - entry.list = std::move(peers); + entry.data = std::move(parsed); }).fail([=] { auto &entry = context->cacheRead(item); entry.requestId = 0; - if (ListUnknown(entry.list.current(), item)) { - entry.list = std::vector(); + if (entry.data.current().unknown) { + entry.data = Peers(); } }).send(); } - return entry.list.value().start_existing(consumer); + return entry.data.value().start_existing(consumer); }; } -[[nodiscard]] std::vector < PeerWithReaction> WithEmptyReactions( - const std::vector &peers) { - return peers | ranges::views::transform([](PeerId peer) { - return PeerWithReaction{ .peer = peer }; - }) | ranges::to_vector; +[[nodiscard]] PeersWithReactions WithEmptyReactions( + const Peers &peers) { + return PeersWithReactions{ + .list = peers.list | ranges::views::transform([](PeerId peer) { + return PeerWithReaction{.peer = peer }; + }) | ranges::to_vector, + .unknown = peers.unknown, + }; } -[[nodiscard]] rpl::producer> WhoReactedIds( +[[nodiscard]] rpl::producer WhoReactedIds( not_null item, not_null context) { auto weak = QPointer(context.get()); @@ -267,46 +286,47 @@ struct State { const MTPDmessages_messageReactionsList &data) { session->data().processUsers(data.vusers()); - auto peers = std::vector(); - peers.reserve(data.vreactions().v.size()); + auto parsed = PeersWithReactions{ + .fullReactionsCount = data.vcount().v, + }; + parsed.list.reserve(data.vreactions().v.size()); for (const auto &vote : data.vreactions().v) { vote.match([&](const auto &data) { - peers.push_back(PeerWithReaction{ + parsed.list.push_back(PeerWithReaction{ .peer = peerFromUser(data.vuser_id()), .reaction = qs(data.vreaction()), }); }); } - entry.list = std::move(peers); + entry.data = std::move(parsed); }); }).fail([=] { auto &entry = context->cacheReacted(item); entry.requestId = 0; - if (ListUnknown(entry.list.current(), item)) { - entry.list = std::vector(); + if (entry.data.current().unknown) { + entry.data = PeersWithReactions(); } }).send(); } - return entry.list.value().start_existing(consumer); + return entry.data.value().start_existing(consumer); }; } [[nodiscard]] auto WhoReadOrReactedIds( not_null item, not_null context) --> rpl::producer> { +-> rpl::producer { return rpl::combine( WhoReactedIds(item, context), WhoReadIds(item, context) - ) | rpl::map([=]( - std::vector reacted, - std::vector read) { - if (ListUnknown(reacted, item) || ListUnknown(read, item)) { - return reacted; + ) | rpl::map([=](PeersWithReactions reacted, Peers read) { + if (reacted.unknown || read.unknown) { + return PeersWithReactions{ .unknown = true }; } - for (const auto &peer : read) { - if (!ranges::contains(reacted, peer, &PeerWithReaction::peer)) { - reacted.push_back({ .peer = peer }); + auto &list = reacted.list; + for (const auto &peer : read.list) { + if (!ranges::contains(list, peer, &PeerWithReaction::peer)) { + list.push_back({ .peer = peer }); } } return reacted; @@ -499,19 +519,20 @@ rpl::producer WhoReacted( } std::move( idsWithReactions - ) | rpl::start_with_next([=]( - const std::vector &peers) { - if (ListUnknown(peers, item)) { + ) | rpl::start_with_next([=](const PeersWithReactions &peers) { + if (peers.unknown) { state->userpics.clear(); consumer.put_next(Ui::WhoReadContent{ .type = state->current.type, .unknown = true, }); return; - } else if (UpdateUserpics(state, item, peers)) { + } + state->current.fullReactionsCount = peers.fullReactionsCount; + if (UpdateUserpics(state, item, peers.list)) { RegenerateParticipants(state, small, large); pushNext(); - } else if (peers.empty()) { + } else if (peers.list.empty()) { pushNext(); } }, lifetime); diff --git a/Telegram/SourceFiles/ui/controls/who_reacted_context_action.cpp b/Telegram/SourceFiles/ui/controls/who_reacted_context_action.cpp index d4204c2046..855de3a00a 100644 --- a/Telegram/SourceFiles/ui/controls/who_reacted_context_action.cpp +++ b/Telegram/SourceFiles/ui/controls/who_reacted_context_action.cpp @@ -455,7 +455,7 @@ void Action::refreshText() { _st.itemStyle, { (_content.unknown ? tr::lng_context_seen_loading(tr::now) - : (count == 1) + : (usersCount == 1) ? _content.participants.front().name : (_content.type == WhoReadType::Reacted || (count > 0 && _content.fullReactionsCount > usersCount))