Attempt to fix a crash in reactions list view.

This commit is contained in:
John Preston 2021-12-31 00:59:29 +03:00
parent 9eba8ccc73
commit 5fe2e649fb
2 changed files with 73 additions and 52 deletions

View File

@ -31,28 +31,50 @@ namespace {
constexpr auto kContextReactionsLimit = 50; constexpr auto kContextReactionsLimit = 50;
struct Peers {
std::vector<PeerId> 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 { struct PeerWithReaction {
PeerId peer = 0; PeerId peer = 0;
QString reaction; 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); return (a.peer == b.peer) && (a.reaction == b.reaction);
} }
struct PeersWithReactions {
std::vector<PeerWithReaction> 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 { struct CachedRead {
explicit CachedRead(PeerId unknownFlag) CachedRead()
: list(std::vector<PeerId>{ unknownFlag }) { : data(Peers{ .unknown = true }) {
} }
rpl::variable<std::vector<PeerId>> list; rpl::variable<Peers> data;
mtpRequestId requestId = 0; mtpRequestId requestId = 0;
}; };
struct CachedReacted { struct CachedReacted {
explicit CachedReacted(PeerId unknownFlag) CachedReacted()
: list( : data(PeersWithReactions{ .unknown = true }) {
std::vector<PeerWithReaction>{ PeerWithReaction{ unknownFlag } }) {
} }
rpl::variable<std::vector<PeerWithReaction>> list; rpl::variable<PeersWithReactions> data;
mtpRequestId requestId = 0; mtpRequestId requestId = 0;
}; };
@ -66,10 +88,7 @@ struct Context {
if (i != end(cachedRead)) { if (i != end(cachedRead)) {
return i->second; return i->second;
} }
return cachedRead.emplace( return cachedRead.emplace(item, CachedRead()).first->second;
item,
CachedRead(item->history()->session().userPeerId())
).first->second;
} }
[[nodiscard]] CachedReacted &cacheReacted(not_null<HistoryItem*> item) { [[nodiscard]] CachedReacted &cacheReacted(not_null<HistoryItem*> item) {
@ -77,10 +96,7 @@ struct Context {
if (i != end(cachedReacted)) { if (i != end(cachedReacted)) {
return i->second; return i->second;
} }
return cachedReacted.emplace( return cachedReacted.emplace(item, CachedReacted()).first->second;
item,
CachedReacted(item->history()->session().userPeerId())
).first->second;
} }
}; };
@ -193,7 +209,7 @@ struct State {
return Ui::WhoReadType::Seen; return Ui::WhoReadType::Seen;
} }
[[nodiscard]] rpl::producer<std::vector<PeerId>> WhoReadIds( [[nodiscard]] rpl::producer<Peers> WhoReadIds(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
not_null<QWidget*> context) { not_null<QWidget*> context) {
auto weak = QPointer<QWidget>(context.get()); auto weak = QPointer<QWidget>(context.get());
@ -213,32 +229,35 @@ struct State {
).done([=](const MTPVector<MTPlong> &result) { ).done([=](const MTPVector<MTPlong> &result) {
auto &entry = context->cacheRead(item); auto &entry = context->cacheRead(item);
entry.requestId = 0; entry.requestId = 0;
auto peers = std::vector<PeerId>(); auto parsed = Peers();
peers.reserve(std::max(int(result.v.size()), 1)); parsed.list.reserve(result.v.size());
for (const auto &id : result.v) { 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([=] { }).fail([=] {
auto &entry = context->cacheRead(item); auto &entry = context->cacheRead(item);
entry.requestId = 0; entry.requestId = 0;
if (ListUnknown(entry.list.current(), item)) { if (entry.data.current().unknown) {
entry.list = std::vector<PeerId>(); entry.data = Peers();
} }
}).send(); }).send();
} }
return entry.list.value().start_existing(consumer); return entry.data.value().start_existing(consumer);
}; };
} }
[[nodiscard]] std::vector < PeerWithReaction> WithEmptyReactions( [[nodiscard]] PeersWithReactions WithEmptyReactions(
const std::vector<PeerId> &peers) { const Peers &peers) {
return peers | ranges::views::transform([](PeerId peer) { return PeersWithReactions{
return PeerWithReaction{ .peer = peer }; .list = peers.list | ranges::views::transform([](PeerId peer) {
}) | ranges::to_vector; return PeerWithReaction{.peer = peer };
}) | ranges::to_vector,
.unknown = peers.unknown,
};
} }
[[nodiscard]] rpl::producer<std::vector<PeerWithReaction>> WhoReactedIds( [[nodiscard]] rpl::producer<PeersWithReactions> WhoReactedIds(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
not_null<QWidget*> context) { not_null<QWidget*> context) {
auto weak = QPointer<QWidget>(context.get()); auto weak = QPointer<QWidget>(context.get());
@ -267,46 +286,47 @@ struct State {
const MTPDmessages_messageReactionsList &data) { const MTPDmessages_messageReactionsList &data) {
session->data().processUsers(data.vusers()); session->data().processUsers(data.vusers());
auto peers = std::vector<PeerWithReaction>(); auto parsed = PeersWithReactions{
peers.reserve(data.vreactions().v.size()); .fullReactionsCount = data.vcount().v,
};
parsed.list.reserve(data.vreactions().v.size());
for (const auto &vote : data.vreactions().v) { for (const auto &vote : data.vreactions().v) {
vote.match([&](const auto &data) { vote.match([&](const auto &data) {
peers.push_back(PeerWithReaction{ parsed.list.push_back(PeerWithReaction{
.peer = peerFromUser(data.vuser_id()), .peer = peerFromUser(data.vuser_id()),
.reaction = qs(data.vreaction()), .reaction = qs(data.vreaction()),
}); });
}); });
} }
entry.list = std::move(peers); entry.data = std::move(parsed);
}); });
}).fail([=] { }).fail([=] {
auto &entry = context->cacheReacted(item); auto &entry = context->cacheReacted(item);
entry.requestId = 0; entry.requestId = 0;
if (ListUnknown(entry.list.current(), item)) { if (entry.data.current().unknown) {
entry.list = std::vector<PeerWithReaction>(); entry.data = PeersWithReactions();
} }
}).send(); }).send();
} }
return entry.list.value().start_existing(consumer); return entry.data.value().start_existing(consumer);
}; };
} }
[[nodiscard]] auto WhoReadOrReactedIds( [[nodiscard]] auto WhoReadOrReactedIds(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
not_null<QWidget*> context) not_null<QWidget*> context)
-> rpl::producer<std::vector<PeerWithReaction>> { -> rpl::producer<PeersWithReactions> {
return rpl::combine( return rpl::combine(
WhoReactedIds(item, context), WhoReactedIds(item, context),
WhoReadIds(item, context) WhoReadIds(item, context)
) | rpl::map([=]( ) | rpl::map([=](PeersWithReactions reacted, Peers read) {
std::vector<PeerWithReaction> reacted, if (reacted.unknown || read.unknown) {
std::vector<PeerId> read) { return PeersWithReactions{ .unknown = true };
if (ListUnknown(reacted, item) || ListUnknown(read, item)) {
return reacted;
} }
for (const auto &peer : read) { auto &list = reacted.list;
if (!ranges::contains(reacted, peer, &PeerWithReaction::peer)) { for (const auto &peer : read.list) {
reacted.push_back({ .peer = peer }); if (!ranges::contains(list, peer, &PeerWithReaction::peer)) {
list.push_back({ .peer = peer });
} }
} }
return reacted; return reacted;
@ -499,19 +519,20 @@ rpl::producer<Ui::WhoReadContent> WhoReacted(
} }
std::move( std::move(
idsWithReactions idsWithReactions
) | rpl::start_with_next([=]( ) | rpl::start_with_next([=](const PeersWithReactions &peers) {
const std::vector<PeerWithReaction> &peers) { if (peers.unknown) {
if (ListUnknown(peers, item)) {
state->userpics.clear(); state->userpics.clear();
consumer.put_next(Ui::WhoReadContent{ consumer.put_next(Ui::WhoReadContent{
.type = state->current.type, .type = state->current.type,
.unknown = true, .unknown = true,
}); });
return; return;
} else if (UpdateUserpics(state, item, peers)) { }
state->current.fullReactionsCount = peers.fullReactionsCount;
if (UpdateUserpics(state, item, peers.list)) {
RegenerateParticipants(state, small, large); RegenerateParticipants(state, small, large);
pushNext(); pushNext();
} else if (peers.empty()) { } else if (peers.list.empty()) {
pushNext(); pushNext();
} }
}, lifetime); }, lifetime);

View File

@ -455,7 +455,7 @@ void Action::refreshText() {
_st.itemStyle, _st.itemStyle,
{ (_content.unknown { (_content.unknown
? tr::lng_context_seen_loading(tr::now) ? tr::lng_context_seen_loading(tr::now)
: (count == 1) : (usersCount == 1)
? _content.participants.front().name ? _content.participants.front().name
: (_content.type == WhoReadType::Reacted : (_content.type == WhoReadType::Reacted
|| (count > 0 && _content.fullReactionsCount > usersCount)) || (count > 0 && _content.fullReactionsCount > usersCount))