Update API scheme, track stars-per-message.

This commit is contained in:
John Preston 2025-02-18 12:54:24 +04:00
parent 2e45d9fc6b
commit 852ab19760
27 changed files with 256 additions and 190 deletions

View file

@ -377,15 +377,15 @@ const Data::PremiumSubscriptionOptions &Premium::subscriptionOptions() const {
return _subscriptionOptions; return _subscriptionOptions;
} }
rpl::producer<> Premium::somePremiumRequiredResolved() const { rpl::producer<> Premium::someMessageMoneyRestrictionsResolved() const {
return _somePremiumRequiredResolved.events(); return _someMessageMoneyRestrictionsResolved.events();
} }
void Premium::resolvePremiumRequired(not_null<UserData*> user) { void Premium::resolveMessageMoneyRestrictions(not_null<UserData*> user) {
_resolvePremiumRequiredUsers.emplace(user); _resolveMessageMoneyRequiredUsers.emplace(user);
if (!_premiumRequiredRequestScheduled if (!_messageMoneyRequestScheduled
&& _resolvePremiumRequestedUsers.empty()) { && _resolveMessageMoneyRequestedUsers.empty()) {
_premiumRequiredRequestScheduled = true; _messageMoneyRequestScheduled = true;
crl::on_main(_session, [=] { crl::on_main(_session, [=] {
requestPremiumRequiredSlice(); requestPremiumRequiredSlice();
}); });
@ -393,50 +393,65 @@ void Premium::resolvePremiumRequired(not_null<UserData*> user) {
} }
void Premium::requestPremiumRequiredSlice() { void Premium::requestPremiumRequiredSlice() {
_premiumRequiredRequestScheduled = false; _messageMoneyRequestScheduled = false;
if (!_resolvePremiumRequestedUsers.empty() if (!_resolveMessageMoneyRequestedUsers.empty()
|| _resolvePremiumRequiredUsers.empty()) { || _resolveMessageMoneyRequiredUsers.empty()) {
return; return;
} }
constexpr auto kPerRequest = 100; constexpr auto kPerRequest = 100;
auto users = MTP_vector_from_range(_resolvePremiumRequiredUsers auto users = MTP_vector_from_range(_resolveMessageMoneyRequiredUsers
| ranges::views::transform(&UserData::inputUser)); | ranges::views::transform(&UserData::inputUser));
if (users.v.size() > kPerRequest) { if (users.v.size() > kPerRequest) {
auto shortened = users.v; auto shortened = users.v;
shortened.resize(kPerRequest); shortened.resize(kPerRequest);
users = MTP_vector<MTPInputUser>(std::move(shortened)); users = MTP_vector<MTPInputUser>(std::move(shortened));
const auto from = begin(_resolvePremiumRequiredUsers); const auto from = begin(_resolveMessageMoneyRequiredUsers);
_resolvePremiumRequestedUsers = { from, from + kPerRequest }; _resolveMessageMoneyRequestedUsers = { from, from + kPerRequest };
_resolvePremiumRequiredUsers.erase(from, from + kPerRequest); _resolveMessageMoneyRequiredUsers.erase(from, from + kPerRequest);
} else { } else {
_resolvePremiumRequestedUsers _resolveMessageMoneyRequestedUsers
= base::take(_resolvePremiumRequiredUsers); = base::take(_resolveMessageMoneyRequiredUsers);
} }
const auto finish = [=](const QVector<MTPBool> &list) { const auto finish = [=](const QVector<MTPRequirementToContact> &list) {
constexpr auto me = UserDataFlag::MeRequiresPremiumToWrite;
constexpr auto known = UserDataFlag::RequirePremiumToWriteKnown;
constexpr auto mask = me | known;
auto index = 0; auto index = 0;
for (const auto &user : base::take(_resolvePremiumRequestedUsers)) { for (const auto &user : base::take(_resolveMessageMoneyRequestedUsers)) {
const auto require = (index < list.size()) const auto set = [&](bool requirePremium, int stars) {
&& mtpIsTrue(list[index++]); using Flag = UserDataFlag;
user->setFlags((user->flags() & ~mask) constexpr auto me = Flag::RequiresPremiumToWrite;
| known constexpr auto known = Flag::MessageMoneyRestrictionsKnown;
| (require ? me : UserDataFlag())); constexpr auto hasPrem = Flag::HasRequirePremiumToWrite;
constexpr auto hasStars = Flag::HasStarsPerMessage;
user->setStarsPerMessage(stars);
user->setFlags((user->flags() & ~(me | hasPrem | hasStars))
| known
| (requirePremium ? (me | hasPrem) : Flag())
| (stars ? hasStars : Flag()));
};
if (index >= list.size()) {
set(false, 0);
continue;
}
list[index++].match([&](const MTPDrequirementToContactEmpty &) {
set(false, 0);
}, [&](const MTPDrequirementToContactPremium &) {
set(true, 0);
}, [&](const MTPDrequirementToContactPaidMessages &data) {
set(false, data.vstars_amount().v);
});
} }
if (!_premiumRequiredRequestScheduled if (!_messageMoneyRequestScheduled
&& !_resolvePremiumRequiredUsers.empty()) { && !_resolveMessageMoneyRequiredUsers.empty()) {
_premiumRequiredRequestScheduled = true; _messageMoneyRequestScheduled = true;
crl::on_main(_session, [=] { crl::on_main(_session, [=] {
requestPremiumRequiredSlice(); requestPremiumRequiredSlice();
}); });
} }
_somePremiumRequiredResolved.fire({}); _someMessageMoneyRestrictionsResolved.fire({});
}; };
_session->api().request( _session->api().request(
MTPusers_GetIsPremiumRequiredToContact(std::move(users)) MTPusers_GetRequirementsToContact(std::move(users))
).done([=](const MTPVector<MTPBool> &result) { ).done([=](const MTPVector<MTPRequirementToContact> &result) {
finish(result.v); finish(result.v);
}).fail([=] { }).fail([=] {
finish({}); finish({});
@ -694,28 +709,32 @@ rpl::producer<rpl::no_value, QString> SponsoredToggle::setToggled(bool v) {
}; };
} }
RequirePremiumState ResolveRequiresPremiumToWrite( MessageMoneyRestriction ResolveMessageMoneyRestrictions(
not_null<PeerData*> peer, not_null<PeerData*> peer,
History *maybeHistory) { History *maybeHistory) {
const auto user = peer->asUser(); const auto user = peer->asUser();
if (!user if (!user) {
|| !user->someRequirePremiumToWrite() return { .known = true };
|| user->session().premium()) { } else if (user->messageMoneyRestrictionsKnown()) {
return RequirePremiumState::No; return {
} else if (user->requirePremiumToWriteKnown()) { .starsPerMessage = user->starsPerMessage(),
return user->meRequiresPremiumToWrite() .premiumRequired = (user->requiresPremiumToWrite()
? RequirePremiumState::Yes && !user->session().premium()),
: RequirePremiumState::No; .known = true,
};
} else if (user->hasStarsPerMessage()) {
return {};
} else if (!user->hasRequirePremiumToWrite()) {
return { .known = true };
} else if (user->flags() & UserDataFlag::MutualContact) { } else if (user->flags() & UserDataFlag::MutualContact) {
return RequirePremiumState::No; return { .known = true };
} else if (!maybeHistory) { } else if (!maybeHistory) {
return RequirePremiumState::Unknown; return {};
} }
const auto update = [&](bool require) { const auto update = [&](bool require) {
using Flag = UserDataFlag; using Flag = UserDataFlag;
constexpr auto known = Flag::RequirePremiumToWriteKnown; constexpr auto known = Flag::MessageMoneyRestrictionsKnown;
constexpr auto me = Flag::MeRequiresPremiumToWrite; constexpr auto me = Flag::RequiresPremiumToWrite;
user->setFlags((user->flags() & ~me) user->setFlags((user->flags() & ~me)
| known | known
| (require ? me : Flag())); | (require ? me : Flag()));
@ -727,16 +746,19 @@ RequirePremiumState ResolveRequiresPremiumToWrite(
const auto item = view->data(); const auto item = view->data();
if (!item->out() && !item->isService()) { if (!item->out() && !item->isService()) {
update(false); update(false);
return RequirePremiumState::No; return { .known = true };
} }
} }
} }
if (user->isContact() // Here we know, that we're not in his contacts. if (user->isContact() // Here we know, that we're not in his contacts.
&& maybeHistory->loadedAtTop() // And no incoming messages. && maybeHistory->loadedAtTop() // And no incoming messages.
&& maybeHistory->loadedAtBottom()) { && maybeHistory->loadedAtBottom()) {
update(true); return {
.premiumRequired = !user->session().premium(),
.known = true,
};
} }
return RequirePremiumState::Unknown; return {};
} }
rpl::producer<DocumentData*> RandomHelloStickerValue( rpl::producer<DocumentData*> RandomHelloStickerValue(

View file

@ -116,8 +116,9 @@ public:
[[nodiscard]] auto subscriptionOptions() const [[nodiscard]] auto subscriptionOptions() const
-> const Data::PremiumSubscriptionOptions &; -> const Data::PremiumSubscriptionOptions &;
[[nodiscard]] rpl::producer<> somePremiumRequiredResolved() const; [[nodiscard]] auto someMessageMoneyRestrictionsResolved() const
void resolvePremiumRequired(not_null<UserData*> user); -> rpl::producer<>;
void resolveMessageMoneyRestrictions(not_null<UserData*> user);
private: private:
void reloadPromo(); void reloadPromo();
@ -166,10 +167,10 @@ private:
Data::PremiumSubscriptionOptions _subscriptionOptions; Data::PremiumSubscriptionOptions _subscriptionOptions;
rpl::event_stream<> _somePremiumRequiredResolved; rpl::event_stream<> _someMessageMoneyRestrictionsResolved;
base::flat_set<not_null<UserData*>> _resolvePremiumRequiredUsers; base::flat_set<not_null<UserData*>> _resolveMessageMoneyRequiredUsers;
base::flat_set<not_null<UserData*>> _resolvePremiumRequestedUsers; base::flat_set<not_null<UserData*>> _resolveMessageMoneyRequestedUsers;
bool _premiumRequiredRequestScheduled = false; bool _messageMoneyRequestScheduled = false;
}; };
@ -244,12 +245,12 @@ private:
}; };
enum class RequirePremiumState { struct MessageMoneyRestriction {
Unknown, int starsPerMessage = 0;
Yes, bool premiumRequired = false;
No, bool known = false;
}; };
[[nodiscard]] RequirePremiumState ResolveRequiresPremiumToWrite( [[nodiscard]] MessageMoneyRestriction ResolveMessageMoneyRestrictions(
not_null<PeerData*> peer, not_null<PeerData*> peer,
History *maybeHistory); History *maybeHistory);

View file

@ -283,8 +283,9 @@ RecipientRow::RecipientRow(
, _maybeHistory(maybeHistory) , _maybeHistory(maybeHistory)
, _resolvePremiumRequired(maybeLockedSt != nullptr) { , _resolvePremiumRequired(maybeLockedSt != nullptr) {
if (maybeLockedSt if (maybeLockedSt
&& (Api::ResolveRequiresPremiumToWrite(peer, maybeHistory) && Api::ResolveMessageMoneyRestrictions(
== Api::RequirePremiumState::Yes)) { peer,
maybeHistory).premiumRequired) {
_lockedSt = maybeLockedSt; _lockedSt = maybeLockedSt;
} }
} }
@ -305,8 +306,9 @@ bool RecipientRow::refreshLock(
not_null<const style::PeerListItem*> maybeLockedSt) { not_null<const style::PeerListItem*> maybeLockedSt) {
if (const auto user = peer()->asUser()) { if (const auto user = peer()->asUser()) {
const auto locked = _resolvePremiumRequired const auto locked = _resolvePremiumRequired
&& (Api::ResolveRequiresPremiumToWrite(user, _maybeHistory) && Api::ResolveMessageMoneyRestrictions(
== Api::RequirePremiumState::Yes); user,
_maybeHistory).premiumRequired;
if (this->locked() != locked) { if (this->locked() != locked) {
setLocked(locked ? maybeLockedSt.get() : nullptr); setLocked(locked ? maybeLockedSt.get() : nullptr);
return true; return true;
@ -320,20 +322,22 @@ void RecipientRow::preloadUserpic() {
if (!_resolvePremiumRequired) { if (!_resolvePremiumRequired) {
return; return;
} else if (Api::ResolveRequiresPremiumToWrite(peer(), _maybeHistory) } else if (!Api::ResolveMessageMoneyRestrictions(
== Api::RequirePremiumState::Unknown) { peer(),
_maybeHistory).known) {
const auto user = peer()->asUser(); const auto user = peer()->asUser();
user->session().api().premium().resolvePremiumRequired(user); user->session().api().premium().resolveMessageMoneyRestrictions(
user);
} }
} }
void TrackPremiumRequiredChanges( void TrackMessageMoneyRestrictionsChanges(
not_null<PeerListController*> controller, not_null<PeerListController*> controller,
rpl::lifetime &lifetime) { rpl::lifetime &lifetime) {
const auto session = &controller->session(); const auto session = &controller->session();
rpl::merge( rpl::merge(
Data::AmPremiumValue(session) | rpl::to_empty, Data::AmPremiumValue(session) | rpl::to_empty,
session->api().premium().somePremiumRequiredResolved() session->api().premium().someMessageMoneyRestrictionsResolved()
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
const auto st = &controller->computeListSt().item; const auto st = &controller->computeListSt().item;
const auto delegate = controller->delegate(); const auto delegate = controller->delegate();
@ -726,7 +730,7 @@ std::unique_ptr<PeerListRow> ContactsBoxController::createRow(
return std::make_unique<PeerListRow>(user); return std::make_unique<PeerListRow>(user);
} }
RecipientPremiumRequiredError WritePremiumRequiredError( RecipientMoneyRestrictionError WriteMoneyRestrictionError(
not_null<UserData*> user) { not_null<UserData*> user) {
return { return {
.text = tr::lng_send_non_premium_message_toast( .text = tr::lng_send_non_premium_message_toast(
@ -759,7 +763,7 @@ ChooseRecipientBoxController::ChooseRecipientBoxController(
, _session(args.session) , _session(args.session)
, _callback(std::move(args.callback)) , _callback(std::move(args.callback))
, _filter(std::move(args.filter)) , _filter(std::move(args.filter))
, _premiumRequiredError(std::move(args.premiumRequiredError)) { , _moneyRestrictionError(std::move(args.moneyRestrictionError)) {
} }
Main::Session &ChooseRecipientBoxController::session() const { Main::Session &ChooseRecipientBoxController::session() const {
@ -769,14 +773,17 @@ Main::Session &ChooseRecipientBoxController::session() const {
void ChooseRecipientBoxController::prepareViewHook() { void ChooseRecipientBoxController::prepareViewHook() {
delegate()->peerListSetTitle(tr::lng_forward_choose()); delegate()->peerListSetTitle(tr::lng_forward_choose());
if (_premiumRequiredError) { if (_moneyRestrictionError) {
TrackPremiumRequiredChanges(this, lifetime()); TrackMessageMoneyRestrictionsChanges(this, lifetime());
} }
} }
bool ChooseRecipientBoxController::showLockedError( bool ChooseRecipientBoxController::showLockedError(
not_null<PeerListRow*> row) { not_null<PeerListRow*> row) {
return RecipientRow::ShowLockedError(this, row, _premiumRequiredError); return RecipientRow::ShowLockedError(
this,
row,
_moneyRestrictionError);
} }
void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) { void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
@ -836,7 +843,7 @@ void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
bool RecipientRow::ShowLockedError( bool RecipientRow::ShowLockedError(
not_null<PeerListController*> controller, not_null<PeerListController*> controller,
not_null<PeerListRow*> row, not_null<PeerListRow*> row,
Fn<RecipientPremiumRequiredError(not_null<UserData*>)> error) { Fn<RecipientMoneyRestrictionError(not_null<UserData*>)> error) {
if (!static_cast<RecipientRow*>(row.get())->locked()) { if (!static_cast<RecipientRow*>(row.get())->locked()) {
return false; return false;
} }
@ -860,15 +867,15 @@ auto ChooseRecipientBoxController::createRow(
: ((peer->isBroadcast() && !Data::CanSendAnything(peer)) : ((peer->isBroadcast() && !Data::CanSendAnything(peer))
|| peer->isRepliesChat() || peer->isRepliesChat()
|| peer->isVerifyCodes() || peer->isVerifyCodes()
|| (peer->isUser() && (_premiumRequiredError || (peer->isUser() && (_moneyRestrictionError
? !peer->asUser()->canSendIgnoreRequirePremium() ? !peer->asUser()->canSendIgnoreMoneyRestrictions()
: !Data::CanSendAnything(peer)))); : !Data::CanSendAnything(peer))));
if (skip) { if (skip) {
return nullptr; return nullptr;
} }
auto result = std::make_unique<Row>( auto result = std::make_unique<Row>(
history, history,
_premiumRequiredError ? &computeListSt().item : nullptr); _moneyRestrictionError ? &computeListSt().item : nullptr);
return result; return result;
} }

View file

@ -93,11 +93,11 @@ private:
}; };
struct RecipientPremiumRequiredError { struct RecipientMoneyRestrictionError {
TextWithEntities text; TextWithEntities text;
}; };
[[nodiscard]] RecipientPremiumRequiredError WritePremiumRequiredError( [[nodiscard]] RecipientMoneyRestrictionError WriteMoneyRestrictionError(
not_null<UserData*> user); not_null<UserData*> user);
class RecipientRow : public PeerListRow { class RecipientRow : public PeerListRow {
@ -112,7 +112,7 @@ public:
[[nodiscard]] static bool ShowLockedError( [[nodiscard]] static bool ShowLockedError(
not_null<PeerListController*> controller, not_null<PeerListController*> controller,
not_null<PeerListRow*> row, not_null<PeerListRow*> row,
Fn<RecipientPremiumRequiredError(not_null<UserData*>)> error); Fn<RecipientMoneyRestrictionError(not_null<UserData*>)> error);
[[nodiscard]] History *maybeHistory() const { [[nodiscard]] History *maybeHistory() const {
return _maybeHistory; return _maybeHistory;
@ -135,7 +135,7 @@ private:
}; };
void TrackPremiumRequiredChanges( void TrackMessageMoneyRestrictionsChanges(
not_null<PeerListController*> controller, not_null<PeerListController*> controller,
rpl::lifetime &lifetime); rpl::lifetime &lifetime);
@ -261,8 +261,8 @@ struct ChooseRecipientArgs {
FnMut<void(not_null<Data::Thread*>)> callback; FnMut<void(not_null<Data::Thread*>)> callback;
Fn<bool(not_null<Data::Thread*>)> filter; Fn<bool(not_null<Data::Thread*>)> filter;
using PremiumRequiredError = RecipientPremiumRequiredError; using MoneyRestrictionError = RecipientMoneyRestrictionError;
Fn<PremiumRequiredError(not_null<UserData*>)> premiumRequiredError; Fn<MoneyRestrictionError(not_null<UserData*>)> moneyRestrictionError;
}; };
class ChooseRecipientBoxController class ChooseRecipientBoxController
@ -290,8 +290,8 @@ private:
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
FnMut<void(not_null<Data::Thread*>)> _callback; FnMut<void(not_null<Data::Thread*>)> _callback;
Fn<bool(not_null<Data::Thread*>)> _filter; Fn<bool(not_null<Data::Thread*>)> _filter;
Fn<RecipientPremiumRequiredError( Fn<RecipientMoneyRestrictionError(
not_null<UserData*>)> _premiumRequiredError; not_null<UserData*>)> _moneyRestrictionError;
}; };

View file

@ -584,8 +584,8 @@ void AddParticipantsBoxController::subscribeToMigration() {
} }
void AddParticipantsBoxController::rowClicked(not_null<PeerListRow*> row) { void AddParticipantsBoxController::rowClicked(not_null<PeerListRow*> row) {
const auto premiumRequiredError = WritePremiumRequiredError; const auto moneyRestrictionError = WriteMoneyRestrictionError;
if (RecipientRow::ShowLockedError(this, row, premiumRequiredError)) { if (RecipientRow::ShowLockedError(this, row, moneyRestrictionError)) {
return; return;
} }
const auto &serverConfig = session().serverConfig(); const auto &serverConfig = session().serverConfig();
@ -614,7 +614,7 @@ void AddParticipantsBoxController::itemDeselectedHook(
void AddParticipantsBoxController::prepareViewHook() { void AddParticipantsBoxController::prepareViewHook() {
updateTitle(); updateTitle();
TrackPremiumRequiredChanges(this, lifetime()); TrackMessageMoneyRestrictionsChanges(this, lifetime());
} }
int AddParticipantsBoxController::alreadyInCount() const { int AddParticipantsBoxController::alreadyInCount() const {

View file

@ -1530,7 +1530,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
}; };
auto filterCallback = [](not_null<Data::Thread*> thread) { auto filterCallback = [](not_null<Data::Thread*> thread) {
if (const auto user = thread->peer()->asUser()) { if (const auto user = thread->peer()->asUser()) {
if (user->canSendIgnoreRequirePremium()) { if (user->canSendIgnoreMoneyRestrictions()) {
return true; return true;
} }
} }
@ -1541,7 +1541,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
.copyCallback = std::move(copyCallback), .copyCallback = std::move(copyCallback),
.submitCallback = std::move(submitCallback), .submitCallback = std::move(submitCallback),
.filterCallback = std::move(filterCallback), .filterCallback = std::move(filterCallback),
.premiumRequiredError = SharePremiumRequiredError(), .moneyRestrictionError = ShareMessageMoneyRestrictionError(),
}); });
*box = Ui::MakeWeak(object.data()); *box = Ui::MakeWeak(object.data());
return object; return object;

View file

@ -721,11 +721,11 @@ ShareBox::Inner::Inner(
_rowHeight = st::shareRowHeight; _rowHeight = st::shareRowHeight;
setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_OpaquePaintEvent);
if (_descriptor.premiumRequiredError) { if (_descriptor.moneyRestrictionError) {
const auto session = _descriptor.session; const auto session = _descriptor.session;
rpl::merge( rpl::merge(
Data::AmPremiumValue(session) | rpl::to_empty, Data::AmPremiumValue(session) | rpl::to_empty,
session->api().premium().somePremiumRequiredResolved() session->api().premium().someMessageMoneyRestrictionsResolved()
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
refreshLockedRows(); refreshLockedRows();
}, lifetime()); }, lifetime());
@ -794,7 +794,7 @@ bool ShareBox::Inner::showLockedError(not_null<Chat*> chat) {
::Settings::ShowPremiumPromoToast( ::Settings::ShowPremiumPromoToast(
Main::MakeSessionShow(_show, _descriptor.session), Main::MakeSessionShow(_show, _descriptor.session),
ChatHelpers::ResolveWindowDefault(), ChatHelpers::ResolveWindowDefault(),
_descriptor.premiumRequiredError(chat->peer->asUser()).text, _descriptor.moneyRestrictionError(chat->peer->asUser()).text,
u"require_premium"_q); u"require_premium"_q);
return true; return true;
} }
@ -803,10 +803,9 @@ void ShareBox::Inner::refreshLockedRows() {
auto changed = false; auto changed = false;
for (const auto &[peer, data] : _dataMap) { for (const auto &[peer, data] : _dataMap) {
const auto history = data->history; const auto history = data->history;
const auto locked = (Api::ResolveRequiresPremiumToWrite( const auto locked = Api::ResolveMessageMoneyRestrictions(
history->peer, history->peer,
history history).premiumRequired;
) == Api::RequirePremiumState::Yes);
if (data->locked != locked) { if (data->locked != locked) {
data->locked = locked; data->locked = locked;
changed = true; changed = true;
@ -814,10 +813,9 @@ void ShareBox::Inner::refreshLockedRows() {
} }
for (const auto &data : d_byUsernameFiltered) { for (const auto &data : d_byUsernameFiltered) {
const auto history = data->history; const auto history = data->history;
const auto locked = (Api::ResolveRequiresPremiumToWrite( const auto locked = Api::ResolveMessageMoneyRestrictions(
history->peer, history->peer,
history history).premiumRequired;
) == Api::RequirePremiumState::Yes);
if (data->locked != locked) { if (data->locked != locked) {
data->locked = locked; data->locked = locked;
changed = true; changed = true;
@ -887,12 +885,11 @@ void ShareBox::Inner::updateChatName(not_null<Chat*> chat) {
} }
void ShareBox::Inner::initChatLocked(not_null<Chat*> chat) { void ShareBox::Inner::initChatLocked(not_null<Chat*> chat) {
if (_descriptor.premiumRequiredError) { if (_descriptor.moneyRestrictionError) {
const auto history = chat->history; const auto history = chat->history;
if (Api::ResolveRequiresPremiumToWrite( if (Api::ResolveMessageMoneyRestrictions(
history->peer, history->peer,
history history).premiumRequired) {
) == Api::RequirePremiumState::Yes) {
chat->locked = true; chat->locked = true;
} }
} }
@ -1019,14 +1016,14 @@ void ShareBox::Inner::loadProfilePhotos() {
void ShareBox::Inner::preloadUserpic(not_null<Dialogs::Entry*> entry) { void ShareBox::Inner::preloadUserpic(not_null<Dialogs::Entry*> entry) {
entry->chatListPreloadData(); entry->chatListPreloadData();
const auto history = entry->asHistory(); const auto history = entry->asHistory();
if (!_descriptor.premiumRequiredError || !history) { if (!_descriptor.moneyRestrictionError || !history) {
return; return;
} else if (Api::ResolveRequiresPremiumToWrite( } else if (!Api::ResolveMessageMoneyRestrictions(
history->peer, history->peer,
history history).known) {
) == Api::RequirePremiumState::Unknown) {
const auto user = history->peer->asUser(); const auto user = history->peer->asUser();
_descriptor.session->api().premium().resolvePremiumRequired(user); _descriptor.session->api().premium().resolveMessageMoneyRestrictions(
user);
} }
} }
@ -1707,7 +1704,7 @@ void FastShareMessage(
const auto requiresInline = item->requiresSendInlineRight(); const auto requiresInline = item->requiresSendInlineRight();
auto filterCallback = [=](not_null<Data::Thread*> thread) { auto filterCallback = [=](not_null<Data::Thread*> thread) {
if (const auto user = thread->peer()->asUser()) { if (const auto user = thread->peer()->asUser()) {
if (user->canSendIgnoreRequirePremium()) { if (user->canSendIgnoreMoneyRestrictions()) {
return true; return true;
} }
} }
@ -1733,7 +1730,7 @@ void FastShareMessage(
.captionsCount = ItemsForwardCaptionsCount(items), .captionsCount = ItemsForwardCaptionsCount(items),
.show = !hasOnlyForcedForwardedInfo, .show = !hasOnlyForcedForwardedInfo,
}, },
.premiumRequiredError = SharePremiumRequiredError(), .moneyRestrictionError = ShareMessageMoneyRestrictionError(),
}), Ui::LayerOption::CloseOther); }), Ui::LayerOption::CloseOther);
} }
@ -1806,7 +1803,7 @@ void FastShareLink(
}; };
auto filterCallback = [](not_null<::Data::Thread*> thread) { auto filterCallback = [](not_null<::Data::Thread*> thread) {
if (const auto user = thread->peer()->asUser()) { if (const auto user = thread->peer()->asUser()) {
if (user->canSendIgnoreRequirePremium()) { if (user->canSendIgnoreMoneyRestrictions()) {
return true; return true;
} }
} }
@ -1819,13 +1816,13 @@ void FastShareLink(
.submitCallback = std::move(submitCallback), .submitCallback = std::move(submitCallback),
.filterCallback = std::move(filterCallback), .filterCallback = std::move(filterCallback),
.st = st, .st = st,
.premiumRequiredError = SharePremiumRequiredError(), .moneyRestrictionError = ShareMessageMoneyRestrictionError(),
}), }),
Ui::LayerOption::KeepOther, Ui::LayerOption::KeepOther,
anim::type::normal); anim::type::normal);
} }
auto SharePremiumRequiredError() auto ShareMessageMoneyRestrictionError()
-> Fn<RecipientPremiumRequiredError(not_null<UserData*>)> { -> Fn<RecipientMoneyRestrictionError(not_null<UserData*>)> {
return WritePremiumRequiredError; return WriteMoneyRestrictionError;
} }

View file

@ -87,9 +87,9 @@ void FastShareLink(
const QString &url, const QString &url,
ShareBoxStyleOverrides st = {}); ShareBoxStyleOverrides st = {});
struct RecipientPremiumRequiredError; struct RecipientMoneyRestrictionError;
[[nodiscard]] auto SharePremiumRequiredError() [[nodiscard]] auto ShareMessageMoneyRestrictionError()
-> Fn<RecipientPremiumRequiredError(not_null<UserData*>)>; -> Fn<RecipientMoneyRestrictionError(not_null<UserData*>)>;
class ShareBox final : public Ui::BoxContent { class ShareBox final : public Ui::BoxContent {
public: public:
@ -123,8 +123,9 @@ public:
bool show = false; bool show = false;
} forwardOptions; } forwardOptions;
using PremiumRequiredError = RecipientPremiumRequiredError; using MoneyRestrictionError = RecipientMoneyRestrictionError;
Fn<PremiumRequiredError(not_null<UserData*>)> premiumRequiredError; Fn<MoneyRestrictionError(
not_null<UserData*>)> moneyRestrictionError;
}; };
ShareBox(QWidget*, Descriptor &&descriptor); ShareBox(QWidget*, Descriptor &&descriptor);

View file

@ -178,7 +178,7 @@ object_ptr<ShareBox> ShareInviteLinkBox(
}; };
auto filterCallback = [](not_null<Data::Thread*> thread) { auto filterCallback = [](not_null<Data::Thread*> thread) {
if (const auto user = thread->peer()->asUser()) { if (const auto user = thread->peer()->asUser()) {
if (user->canSendIgnoreRequirePremium()) { if (user->canSendIgnoreMoneyRestrictions()) {
return true; return true;
} }
} }
@ -199,7 +199,7 @@ object_ptr<ShareBox> ShareInviteLinkBox(
tr::lng_group_call_copy_speaker_link(), tr::lng_group_call_copy_speaker_link(),
tr::lng_group_call_copy_listener_link()), tr::lng_group_call_copy_listener_link()),
.st = st.shareBox ? *st.shareBox : ShareBoxStyleOverrides(), .st = st.shareBox ? *st.shareBox : ShareBoxStyleOverrides(),
.premiumRequiredError = SharePremiumRequiredError(), .moneyRestrictionError = ShareMessageMoneyRestrictionError(),
}); });
*box = result.data(); *box = result.data();
return result; return result;

View file

@ -1272,8 +1272,6 @@ void ApplyChannelUpdate(
} else { } else {
channel->setLinkedChat(nullptr); channel->setLinkedChat(nullptr);
} }
channel->setStarsPerMessage(
update.vsend_paid_messages_stars().value_or_empty());
if (const auto history = channel->owner().historyLoaded(channel)) { if (const auto history = channel->owner().historyLoaded(channel)) {
if (const auto available = update.vavailable_min_id()) { if (const auto available = update.vavailable_min_id()) {
history->clearUpTill(available->v); history->clearUpTill(available->v);

View file

@ -120,7 +120,7 @@ bool CanSendAnyOf(
|| user->isRepliesChat() || user->isRepliesChat()
|| user->isVerifyCodes()) { || user->isVerifyCodes()) {
return false; return false;
} else if (user->meRequiresPremiumToWrite() } else if (user->requiresPremiumToWrite()
&& !user->session().premium()) { && !user->session().premium()) {
return false; return false;
} else if (rights } else if (rights
@ -177,7 +177,7 @@ SendError RestrictionError(
using Flag = ChatRestriction; using Flag = ChatRestriction;
if (const auto restricted = peer->amRestricted(restriction)) { if (const auto restricted = peer->amRestricted(restriction)) {
if (const auto user = peer->asUser()) { if (const auto user = peer->asUser()) {
if (user->meRequiresPremiumToWrite() if (user->requiresPremiumToWrite()
&& !user->session().premium()) { && !user->session().premium()) {
return SendError({ return SendError({
.text = tr::lng_restricted_send_non_premium( .text = tr::lng_restricted_send_non_premium(

View file

@ -1331,7 +1331,7 @@ Data::RestrictionCheckResult PeerData::amRestricted(
} }
}; };
if (const auto user = asUser()) { if (const auto user = asUser()) {
if (user->meRequiresPremiumToWrite() && !user->session().premium()) { if (user->requiresPremiumToWrite() && !user->session().premium()) {
return Result::Explicit(); return Result::Explicit();
} }
return (right == ChatRestriction::SendVoiceMessages return (right == ChatRestriction::SendVoiceMessages

View file

@ -224,11 +224,11 @@ inline auto DefaultRestrictionValue(
| ChatRestriction::SendVideoMessages); | ChatRestriction::SendVideoMessages);
auto allowedAny = PeerFlagsValue( auto allowedAny = PeerFlagsValue(
user, user,
(UserDataFlag::Deleted | UserDataFlag::MeRequiresPremiumToWrite) (UserDataFlag::Deleted | UserDataFlag::RequiresPremiumToWrite)
) | rpl::map([=](UserDataFlags flags) { ) | rpl::map([=](UserDataFlags flags) {
return (flags & UserDataFlag::Deleted) return (flags & UserDataFlag::Deleted)
? rpl::single(false) ? rpl::single(false)
: !(flags & UserDataFlag::MeRequiresPremiumToWrite) : !(flags & UserDataFlag::RequiresPremiumToWrite)
? rpl::single(true) ? rpl::single(true)
: AmPremiumValue(&user->session()); : AmPremiumValue(&user->session());
}) | rpl::flatten_latest(); }) | rpl::flatten_latest();

View file

@ -532,14 +532,22 @@ not_null<UserData*> Session::processUser(const MTPUser &data) {
| Flag::BotInlineGeo | Flag::BotInlineGeo
| Flag::Premium | Flag::Premium
| Flag::Support | Flag::Support
| Flag::SomeRequirePremiumToWrite | Flag::HasRequirePremiumToWrite
| Flag::RequirePremiumToWriteKnown | Flag::HasStarsPerMessage
| Flag::MessageMoneyRestrictionsKnown
| (!minimal | (!minimal
? Flag::Contact ? Flag::Contact
| Flag::MutualContact | Flag::MutualContact
| Flag::DiscardMinPhoto | Flag::DiscardMinPhoto
| Flag::StoriesHidden | Flag::StoriesHidden
: Flag()); : Flag());
const auto hasRequirePremiumToWrite
= data.is_contact_require_premium();
const auto hasStarsPerMessage
= data.vsend_paid_messages_stars().has_value();
if (!hasStarsPerMessage) {
result->setStarsPerMessage(0);
}
const auto storiesState = minimal const auto storiesState = minimal
? std::optional<Data::Stories::PeerSourceState>() ? std::optional<Data::Stories::PeerSourceState>()
: data.is_stories_unavailable() : data.is_stories_unavailable()
@ -554,14 +562,25 @@ not_null<UserData*> Session::processUser(const MTPUser &data) {
| (data.is_bot_inline_geo() ? Flag::BotInlineGeo : Flag()) | (data.is_bot_inline_geo() ? Flag::BotInlineGeo : Flag())
| (data.is_premium() ? Flag::Premium : Flag()) | (data.is_premium() ? Flag::Premium : Flag())
| (data.is_support() ? Flag::Support : Flag()) | (data.is_support() ? Flag::Support : Flag())
| (data.is_contact_require_premium() | (hasRequirePremiumToWrite
? (Flag::SomeRequirePremiumToWrite ? (Flag::HasRequirePremiumToWrite
| (result->someRequirePremiumToWrite() | (result->hasRequirePremiumToWrite()
? (result->requirePremiumToWriteKnown() ? (result->messageMoneyRestrictionsKnown()
? Flag::RequirePremiumToWriteKnown ? Flag::MessageMoneyRestrictionsKnown
: Flag()) : Flag())
: Flag())) : Flag()))
: Flag()) : Flag())
| (hasStarsPerMessage
? (Flag::HasStarsPerMessage
| (result->hasStarsPerMessage()
? (result->messageMoneyRestrictionsKnown()
? Flag::MessageMoneyRestrictionsKnown
: Flag())
: Flag()))
: Flag())
| ((!hasRequirePremiumToWrite && !hasStarsPerMessage)
? Flag::MessageMoneyRestrictionsKnown
: Flag())
| (!minimal | (!minimal
? (data.is_contact() ? Flag::Contact : Flag()) ? (data.is_contact() ? Flag::Contact : Flag())
| (data.is_mutual_contact() ? Flag::MutualContact : Flag()) | (data.is_mutual_contact() ? Flag::MutualContact : Flag())
@ -999,6 +1018,8 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
} }
channel->setPhoto(data.vphoto()); channel->setPhoto(data.vphoto());
channel->setStarsPerMessage(
data.vsend_paid_messages_stars().value_or_empty());
if (wasInChannel != channel->amIn()) { if (wasInChannel != channel->amIn()) {
flags |= UpdateFlag::ChannelAmIn; flags |= UpdateFlag::ChannelAmIn;
@ -4631,7 +4652,8 @@ void Session::serviceNotification(
MTPPeerColor(), // color MTPPeerColor(), // color
MTPPeerColor(), // profile_color MTPPeerColor(), // profile_color
MTPint(), // bot_active_users MTPint(), // bot_active_users
MTPlong())); // bot_verification_icon MTPlong(), // bot_verification_icon
MTPlong())); // send_paid_messages_stars
} }
const auto history = this->history(PeerData::kServiceNotificationsId); const auto history = this->history(PeerData::kServiceNotificationsId);
const auto insert = [=] { const auto insert = [=] {

View file

@ -513,19 +513,23 @@ bool UserData::hasStoriesHidden() const {
return (flags() & UserDataFlag::StoriesHidden); return (flags() & UserDataFlag::StoriesHidden);
} }
bool UserData::someRequirePremiumToWrite() const { bool UserData::hasRequirePremiumToWrite() const {
return (flags() & UserDataFlag::SomeRequirePremiumToWrite); return (flags() & UserDataFlag::HasRequirePremiumToWrite);
} }
bool UserData::meRequiresPremiumToWrite() const { bool UserData::hasStarsPerMessage() const {
return !isSelf() && (flags() & UserDataFlag::MeRequiresPremiumToWrite); return (flags() & UserDataFlag::HasStarsPerMessage);
} }
bool UserData::requirePremiumToWriteKnown() const { bool UserData::requiresPremiumToWrite() const {
return (flags() & UserDataFlag::RequirePremiumToWriteKnown); return !isSelf() && (flags() & UserDataFlag::RequiresPremiumToWrite);
} }
bool UserData::canSendIgnoreRequirePremium() const { bool UserData::messageMoneyRestrictionsKnown() const {
return (flags() & UserDataFlag::MessageMoneyRestrictionsKnown);
}
bool UserData::canSendIgnoreMoneyRestrictions() const {
return !isInaccessible() && !isRepliesChat() && !isVerifyCodes(); return !isInaccessible() && !isRepliesChat() && !isVerifyCodes();
} }
@ -732,6 +736,8 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
if (const auto pinned = update.vpinned_msg_id()) { if (const auto pinned = update.vpinned_msg_id()) {
SetTopPinnedMessageId(user, pinned->v); SetTopPinnedMessageId(user, pinned->v);
} }
user->setStarsPerMessage(
update.vsend_paid_messages_stars().value_or_empty());
using Flag = UserDataFlag; using Flag = UserDataFlag;
const auto mask = Flag::Blocked const auto mask = Flag::Blocked
| Flag::HasPhoneCalls | Flag::HasPhoneCalls
@ -739,8 +745,8 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
| Flag::CanPinMessages | Flag::CanPinMessages
| Flag::VoiceMessagesForbidden | Flag::VoiceMessagesForbidden
| Flag::ReadDatesPrivate | Flag::ReadDatesPrivate
| Flag::RequirePremiumToWriteKnown | Flag::MessageMoneyRestrictionsKnown
| Flag::MeRequiresPremiumToWrite; | Flag::RequiresPremiumToWrite;
user->setFlags((user->flags() & ~mask) user->setFlags((user->flags() & ~mask)
| (update.is_phone_calls_private() | (update.is_phone_calls_private()
? Flag::PhoneCallsPrivate ? Flag::PhoneCallsPrivate
@ -752,9 +758,9 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
? Flag::VoiceMessagesForbidden ? Flag::VoiceMessagesForbidden
: Flag()) : Flag())
| (update.is_read_dates_private() ? Flag::ReadDatesPrivate : Flag()) | (update.is_read_dates_private() ? Flag::ReadDatesPrivate : Flag())
| Flag::RequirePremiumToWriteKnown | Flag::MessageMoneyRestrictionsKnown
| (update.is_contact_require_premium() | (update.is_contact_require_premium()
? Flag::MeRequiresPremiumToWrite ? Flag::RequiresPremiumToWrite
: Flag())); : Flag()));
user->setIsBlocked(update.is_blocked()); user->setIsBlocked(update.is_blocked());
user->setCallsStatus(update.is_phone_calls_private() user->setCallsStatus(update.is_phone_calls_private()
@ -770,8 +776,6 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
user->setTranslationDisabled(update.is_translations_disabled()); user->setTranslationDisabled(update.is_translations_disabled());
user->setPrivateForwardName( user->setPrivateForwardName(
update.vprivate_forward_name().value_or_empty()); update.vprivate_forward_name().value_or_empty());
user->setStarsPerMessage(
update.vsend_paid_messages_stars().value_or_empty());
if (const auto info = user->botInfo.get()) { if (const auto info = user->botInfo.get()) {
const auto group = update.vbot_group_admin_rights() const auto group = update.vbot_group_admin_rights()

View file

@ -110,10 +110,11 @@ enum class UserDataFlag : uint32 {
StoriesHidden = (1 << 18), StoriesHidden = (1 << 18),
HasActiveStories = (1 << 19), HasActiveStories = (1 << 19),
HasUnreadStories = (1 << 20), HasUnreadStories = (1 << 20),
MeRequiresPremiumToWrite = (1 << 21), RequiresPremiumToWrite = (1 << 21),
SomeRequirePremiumToWrite = (1 << 22), HasRequirePremiumToWrite = (1 << 22),
RequirePremiumToWriteKnown = (1 << 23), HasStarsPerMessage = (1 << 23),
ReadDatesPrivate = (1 << 24), MessageMoneyRestrictionsKnown = (1 << 24),
ReadDatesPrivate = (1 << 25),
}; };
inline constexpr bool is_flag_type(UserDataFlag) { return true; }; inline constexpr bool is_flag_type(UserDataFlag) { return true; };
using UserDataFlags = base::flags<UserDataFlag>; using UserDataFlags = base::flags<UserDataFlag>;
@ -174,10 +175,11 @@ public:
[[nodiscard]] bool applyMinPhoto() const; [[nodiscard]] bool applyMinPhoto() const;
[[nodiscard]] bool hasPersonalPhoto() const; [[nodiscard]] bool hasPersonalPhoto() const;
[[nodiscard]] bool hasStoriesHidden() const; [[nodiscard]] bool hasStoriesHidden() const;
[[nodiscard]] bool someRequirePremiumToWrite() const; [[nodiscard]] bool hasRequirePremiumToWrite() const;
[[nodiscard]] bool meRequiresPremiumToWrite() const; [[nodiscard]] bool hasStarsPerMessage() const;
[[nodiscard]] bool requirePremiumToWriteKnown() const; [[nodiscard]] bool requiresPremiumToWrite() const;
[[nodiscard]] bool canSendIgnoreRequirePremium() const; [[nodiscard]] bool messageMoneyRestrictionsKnown() const;
[[nodiscard]] bool canSendIgnoreMoneyRestrictions() const;
[[nodiscard]] bool readDatesPrivate() const; [[nodiscard]] bool readDatesPrivate() const;
void setStarsPerMessage(int stars); void setStarsPerMessage(int stars);

View file

@ -4350,15 +4350,14 @@ void HistoryInner::refreshAboutView(bool force) {
if (!info->inited) { if (!info->inited) {
session().api().requestFullPeer(user); session().api().requestFullPeer(user);
} }
} else if (user->meRequiresPremiumToWrite()
&& !user->session().premium()
&& !historyHeight()) {
refresh();
} else if (!historyHeight()) { } else if (!historyHeight()) {
if (!user->isFullLoaded()) { if (user->starsPerMessage() > 0
session().api().requestFullPeer(user); || (user->requiresPremiumToWrite()
} else { && !user->session().premium())
|| user->isFullLoaded()) {
refresh(); refresh();
} else {
session().api().requestFullPeer(user);
} }
} }
} }

View file

@ -780,7 +780,7 @@ HistoryWidget::HistoryWidget(
) | rpl::start_with_next([=](UserData::Flags::Change change) { ) | rpl::start_with_next([=](UserData::Flags::Change change) {
if (change.diff & UserData::Flag::Premium) { if (change.diff & UserData::Flag::Premium) {
if (const auto user = _peer ? _peer->asUser() : nullptr) { if (const auto user = _peer ? _peer->asUser() : nullptr) {
if (user->meRequiresPremiumToWrite()) { if (user->requiresPremiumToWrite()) {
handlePeerUpdate(); handlePeerUpdate();
} }
} }

View file

@ -1199,12 +1199,15 @@ struct AuthorSelector {
_peer->owner().history(_peer), _peer->owner().history(_peer),
&computeListSt().item)); &computeListSt().item));
delegate()->peerListRefreshRows(); delegate()->peerListRefreshRows();
TrackPremiumRequiredChanges(this, _lifetime); TrackMessageMoneyRestrictionsChanges(this, _lifetime);
} }
void loadMoreRows() override { void loadMoreRows() override {
} }
void rowClicked(not_null<PeerListRow*> row) override { void rowClicked(not_null<PeerListRow*> row) override {
if (RecipientRow::ShowLockedError(this, row, WritePremiumRequiredError)) { if (RecipientRow::ShowLockedError(
this,
row,
WriteMoneyRestrictionError)) {
return; return;
} else if (const auto onstack = _click) { } else if (const auto onstack = _click) {
onstack(); onstack();
@ -1298,7 +1301,7 @@ void ShowReplyToChatBox(
.callback = [=](Chosen thread) { .callback = [=](Chosen thread) {
_singleChosen.fire_copy(thread); _singleChosen.fire_copy(thread);
}, },
.premiumRequiredError = WritePremiumRequiredError, .moneyRestrictionError = WriteMoneyRestrictionError,
}) { }) {
_authorRow = AuthorRowSelector( _authorRow = AuthorRowSelector(
session, session,

View file

@ -259,7 +259,9 @@ bool AboutView::refresh() {
if (user && !user->isSelf() && _history->isDisplayedEmpty()) { if (user && !user->isSelf() && _history->isDisplayedEmpty()) {
if (_item) { if (_item) {
return false; return false;
} else if (user->meRequiresPremiumToWrite() //} else if (user->starsPerMessage() > 0) {
// setItem(makeStarsPerMessage(), nullptr);
} else if (user->requiresPremiumToWrite()
&& !user->session().premium()) { && !user->session().premium()) {
setItem(makePremiumRequired(), nullptr); setItem(makePremiumRequired(), nullptr);
} else if (user->isBlocked()) { } else if (user->isBlocked()) {

View file

@ -61,7 +61,8 @@ PeerId GenerateUser(not_null<History*> history, const QString &name) {
MTPPeerColor(), // color MTPPeerColor(), // color
MTPPeerColor(), // profile_color MTPPeerColor(), // profile_color
MTPint(), // bot_active_users MTPint(), // bot_active_users
MTPlong())); // bot_verification_icon MTPlong(), // bot_verification_icon
MTPlong())); // send_paid_messages_stars
return peerId; return peerId;
} }

View file

@ -174,7 +174,8 @@ void Account::createSession(
MTPPeerColor(), // color MTPPeerColor(), // color
MTPPeerColor(), // profile_color MTPPeerColor(), // profile_color
MTPint(), // bot_active_users MTPint(), // bot_active_users
MTPlong()), // bot_verification_icon MTPlong(), // bot_verification_icon
MTPlong()), // send_paid_messages_stars
serialized, serialized,
streamVersion, streamVersion,
std::move(settings)); std::move(settings));

View file

@ -705,7 +705,7 @@ void ReplyArea::show(
using namespace HistoryView::Controls; using namespace HistoryView::Controls;
return (can return (can
|| !user || !user
|| !user->meRequiresPremiumToWrite() || !user->requiresPremiumToWrite()
|| user->session().premium()) || user->session().premium())
? WriteRestriction() ? WriteRestriction()
: WriteRestriction{ : WriteRestriction{

View file

@ -66,7 +66,7 @@ namespace Media::Stories {
const auto state = std::make_shared<State>(); const auto state = std::make_shared<State>();
auto filterCallback = [=](not_null<Data::Thread*> thread) { auto filterCallback = [=](not_null<Data::Thread*> thread) {
if (const auto user = thread->peer()->asUser()) { if (const auto user = thread->peer()->asUser()) {
if (user->canSendIgnoreRequirePremium()) { if (user->canSendIgnoreMoneyRestrictions()) {
return true; return true;
} }
} }
@ -183,7 +183,7 @@ namespace Media::Stories {
.submitCallback = std::move(submitCallback), .submitCallback = std::move(submitCallback),
.filterCallback = std::move(filterCallback), .filterCallback = std::move(filterCallback),
.st = st.shareBox ? *st.shareBox : ShareBoxStyleOverrides(), .st = st.shareBox ? *st.shareBox : ShareBoxStyleOverrides(),
.premiumRequiredError = SharePremiumRequiredError(), .moneyRestrictionError = ShareMessageMoneyRestrictionError(),
}); });
} }
@ -232,7 +232,7 @@ object_ptr<Ui::BoxContent> PrepareShareAtTimeBox(
const auto requiresInline = item->requiresSendInlineRight(); const auto requiresInline = item->requiresSendInlineRight();
auto filterCallback = [=](not_null<Data::Thread*> thread) { auto filterCallback = [=](not_null<Data::Thread*> thread) {
if (const auto user = thread->peer()->asUser()) { if (const auto user = thread->peer()->asUser()) {
if (user->canSendIgnoreRequirePremium()) { if (user->canSendIgnoreMoneyRestrictions()) {
return true; return true;
} }
} }
@ -262,7 +262,7 @@ object_ptr<Ui::BoxContent> PrepareShareAtTimeBox(
.captionsCount = ItemsForwardCaptionsCount({ item }), .captionsCount = ItemsForwardCaptionsCount({ item }),
.show = !hasOnlyForcedForwardedInfo, .show = !hasOnlyForcedForwardedInfo,
}, },
.premiumRequiredError = SharePremiumRequiredError(), .moneyRestrictionError = ShareMessageMoneyRestrictionError(),
}); });
} }

View file

@ -113,7 +113,8 @@ bool TTLValidator::can() const {
&& !_peer->isSelf() && !_peer->isSelf()
&& !_peer->isNotificationsUser() && !_peer->isNotificationsUser()
&& !_peer->asUser()->isInaccessible() && !_peer->asUser()->isInaccessible()
&& (!_peer->asUser()->meRequiresPremiumToWrite() && !_peer->asUser()->starsPerMessage()
&& (!_peer->asUser()->requiresPremiumToWrite()
|| _peer->session().premium())) || _peer->session().premium()))
|| (_peer->isChat() || (_peer->isChat()
&& _peer->asChat()->canEditInformation() && _peer->asChat()->canEditInformation()

View file

@ -84,7 +84,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
storage.fileWebp#1081464c = storage.FileType; storage.fileWebp#1081464c = storage.FileType;
userEmpty#d3bc4b7a id:long = User; userEmpty#d3bc4b7a id:long = User;
user#4b46c37e flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true flags2:# bot_can_edit:flags2.1?true close_friend:flags2.2?true stories_hidden:flags2.3?true stories_unavailable:flags2.4?true contact_require_premium:flags2.10?true bot_business:flags2.11?true bot_has_main_app:flags2.13?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector<Username> stories_max_id:flags2.5?int color:flags2.8?PeerColor profile_color:flags2.9?PeerColor bot_active_users:flags2.12?int bot_verification_icon:flags2.14?long = User; user#20b1422 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true flags2:# bot_can_edit:flags2.1?true close_friend:flags2.2?true stories_hidden:flags2.3?true stories_unavailable:flags2.4?true contact_require_premium:flags2.10?true bot_business:flags2.11?true bot_has_main_app:flags2.13?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector<Username> stories_max_id:flags2.5?int color:flags2.8?PeerColor profile_color:flags2.9?PeerColor bot_active_users:flags2.12?int bot_verification_icon:flags2.14?long send_paid_messages_stars:flags2.15?long = User;
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
userProfilePhoto#82d1f706 flags:# has_video:flags.0?true personal:flags.2?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto; userProfilePhoto#82d1f706 flags:# has_video:flags.0?true personal:flags.2?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;
@ -99,11 +99,11 @@ userStatusLastMonth#65899777 flags:# by_me:flags.0?true = UserStatus;
chatEmpty#29562865 id:long = Chat; chatEmpty#29562865 id:long = Chat;
chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
chatForbidden#6592a1a7 id:long title:string = Chat; chatForbidden#6592a1a7 id:long title:string = Chat;
channel#e00998b7 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 signature_profiles:flags2.12?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 color:flags2.7?PeerColor profile_color:flags2.8?PeerColor emoji_status:flags2.9?EmojiStatus level:flags2.10?int subscription_until_date:flags2.11?int bot_verification_icon:flags2.13?long = Chat; channel#7482147e 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 signature_profiles:flags2.12?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 color:flags2.7?PeerColor profile_color:flags2.8?PeerColor emoji_status:flags2.9?EmojiStatus level:flags2.10?int subscription_until_date:flags2.11?int bot_verification_icon:flags2.13?long send_paid_messages_stars:flags2.14?long = Chat;
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat; channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#2633421b 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 reactions_limit:flags.20?int = ChatFull; chatFull#2633421b 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 reactions_limit:flags.20?int = ChatFull;
channelFull#67d2e29d 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 view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true paid_media_allowed:flags2.14?true can_view_stars_revenue:flags2.15?true paid_reactions_available:flags2.16?true stargifts_available:flags2.19?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 reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet bot_verification:flags2.17?BotVerification stargifts_count:flags2.18?int send_paid_messages_stars:flags2.20?long = ChatFull; channelFull#52d6806b 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 view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true paid_media_allowed:flags2.14?true can_view_stars_revenue:flags2.15?true paid_reactions_available:flags2.16?true stargifts_available:flags2.19?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 reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet bot_verification:flags2.17?BotVerification stargifts_count:flags2.18?int = ChatFull;
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant; chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant; chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
@ -1942,6 +1942,10 @@ paidReactionPrivacyPeer#dc6cfcf0 peer:InputPeer = PaidReactionPrivacy;
account.paidMessagesRevenue#1e109708 stars_amount:long = account.PaidMessagesRevenue; account.paidMessagesRevenue#1e109708 stars_amount:long = account.PaidMessagesRevenue;
requirementToContactEmpty#50a9839 = RequirementToContact;
requirementToContactPremium#e581e4e9 = RequirementToContact;
requirementToContactPaidMessages#b4f67e93 stars_amount:long = RequirementToContact;
---functions--- ---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -2099,7 +2103,7 @@ account.getPaidMessagesRevenue#f1266f38 user_id:InputUser = account.PaidMessages
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>; users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#b60f5918 id:InputUser = users.UserFull; users.getFullUser#b60f5918 id:InputUser = users.UserFull;
users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector<SecureValueError> = Bool; users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector<SecureValueError> = Bool;
users.getIsPremiumRequiredToContact#a622aa10 id:Vector<InputUser> = Vector<Bool>; users.getRequirementsToContact#d89a83a3 id:Vector<InputUser> = Vector<RequirementToContact>;
contacts.getContactIDs#7adc669d hash:long = Vector<int>; contacts.getContactIDs#7adc669d hash:long = Vector<int>;
contacts.getStatuses#c4a353ee = Vector<ContactStatus>; contacts.getStatuses#c4a353ee = Vector<ContactStatus>;

View file

@ -1201,7 +1201,8 @@ void Filler::addThemeEdit() {
if (!user || user->isInaccessible()) { if (!user || user->isInaccessible()) {
return; return;
} }
if (user->meRequiresPremiumToWrite() && !user->session().premium()) { if ((user->requiresPremiumToWrite() && !user->session().premium())
|| user->starsPerMessage() > 0) {
return; return;
} }
const auto controller = _controller; const auto controller = _controller;
@ -1709,7 +1710,7 @@ void PeerMenuShareContactBox(
ChooseRecipientArgs{ ChooseRecipientArgs{
.session = &navigation->session(), .session = &navigation->session(),
.callback = std::move(callback), .callback = std::move(callback),
.premiumRequiredError = WritePremiumRequiredError, .moneyRestrictionError = WriteMoneyRestrictionError,
}), }),
[](not_null<PeerListBox*> box) { [](not_null<PeerListBox*> box) {
box->addButton(tr::lng_cancel(), [=] { box->addButton(tr::lng_cancel(), [=] {
@ -1924,7 +1925,7 @@ object_ptr<Ui::BoxContent> PrepareChooseRecipientBox(
.session = session, .session = session,
.callback = std::move(callback), .callback = std::move(callback),
.filter = filter, .filter = filter,
.premiumRequiredError = WritePremiumRequiredError, .moneyRestrictionError = WriteMoneyRestrictionError,
}) })
, _selectable(selectable) { , _selectable(selectable) {
} }
@ -2145,7 +2146,7 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
.callback = [=](Chosen thread) { .callback = [=](Chosen thread) {
_singleChosen.fire_copy(thread); _singleChosen.fire_copy(thread);
}, },
.premiumRequiredError = WritePremiumRequiredError, .moneyRestrictionError = WriteMoneyRestrictionError,
}) { }) {
} }
@ -2555,7 +2556,7 @@ QPointer<Ui::BoxContent> ShowShareGameBox(
.session = &navigation->session(), .session = &navigation->session(),
.callback = std::move(chosen), .callback = std::move(chosen),
.filter = std::move(filter), .filter = std::move(filter),
.premiumRequiredError = WritePremiumRequiredError, .moneyRestrictionError = WriteMoneyRestrictionError,
}), }),
std::move(initBox))); std::move(initBox)));
return weak->data(); return weak->data();