Implement views/reactions polling in channels.

This commit is contained in:
John Preston 2023-09-15 20:41:36 +04:00
parent e60e65f574
commit 7828a92f08
5 changed files with 168 additions and 71 deletions

View File

@ -1318,10 +1318,14 @@ void Stories::sendIncrementViewsRequests() {
}
void Stories::loadViewsSlice(
not_null<PeerData*> peer,
StoryId id,
QString offset,
Fn<void(StoryViews)> done) {
if (_viewsStoryId == id
Expects(peer->isSelf() || !done);
if (_viewsStoryPeer == peer
&& _viewsStoryId == id
&& _viewsOffset == offset
&& (!offset.isEmpty() || _viewsRequestId)) {
if (_viewsRequestId) {
@ -1329,21 +1333,32 @@ void Stories::loadViewsSlice(
}
return;
}
_viewsStoryPeer = peer;
_viewsStoryId = id;
_viewsOffset = offset;
_viewsDone = std::move(done);
const auto api = &_owner->session().api();
const auto perPage = _viewsDone ? kViewsPerPage : kPollingViewsPerPage;
api->request(_viewsRequestId).cancel();
if (peer->isSelf()) {
sendViewsSliceRequest();
} else {
sendViewsCountsRequest();
}
}
void Stories::sendViewsSliceRequest() {
Expects(_viewsStoryPeer != nullptr);
Expects(_viewsStoryPeer->isSelf());
using Flag = MTPstories_GetStoryViewsList::Flag;
const auto api = &_owner->session().api();
_owner->session().api().request(_viewsRequestId).cancel();
_viewsRequestId = api->request(MTPstories_GetStoryViewsList(
MTP_flags(Flag::f_reactions_first),
MTP_inputPeerSelf(),
_viewsStoryPeer->input,
MTPstring(), // q
MTP_int(id),
MTP_string(offset),
MTP_int(perPage)
MTP_int(_viewsStoryId),
MTP_string(_viewsOffset),
MTP_int(_viewsDone ? kViewsPerPage : kPollingViewsPerPage)
)).done([=](const MTPstories_StoryViewsList &result) {
_viewsRequestId = 0;
@ -1382,6 +1397,34 @@ void Stories::loadViewsSlice(
}).send();
}
void Stories::sendViewsCountsRequest() {
Expects(_viewsStoryPeer != nullptr);
Expects(!_viewsDone);
const auto api = &_owner->session().api();
_owner->session().api().request(_viewsRequestId).cancel();
_viewsRequestId = api->request(MTPstories_GetStoriesViews(
_viewsStoryPeer->input,
MTP_vector<MTPint>(1, MTP_int(_viewsStoryId))
)).done([=](const MTPstories_StoryViews &result) {
_viewsRequestId = 0;
const auto &data = result.data();
_owner->processUsers(data.vusers());
if (data.vviews().v.size() == 1) {
const auto fullId = FullStoryId{
_viewsStoryPeer->id,
_viewsStoryId,
};
if (const auto story = lookup(fullId)) {
(*story)->applyViewsCounts(data.vviews().v.front().data());
}
}
}).fail([=] {
_viewsRequestId = 0;
}).send();
}
bool Stories::hasArchive(not_null<PeerData*> peer) const {
if (peer->isSelf()) {
return true;
@ -1745,7 +1788,7 @@ void Stories::registerPolling(not_null<Story*> story, Polling polling) {
case Polling::Chat: ++settings.chat; break;
case Polling::Viewer:
++settings.viewer;
if (story->peer()->isSelf()
if ((story->peer()->isSelf() || story->peer()->isChannel())
&& _pollingViews.emplace(story).second) {
sendPollingViewsRequests();
}
@ -1838,7 +1881,8 @@ void Stories::sendPollingViewsRequests() {
return;
} else if (!_viewsRequestId) {
Assert(_viewsDone == nullptr);
loadViewsSlice(_pollingViews.front()->id(), QString(), nullptr);
const auto story = _pollingViews.front();
loadViewsSlice(story->peer(), story->id(), QString(), nullptr);
}
_pollingViewsTimer.callOnce(kPollViewsInterval);
}

View File

@ -178,6 +178,7 @@ public:
static constexpr auto kViewsPerPage = 50;
void loadViewsSlice(
not_null<PeerData*> peer,
StoryId id,
QString offset,
Fn<void(StoryViews)> done);
@ -315,6 +316,8 @@ private:
TimeId now);
void sendPollingRequests();
void sendPollingViewsRequests();
void sendViewsSliceRequest();
void sendViewsCountsRequest();
const not_null<Session*> _owner;
std::unordered_map<
@ -370,6 +373,7 @@ private:
base::Timer _incrementViewsTimer;
base::flat_set<PeerId> _incrementViewsRequests;
PeerData *_viewsStoryPeer = nullptr;
StoryId _viewsStoryId = 0;
QString _viewsOffset;
Fn<void(StoryViews)> _viewsDone;

View File

@ -516,6 +516,46 @@ void Story::applyChanges(
applyFields(std::move(media), data, now, false);
}
Story::ViewsCounts Story::parseViewsCounts(
const MTPDstoryViews &data,
const Data::ReactionId &mine) {
auto result = ViewsCounts{
.views = data.vviews_count().v,
.reactions = data.vreactions_count().value_or_empty(),
};
if (const auto list = data.vrecent_viewers()) {
result.viewers.reserve(list->v.size());
auto &owner = _peer->owner();
auto &&cut = list->v
| ranges::views::take(kRecentViewersMax);
for (const auto &id : cut) {
result.viewers.push_back(owner.peer(peerFromUser(id)));
}
}
auto total = 0;
if (const auto list = data.vreactions()
; list && _peer->isChannel()) {
result.reactionsCounts.reserve(list->v.size());
for (const auto &reaction : list->v) {
const auto &data = reaction.data();
const auto id = Data::ReactionFromMTP(data.vreaction());
const auto count = data.vcount().v;
result.reactionsCounts[id] = count;
total += count;
}
}
if (!mine.empty()) {
if (auto &count = result.reactionsCounts[mine]; !count) {
count = 1;
++total;
}
}
if (result.reactions < total) {
result.reactions = total;
}
return result;
}
void Story::applyFields(
StoryMedia media,
const MTPDstoryItem &data,
@ -547,51 +587,18 @@ void Story::applyFields(
&owner().session(),
data.ventities().value_or_empty()),
};
auto views = _views.total;
auto reactions = _views.reactions;
auto viewers = std::vector<not_null<PeerData*>>();
auto counts = ViewsCounts();
auto viewsKnown = _views.known;
auto reactionsCounts = base::flat_map<Data::ReactionId, int>();
if (const auto info = data.vviews()) {
const auto &data = info->data();
views = data.vviews_count().v;
reactions = data.vreactions_count().value_or_empty();
counts = parseViewsCounts(info->data(), reaction);
viewsKnown = true;
if (const auto list = data.vrecent_viewers()) {
viewers.reserve(list->v.size());
auto &owner = _peer->owner();
auto &&cut = list->v
| ranges::views::take(kRecentViewersMax);
for (const auto &id : cut) {
viewers.push_back(owner.peer(peerFromUser(id)));
}
}
auto total = 0;
if (const auto list = data.vreactions()
; list && _peer->isChannel()) {
reactionsCounts.reserve(list->v.size());
for (const auto &reaction : list->v) {
const auto &data = reaction.data();
const auto id = Data::ReactionFromMTP(data.vreaction());
const auto count = data.vcount().v;
reactionsCounts[id] = count;
total += count;
}
}
if (!reaction.empty()) {
if (auto &mine = reactionsCounts[reaction]; !mine) {
mine = 1;
++total;
}
}
if (reactions < total) {
reactions = total;
}
} else {
viewers = _recentViewers;
counts.views = _views.total;
counts.reactions = _views.reactions;
counts.viewers = _recentViewers;
for (const auto &suggested : _suggestedReactions) {
if (const auto count = suggested.count) {
reactionsCounts[suggested.reaction] = count;
counts.reactionsCounts[suggested.reaction] = count;
}
}
}
@ -604,8 +611,9 @@ void Story::applyFields(
if (const auto location = ParseLocation(area)) {
locations.push_back(*location);
} else if (auto reaction = ParseSuggestedReaction(area)) {
const auto i = reactionsCounts.find(reaction->reaction);
if (i != end(reactionsCounts)) {
const auto i = counts.reactionsCounts.find(
reaction->reaction);
if (i != end(counts.reactionsCounts)) {
reaction->count = i->second;
}
suggestedReactions.push_back(*reaction);
@ -617,9 +625,6 @@ void Story::applyFields(
const auto editedChanged = (_edited != edited);
const auto mediaChanged = (_media != media);
const auto captionChanged = (_caption != caption);
const auto viewsChanged = (_views.total != views)
|| (_views.reactions != reactions)
|| (_recentViewers != viewers);
const auto locationsChanged = (_locations != locations);
const auto suggestedReactionsChanged
= (_suggestedReactions != suggestedReactions);
@ -633,18 +638,6 @@ void Story::applyFields(
_edited = edited;
_pinned = pinned;
_noForwards = noForwards;
if (_views.reactions != reactions
|| _views.total != views
|| _views.known != viewsKnown) {
_views = StoryViews{
.reactions = reactions,
.total = views,
.known = viewsKnown,
};
}
if (viewsChanged) {
_recentViewers = std::move(viewers);
}
if (mediaChanged) {
_media = std::move(media);
}
@ -660,17 +653,18 @@ void Story::applyFields(
if (reactionChanged) {
_sentReactionId = reaction;
}
updateViewsCounts(std::move(counts), viewsKnown, initial);
const auto changed = editedChanged
|| captionChanged
|| mediaChanged
|| locationsChanged
|| locationsChanged;
const auto reactionsChanged = reactionChanged
|| suggestedReactionsChanged;
if (!initial && (changed || viewsChanged || reactionChanged)) {
if (!initial && (changed || reactionsChanged)) {
_peer->session().changes().storyUpdated(this, UpdateFlag()
| (changed ? UpdateFlag::Edited : UpdateFlag())
| (viewsChanged ? UpdateFlag::ViewsChanged : UpdateFlag())
| (reactionChanged ? UpdateFlag::Reaction : UpdateFlag()));
| (reactionsChanged ? UpdateFlag::Reaction : UpdateFlag()));
}
if (!initial && (captionChanged || mediaChanged)) {
if (const auto item = _peer->owner().stories().lookupItem(this)) {
@ -683,6 +677,44 @@ void Story::applyFields(
}
}
void Story::updateViewsCounts(ViewsCounts &&counts, bool known, bool initial) {
const auto viewsChanged = (_views.total != counts.views)
|| (_views.reactions != counts.reactions)
|| (_recentViewers != counts.viewers);
if (_views.reactions != counts.reactions
|| _views.total != counts.views
|| _views.known != known) {
_views = StoryViews{
.reactions = counts.reactions,
.total = counts.views,
.known = known,
};
}
if (viewsChanged) {
_recentViewers = std::move(counts.viewers);
_peer->session().changes().storyUpdated(
this,
UpdateFlag::ViewsChanged);
}
}
void Story::applyViewsCounts(const MTPDstoryViews &data) {
auto counts = parseViewsCounts(data, _sentReactionId);
auto suggestedCountsChanged = false;
for (auto &suggested : _suggestedReactions) {
const auto i = counts.reactionsCounts.find(suggested.reaction);
const auto v = (i != end(counts.reactionsCounts)) ? i->second : 0;
if (suggested.count != v) {
suggested.count = v;
suggestedCountsChanged = true;
}
}
updateViewsCounts(std::move(counts), true, false);
if (suggestedCountsChanged) {
_peer->session().changes().storyUpdated(this, UpdateFlag::Reaction);
}
}
TimeId Story::lastUpdateTime() const {
return _lastUpdateTime;
}

View File

@ -178,9 +178,17 @@ public:
StoryMedia media,
const MTPDstoryItem &data,
TimeId now);
void applyViewsCounts(const MTPDstoryViews &data);
[[nodiscard]] TimeId lastUpdateTime() const;
private:
struct ViewsCounts {
int views = 0;
int reactions = 0;
base::flat_map<Data::ReactionId, int> reactionsCounts;
std::vector<not_null<PeerData*>> viewers;
};
void changeSuggestedReactionCount(Data::ReactionId id, int delta);
void applyFields(
StoryMedia media,
@ -188,6 +196,11 @@ private:
TimeId now,
bool initial);
void updateViewsCounts(ViewsCounts &&counts, bool known, bool initial);
[[nodiscard]] ViewsCounts parseViewsCounts(
const MTPDstoryViews &data,
const Data::ReactionId &mine);
const StoryId _id = 0;
const not_null<PeerData*> _peer;
Data::ReactionId _sentReactionId;

View File

@ -1367,7 +1367,11 @@ const Data::StoryViews &Controller::views(int limit, bool initial) {
const auto done = viewsGotMoreCallback();
const auto peer = shownPeer();
auto &stories = peer->owner().stories();
stories.loadViewsSlice(_shown.story, _viewsSlice.nextOffset, done);
stories.loadViewsSlice(
peer,
_shown.story,
_viewsSlice.nextOffset,
done);
}
return _viewsSlice;
}