mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +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(
|
void Stories::loadViewsSlice(
|
||||||
|
not_null<PeerData*> peer,
|
||||||
StoryId id,
|
StoryId id,
|
||||||
QString offset,
|
QString offset,
|
||||||
Fn<void(StoryViews)> done) {
|
Fn<void(StoryViews)> done) {
|
||||||
if (_viewsStoryId == id
|
Expects(peer->isSelf() || !done);
|
||||||
|
|
||||||
|
if (_viewsStoryPeer == peer
|
||||||
|
&& _viewsStoryId == id
|
||||||
&& _viewsOffset == offset
|
&& _viewsOffset == offset
|
||||||
&& (!offset.isEmpty() || _viewsRequestId)) {
|
&& (!offset.isEmpty() || _viewsRequestId)) {
|
||||||
if (_viewsRequestId) {
|
if (_viewsRequestId) {
|
||||||
|
@ -1329,21 +1333,32 @@ void Stories::loadViewsSlice(
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
_viewsStoryPeer = peer;
|
||||||
_viewsStoryId = id;
|
_viewsStoryId = id;
|
||||||
_viewsOffset = offset;
|
_viewsOffset = offset;
|
||||||
_viewsDone = std::move(done);
|
_viewsDone = std::move(done);
|
||||||
|
|
||||||
const auto api = &_owner->session().api();
|
if (peer->isSelf()) {
|
||||||
const auto perPage = _viewsDone ? kViewsPerPage : kPollingViewsPerPage;
|
sendViewsSliceRequest();
|
||||||
api->request(_viewsRequestId).cancel();
|
} else {
|
||||||
|
sendViewsCountsRequest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stories::sendViewsSliceRequest() {
|
||||||
|
Expects(_viewsStoryPeer != nullptr);
|
||||||
|
Expects(_viewsStoryPeer->isSelf());
|
||||||
|
|
||||||
using Flag = MTPstories_GetStoryViewsList::Flag;
|
using Flag = MTPstories_GetStoryViewsList::Flag;
|
||||||
|
const auto api = &_owner->session().api();
|
||||||
|
_owner->session().api().request(_viewsRequestId).cancel();
|
||||||
_viewsRequestId = api->request(MTPstories_GetStoryViewsList(
|
_viewsRequestId = api->request(MTPstories_GetStoryViewsList(
|
||||||
MTP_flags(Flag::f_reactions_first),
|
MTP_flags(Flag::f_reactions_first),
|
||||||
MTP_inputPeerSelf(),
|
_viewsStoryPeer->input,
|
||||||
MTPstring(), // q
|
MTPstring(), // q
|
||||||
MTP_int(id),
|
MTP_int(_viewsStoryId),
|
||||||
MTP_string(offset),
|
MTP_string(_viewsOffset),
|
||||||
MTP_int(perPage)
|
MTP_int(_viewsDone ? kViewsPerPage : kPollingViewsPerPage)
|
||||||
)).done([=](const MTPstories_StoryViewsList &result) {
|
)).done([=](const MTPstories_StoryViewsList &result) {
|
||||||
_viewsRequestId = 0;
|
_viewsRequestId = 0;
|
||||||
|
|
||||||
|
@ -1382,6 +1397,34 @@ void Stories::loadViewsSlice(
|
||||||
}).send();
|
}).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 {
|
bool Stories::hasArchive(not_null<PeerData*> peer) const {
|
||||||
if (peer->isSelf()) {
|
if (peer->isSelf()) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -1745,7 +1788,7 @@ void Stories::registerPolling(not_null<Story*> story, Polling polling) {
|
||||||
case Polling::Chat: ++settings.chat; break;
|
case Polling::Chat: ++settings.chat; break;
|
||||||
case Polling::Viewer:
|
case Polling::Viewer:
|
||||||
++settings.viewer;
|
++settings.viewer;
|
||||||
if (story->peer()->isSelf()
|
if ((story->peer()->isSelf() || story->peer()->isChannel())
|
||||||
&& _pollingViews.emplace(story).second) {
|
&& _pollingViews.emplace(story).second) {
|
||||||
sendPollingViewsRequests();
|
sendPollingViewsRequests();
|
||||||
}
|
}
|
||||||
|
@ -1838,7 +1881,8 @@ void Stories::sendPollingViewsRequests() {
|
||||||
return;
|
return;
|
||||||
} else if (!_viewsRequestId) {
|
} else if (!_viewsRequestId) {
|
||||||
Assert(_viewsDone == nullptr);
|
Assert(_viewsDone == nullptr);
|
||||||
loadViewsSlice(_pollingViews.front()->id(), QString(), nullptr);
|
const auto story = _pollingViews.front();
|
||||||
|
loadViewsSlice(story->peer(), story->id(), QString(), nullptr);
|
||||||
}
|
}
|
||||||
_pollingViewsTimer.callOnce(kPollViewsInterval);
|
_pollingViewsTimer.callOnce(kPollViewsInterval);
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,6 +178,7 @@ public:
|
||||||
|
|
||||||
static constexpr auto kViewsPerPage = 50;
|
static constexpr auto kViewsPerPage = 50;
|
||||||
void loadViewsSlice(
|
void loadViewsSlice(
|
||||||
|
not_null<PeerData*> peer,
|
||||||
StoryId id,
|
StoryId id,
|
||||||
QString offset,
|
QString offset,
|
||||||
Fn<void(StoryViews)> done);
|
Fn<void(StoryViews)> done);
|
||||||
|
@ -315,6 +316,8 @@ private:
|
||||||
TimeId now);
|
TimeId now);
|
||||||
void sendPollingRequests();
|
void sendPollingRequests();
|
||||||
void sendPollingViewsRequests();
|
void sendPollingViewsRequests();
|
||||||
|
void sendViewsSliceRequest();
|
||||||
|
void sendViewsCountsRequest();
|
||||||
|
|
||||||
const not_null<Session*> _owner;
|
const not_null<Session*> _owner;
|
||||||
std::unordered_map<
|
std::unordered_map<
|
||||||
|
@ -370,6 +373,7 @@ private:
|
||||||
base::Timer _incrementViewsTimer;
|
base::Timer _incrementViewsTimer;
|
||||||
base::flat_set<PeerId> _incrementViewsRequests;
|
base::flat_set<PeerId> _incrementViewsRequests;
|
||||||
|
|
||||||
|
PeerData *_viewsStoryPeer = nullptr;
|
||||||
StoryId _viewsStoryId = 0;
|
StoryId _viewsStoryId = 0;
|
||||||
QString _viewsOffset;
|
QString _viewsOffset;
|
||||||
Fn<void(StoryViews)> _viewsDone;
|
Fn<void(StoryViews)> _viewsDone;
|
||||||
|
|
|
@ -516,6 +516,46 @@ void Story::applyChanges(
|
||||||
applyFields(std::move(media), data, now, false);
|
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(
|
void Story::applyFields(
|
||||||
StoryMedia media,
|
StoryMedia media,
|
||||||
const MTPDstoryItem &data,
|
const MTPDstoryItem &data,
|
||||||
|
@ -547,51 +587,18 @@ void Story::applyFields(
|
||||||
&owner().session(),
|
&owner().session(),
|
||||||
data.ventities().value_or_empty()),
|
data.ventities().value_or_empty()),
|
||||||
};
|
};
|
||||||
auto views = _views.total;
|
auto counts = ViewsCounts();
|
||||||
auto reactions = _views.reactions;
|
|
||||||
auto viewers = std::vector<not_null<PeerData*>>();
|
|
||||||
auto viewsKnown = _views.known;
|
auto viewsKnown = _views.known;
|
||||||
auto reactionsCounts = base::flat_map<Data::ReactionId, int>();
|
|
||||||
if (const auto info = data.vviews()) {
|
if (const auto info = data.vviews()) {
|
||||||
const auto &data = info->data();
|
counts = parseViewsCounts(info->data(), reaction);
|
||||||
views = data.vviews_count().v;
|
|
||||||
reactions = data.vreactions_count().value_or_empty();
|
|
||||||
viewsKnown = true;
|
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 {
|
} else {
|
||||||
viewers = _recentViewers;
|
counts.views = _views.total;
|
||||||
|
counts.reactions = _views.reactions;
|
||||||
|
counts.viewers = _recentViewers;
|
||||||
for (const auto &suggested : _suggestedReactions) {
|
for (const auto &suggested : _suggestedReactions) {
|
||||||
if (const auto count = suggested.count) {
|
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)) {
|
if (const auto location = ParseLocation(area)) {
|
||||||
locations.push_back(*location);
|
locations.push_back(*location);
|
||||||
} else if (auto reaction = ParseSuggestedReaction(area)) {
|
} else if (auto reaction = ParseSuggestedReaction(area)) {
|
||||||
const auto i = reactionsCounts.find(reaction->reaction);
|
const auto i = counts.reactionsCounts.find(
|
||||||
if (i != end(reactionsCounts)) {
|
reaction->reaction);
|
||||||
|
if (i != end(counts.reactionsCounts)) {
|
||||||
reaction->count = i->second;
|
reaction->count = i->second;
|
||||||
}
|
}
|
||||||
suggestedReactions.push_back(*reaction);
|
suggestedReactions.push_back(*reaction);
|
||||||
|
@ -617,9 +625,6 @@ void Story::applyFields(
|
||||||
const auto editedChanged = (_edited != edited);
|
const auto editedChanged = (_edited != edited);
|
||||||
const auto mediaChanged = (_media != media);
|
const auto mediaChanged = (_media != media);
|
||||||
const auto captionChanged = (_caption != caption);
|
const auto captionChanged = (_caption != caption);
|
||||||
const auto viewsChanged = (_views.total != views)
|
|
||||||
|| (_views.reactions != reactions)
|
|
||||||
|| (_recentViewers != viewers);
|
|
||||||
const auto locationsChanged = (_locations != locations);
|
const auto locationsChanged = (_locations != locations);
|
||||||
const auto suggestedReactionsChanged
|
const auto suggestedReactionsChanged
|
||||||
= (_suggestedReactions != suggestedReactions);
|
= (_suggestedReactions != suggestedReactions);
|
||||||
|
@ -633,18 +638,6 @@ void Story::applyFields(
|
||||||
_edited = edited;
|
_edited = edited;
|
||||||
_pinned = pinned;
|
_pinned = pinned;
|
||||||
_noForwards = noForwards;
|
_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) {
|
if (mediaChanged) {
|
||||||
_media = std::move(media);
|
_media = std::move(media);
|
||||||
}
|
}
|
||||||
|
@ -660,17 +653,18 @@ void Story::applyFields(
|
||||||
if (reactionChanged) {
|
if (reactionChanged) {
|
||||||
_sentReactionId = reaction;
|
_sentReactionId = reaction;
|
||||||
}
|
}
|
||||||
|
updateViewsCounts(std::move(counts), viewsKnown, initial);
|
||||||
|
|
||||||
const auto changed = editedChanged
|
const auto changed = editedChanged
|
||||||
|| captionChanged
|
|| captionChanged
|
||||||
|| mediaChanged
|
|| mediaChanged
|
||||||
|| locationsChanged
|
|| locationsChanged;
|
||||||
|
const auto reactionsChanged = reactionChanged
|
||||||
|| suggestedReactionsChanged;
|
|| suggestedReactionsChanged;
|
||||||
if (!initial && (changed || viewsChanged || reactionChanged)) {
|
if (!initial && (changed || reactionsChanged)) {
|
||||||
_peer->session().changes().storyUpdated(this, UpdateFlag()
|
_peer->session().changes().storyUpdated(this, UpdateFlag()
|
||||||
| (changed ? UpdateFlag::Edited : UpdateFlag())
|
| (changed ? UpdateFlag::Edited : UpdateFlag())
|
||||||
| (viewsChanged ? UpdateFlag::ViewsChanged : UpdateFlag())
|
| (reactionsChanged ? UpdateFlag::Reaction : UpdateFlag()));
|
||||||
| (reactionChanged ? UpdateFlag::Reaction : UpdateFlag()));
|
|
||||||
}
|
}
|
||||||
if (!initial && (captionChanged || mediaChanged)) {
|
if (!initial && (captionChanged || mediaChanged)) {
|
||||||
if (const auto item = _peer->owner().stories().lookupItem(this)) {
|
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 {
|
TimeId Story::lastUpdateTime() const {
|
||||||
return _lastUpdateTime;
|
return _lastUpdateTime;
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,9 +178,17 @@ public:
|
||||||
StoryMedia media,
|
StoryMedia media,
|
||||||
const MTPDstoryItem &data,
|
const MTPDstoryItem &data,
|
||||||
TimeId now);
|
TimeId now);
|
||||||
|
void applyViewsCounts(const MTPDstoryViews &data);
|
||||||
[[nodiscard]] TimeId lastUpdateTime() const;
|
[[nodiscard]] TimeId lastUpdateTime() const;
|
||||||
|
|
||||||
private:
|
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 changeSuggestedReactionCount(Data::ReactionId id, int delta);
|
||||||
void applyFields(
|
void applyFields(
|
||||||
StoryMedia media,
|
StoryMedia media,
|
||||||
|
@ -188,6 +196,11 @@ private:
|
||||||
TimeId now,
|
TimeId now,
|
||||||
bool initial);
|
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 StoryId _id = 0;
|
||||||
const not_null<PeerData*> _peer;
|
const not_null<PeerData*> _peer;
|
||||||
Data::ReactionId _sentReactionId;
|
Data::ReactionId _sentReactionId;
|
||||||
|
|
|
@ -1367,7 +1367,11 @@ const Data::StoryViews &Controller::views(int limit, bool initial) {
|
||||||
const auto done = viewsGotMoreCallback();
|
const auto done = viewsGotMoreCallback();
|
||||||
const auto peer = shownPeer();
|
const auto peer = shownPeer();
|
||||||
auto &stories = peer->owner().stories();
|
auto &stories = peer->owner().stories();
|
||||||
stories.loadViewsSlice(_shown.story, _viewsSlice.nextOffset, done);
|
stories.loadViewsSlice(
|
||||||
|
peer,
|
||||||
|
_shown.story,
|
||||||
|
_viewsSlice.nextOffset,
|
||||||
|
done);
|
||||||
}
|
}
|
||||||
return _viewsSlice;
|
return _viewsSlice;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue