diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 6d431ad44d..7875e86e2e 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -646,6 +646,7 @@ PRIVATE data/data_shared_media.h data/data_sparse_ids.cpp data/data_sparse_ids.h + data/data_star_gift.cpp data/data_star_gift.h data/data_statistics.h data/data_stories.cpp diff --git a/Telegram/SourceFiles/boxes/star_gift_box.cpp b/Telegram/SourceFiles/boxes/star_gift_box.cpp index 89a9b9cbed..d28b5d536d 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/star_gift_box.cpp @@ -2094,6 +2094,8 @@ void SendStarsFormRequest( )).done([=](const MTPpayments_PaymentResult &result) { result.match([&](const MTPDpayments_paymentResult &data) { session->api().applyUpdates(data.vupdates()); + session->credits().tonLoad(true); + session->credits().load(true); done(Payments::CheckoutResult::Paid, &data.vupdates()); }, [&](const MTPDpayments_paymentVerificationNeeded &data) { done(Payments::CheckoutResult::Failed, nullptr); @@ -2808,11 +2810,17 @@ void SendGiftBox( using Payments::CheckoutResult; const auto formReady = [=]( uint64 formId, - uint64 price, + CreditsAmount price, std::optional failure) { state->transferRequested = nullptr; - if (!failure || *failure == CheckoutResult::Free) { - unique->starsForTransfer = price; + if (!failure && !price.stars()) { + LOG(("API Error: " + "Bad transfer invoice currenct.")); + } else if (!failure + || *failure == CheckoutResult::Free) { + unique->starsForTransfer = failure + ? 0 + : price.whole(); ShowTransferToBox( window, peer, @@ -2823,7 +2831,7 @@ void SendGiftBox( done(); } }; - RequestStarsForm( + RequestOurForm( window->uiShow(), MTP_inputInvoiceStarGiftTransfer( Api::InputSavedStarGiftId(savedId, unique), @@ -4574,12 +4582,7 @@ void UniqueGiftSellBox( }; const auto state = box->lifetime().make_state(); state->onlyTon = unique->onlyAcceptTon; - const auto priceNow = unique->onlyAcceptTon - ? CreditsAmount( - unique->nanoTonForResale / Ui::kNanosInOne, - unique->nanoTonForResale % Ui::kNanosInOne, - CreditsType::Ton) - : CreditsAmount(unique->starsForResale); + const auto priceNow = Data::UniqueGiftResaleAsked(*unique); state->price = priceNow ? priceNow : price @@ -5171,15 +5174,49 @@ void SubmitStarsForm( ready); } -void RequestStarsForm( +void SubmitTonForm( + std::shared_ptr show, + MTPInputInvoice invoice, + uint64 formId, + CreditsAmount ton, + Fn done) { + const auto ready = [=] { + SendStarsFormRequest( + show, + Settings::SmallBalanceResult::Already, + formId, + invoice, + done); + }; + struct State { + rpl::lifetime lifetime; + bool success = false; + }; + const auto state = std::make_shared(); + + const auto session = &show->session(); + session->credits().tonLoad(); + session->credits().tonLoadedValue( + ) | rpl::filter(rpl::mappers::_1) | rpl::start_with_next([=] { + state->lifetime.destroy(); + + if (session->credits().tonBalance() < ton) { + show->show(Box(InsufficientTonBox, session->user(), ton)); + } else { + ready(); + } + }, state->lifetime); +} + +void RequestOurForm( std::shared_ptr show, MTPInputInvoice invoice, Fn failure)> done) { const auto fail = [=](Payments::CheckoutResult failure) { - done(0, 0, failure); + done(0, {}, failure); }; show->session().api().request(MTPpayments_GetPaymentForm( MTP_flags(0), @@ -5187,10 +5224,24 @@ void RequestStarsForm( MTPDataJSON() // theme_params )).done([=](const MTPpayments_PaymentForm &result) { result.match([&](const MTPDpayments_paymentFormStarGift &data) { - const auto prices = data.vinvoice().data().vprices().v; + const auto &invoice = data.vinvoice().data(); + const auto prices = invoice.vprices().v; if (show->valid() && !prices.isEmpty()) { const auto price = prices.front().data().vamount().v; - done(data.vform_id().v, price, std::nullopt); + const auto currency = qs(invoice.vcurrency()); + const auto amount = (currency == Ui::kCreditsCurrency) + ? CreditsAmount(price) + : (currency == u"TON"_q) + ? CreditsAmount( + price / Ui::kNanosInOne, + price % Ui::kNanosInOne, + CreditsType::Ton) + : std::optional(); + if (amount) { + done(data.vform_id().v, *amount, std::nullopt); + } else { + fail(Payments::CheckoutResult::Failed); + } } else { fail(Payments::CheckoutResult::Failed); } @@ -5214,14 +5265,16 @@ void RequestStarsFormAndSubmit( std::shared_ptr show, MTPInputInvoice invoice, Fn done) { - RequestStarsForm(show, invoice, [=]( + RequestOurForm(show, invoice, [=]( uint64 formId, - uint64 price, + CreditsAmount price, std::optional failure) { - if (!failure) { - SubmitStarsForm(show, invoice, formId, price, done); - } else { + if (failure) { done(*failure, nullptr); + } else if (!price.stars()) { + done(Payments::CheckoutResult::Failed, nullptr); + } else { + SubmitStarsForm(show, invoice, formId, price.whole(), done); } }); } diff --git a/Telegram/SourceFiles/boxes/star_gift_box.h b/Telegram/SourceFiles/boxes/star_gift_box.h index f9daf942fa..39698139c5 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.h +++ b/Telegram/SourceFiles/boxes/star_gift_box.h @@ -129,12 +129,18 @@ void SubmitStarsForm( uint64 formId, uint64 price, Fn done); -void RequestStarsForm( +void SubmitTonForm( + std::shared_ptr show, + MTPInputInvoice invoice, + uint64 formId, + CreditsAmount ton, + Fn done); +void RequestOurForm( std::shared_ptr show, MTPInputInvoice invoice, Fn failure)> done); void RequestStarsFormAndSubmit( std::shared_ptr show, diff --git a/Telegram/SourceFiles/boxes/transfer_gift_box.cpp b/Telegram/SourceFiles/boxes/transfer_gift_box.cpp index 389f919d49..b47672ae67 100644 --- a/Telegram/SourceFiles/boxes/transfer_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/transfer_gift_box.cpp @@ -487,6 +487,7 @@ void BuyResaleGift( std::shared_ptr show, not_null to, std::shared_ptr gift, + CreditsType type, Fn done) { auto paymentDone = [=]( Payments::CheckoutResult result, @@ -504,22 +505,42 @@ void BuyResaleGift( using Flag = MTPDinputInvoiceStarGiftResale::Flag; const auto invoice = MTP_inputInvoiceStarGiftResale( - MTP_flags(gift->onlyAcceptTon ? Flag::f_ton : Flag()), + MTP_flags((type == CreditsType::Ton) ? Flag::f_ton : Flag()), MTP_string(gift->slug), to->input); - Ui::RequestStarsForm(show, invoice, [=]( + Ui::RequestOurForm(show, invoice, [=]( uint64 formId, - uint64 price, + CreditsAmount price, std::optional failure) { + if ((type == CreditsType::Ton && price.stars()) + || (type == CreditsType::Stars && price.ton())) { + paymentDone(Payments::CheckoutResult::Failed, nullptr); + return; + } const auto submit = [=] { - SubmitStarsForm(show, invoice, formId, price, paymentDone); + if (price.stars()) { + SubmitStarsForm( + show, + invoice, + formId, + price.whole(), + paymentDone); + } else { + SubmitTonForm(show, invoice, formId, price, paymentDone); + } }; + const auto was = (type == CreditsType::Ton) + ? Data::UniqueGiftResaleTon(*gift) + : Data::UniqueGiftResaleStars(*gift); if (failure) { paymentDone(*failure, nullptr); - } else if (price != gift->starsForResale) { - const auto cost = Ui::Text::IconEmoji(&st::starIconEmoji).append( - Lang::FormatCountDecimal(price)); + } else if (price != was) { + const auto cost = price.ton() + ? Ui::Text::IconEmoji(&st::tonIconEmoji).append( + Lang::FormatCreditsAmountDecimal(price)) + : Ui::Text::IconEmoji(&st::starIconEmoji).append( + Lang::FormatCountDecimal(price.whole())); const auto cancelled = [=](Fn close) { paymentDone(Payments::CheckoutResult::Cancelled, nullptr); close(); @@ -663,16 +684,7 @@ void ShowBuyResaleGiftBox( auto transfer = tr::lng_gift_buy_resale_button( lt_cost, - rpl::single(gift->onlyAcceptTon - ? Ui::Text::IconEmoji( - &st::tonIconEmoji - ).append( - Lang::FormatCreditsAmountDecimal(CreditsAmount( - gift->nanoTonForResale / Ui::kNanosInOne, - gift->nanoTonForResale % Ui::kNanosInOne, - CreditsType::Ton))) - : Ui::Text::IconEmoji(&st::starIconEmoji).append( - Lang::FormatCountDecimal(gift->starsForResale))), + rpl::single(Data::FormatGiftResaleAsked(*gift)), Ui::Text::WithEntities); struct State { @@ -697,7 +709,10 @@ void ShowBuyResaleGiftBox( close(); } }; - BuyResaleGift(show, to, gift, done); + const auto type = gift->onlyAcceptTon + ? CreditsType::Ton + : CreditsType::Stars; + BuyResaleGift(show, to, gift, type, done); }; Ui::ConfirmBox(box, { @@ -709,7 +724,8 @@ void ShowBuyResaleGiftBox( (gift->onlyAcceptTon ? tr::lng_action_gift_for_ton( lt_count, - rpl::single(gift->nanoTonForResale / 1'000'000'000.), + rpl::single(gift->nanoTonForResale + / float64(Ui::kNanosInOne)), Ui::Text::Bold) : tr::lng_action_gift_for_stars( lt_count_decimal, @@ -723,7 +739,8 @@ void ShowBuyResaleGiftBox( (gift->onlyAcceptTon ? tr::lng_action_gift_for_ton( lt_count, - rpl::single(gift->nanoTonForResale / 1'000'000'000.), + rpl::single(gift->nanoTonForResale + / float64(Ui::kNanosInOne)), Ui::Text::Bold) : tr::lng_action_gift_for_stars( lt_count_decimal, diff --git a/Telegram/SourceFiles/data/data_star_gift.cpp b/Telegram/SourceFiles/data/data_star_gift.cpp new file mode 100644 index 0000000000..998c79950b --- /dev/null +++ b/Telegram/SourceFiles/data/data_star_gift.cpp @@ -0,0 +1,56 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "data/data_star_gift.h" + +#include "lang/lang_tag.h" +#include "ui/controls/ton_common.h" +#include "ui/text/text_utilities.h" +#include "styles/style_credits.h" + +namespace Data { + +QString UniqueGiftName(const UniqueGift &gift) { + return gift.title + u" #"_q + QString::number(gift.number); +} + +CreditsAmount UniqueGiftResaleStars(const UniqueGift &gift) { + return CreditsAmount(gift.starsForResale); +} + +CreditsAmount UniqueGiftResaleTon(const UniqueGift &gift) { + return CreditsAmount( + gift.nanoTonForResale / Ui::kNanosInOne, + gift.nanoTonForResale % Ui::kNanosInOne, + CreditsType::Ton); +} + +CreditsAmount UniqueGiftResaleAsked(const UniqueGift &gift) { + return gift.onlyAcceptTon + ? UniqueGiftResaleTon(gift) + : UniqueGiftResaleStars(gift); +} + +TextWithEntities FormatGiftResaleStars(const UniqueGift &gift) { + return Ui::Text::IconEmoji( + &st::starIconEmoji + ).append(Lang::FormatCountDecimal(gift.starsForResale)); +} + +TextWithEntities FormatGiftResaleTon(const UniqueGift &gift) { + return Ui::Text::IconEmoji( + &st::tonIconEmoji + ).append(Lang::FormatCreditsAmountDecimal(UniqueGiftResaleTon(gift))); +} + +TextWithEntities FormatGiftResaleAsked(const UniqueGift &gift) { + return gift.onlyAcceptTon + ? FormatGiftResaleTon(gift) + : FormatGiftResaleStars(gift); +} + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_star_gift.h b/Telegram/SourceFiles/data/data_star_gift.h index 122f7b5b54..b44707aaf9 100644 --- a/Telegram/SourceFiles/data/data_star_gift.h +++ b/Telegram/SourceFiles/data/data_star_gift.h @@ -59,9 +59,15 @@ struct UniqueGift { UniqueGiftOriginalDetails originalDetails; }; -[[nodiscard]] inline QString UniqueGiftName(const UniqueGift &gift) { - return gift.title + u" #"_q + QString::number(gift.number); -} +[[nodiscard]] QString UniqueGiftName(const UniqueGift &gift); + +[[nodiscard]] CreditsAmount UniqueGiftResaleStars(const UniqueGift &gift); +[[nodiscard]] CreditsAmount UniqueGiftResaleTon(const UniqueGift &gift); +[[nodiscard]] CreditsAmount UniqueGiftResaleAsked(const UniqueGift &gift); + +[[nodiscard]] TextWithEntities FormatGiftResaleStars(const UniqueGift &gift); +[[nodiscard]] TextWithEntities FormatGiftResaleTon(const UniqueGift &gift); +[[nodiscard]] TextWithEntities FormatGiftResaleAsked(const UniqueGift &gift); struct StarGift { uint64 id = 0; diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 0b13184819..e82e6d3012 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -1242,12 +1242,7 @@ void GenericCreditsEntryBox( return (update.action == Data::GiftUpdate::Action::ResaleChange) && (update.slug == slug); }) | rpl::to_empty) | rpl::map([unique = e.uniqueGift] { - return unique->onlyAcceptTon - ? CreditsAmount( - unique->nanoTonForResale / Ui::kNanosInOne, - unique->nanoTonForResale % Ui::kNanosInOne, - CreditsType::Ton) - : CreditsAmount(unique->starsForResale); + return Data::UniqueGiftResaleAsked(*unique); }); auto change = [=] { const auto style = st.giftWearBox @@ -2113,20 +2108,11 @@ void GenericCreditsEntryBox( button, tr::lng_gift_buy_resale_button( lt_cost, - rpl::single(Ui::Text::IconEmoji( - &st::tonIconEmoji - ).append( - Lang::FormatCreditsAmountDecimal(CreditsAmount( - uniqueGift->nanoTonForResale / Ui::kNanosInOne, - uniqueGift->nanoTonForResale % Ui::kNanosInOne, - CreditsType::Ton)))), + rpl::single(Data::FormatGiftResaleTon(*uniqueGift)), Ui::Text::WithEntities), tr::lng_gift_buy_resale_equals( lt_cost, - rpl::single(Ui::Text::IconEmoji( - &st::starIconEmojiSmall - ).append(Lang::FormatCountDecimal( - uniqueGift->starsForResale))), + rpl::single(Data::FormatGiftResaleStars(*uniqueGift)), Ui::Text::WithEntities), st::giftResaleButtonTitle, st::giftResaleButtonSubtitle);