diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 27d3360f96..8a36b9a98d 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3447,12 +3447,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_send_limited_left#other" = "{count} left"; "lng_gift_send_button" = "Send a Gift for {cost}"; "lng_gift_send_button_self" = "Buy a Gift for {cost}"; +"lng_gift_buy_resale_title" = "Buy {name}"; "lng_gift_buy_resale_button" = "Buy for {cost}"; -"lng_gift_buy_resale_confirm" = "Do you want to buy {gift} for {cost} and gift it to {user}?"; -"lng_gift_buy_resale_confirm_self" = "Do you want to buy {gift} for {cost}?"; +"lng_gift_buy_resale_confirm" = "Do you want to buy {name} for {price} and gift it to {user}?"; +"lng_gift_buy_resale_confirm_self" = "Do you want to buy {name} for {price}?"; "lng_gift_sent_title" = "Gift Sent!"; "lng_gift_sent_resale_done" = "{user} has been notified about your gift."; -"lng_gift_sent_resale_done_self" = "{gift} is not yours."; +"lng_gift_sent_resale_done_self" = "{gift} is now yours."; "lng_gift_sent_about#one" = "You spent **{count}** Star from your balance."; "lng_gift_sent_about#other" = "You spent **{count}** Stars from your balance."; "lng_gift_limited_of_one" = "unique"; diff --git a/Telegram/SourceFiles/api/api_premium.cpp b/Telegram/SourceFiles/api/api_premium.cpp index af1ccf95bd..3837b2fbe5 100644 --- a/Telegram/SourceFiles/api/api_premium.cpp +++ b/Telegram/SourceFiles/api/api_premium.cpp @@ -853,10 +853,10 @@ std::optional FromTL( ? peerFromMTP(*data.vowner_id()) : PeerId()), .number = data.vnum().v, + .starsForResale = int(data.vresell_stars().value_or_empty()), .model = *model, .pattern = *pattern, }), - .starsResellMin = int64(data.vresell_stars().value_or_empty()), .document = model->document, .limitedLeft = (total - data.vavailability_issued().v), .limitedCount = total, diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp index 691821c16c..a2dc824d57 100644 --- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp +++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp @@ -1336,7 +1336,13 @@ void AddStarGiftTable( }, tooltip->lifetime()); }; - if (unique && entry.bareGiftOwnerId) { + if (unique && entry.bareGiftResaleRecipientId) { + AddTableRow( + table, + tr::lng_credits_box_history_entry_peer(), + MakePeerTableValue(table, show, PeerId(entry.bareGiftResaleRecipientId)), + st::giveawayGiftCodePeerMargin); + } else if (unique && entry.bareGiftOwnerId) { const auto ownerId = PeerId(entry.bareGiftOwnerId); const auto was = std::make_shared>(); const auto handleChange = [=]( diff --git a/Telegram/SourceFiles/boxes/star_gift_box.cpp b/Telegram/SourceFiles/boxes/star_gift_box.cpp index ea929a9bec..976c48f9f5 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/star_gift_box.cpp @@ -1896,16 +1896,15 @@ void ShowGiftUpgradedToast( } void SendStarsFormRequest( - not_null controller, + std::shared_ptr show, Settings::SmallBalanceResult result, uint64 formId, MTPInputInvoice invoice, Fn done) { using BalanceResult = Settings::SmallBalanceResult; - const auto session = &controller->session(); + const auto session = &show->session(); if (result == BalanceResult::Success || result == BalanceResult::Already) { - const auto weak = base::make_weak(controller); session->api().request(MTPpayments_SendStarsForm( MTP_long(formId), invoice @@ -1917,9 +1916,7 @@ void SendStarsFormRequest( done(Payments::CheckoutResult::Failed, nullptr); }); }).fail([=](const MTP::Error &error) { - if (const auto strong = weak.get()) { - strong->showToast(error.type()); - } + show->showToast(error.type()); done(Payments::CheckoutResult::Failed, nullptr); }).send(); } else if (result == BalanceResult::Cancelled) { @@ -1965,7 +1962,7 @@ void UpgradeGift( } using Flag = MTPDinputInvoiceStarGiftUpgrade::Flag; RequestStarsFormAndSubmit( - window, + window->uiShow(), MTP_inputInvoiceStarGiftUpgrade( MTP_flags(keepDetails ? Flag::f_keep_original_details : Flag()), Api::InputSavedStarGiftId(savedId)), @@ -2613,7 +2610,7 @@ void SendGiftBox( button->setDescriptor(descriptor, GiftButton::Mode::Full); button->setClickedCallback([=] { const auto star = std::get_if(&descriptor); - if (star && star->info.unique) { + if (star && star->info.unique && star->mine) { const auto done = [=] { window->session().credits().load(true); window->showPeerHistory(peer); @@ -2624,7 +2621,14 @@ void SendGiftBox( star->info.unique, star->transferId, done); - } else if (star->resale) { + } else if (star && star->info.unique && star->resale) { + window->show(Box( + Settings::GlobalStarGiftBox, + window->uiShow(), + star->info, + peer->id, + Settings::CreditsEntryBoxStyleOverrides())); + } else if (star && star->resale) { const auto id = star->info.id; if (state->resaleRequestingId == id) { return; @@ -4460,11 +4464,10 @@ void AddUniqueCloseButton( } void RequestStarsFormAndSubmit( - not_null window, + std::shared_ptr show, MTPInputInvoice invoice, Fn done) { - const auto weak = base::make_weak(window); - window->session().api().request(MTPpayments_GetPaymentForm( + show->session().api().request(MTPpayments_GetPaymentForm( MTP_flags(0), invoice, MTPDataJSON() // theme_params @@ -4472,16 +4475,15 @@ void RequestStarsFormAndSubmit( result.match([&](const MTPDpayments_paymentFormStarGift &data) { const auto formId = data.vform_id().v; const auto prices = data.vinvoice().data().vprices().v; - const auto strong = weak.get(); - if (!strong) { + if (!show->valid()) { done(Payments::CheckoutResult::Failed, nullptr); return; } const auto ready = [=](Settings::SmallBalanceResult result) { - SendStarsFormRequest(strong, result, formId, invoice, done); + SendStarsFormRequest(show, result, formId, invoice, done); }; Settings::MaybeRequestBalanceIncrease( - strong->uiShow(), + show, prices.front().data().vamount().v, Settings::SmallBalanceDeepLink{}, ready); @@ -4493,31 +4495,45 @@ void RequestStarsFormAndSubmit( if (type == u"STARGIFT_EXPORT_IN_PROGRESS"_q) { done(Payments::CheckoutResult::Cancelled, nullptr); } else { - if (const auto strong = weak.get()) { - strong->showToast(type); - } + show->showToast(type); done(Payments::CheckoutResult::Failed, nullptr); } }).send(); } void ShowGiftTransferredToast( - base::weak_ptr weak, + std::shared_ptr show, not_null to, const Data::UniqueGift &gift) { - if (const auto strong = weak.get()) { - strong->showToast({ - .title = tr::lng_gift_transferred_title(tr::now), - .text = tr::lng_gift_transferred_about( - tr::now, + show->showToast({ + .title = tr::lng_gift_transferred_title(tr::now), + .text = tr::lng_gift_transferred_about( + tr::now, lt_name, Text::Bold(Data::UniqueGiftName(gift)), lt_recipient, Text::Bold(to->shortName()), Ui::Text::WithEntities), - .duration = kUpgradeDoneToastDuration, - }); - } + .duration = kUpgradeDoneToastDuration, + }); } +void ShowResaleGiftBoughtToast( + std::shared_ptr show, + not_null to, + const Data::UniqueGift &gift) { + show->showToast({ + .title = tr::lng_gift_sent_title(tr::now), + .text = (to->isSelf() + ? tr::lng_gift_sent_resale_done_self( + tr::now, + lt_gift, + Data::UniqueGiftName(gift)) + : tr::lng_gift_sent_resale_done( + tr::now, + lt_user, + to->shortName())), + .duration = kUpgradeDoneToastDuration, + }); +} } // namespace Ui diff --git a/Telegram/SourceFiles/boxes/star_gift_box.h b/Telegram/SourceFiles/boxes/star_gift_box.h index f00f4ae3c4..722c90a3dc 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.h +++ b/Telegram/SourceFiles/boxes/star_gift_box.h @@ -19,6 +19,10 @@ struct GiftCode; struct CreditsHistoryEntry; } // namespace Data +namespace Main { +class SessionShow; +} // namespace Main + namespace Payments { enum class CheckoutResult; } // namespace Payments @@ -101,12 +105,17 @@ void AddUniqueCloseButton( Fn)> fillMenu = nullptr); void RequestStarsFormAndSubmit( - not_null window, + std::shared_ptr show, MTPInputInvoice invoice, Fn done); void ShowGiftTransferredToast( - base::weak_ptr weak, + std::shared_ptr show, + not_null to, + const Data::UniqueGift &gift); + +void ShowResaleGiftBoughtToast( + std::shared_ptr show, not_null to, const Data::UniqueGift &gift); diff --git a/Telegram/SourceFiles/boxes/transfer_gift_box.cpp b/Telegram/SourceFiles/boxes/transfer_gift_box.cpp index f0f52c1855..735add7b71 100644 --- a/Telegram/SourceFiles/boxes/transfer_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/transfer_gift_box.cpp @@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/vertical_list.h" #include "window/window_session_controller.h" #include "styles/style_boxes.h" // peerListSingleRow. +#include "styles/style_credits.h" // starIconEmoji. #include "styles/style_dialogs.h" // recentPeersSpecialName. #include "styles/style_layers.h" // boxLabel. @@ -431,12 +432,12 @@ void TransferGift( const MTPUpdates *updates) { done(result); if (result == Payments::CheckoutResult::Paid) { + session->data().notifyGiftUpdate({ + .id = savedId, + .action = Data::GiftUpdate::Action::Transfer, + }); if (const auto strong = weak.get()) { - strong->session().data().notifyGiftUpdate({ - .id = savedId, - .action = Data::GiftUpdate::Action::Transfer, - }); - Ui::ShowGiftTransferredToast(strong, to, *gift); + Ui::ShowGiftTransferredToast(strong->uiShow(), to, *gift); } } }; @@ -456,13 +457,35 @@ void TransferGift( return; } Ui::RequestStarsFormAndSubmit( - window, + window->uiShow(), MTP_inputInvoiceStarGiftTransfer( Api::InputSavedStarGiftId(savedId), to->input), std::move(formDone)); } +void BuyResaleGift( + std::shared_ptr show, + not_null to, + std::shared_ptr gift, + Fn done) { + Expects(to->isUser()); + + auto formDone = [=]( + Payments::CheckoutResult result, + const MTPUpdates *updates) { + done(result); + if (result == Payments::CheckoutResult::Paid) { + AssertIsDebug(Refresh owners gifts list, refresh self list); + Ui::ShowResaleGiftBoughtToast(show, to, *gift); + } + }; + Ui::RequestStarsFormAndSubmit( + show, + MTP_inputInvoiceStarGiftResale(MTP_string(gift->slug), to->input), + std::move(formDone)); +} + } // namespace void ShowTransferToBox( @@ -566,3 +589,73 @@ void ShowTransferGiftBox( Box(std::move(controller), std::move(initBox)), Ui::LayerOption::KeepOther); } + +void ShowBuyResaleGiftBox( + std::shared_ptr show, + std::shared_ptr gift, + not_null to, + Fn closeParentBox) { + show->show(Box([=](not_null box) { + box->setTitle(tr::lng_gift_buy_resale_title( + lt_name, + rpl::single(UniqueGiftName(*gift)))); + + auto transfer = tr::lng_gift_buy_resale_button( + lt_cost, + rpl::single( + Ui::Text::IconEmoji(&st::starIconEmoji).append( + Lang::FormatCountDecimal(gift->starsForResale))), + Ui::Text::WithEntities); + + struct State { + bool sent = false; + }; + const auto state = std::make_shared(); + auto callback = [=](Fn close) { + if (state->sent) { + return; + } + state->sent = true; + const auto weak = Ui::MakeWeak(box); + const auto done = [=](Payments::CheckoutResult result) { + if (result == Payments::CheckoutResult::Cancelled) { + closeParentBox(); + close(); + } else if (result != Payments::CheckoutResult::Paid) { + state->sent = false; + } else { + show->showToast(u"done!"_q); + closeParentBox(); + close(); + } + }; + BuyResaleGift(show, to, gift, done); + }; + + Ui::ConfirmBox(box, { + .text = to->isSelf() + ? tr::lng_gift_buy_resale_confirm_self( + lt_name, + rpl::single(Ui::Text::Bold(UniqueGiftName(*gift))), + lt_price, + tr::lng_action_gift_for_stars( + lt_count, + rpl::single(gift->starsForResale * 1.), + Ui::Text::Bold), + Ui::Text::WithEntities) + : tr::lng_gift_buy_resale_confirm( + lt_name, + rpl::single(Ui::Text::Bold(UniqueGiftName(*gift))), + lt_price, + tr::lng_action_gift_for_stars( + lt_count, + rpl::single(gift->starsForResale * 1.), + Ui::Text::Bold), + lt_user, + rpl::single(Ui::Text::Bold(to->shortName())), + Ui::Text::WithEntities), + .confirmed = std::move(callback), + .confirmText = std::move(transfer), + }); + })); +} diff --git a/Telegram/SourceFiles/boxes/transfer_gift_box.h b/Telegram/SourceFiles/boxes/transfer_gift_box.h index 5cb051a892..71e1dade91 100644 --- a/Telegram/SourceFiles/boxes/transfer_gift_box.h +++ b/Telegram/SourceFiles/boxes/transfer_gift_box.h @@ -11,6 +11,10 @@ namespace Window { class SessionController; } // namespace Window +namespace ChatHelpers { +class Show; +} // namespace ChatHelpers + namespace Data { struct UniqueGift; class SavedStarGiftId; @@ -27,3 +31,9 @@ void ShowTransferGiftBox( not_null window, std::shared_ptr gift, Data::SavedStarGiftId savedId); + +void ShowBuyResaleGiftBox( + std::shared_ptr show, + std::shared_ptr gift, + not_null to, + Fn closeParentBox); diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index d3b7496cf1..832276e05f 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -1920,7 +1920,7 @@ void ResolveAndShowUniqueGift( session->data().processUsers(data.vusers()); if (const auto gift = Api::FromTL(session, data.vgift())) { using namespace ::Settings; - show->show(Box(GlobalStarGiftBox, show, *gift, st)); + show->show(Box(GlobalStarGiftBox, show, *gift, PeerId(), st)); } }).fail([=](const MTP::Error &error) { clear(); diff --git a/Telegram/SourceFiles/data/data_credits.h b/Telegram/SourceFiles/data/data_credits.h index af37f4e588..1405492213 100644 --- a/Telegram/SourceFiles/data/data_credits.h +++ b/Telegram/SourceFiles/data/data_credits.h @@ -65,6 +65,7 @@ struct CreditsHistoryEntry final { uint64 bareGiveawayMsgId = 0; uint64 bareGiftStickerId = 0; uint64 bareGiftOwnerId = 0; + uint64 bareGiftResaleRecipientId = 0; uint64 bareActorId = 0; uint64 bareEntryOwnerId = 0; uint64 giftChannelSavedId = 0; diff --git a/Telegram/SourceFiles/data/data_star_gift.h b/Telegram/SourceFiles/data/data_star_gift.h index cc60ef62b1..5076110131 100644 --- a/Telegram/SourceFiles/data/data_star_gift.h +++ b/Telegram/SourceFiles/data/data_star_gift.h @@ -46,6 +46,7 @@ struct UniqueGift { PeerId ownerId = 0; int number = 0; int starsForTransfer = -1; + int starsForResale = -1; TimeId exportAt = 0; UniqueGiftModel model; UniqueGiftPattern pattern; diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp index 3f51876de8..a30cd5b333 100644 --- a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp @@ -145,7 +145,9 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) { ? (unique ? _delegate->monostar() : _delegate->star()).append(' ').append( - Lang::FormatCountDecimal(data.info.starsResellMin) + Lang::FormatCountDecimal(unique + ? unique->starsForResale + : data.info.starsResellMin) ).append(data.info.resellCount > 1 ? "+" : "") : unique ? tr::lng_gift_transfer_button( diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index d5ab4d1712..0bfc032364 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -1923,6 +1923,16 @@ void GenericCreditsEntryBox( if (willBusy) { state->confirmButtonBusy = true; send(); + } else if (uniqueGift && uniqueGift->starsForResale && !giftToSelf) { + const auto to = e.bareGiftResaleRecipientId + ? show->session().data().peer( + PeerId(e.bareGiftResaleRecipientId)) + : show->session().user(); + ShowBuyResaleGiftBox( + show, + e.uniqueGift, + to, + crl::guard(box, [=] { box->closeBox(); })); } else if (canUpgradeFree) { upgrade(); } else if (canToggle && !e.savedToProfile) { @@ -1931,6 +1941,14 @@ void GenericCreditsEntryBox( box->closeBox(); } }); + if (uniqueGift && uniqueGift->starsForResale && !giftToSelf) { + button->setText(tr::lng_gift_buy_resale_button( + lt_cost, + rpl::single( + Ui::Text::IconEmoji(&st::starIconEmoji).append( + Lang::FormatCountDecimal(uniqueGift->starsForResale))), + Ui::Text::WithEntities)); + } { using namespace Info::Statistics; const auto loadingAnimation = InfiniteRadialAnimationWidget( @@ -2012,6 +2030,7 @@ void GlobalStarGiftBox( not_null box, std::shared_ptr show, const Data::StarGift &data, + PeerId resaleRecipientId, CreditsEntryBoxStyleOverrides st) { const auto ownerId = data.unique ? data.unique->ownerId.value : 0; Settings::GenericCreditsEntryBox( @@ -2021,6 +2040,7 @@ void GlobalStarGiftBox( .credits = StarsAmount(data.stars), .bareGiftStickerId = data.document->id, .bareGiftOwnerId = ownerId, + .bareGiftResaleRecipientId = resaleRecipientId.value, .stargiftId = data.id, .uniqueGift = data.unique, .peerType = Data::CreditsHistoryEntry::PeerType::Peer, diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.h b/Telegram/SourceFiles/settings/settings_credits_graphics.h index aa43eff741..5ab1f9ab84 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.h +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.h @@ -154,6 +154,7 @@ void GlobalStarGiftBox( not_null box, std::shared_ptr show, const Data::StarGift &data, + PeerId resaleRecipientId, CreditsEntryBoxStyleOverrides st = {}); [[nodiscard]] Data::CreditsHistoryEntry SavedStarGiftEntry( diff --git a/Telegram/SourceFiles/ui/boxes/confirm_box.cpp b/Telegram/SourceFiles/ui/boxes/confirm_box.cpp index cfe7d23706..512b11a893 100644 --- a/Telegram/SourceFiles/ui/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/confirm_box.cpp @@ -58,14 +58,23 @@ void ConfirmBox(not_null box, ConfirmBoxArgs &&args) { }; const auto &defaultButtonStyle = box->getDelegate()->style().button; - + const auto confirmTextPlain = v::text::is_plain(args.confirmText); const auto confirmButton = box->addButton( - v::text::take_plain(std::move(args.confirmText), tr::lng_box_ok()), + (confirmTextPlain + ? v::text::take_plain( + std::move(args.confirmText), + tr::lng_box_ok()) + : rpl::single(QString())), [=, c = prepareCallback(args.confirmed)]() { lifetime->destroy(); c(); }, args.confirmStyle ? *args.confirmStyle : defaultButtonStyle); + if (!confirmTextPlain) { + confirmButton->setText( + v::text::take_marked(std::move(args.confirmText))); + } + box->events( ) | rpl::start_with_next([=](not_null e) { if ((e->type() != QEvent::KeyPress) || !confirmButton) {