diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp index eba2b0e3af..5c74632618 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp @@ -45,9 +45,7 @@ namespace { constexpr auto kPremiumsRowId = PeerId(FakeChatId(BareId(1))).value; constexpr auto kMiniAppsRowId = PeerId(FakeChatId(BareId(2))).value; -constexpr auto kGetPercent = 85; constexpr auto kStarsMin = 1; -constexpr auto kStarsMax = 10000; constexpr auto kDefaultChargeStars = 10; using Exceptions = Api::UserPrivacy::Exceptions; @@ -466,6 +464,7 @@ auto PrivacyExceptionsBoxController::createRow(not_null history) int valuesCount, Fn valueByIndex, int value, + int maxValue, Fn valueProgress, Fn valueFinished) { auto result = object_ptr(parent); @@ -478,7 +477,7 @@ auto PrivacyExceptionsBoxController::createRow(not_null history) *labelStyle); const auto max = Ui::CreateChild( raw, - QString::number(kStarsMax), + QString::number(maxValue), *labelStyle); const auto current = Ui::CreateChild( raw, @@ -999,28 +998,22 @@ void EditMessagesPrivacyBox( Ui::AddDividerText(inner, tr::lng_messages_privacy_about()); - const auto charged = inner->add( - object_ptr( - inner, - group, - kOptionCharge, - tr::lng_messages_privacy_charge(tr::now), - st::messagePrivacyCheck), - st::settingsSendTypePadding + style::margins( - 0, - st::messagePrivacyBottomSkip, - 0, - st::messagePrivacyBottomSkip)); + const auto available = session->appConfig().paidMessagesAvailable(); - Ui::AddDividerText(inner, tr::lng_messages_privacy_charge_about()); - - const auto chargeWrap = inner->add( - object_ptr>( - inner, - object_ptr(inner))); - const auto chargeInner = chargeWrap->entity(); - - Ui::AddSkip(chargeInner); + const auto charged = available + ? inner->add( + object_ptr( + inner, + group, + kOptionCharge, + tr::lng_messages_privacy_charge(tr::now), + st::messagePrivacyCheck), + st::settingsSendTypePadding + style::margins( + 0, + st::messagePrivacyBottomSkip, + 0, + st::messagePrivacyBottomSkip)) + : nullptr; struct State { rpl::variable stars; @@ -1028,54 +1021,67 @@ void EditMessagesPrivacyBox( const auto state = std::make_shared(); const auto savedValue = privacy->newChargeStarsCurrent(); - state->stars = SetupChargeSlider( - chargeInner, - session->user(), - savedValue); + if (available) { + Ui::AddDividerText(inner, tr::lng_messages_privacy_charge_about()); - Ui::AddSkip(chargeInner); - Ui::AddSubsectionTitle( - chargeInner, - tr::lng_messages_privacy_exceptions()); + const auto chargeWrap = inner->add( + object_ptr>( + inner, + object_ptr(inner))); + const auto chargeInner = chargeWrap->entity(); - const auto key = Api::UserPrivacy::Key::NoPaidMessages; - session->api().userPrivacy().reload(key); - auto label = session->api().userPrivacy().value( - key - ) | rpl::map([=](const Api::UserPrivacy::Rule &value) { - using namespace Settings; - const auto always = ExceptionUsersCount(value.always.peers); - return always - ? tr::lng_edit_privacy_exceptions_count( - tr::now, - lt_count, - always) - : QString(); - }); + Ui::AddSkip(chargeInner); - const auto exceptions = Settings::AddButtonWithLabel( - chargeInner, - tr::lng_messages_privacy_remove_fee(), - std::move(label), - st::settingsButtonNoIcon); + state->stars = SetupChargeSlider( + chargeInner, + session->user(), + savedValue); - const auto shower = exceptions->lifetime().make_state(); - exceptions->setClickedCallback([=] { - *shower = session->api().userPrivacy().value( + Ui::AddSkip(chargeInner); + Ui::AddSubsectionTitle( + chargeInner, + tr::lng_messages_privacy_exceptions()); + + const auto key = Api::UserPrivacy::Key::NoPaidMessages; + session->api().userPrivacy().reload(key); + auto label = session->api().userPrivacy().value( key - ) | rpl::take( - 1 - ) | rpl::start_with_next([=](const Api::UserPrivacy::Rule &value) { - EditNoPaidMessagesExceptions(controller, value); + ) | rpl::map([=](const Api::UserPrivacy::Rule &value) { + using namespace Settings; + const auto always = ExceptionUsersCount(value.always.peers); + return always + ? tr::lng_edit_privacy_exceptions_count( + tr::now, + lt_count, + always) + : QString(); }); - }); - Ui::AddSkip(chargeInner); - Ui::AddDividerText(chargeInner, tr::lng_messages_privacy_remove_about()); - using namespace rpl::mappers; - chargeWrap->toggleOn(group->value() | rpl::map(_1 == kOptionCharge)); - chargeWrap->finishAnimating(); + const auto exceptions = Settings::AddButtonWithLabel( + chargeInner, + tr::lng_messages_privacy_remove_fee(), + std::move(label), + st::settingsButtonNoIcon); + const auto shower = exceptions->lifetime().make_state(); + exceptions->setClickedCallback([=] { + *shower = session->api().userPrivacy().value( + key + ) | rpl::take( + 1 + ) | rpl::start_with_next([=](const Api::UserPrivacy::Rule &value) { + EditNoPaidMessagesExceptions(controller, value); + }); + }); + Ui::AddSkip(chargeInner); + Ui::AddDividerText( + chargeInner, + tr::lng_messages_privacy_remove_about()); + + using namespace rpl::mappers; + chargeWrap->toggleOn(group->value() | rpl::map(_1 == kOptionCharge)); + chargeWrap->finishAnimating(); + } using WeakToast = base::weak_ptr; const auto toast = std::make_shared(); const auto showToast = [=] { @@ -1108,7 +1114,9 @@ void EditMessagesPrivacyBox( if (!allowed()) { CreateRadiobuttonLock(restricted, st::messagePrivacyCheck); - CreateRadiobuttonLock(charged, st::messagePrivacyCheck); + if (charged) { + CreateRadiobuttonLock(charged, st::messagePrivacyCheck); + } group->setChangedCallback([=](int value) { if (value == kOptionPremium || value == kOptionCharge) { @@ -1170,19 +1178,20 @@ rpl::producer SetupChargeSlider( : tr::lng_messages_privacy_price()); auto values = std::vector(); + const auto maxStars = peer->session().appConfig().paidMessageStarsMax(); if (chargeStars < kStarsMin) { values.push_back(chargeStars); } - for (auto i = kStarsMin; i < 100; ++i) { + for (auto i = kStarsMin; i < std::min(100, maxStars); ++i) { values.push_back(i); } - for (auto i = 100; i < 1000; i += 10) { + for (auto i = 100; i < std::min(1000, maxStars); i += 10) { if (i < chargeStars + 10 && chargeStars < i) { values.push_back(chargeStars); } values.push_back(i); } - for (auto i = 1000; i < kStarsMax + 1; i += 100) { + for (auto i = 1000; i < maxStars + 1; i += 100) { if (i < chargeStars + 100 && chargeStars < i) { values.push_back(chargeStars); } @@ -1200,6 +1209,7 @@ rpl::producer SetupChargeSlider( valuesCount, [=](int index) { return values[index]; }, chargeStars, + maxStars, setStars, setStars), st::boxRowPadding); @@ -1208,19 +1218,18 @@ rpl::producer SetupChargeSlider( Ui::AddSkip(container, skip); auto dollars = state->stars.value() | rpl::map([=](int stars) { - const auto ratio = peer->session().appConfig().get( - u"stars_usd_withdraw_rate_x1000"_q, - 1200); - const auto dollars = int(base::SafeRound(stars * (ratio / 1000.))); + const auto ratio = peer->session().appConfig().starsWithdrawRate(); + const auto dollars = int(base::SafeRound(stars * ratio)); return '~' + Ui::FillAmountAndCurrency(dollars, u"USD"_q); }); + const auto percent = peer->session().appConfig().paidMessageCommission(); Ui::AddDividerText( container, (group ? tr::lng_rights_charge_price_about : tr::lng_messages_privacy_price_about)( lt_percent, - rpl::single(QString::number(kGetPercent) + '%'), + rpl::single(QString::number(percent / 10.) + '%'), lt_amount, std::move(dollars))); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp index ecb0b8e76a..486383f0bf 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp @@ -1160,34 +1160,39 @@ void ShowEditPeerPermissionsBox( rpl::variable starsPerMessage; }; const auto state = inner->lifetime().make_state(); + const auto channel = peer->asChannel(); + const auto available = channel && channel->paidMessagesAvailable(); Ui::AddSkip(inner); Ui::AddDivider(inner); - Ui::AddSkip(inner); - const auto starsPerMessage = peer->isChannel() - ? peer->asChannel()->starsPerMessage() - : 0; - const auto charging = inner->add(object_ptr( - inner, - tr::lng_rights_charge_stars(), - st::settingsButtonNoIcon)); - charging->toggleOn(rpl::single(starsPerMessage > 0)); - Ui::AddSkip(inner); - Ui::AddDividerText(inner, tr::lng_rights_charge_stars_about()); - - const auto chargeWrap = inner->add( - object_ptr>( + auto charging = (Ui::SettingsButton*)nullptr; + if (available) { + Ui::AddSkip(inner); + const auto starsPerMessage = peer->isChannel() + ? peer->asChannel()->starsPerMessage() + : 0; + charging = inner->add(object_ptr( inner, - object_ptr(inner))); - chargeWrap->toggleOn(charging->toggledValue()); - chargeWrap->finishAnimating(); - const auto chargeInner = chargeWrap->entity(); + tr::lng_rights_charge_stars(), + st::settingsButtonNoIcon)); + charging->toggleOn(rpl::single(starsPerMessage > 0)); + Ui::AddSkip(inner); + Ui::AddDividerText(inner, tr::lng_rights_charge_stars_about()); - Ui::AddSkip(chargeInner); - state->starsPerMessage = SetupChargeSlider( - chargeInner, - peer, - starsPerMessage); + const auto chargeWrap = inner->add( + object_ptr>( + inner, + object_ptr(inner))); + chargeWrap->toggleOn(charging->toggledValue()); + chargeWrap->finishAnimating(); + const auto chargeInner = chargeWrap->entity(); + + Ui::AddSkip(chargeInner); + state->starsPerMessage = SetupChargeSlider( + chargeInner, + peer, + starsPerMessage); + } static constexpr auto kSendRestrictions = Flag::EmbedLinks | Flag::SendGames @@ -1242,7 +1247,7 @@ void ShowEditPeerPermissionsBox( const auto boostsUnrestrict = hasRestrictions ? state->boostsUnrestrict.current() : 0; - const auto starsPerMessage = charging->toggled() + const auto starsPerMessage = (charging && charging->toggled()) ? state->starsPerMessage.current() : 0; done({ diff --git a/Telegram/SourceFiles/data/components/credits.cpp b/Telegram/SourceFiles/data/components/credits.cpp index cea4020d8e..a3e57c0d22 100644 --- a/Telegram/SourceFiles/data/components/credits.cpp +++ b/Telegram/SourceFiles/data/components/credits.cpp @@ -37,10 +37,7 @@ void Credits::apply(const MTPDupdateStarsBalance &data) { rpl::producer Credits::rateValue( not_null ownedBotOrChannel) { - return rpl::single( - _session->appConfig().get( - u"stars_usd_withdraw_rate_x1000"_q, - 1200) / 1000.); + return rpl::single(_session->appConfig().starsWithdrawRate()); } void Credits::load(bool force) { diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index f02f31d3d0..f21769328a 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -1167,7 +1167,8 @@ void ApplyChannelUpdate( | Flag::CanViewRevenue | Flag::PaidMediaAllowed | Flag::CanViewCreditsRevenue - | Flag::StargiftsAvailable; + | Flag::StargiftsAvailable + | Flag::PaidMessagesAvailable; channel->setFlags((channel->flags() & ~mask) | (update.is_can_set_username() ? Flag::CanSetUsername : Flag()) | (update.is_can_view_participants() @@ -1191,6 +1192,9 @@ void ApplyChannelUpdate( : Flag()) | (update.is_stargifts_available() ? Flag::StargiftsAvailable + : Flag()) + | (update.is_paid_messages_available() + ? Flag::PaidMessagesAvailable : Flag())); channel->setUserpicPhoto(update.vchat_photo()); if (const auto migratedFrom = update.vmigrated_from_chat_id()) { diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index d9e8353201..115516073c 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -72,6 +72,7 @@ enum class ChannelDataFlag : uint64 { CanViewCreditsRevenue = (1ULL << 34), SignatureProfiles = (1ULL << 35), StargiftsAvailable = (1ULL << 36), + PaidMessagesAvailable = (1ULL << 37), }; inline constexpr bool is_flag_type(ChannelDataFlag) { return true; }; using ChannelDataFlags = base::flags; @@ -262,6 +263,9 @@ public: [[nodiscard]] bool stargiftsAvailable() const { return flags() & Flag::StargiftsAvailable; } + [[nodiscard]] bool paidMessagesAvailable() const { + return flags() & Flag::PaidMessagesAvailable; + } [[nodiscard]] static ChatRestrictionsInfo KickedRestrictedRights( not_null participant); diff --git a/Telegram/SourceFiles/main/main_app_config.cpp b/Telegram/SourceFiles/main/main_app_config.cpp index c26d57126e..d1f10b7e8c 100644 --- a/Telegram/SourceFiles/main/main_app_config.cpp +++ b/Telegram/SourceFiles/main/main_app_config.cpp @@ -73,6 +73,22 @@ int AppConfig::starrefCommissionMax() const { return get(u"starref_max_commission_permille"_q, 900); } +float64 AppConfig::starsWithdrawRate() const { + return get(u"stars_usd_withdraw_rate_x1000"_q, 1300) / 1000.; +} + +bool AppConfig::paidMessagesAvailable() const { + return get(u"stars_paid_messages_available"_q, false); +} + +int AppConfig::paidMessageStarsMax() const { + return get(u"stars_paid_message_amount_max"_q, 10'000); +} + +int AppConfig::paidMessageCommission() const { + return get(u"stars_paid_message_commission_permille"_q, 850); +} + void AppConfig::refresh(bool force) { if (_requestId || !_api) { if (force) { diff --git a/Telegram/SourceFiles/main/main_app_config.h b/Telegram/SourceFiles/main/main_app_config.h index 3092aa5674..58a7da4de4 100644 --- a/Telegram/SourceFiles/main/main_app_config.h +++ b/Telegram/SourceFiles/main/main_app_config.h @@ -72,6 +72,11 @@ public: [[nodiscard]] int starrefCommissionMin() const; [[nodiscard]] int starrefCommissionMax() const; + [[nodiscard]] float64 starsWithdrawRate() const; + [[nodiscard]] bool paidMessagesAvailable() const; + [[nodiscard]] int paidMessageStarsMax() const; + [[nodiscard]] int paidMessageCommission() const; + void refresh(bool force = false); private: