Update API scheme. Show interactions in channel stories.

This commit is contained in:
John Preston 2023-12-15 00:01:44 +00:00
parent 28d68acfe6
commit 1fb4a2f4ba
8 changed files with 198 additions and 15 deletions

View file

@ -1353,6 +1353,96 @@ void Stories::loadViewsSlice(
}
}
void Stories::loadReactionsSlice(
not_null<PeerData*> peer,
StoryId id,
QString offset,
Fn<void(StoryViews)> done) {
Expects(peer->isChannel());
if (_reactionsStoryPeer == peer
&& _reactionsStoryId == id
&& _reactionsOffset == offset) {
if (_reactionsRequestId) {
_reactionsDone = std::move(done);
}
return;
}
_reactionsStoryPeer = peer;
_reactionsStoryId = id;
_reactionsOffset = offset;
_reactionsDone = std::move(done);
using Flag = MTPstories_GetStoryReactionsList::Flag;
const auto api = &_owner->session().api();
_owner->session().api().request(_reactionsRequestId).cancel();
_reactionsRequestId = api->request(MTPstories_GetStoryReactionsList(
MTP_flags(offset.isEmpty() ? Flag() : Flag::f_offset),
_reactionsStoryPeer->input,
MTP_int(_reactionsStoryId),
MTPReaction(),
MTP_string(_reactionsOffset),
MTP_int(kViewsPerPage)
)).done([=](const MTPstories_StoryReactionsList &result) {
_reactionsRequestId = 0;
const auto &data = result.data();
auto slice = StoryViews{
.nextOffset = data.vnext_offset().value_or_empty(),
.reactions = data.vcount().v,
.total = data.vcount().v,
};
_owner->processUsers(data.vusers());
_owner->processChats(data.vchats());
slice.list.reserve(data.vreactions().v.size());
for (const auto &reaction : data.vreactions().v) {
reaction.match([&](const MTPDstoryReaction &data) {
slice.list.push_back({
.peer = _owner->peer(peerFromMTP(data.vpeer_id())),
.reaction = ReactionFromMTP(data.vreaction()),
.date = data.vdate().v,
});
}, [&](const MTPDstoryReactionPublicRepost &data) {
const auto story = applySingle(
peerFromMTP(data.vpeer_id()),
data.vstory());
if (story) {
slice.list.push_back({
.peer = story->peer(),
.repostId = story->id(),
});
}
}, [&](const MTPDstoryReactionPublicForward &data) {
const auto item = _owner->addNewMessage(
data.vmessage(),
{},
NewMessageType::Existing);
if (item) {
slice.list.push_back({
.peer = item->history()->peer,
.forwardId = item->id,
});
}
});
}
const auto fullId = FullStoryId{
.peer = _reactionsStoryPeer->id,
.story = _reactionsStoryId,
};
if (const auto story = lookup(fullId)) {
(*story)->applyChannelReactionsSlice(_reactionsOffset, slice);
}
if (const auto done = base::take(_reactionsDone)) {
done(std::move(slice));
}
}).fail([=] {
_reactionsRequestId = 0;
if (const auto done = base::take(_reactionsDone)) {
done({});
}
}).send();
}
void Stories::sendViewsSliceRequest() {
Expects(_viewsStoryPeer != nullptr);
Expects(_viewsStoryPeer->isSelf());
@ -1379,6 +1469,7 @@ void Stories::sendViewsSliceRequest() {
.total = data.vcount().v,
};
_owner->processUsers(data.vusers());
_owner->processChats(data.vchats());
slice.list.reserve(data.vviews().v.size());
for (const auto &view : data.vviews().v) {
view.match([&](const MTPDstoryView &data) {

View file

@ -182,6 +182,11 @@ public:
StoryId id,
QString offset,
Fn<void(StoryViews)> done);
void loadReactionsSlice(
not_null<PeerData*> peer,
StoryId id,
QString offset,
Fn<void(StoryViews)> done);
[[nodiscard]] bool hasArchive(not_null<PeerData*> peer) const;
@ -379,6 +384,12 @@ private:
Fn<void(StoryViews)> _viewsDone;
mtpRequestId _viewsRequestId = 0;
PeerData *_reactionsStoryPeer = nullptr;
StoryId _reactionsStoryId = 0;
QString _reactionsOffset;
Fn<void(StoryViews)> _reactionsDone;
mtpRequestId _reactionsRequestId = 0;
base::flat_set<FullStoryId> _preloaded;
std::vector<FullStoryId> _toPreloadSources[kStorySourcesListCount];
std::vector<FullStoryId> _toPreloadViewer;

View file

@ -513,6 +513,10 @@ const StoryViews &Story::viewsList() const {
return _views;
}
const StoryViews &Story::channelReactionsList() const {
return _channelReactions;
}
int Story::interactions() const {
return _views.total;
}
@ -543,6 +547,9 @@ void Story::applyViewsSlice(
_views.known = true;
if (offset.isEmpty()) {
_views = slice;
if (!_channelReactions.total) {
_channelReactions.total = _views.reactions + _views.forwards;
}
} else if (_views.nextOffset == offset) {
_views.list.insert(
end(_views.list),
@ -591,6 +598,33 @@ void Story::applyViewsSlice(
}
}
void Story::applyChannelReactionsSlice(
const QString &offset,
const StoryViews &slice) {
const auto changed = (_channelReactions.reactions != slice.reactions)
|| (_channelReactions.total != slice.total);
_channelReactions.reactions = slice.reactions;
_channelReactions.total = slice.total;
_channelReactions.known = true;
if (offset.isEmpty()) {
_channelReactions = slice;
} else if (_channelReactions.nextOffset == offset) {
_channelReactions.list.insert(
end(_channelReactions.list),
begin(slice.list),
end(slice.list));
_channelReactions.nextOffset = slice.nextOffset;
if (_channelReactions.nextOffset.isEmpty()) {
_channelReactions.total = int(_channelReactions.list.size());
}
}
if (changed) {
_peer->session().changes().storyUpdated(
this,
UpdateFlag::ViewsChanged);
}
}
const std::vector<StoryLocation> &Story::locations() const {
return _locations;
}
@ -793,6 +827,9 @@ void Story::updateViewsCounts(ViewsCounts &&counts, bool known, bool initial) {
.total = total,
.known = known,
};
if (!_channelReactions.total) {
_channelReactions.total = _views.reactions + _views.forwards;
}
}
if (viewsChanged) {
_recentViewers = std::move(counts.viewers);

View file

@ -179,11 +179,15 @@ public:
[[nodiscard]] auto recentViewers() const
-> const std::vector<not_null<PeerData*>> &;
[[nodiscard]] const StoryViews &viewsList() const;
[[nodiscard]] const StoryViews &channelReactionsList() const;
[[nodiscard]] int interactions() const;
[[nodiscard]] int views() const;
[[nodiscard]] int forwards() const;
[[nodiscard]] int reactions() const;
void applyViewsSlice(const QString &offset, const StoryViews &slice);
void applyChannelReactionsSlice(
const QString &offset,
const StoryViews &slice);
[[nodiscard]] const std::vector<StoryLocation> &locations() const;
[[nodiscard]] auto suggestedReactions() const
@ -235,6 +239,7 @@ private:
std::vector<StoryLocation> _locations;
std::vector<SuggestedReaction> _suggestedReactions;
StoryViews _views;
StoryViews _channelReactions;
const TimeId _date = 0;
const TimeId _expires = 0;
TimeId _lastUpdateTime = 0;

View file

@ -907,6 +907,7 @@ void Controller::show(
.views = story->views(),
.total = story->interactions(),
.type = RecentViewsTypeFor(peer),
.canViewReactions = CanViewReactionsFor(peer),
}, _reactions->likedValue());
if (const auto nowLikeButton = _recentViews->likeButton()) {
if (wasLikeButton != nowLikeButton) {
@ -998,13 +999,15 @@ void Controller::subscribeToSession() {
show(update.story, _context);
_delegate->storiesRedisplay(update.story);
} else {
const auto peer = update.story->peer();
_recentViews->show({
.list = update.story->recentViewers(),
.reactions = update.story->reactions(),
.forwards = update.story->forwards(),
.views = update.story->views(),
.total = update.story->interactions(),
.type = RecentViewsTypeFor(update.story->peer()),
.type = RecentViewsTypeFor(peer),
.canViewReactions = CanViewReactionsFor(peer),
});
updateAreas(update.story);
}
@ -1414,11 +1417,19 @@ const Data::StoryViews &Controller::views(int limit, bool initial) {
const auto done = viewsGotMoreCallback();
const auto peer = shownPeer();
auto &stories = peer->owner().stories();
stories.loadViewsSlice(
peer,
_shown.story,
_viewsSlice.nextOffset,
done);
if (peer->isChannel()) {
stories.loadReactionsSlice(
peer,
_shown.story,
_viewsSlice.nextOffset,
done);
} else {
stories.loadViewsSlice(
peer,
_shown.story,
_viewsSlice.nextOffset,
done);
}
}
return _viewsSlice;
}
@ -1433,7 +1444,11 @@ Fn<void(Data::StoryViews)> Controller::viewsGotMoreCallback() {
const auto peer = shownPeer();
auto &stories = peer->owner().stories();
if (const auto maybeStory = stories.lookup(_shown)) {
_viewsSlice = (*maybeStory)->viewsList();
if (peer->isChannel()) {
_viewsSlice = (*maybeStory)->channelReactionsList();
} else {
_viewsSlice = (*maybeStory)->viewsList();
}
} else {
_viewsSlice = {};
}
@ -1596,8 +1611,12 @@ void Controller::refreshViewsFromData() {
const auto peer = shownPeer();
auto &stories = peer->owner().stories();
const auto maybeStory = stories.lookup(_shown);
if (!maybeStory || !peer->isSelf()) {
const auto check = peer->isSelf()
|| CanViewReactionsFor(peer);
if (!maybeStory || !check) {
_viewsSlice = {};
} else if (peer->isChannel()) {
_viewsSlice = (*maybeStory)->channelReactionsList();
} else {
_viewsSlice = (*maybeStory)->viewsList();
}

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_who_reacted.h" // FormatReadDate.
#include "chat_helpers/compose/compose_show.h"
#include "data/stickers/data_custom_emoji.h"
#include "data/data_channel.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "data/data_stories.h"
@ -145,6 +146,13 @@ RecentViewsType RecentViewsTypeFor(not_null<PeerData*> peer) {
: RecentViewsType::Other;
}
bool CanViewReactionsFor(not_null<PeerData*> peer) {
if (const auto channel = peer->asChannel()) {
return channel->amCreator() || channel->hasAdminRights();
}
return false;
}
RecentViews::RecentViews(not_null<Controller*> controller)
: _controller(controller) {
}
@ -178,8 +186,10 @@ void RecentViews::show(
|| (_data.forwards != data.forwards)
|| (_data.reactions != data.reactions);
const auto usersChanged = !_userpics || (_data.list != data.list);
const auto canViewReactions = data.canViewReactions
&& (data.reactions > 0 || data.forwards > 0);
_data = data;
if (_data.type != RecentViewsType::Self) {
if (_data.type != RecentViewsType::Self && !canViewReactions) {
_text = {};
_clickHandlerLifetime.destroy();
_userpicsLifetime.destroy();
@ -229,7 +239,8 @@ Ui::RpWidget *RecentViews::likeIconWidget() const {
}
void RecentViews::refreshClickHandler() {
const auto nowEmpty = _data.list.empty();
const auto nowEmpty = (_data.type != RecentViewsType::Channel)
&& _data.list.empty();
const auto wasEmpty = !_clickHandlerLifetime;
const auto raw = _widget.get();
if (wasEmpty == nowEmpty) {
@ -389,13 +400,16 @@ void RecentViews::updateViewsReactionsGeometry() {
void RecentViews::updatePartsGeometry() {
const auto skip = st::storiesRecentViewsSkip;
const auto full = _userpicsWidth + skip + _text.maxWidth();
const auto add = (_data.type == RecentViewsType::Channel)
? st::storiesViewsTextPosition.y()
: 0;
const auto use = std::min(full, _outer.width());
const auto ux = _outer.x() + (_outer.width() - use) / 2;
const auto uheight = st::storiesWhoViewed.userpics.size;
const auto uy = _outer.y() + (_outer.height() - uheight) / 2;
const auto uy = _outer.y() + (_outer.height() - uheight) / 2 + add;
const auto tx = ux + _userpicsWidth + skip;
const auto theight = st::normalFont->height;
const auto ty = _outer.y() + (_outer.height() - theight) / 2;
const auto ty = _outer.y() + (_outer.height() - theight) / 2 + add;
const auto my = std::min(uy, ty);
const auto mheight = std::max(uheight, theight);
const auto padding = skip;
@ -406,7 +420,9 @@ void RecentViews::updatePartsGeometry() {
}
void RecentViews::updateText() {
const auto text = _data.views
const auto text = (_data.type == RecentViewsType::Channel)
? u"View reactions"_q
: _data.views
? (tr::lng_stories_views(tr::now, lt_count, _data.views)
+ (_data.reactions
? (u" "_q + QChar(10084) + QString::number(_data.reactions))
@ -417,7 +433,8 @@ void RecentViews::updateText() {
}
void RecentViews::showMenu() {
if (_menu || _data.list.empty()) {
if (_menu
|| (_data.type != RecentViewsType::Channel && _data.list.empty())) {
return;
}

View file

@ -48,6 +48,7 @@ struct RecentViewsData {
int views = 0;
int total = 0;
RecentViewsType type = RecentViewsType::Other;
bool canViewReactions = false;
friend inline auto operator<=>(
const RecentViewsData &,
@ -58,6 +59,7 @@ struct RecentViewsData {
};
[[nodiscard]] RecentViewsType RecentViewsTypeFor(not_null<PeerData*> peer);
[[nodiscard]] bool CanViewReactionsFor(not_null<PeerData*> peer);
class RecentViews final {
public:

View file

@ -396,6 +396,7 @@ updateSentStoryReaction#7d627683 peer:Peer story_id:int reaction:Reaction = Upda
updateBotChatBoost#904dd49c peer:Peer boost:Boost qts:int = Update;
updateChannelViewForumAsMessages#7b68920 channel_id:long enabled:Bool = Update;
updatePeerWallpaper#ae3f101d flags:# wallpaper_overridden:flags.1?true peer:Peer wallpaper:flags.0?WallPaper = Update;
updateBotMessageReaction#ac21d3ce peer:Peer msg_id:int date:int actor:Peer old_reactions:Vector<Reaction> new_reactions:Vector<Reaction> qts:int = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -1558,7 +1559,7 @@ storyView#b0bdeac5 flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?
storyViewPublicForward#9083670b flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true message:Message = StoryView;
storyViewPublicRepost#bd74cf49 flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true peer_id:Peer story:StoryItem = StoryView;
stories.storyViewsList#19a16886 flags:# count:int views_count:int forwards_count:int reactions_count:int views:Vector<StoryView> users:Vector<User> next_offset:flags.0?string = stories.StoryViewsList;
stories.storyViewsList#59d78fc5 flags:# count:int views_count:int forwards_count:int reactions_count:int views:Vector<StoryView> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = stories.StoryViewsList;
stories.storyViews#de9eed1d views:Vector<StoryViews> users:Vector<User> = stories.StoryViews;