2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-08-22 02:07:24 +00:00

Allow buying gifts resold for TONs.

This commit is contained in:
John Preston 2025-07-30 10:03:09 +04:00
parent 2a50d0856b
commit 3b1c7dc5e1
7 changed files with 187 additions and 62 deletions

View File

@ -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

View File

@ -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<CheckoutResult> 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>();
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<Main::SessionShow> show,
MTPInputInvoice invoice,
uint64 formId,
CreditsAmount ton,
Fn<void(Payments::CheckoutResult, const MTPUpdates *)> 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<State>();
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<Main::SessionShow> show,
MTPInputInvoice invoice,
Fn<void(
uint64 formId,
uint64 price,
CreditsAmount price,
std::optional<Payments::CheckoutResult> 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<CreditsAmount>();
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<Main::SessionShow> show,
MTPInputInvoice invoice,
Fn<void(Payments::CheckoutResult, const MTPUpdates *)> done) {
RequestStarsForm(show, invoice, [=](
RequestOurForm(show, invoice, [=](
uint64 formId,
uint64 price,
CreditsAmount price,
std::optional<Payments::CheckoutResult> 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);
}
});
}

View File

@ -129,12 +129,18 @@ void SubmitStarsForm(
uint64 formId,
uint64 price,
Fn<void(Payments::CheckoutResult, const MTPUpdates *)> done);
void RequestStarsForm(
void SubmitTonForm(
std::shared_ptr<Main::SessionShow> show,
MTPInputInvoice invoice,
uint64 formId,
CreditsAmount ton,
Fn<void(Payments::CheckoutResult, const MTPUpdates *)> done);
void RequestOurForm(
std::shared_ptr<Main::SessionShow> show,
MTPInputInvoice invoice,
Fn<void(
uint64 formId,
uint64 price,
CreditsAmount price,
std::optional<Payments::CheckoutResult> failure)> done);
void RequestStarsFormAndSubmit(
std::shared_ptr<Main::SessionShow> show,

View File

@ -487,6 +487,7 @@ void BuyResaleGift(
std::shared_ptr<ChatHelpers::Show> show,
not_null<PeerData*> to,
std::shared_ptr<Data::UniqueGift> gift,
CreditsType type,
Fn<void(Payments::CheckoutResult)> 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<Payments::CheckoutResult> 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<void()> 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,

View File

@ -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

View File

@ -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;

View File

@ -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);