From 09175d2b6b64090158c0e1beec05bddb3a0d3e3c Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 11 Sep 2025 21:59:56 +0400 Subject: [PATCH] Allow removing unique gift details. --- Telegram/Resources/langs/lang.strings | 4 + Telegram/SourceFiles/api/api_premium.cpp | 2 + .../SourceFiles/boxes/gift_premium_box.cpp | 196 +++++++++++++----- Telegram/SourceFiles/boxes/gift_premium_box.h | 3 +- Telegram/SourceFiles/boxes/star_gift_box.cpp | 22 ++ Telegram/SourceFiles/data/data_credits.h | 1 + Telegram/SourceFiles/data/data_media_types.h | 1 + Telegram/SourceFiles/data/data_star_gift.h | 1 + Telegram/SourceFiles/history/history_item.cpp | 2 + .../boosts/giveaway/giveaway.style | 11 + .../settings/settings_credits_graphics.cpp | 32 ++- 11 files changed, 226 insertions(+), 49 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index a94765c5eb..4fd047e29e 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3694,6 +3694,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_unique_info_sender_comment" = "Gifted by {from} to {recipient} on {date} with the comment \"{text}\"."; "lng_gift_unique_info_reciever" = "Gifted to {recipient} on {date}."; "lng_gift_unique_info_reciever_comment" = "Gifted to {recipient} on {date} with the comment \"{text}\"."; +"lng_gift_unique_info_remove_title" = "Remove Description"; +"lng_gift_unique_info_remove_text" = "Do you want to permanently remove this description from your gift?"; +"lng_gift_unique_info_remove_confirm" = "Remove for {cost}"; +"lng_gift_unique_info_removed" = "Removed {name}'s Description!"; "lng_gift_availability_left#one" = "{count} of {amount} left"; "lng_gift_availability_left#other" = "{count} of {amount} left"; "lng_gift_availability_none" = "None of {amount} left"; diff --git a/Telegram/SourceFiles/api/api_premium.cpp b/Telegram/SourceFiles/api/api_premium.cpp index 545ef29be3..f2daac65db 100644 --- a/Telegram/SourceFiles/api/api_premium.cpp +++ b/Telegram/SourceFiles/api/api_premium.cpp @@ -986,6 +986,8 @@ std::optional FromTL( .starsConverted = int64(data.vconvert_stars().value_or_empty()), .starsUpgradedBySender = int64( data.vupgrade_stars().value_or_empty()), + .starsForDetailsRemove = int64( + data.vdrop_original_details_stars().value_or_empty()), .giftPrepayUpgradeHash = qs( data.vprepaid_upgrade_hash().value_or_empty()), .fromId = (data.vfrom_id() diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp index 24a06d1b5d..415855b5ea 100644 --- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp +++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp @@ -45,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_premium.h" #include "ui/basic_click_handlers.h" // UrlClickHandler::Open. #include "ui/boxes/boost_box.h" // StartFireworks. +#include "ui/boxes/confirm_box.h" #include "ui/controls/userpic_button.h" #include "ui/effects/credits_graphics.h" #include "ui/effects/premium_graphics.h" @@ -1433,13 +1434,108 @@ QString TonAddressUrl( return prefix + address; } +struct AddedUniqueDetails { + object_ptr widget; + not_null label; +}; +[[nodiscard]] AddedUniqueDetails MakeUniqueDetails( + std::shared_ptr show, + not_null parent, + rpl::producer text, + Settings::CreditsEntryBoxStyleOverrides st, + Ui::Text::MarkedContext context, + int removeCost, + Fn removed)> remove) { + auto owned = object_ptr( + parent, + rpl::duplicate(text), + (st.tableValueMessage + ? *st.tableValueMessage + : st::giveawayGiftMessage), + st::defaultPopupMenu, + context); + const auto label = owned.data(); + if (!remove) { + return { + .widget = std::move(owned), + .label = label, + }; + } + owned.release(); + + auto result = object_ptr(parent); + const auto raw = result.data(); + + label->setParent(raw); + label->show(); + const auto icon = Ui::CreateChild( + raw, + st::giveawayGiftMessageRemove); + raw->widthValue() | rpl::start_with_next([=](int outer) { + label->resizeToWidth(outer - icon->width()); + const auto height = std::max(label->height(), icon->height()); + + icon->moveToRight(0, (height - icon->height()) / 2); + label->move(0, (height - label->height()) / 2); + + raw->resize(outer, height); + }, raw->lifetime()); + icon->setClickedCallback([=] { + const auto weak = base::make_weak(raw); + show->show(Box([=](not_null box) { + const auto confirming = base::make_weak(box); + const auto confirmed = [=] { + remove([=] { + delete weak.get(); + if (const auto strong = confirming.get()) { + strong->closeBox(); + } + }); + }; + Ui::ConfirmBox(box, { + .text = tr::lng_gift_unique_info_remove_text(), + .confirmed = confirmed, + .confirmText = tr::lng_gift_unique_info_remove_confirm( + lt_cost, + rpl::single( + Ui::Text::IconEmoji(&st::starIconEmoji).append( + Lang::FormatCountDecimal(removeCost))), + Ui::Text::RichLangValue), + .title = tr::lng_gift_unique_info_remove_title(), + }); + box->addRow( + object_ptr( + box, + st.table ? *st.table : st::giveawayGiftCodeTable) + )->addRow( + object_ptr( + parent, + rpl::duplicate(text), + (st.tableValueMessage + ? *st.tableValueMessage + : st::giveawayGiftMessage), + st::defaultPopupMenu, + context), + nullptr, + st::giveawayGiftCodeLabelMargin, + st::giveawayGiftCodeValueMargin); + })); + }); + + return { + .widget = std::move(result), + .label = label, + }; +} + void AddStarGiftTable( std::shared_ptr show, not_null container, Settings::CreditsEntryBoxStyleOverrides st, const Data::CreditsHistoryEntry &entry, Fn convertToStars, - Fn startUpgrade) { + Fn startUpgrade, + Fn removed)> removeDetails) { const auto table = container->add( object_ptr( container, @@ -1467,7 +1563,10 @@ void AddStarGiftTable( AddTableRow( table, tr::lng_credits_box_history_entry_peer(), - MakePeerTableValue(table, show, PeerId(entry.bareGiftResaleRecipientId)), + MakePeerTableValue( + table, + show, + PeerId(entry.bareGiftResaleRecipientId)), st::giveawayGiftCodePeerMargin); } else if (unique && entry.bareGiftOwnerId) { const auto ownerId = PeerId(entry.bareGiftOwnerId); @@ -1638,60 +1737,63 @@ void AddStarGiftTable( : nullptr; const auto date = base::unixtime::parse(original.date).date(); const auto dateText = TextWithEntities{ langDayOfMonth(date) }; - auto label = object_ptr( + auto details = from + ? (original.message.empty() + ? tr::lng_gift_unique_info_sender( + lt_from, + rpl::single(Ui::Text::Link(from->name(), 2)), + lt_recipient, + rpl::single(Ui::Text::Link(to->name(), 1)), + lt_date, + rpl::single(dateText), + Ui::Text::WithEntities) + : tr::lng_gift_unique_info_sender_comment( + lt_from, + rpl::single(Ui::Text::Link(from->name(), 2)), + lt_recipient, + rpl::single(Ui::Text::Link(to->name(), 1)), + lt_date, + rpl::single(dateText), + lt_text, + rpl::single(original.message), + Ui::Text::WithEntities)) + : (original.message.empty() + ? tr::lng_gift_unique_info_reciever( + lt_recipient, + rpl::single(Ui::Text::Link(to->name(), 1)), + lt_date, + rpl::single(dateText), + Ui::Text::WithEntities) + : tr::lng_gift_unique_info_reciever_comment( + lt_recipient, + rpl::single(Ui::Text::Link(to->name(), 1)), + lt_date, + rpl::single(dateText), + lt_text, + rpl::single(original.message), + Ui::Text::WithEntities)); + const auto tmp = std::make_shared(nullptr); + auto made = MakeUniqueDetails( + show, table, - (from - ? (original.message.empty() - ? tr::lng_gift_unique_info_sender( - lt_from, - rpl::single(Ui::Text::Link(from->name(), 2)), - lt_recipient, - rpl::single(Ui::Text::Link(to->name(), 1)), - lt_date, - rpl::single(dateText), - Ui::Text::WithEntities) - : tr::lng_gift_unique_info_sender_comment( - lt_from, - rpl::single(Ui::Text::Link(from->name(), 2)), - lt_recipient, - rpl::single(Ui::Text::Link(to->name(), 1)), - lt_date, - rpl::single(dateText), - lt_text, - rpl::single(original.message), - Ui::Text::WithEntities)) - : (original.message.empty() - ? tr::lng_gift_unique_info_reciever( - lt_recipient, - rpl::single(Ui::Text::Link(to->name(), 1)), - lt_date, - rpl::single(dateText), - Ui::Text::WithEntities) - : tr::lng_gift_unique_info_reciever_comment( - lt_recipient, - rpl::single(Ui::Text::Link(to->name(), 1)), - lt_date, - rpl::single(dateText), - lt_text, - rpl::single(original.message), - Ui::Text::WithEntities))), - (st.tableValueMessage - ? *st.tableValueMessage - : st::giveawayGiftMessage), - st::defaultPopupMenu, - Core::TextContext({ .session = session })); + std::move(details), + st, + Core::TextContext({ .session = session }), + entry.starsForDetailsRemove, + std::move(removeDetails)); const auto showBoxLink = [=](not_null peer) { return std::make_shared([=] { show->showBox(PrepareShortInfoBox(peer, show)); }); }; - label->setLink(1, showBoxLink(to)); + made.label->setLink(1, showBoxLink(to)); if (from) { - label->setLink(2, showBoxLink(from)); + made.label->setLink(2, showBoxLink(from)); } - label->setSelectable(true); + made.label->setSelectable(true); + *tmp = made.widget.data(); table->addRow( - std::move(label), + std::move(made.widget), nullptr, st::giveawayGiftCodeLabelMargin, st::giveawayGiftCodeValueMargin); diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.h b/Telegram/SourceFiles/boxes/gift_premium_box.h index f54b1e9c8e..64c103a1fa 100644 --- a/Telegram/SourceFiles/boxes/gift_premium_box.h +++ b/Telegram/SourceFiles/boxes/gift_premium_box.h @@ -78,7 +78,8 @@ void AddStarGiftTable( Settings::CreditsEntryBoxStyleOverrides st, const Data::CreditsHistoryEntry &entry, Fn convertToStars, - Fn startUpgrade); + Fn startUpgrade, + Fn removed)> removeDetails); void AddTransferGiftTable( std::shared_ptr show, not_null container, diff --git a/Telegram/SourceFiles/boxes/star_gift_box.cpp b/Telegram/SourceFiles/boxes/star_gift_box.cpp index ce1b133b8b..ffb0d5c1e7 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/star_gift_box.cpp @@ -5672,6 +5672,28 @@ void RequestOurForm( } else { fail(Payments::CheckoutResult::Failed); } + }, [&](const MTPDpayments_paymentFormStars &data) { + show->session().data().processUsers(data.vusers()); + const auto currency = qs(data.vinvoice().data().vcurrency()); + const auto &prices = data.vinvoice().data().vprices().v; + if (!prices.empty()) { + const auto price = prices.front().data().vamount().v; + 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); + } }, [&](const auto &) { fail(Payments::CheckoutResult::Failed); }); diff --git a/Telegram/SourceFiles/data/data_credits.h b/Telegram/SourceFiles/data/data_credits.h index 5102d58a4b..eb653a4df6 100644 --- a/Telegram/SourceFiles/data/data_credits.h +++ b/Telegram/SourceFiles/data/data_credits.h @@ -97,6 +97,7 @@ struct CreditsHistoryEntry final { int starsConverted = 0; int starsToUpgrade = 0; int starsUpgradedBySender = 0; + int starsForDetailsRemove = 0; int premiumMonthsForStars = 0; int floodSkip = 0; bool converted : 1 = false; diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index 4a722d6599..1e3911a3fa 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -157,6 +157,7 @@ struct GiftCode { int starsConverted = 0; int starsToUpgrade = 0; int starsUpgradedBySender = 0; + int starsForDetailsRemove = 0; int limitedCount = 0; int limitedLeft = 0; int64 count = 0; diff --git a/Telegram/SourceFiles/data/data_star_gift.h b/Telegram/SourceFiles/data/data_star_gift.h index c9e41a84cd..1e999136c8 100644 --- a/Telegram/SourceFiles/data/data_star_gift.h +++ b/Telegram/SourceFiles/data/data_star_gift.h @@ -175,6 +175,7 @@ struct SavedStarGift { TextWithEntities message; int64 starsConverted = 0; int64 starsUpgradedBySender = 0; + int64 starsForDetailsRemove = 0; QString giftPrepayUpgradeHash; PeerId fromId = 0; TimeId date = 0; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index b736e747d9..e2e114b5b3 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -6578,6 +6578,8 @@ void HistoryItem::applyAction(const MTPMessageAction &action) { ? history()->owner().peer(from).get() : nullptr), .channelSavedId = data.vsaved_id().value_or_empty(), + .starsForDetailsRemove = int( + data.vdrop_original_details_stars().value_or_empty()), .type = Data::GiftType::StarGift, .transferred = data.is_transferred(), .refunded = data.is_refunded(), diff --git a/Telegram/SourceFiles/info/channel_statistics/boosts/giveaway/giveaway.style b/Telegram/SourceFiles/info/channel_statistics/boosts/giveaway/giveaway.style index 956c16c2cc..8779aaf9e6 100644 --- a/Telegram/SourceFiles/info/channel_statistics/boosts/giveaway/giveaway.style +++ b/Telegram/SourceFiles/info/channel_statistics/boosts/giveaway/giveaway.style @@ -91,6 +91,17 @@ giveawayGiftMessage: FlatLabel(defaultTableValue) { minWidth: 128px; maxHeight: 0px; } +giveawayGiftMessageRemove: IconButton(defaultIconButton) { + width: 32px; + height: 32px; + icon: icon{{ "menu/delete", windowActiveTextFg }}; + iconOver: icon{{ "menu/delete", windowActiveTextFg }}; + rippleAreaPosition: point(0px, 0px); + rippleAreaSize: 32px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: lightButtonBgRipple; + } +} giveawayGiftCodeValueMargin: margins(13px, 9px, 13px, 9px); giveawayGiftCodePeerMargin: margins(11px, 6px, 11px, 4px); giveawayGiftCodeUserpic: UserpicButton(defaultUserpicButton) { diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 8fa96eb367..e0a27fd228 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -1848,6 +1848,8 @@ void GenericCreditsEntryBox( const auto canGiftUpgrade = !e.uniqueGift && !e.in && !e.giftPrepayUpgradeHash.isEmpty(); + const auto canRemoveDetails = e.uniqueGift + && (e.starsForDetailsRemove > 0); const auto upgradeGuard = std::make_shared(); const auto upgrade = [=] { const auto window = show->resolveWindow(); @@ -1879,6 +1881,31 @@ void GenericCreditsEntryBox( && !e.anonymous)), }); }; + const auto removeDetails = [=](Fn removed) { + const auto session = &show->session(); + const auto unique = e.uniqueGift; + const auto savedId = EntryToSavedStarGiftId(session, e); + auto done = [=]( + Payments::CheckoutResult result, + const MTPUpdates *updates) { + if (result == Payments::CheckoutResult::Paid) { + removed(); + + const auto name = Data::UniqueGiftName(*unique); + show->showToast(tr::lng_gift_unique_info_removed( + tr::now, + lt_name, + Ui::Text::Bold(name), + Ui::Text::WithEntities)); + unique->originalDetails = Data::UniqueGiftOriginalDetails(); + } + }; + RequestStarsFormAndSubmit( + show, + MTP_inputInvoiceStarGiftDropOriginalDetails( + Api::InputSavedStarGiftId(savedId, e.uniqueGift)), + std::move(done)); + }; if (isStarGift && e.id.isEmpty()) { const auto convert = [=, weak = base::make_weak(box)] { @@ -1933,7 +1960,8 @@ void GenericCreditsEntryBox( st, e, canConvert ? convert : Fn(), - canUpgrade ? upgrade : Fn()); + canUpgrade ? upgrade : Fn(), + canRemoveDetails ? removeDetails : Fn)>()); } else { AddCreditsHistoryEntryTable(show, content, st, e); AddSubscriptionEntryTable(show, content, st, s); @@ -2552,6 +2580,7 @@ Data::CreditsHistoryEntry SavedStarGiftEntry( .starsConverted = int(data.starsConverted), .starsToUpgrade = int(data.info.starsToUpgrade), .starsUpgradedBySender = int(data.starsUpgradedBySender), + .starsForDetailsRemove = int(data.starsForDetailsRemove), .converted = false, .anonymous = data.anonymous, .stargift = true, @@ -2655,6 +2684,7 @@ void ShowStarGiftViewBox( .starsConverted = data.starsConverted, .starsToUpgrade = data.starsToUpgrade, .starsUpgradedBySender = data.starsUpgradedBySender, + .starsForDetailsRemove = data.starsForDetailsRemove, .converted = data.converted, .anonymous = data.anonymous, .stargift = true,