diff --git a/Telegram/SourceFiles/api/api_premium.cpp b/Telegram/SourceFiles/api/api_premium.cpp index e03b15b52..4ca994a7d 100644 --- a/Telegram/SourceFiles/api/api_premium.cpp +++ b/Telegram/SourceFiles/api/api_premium.cpp @@ -10,11 +10,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_premium_option.h" #include "api/api_text_entities.h" #include "apiwrap.h" +#include "base/random.h" #include "data/data_document.h" #include "data/data_peer.h" #include "data/data_peer_values.h" #include "data/data_session.h" #include "main/main_session.h" +#include "payments/payments_form.h" #include "ui/text/format_values.h" namespace Api { @@ -353,9 +355,16 @@ rpl::producer PremiumGiftCodeOptions::request() { for (const auto &tlOption : result.v) { const auto &data = tlOption.data(); tlMapOptions[data.vusers().v].push_back(tlOption); + + const auto token = Token{ data.vusers().v, data.vmonths().v }; + _stores[token] = Store{ + .amount = data.vamount().v, + .product = qs(data.vstore_product().value_or_empty()), + .quantity = data.vstore_quantity().value_or_empty(), + }; } for (const auto &[amount, tlOptions] : tlMapOptions) { - if (amount == 1) { + if (amount == 1 && _optionsForOnePerson.currency.isEmpty()) { _optionsForOnePerson.currency = qs( tlOptions.front().data().vcurrency()); for (const auto &option : tlOptions) { @@ -376,6 +385,26 @@ rpl::producer PremiumGiftCodeOptions::request() { }; } +Payments::InvoicePremiumGiftCode PremiumGiftCodeOptions::invoice( + int users, + int monthsIndex) { + const auto randomId = base::RandomValue(); + const auto token = Token{ + users, + _optionsForOnePerson.months[monthsIndex], + }; + const auto &store = _stores[token]; + return Payments::InvoicePremiumGiftCode{ + .randomId = randomId, + .currency = _optionsForOnePerson.currency, + .amount = store.amount, + .storeProduct = store.product, + .storeQuantity = store.quantity, + .users = token.users, + .months = token.months, + }; +} + Data::SubscriptionOptions PremiumGiftCodeOptions::options(int amount) { const auto it = _subscriptionOptions.find(amount); if (it != end(_subscriptionOptions)) { diff --git a/Telegram/SourceFiles/api/api_premium.h b/Telegram/SourceFiles/api/api_premium.h index 95c0dfad1..5a2205725 100644 --- a/Telegram/SourceFiles/api/api_premium.h +++ b/Telegram/SourceFiles/api/api_premium.h @@ -16,6 +16,10 @@ namespace Main { class Session; } // namespace Main +namespace Payments { +struct InvoicePremiumGiftCode; +} // namespace Payments + namespace Api { struct GiftCode { @@ -147,10 +151,25 @@ public: [[nodiscard]] rpl::producer request(); [[nodiscard]] Data::SubscriptionOptions options(int amount); + [[nodiscard]] Payments::InvoicePremiumGiftCode invoice( + int users, + int monthsIndex); private: - const not_null _peer; + struct Token final { + int users = 0; + int months = 0; + + friend inline constexpr auto operator<=>(Token, Token) = default; + + }; + struct Store final { + uint64 amount = 0; + QString product; + int quantity = 0; + }; using Amount = int; + const not_null _peer; base::flat_map _subscriptionOptions; struct { std::vector months; @@ -158,6 +177,8 @@ private: QString currency; } _optionsForOnePerson; + base::flat_map _stores; + MTP::Sender _api; }; diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp index d85cd6201..739bb5a28 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp @@ -15,6 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/boosts/giveaway/giveaway_type_row.h" #include "info/info_controller.h" #include "lang/lang_keys.h" +#include "payments/payments_checkout_process.h" // Payments::CheckoutProcess +#include "payments/payments_form.h" // Payments::InvoicePremiumGiftCode #include "settings/settings_common.h" #include "settings/settings_premium.h" // Settings::ShowPremium #include "ui/effects/premium_graphics.h" @@ -79,6 +81,8 @@ void CreateGiveawayBox( rpl::event_stream<> toAwardAmountChanged; rpl::variable typeValue; + + bool confirmButtonBusy = false; }; const auto state = box->lifetime().make_state(peer); const auto typeGroup = std::make_shared(); @@ -213,9 +217,54 @@ void CreateGiveawayBox( rebuildListOptions(1); }); } + { + // TODO mini-icon. + const auto &stButton = st::premiumGiftBox; + box->setStyle(stButton); + auto button = object_ptr( + box, + state->toAwardAmountChanged.events_starting_with( + rpl::empty_value() + ) | rpl::map([=] { + return (typeGroup->value() == GiveawayType::SpecificUsers) + ? tr::lng_giveaway_award() + : tr::lng_giveaway_start(); + }) | rpl::flatten_latest(), + st::giveawayGiftCodeStartButton); + button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform); + button->resizeToWidth(box->width() + - stButton.buttonPadding.left() + - stButton.buttonPadding.right()); + button->setClickedCallback([=] { + if (state->confirmButtonBusy) { + return; + } + if (typeGroup->value() == GiveawayType::SpecificUsers) { + if (state->selectedToAward.empty()) { + return; + } + auto invoice = state->apiOptions.invoice( + state->selectedToAward.size(), + durationGroup->value()); + invoice.purpose = Payments::InvoicePremiumGiftCodeUsers{ + ranges::views::all( + state->selectedToAward + ) | ranges::views::transform([]( + const not_null p) { + return not_null{ p->asUser() }; + }) | ranges::to_vector, + peer->asChannel(), + }; + state->confirmButtonBusy = true; + Payments::CheckoutProcess::Start( + std::move(invoice), + crl::guard(box, [=](auto) { + state->confirmButtonBusy = false; + box->window()->setFocus(); + })); + } + }); + box->addButton(std::move(button)); + } state->typeValue.force_assign(GiveawayType::Random); - - box->addButton(tr::lng_box_ok(), [=] { - box->closeBox(); - }); } diff --git a/Telegram/SourceFiles/ui/effects/premium.style b/Telegram/SourceFiles/ui/effects/premium.style index ee1256fea..6d1ae3772 100644 --- a/Telegram/SourceFiles/ui/effects/premium.style +++ b/Telegram/SourceFiles/ui/effects/premium.style @@ -291,6 +291,11 @@ giveawayGiftCodeLinkMargin: margins(24px, 8px, 24px, 12px); giveawayGiftCodeGiftOption: PremiumOption(premiumGiftOption) { badgeShift: point(5px, 0px); } +giveawayGiftCodeStartButton: RoundButton(defaultActiveButton) { + height: 42px; + textTop: 12px; + radius: 6px; +} boostLinkStatsButton: IconButton(defaultIconButton) { width: giveawayGiftCodeLinkCopyWidth;