mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Implement views/reactions polling in channels.
This commit is contained in:
parent
e60e65f574
commit
7828a92f08
5 changed files with 168 additions and 71 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue