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_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_link" = "Terms of Service";
"lng_payments_terms_accept" = "Accept";
@ -3091,9 +3092,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gigagroup_suggest_more" = "Learn more";
"lng_rights_channel_info" = "Change channel info";
"lng_rights_channel_manage" = "Manage messages";
"lng_rights_channel_post" = "Post messages";
"lng_rights_channel_edit" = "Edit 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_group_info" = "Change group info";
"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_edit_messages" = "Edit 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_ban_users" = "Ban users";
"lng_admin_log_admin_invite_users" = "Add members";

View File

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

View File

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

View File

@ -51,13 +51,14 @@ constexpr auto kForceDisableTooltipDuration = 3 * crl::time(1000);
}
[[nodiscard]] auto NestedRestrictionLabelsList(
Data::RestrictionsSetOptions options) {
Data::RestrictionsSetOptions options)
-> std::vector<NestedEditFlagsLabels<ChatRestrictions>> {
using Flag = ChatRestriction;
auto first = std::vector<RestrictionLabel>{
{ 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::SendVideos, tr::lng_rights_chat_videos(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),
end(second));
}
return std::vector<NestedEditFlagsLabels<ChatRestrictions>>{
return {
{ 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) },
};
}
@ -1031,42 +1087,11 @@ std::vector<RestrictionLabel> RestrictionLabels(
std::vector<AdminRightLabel> AdminRightLabels(
Data::AdminRightsSetOptions options) {
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 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) }
};
auto result = std::vector<AdminRightLabel>();
for (const auto &[_, r] : NestedAdminRightLabels(options)) {
result.insert(result.end(), r.begin(), r.end());
}
return result;
}
EditFlagsControl<ChatRestrictions> CreateEditRestrictions(
@ -1107,7 +1132,7 @@ EditFlagsControl<ChatAdminRights> CreateEditAdminRights(
rights,
{
.header = std::move(header),
.labels = { { std::nullopt, AdminRightLabels(options) } },
.labels = NestedAdminRightLabels(options),
.disabledMessages = std::move(disabledMessages),
});
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_chat.h"
#include "data/data_session.h"
#include "data/data_stories.h"
#include "data/data_folder.h"
#include "data/data_forum.h"
#include "data/data_forum_icons.h"
@ -530,6 +531,11 @@ bool ChannelData::canBanMembers() const {
|| (adminRights() & AdminRight::BanUsers);
}
bool ChannelData::canPostMessages() const {
return amCreator()
|| (adminRights() & AdminRight::PostMessages);
}
bool ChannelData::canEditMessages() const {
return amCreator()
|| (adminRights() & AdminRight::EditMessages);
@ -559,11 +565,6 @@ bool ChannelData::canAddAdmins() const {
|| (adminRights() & AdminRight::AddAdmins);
}
bool ChannelData::canPublish() const {
return amCreator()
|| (adminRights() & AdminRight::PostMessages);
}
bool ChannelData::allowsForwarding() const {
return !(flags() & Flag::NoForwards);
}
@ -877,6 +878,38 @@ const Data::AllowedReactions &ChannelData::allowedReactions() const {
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) {
if (const auto forum = this->forum()) {
forum->applyReceivedTopics(topics);
@ -1046,6 +1079,7 @@ void ApplyChannelUpdate(
} else {
channel->setAllowedReactions({});
}
channel->owner().stories().apply(channel, update.vstories());
channel->fullUpdated();
channel->setPendingRequestsCount(
update.vrequests_pending().value_or_empty(),

View File

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

View File

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

View File

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

View File

@ -898,6 +898,33 @@ bool PeerData::isRepliesChat() const {
: 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 {
if (const auto channel = asChannel()) {
return channel->forum();
@ -1108,6 +1135,34 @@ const Data::WallPaper *PeerData::wallPaper() const {
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) {
const auto status = is
? BlockStatus::Blocked

View File

@ -183,9 +183,9 @@ public:
[[nodiscard]] bool isForum() const;
[[nodiscard]] bool isGigagroup() const;
[[nodiscard]] bool isRepliesChat() const;
[[nodiscard]] bool sharedMediaInfo() const {
return isSelf() || isRepliesChat();
}
[[nodiscard]] bool sharedMediaInfo() const;
[[nodiscard]] bool hasStoriesHidden() const;
void setStoriesHidden(bool hidden);
[[nodiscard]] bool isNotificationsUser() const {
return (id == peerFromUser(333000))
@ -407,6 +407,16 @@ public:
void setWallPaper(std::optional<Data::WallPaper> paper);
[[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;
MTPinputPeer input = MTP_inputPeerEmpty();

View File

@ -909,7 +909,17 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
| Flag::NoForwards
| Flag::JoinToWrite
| 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())
| (data.is_verified() ? Flag::Verified : 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_forum() && data.is_megagroup())
? Flag::Forum
: Flag())
| ((!minimal
&& !data.is_stories_hidden_min()
&& data.is_stories_hidden())
? Flag::StoriesHidden
: Flag());
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());
@ -3345,12 +3367,12 @@ void Session::webpageApplyFields(
for (const auto &attribute : attributes->v) {
attribute.match([&](const MTPDwebPageAttributeStory &data) {
storyId = FullStoryId{
peerFromUser(data.vuser_id()),
peerFromMTP(data.vpeer()),
data.vid().v,
};
if (const auto embed = data.vstory()) {
story = stories().applyFromWebpage(
peerFromUser(data.vuser_id()),
peerFromMTP(data.vpeer()),
*embed);
} else if (const auto maybe = stories().lookup(storyId)) {
story = *maybe;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -49,7 +49,7 @@ namespace Media::Stories {
class Controller;
struct ReplyAreaData {
UserData *user = nullptr;
PeerData *peer = nullptr;
StoryId id = 0;
friend inline auto operator<=>(ReplyAreaData, ReplyAreaData) = default;
@ -148,7 +148,7 @@ private:
std::unique_ptr<Cant> _cant;
ReplyAreaData _data;
base::has_weak_ptr _shownUserGuard;
base::has_weak_ptr _shownPeerGuard;
bool _chooseAttachRequest = false;
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_forum_topic.h"
#include "data/data_histories.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "data/data_stories.h"
#include "data/data_thread.h"
#include "data/data_user.h"
#include "history/history.h"
#include "history/history_item_helpers.h" // GetErrorTextForSending.
#include "history/view/history_view_context_menu.h" // CopyStoryLink.
@ -79,9 +79,7 @@ namespace Media::Stories {
if (!story) {
return;
}
const auto user = story->peer()->asUser();
Assert(user != nullptr);
const auto peer = story->peer();
const auto error = [&] {
for (const auto thread : result) {
const auto error = GetErrorTextForSending(
@ -115,14 +113,16 @@ namespace Media::Stories {
message.action.clearDraft = false;
api->sendMessage(std::move(message));
}
const auto peer = thread->peer();
const auto threadPeer = thread->peer();
const auto threadHistory = thread->owningHistory();
const auto randomId = base::RandomValue<uint64>();
auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (action.replyTo) {
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
}
const auto silentPost = ShouldSendSilent(peer, action.options);
const auto silentPost = ShouldSendSilent(
threadPeer,
action.options);
if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
@ -140,23 +140,23 @@ namespace Media::Stories {
randomId,
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(sendFlags),
peer->input,
threadPeer->input,
Data::Histories::ReplyToPlaceholder(),
MTP_inputMediaStory(
user->inputUser,
MTP_int(id.story)),
MTP_inputMediaStory(peer->input, MTP_int(id.story)),
MTPstring(),
MTP_long(randomId),
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(),
MTP_int(action.options.scheduled),
MTP_inputPeerEmpty()
), [=](const MTPUpdates &result, const MTP::Response &response) {
done();
}, [=](const MTP::Error &error, const MTP::Response &response) {
api->sendMessageFail(error, peer, randomId);
done();
});
), [=](
const MTPUpdates &result,
const MTP::Response &response) {
done();
}, [=](const MTP::Error &error, const MTP::Response &response) {
api->sendMessageFail(error, threadPeer, randomId);
done();
});
++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_media.h"
#include "data/data_file_origin.h"
#include "data/data_peer.h"
#include "data/data_photo.h"
#include "data/data_photo_media.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "media/stories/media_stories_controller.h"
@ -244,8 +244,8 @@ Sibling::Sibling(
const Data::StoriesSource &source,
StoryId suggestedId)
: _controller(controller)
, _id{ source.user->id, LookupShownId(source, suggestedId) }
, _peer(source.user) {
, _id{ source.peer->id, LookupShownId(source, suggestedId) }
, _peer(source.peer) {
checkStory();
_goodShown.stop();
}
@ -305,7 +305,7 @@ bool Sibling::shows(
const Data::StoriesSource &source,
StoryId suggestedId) const {
const auto fullId = FullStoryId{
source.user->id,
source.peer->id,
LookupShownId(source, suggestedId),
};
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;
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;
inputMediaStory#9a86b58f user_id:InputUser id:int = InputMedia;
inputMediaStory#89fdd778 peer:InputPeer id:int = InputMedia;
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;
@ -96,11 +96,11 @@ userStatusLastMonth#77ebc742 = UserStatus;
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;
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;
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;
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;
messageMediaPoll#4bd6e798 poll:Poll results:PollResults = 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;
messageActionChatCreate#bd47cbad title:string users:Vector<long> = MessageAction;
@ -221,7 +221,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
inputReportReasonIllegalDrugs#a8eb2be = 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;
@ -383,11 +383,11 @@ updateChannelPinnedTopics#fe198602 flags:# channel_id:long order:flags.0?Vector<
updateUser#20529438 user_id:long = Update;
updateAutoSaveSettings#ec05b097 = Update;
updateGroupInvitePrivacyForbidden#ccf08ad6 user_id:long = Update;
updateStory#205a4133 user_id:long story:StoryItem = Update;
updateReadStories#feb5345a user_id:long max_id:int = Update;
updateStory#75b3b798 peer:Peer story:StoryItem = Update;
updateReadStories#f74e932b peer:Peer max_id:int = Update;
updateStoryID#1bf335b9 id:int random_id:long = 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;
@ -843,7 +843,7 @@ dataJSON#7d748d04 data:string = DataJSON;
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;
@ -1123,7 +1123,7 @@ chatOnlines#f041e250 onlines:int = ChatOnlines;
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;
@ -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;
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;
@ -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;
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;
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;
userStories#8611a200 flags:# user_id:long max_read_id:flags.0?int stories:Vector<StoryItem> = UserStories;
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;
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.userStories#37a6ff5f stories:UserStories users:Vector<User> = stories.UserStories;
stories.stories#5dd8c3c8 count:int stories:Vector<StoryItem> chats:Vector<Chat> users:Vector<User> = stories.Stories;
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;
inputMediaAreaVenue#b282217f coordinates:MediaAreaCoordinates query_id:long result_id:string = 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---
@ -1688,7 +1698,6 @@ account.invalidateSignInCodes#ca8ae8ba codes:Vector<string> = Bool;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
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.getStatuses#c4a353ee = Vector<ContactStatus>;
@ -1714,7 +1723,6 @@ contacts.resolvePhone#8af94344 phone:string = contacts.ResolvedPeer;
contacts.exportContactToken#f8654027 = ExportedContactToken;
contacts.importContactToken#13005788 token:string = User;
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;
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.leaveChatlist#74fae13a chatlist:InputChatlist peers:Vector<InputPeer> = Updates;
stories.canSendStory#b100d45d = 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.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.deleteStories#b5d501d7 id:Vector<int> = Vector<int>;
stories.togglePinned#51602944 id:Vector<int> pinned:Bool = Vector<int>;
stories.canSendStory#c7dfdfdd peer:InputPeer = Bool;
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#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#ae59db5f peer:InputPeer id:Vector<int> = 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.getUserStories#96d528e0 user_id:InputUser = stories.UserStories;
stories.getPinnedStories#b471137 user_id:InputUser offset_id:int limit:int = stories.Stories;
stories.getStoriesArchive#1f5bc5d2 offset_id:int limit:int = stories.Stories;
stories.getStoriesByID#6a15cf46 user_id:InputUser id:Vector<int> = stories.Stories;
stories.getPinnedStories#5821a5dc peer:InputPeer offset_id:int limit:int = stories.Stories;
stories.getStoriesArchive#b4352016 peer:InputPeer offset_id:int limit:int = stories.Stories;
stories.getStoriesByID#5774ca74 peer:InputPeer id:Vector<int> = stories.Stories;
stories.toggleAllStoriesHidden#7c2557c4 hidden:Bool = Bool;
stories.getAllReadUserStories#729c562c = Updates;
stories.readStories#edc5105b user_id:InputUser max_id:int = Vector<int>;
stories.incrementStoryViews#22126127 user_id:InputUser id:Vector<int> = Bool;
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#9a75d6a6 id:Vector<int> = stories.StoryViews;
stories.exportStoryLink#16e443ce user_id:InputUser id:int = ExportedStoryLink;
stories.report#c95be06a user_id:InputUser id:Vector<int> reason:ReportReason message:string = Bool;
stories.readStories#a556dac8 peer:InputPeer max_id:int = Vector<int>;
stories.incrementStoryViews#b2028afb peer:InputPeer 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.getStoriesViews#28e16cc8 peer:InputPeer id:Vector<int> = stories.StoryViews;
stories.exportStoryLink#7b8def20 peer:InputPeer id:int = ExportedStoryLink;
stories.report#1923fa8c peer:InputPeer id:Vector<int> reason:ReportReason message:string = Bool;
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
&& method.savedCredentialsIndex >= method.savedCredentials.size()) {
editPaymentMethod();
} else if (invoice.isRecurring && !_form->details().termsAccepted) {
} else if (!invoice.termsUrl.isEmpty()
&& !_form->details().termsAccepted) {
_panel->requestTermsAcceptance(
_form->details().termsBotUsername,
invoice.recurringTermsUrl);
invoice.termsUrl,
invoice.isRecurring);
} else {
RegisterPaymentStart(this, { _form->invoice().cover.title });
_submitState = SubmitState::Finishing;

View File

@ -384,8 +384,7 @@ void Form::processInvoice(const MTPDinvoice &data) {
.isFlexible = data.is_flexible(),
.isTest = data.is_test(),
.recurringTermsUrl = qs(
data.vrecurring_terms_url().value_or_empty()),
.termsUrl = qs(data.vterms_url().value_or_empty()),
.phoneSentToProvider = data.is_phone_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(
const QString &username,
const QString &url) {
const QString &url,
bool recurring) {
showBox(Box([=](not_null<GenericBox*> box) {
box->setTitle(tr::lng_payments_terms_title());
box->addRow(object_ptr<Ui::FlatLabel>(
box.get(),
tr::lng_payments_terms_text(
lt_bot,
rpl::single(Ui::Text::Bold('@' + username)),
Ui::Text::WithEntities),
(recurring
? tr::lng_payments_terms_text
: tr::lng_payments_terms_text_once)(
lt_bot,
rpl::single(Ui::Text::Bold('@' + username)),
Ui::Text::WithEntities),
st::boxLabel));
const auto update = std::make_shared<Fn<void()>>();
auto checkView = std::make_unique<Ui::CheckView>(

View File

@ -78,7 +78,10 @@ public:
void askSetPassword();
void showCloseConfirm();
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(
const QString &url,

View File

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

View File

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