Update API scheme to layer 164.

This commit is contained in:
John Preston 2023-08-31 12:58:34 +04:00
parent 1bde096417
commit d5b429e910
37 changed files with 586 additions and 358 deletions

View file

@ -2759,6 +2759,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_payments_terms_title" = "Terms of Service"; "lng_payments_terms_title" = "Terms of Service";
"lng_payments_terms_text" = "Subscribe and accept terms of service of {bot}?"; "lng_payments_terms_text" = "Subscribe and accept terms of service of {bot}?";
"lng_payments_terms_text_once" = "Are you accepting terms of service of {bot}?";
"lng_payments_terms_agree" = "I agree to {link}"; "lng_payments_terms_agree" = "I agree to {link}";
"lng_payments_terms_link" = "Terms of Service"; "lng_payments_terms_link" = "Terms of Service";
"lng_payments_terms_accept" = "Accept"; "lng_payments_terms_accept" = "Accept";
@ -3091,9 +3092,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gigagroup_suggest_more" = "Learn more"; "lng_gigagroup_suggest_more" = "Learn more";
"lng_rights_channel_info" = "Change channel info"; "lng_rights_channel_info" = "Change channel info";
"lng_rights_channel_manage" = "Manage messages";
"lng_rights_channel_post" = "Post messages"; "lng_rights_channel_post" = "Post messages";
"lng_rights_channel_edit" = "Edit messages of others"; "lng_rights_channel_edit" = "Edit messages of others";
"lng_rights_channel_delete" = "Delete messages of others"; "lng_rights_channel_delete" = "Delete messages of others";
"lng_rights_channel_manage_stories" = "Manage stories";
"lng_rights_channel_post_stories" = "Post stories";
"lng_rights_channel_edit_stories" = "Edit stories of others";
"lng_rights_channel_delete_stories" = "Delete stories of others";
"lng_rights_channel_manage_calls" = "Manage live streams"; "lng_rights_channel_manage_calls" = "Manage live streams";
"lng_rights_group_info" = "Change group info"; "lng_rights_group_info" = "Change group info";
"lng_rights_group_ban" = "Ban users"; "lng_rights_group_ban" = "Ban users";
@ -3352,6 +3358,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_admin_log_admin_post_messages" = "Post messages"; "lng_admin_log_admin_post_messages" = "Post messages";
"lng_admin_log_admin_edit_messages" = "Edit messages"; "lng_admin_log_admin_edit_messages" = "Edit messages";
"lng_admin_log_admin_delete_messages" = "Delete messages"; "lng_admin_log_admin_delete_messages" = "Delete messages";
"lng_admin_log_admin_post_stories" = "Post stories";
"lng_admin_log_admin_edit_stories" = "Edit stories";
"lng_admin_log_admin_delete_stories" = "Delete stories";
"lng_admin_log_admin_remain_anonymous" = "Remain anonymous"; "lng_admin_log_admin_remain_anonymous" = "Remain anonymous";
"lng_admin_log_admin_ban_users" = "Ban users"; "lng_admin_log_admin_ban_users" = "Ban users";
"lng_admin_log_admin_invite_users" = "Add members"; "lng_admin_log_admin_invite_users" = "Add members";

View file

@ -78,12 +78,8 @@ void SendReport(
MTP_string(comment) MTP_string(comment)
)).done(std::move(done)).send(); )).done(std::move(done)).send();
}, [&](StoryId id) { }, [&](StoryId id) {
const auto user = peer->asUser();
if (!user) {
return;
}
peer->session().api().request(MTPstories_Report( peer->session().api().request(MTPstories_Report(
user->inputUser, peer->input,
MTP_vector<MTPint>(1, MTP_int(id)), MTP_vector<MTPint>(1, MTP_int(id)),
ReasonToTL(reason), ReasonToTL(reason),
MTP_string(comment) MTP_string(comment)

View file

@ -789,7 +789,7 @@ QString ApiWrap::exportDirectStoryLink(not_null<Data::Story*> story) {
? i->second ? i->second
: fallback(); : fallback();
request(MTPstories_ExportStoryLink( request(MTPstories_ExportStoryLink(
story->peer()->asUser()->inputUser, story->peer()->input,
MTP_int(story->id()) MTP_int(story->id())
)).done([=](const MTPExportedStoryLink &result) { )).done([=](const MTPExportedStoryLink &result) {
const auto link = qs(result.data().vlink()); const auto link = qs(result.data().vlink());
@ -2533,14 +2533,9 @@ void ApiWrap::refreshFileReference(
}, [&](Data::FileOriginPremiumPreviews data) { }, [&](Data::FileOriginPremiumPreviews data) {
request(MTPhelp_GetPremiumPromo()); request(MTPhelp_GetPremiumPromo());
}, [&](Data::FileOriginStory data) { }, [&](Data::FileOriginStory data) {
const auto user = _session->data().peer(data.peerId)->asUser(); request(MTPstories_GetStoriesByID(
if (user) { _session->data().peer(data.peerId)->input,
request(MTPstories_GetStoriesByID( MTP_vector<MTPint>(1, MTP_int(data.storyId))));
user->inputUser,
MTP_vector<MTPint>(1, MTP_int(data.storyId))));
} else {
fail();
}
}, [&](v::null_t) { }, [&](v::null_t) {
fail(); fail();
}); });

View file

@ -51,13 +51,14 @@ constexpr auto kForceDisableTooltipDuration = 3 * crl::time(1000);
} }
[[nodiscard]] auto NestedRestrictionLabelsList( [[nodiscard]] auto NestedRestrictionLabelsList(
Data::RestrictionsSetOptions options) { Data::RestrictionsSetOptions options)
-> std::vector<NestedEditFlagsLabels<ChatRestrictions>> {
using Flag = ChatRestriction; using Flag = ChatRestriction;
auto first = std::vector<RestrictionLabel>{ auto first = std::vector<RestrictionLabel>{
{ Flag::SendOther, tr::lng_rights_chat_send_text(tr::now) }, { Flag::SendOther, tr::lng_rights_chat_send_text(tr::now) },
}; };
auto inner = std::vector<RestrictionLabel>{ auto media = std::vector<RestrictionLabel>{
{ Flag::SendPhotos, tr::lng_rights_chat_photos(tr::now) }, { Flag::SendPhotos, tr::lng_rights_chat_photos(tr::now) },
{ Flag::SendVideos, tr::lng_rights_chat_videos(tr::now) }, { Flag::SendVideos, tr::lng_rights_chat_videos(tr::now) },
{ Flag::SendVideoMessages, tr::lng_rights_chat_video_messages(tr::now) }, { Flag::SendVideoMessages, tr::lng_rights_chat_video_messages(tr::now) },
@ -85,9 +86,64 @@ constexpr auto kForceDisableTooltipDuration = 3 * crl::time(1000);
&RestrictionLabel::flags), &RestrictionLabel::flags),
end(second)); end(second));
} }
return std::vector<NestedEditFlagsLabels<ChatRestrictions>>{ return {
{ std::nullopt, std::move(first) }, { std::nullopt, std::move(first) },
{ tr::lng_rights_chat_send_media(), std::move(inner) }, { tr::lng_rights_chat_send_media(), std::move(media) },
{ std::nullopt, std::move(second) },
};
}
[[nodiscard]] auto NestedAdminRightLabels(
Data::AdminRightsSetOptions options)
-> std::vector<NestedEditFlagsLabels<ChatAdminRights>> {
using Flag = ChatAdminRight;
if (options.isGroup) {
auto result = std::vector<AdminRightLabel>{
{ Flag::ChangeInfo, tr::lng_rights_group_info(tr::now) },
{ Flag::DeleteMessages, tr::lng_rights_group_delete(tr::now) },
{ Flag::BanUsers, tr::lng_rights_group_ban(tr::now) },
{ Flag::InviteByLinkOrAdd, options.anyoneCanAddMembers
? tr::lng_rights_group_invite_link(tr::now)
: tr::lng_rights_group_invite(tr::now) },
{ Flag::ManageTopics, tr::lng_rights_group_topics(tr::now) },
{ Flag::PinMessages, tr::lng_rights_group_pin(tr::now) },
{ Flag::ManageCall, tr::lng_rights_group_manage_calls(tr::now) },
{ Flag::Anonymous, tr::lng_rights_group_anonymous(tr::now) },
{ Flag::AddAdmins, tr::lng_rights_add_admins(tr::now) },
};
if (!options.isForum) {
result.erase(
ranges::remove(
result,
Flag::ManageTopics | Flag(),
&AdminRightLabel::flags),
end(result));
}
return { { std::nullopt, std::move(result) } };
}
auto first = std::vector<AdminRightLabel>{
{ Flag::ChangeInfo, tr::lng_rights_channel_info(tr::now) },
};
auto messages = std::vector<AdminRightLabel>{
{ Flag::PostMessages, tr::lng_rights_channel_post(tr::now) },
{ Flag::EditMessages, tr::lng_rights_channel_edit(tr::now) },
{ Flag::DeleteMessages, tr::lng_rights_channel_delete(tr::now) },
};
auto stories = std::vector<AdminRightLabel>{
{ Flag::PostStories, tr::lng_rights_channel_post_stories(tr::now) },
{ Flag::EditStories, tr::lng_rights_channel_edit_stories(tr::now) },
{ Flag::DeleteStories, tr::lng_rights_channel_delete_stories(tr::now) },
};
auto second = std::vector<AdminRightLabel>{
{ Flag::InviteByLinkOrAdd, tr::lng_rights_group_invite(tr::now) },
{ Flag::ManageCall, tr::lng_rights_channel_manage_calls(tr::now) },
{ Flag::AddAdmins, tr::lng_rights_add_admins(tr::now) },
};
return {
{ std::nullopt, std::move(first) },
{ tr::lng_rights_channel_manage(), std::move(messages) },
{ tr::lng_rights_channel_manage_stories(), std::move(stories) },
{ std::nullopt, std::move(second) }, { std::nullopt, std::move(second) },
}; };
} }
@ -1031,42 +1087,11 @@ std::vector<RestrictionLabel> RestrictionLabels(
std::vector<AdminRightLabel> AdminRightLabels( std::vector<AdminRightLabel> AdminRightLabels(
Data::AdminRightsSetOptions options) { Data::AdminRightsSetOptions options) {
using Flag = ChatAdminRight; auto result = std::vector<AdminRightLabel>();
for (const auto &[_, r] : NestedAdminRightLabels(options)) {
if (options.isGroup) { result.insert(result.end(), r.begin(), r.end());
auto result = std::vector<AdminRightLabel>{
{ Flag::ChangeInfo, tr::lng_rights_group_info(tr::now) },
{ Flag::DeleteMessages, tr::lng_rights_group_delete(tr::now) },
{ Flag::BanUsers, tr::lng_rights_group_ban(tr::now) },
{ Flag::InviteByLinkOrAdd, options.anyoneCanAddMembers
? tr::lng_rights_group_invite_link(tr::now)
: tr::lng_rights_group_invite(tr::now) },
{ Flag::ManageTopics, tr::lng_rights_group_topics(tr::now) },
{ Flag::PinMessages, tr::lng_rights_group_pin(tr::now) },
{ Flag::ManageCall, tr::lng_rights_group_manage_calls(tr::now) },
{ Flag::Anonymous, tr::lng_rights_group_anonymous(tr::now) },
{ Flag::AddAdmins, tr::lng_rights_add_admins(tr::now) },
};
if (!options.isForum) {
result.erase(
ranges::remove(
result,
Flag::ManageTopics | Flag(),
&AdminRightLabel::flags),
end(result));
}
return result;
} else {
return {
{ Flag::ChangeInfo, tr::lng_rights_channel_info(tr::now) },
{ Flag::PostMessages, tr::lng_rights_channel_post(tr::now) },
{ Flag::EditMessages, tr::lng_rights_channel_edit(tr::now) },
{ Flag::DeleteMessages, tr::lng_rights_channel_delete(tr::now) },
{ Flag::InviteByLinkOrAdd, tr::lng_rights_group_invite(tr::now) },
{ Flag::ManageCall, tr::lng_rights_channel_manage_calls(tr::now) },
{ Flag::AddAdmins, tr::lng_rights_add_admins(tr::now) }
};
} }
return result;
} }
EditFlagsControl<ChatRestrictions> CreateEditRestrictions( EditFlagsControl<ChatRestrictions> CreateEditRestrictions(
@ -1107,7 +1132,7 @@ EditFlagsControl<ChatAdminRights> CreateEditAdminRights(
rights, rights,
{ {
.header = std::move(header), .header = std::move(header),
.labels = { { std::nullopt, AdminRightLabels(options) } }, .labels = NestedAdminRightLabels(options),
.disabledMessages = std::move(disabledMessages), .disabledMessages = std::move(disabledMessages),
}); });
result.widget = std::move(widget); result.widget = std::move(widget);

View file

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_chat.h" #include "data/data_chat.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_stories.h"
#include "data/data_folder.h" #include "data/data_folder.h"
#include "data/data_forum.h" #include "data/data_forum.h"
#include "data/data_forum_icons.h" #include "data/data_forum_icons.h"
@ -530,6 +531,11 @@ bool ChannelData::canBanMembers() const {
|| (adminRights() & AdminRight::BanUsers); || (adminRights() & AdminRight::BanUsers);
} }
bool ChannelData::canPostMessages() const {
return amCreator()
|| (adminRights() & AdminRight::PostMessages);
}
bool ChannelData::canEditMessages() const { bool ChannelData::canEditMessages() const {
return amCreator() return amCreator()
|| (adminRights() & AdminRight::EditMessages); || (adminRights() & AdminRight::EditMessages);
@ -559,11 +565,6 @@ bool ChannelData::canAddAdmins() const {
|| (adminRights() & AdminRight::AddAdmins); || (adminRights() & AdminRight::AddAdmins);
} }
bool ChannelData::canPublish() const {
return amCreator()
|| (adminRights() & AdminRight::PostMessages);
}
bool ChannelData::allowsForwarding() const { bool ChannelData::allowsForwarding() const {
return !(flags() & Flag::NoForwards); return !(flags() & Flag::NoForwards);
} }
@ -877,6 +878,38 @@ const Data::AllowedReactions &ChannelData::allowedReactions() const {
return _allowedReactions; return _allowedReactions;
} }
bool ChannelData::hasActiveStories() const {
return flags() & Flag::HasActiveStories;
}
bool ChannelData::hasUnreadStories() const {
return flags() & Flag::HasUnreadStories;
}
void ChannelData::setStoriesState(StoriesState state) {
Expects(state != StoriesState::Unknown);
const auto was = flags();
switch (state) {
case StoriesState::None:
_flags.remove(Flag::HasActiveStories | Flag::HasUnreadStories);
break;
case StoriesState::HasRead:
_flags.set(
(flags() & ~Flag::HasUnreadStories) | Flag::HasActiveStories);
break;
case StoriesState::HasUnread:
_flags.add(Flag::HasActiveStories | Flag::HasUnreadStories);
break;
}
if (flags() != was) {
if (const auto history = owner().historyLoaded(this)) {
history->updateChatListEntryPostponed();
}
session().changes().peerUpdated(this, UpdateFlag::StoriesState);
}
}
void ChannelData::processTopics(const MTPVector<MTPForumTopic> &topics) { void ChannelData::processTopics(const MTPVector<MTPForumTopic> &topics) {
if (const auto forum = this->forum()) { if (const auto forum = this->forum()) {
forum->applyReceivedTopics(topics); forum->applyReceivedTopics(topics);
@ -1046,6 +1079,7 @@ void ApplyChannelUpdate(
} else { } else {
channel->setAllowedReactions({}); channel->setAllowedReactions({});
} }
channel->owner().stories().apply(channel, update.vstories());
channel->fullUpdated(); channel->fullUpdated();
channel->setPendingRequestsCount( channel->setPendingRequestsCount(
update.vrequests_pending().value_or_empty(), update.vrequests_pending().value_or_empty(),

View file

@ -59,6 +59,9 @@ enum class ChannelDataFlag {
Forum = (1 << 23), Forum = (1 << 23),
AntiSpam = (1 << 24), AntiSpam = (1 << 24),
ParticipantsHidden = (1 << 25), ParticipantsHidden = (1 << 25),
StoriesHidden = (1 << 26),
HasActiveStories = (1 << 27),
HasUnreadStories = (1 << 28),
}; };
inline constexpr bool is_flag_type(ChannelDataFlag) { return true; }; inline constexpr bool is_flag_type(ChannelDataFlag) { return true; };
using ChannelDataFlags = base::flags<ChannelDataFlag>; using ChannelDataFlags = base::flags<ChannelDataFlag>;
@ -226,6 +229,9 @@ public:
[[nodiscard]] bool isFake() const { [[nodiscard]] bool isFake() const {
return flags() & Flag::Fake; return flags() & Flag::Fake;
} }
[[nodiscard]] bool hasStoriesHidden() const {
return flags() & Flag::StoriesHidden;
}
[[nodiscard]] static ChatRestrictionsInfo KickedRestrictedRights( [[nodiscard]] static ChatRestrictionsInfo KickedRestrictedRights(
not_null<PeerData*> participant); not_null<PeerData*> participant);
@ -329,10 +335,13 @@ public:
[[nodiscard]] bool canBanMembers() const; [[nodiscard]] bool canBanMembers() const;
[[nodiscard]] bool anyoneCanAddMembers() const; [[nodiscard]] bool anyoneCanAddMembers() const;
[[nodiscard]] bool canPostMessages() const;
[[nodiscard]] bool canEditMessages() const; [[nodiscard]] bool canEditMessages() const;
[[nodiscard]] bool canDeleteMessages() const; [[nodiscard]] bool canDeleteMessages() const;
[[nodiscard]] bool canPostStories() const;
[[nodiscard]] bool canEditStories() const;
[[nodiscard]] bool canDeleteStories() const;
[[nodiscard]] bool hiddenPreHistory() const; [[nodiscard]] bool hiddenPreHistory() const;
[[nodiscard]] bool canPublish() const;
[[nodiscard]] bool canViewMembers() const; [[nodiscard]] bool canViewMembers() const;
[[nodiscard]] bool canViewAdmins() const; [[nodiscard]] bool canViewAdmins() const;
[[nodiscard]] bool canViewBanned() const; [[nodiscard]] bool canViewBanned() const;
@ -437,6 +446,10 @@ public:
void setAllowedReactions(Data::AllowedReactions value); void setAllowedReactions(Data::AllowedReactions value);
[[nodiscard]] const Data::AllowedReactions &allowedReactions() const; [[nodiscard]] const Data::AllowedReactions &allowedReactions() const;
[[nodiscard]] bool hasActiveStories() const;
[[nodiscard]] bool hasUnreadStories() const;
void setStoriesState(StoriesState state);
[[nodiscard]] Data::Forum *forum() const { [[nodiscard]] Data::Forum *forum() const {
return mgInfo ? mgInfo->forum() : nullptr; return mgInfo ? mgInfo->forum() : nullptr;
} }

View file

@ -146,7 +146,7 @@ bool CanSendAnyOf(
&& !(channel->flags() & Flag::JoinToWrite)); && !(channel->flags() & Flag::JoinToWrite));
if (!allowed || (forbidInForums && channel->isForum())) { if (!allowed || (forbidInForums && channel->isForum())) {
return false; return false;
} else if (channel->canPublish()) { } else if (channel->canPostMessages()) {
return true; return true;
} else if (channel->isBroadcast()) { } else if (channel->isBroadcast()) {
return false; return false;

View file

@ -25,6 +25,9 @@ enum class ChatAdminRight {
ManageCall = (1 << 11), ManageCall = (1 << 11),
Other = (1 << 12), Other = (1 << 12),
ManageTopics = (1 << 13), ManageTopics = (1 << 13),
PostStories = (1 << 14),
EditStories = (1 << 15),
DeleteStories = (1 << 16),
}; };
inline constexpr bool is_flag_type(ChatAdminRight) { return true; } inline constexpr bool is_flag_type(ChatAdminRight) { return true; }
using ChatAdminRights = base::flags<ChatAdminRight>; using ChatAdminRights = base::flags<ChatAdminRight>;

View file

@ -898,6 +898,33 @@ bool PeerData::isRepliesChat() const {
: kTestId) == id; : kTestId) == id;
} }
bool PeerData::sharedMediaInfo() const {
return isSelf() || isRepliesChat();
}
bool PeerData::hasStoriesHidden() const {
if (const auto user = asUser()) {
return user->hasStoriesHidden();
} else if (const auto channel = asChannel()) {
return channel->hasStoriesHidden();
}
return false;
}
void PeerData::setStoriesHidden(bool hidden) {
if (const auto user = asUser()) {
user->setFlags(hidden
? (user->flags() | UserDataFlag::StoriesHidden)
: (user->flags() & ~UserDataFlag::StoriesHidden));
} else if (const auto channel = asChannel()) {
channel->setFlags(hidden
? (channel->flags() | ChannelDataFlag::StoriesHidden)
: (channel->flags() & ~ChannelDataFlag::StoriesHidden));
} else {
Unexpected("PeerData::setStoriesHidden for non-user/non-channel.");
}
}
Data::Forum *PeerData::forum() const { Data::Forum *PeerData::forum() const {
if (const auto channel = asChannel()) { if (const auto channel = asChannel()) {
return channel->forum(); return channel->forum();
@ -1108,6 +1135,34 @@ const Data::WallPaper *PeerData::wallPaper() const {
return _wallPaper.get(); return _wallPaper.get();
} }
bool PeerData::hasActiveStories() const {
if (const auto user = asUser()) {
return user->hasActiveStories();
} else if (const auto channel = asChannel()) {
return channel->hasActiveStories();
}
return false;
}
bool PeerData::hasUnreadStories() const {
if (const auto user = asUser()) {
return user->hasUnreadStories();
} else if (const auto channel = asChannel()) {
return channel->hasUnreadStories();
}
return false;
}
void PeerData::setStoriesState(StoriesState state) {
if (const auto user = asUser()) {
return user->setStoriesState(state);
} else if (const auto channel = asChannel()) {
return channel->setStoriesState(state);
} else {
Unexpected("PeerData::setStoriesState for non-user/non-channel.");
}
}
void PeerData::setIsBlocked(bool is) { void PeerData::setIsBlocked(bool is) {
const auto status = is const auto status = is
? BlockStatus::Blocked ? BlockStatus::Blocked

View file

@ -183,9 +183,9 @@ public:
[[nodiscard]] bool isForum() const; [[nodiscard]] bool isForum() const;
[[nodiscard]] bool isGigagroup() const; [[nodiscard]] bool isGigagroup() const;
[[nodiscard]] bool isRepliesChat() const; [[nodiscard]] bool isRepliesChat() const;
[[nodiscard]] bool sharedMediaInfo() const { [[nodiscard]] bool sharedMediaInfo() const;
return isSelf() || isRepliesChat(); [[nodiscard]] bool hasStoriesHidden() const;
} void setStoriesHidden(bool hidden);
[[nodiscard]] bool isNotificationsUser() const { [[nodiscard]] bool isNotificationsUser() const {
return (id == peerFromUser(333000)) return (id == peerFromUser(333000))
@ -407,6 +407,16 @@ public:
void setWallPaper(std::optional<Data::WallPaper> paper); void setWallPaper(std::optional<Data::WallPaper> paper);
[[nodiscard]] const Data::WallPaper *wallPaper() const; [[nodiscard]] const Data::WallPaper *wallPaper() const;
enum class StoriesState {
Unknown,
None,
HasRead,
HasUnread,
};
[[nodiscard]] bool hasActiveStories() const;
[[nodiscard]] bool hasUnreadStories() const;
void setStoriesState(StoriesState state);
const PeerId id; const PeerId id;
MTPinputPeer input = MTP_inputPeerEmpty(); MTPinputPeer input = MTP_inputPeerEmpty();

View file

@ -909,7 +909,17 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
| Flag::NoForwards | Flag::NoForwards
| Flag::JoinToWrite | Flag::JoinToWrite
| Flag::RequestToJoin | Flag::RequestToJoin
| Flag::Forum; | Flag::Forum
| ((!minimal && !data.is_stories_hidden_min())
? Flag::StoriesHidden
: Flag());
const auto storiesState = minimal
? std::optional<Data::Stories::PeerSourceState>()
: data.is_stories_unavailable()
? Data::Stories::PeerSourceState()
: !data.vstories_max_id()
? std::optional<Data::Stories::PeerSourceState>()
: stories().peerSourceState(channel, data.vstories_max_id()->v);
const auto flagsSet = (data.is_broadcast() ? Flag::Broadcast : Flag()) const auto flagsSet = (data.is_broadcast() ? Flag::Broadcast : Flag())
| (data.is_verified() ? Flag::Verified : Flag()) | (data.is_verified() ? Flag::Verified : Flag())
| (data.is_scam() ? Flag::Scam : Flag()) | (data.is_scam() ? Flag::Scam : Flag())
@ -935,8 +945,20 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
| (data.is_join_request() ? Flag::RequestToJoin : Flag()) | (data.is_join_request() ? Flag::RequestToJoin : Flag())
| ((data.is_forum() && data.is_megagroup()) | ((data.is_forum() && data.is_megagroup())
? Flag::Forum ? Flag::Forum
: Flag())
| ((!minimal
&& !data.is_stories_hidden_min()
&& data.is_stories_hidden())
? Flag::StoriesHidden
: Flag()); : Flag());
channel->setFlags((channel->flags() & ~flagsMask) | flagsSet); channel->setFlags((channel->flags() & ~flagsMask) | flagsSet);
if (!minimal && storiesState) {
result->setStoriesState(!storiesState->maxId
? UserData::StoriesState::None
: (storiesState->maxId > storiesState->readTill)
? UserData::StoriesState::HasUnread
: UserData::StoriesState::HasRead);
}
channel->setPhoto(data.vphoto()); channel->setPhoto(data.vphoto());
@ -3345,12 +3367,12 @@ void Session::webpageApplyFields(
for (const auto &attribute : attributes->v) { for (const auto &attribute : attributes->v) {
attribute.match([&](const MTPDwebPageAttributeStory &data) { attribute.match([&](const MTPDwebPageAttributeStory &data) {
storyId = FullStoryId{ storyId = FullStoryId{
peerFromUser(data.vuser_id()), peerFromMTP(data.vpeer()),
data.vid().v, data.vid().v,
}; };
if (const auto embed = data.vstory()) { if (const auto embed = data.vstory()) {
story = stories().applyFromWebpage( story = stories().applyFromWebpage(
peerFromUser(data.vuser_id()), peerFromMTP(data.vpeer()),
*embed); *embed);
} else if (const auto maybe = stories().lookup(storyId)) { } else if (const auto maybe = stories().lookup(storyId)) {
story = *maybe; story = *maybe;

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h" #include "apiwrap.h"
#include "core/application.h" #include "core/application.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_channel.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_folder.h" #include "data/data_folder.h"
#include "data/data_photo.h" #include "data/data_photo.h"
@ -78,11 +79,11 @@ using UpdateFlag = StoryUpdate::Flag;
StoriesSourceInfo StoriesSource::info() const { StoriesSourceInfo StoriesSource::info() const {
return { return {
.id = user->id, .id = peer->id,
.last = ids.empty() ? 0 : ids.back().date, .last = ids.empty() ? 0 : ids.back().date,
.count = uint32(std::min(int(ids.size()), kMaxSegmentsCount)), .count = uint32(std::min(int(ids.size()), kMaxSegmentsCount)),
.unreadCount = uint32(std::min(unreadCount(), kMaxSegmentsCount)), .unreadCount = uint32(std::min(unreadCount(), kMaxSegmentsCount)),
.premium = user->isPremium() ? 1U : 0U, .premium = (peer->isUser() && peer->asUser()->isPremium()) ? 1U : 0,
}; };
} }
@ -122,10 +123,10 @@ Main::Session &Stories::session() const {
} }
void Stories::apply(const MTPDupdateStory &data) { void Stories::apply(const MTPDupdateStory &data) {
const auto peerId = peerFromUser(data.vuser_id()); const auto peerId = peerFromMTP(data.vpeer());
const auto user = not_null(_owner->peer(peerId)->asUser()); const auto peer = _owner->peer(peerId);
const auto now = base::unixtime::now(); const auto now = base::unixtime::now();
const auto idDates = parseAndApply(user, data.vstory(), now); const auto idDates = parseAndApply(peer, data.vstory(), now);
if (!idDates) { if (!idDates) {
return; return;
} }
@ -136,7 +137,7 @@ void Stories::apply(const MTPDupdateStory &data) {
} }
const auto i = _all.find(peerId); const auto i = _all.find(peerId);
if (i == end(_all)) { if (i == end(_all)) {
requestUserStories(user); requestPeerStories(peer);
return; return;
} else if (i->second.ids.contains(idDates)) { } else if (i->second.ids.contains(idDates)) {
return; return;
@ -144,8 +145,8 @@ void Stories::apply(const MTPDupdateStory &data) {
const auto wasInfo = i->second.info(); const auto wasInfo = i->second.info();
i->second.ids.emplace(idDates); i->second.ids.emplace(idDates);
const auto nowInfo = i->second.info(); const auto nowInfo = i->second.info();
if (user->isSelf() && i->second.readTill < idDates.id) { if (peer->isSelf() && i->second.readTill < idDates.id) {
_readTill[user->id] = i->second.readTill = idDates.id; _readTill[peerId] = i->second.readTill = idDates.id;
} }
if (wasInfo == nowInfo) { if (wasInfo == nowInfo) {
return; return;
@ -161,17 +162,17 @@ void Stories::apply(const MTPDupdateStory &data) {
sort(list); sort(list);
} }
}; };
if (user->hasStoriesHidden()) { if (peer->hasStoriesHidden()) {
refreshInList(StorySourcesList::Hidden); refreshInList(StorySourcesList::Hidden);
} else { } else {
refreshInList(StorySourcesList::NotHidden); refreshInList(StorySourcesList::NotHidden);
} }
_sourceChanged.fire_copy(peerId); _sourceChanged.fire_copy(peerId);
updateUserStoriesState(user); updatePeerStoriesState(peer);
} }
void Stories::apply(const MTPDupdateReadStories &data) { void Stories::apply(const MTPDupdateReadStories &data) {
bumpReadTill(peerFromUser(data.vuser_id()), data.vmax_id().v); bumpReadTill(peerFromMTP(data.vpeer()), data.vmax_id().v);
} }
void Stories::apply(const MTPStoriesStealthMode &stealthMode) { void Stories::apply(const MTPStoriesStealthMode &stealthMode) {
@ -182,13 +183,13 @@ void Stories::apply(const MTPStoriesStealthMode &stealthMode) {
}; };
} }
void Stories::apply(not_null<PeerData*> peer, const MTPUserStories *data) { void Stories::apply(not_null<PeerData*> peer, const MTPPeerStories *data) {
if (!data) { if (!data) {
applyDeletedFromSources(peer->id, StorySourcesList::NotHidden); applyDeletedFromSources(peer->id, StorySourcesList::NotHidden);
applyDeletedFromSources(peer->id, StorySourcesList::Hidden); applyDeletedFromSources(peer->id, StorySourcesList::Hidden);
_all.erase(peer->id); _all.erase(peer->id);
_sourceChanged.fire_copy(peer->id); _sourceChanged.fire_copy(peer->id);
updateUserStoriesState(peer); updatePeerStoriesState(peer);
} else { } else {
parseAndApply(*data); parseAndApply(*data);
} }
@ -205,10 +206,10 @@ Story *Stories::applyFromWebpage(PeerId peerId, const MTPstoryItem &story) {
return value ? value->get() : nullptr; return value ? value->get() : nullptr;
} }
void Stories::requestUserStories( void Stories::requestPeerStories(
not_null<UserData*> user, not_null<PeerData*> peer,
Fn<void()> done) { Fn<void()> done) {
const auto [i, ok] = _requestingUserStories.emplace(user); const auto [i, ok] = _requestingPeerStories.emplace(peer);
if (done) { if (done) {
i->second.push_back(std::move(done)); i->second.push_back(std::move(done));
} }
@ -216,22 +217,23 @@ void Stories::requestUserStories(
return; return;
} }
const auto finish = [=] { const auto finish = [=] {
if (const auto callbacks = _requestingUserStories.take(user)) { if (const auto callbacks = _requestingPeerStories.take(peer)) {
for (const auto &callback : *callbacks) { for (const auto &callback : *callbacks) {
callback(); callback();
} }
} }
}; };
_owner->session().api().request(MTPstories_GetUserStories( _owner->session().api().request(MTPstories_GetPeerStories(
user->inputUser peer->input
)).done([=](const MTPstories_UserStories &result) { )).done([=](const MTPstories_PeerStories &result) {
const auto &data = result.data(); const auto &data = result.data();
_owner->processUsers(data.vusers()); _owner->processUsers(data.vusers());
_owner->processChats(data.vchats());
parseAndApply(data.vstories()); parseAndApply(data.vstories());
finish(); finish();
}).fail([=] { }).fail([=] {
applyDeletedFromSources(user->id, StorySourcesList::NotHidden); applyDeletedFromSources(peer->id, StorySourcesList::NotHidden);
applyDeletedFromSources(user->id, StorySourcesList::Hidden); applyDeletedFromSources(peer->id, StorySourcesList::Hidden);
finish(); finish();
}).send(); }).send();
} }
@ -291,33 +293,33 @@ void Stories::processExpired() {
} }
} }
void Stories::parseAndApply(const MTPUserStories &stories) { void Stories::parseAndApply(const MTPPeerStories &stories) {
const auto &data = stories.data(); const auto &data = stories.data();
const auto peerId = peerFromUser(data.vuser_id()); const auto peerId = peerFromMTP(data.vpeer());
const auto already = _readTill.find(peerId); const auto already = _readTill.find(peerId);
const auto readTill = std::max( const auto readTill = std::max(
data.vmax_read_id().value_or_empty(), data.vmax_read_id().value_or_empty(),
(already != end(_readTill) ? already->second : 0)); (already != end(_readTill) ? already->second : 0));
const auto user = _owner->peer(peerId)->asUser(); const auto peer = _owner->peer(peerId);
auto result = StoriesSource{ auto result = StoriesSource{
.user = user, .peer = peer,
.readTill = readTill, .readTill = readTill,
.hidden = user->hasStoriesHidden(), .hidden = peer->hasStoriesHidden(),
}; };
const auto &list = data.vstories().v; const auto &list = data.vstories().v;
const auto now = base::unixtime::now(); const auto now = base::unixtime::now();
result.ids.reserve(list.size()); result.ids.reserve(list.size());
for (const auto &story : list) { for (const auto &story : list) {
if (const auto id = parseAndApply(result.user, story, now)) { if (const auto id = parseAndApply(result.peer, story, now)) {
result.ids.emplace(id); result.ids.emplace(id);
} }
} }
if (result.ids.empty()) { if (result.ids.empty()) {
applyDeletedFromSources(peerId, StorySourcesList::NotHidden); applyDeletedFromSources(peerId, StorySourcesList::NotHidden);
applyDeletedFromSources(peerId, StorySourcesList::Hidden); applyDeletedFromSources(peerId, StorySourcesList::Hidden);
user->setStoriesState(UserData::StoriesState::None); peer->setStoriesState(PeerData::StoriesState::None);
return; return;
} else if (user->isSelf()) { } else if (peer->isSelf()) {
result.readTill = result.ids.back().id; result.readTill = result.ids.back().id;
} }
_readTill[peerId] = result.readTill; _readTill[peerId] = result.readTill;
@ -345,11 +347,13 @@ void Stories::parseAndApply(const MTPUserStories &stories) {
} }
sort(list); sort(list);
}; };
if (result.user->isSelf() if (result.peer->isSelf()
|| result.user->isBot() || (result.peer->isChannel() && result.peer->asChannel()->amIn())
|| result.user->isServiceUser() || (result.peer->isUser()
|| result.user->isContact()) { && (result.peer->asUser()->isBot()
const auto hidden = result.user->hasStoriesHidden(); || result.peer->asUser()->isContact()))
|| result.peer->isServiceUser()) {
const auto hidden = result.peer->hasStoriesHidden();
using List = StorySourcesList; using List = StorySourcesList;
add(hidden ? List::Hidden : List::NotHidden); add(hidden ? List::Hidden : List::NotHidden);
applyDeletedFromSources( applyDeletedFromSources(
@ -360,7 +364,7 @@ void Stories::parseAndApply(const MTPUserStories &stories) {
applyDeletedFromSources(peerId, StorySourcesList::Hidden); applyDeletedFromSources(peerId, StorySourcesList::Hidden);
} }
_sourceChanged.fire_copy(peerId); _sourceChanged.fire_copy(peerId);
updateUserStoriesState(result.user); updatePeerStoriesState(result.peer);
} }
Story *Stories::parseAndApply( Story *Stories::parseAndApply(
@ -539,9 +543,10 @@ void Stories::loadMore(StorySourcesList list) {
result.match([&](const MTPDstories_allStories &data) { result.match([&](const MTPDstories_allStories &data) {
_owner->processUsers(data.vusers()); _owner->processUsers(data.vusers());
_owner->processChats(data.vchats());
_sourcesStates[index] = qs(data.vstate()); _sourcesStates[index] = qs(data.vstate());
_sourcesLoaded[index] = !data.is_has_more(); _sourcesLoaded[index] = !data.is_has_more();
for (const auto &single : data.vuser_stories().v) { for (const auto &single : data.vpeer_stories().v) {
parseAndApply(single); parseAndApply(single);
} }
}, [](const MTPDstories_allStoriesNotModified &) { }, [](const MTPDstories_allStoriesNotModified &) {
@ -654,18 +659,15 @@ void Stories::sendResolveRequests() {
crl::on_main(&session(), [=] { sendResolveRequests(); }); crl::on_main(&session(), [=] { sendResolveRequests(); });
} }
}; };
const auto user = _owner->session().data().peer(peerId)->asUser(); const auto peer = _owner->session().data().peer(peerId);
if (!user) {
finish(peerId);
continue;
}
api->request(MTPstories_GetStoriesByID( api->request(MTPstories_GetStoriesByID(
user->inputUser, peer->input,
MTP_vector<MTPint>(prepared) MTP_vector<MTPint>(prepared)
)).done([=](const MTPstories_Stories &result) { )).done([=](const MTPstories_Stories &result) {
owner().processUsers(result.data().vusers()); owner().processUsers(result.data().vusers());
processResolvedStories(user, result.data().vstories().v); owner().processChats(result.data().vchats());
finish(user->id); processResolvedStories(peer, result.data().vstories().v);
finish(peer->id);
}).fail([=] { }).fail([=] {
finish(peerId); finish(peerId);
}).send(); }).send();
@ -786,14 +788,14 @@ void Stories::applyRemovedFromActive(FullStoryId id) {
const auto j = i->second.ids.lower_bound(StoryIdDates{ id.story }); const auto j = i->second.ids.lower_bound(StoryIdDates{ id.story });
if (j != end(i->second.ids) && j->id == id.story) { if (j != end(i->second.ids) && j->id == id.story) {
i->second.ids.erase(j); i->second.ids.erase(j);
const auto user = i->second.user; const auto peer = i->second.peer;
if (i->second.ids.empty()) { if (i->second.ids.empty()) {
_all.erase(i); _all.erase(i);
removeFromList(StorySourcesList::NotHidden); removeFromList(StorySourcesList::NotHidden);
removeFromList(StorySourcesList::Hidden); removeFromList(StorySourcesList::Hidden);
} }
_sourceChanged.fire_copy(id.peer); _sourceChanged.fire_copy(id.peer);
updateUserStoriesState(user); updatePeerStoriesState(peer);
} }
} }
} }
@ -881,7 +883,7 @@ void Stories::sendReaction(FullStoryId id, Data::ReactionId reaction) {
const auto api = &session().api(); const auto api = &session().api();
api->request(MTPstories_SendReaction( api->request(MTPstories_SendReaction(
MTP_flags(0), MTP_flags(0),
story->peer()->asUser()->inputUser, story->peer()->input,
MTP_int(id.story), MTP_int(id.story),
ReactionToMTP(reaction) ReactionToMTP(reaction)
)).send(); )).send();
@ -1054,7 +1056,7 @@ bool Stories::bumpReadTill(PeerId peerId, StoryId maxReadTill) {
if (till < maxReadTill) { if (till < maxReadTill) {
const auto from = till; const auto from = till;
till = maxReadTill; till = maxReadTill;
updateUserStoriesState(_owner->peer(peerId)); updatePeerStoriesState(_owner->peer(peerId));
const auto i = _stories.find(peerId); const auto i = _stories.find(peerId);
if (i != end(_stories)) { if (i != end(_stories)) {
refreshItems = ranges::make_subrange( refreshItems = ranges::make_subrange(
@ -1097,19 +1099,16 @@ void Stories::toggleHidden(
PeerId peerId, PeerId peerId,
bool hidden, bool hidden,
std::shared_ptr<Ui::Show> show) { std::shared_ptr<Ui::Show> show) {
const auto user = _owner->peer(peerId)->asUser(); const auto peer = _owner->peer(peerId);
Assert(user != nullptr); if (peer->hasStoriesHidden() != hidden) {
if (user->hasStoriesHidden() != hidden) { peer->setStoriesHidden(hidden);
user->setFlags(hidden session().api().request(MTPstories_TogglePeerStoriesHidden(
? (user->flags() | UserDataFlag::StoriesHidden) peer->input,
: (user->flags() & ~UserDataFlag::StoriesHidden));
session().api().request(MTPcontacts_ToggleStoriesHidden(
user->inputUser,
MTP_bool(hidden) MTP_bool(hidden)
)).send(); )).send();
} }
const auto name = user->shortName(); const auto name = peer->shortName();
const auto guard = gsl::finally([&] { const auto guard = gsl::finally([&] {
if (show) { if (show) {
const auto phrase = hidden const auto phrase = hidden
@ -1166,8 +1165,6 @@ void Stories::toggleHidden(
void Stories::sendMarkAsReadRequest( void Stories::sendMarkAsReadRequest(
not_null<PeerData*> peer, not_null<PeerData*> peer,
StoryId tillId) { StoryId tillId) {
Expects(peer->isUser());
const auto peerId = peer->id; const auto peerId = peer->id;
_markReadRequests.emplace(peerId); _markReadRequests.emplace(peerId);
const auto finish = [=] { const auto finish = [=] {
@ -1181,7 +1178,7 @@ void Stories::sendMarkAsReadRequest(
const auto api = &_owner->session().api(); const auto api = &_owner->session().api();
api->request(MTPstories_ReadStories( api->request(MTPstories_ReadStories(
peer->asUser()->inputUser, peer->input,
MTP_int(tillId) MTP_int(tillId)
)).done(finish).fail(finish).send(); )).done(finish).fail(finish).send();
} }
@ -1205,7 +1202,7 @@ void Stories::sendMarkAsReadRequests() {
} }
const auto j = _all.find(peerId); const auto j = _all.find(peerId);
if (j != end(_all)) { if (j != end(_all)) {
sendMarkAsReadRequest(j->second.user, j->second.readTill); sendMarkAsReadRequest(j->second.peer, j->second.readTill);
} }
i = _markReadPending.erase(i); i = _markReadPending.erase(i);
} }
@ -1243,7 +1240,7 @@ void Stories::sendIncrementViewsRequests() {
checkQuitPreventFinished(); checkQuitPreventFinished();
}; };
api->request(MTPstories_IncrementStoryViews( api->request(MTPstories_IncrementStoryViews(
_owner->peer(peer)->asUser()->inputUser, _owner->peer(peer)->input,
MTP_vector<MTPint>(std::move(ids)) MTP_vector<MTPint>(std::move(ids))
)).done(finish).fail(finish).send(); )).done(finish).fail(finish).send();
_incrementViewsPending.remove(peer); _incrementViewsPending.remove(peer);
@ -1272,6 +1269,7 @@ void Stories::loadViewsSlice(
using Flag = MTPstories_GetStoryViewsList::Flag; using Flag = MTPstories_GetStoryViewsList::Flag;
_viewsRequestId = api->request(MTPstories_GetStoryViewsList( _viewsRequestId = api->request(MTPstories_GetStoryViewsList(
MTP_flags(Flag::f_reactions_first), MTP_flags(Flag::f_reactions_first),
MTP_inputPeerSelf(),
MTPstring(), // q MTPstring(), // q
MTP_int(id), MTP_int(id),
MTP_string(offset), MTP_string(offset),
@ -1364,6 +1362,7 @@ void Stories::archiveLoadMore() {
} }
const auto api = &_owner->session().api(); const auto api = &_owner->session().api();
_archiveRequestId = api->request(MTPstories_GetStoriesArchive( _archiveRequestId = api->request(MTPstories_GetStoriesArchive(
MTP_inputPeerSelf(),
MTP_int(_archiveLastId), MTP_int(_archiveLastId),
MTP_int(_archiveLastId ? kArchivePerPage : kArchiveFirstPerPage) MTP_int(_archiveLastId ? kArchivePerPage : kArchiveFirstPerPage)
)).done([=](const MTPstories_Stories &result) { )).done([=](const MTPstories_Stories &result) {
@ -1399,8 +1398,6 @@ void Stories::archiveLoadMore() {
} }
void Stories::savedLoadMore(PeerId peerId) { void Stories::savedLoadMore(PeerId peerId) {
Expects(peerIsUser(peerId));
auto &saved = _saved[peerId]; auto &saved = _saved[peerId];
if (saved.requestId || saved.loaded) { if (saved.requestId || saved.loaded) {
return; return;
@ -1408,7 +1405,7 @@ void Stories::savedLoadMore(PeerId peerId) {
const auto api = &_owner->session().api(); const auto api = &_owner->session().api();
const auto peer = _owner->peer(peerId); const auto peer = _owner->peer(peerId);
saved.requestId = api->request(MTPstories_GetPinnedStories( saved.requestId = api->request(MTPstories_GetPinnedStories(
peer->asUser()->inputUser, peer->input,
MTP_int(saved.lastId), MTP_int(saved.lastId),
MTP_int(saved.lastId ? kSavedPerPage : kSavedFirstPerPage) MTP_int(saved.lastId ? kSavedPerPage : kSavedFirstPerPage)
)).done([=](const MTPstories_Stories &result) { )).done([=](const MTPstories_Stories &result) {
@ -1456,6 +1453,7 @@ void Stories::deleteList(const std::vector<FullStoryId> &ids) {
if (!list.empty()) { if (!list.empty()) {
const auto api = &_owner->session().api(); const auto api = &_owner->session().api();
api->request(MTPstories_DeleteStories( api->request(MTPstories_DeleteStories(
MTP_inputPeerSelf(),
MTP_vector<MTPint>(list) MTP_vector<MTPint>(list)
)).done([=](const MTPVector<MTPint> &result) { )).done([=](const MTPVector<MTPint> &result) {
for (const auto &id : result.v) { for (const auto &id : result.v) {
@ -1481,6 +1479,7 @@ void Stories::togglePinnedList(
} }
const auto api = &_owner->session().api(); const auto api = &_owner->session().api();
api->request(MTPstories_TogglePinned( api->request(MTPstories_TogglePinned(
MTP_inputPeerSelf(),
MTP_vector<MTPint>(list), MTP_vector<MTPint>(list),
MTP_bool(pinned) MTP_bool(pinned)
)).done([=](const MTPVector<MTPint> &result) { )).done([=](const MTPVector<MTPint> &result) {
@ -1608,7 +1607,7 @@ std::optional<Stories::PeerSourceState> Stories::peerSourceState(
}; };
} }
requestReadTills(); requestReadTills();
_pendingUserStateMaxId[peer] = storyMaxId; _pendingPeerStateMaxId[peer] = storyMaxId;
return std::nullopt; return std::nullopt;
} }
@ -1617,12 +1616,12 @@ void Stories::requestReadTills() {
return; return;
} }
const auto api = &_owner->session().api(); const auto api = &_owner->session().api();
_readTillsRequestId = api->request(MTPstories_GetAllReadUserStories( _readTillsRequestId = api->request(MTPstories_GetAllReadPeerStories(
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
_readTillReceived = true; _readTillReceived = true;
api->applyUpdates(result); api->applyUpdates(result);
for (auto &[peer, maxId] : base::take(_pendingUserStateMaxId)) { for (auto &[peer, maxId] : base::take(_pendingPeerStateMaxId)) {
updateUserStoriesState(peer); updatePeerStoriesState(peer);
} }
for (const auto &storyId : base::take(_pendingReadTillItems)) { for (const auto &storyId : base::take(_pendingReadTillItems)) {
_owner->refreshStoryItemViews(storyId); _owner->refreshStoryItemViews(storyId);
@ -1745,24 +1744,22 @@ void Stories::sendPollingViewsRequests() {
_pollingViewsTimer.callOnce(kPollViewsInterval); _pollingViewsTimer.callOnce(kPollViewsInterval);
} }
void Stories::updateUserStoriesState(not_null<PeerData*> peer) { void Stories::updatePeerStoriesState(not_null<PeerData*> peer) {
const auto till = _readTill.find(peer->id); const auto till = _readTill.find(peer->id);
const auto readTill = (till != end(_readTill)) ? till->second : 0; const auto readTill = (till != end(_readTill)) ? till->second : 0;
const auto pendingMaxId = [&] { const auto pendingMaxId = [&] {
const auto j = _pendingUserStateMaxId.find(peer); const auto j = _pendingPeerStateMaxId.find(peer);
return (j != end(_pendingUserStateMaxId)) ? j->second : 0; return (j != end(_pendingPeerStateMaxId)) ? j->second : 0;
}; };
const auto i = _all.find(peer->id); const auto i = _all.find(peer->id);
const auto max = (i != end(_all)) const auto max = (i != end(_all))
? (i->second.ids.empty() ? 0 : i->second.ids.back().id) ? (i->second.ids.empty() ? 0 : i->second.ids.back().id)
: pendingMaxId(); : pendingMaxId();
if (const auto user = peer->asUser()) { peer->setStoriesState(!max
user->setStoriesState(!max ? PeerData::StoriesState::None
? UserData::StoriesState::None : (max <= readTill)
: (max <= readTill) ? PeerData::StoriesState::HasRead
? UserData::StoriesState::HasRead : PeerData::StoriesState::HasUnread);
: UserData::StoriesState::HasUnread);
}
} }
void Stories::preloadSourcesChanged(StorySourcesList list) { void Stories::preloadSourcesChanged(StorySourcesList list) {

View file

@ -52,7 +52,7 @@ struct StoriesSourceInfo {
}; };
struct StoriesSource { struct StoriesSource {
not_null<UserData*> user; not_null<PeerData*> peer;
base::flat_set<StoryIdDates> ids; base::flat_set<StoryIdDates> ids;
StoryId readTill = 0; StoryId readTill = 0;
bool hidden = false; bool hidden = false;
@ -148,7 +148,7 @@ public:
void apply(const MTPDupdateStory &data); void apply(const MTPDupdateStory &data);
void apply(const MTPDupdateReadStories &data); void apply(const MTPDupdateReadStories &data);
void apply(const MTPStoriesStealthMode &stealthMode); void apply(const MTPStoriesStealthMode &stealthMode);
void apply(not_null<PeerData*> peer, const MTPUserStories *data); void apply(not_null<PeerData*> peer, const MTPPeerStories *data);
Story *applyFromWebpage(PeerId peerId, const MTPstoryItem &story); Story *applyFromWebpage(PeerId peerId, const MTPstoryItem &story);
void loadAround(FullStoryId id, StoriesContext context); void loadAround(FullStoryId id, StoriesContext context);
@ -228,8 +228,8 @@ public:
[[nodiscard]] bool registerPolling(FullStoryId id, Polling polling); [[nodiscard]] bool registerPolling(FullStoryId id, Polling polling);
void unregisterPolling(FullStoryId id, Polling polling); void unregisterPolling(FullStoryId id, Polling polling);
void requestUserStories( void requestPeerStories(
not_null<UserData*> user, not_null<PeerData*> peer,
Fn<void()> done = nullptr); Fn<void()> done = nullptr);
void savedStateChanged(not_null<Story*> story); void savedStateChanged(not_null<Story*> story);
@ -255,7 +255,7 @@ private:
int viewer = 0; int viewer = 0;
}; };
void parseAndApply(const MTPUserStories &stories); void parseAndApply(const MTPPeerStories &stories);
[[nodiscard]] Story *parseAndApply( [[nodiscard]] Story *parseAndApply(
not_null<PeerData*> peer, not_null<PeerData*> peer,
const MTPDstoryItem &data, const MTPDstoryItem &data,
@ -269,7 +269,7 @@ private:
const QVector<MTPStoryItem> &list); const QVector<MTPStoryItem> &list);
void sendResolveRequests(); void sendResolveRequests();
void finalizeResolve(FullStoryId id); void finalizeResolve(FullStoryId id);
void updateUserStoriesState(not_null<PeerData*> peer); void updatePeerStoriesState(not_null<PeerData*> peer);
void applyDeleted(FullStoryId id); void applyDeleted(FullStoryId id);
void applyExpired(FullStoryId id); void applyExpired(FullStoryId id);
@ -360,8 +360,8 @@ private:
base::Timer _markReadTimer; base::Timer _markReadTimer;
base::flat_set<PeerId> _markReadRequests; base::flat_set<PeerId> _markReadRequests;
base::flat_map< base::flat_map<
not_null<UserData*>, not_null<PeerData*>,
std::vector<Fn<void()>>> _requestingUserStories; std::vector<Fn<void()>>> _requestingPeerStories;
base::flat_map<PeerId, base::flat_set<StoryId>> _incrementViewsPending; base::flat_map<PeerId, base::flat_set<StoryId>> _incrementViewsPending;
base::Timer _incrementViewsTimer; base::Timer _incrementViewsTimer;
@ -381,7 +381,7 @@ private:
base::flat_map<PeerId, StoryId> _readTill; base::flat_map<PeerId, StoryId> _readTill;
base::flat_set<FullStoryId> _pendingReadTillItems; base::flat_set<FullStoryId> _pendingReadTillItems;
base::flat_map<not_null<PeerData*>, StoryId> _pendingUserStateMaxId; base::flat_map<not_null<PeerData*>, StoryId> _pendingPeerStateMaxId;
mtpRequestId _readTillsRequestId = 0; mtpRequestId _readTillsRequestId = 0;
bool _readTillReceived = false; bool _readTillReceived = false;

View file

@ -66,6 +66,25 @@ using UpdateFlag = StoryUpdate::Flag;
}); });
}, [](const MTPDgeoPointEmpty &) { }, [](const MTPDgeoPointEmpty &) {
}); });
}, [&](const MTPDmediaAreaSuggestedReaction &data) {
}, [&](const MTPDinputMediaAreaVenue &data) {
LOG(("API Error: Unexpected inputMediaAreaVenue in API data."));
});
return result;
}
[[nodiscard]] auto ParseSuggestedReaction(const MTPMediaArea &area)
-> std::optional<SuggestedReaction> {
auto result = std::optional<SuggestedReaction>();
area.match([&](const MTPDmediaAreaVenue &data) {
}, [&](const MTPDmediaAreaGeoPoint &data) {
}, [&](const MTPDmediaAreaSuggestedReaction &data) {
result.emplace(SuggestedReaction{
.area = ParseArea(data.vcoordinates()),
.reaction = Data::ReactionFromMTP(data.vreaction()),
.flipped = data.is_flipped(),
.dark = data.is_dark(),
});
}, [&](const MTPDinputMediaAreaVenue &data) { }, [&](const MTPDinputMediaAreaVenue &data) {
LOG(("API Error: Unexpected inputMediaAreaVenue in API data.")); LOG(("API Error: Unexpected inputMediaAreaVenue in API data."));
}); });
@ -317,6 +336,10 @@ bool Story::edited() const {
return _edited; return _edited;
} }
bool Story::out() const {
return _out;
}
bool Story::canDownloadIfPremium() const { bool Story::canDownloadIfPremium() const {
return !forbidsForward() || _peer->isSelf(); return !forbidsForward() || _peer->isSelf();
} }
@ -455,6 +478,10 @@ const std::vector<StoryLocation> &Story::locations() const {
return _locations; return _locations;
} }
const std::vector<SuggestedReaction> &Story::suggestedReactions() const {
return _suggestedReactions;
}
void Story::applyChanges( void Story::applyChanges(
StoryMedia media, StoryMedia media,
const MTPDstoryItem &data, const MTPDstoryItem &data,
@ -486,6 +513,7 @@ void Story::applyFields(
? StoryPrivacy::SelectedContacts ? StoryPrivacy::SelectedContacts
: StoryPrivacy::Other; : StoryPrivacy::Other;
const auto noForwards = data.is_noforwards(); const auto noForwards = data.is_noforwards();
const auto out = data.is_min() ? _out : data.is_out();
auto caption = TextWithEntities{ auto caption = TextWithEntities{
data.vcaption().value_or_empty(), data.vcaption().value_or_empty(),
Api::EntitiesFromMTP( Api::EntitiesFromMTP(
@ -497,7 +525,7 @@ void Story::applyFields(
auto viewers = std::vector<not_null<PeerData*>>(); auto viewers = std::vector<not_null<PeerData*>>();
if (const auto info = data.vviews()) { if (const auto info = data.vviews()) {
views = info->data().vviews_count().v; views = info->data().vviews_count().v;
reactions = info->data().vreactions_count().v; reactions = info->data().vreactions_count().value_or_empty();
if (const auto list = info->data().vrecent_viewers()) { if (const auto list = info->data().vrecent_viewers()) {
viewers.reserve(list->v.size()); viewers.reserve(list->v.size());
auto &owner = _peer->owner(); auto &owner = _peer->owner();
@ -511,11 +539,15 @@ void Story::applyFields(
viewers = _recentViewers; viewers = _recentViewers;
} }
auto locations = std::vector<StoryLocation>(); auto locations = std::vector<StoryLocation>();
auto suggestedReactions = std::vector<SuggestedReaction>();
if (const auto areas = data.vmedia_areas()) { if (const auto areas = data.vmedia_areas()) {
locations.reserve(areas->v.size()); locations.reserve(areas->v.size());
suggestedReactions.reserve(areas->v.size());
for (const auto &area : areas->v) { for (const auto &area : areas->v) {
if (const auto location = ParseLocation(area)) { if (const auto location = ParseLocation(area)) {
locations.push_back(*location); locations.push_back(*location);
} else if (const auto reaction = ParseSuggestedReaction(area)) {
suggestedReactions.push_back(*reaction);
} }
} }
} }
@ -528,13 +560,15 @@ void Story::applyFields(
|| (_views.reactions != reactions) || (_views.reactions != reactions)
|| (_recentViewers != viewers); || (_recentViewers != viewers);
const auto locationsChanged = (_locations != locations); const auto locationsChanged = (_locations != locations);
const auto suggestedReactionsChanged
= (_suggestedReactions != suggestedReactions);
const auto reactionChanged = (_sentReactionId != reaction); const auto reactionChanged = (_sentReactionId != reaction);
_out = out;
_privacyPublic = (privacy == StoryPrivacy::Public); _privacyPublic = (privacy == StoryPrivacy::Public);
_privacyCloseFriends = (privacy == StoryPrivacy::CloseFriends); _privacyCloseFriends = (privacy == StoryPrivacy::CloseFriends);
_privacyContacts = (privacy == StoryPrivacy::Contacts); _privacyContacts = (privacy == StoryPrivacy::Contacts);
_privacySelectedContacts = (privacy == StoryPrivacy::SelectedContacts); _privacySelectedContacts = (privacy == StoryPrivacy::SelectedContacts);
_noForwards = noForwards;
_edited = edited; _edited = edited;
_pinned = pinned; _pinned = pinned;
_noForwards = noForwards; _noForwards = noForwards;
@ -553,6 +587,9 @@ void Story::applyFields(
if (locationsChanged) { if (locationsChanged) {
_locations = std::move(locations); _locations = std::move(locations);
} }
if (suggestedReactionsChanged) {
_suggestedReactions = std::move(suggestedReactions);
}
if (reactionChanged) { if (reactionChanged) {
_sentReactionId = reaction; _sentReactionId = reaction;
} }
@ -560,7 +597,8 @@ void Story::applyFields(
const auto changed = editedChanged const auto changed = editedChanged
|| captionChanged || captionChanged
|| mediaChanged || mediaChanged
|| locationsChanged; || locationsChanged
|| suggestedReactionsChanged;
if (!initial && (changed || viewsChanged || reactionChanged)) { if (!initial && (changed || viewsChanged || reactionChanged)) {
_peer->session().changes().storyUpdated(this, UpdateFlag() _peer->session().changes().storyUpdated(this, UpdateFlag()
| (changed ? UpdateFlag::Edited : UpdateFlag()) | (changed ? UpdateFlag::Edited : UpdateFlag())

View file

@ -96,6 +96,17 @@ struct StoryLocation {
const StoryLocation &) = default; const StoryLocation &) = default;
}; };
struct SuggestedReaction {
StoryArea area;
Data::ReactionId reaction;
bool flipped = false;
bool dark = false;
friend inline bool operator==(
const SuggestedReaction &,
const SuggestedReaction &) = default;
};
class Story final { class Story final {
public: public:
Story( Story(
@ -132,6 +143,7 @@ public:
[[nodiscard]] StoryPrivacy privacy() const; [[nodiscard]] StoryPrivacy privacy() const;
[[nodiscard]] bool forbidsForward() const; [[nodiscard]] bool forbidsForward() const;
[[nodiscard]] bool edited() const; [[nodiscard]] bool edited() const;
[[nodiscard]] bool out() const;
[[nodiscard]] bool canDownloadIfPremium() const; [[nodiscard]] bool canDownloadIfPremium() const;
[[nodiscard]] bool canDownloadChecked() const; [[nodiscard]] bool canDownloadChecked() const;
@ -157,6 +169,8 @@ public:
void applyViewsSlice(const QString &offset, const StoryViews &slice); void applyViewsSlice(const QString &offset, const StoryViews &slice);
[[nodiscard]] const std::vector<StoryLocation> &locations() const; [[nodiscard]] const std::vector<StoryLocation> &locations() const;
[[nodiscard]] auto suggestedReactions() const
-> const std::vector<SuggestedReaction> &;
void applyChanges( void applyChanges(
StoryMedia media, StoryMedia media,
@ -178,10 +192,12 @@ private:
TextWithEntities _caption; TextWithEntities _caption;
std::vector<not_null<PeerData*>> _recentViewers; std::vector<not_null<PeerData*>> _recentViewers;
std::vector<StoryLocation> _locations; std::vector<StoryLocation> _locations;
std::vector<SuggestedReaction> _suggestedReactions;
StoryViews _views; StoryViews _views;
const TimeId _date = 0; const TimeId _date = 0;
const TimeId _expires = 0; const TimeId _expires = 0;
TimeId _lastUpdateTime = 0; TimeId _lastUpdateTime = 0;
bool _out : 1 = false;
bool _pinned : 1 = false; bool _pinned : 1 = false;
bool _privacyPublic : 1 = false; bool _privacyPublic : 1 = false;
bool _privacyCloseFriends : 1 = false; bool _privacyCloseFriends : 1 = false;

View file

@ -127,18 +127,17 @@ void UserData::setPrivateForwardName(const QString &name) {
} }
bool UserData::hasActiveStories() const { bool UserData::hasActiveStories() const {
return flags() & UserDataFlag::HasActiveStories; return flags() & Flag::HasActiveStories;
} }
bool UserData::hasUnreadStories() const { bool UserData::hasUnreadStories() const {
return flags() & UserDataFlag::HasUnreadStories; return flags() & Flag::HasUnreadStories;
} }
void UserData::setStoriesState(StoriesState state) { void UserData::setStoriesState(StoriesState state) {
Expects(state != StoriesState::Unknown); Expects(state != StoriesState::Unknown);
const auto was = flags(); const auto was = flags();
using Flag = UserDataFlag;
switch (state) { switch (state) {
case StoriesState::None: case StoriesState::None:
_flags.remove(Flag::HasActiveStories | Flag::HasUnreadStories); _flags.remove(Flag::HasActiveStories | Flag::HasUnreadStories);

View file

@ -176,12 +176,6 @@ public:
[[nodiscard]] QString privateForwardName() const; [[nodiscard]] QString privateForwardName() const;
void setPrivateForwardName(const QString &name); void setPrivateForwardName(const QString &name);
enum class StoriesState {
Unknown,
None,
HasRead,
HasUnread,
};
[[nodiscard]] bool hasActiveStories() const; [[nodiscard]] bool hasActiveStories() const;
[[nodiscard]] bool hasUnreadStories() const; [[nodiscard]] bool hasUnreadStories() const;
void setStoriesState(StoriesState state); void setStoriesState(StoriesState state);

View file

@ -134,7 +134,7 @@ private:
const not_null<Data::Stories*> _data; const not_null<Data::Stories*> _data;
const Data::StorySourcesList _list; const Data::StorySourcesList _list;
base::flat_map< base::flat_map<
not_null<UserData*>, not_null<PeerData*>,
std::shared_ptr<Thumbnail>> _userpics; std::shared_ptr<Thumbnail>> _userpics;
}; };
@ -338,20 +338,20 @@ Content State::next() {
Assert(source != nullptr); Assert(source != nullptr);
auto userpic = std::shared_ptr<Thumbnail>(); auto userpic = std::shared_ptr<Thumbnail>();
const auto user = source->user; const auto peer = source->peer;
if (const auto i = _userpics.find(user); i != end(_userpics)) { if (const auto i = _userpics.find(peer); i != end(_userpics)) {
userpic = i->second; userpic = i->second;
} else { } else {
userpic = MakeUserpicThumbnail(user); userpic = MakeUserpicThumbnail(peer);
_userpics.emplace(user, userpic); _userpics.emplace(peer, userpic);
} }
result.elements.push_back({ result.elements.push_back({
.id = uint64(user->id.value), .id = uint64(peer->id.value),
.name = user->shortName(), .name = peer->shortName(),
.thumbnail = std::move(userpic), .thumbnail = std::move(userpic),
.count = info.count, .count = info.count,
.unreadCount = info.unreadCount, .unreadCount = info.unreadCount,
.skipSmall = user->isSelf() ? 1U : 0U, .skipSmall = peer->isSelf() ? 1U : 0U,
}); });
} }
return result; return result;

View file

@ -504,6 +504,7 @@ void ApiWrap::requestStoriesCount() {
Expects(_startProcess != nullptr); Expects(_startProcess != nullptr);
mainRequest(MTPstories_GetStoriesArchive( mainRequest(MTPstories_GetStoriesArchive(
MTP_inputPeerSelf(),
MTP_int(0), // offset_id MTP_int(0), // offset_id
MTP_int(0) // limit MTP_int(0) // limit
)).done([=](const MTPstories_Stories &result) { )).done([=](const MTPstories_Stories &result) {
@ -907,6 +908,7 @@ void ApiWrap::requestStories(
_storiesProcess->finish = std::move(finish); _storiesProcess->finish = std::move(finish);
mainRequest(MTPstories_GetStoriesArchive( mainRequest(MTPstories_GetStoriesArchive(
MTP_inputPeerSelf(),
MTP_int(_storiesProcess->offsetId), MTP_int(_storiesProcess->offsetId),
MTP_int(kStoriesSliceLimit) MTP_int(kStoriesSliceLimit)
)).done([=](const MTPstories_Stories &result) mutable { )).done([=](const MTPstories_Stories &result) mutable {
@ -993,6 +995,7 @@ void ApiWrap::finishStoriesSlice() {
} }
mainRequest(MTPstories_GetStoriesArchive( mainRequest(MTPstories_GetStoriesArchive(
MTP_inputPeerSelf(),
MTP_int(_storiesProcess->offsetId), MTP_int(_storiesProcess->offsetId),
MTP_int(kStoriesSliceLimit) MTP_int(kStoriesSliceLimit)
)).done([=](const MTPstories_Stories &result) { )).done([=](const MTPstories_Stories &result) {
@ -2186,7 +2189,7 @@ void ApiWrap::filePartRefreshReference(int64 offset) {
const auto &origin = _fileProcess->origin; const auto &origin = _fileProcess->origin;
if (origin.storyId) { if (origin.storyId) {
_fileProcess->requestId = mainRequest(MTPstories_GetStoriesByID( _fileProcess->requestId = mainRequest(MTPstories_GetStoriesByID(
MTP_inputUserSelf(), MTP_inputPeerSelf(),
MTP_vector<MTPint>(1, MTP_int(origin.storyId)) MTP_vector<MTPint>(1, MTP_int(origin.storyId))
)).fail([=](const MTP::Error &error) { )).fail([=](const MTP::Error &error) {
_fileProcess->requestId = 0; _fileProcess->requestId = 0;

View file

@ -287,7 +287,7 @@ std::unique_ptr<Data::Media> HistoryItem::CreateMedia(
media.vvalue().v); media.vvalue().v);
}, [&](const MTPDmessageMediaStory &media) -> Result { }, [&](const MTPDmessageMediaStory &media) -> Result {
return std::make_unique<Data::MediaStory>(item, FullStoryId{ return std::make_unique<Data::MediaStory>(item, FullStoryId{
peerFromUser(media.vuser_id()), peerFromMTP(media.vpeer()),
media.vid().v, media.vid().v,
}, media.is_via_mention()); }, media.is_via_mention());
}, [](const MTPDmessageMediaEmpty &) -> Result { }, [](const MTPDmessageMediaEmpty &) -> Result {
@ -1981,7 +1981,7 @@ bool HistoryItem::canBeEdited() const {
return true; return true;
} else if (out()) { } else if (out()) {
if (isPost()) { if (isPost()) {
return channel->canPublish(); return channel->canPostMessages();
} else if (const auto topic = this->topic()) { } else if (const auto topic = this->topic()) {
return Data::CanSendAnything(topic); return Data::CanSendAnything(topic);
} else { } else {
@ -2033,9 +2033,8 @@ bool HistoryItem::canDelete() const {
} }
if (channel->canDeleteMessages()) { if (channel->canDeleteMessages()) {
return true; return true;
} } else if (out() && !isService()) {
if (out() && !isService()) { return isPost() ? channel->canPostMessages() : true;
return isPost() ? channel->canPublish() : true;
} }
return false; return false;
} }

View file

@ -4575,7 +4575,7 @@ bool HistoryWidget::isChoosingTheme() const {
bool HistoryWidget::isMuteUnmute() const { bool HistoryWidget::isMuteUnmute() const {
return _peer return _peer
&& ((_peer->isBroadcast() && !_peer->asChannel()->canPublish()) && ((_peer->isBroadcast() && !_peer->asChannel()->canPostMessages())
|| (_peer->isGigagroup() && !Data::CanSendAnything(_peer)) || (_peer->isGigagroup() && !Data::CanSendAnything(_peer))
|| _peer->isRepliesChat()); || _peer->isRepliesChat());
} }

View file

@ -18,9 +18,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_peer.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_stories.h" #include "data/data_stories.h"
#include "data/data_user.h"
#include "history/view/reactions/history_view_reactions_strip.h" #include "history/view/reactions/history_view_reactions_strip.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "main/main_session.h" #include "main/main_session.h"
@ -62,7 +62,7 @@ constexpr auto kSiblingMultiplierMax = 0.72;
constexpr auto kSiblingOutsidePart = 0.24; constexpr auto kSiblingOutsidePart = 0.24;
constexpr auto kSiblingUserpicSize = 0.3; constexpr auto kSiblingUserpicSize = 0.3;
constexpr auto kInnerHeightMultiplier = 1.6; constexpr auto kInnerHeightMultiplier = 1.6;
constexpr auto kPreloadUsersCount = 3; constexpr auto kPreloadPeersCount = 3;
constexpr auto kPreloadStoriesCount = 5; constexpr auto kPreloadStoriesCount = 5;
constexpr auto kPreloadNextMediaCount = 3; constexpr auto kPreloadNextMediaCount = 3;
constexpr auto kPreloadPreviousMediaCount = 1; constexpr auto kPreloadPreviousMediaCount = 1;
@ -140,10 +140,10 @@ private:
class Controller::Unsupported final { class Controller::Unsupported final {
public: public:
Unsupported(not_null<Controller*> controller, not_null<UserData*> user); Unsupported(not_null<Controller*> controller, not_null<PeerData*> peer);
private: private:
void setup(not_null<UserData*> user); void setup(not_null<PeerData*> peer);
const not_null<Controller*> _controller; const not_null<Controller*> _controller;
std::unique_ptr<Ui::RpWidget> _bg; std::unique_ptr<Ui::RpWidget> _bg;
@ -206,13 +206,13 @@ void Controller::PhotoPlayback::callback() {
Controller::Unsupported::Unsupported( Controller::Unsupported::Unsupported(
not_null<Controller*> controller, not_null<Controller*> controller,
not_null<UserData*> user) not_null<PeerData*> peer)
: _controller(controller) : _controller(controller)
, _bgRound(st::storiesRadius, st::storiesComposeBg) { , _bgRound(st::storiesRadius, st::storiesComposeBg) {
setup(user); setup(peer);
} }
void Controller::Unsupported::setup(not_null<UserData*> user) { void Controller::Unsupported::setup(not_null<PeerData*> peer) {
const auto wrap = _controller->wrap(); const auto wrap = _controller->wrap();
_bg = std::make_unique<Ui::RpWidget>(wrap); _bg = std::make_unique<Ui::RpWidget>(wrap);
@ -596,8 +596,8 @@ void Controller::toggleLiked() {
void Controller::reactionChosen(ReactionsMode mode, ChosenReaction chosen) { void Controller::reactionChosen(ReactionsMode mode, ChosenReaction chosen) {
if (mode == ReactionsMode::Message) { if (mode == ReactionsMode::Message) {
_replyArea->sendReaction(chosen.id); _replyArea->sendReaction(chosen.id);
} else if (const auto user = shownUser()) { } else if (const auto peer = shownPeer()) {
user->owner().stories().sendReaction(_shown, chosen.id); peer->owner().stories().sendReaction(_shown, chosen.id);
} }
unfocusReply(); unfocusReply();
} }
@ -638,11 +638,11 @@ auto Controller::cachedReactionIconFactory() const
} }
void Controller::rebuildFromContext( void Controller::rebuildFromContext(
not_null<UserData*> user, not_null<PeerData*> peer,
FullStoryId storyId) { FullStoryId storyId) {
using namespace Data; using namespace Data;
auto &stories = user->owner().stories(); auto &stories = peer->owner().stories();
auto list = std::optional<StoriesList>(); auto list = std::optional<StoriesList>();
auto source = (const StoriesSource*)nullptr; auto source = (const StoriesSource*)nullptr;
const auto peerId = storyId.peer; const auto peerId = storyId.peer;
@ -659,7 +659,7 @@ void Controller::rebuildFromContext(
const auto i = ids.find(id); const auto i = ids.find(id);
if (i != end(ids)) { if (i != end(ids)) {
list = StoriesList{ list = StoriesList{
.user = user, .peer = peer,
.ids = *saved, .ids = *saved,
.total = stories.savedCount(peerId), .total = stories.savedCount(peerId),
}; };
@ -673,7 +673,7 @@ void Controller::rebuildFromContext(
} }
hideSiblings(); hideSiblings();
}, [&](StoriesContextArchive) { }, [&](StoriesContextArchive) {
Expects(user->isSelf()); Expects(peer->isSelf());
if (stories.archiveCountKnown()) { if (stories.archiveCountKnown()) {
const auto &archive = stories.archive(); const auto &archive = stories.archive();
@ -681,7 +681,7 @@ void Controller::rebuildFromContext(
const auto i = ids.find(id); const auto i = ids.find(id);
if (i != end(ids)) { if (i != end(ids)) {
list = StoriesList{ list = StoriesList{
.user = user, .peer = peer,
.ids = archive, .ids = archive,
.total = stories.archiveCount(), .total = stories.archiveCount(),
}; };
@ -706,8 +706,8 @@ void Controller::rebuildFromContext(
} }
rebuildCachedSourcesList(sources, (i - begin(sources))); rebuildCachedSourcesList(sources, (i - begin(sources)));
_cachedSourcesList[_cachedSourceIndex].shownId = storyId.story; _cachedSourcesList[_cachedSourceIndex].shownId = storyId.story;
showSiblings(&user->session()); showSiblings(&peer->session());
if (int(sources.end() - i) < kPreloadUsersCount) { if (int(sources.end() - i) < kPreloadPeersCount) {
stories.loadMore(list); stories.loadMore(list);
} }
} }
@ -719,7 +719,7 @@ void Controller::rebuildFromContext(
if (_list != list) { if (_list != list) {
_list = std::move(list); _list = std::move(list);
} }
if (const auto maybe = user->owner().stories().lookup(storyId)) { if (const auto maybe = peer->owner().stories().lookup(storyId)) {
const auto now = *maybe; const auto now = *maybe;
const auto range = ComputeSameDayRange(now, _list->ids, _index); const auto range = ComputeSameDayRange(now, _list->ids, _index);
_sliderCount = range.till - range.from + 1; _sliderCount = range.till - range.from + 1;
@ -737,7 +737,7 @@ void Controller::rebuildFromContext(
if (!source) { if (!source) {
_source = std::nullopt; _source = std::nullopt;
_list = StoriesList{ _list = StoriesList{
.user = user, .peer = peer,
.ids = { { id } }, .ids = { { id } },
.total = 1, .total = 1,
}; };
@ -761,17 +761,17 @@ void Controller::preloadNext() {
auto ids = std::vector<FullStoryId>(); auto ids = std::vector<FullStoryId>();
ids.reserve(kPreloadPreviousMediaCount + kPreloadNextMediaCount); ids.reserve(kPreloadPreviousMediaCount + kPreloadNextMediaCount);
const auto user = shownUser(); const auto peer = shownPeer();
const auto count = shownCount(); const auto count = shownCount();
const auto till = std::min(_index + kPreloadNextMediaCount, count); const auto till = std::min(_index + kPreloadNextMediaCount, count);
for (auto i = _index + 1; i != till; ++i) { for (auto i = _index + 1; i != till; ++i) {
ids.push_back({ .peer = user->id, .story = shownId(i) }); ids.push_back({ .peer = peer->id, .story = shownId(i) });
} }
const auto from = std::max(_index - kPreloadPreviousMediaCount, 0); const auto from = std::max(_index - kPreloadPreviousMediaCount, 0);
for (auto i = _index; i != from;) { for (auto i = _index; i != from;) {
ids.push_back({ .peer = user->id, .story = shownId(--i) }); ids.push_back({ .peer = peer->id, .story = shownId(--i) });
} }
user->owner().stories().setPreloadingInViewer(std::move(ids)); peer->owner().stories().setPreloadingInViewer(std::move(ids));
} }
void Controller::checkMoveByDelta() { void Controller::checkMoveByDelta() {
@ -786,18 +786,18 @@ void Controller::show(
Data::StoriesContext context) { Data::StoriesContext context) {
auto &stories = story->owner().stories(); auto &stories = story->owner().stories();
const auto storyId = story->fullId(); const auto storyId = story->fullId();
const auto user = story->peer()->asUser(); const auto peer = story->peer();
_context = context; _context = context;
_waitingForId = {}; _waitingForId = {};
_waitingForDelta = 0; _waitingForDelta = 0;
rebuildFromContext(user, storyId); rebuildFromContext(peer, storyId);
_contextLifetime.destroy(); _contextLifetime.destroy();
const auto subscribeToSource = [&] { const auto subscribeToSource = [&] {
stories.sourceChanged() | rpl::filter( stories.sourceChanged() | rpl::filter(
rpl::mappers::_1 == storyId.peer rpl::mappers::_1 == storyId.peer
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
rebuildFromContext(user, storyId); rebuildFromContext(peer, storyId);
}, _contextLifetime); }, _contextLifetime);
}; };
v::match(_context.data, [&](Data::StoriesContextSingle) { v::match(_context.data, [&](Data::StoriesContextSingle) {
@ -807,13 +807,13 @@ void Controller::show(
stories.savedChanged() | rpl::filter( stories.savedChanged() | rpl::filter(
rpl::mappers::_1 == storyId.peer rpl::mappers::_1 == storyId.peer
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
rebuildFromContext(user, storyId); rebuildFromContext(peer, storyId);
checkMoveByDelta(); checkMoveByDelta();
}, _contextLifetime); }, _contextLifetime);
}, [&](Data::StoriesContextArchive) { }, [&](Data::StoriesContextArchive) {
stories.archiveChanged( stories.archiveChanged(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
rebuildFromContext(user, storyId); rebuildFromContext(peer, storyId);
checkMoveByDelta(); checkMoveByDelta();
}, _contextLifetime); }, _contextLifetime);
}, [&](Data::StorySourcesList) { }, [&](Data::StorySourcesList) {
@ -834,7 +834,7 @@ void Controller::show(
if (!unsupported) { if (!unsupported) {
_unsupported = nullptr; _unsupported = nullptr;
} else { } else {
_unsupported = std::make_unique<Unsupported>(this, user); _unsupported = std::make_unique<Unsupported>(this, peer);
_header->raise(); _header->raise();
_slider->raise(); _slider->raise();
} }
@ -845,7 +845,7 @@ void Controller::show(
_contentFadeAnimation.stop(); _contentFadeAnimation.stop();
const auto document = story->document(); const auto document = story->document();
_header->show({ _header->show({
.user = user, .peer = peer,
.date = story->date(), .date = story->date(),
.fullIndex = _sliderCount ? _index : 0, .fullIndex = _sliderCount ? _index : 0,
.fullCount = _sliderCount ? shownCount() : 0, .fullCount = _sliderCount ? shownCount() : 0,
@ -866,7 +866,7 @@ void Controller::show(
} }
_replyArea->show({ _replyArea->show({
.user = unsupported ? nullptr : user, .peer = unsupported ? nullptr : peer.get(),
.id = story->id(), .id = story->id(),
}, _reactions->likedValue()); }, _reactions->likedValue());
@ -874,13 +874,13 @@ void Controller::show(
.list = story->recentViewers(), .list = story->recentViewers(),
.reactions = story->reactions(), .reactions = story->reactions(),
.total = story->views(), .total = story->views(),
.valid = user->isSelf(), .valid = peer->isSelf(),
}); });
stories.loadAround(storyId, context); stories.loadAround(storyId, context);
updatePlayingAllowed(); updatePlayingAllowed();
user->updateFull(); peer->updateFull();
} }
bool Controller::changeShown(Data::Story *story) { bool Controller::changeShown(Data::Story *story) {
@ -1129,7 +1129,7 @@ void Controller::markAsRead() {
return; return;
} }
_viewed = true; _viewed = true;
shownUser()->owner().stories().markAsRead(_shown, _started); shownPeer()->owner().stories().markAsRead(_shown, _started);
} }
bool Controller::subjumpAvailable(int delta) const { bool Controller::subjumpAvailable(int delta) const {
@ -1169,12 +1169,12 @@ void Controller::subjumpTo(int index) {
Expects(shown()); Expects(shown());
Expects(index >= 0 && index < shownCount()); Expects(index >= 0 && index < shownCount());
const auto user = shownUser(); const auto peer = shownPeer();
const auto id = FullStoryId{ const auto id = FullStoryId{
.peer = user->id, .peer = peer->id,
.story = shownId(index), .story = shownId(index),
}; };
auto &stories = user->owner().stories(); auto &stories = peer->owner().stories();
if (!id.story) { if (!id.story) {
const auto delta = index - _index; const auto delta = index - _index;
if (_waitingForDelta != delta) { if (_waitingForDelta != delta) {
@ -1183,7 +1183,7 @@ void Controller::subjumpTo(int index) {
loadMoreToList(); loadMoreToList();
} }
} else if (stories.lookup(id)) { } else if (stories.lookup(id)) {
_delegate->storiesJumpTo(&user->session(), id, _context); _delegate->storiesJumpTo(&peer->session(), id, _context);
} else if (_waitingForId != id) { } else if (_waitingForId != id) {
_waitingForId = id; _waitingForId = id;
_waitingForDelta = 0; _waitingForDelta = 0;
@ -1195,8 +1195,8 @@ void Controller::checkWaitingFor() {
Expects(_waitingForId.valid()); Expects(_waitingForId.valid());
Expects(shown()); Expects(shown());
const auto user = shownUser(); const auto peer = shownPeer();
auto &stories = user->owner().stories(); auto &stories = peer->owner().stories();
const auto maybe = stories.lookup(_waitingForId); const auto maybe = stories.lookup(_waitingForId);
if (!maybe) { if (!maybe) {
if (maybe.error() == Data::NoStory::Deleted) { if (maybe.error() == Data::NoStory::Deleted) {
@ -1205,7 +1205,7 @@ void Controller::checkWaitingFor() {
return; return;
} }
_delegate->storiesJumpTo( _delegate->storiesJumpTo(
&user->session(), &peer->session(),
base::take(_waitingForId), base::take(_waitingForId),
_context); _context);
} }
@ -1290,8 +1290,8 @@ const Data::StoryViews &Controller::views(int limit, bool initial) {
if (_viewsSlice.total > _viewsSlice.list.size() if (_viewsSlice.total > _viewsSlice.list.size()
&& _viewsSlice.list.size() < limit) { && _viewsSlice.list.size() < limit) {
const auto done = viewsGotMoreCallback(); const auto done = viewsGotMoreCallback();
const auto user = shownUser(); const auto peer = shownPeer();
auto &stories = user->owner().stories(); auto &stories = peer->owner().stories();
stories.loadViewsSlice(_shown.story, _viewsSlice.nextOffset, done); stories.loadViewsSlice(_shown.story, _viewsSlice.nextOffset, done);
} }
return _viewsSlice; return _viewsSlice;
@ -1304,8 +1304,8 @@ rpl::producer<> Controller::moreViewsLoaded() const {
Fn<void(Data::StoryViews)> Controller::viewsGotMoreCallback() { Fn<void(Data::StoryViews)> Controller::viewsGotMoreCallback() {
return crl::guard(&_viewsLoadGuard, [=](Data::StoryViews result) { return crl::guard(&_viewsLoadGuard, [=](Data::StoryViews result) {
if (_viewsSlice.list.empty()) { if (_viewsSlice.list.empty()) {
const auto user = shownUser(); const auto peer = shownPeer();
auto &stories = user->owner().stories(); auto &stories = peer->owner().stories();
if (const auto maybeStory = stories.lookup(_shown)) { if (const auto maybeStory = stories.lookup(_shown)) {
_viewsSlice = (*maybeStory)->viewsList(); _viewsSlice = (*maybeStory)->viewsList();
} else { } else {
@ -1329,11 +1329,11 @@ bool Controller::shown() const {
return _source || _list; return _source || _list;
} }
UserData *Controller::shownUser() const { PeerData *Controller::shownPeer() const {
return _source return _source
? _source->user.get() ? _source->peer.get()
: _list : _list
? _list->user.get() ? _list->peer.get()
: nullptr; : nullptr;
} }
@ -1356,13 +1356,13 @@ void Controller::loadMoreToList() {
using namespace Data; using namespace Data;
const auto user = shownUser(); const auto peer = shownPeer();
const auto peerId = _shown.peer; const auto peerId = _shown.peer;
auto &stories = user->owner().stories(); auto &stories = peer->owner().stories();
v::match(_context.data, [&](StoriesContextSaved) { v::match(_context.data, [&](StoriesContextSaved) {
stories.savedLoadMore(peerId); stories.savedLoadMore(peerId);
}, [&](StoriesContextArchive) { }, [&](StoriesContextArchive) {
Expects(user->isSelf()); Expects(peer->isSelf());
stories.archiveLoadMore(); stories.archiveLoadMore();
}, [](const auto &) { }, [](const auto &) {
@ -1462,10 +1462,10 @@ void Controller::rebuildCachedSourcesList(
void Controller::refreshViewsFromData() { void Controller::refreshViewsFromData() {
Expects(shown()); Expects(shown());
const auto user = shownUser(); const auto peer = shownPeer();
auto &stories = user->owner().stories(); auto &stories = peer->owner().stories();
const auto maybeStory = stories.lookup(_shown); const auto maybeStory = stories.lookup(_shown);
if (!maybeStory || !user->isSelf()) { if (!maybeStory || !peer->isSelf()) {
_viewsSlice = {}; _viewsSlice = {};
} else { } else {
_viewsSlice = (*maybeStory)->viewsList(); _viewsSlice = (*maybeStory)->viewsList();

View file

@ -181,7 +181,7 @@ private:
class Unsupported; class Unsupported;
using ChosenReaction = HistoryView::Reactions::ChosenReaction; using ChosenReaction = HistoryView::Reactions::ChosenReaction;
struct StoriesList { struct StoriesList {
not_null<UserData*> user; not_null<PeerData*> peer;
Data::StoriesIds ids; Data::StoriesIds ids;
int total = 0; int total = 0;
@ -233,10 +233,10 @@ private:
-> Fn<void(Data::StoryViews)>; -> Fn<void(Data::StoryViews)>;
[[nodiscard]] bool shown() const; [[nodiscard]] bool shown() const;
[[nodiscard]] UserData *shownUser() const; [[nodiscard]] PeerData *shownPeer() const;
[[nodiscard]] int shownCount() const; [[nodiscard]] int shownCount() const;
[[nodiscard]] StoryId shownId(int index) const; [[nodiscard]] StoryId shownId(int index) const;
void rebuildFromContext(not_null<UserData*> user, FullStoryId storyId); void rebuildFromContext(not_null<PeerData*> peer, FullStoryId storyId);
void checkMoveByDelta(); void checkMoveByDelta();
void loadMoreToList(); void loadMoreToList();
void preloadNext(); void preloadNext();

View file

@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h" #include "base/unixtime.h"
#include "chat_helpers/compose/compose_show.h" #include "chat_helpers/compose/compose_show.h"
#include "data/data_user.h" #include "data/data_peer.h"
#include "media/stories/media_stories_controller.h" #include "media/stories/media_stories_controller.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "ui/controls/userpic_button.h" #include "ui/controls/userpic_button.h"
@ -268,7 +268,7 @@ void Header::show(HeaderData data) {
if (_data == data) { if (_data == data) {
return; return;
} }
const auto userChanged = !_data || (_data->user != data.user); const auto peerChanged = !_data || (_data->peer != data.peer);
_data = data; _data = data;
const auto updateInfoGeometry = [=] { const auto updateInfoGeometry = [=] {
if (_name && _date) { if (_name && _date) {
@ -282,7 +282,7 @@ void Header::show(HeaderData data) {
}; };
_tooltip = nullptr; _tooltip = nullptr;
_tooltipShown = false; _tooltipShown = false;
if (userChanged) { if (peerChanged) {
_volume = nullptr; _volume = nullptr;
_date = nullptr; _date = nullptr;
_name = nullptr; _name = nullptr;
@ -298,12 +298,12 @@ void Header::show(HeaderData data) {
_info = std::make_unique<Ui::AbstractButton>(raw); _info = std::make_unique<Ui::AbstractButton>(raw);
_info->setClickedCallback([=] { _info->setClickedCallback([=] {
_controller->uiShow()->show(PrepareShortInfoBox(_data->user)); _controller->uiShow()->show(PrepareShortInfoBox(_data->peer));
}); });
_userpic = std::make_unique<Ui::UserpicButton>( _userpic = std::make_unique<Ui::UserpicButton>(
raw, raw,
data.user, data.peer,
st::storiesHeaderPhoto); st::storiesHeaderPhoto);
_userpic->setAttribute(Qt::WA_TransparentForMouseEvents); _userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
_userpic->show(); _userpic->show();
@ -313,9 +313,9 @@ void Header::show(HeaderData data) {
_name = std::make_unique<Ui::FlatLabel>( _name = std::make_unique<Ui::FlatLabel>(
raw, raw,
rpl::single(data.user->isSelf() rpl::single(data.peer->isSelf()
? tr::lng_stories_my_name(tr::now) ? tr::lng_stories_my_name(tr::now)
: data.user->name()), : data.peer->name()),
st::storiesHeaderName); st::storiesHeaderName);
_name->setAttribute(Qt::WA_TransparentForMouseEvents); _name->setAttribute(Qt::WA_TransparentForMouseEvents);
_name->setOpacity(kNameOpacity); _name->setOpacity(kNameOpacity);
@ -605,8 +605,8 @@ void Header::toggleTooltip(Tooltip type, bool show) {
} }
const auto text = [&]() -> TextWithEntities { const auto text = [&]() -> TextWithEntities {
using Privacy = Data::StoryPrivacy; using Privacy = Data::StoryPrivacy;
const auto boldName = Ui::Text::Bold(_data->user->shortName()); const auto boldName = Ui::Text::Bold(_data->peer->shortName());
const auto self = _data->user->isSelf(); const auto self = _data->peer->isSelf();
switch (type) { switch (type) {
case Tooltip::SilentVideo: case Tooltip::SilentVideo:
return { tr::lng_stories_about_silent(tr::now) }; return { tr::lng_stories_about_silent(tr::now) };

View file

@ -31,7 +31,7 @@ class Controller;
enum class PauseState; enum class PauseState;
struct HeaderData { struct HeaderData {
not_null<UserData*> user; not_null<PeerData*> peer;
TimeId date = 0; TimeId date = 0;
int fullIndex = 0; int fullIndex = 0;
int fullCount = 0; int fullCount = 0;

View file

@ -171,13 +171,13 @@ void ReplyArea::initGeometry() {
} }
void ReplyArea::sendReaction(const Data::ReactionId &id) { void ReplyArea::sendReaction(const Data::ReactionId &id) {
Expects(_data.user != nullptr); Expects(_data.peer != nullptr);
auto message = Api::MessageToSend(prepareSendAction({})); auto message = Api::MessageToSend(prepareSendAction({}));
if (const auto emoji = id.emoji(); !emoji.isEmpty()) { if (const auto emoji = id.emoji(); !emoji.isEmpty()) {
message.textWithTags = { emoji }; message.textWithTags = { emoji };
} else if (const auto customId = id.custom()) { } else if (const auto customId = id.custom()) {
const auto document = _data.user->owner().document(customId); const auto document = _data.peer->owner().document(customId);
if (const auto sticker = document->sticker()) { if (const auto sticker = document->sticker()) {
const auto text = sticker->alt; const auto text = sticker->alt;
const auto id = Data::SerializeCustomEmojiId(customId); const auto id = Data::SerializeCustomEmojiId(customId);
@ -207,7 +207,7 @@ void ReplyArea::send(
Api::SendOptions options, Api::SendOptions options,
bool skipToast) { bool skipToast) {
const auto error = GetErrorTextForSending( const auto error = GetErrorTextForSending(
_data.user, _data.peer,
{ {
.topicRootId = MsgId(0), .topicRootId = MsgId(0),
.text = &message.textWithTags, .text = &message.textWithTags,
@ -239,11 +239,11 @@ bool ReplyArea::sendExistingDocument(
not_null<DocumentData*> document, not_null<DocumentData*> document,
Api::SendOptions options, Api::SendOptions options,
std::optional<MsgId> localId) { std::optional<MsgId> localId) {
Expects(_data.user != nullptr); Expects(_data.peer != nullptr);
const auto show = _controller->uiShow(); const auto show = _controller->uiShow();
const auto error = Data::RestrictionError( const auto error = Data::RestrictionError(
_data.user, _data.peer,
ChatRestriction::SendStickers); ChatRestriction::SendStickers);
if (error) { if (error) {
show->showToast(*error); show->showToast(*error);
@ -269,11 +269,11 @@ void ReplyArea::sendExistingPhoto(not_null<PhotoData*> photo) {
bool ReplyArea::sendExistingPhoto( bool ReplyArea::sendExistingPhoto(
not_null<PhotoData*> photo, not_null<PhotoData*> photo,
Api::SendOptions options) { Api::SendOptions options) {
Expects(_data.user != nullptr); Expects(_data.peer != nullptr);
const auto show = _controller->uiShow(); const auto show = _controller->uiShow();
const auto error = Data::RestrictionError( const auto error = Data::RestrictionError(
_data.user, _data.peer,
ChatRestriction::SendPhotos); ChatRestriction::SendPhotos);
if (error) { if (error) {
show->showToast(*error); show->showToast(*error);
@ -348,7 +348,7 @@ bool ReplyArea::showSendingFilesError(
const Ui::PreparedList &list, const Ui::PreparedList &list,
std::optional<bool> compress) const { std::optional<bool> compress) const {
const auto text = [&] { const auto text = [&] {
const auto peer = _data.user; const auto peer = _data.peer;
const auto error = Data::FileRestrictionError(peer, list, compress); const auto error = Data::FileRestrictionError(peer, list, compress);
if (error) { if (error) {
return *error; return *error;
@ -383,29 +383,29 @@ bool ReplyArea::showSendingFilesError(
} }
not_null<History*> ReplyArea::history() const { not_null<History*> ReplyArea::history() const {
Expects(_data.user != nullptr); Expects(_data.peer != nullptr);
return _data.user->owner().history(_data.user); return _data.peer->owner().history(_data.peer);
} }
Api::SendAction ReplyArea::prepareSendAction( Api::SendAction ReplyArea::prepareSendAction(
Api::SendOptions options) const { Api::SendOptions options) const {
Expects(_data.user != nullptr); Expects(_data.peer != nullptr);
auto result = Api::SendAction(history(), options); auto result = Api::SendAction(history(), options);
result.options.sendAs = _controls->sendAsPeer(); result.options.sendAs = _controls->sendAsPeer();
result.replyTo.storyId = { .peer = _data.user->id, .story = _data.id }; result.replyTo.storyId = { .peer = _data.peer->id, .story = _data.id };
return result; return result;
} }
void ReplyArea::chooseAttach( void ReplyArea::chooseAttach(
std::optional<bool> overrideSendImagesAsPhotos) { std::optional<bool> overrideSendImagesAsPhotos) {
_chooseAttachRequest = false; _chooseAttachRequest = false;
if (!_data.user) { if (!_data.peer) {
return; return;
} }
const auto user = not_null(_data.user); const auto peer = not_null(_data.peer);
if (const auto error = Data::AnyFileRestrictionError(user)) { if (const auto error = Data::AnyFileRestrictionError(peer)) {
_controller->uiShow()->showToast(*error); _controller->uiShow()->showToast(*error);
return; return;
} }
@ -413,7 +413,7 @@ void ReplyArea::chooseAttach(
const auto filter = (overrideSendImagesAsPhotos == true) const auto filter = (overrideSendImagesAsPhotos == true)
? FileDialog::ImagesOrAllFilter() ? FileDialog::ImagesOrAllFilter()
: FileDialog::AllOrImagesFilter(); : FileDialog::AllOrImagesFilter();
const auto weak = make_weak(&_shownUserGuard); const auto weak = make_weak(&_shownPeerGuard);
const auto callback = [=](FileDialog::OpenResult &&result) { const auto callback = [=](FileDialog::OpenResult &&result) {
const auto guard = gsl::finally([&] { const auto guard = gsl::finally([&] {
_choosingAttach = false; _choosingAttach = false;
@ -504,8 +504,8 @@ bool ReplyArea::confirmSendingFiles(
.show = show, .show = show,
.list = std::move(list), .list = std::move(list),
.caption = _controls->getTextWithAppliedMarkdown(), .caption = _controls->getTextWithAppliedMarkdown(),
.limits = DefaultLimitsForPeer(_data.user), .limits = DefaultLimitsForPeer(_data.peer),
.check = DefaultCheckForPeer(show, _data.user), .check = DefaultCheckForPeer(show, _data.peer),
.sendType = Api::SendType::Normal, .sendType = Api::SendType::Normal,
.sendMenuType = SendMenu::Type::SilentOnly, .sendMenuType = SendMenu::Type::SilentOnly,
.stOverride = &st::storiesComposeControls, .stOverride = &st::storiesComposeControls,
@ -533,7 +533,7 @@ void ReplyArea::sendingFilesConfirmed(
auto groups = DivideByGroups( auto groups = DivideByGroups(
std::move(list), std::move(list),
way, way,
_data.user->slowmodeApplied()); _data.peer->slowmodeApplied());
const auto type = way.sendImagesAsPhotos() const auto type = way.sendImagesAsPhotos()
? SendMediaType::Photo ? SendMediaType::Photo
: SendMediaType::File; : SendMediaType::File;
@ -655,17 +655,17 @@ void ReplyArea::show(
if (_data == data) { if (_data == data) {
return; return;
} }
const auto userChanged = (_data.user != data.user); const auto peerChanged = (_data.peer != data.peer);
_data = data; _data = data;
if (!userChanged) { if (!peerChanged) {
if (_data.user) { if (_data.peer) {
_controls->clear(); _controls->clear();
} }
return; return;
} }
invalidate_weak_ptrs(&_shownUserGuard); invalidate_weak_ptrs(&_shownPeerGuard);
const auto user = data.user; const auto peer = data.peer;
const auto history = user ? user->owner().history(user).get() : nullptr; const auto history = peer ? peer->owner().history(peer).get() : nullptr;
_controls->setHistory({ _controls->setHistory({
.history = history, .history = history,
.liked = std::move( .liked = std::move(
@ -675,8 +675,8 @@ void ReplyArea::show(
}), }),
}); });
_controls->clear(); _controls->clear();
const auto hidden = user && user->isSelf(); const auto hidden = peer && peer->isSelf();
const auto cant = !user || user->isServiceUser(); const auto cant = !peer || peer->isServiceUser();
if (!hidden && !cant) { if (!hidden && !cant) {
_controls->show(); _controls->show();
} else { } else {
@ -698,9 +698,9 @@ void ReplyArea::show(
} }
Main::Session &ReplyArea::session() const { Main::Session &ReplyArea::session() const {
Expects(_data.user != nullptr); Expects(_data.peer != nullptr);
return _data.user->session(); return _data.peer->session();
} }
bool ReplyArea::focused() const { bool ReplyArea::focused() const {

View file

@ -49,7 +49,7 @@ namespace Media::Stories {
class Controller; class Controller;
struct ReplyAreaData { struct ReplyAreaData {
UserData *user = nullptr; PeerData *peer = nullptr;
StoryId id = 0; StoryId id = 0;
friend inline auto operator<=>(ReplyAreaData, ReplyAreaData) = default; friend inline auto operator<=>(ReplyAreaData, ReplyAreaData) = default;
@ -148,7 +148,7 @@ private:
std::unique_ptr<Cant> _cant; std::unique_ptr<Cant> _cant;
ReplyAreaData _data; ReplyAreaData _data;
base::has_weak_ptr _shownUserGuard; base::has_weak_ptr _shownPeerGuard;
bool _chooseAttachRequest = false; bool _chooseAttachRequest = false;
rpl::variable<bool> _choosingAttach; rpl::variable<bool> _choosingAttach;

View file

@ -15,10 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat_participant_status.h" #include "data/data_chat_participant_status.h"
#include "data/data_forum_topic.h" #include "data/data_forum_topic.h"
#include "data/data_histories.h" #include "data/data_histories.h"
#include "data/data_peer.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_stories.h" #include "data/data_stories.h"
#include "data/data_thread.h" #include "data/data_thread.h"
#include "data/data_user.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item_helpers.h" // GetErrorTextForSending. #include "history/history_item_helpers.h" // GetErrorTextForSending.
#include "history/view/history_view_context_menu.h" // CopyStoryLink. #include "history/view/history_view_context_menu.h" // CopyStoryLink.
@ -79,9 +79,7 @@ namespace Media::Stories {
if (!story) { if (!story) {
return; return;
} }
const auto user = story->peer()->asUser(); const auto peer = story->peer();
Assert(user != nullptr);
const auto error = [&] { const auto error = [&] {
for (const auto thread : result) { for (const auto thread : result) {
const auto error = GetErrorTextForSending( const auto error = GetErrorTextForSending(
@ -115,14 +113,16 @@ namespace Media::Stories {
message.action.clearDraft = false; message.action.clearDraft = false;
api->sendMessage(std::move(message)); api->sendMessage(std::move(message));
} }
const auto peer = thread->peer(); const auto threadPeer = thread->peer();
const auto threadHistory = thread->owningHistory(); const auto threadHistory = thread->owningHistory();
const auto randomId = base::RandomValue<uint64>(); const auto randomId = base::RandomValue<uint64>();
auto sendFlags = MTPmessages_SendMedia::Flags(0); auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (action.replyTo) { if (action.replyTo) {
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to; sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
} }
const auto silentPost = ShouldSendSilent(peer, action.options); const auto silentPost = ShouldSendSilent(
threadPeer,
action.options);
if (silentPost) { if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent; sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
} }
@ -140,23 +140,23 @@ namespace Media::Stories {
randomId, randomId,
Data::Histories::PrepareMessage<MTPmessages_SendMedia>( Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(sendFlags), MTP_flags(sendFlags),
peer->input, threadPeer->input,
Data::Histories::ReplyToPlaceholder(), Data::Histories::ReplyToPlaceholder(),
MTP_inputMediaStory( MTP_inputMediaStory(peer->input, MTP_int(id.story)),
user->inputUser,
MTP_int(id.story)),
MTPstring(), MTPstring(),
MTP_long(randomId), MTP_long(randomId),
MTPReplyMarkup(), MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(), MTPVector<MTPMessageEntity>(),
MTP_int(action.options.scheduled), MTP_int(action.options.scheduled),
MTP_inputPeerEmpty() MTP_inputPeerEmpty()
), [=](const MTPUpdates &result, const MTP::Response &response) { ), [=](
done(); const MTPUpdates &result,
}, [=](const MTP::Error &error, const MTP::Response &response) { const MTP::Response &response) {
api->sendMessageFail(error, peer, randomId); done();
done(); }, [=](const MTP::Error &error, const MTP::Response &response) {
}); api->sendMessageFail(error, threadPeer, randomId);
done();
});
++state->requests; ++state->requests;
} }
}; };

View file

@ -11,10 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_document_media.h" #include "data/data_document_media.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_peer.h"
#include "data/data_photo.h" #include "data/data_photo.h"
#include "data/data_photo_media.h" #include "data/data_photo_media.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "media/stories/media_stories_controller.h" #include "media/stories/media_stories_controller.h"
@ -244,8 +244,8 @@ Sibling::Sibling(
const Data::StoriesSource &source, const Data::StoriesSource &source,
StoryId suggestedId) StoryId suggestedId)
: _controller(controller) : _controller(controller)
, _id{ source.user->id, LookupShownId(source, suggestedId) } , _id{ source.peer->id, LookupShownId(source, suggestedId) }
, _peer(source.user) { , _peer(source.peer) {
checkStory(); checkStory();
_goodShown.stop(); _goodShown.stop();
} }
@ -305,7 +305,7 @@ bool Sibling::shows(
const Data::StoriesSource &source, const Data::StoriesSource &source,
StoryId suggestedId) const { StoryId suggestedId) const {
const auto fullId = FullStoryId{ const auto fullId = FullStoryId{
source.user->id, source.peer->id,
LookupShownId(source, suggestedId), LookupShownId(source, suggestedId),
}; };
return (_id == fullId); return (_id == fullId);

View file

@ -42,7 +42,7 @@ inputMediaInvoice#8eb5a6d5 flags:# title:string description:string photo:flags.0
inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia; inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia;
inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia; inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia;
inputMediaDice#e66fbf7b emoticon:string = InputMedia; inputMediaDice#e66fbf7b emoticon:string = InputMedia;
inputMediaStory#9a86b58f user_id:InputUser id:int = InputMedia; inputMediaStory#89fdd778 peer:InputPeer id:int = InputMedia;
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto; inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
inputChatUploadedPhoto#bdcdaec0 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.3?VideoSize = InputChatPhoto; inputChatUploadedPhoto#bdcdaec0 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.3?VideoSize = InputChatPhoto;
@ -96,11 +96,11 @@ userStatusLastMonth#77ebc742 = UserStatus;
chatEmpty#29562865 id:long = Chat; chatEmpty#29562865 id:long = Chat;
chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
chatForbidden#6592a1a7 id:long title:string = Chat; chatForbidden#6592a1a7 id:long title:string = Chat;
channel#83259464 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector<Username> = Chat; channel#94f592db flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector<Username> stories_max_id:flags2.4?int = Chat;
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat; channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#c9d31138 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions = ChatFull; chatFull#c9d31138 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions = ChatFull;
channelFull#f2355507 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions = ChatFull; channelFull#723027bd flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions stories:flags2.4?PeerStories = ChatFull;
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant; chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant; chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
@ -129,7 +129,7 @@ messageMediaInvoice#f6a548d3 flags:# shipping_address_requested:flags.1?true tes
messageMediaGeoLive#b940c666 flags:# geo:GeoPoint heading:flags.0?int period:int proximity_notification_radius:flags.1?int = MessageMedia; messageMediaGeoLive#b940c666 flags:# geo:GeoPoint heading:flags.0?int period:int proximity_notification_radius:flags.1?int = MessageMedia;
messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia; messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia;
messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia; messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia;
messageMediaStory#cbb20d88 flags:# via_mention:flags.1?true user_id:long id:int story:flags.0?StoryItem = MessageMedia; messageMediaStory#68cb6283 flags:# via_mention:flags.1?true peer:Peer id:int story:flags.0?StoryItem = MessageMedia;
messageActionEmpty#b6aef7b0 = MessageAction; messageActionEmpty#b6aef7b0 = MessageAction;
messageActionChatCreate#bd47cbad title:string users:Vector<long> = MessageAction; messageActionChatCreate#bd47cbad title:string users:Vector<long> = MessageAction;
@ -221,7 +221,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
inputReportReasonIllegalDrugs#a8eb2be = ReportReason; inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
inputReportReasonPersonalDetails#9ec7863d = ReportReason; inputReportReasonPersonalDetails#9ec7863d = ReportReason;
userFull#4fe1cc86 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?UserStories = UserFull; userFull#b9b12c6c flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?PeerStories = UserFull;
contact#145ade0b user_id:long mutual:Bool = Contact; contact#145ade0b user_id:long mutual:Bool = Contact;
@ -383,11 +383,11 @@ updateChannelPinnedTopics#fe198602 flags:# channel_id:long order:flags.0?Vector<
updateUser#20529438 user_id:long = Update; updateUser#20529438 user_id:long = Update;
updateAutoSaveSettings#ec05b097 = Update; updateAutoSaveSettings#ec05b097 = Update;
updateGroupInvitePrivacyForbidden#ccf08ad6 user_id:long = Update; updateGroupInvitePrivacyForbidden#ccf08ad6 user_id:long = Update;
updateStory#205a4133 user_id:long story:StoryItem = Update; updateStory#75b3b798 peer:Peer story:StoryItem = Update;
updateReadStories#feb5345a user_id:long max_id:int = Update; updateReadStories#f74e932b peer:Peer max_id:int = Update;
updateStoryID#1bf335b9 id:int random_id:long = Update; updateStoryID#1bf335b9 id:int random_id:long = Update;
updateStoriesStealthMode#2c084dc1 stealth_mode:StoriesStealthMode = Update; updateStoriesStealthMode#2c084dc1 stealth_mode:StoriesStealthMode = Update;
updateSentStoryReaction#e3a73d20 user_id:long story_id:int reaction:Reaction = Update; updateSentStoryReaction#7d627683 peer:Peer story_id:int reaction:Reaction = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -843,7 +843,7 @@ dataJSON#7d748d04 data:string = DataJSON;
labeledPrice#cb296bf8 label:string amount:long = LabeledPrice; labeledPrice#cb296bf8 label:string amount:long = LabeledPrice;
invoice#3e85a91b flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true recurring:flags.9?true currency:string prices:Vector<LabeledPrice> max_tip_amount:flags.8?long suggested_tip_amounts:flags.8?Vector<long> recurring_terms_url:flags.9?string = Invoice; invoice#5db95a15 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true recurring:flags.9?true currency:string prices:Vector<LabeledPrice> max_tip_amount:flags.8?long suggested_tip_amounts:flags.8?Vector<long> terms_url:flags.10?string = Invoice;
paymentCharge#ea02c27e id:string provider_charge_id:string = PaymentCharge; paymentCharge#ea02c27e id:string provider_charge_id:string = PaymentCharge;
@ -1123,7 +1123,7 @@ chatOnlines#f041e250 onlines:int = ChatOnlines;
statsURL#47a971e0 url:string = StatsURL; statsURL#47a971e0 url:string = StatsURL;
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true manage_topics:flags.13?true = ChatAdminRights; chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true manage_topics:flags.13?true post_stories:flags.14?true edit_stories:flags.15?true delete_stories:flags.16?true = ChatAdminRights;
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true manage_topics:flags.18?true send_photos:flags.19?true send_videos:flags.20?true send_roundvideos:flags.21?true send_audios:flags.22?true send_voices:flags.23?true send_docs:flags.24?true send_plain:flags.25?true until_date:int = ChatBannedRights; chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true manage_topics:flags.18?true send_photos:flags.19?true send_videos:flags.20?true send_roundvideos:flags.21?true send_audios:flags.22?true send_voices:flags.23?true send_docs:flags.24?true send_plain:flags.25?true until_date:int = ChatBannedRights;
@ -1198,7 +1198,7 @@ inputThemeSettings#8fde504f flags:# message_colors_animated:flags.2?true base_th
themeSettings#fa58b6d4 flags:# message_colors_animated:flags.2?true base_theme:BaseTheme accent_color:int outbox_accent_color:flags.3?int message_colors:flags.0?Vector<int> wallpaper:flags.1?WallPaper = ThemeSettings; themeSettings#fa58b6d4 flags:# message_colors_animated:flags.2?true base_theme:BaseTheme accent_color:int outbox_accent_color:flags.3?int message_colors:flags.0?Vector<int> wallpaper:flags.1?WallPaper = ThemeSettings;
webPageAttributeTheme#54b56617 flags:# documents:flags.0?Vector<Document> settings:flags.1?ThemeSettings = WebPageAttribute; webPageAttributeTheme#54b56617 flags:# documents:flags.0?Vector<Document> settings:flags.1?ThemeSettings = WebPageAttribute;
webPageAttributeStory#939a4671 flags:# user_id:long id:int story:flags.0?StoryItem = WebPageAttribute; webPageAttributeStory#2e94c3e7 flags:# peer:Peer id:int story:flags.0?StoryItem = WebPageAttribute;
messages.votesList#4899484e flags:# count:int votes:Vector<MessagePeerVote> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = messages.VotesList; messages.votesList#4899484e flags:# count:int votes:Vector<MessagePeerVote> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = messages.VotesList;
@ -1529,20 +1529,16 @@ messagePeerVoteMultiple#4628f6e6 peer:Peer options:Vector<bytes> date:int = Mess
sponsoredWebPage#3db8ec63 flags:# url:string site_name:string photo:flags.0?Photo = SponsoredWebPage; sponsoredWebPage#3db8ec63 flags:# url:string site_name:string photo:flags.0?Photo = SponsoredWebPage;
storyViews#c64c0b97 flags:# has_viewers:flags.1?true views_count:int reactions_count:int recent_viewers:flags.0?Vector<long> = StoryViews; storyViews#8d595cd6 flags:# has_viewers:flags.1?true views_count:int forwards_count:flags.2?int reactions:flags.3?Vector<ReactionCount> reactions_count:flags.4?int recent_viewers:flags.0?Vector<long> = StoryViews;
storyItemDeleted#51e6ee4f id:int = StoryItem; storyItemDeleted#51e6ee4f id:int = StoryItem;
storyItemSkipped#ffadc913 flags:# close_friends:flags.8?true id:int date:int expire_date:int = StoryItem; storyItemSkipped#ffadc913 flags:# close_friends:flags.8?true id:int date:int expire_date:int = StoryItem;
storyItem#44c457ce flags:# pinned:flags.5?true public:flags.7?true close_friends:flags.8?true min:flags.9?true noforwards:flags.10?true edited:flags.11?true contacts:flags.12?true selected_contacts:flags.13?true id:int date:int expire_date:int caption:flags.0?string entities:flags.1?Vector<MessageEntity> media:MessageMedia media_areas:flags.14?Vector<MediaArea> privacy:flags.2?Vector<PrivacyRule> views:flags.3?StoryViews sent_reaction:flags.15?Reaction = StoryItem; storyItem#44c457ce flags:# pinned:flags.5?true public:flags.7?true close_friends:flags.8?true min:flags.9?true noforwards:flags.10?true edited:flags.11?true contacts:flags.12?true selected_contacts:flags.13?true out:flags.16?true id:int date:int expire_date:int caption:flags.0?string entities:flags.1?Vector<MessageEntity> media:MessageMedia media_areas:flags.14?Vector<MediaArea> privacy:flags.2?Vector<PrivacyRule> views:flags.3?StoryViews sent_reaction:flags.15?Reaction = StoryItem;
userStories#8611a200 flags:# user_id:long max_read_id:flags.0?int stories:Vector<StoryItem> = UserStories;
stories.allStoriesNotModified#1158fe3e flags:# state:string stealth_mode:StoriesStealthMode = stories.AllStories; stories.allStoriesNotModified#1158fe3e flags:# state:string stealth_mode:StoriesStealthMode = stories.AllStories;
stories.allStories#519d899e flags:# has_more:flags.0?true count:int state:string user_stories:Vector<UserStories> users:Vector<User> stealth_mode:StoriesStealthMode = stories.AllStories; stories.allStories#6efc5e81 flags:# has_more:flags.0?true count:int state:string peer_stories:Vector<PeerStories> chats:Vector<Chat> users:Vector<User> stealth_mode:StoriesStealthMode = stories.AllStories;
stories.stories#4fe57df1 count:int stories:Vector<StoryItem> users:Vector<User> = stories.Stories; stories.stories#5dd8c3c8 count:int stories:Vector<StoryItem> chats:Vector<Chat> users:Vector<User> = stories.Stories;
stories.userStories#37a6ff5f stories:UserStories users:Vector<User> = stories.UserStories;
storyView#b0bdeac5 flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true user_id:long date:int reaction:flags.2?Reaction = StoryView; storyView#b0bdeac5 flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true user_id:long date:int reaction:flags.2?Reaction = StoryView;
@ -1562,6 +1558,20 @@ mediaAreaCoordinates#3d1ea4e x:double y:double w:double h:double rotation:double
mediaAreaVenue#be82db9c coordinates:MediaAreaCoordinates geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MediaArea; mediaAreaVenue#be82db9c coordinates:MediaAreaCoordinates geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MediaArea;
inputMediaAreaVenue#b282217f coordinates:MediaAreaCoordinates query_id:long result_id:string = MediaArea; inputMediaAreaVenue#b282217f coordinates:MediaAreaCoordinates query_id:long result_id:string = MediaArea;
mediaAreaGeoPoint#df8b3b22 coordinates:MediaAreaCoordinates geo:GeoPoint = MediaArea; mediaAreaGeoPoint#df8b3b22 coordinates:MediaAreaCoordinates geo:GeoPoint = MediaArea;
mediaAreaSuggestedReaction#14455871 flags:# dark:flags.0?true flipped:flags.1?true coordinates:MediaAreaCoordinates reaction:Reaction = MediaArea;
peerStories#9a35e999 flags:# peer:Peer max_read_id:flags.0?int stories:Vector<StoryItem> = PeerStories;
stories.peerStories#cae68768 stories:PeerStories chats:Vector<Chat> users:Vector<User> = stories.PeerStories;
stories.boostsStatus#66ea1fef flags:# my_boost:flags.2?true level:int current_level_boosts:int boosts:int next_level_boosts:flags.0?int premium_audience:flags.1?StatsPercentValue = stories.BoostsStatus;
stories.canApplyBoostOk#c3173587 = stories.CanApplyBoostResult;
stories.canApplyBoostReplace#712c4655 current_boost:Peer chats:Vector<Chat> = stories.CanApplyBoostResult;
booster#e9e6380 user_id:long expires:int = Booster;
stories.boostersList#f3dd3d1d flags:# count:int boosters:Vector<Booster> next_offset:flags.0?string users:Vector<User> = stories.BoostersList;
---functions--- ---functions---
@ -1688,7 +1698,6 @@ account.invalidateSignInCodes#ca8ae8ba codes:Vector<string> = Bool;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>; users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#b60f5918 id:InputUser = users.UserFull; users.getFullUser#b60f5918 id:InputUser = users.UserFull;
users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector<SecureValueError> = Bool; users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector<SecureValueError> = Bool;
users.getStoriesMaxIDs#ca1cb9ab id:Vector<InputUser> = Vector<int>;
contacts.getContactIDs#7adc669d hash:long = Vector<int>; contacts.getContactIDs#7adc669d hash:long = Vector<int>;
contacts.getStatuses#c4a353ee = Vector<ContactStatus>; contacts.getStatuses#c4a353ee = Vector<ContactStatus>;
@ -1714,7 +1723,6 @@ contacts.resolvePhone#8af94344 phone:string = contacts.ResolvedPeer;
contacts.exportContactToken#f8654027 = ExportedContactToken; contacts.exportContactToken#f8654027 = ExportedContactToken;
contacts.importContactToken#13005788 token:string = User; contacts.importContactToken#13005788 token:string = User;
contacts.editCloseFriends#ba6705f0 id:Vector<long> = Bool; contacts.editCloseFriends#ba6705f0 id:Vector<long> = Bool;
contacts.toggleStoriesHidden#753fb865 id:InputUser hidden:Bool = Bool;
contacts.setBlocked#94c65c76 flags:# my_stories_from:flags.0?true id:Vector<InputPeer> limit:int = Bool; contacts.setBlocked#94c65c76 flags:# my_stories_from:flags.0?true id:Vector<InputPeer> limit:int = Bool;
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages; messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
@ -2103,23 +2111,30 @@ chatlists.hideChatlistUpdates#66e486fb chatlist:InputChatlist = Bool;
chatlists.getLeaveChatlistSuggestions#fdbcd714 chatlist:InputChatlist = Vector<Peer>; chatlists.getLeaveChatlistSuggestions#fdbcd714 chatlist:InputChatlist = Vector<Peer>;
chatlists.leaveChatlist#74fae13a chatlist:InputChatlist peers:Vector<InputPeer> = Updates; chatlists.leaveChatlist#74fae13a chatlist:InputChatlist peers:Vector<InputPeer> = Updates;
stories.canSendStory#b100d45d = Bool; stories.canSendStory#c7dfdfdd peer:InputPeer = Bool;
stories.sendStory#d455fcec flags:# pinned:flags.2?true noforwards:flags.4?true media:InputMedia media_areas:flags.5?Vector<MediaArea> caption:flags.0?string entities:flags.1?Vector<MessageEntity> privacy_rules:Vector<InputPrivacyRule> random_id:long period:flags.3?int = Updates; stories.sendStory#bcb73644 flags:# pinned:flags.2?true noforwards:flags.4?true peer:InputPeer media:InputMedia media_areas:flags.5?Vector<MediaArea> caption:flags.0?string entities:flags.1?Vector<MessageEntity> privacy_rules:Vector<InputPrivacyRule> random_id:long period:flags.3?int = Updates;
stories.editStory#a9b91ae4 flags:# id:int media:flags.0?InputMedia media_areas:flags.3?Vector<MediaArea> caption:flags.1?string entities:flags.1?Vector<MessageEntity> privacy_rules:flags.2?Vector<InputPrivacyRule> = Updates; stories.editStory#b583ba46 flags:# peer:InputPeer id:int media:flags.0?InputMedia media_areas:flags.3?Vector<MediaArea> caption:flags.1?string entities:flags.1?Vector<MessageEntity> privacy_rules:flags.2?Vector<InputPrivacyRule> = Updates;
stories.deleteStories#b5d501d7 id:Vector<int> = Vector<int>; stories.deleteStories#ae59db5f peer:InputPeer id:Vector<int> = Vector<int>;
stories.togglePinned#51602944 id:Vector<int> pinned:Bool = Vector<int>; stories.togglePinned#9a75a1ef peer:InputPeer id:Vector<int> pinned:Bool = Vector<int>;
stories.getAllStories#eeb0d625 flags:# next:flags.1?true hidden:flags.2?true state:flags.0?string = stories.AllStories; stories.getAllStories#eeb0d625 flags:# next:flags.1?true hidden:flags.2?true state:flags.0?string = stories.AllStories;
stories.getUserStories#96d528e0 user_id:InputUser = stories.UserStories; stories.getPinnedStories#5821a5dc peer:InputPeer offset_id:int limit:int = stories.Stories;
stories.getPinnedStories#b471137 user_id:InputUser offset_id:int limit:int = stories.Stories; stories.getStoriesArchive#b4352016 peer:InputPeer offset_id:int limit:int = stories.Stories;
stories.getStoriesArchive#1f5bc5d2 offset_id:int limit:int = stories.Stories; stories.getStoriesByID#5774ca74 peer:InputPeer id:Vector<int> = stories.Stories;
stories.getStoriesByID#6a15cf46 user_id:InputUser id:Vector<int> = stories.Stories;
stories.toggleAllStoriesHidden#7c2557c4 hidden:Bool = Bool; stories.toggleAllStoriesHidden#7c2557c4 hidden:Bool = Bool;
stories.getAllReadUserStories#729c562c = Updates; stories.readStories#a556dac8 peer:InputPeer max_id:int = Vector<int>;
stories.readStories#edc5105b user_id:InputUser max_id:int = Vector<int>; stories.incrementStoryViews#b2028afb peer:InputPeer id:Vector<int> = Bool;
stories.incrementStoryViews#22126127 user_id:InputUser id:Vector<int> = Bool; stories.getStoryViewsList#7ed23c57 flags:# just_contacts:flags.0?true reactions_first:flags.2?true peer:InputPeer q:flags.1?string id:int offset:string limit:int = stories.StoryViewsList;
stories.getStoryViewsList#f95f61a4 flags:# just_contacts:flags.0?true reactions_first:flags.2?true q:flags.1?string id:int offset:string limit:int = stories.StoryViewsList; stories.getStoriesViews#28e16cc8 peer:InputPeer id:Vector<int> = stories.StoryViews;
stories.getStoriesViews#9a75d6a6 id:Vector<int> = stories.StoryViews; stories.exportStoryLink#7b8def20 peer:InputPeer id:int = ExportedStoryLink;
stories.exportStoryLink#16e443ce user_id:InputUser id:int = ExportedStoryLink; stories.report#1923fa8c peer:InputPeer id:Vector<int> reason:ReportReason message:string = Bool;
stories.report#c95be06a user_id:InputUser id:Vector<int> reason:ReportReason message:string = Bool;
stories.activateStealthMode#57bbd166 flags:# past:flags.0?true future:flags.1?true = Updates; stories.activateStealthMode#57bbd166 flags:# past:flags.0?true future:flags.1?true = Updates;
stories.sendReaction#49aaa9b3 flags:# add_to_recent:flags.0?true user_id:InputUser story_id:int reaction:Reaction = Updates; stories.sendReaction#7fd736b2 flags:# add_to_recent:flags.0?true peer:InputPeer story_id:int reaction:Reaction = Updates;
stories.getPeerStories#2c4ada50 peer:InputPeer = stories.PeerStories;
stories.getAllReadPeerStories#9b5ae7f9 = Updates;
stories.getPeerMaxIDs#535983c3 id:Vector<InputPeer> = Vector<int>;
stories.getChatsToSend#a56a8b60 = messages.Chats;
stories.togglePeerStoriesHidden#bd0415c4 peer:InputPeer hidden:Bool = Bool;
stories.getBoostsStatus#4c449472 peer:InputPeer = stories.BoostsStatus;
stories.getBoostersList#337ef980 peer:InputPeer offset:string limit:int = stories.BoostersList;
stories.canApplyBoost#db05c1bd peer:InputPeer = stories.CanApplyBoostResult;
stories.applyBoost#f29d7c2b peer:InputPeer = Bool;

View file

@ -1 +1 @@
// LAYER 163 // LAYER 164

View file

@ -529,10 +529,12 @@ void CheckoutProcess::panelSubmit() {
} else if (!method.newCredentials } else if (!method.newCredentials
&& method.savedCredentialsIndex >= method.savedCredentials.size()) { && method.savedCredentialsIndex >= method.savedCredentials.size()) {
editPaymentMethod(); editPaymentMethod();
} else if (invoice.isRecurring && !_form->details().termsAccepted) { } else if (!invoice.termsUrl.isEmpty()
&& !_form->details().termsAccepted) {
_panel->requestTermsAcceptance( _panel->requestTermsAcceptance(
_form->details().termsBotUsername, _form->details().termsBotUsername,
invoice.recurringTermsUrl); invoice.termsUrl,
invoice.isRecurring);
} else { } else {
RegisterPaymentStart(this, { _form->invoice().cover.title }); RegisterPaymentStart(this, { _form->invoice().cover.title });
_submitState = SubmitState::Finishing; _submitState = SubmitState::Finishing;

View file

@ -384,8 +384,7 @@ void Form::processInvoice(const MTPDinvoice &data) {
.isFlexible = data.is_flexible(), .isFlexible = data.is_flexible(),
.isTest = data.is_test(), .isTest = data.is_test(),
.recurringTermsUrl = qs( .termsUrl = qs(data.vterms_url().value_or_empty()),
data.vrecurring_terms_url().value_or_empty()),
.phoneSentToProvider = data.is_phone_to_provider(), .phoneSentToProvider = data.is_phone_to_provider(),
.emailSentToProvider = data.is_email_to_provider(), .emailSentToProvider = data.is_email_to_provider(),

View file

@ -704,15 +704,18 @@ void Panel::showWarning(const QString &bot, const QString &provider) {
void Panel::requestTermsAcceptance( void Panel::requestTermsAcceptance(
const QString &username, const QString &username,
const QString &url) { const QString &url,
bool recurring) {
showBox(Box([=](not_null<GenericBox*> box) { showBox(Box([=](not_null<GenericBox*> box) {
box->setTitle(tr::lng_payments_terms_title()); box->setTitle(tr::lng_payments_terms_title());
box->addRow(object_ptr<Ui::FlatLabel>( box->addRow(object_ptr<Ui::FlatLabel>(
box.get(), box.get(),
tr::lng_payments_terms_text( (recurring
lt_bot, ? tr::lng_payments_terms_text
rpl::single(Ui::Text::Bold('@' + username)), : tr::lng_payments_terms_text_once)(
Ui::Text::WithEntities), lt_bot,
rpl::single(Ui::Text::Bold('@' + username)),
Ui::Text::WithEntities),
st::boxLabel)); st::boxLabel));
const auto update = std::make_shared<Fn<void()>>(); const auto update = std::make_shared<Fn<void()>>();
auto checkView = std::make_unique<Ui::CheckView>( auto checkView = std::make_unique<Ui::CheckView>(

View file

@ -78,7 +78,10 @@ public:
void askSetPassword(); void askSetPassword();
void showCloseConfirm(); void showCloseConfirm();
void showWarning(const QString &bot, const QString &provider); void showWarning(const QString &bot, const QString &provider);
void requestTermsAcceptance(const QString &username, const QString &url); void requestTermsAcceptance(
const QString &username,
const QString &url,
bool recurring);
bool showWebview( bool showWebview(
const QString &url, const QString &url,

View file

@ -56,7 +56,7 @@ struct Invoice {
bool isTest = false; bool isTest = false;
QString provider; QString provider;
QString recurringTermsUrl; QString termsUrl;
bool phoneSentToProvider = false; bool phoneSentToProvider = false;
bool emailSentToProvider = false; bool emailSentToProvider = false;

View file

@ -2557,19 +2557,17 @@ void SessionController::openPeerStories(
if (const auto source = stories.source(peerId)) { if (const auto source = stories.source(peerId)) {
if (const auto idDates = source->toOpen()) { if (const auto idDates = source->toOpen()) {
openPeerStory( openPeerStory(
source->user, source->peer,
idDates.id, idDates.id,
(list (list
? StoriesContext{ *list } ? StoriesContext{ *list }
: StoriesContext{ StoriesContextPeer() })); : StoriesContext{ StoriesContextPeer() }));
} }
} else if (const auto userId = peerToUser(peerId)) { } else if (const auto peer = session().data().peerLoaded(peerId)) {
if (const auto user = session().data().userLoaded(userId)) { const auto done = crl::guard(&_storyOpenGuard, [=] {
const auto done = crl::guard(&_storyOpenGuard, [=] { openPeerStories(peerId, list);
openPeerStories(peerId, list); });
}); stories.requestPeerStories(peer, done);
stories.requestUserStories(user, done);
}
} }
} }