From 04a7f14c0e0074be6808d272262e32021b72bdab Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 23 Apr 2025 13:43:12 +0400 Subject: [PATCH] Add price tag in resale gift view. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/boxes/star_gift_box.cpp | 100 ++++++++++++++++-- Telegram/SourceFiles/boxes/star_gift_box.h | 5 +- .../settings/settings_credits_graphics.cpp | 55 ++++++++-- Telegram/SourceFiles/ui/effects/credits.style | 5 + 5 files changed, 145 insertions(+), 21 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 4e8c9c3ea3..985e95a301 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3596,6 +3596,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_sell_put" = "Put for Sale"; "lng_gift_sell_update" = "Update the Price"; "lng_gift_sell_toast" = "{name} is now for sale!"; +"lng_gift_sell_updated" = "Sale price for {name} was updated."; "lng_gift_sell_removed" = "{name} is removed from sale."; "lng_gift_menu_show" = "Show"; "lng_gift_menu_hide" = "Hide"; diff --git a/Telegram/SourceFiles/boxes/star_gift_box.cpp b/Telegram/SourceFiles/boxes/star_gift_box.cpp index 348deb1258..e56f8b028c 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/star_gift_box.cpp @@ -89,6 +89,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/checkbox.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/shadow.h" +#include "ui/wrap/fade_wrap.h" #include "ui/wrap/slide_wrap.h" #include "window/themes/window_theme.h" #include "window/section_widget.h" @@ -3643,12 +3644,87 @@ void ShowStarGiftBox( }, i->second.lifetime); } +void SetupResalePriceButton( + not_null parent, + rpl::producer background, + rpl::producer price, + Fn click) { + const auto resale = Ui::CreateChild< + Ui::FadeWrapScaled + >(parent, object_ptr(parent)); + resale->move(0, 0); + + const auto button = resale->entity(); + const auto text = Ui::CreateChild( + button, + QString(), + st::uniqueGiftResalePrice); + text->setAttribute(Qt::WA_TransparentForMouseEvents); + text->sizeValue() | rpl::start_with_next([=](QSize size) { + const auto padding = st::uniqueGiftResalePadding; + const auto margin = st::uniqueGiftResaleMargin; + button->resize(size.grownBy(padding + margin)); + text->move((margin + padding).left(), (margin + padding).top()); + }, button->lifetime()); + text->setTextColorOverride(QColor(255, 255, 255, 255)); + + std::move(price) | rpl::start_with_next([=](int value) { + if (value > 0) { + text->setMarkedText( + Ui::Text::IconEmoji(&st::starIconEmoji).append( + Lang::FormatCountDecimal(value))); + resale->toggle(true, anim::type::normal); + } else { + resale->toggle(false, anim::type::normal); + } + }, resale->lifetime()); + resale->finishAnimating(); + + const auto bg = button->lifetime().make_state>( + std::move(background)); + button->paintRequest() | rpl::start_with_next([=] { + auto p = QPainter(button); + auto hq = PainterHighQualityEnabler(p); + + const auto inner = button->rect().marginsRemoved( + st::uniqueGiftResaleMargin); + const auto radius = inner.height() / 2.; + p.setPen(Qt::NoPen); + p.setBrush(bg->current()); + p.drawRoundedRect(inner, radius, radius); + }, button->lifetime()); + bg->changes() | rpl::start_with_next([=] { + button->update(); + }, button->lifetime()); + + if (click) { + resale->entity()->setClickedCallback(std::move(click)); + } else { + resale->setAttribute(Qt::WA_TransparentForMouseEvents); + } +} + void AddUniqueGiftCover( not_null container, rpl::producer data, - rpl::producer subtitleOverride) { + rpl::producer subtitleOverride, + rpl::producer resalePrice, + Fn resaleClick) { const auto cover = container->add(object_ptr(container)); + if (resalePrice) { + auto background = rpl::duplicate( + data + ) | rpl::map([=](const Data::UniqueGift &unique) { + return unique.backdrop.patternColor; + }); + SetupResalePriceButton( + cover, + std::move(background), + std::move(resalePrice), + std::move(resaleClick)); + } + const auto title = CreateChild( cover, rpl::duplicate( @@ -4068,16 +4144,21 @@ void UpdateGiftSellPrice( std::shared_ptr unique, Data::SavedStarGiftId savedId, int price) { + const auto was = unique->starsForResale; const auto session = &show->session(); session->api().request(MTPpayments_UpdateStarGiftPrice( Api::InputSavedStarGiftId(savedId, unique), MTP_long(price) )).done([=](const MTPUpdates &result) { session->api().applyUpdates(result); - show->showToast(tr::lng_gift_sell_toast( - tr::now, - lt_name, - Data::UniqueGiftName(*unique))); + show->showToast((!price + ? tr::lng_gift_sell_removed + : (was > 0) + ? tr::lng_gift_sell_updated + : tr::lng_gift_sell_toast)( + tr::now, + lt_name, + Data::UniqueGiftName(*unique))); unique->starsForResale = price; session->data().notifyGiftUpdate({ @@ -4088,12 +4169,10 @@ void UpdateGiftSellPrice( }).fail([=](const MTP::Error &error) { show->showToast(error.type()); }).send(); - } void ShowUniqueGiftSellBox( std::shared_ptr show, - not_null peer, std::shared_ptr unique, Data::SavedStarGiftId savedId, Settings::GiftWearBoxStyleOverride st) { @@ -4191,9 +4270,7 @@ void ShowUniqueGiftSellBox( good ? st::windowSubTextFg->c : st::boxTextFgError->c); }, details->lifetime()); - const auto button = box->addButton(priceNow - ? tr::lng_gift_sell_update() - : tr::lng_gift_sell_put(), [=] { + QObject::connect(field, &NumberInput::submitted, [=] { const auto count = field->getLastText().toInt(); if (count < minimal) { field->showError(); @@ -4203,6 +4280,9 @@ void ShowUniqueGiftSellBox( box->closeBox(); UpdateGiftSellPrice(show, unique, savedId, count); }); + const auto button = box->addButton(priceNow + ? tr::lng_gift_sell_update() + : tr::lng_gift_sell_put(), [=] { field->submitted({}); }); rpl::combine( box->widthValue(), button->widthValue() diff --git a/Telegram/SourceFiles/boxes/star_gift_box.h b/Telegram/SourceFiles/boxes/star_gift_box.h index ca9a28af5b..7137e506d0 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.h +++ b/Telegram/SourceFiles/boxes/star_gift_box.h @@ -57,7 +57,9 @@ void ShowStarGiftBox( void AddUniqueGiftCover( not_null container, rpl::producer data, - rpl::producer subtitleOverride = nullptr); + rpl::producer subtitleOverride = nullptr, + rpl::producer resalePrice = nullptr, + Fn resaleClick = nullptr); void AddWearGiftCover( not_null container, const Data::UniqueGift &data, @@ -76,7 +78,6 @@ void UpdateGiftSellPrice( int price); void ShowUniqueGiftSellBox( std::shared_ptr show, - not_null peer, std::shared_ptr unique, Data::SavedStarGiftId savedId, Settings::GiftWearBoxStyleOverride st); diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 686ebfe887..faf31455b2 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -904,6 +904,22 @@ void ProcessReceivedSubscriptions( } } +[[nodiscard]] bool CanResellGift( + not_null session, + const Data::CreditsHistoryEntry &e) { + const auto unique = e.uniqueGift.get(); + const auto owner = unique + ? session->data().peer(unique->ownerId).get() + : nullptr; + return !owner + ? false + : owner->isSelf() + ? e.in + : false; + // Currently we're not reselling channel gifts. + // (owner->isChannel() && owner->asChannel()->canTransferGifts()); +} + void FillUniqueGiftMenu( std::shared_ptr show, not_null menu, @@ -1042,10 +1058,7 @@ void FillUniqueGiftMenu( }, st.wear ? st.wear : &st::menuIconNftWear); } } - const auto sell = owner->isSelf() - ? e.in - : (owner->isChannel() && owner->asChannel()->canTransferGifts()); - if (sell) { + if (CanResellGift(&show->session(), e)) { const auto resalePrice = unique->starsForResale; if (resalePrice > 0) { menu->addAction(tr::lng_gift_transfer_unlist(tr::now), [=] { @@ -1068,7 +1081,7 @@ void FillUniqueGiftMenu( const auto style = st.giftWearBox ? *st.giftWearBox : GiftWearBoxStyleOverride(); - ShowUniqueGiftSellBox(show, owner, unique, savedId, style); + ShowUniqueGiftSellBox(show, unique, savedId, style); }, st.resell ? st.resell : &st::menuIconTagSell); } } @@ -1169,7 +1182,8 @@ void GenericCreditsEntryBox( if (auto savedId = EntryToSavedStarGiftId(session, e)) { session->data().giftUpdates( ) | rpl::start_with_next([=](const Data::GiftUpdate &update) { - if (update.id == savedId) { + if (update.id == savedId + && update.action != Data::GiftUpdate::Action::ResaleChange) { box->closeBox(); } }, box->lifetime()); @@ -1207,9 +1221,32 @@ void GenericCreditsEntryBox( if (uniqueGift) { box->setNoContentMargin(true); - //const auto canEditResale = (e.bareGiftOwnerId == selfPeerId); - //auto starsResale = rpl - AddUniqueGiftCover(content, rpl::single(*uniqueGift)); + const auto slug = uniqueGift->slug; + auto price = rpl::single( + rpl::empty + ) | rpl::then(session->data().giftUpdates( + ) | rpl::filter([=](const Data::GiftUpdate &update) { + return (update.action == Data::GiftUpdate::Action::ResaleChange) + && (update.slug == slug); + }) | rpl::to_empty) | rpl::map([unique = e.uniqueGift] { + return unique->starsForResale; + }); + auto change = [=] { + const auto style = st.giftWearBox + ? *st.giftWearBox + : GiftWearBoxStyleOverride(); + ShowUniqueGiftSellBox( + show, + e.uniqueGift, + EntryToSavedStarGiftId(session, e), + style); + }; + AddUniqueGiftCover( + content, + rpl::single(*uniqueGift), + {}, + std::move(price), + CanResellGift(session, e) ? std::move(change) : Fn()); AddSkip(content, st::defaultVerticalListSkip * 2); diff --git a/Telegram/SourceFiles/ui/effects/credits.style b/Telegram/SourceFiles/ui/effects/credits.style index 47aa63999b..ee92bf7d39 100644 --- a/Telegram/SourceFiles/ui/effects/credits.style +++ b/Telegram/SourceFiles/ui/effects/credits.style @@ -221,6 +221,11 @@ uniqueGiftModelTop: 20px; uniqueGiftTitle: FlatLabel(boxTitle) { align: align(top); } +uniqueGiftResalePrice: FlatLabel(defaultFlatLabel) { + style: semiboldTextStyle; +} +uniqueGiftResalePadding: margins(4px, 4px, 8px, 4px); +uniqueGiftResaleMargin: margins(10px, 10px, 10px, 10px); uniqueGiftTitleTop: 140px; uniqueGiftSubtitle: FlatLabel(defaultFlatLabel) { minWidth: 256px;