From addd37fb1f7d2e632d40831e90c9a1b041aaae88 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 21 Jan 2025 20:48:29 +0400 Subject: [PATCH] Make channel gifts wearing / transferring. --- Telegram/Resources/langs/lang.strings | 5 +- .../SourceFiles/boxes/gift_premium_box.cpp | 3 +- Telegram/SourceFiles/boxes/star_gift_box.cpp | 57 +++++++++++--- Telegram/SourceFiles/boxes/star_gift_box.h | 1 + .../peer_gifts/info_peer_gifts_widget.cpp | 4 + .../settings/settings_credits_graphics.cpp | 74 ++++++++++--------- 6 files changed, 100 insertions(+), 44 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index a1d07c974..2d277f91e 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3301,7 +3301,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_channel_title" = "Send a Gift"; "lng_gift_channel_about" = "Select a gift to show appreciation for {name}."; "lng_gift_unique_owner" = "Owner"; -"lng_gift_unique_owner_change" = "change"; "lng_gift_unique_status" = "Status"; "lng_gift_unique_status_non" = "Non-Unique"; "lng_gift_unique_status_upgrade" = "upgrade"; @@ -3332,7 +3331,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_convert_sure_caution" = "This action cannot be undone. This will permanently destroy the gift."; "lng_gift_convert_sure" = "Convert"; "lng_gift_display_done" = "The gift is now shown on your profile page."; +"lng_gift_display_done_channel" = "The gift is now shown in channel's Gifts."; "lng_gift_display_done_hide" = "The gift is now hidden from your profile page."; +"lng_gift_display_done_hide_channel" = "The gift is now hidden from channel's Gifts."; "lng_gift_got_stars#one" = "You got **{count} Star** for this gift."; "lng_gift_got_stars#other" = "You got **{count} Stars** for this gift."; "lng_gift_sold_out_title" = "Sold Out!"; @@ -3390,8 +3391,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_wear_about" = "and get these benefits:"; "lng_gift_wear_badge_title" = "Radiant Badge"; "lng_gift_wear_badge_about" = "The glittering icon of this item will be displayed next to your name."; +"lng_gift_wear_badge_about_channel" = "The glittering icon of this item will be displayed next to channel's name."; "lng_gift_wear_proof_title" = "Proof of Ownership"; "lng_gift_wear_proof_about" = "Clicking the icon of this item next to your name will show its info and owner."; +"lng_gift_wear_proof_about_channel" = "Clicking the icon of this item next to channel's name will show its info and owner."; "lng_gift_wear_start" = "Start Wearing"; "lng_gift_wear_subscribe" = "Subscribe to {link} to wear collectibles."; "lng_gift_wear_start_toast" = "You put on {name}"; diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp index 0be756f0d..aa83032a9 100644 --- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp +++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp @@ -218,7 +218,8 @@ constexpr auto kRarityTooltipDuration = 3 * crl::time(1000); state->content = EmojiStatusIdValue( peer ) | rpl::map([=](EmojiStatusId emojiStatusId) { - if (!peer->session().premium()) { + if (!peer->session().premium() + || (!peer->isSelf() && !emojiStatusId)) { return Badge::Content(); } return Badge::Content{ diff --git a/Telegram/SourceFiles/boxes/star_gift_box.cpp b/Telegram/SourceFiles/boxes/star_gift_box.cpp index 922ada959..28d61f6f6 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/star_gift_box.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer_rpl.h" #include "base/unixtime.h" #include "boxes/filters/edit_filter_chats_list.h" +#include "boxes/peers/edit_peer_color_box.h" #include "boxes/gift_premium_box.h" #include "boxes/peer_list_controllers.h" #include "boxes/premium_preview_box.h" @@ -26,12 +27,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/tabbed_panel.h" #include "chat_helpers/tabbed_selector.h" #include "core/ui_integration.h" +#include "data/data_channel.h" #include "data/data_credits.h" #include "data/data_document.h" #include "data/data_document_media.h" #include "data/data_emoji_statuses.h" #include "data/data_file_origin.h" #include "data/data_peer_values.h" +#include "data/data_premium_limits.h" #include "data/data_session.h" #include "data/data_user.h" #include "data/stickers/data_custom_emoji.h" @@ -55,6 +58,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_credits.h" #include "settings/settings_credits_graphics.h" #include "settings/settings_premium.h" +#include "ui/boxes/boost_box.h" #include "ui/chat/chat_style.h" #include "ui/chat/chat_theme.h" #include "ui/controls/emoji_button.h" @@ -2238,7 +2242,11 @@ void AddWearGiftCover( title->setTextColorOverride(QColor(255, 255, 255)); const auto subtitle = CreateChild( cover, - tr::lng_status_online(), + (peer->isChannel() + ? tr::lng_chat_status_subscribers( + lt_count, + rpl::single(peer->asChannel()->membersCount() * 1.)) + : tr::lng_status_online()), st::uniqueGiftSubtitle); subtitle->setTextColorOverride(data.backdrop.textColor); @@ -2304,15 +2312,18 @@ void AddWearGiftCover( void ShowUniqueGiftWearBox( std::shared_ptr show, + not_null peer, const Data::UniqueGift &gift, Settings::GiftWearBoxStyleOverride st) { show->show(Box([=](not_null box) { box->setNoContentMargin(true); + box->setWidth((st::boxWidth + st::boxWideWidth) / 2); // =) box->setStyle(st.box ? *st.box : st::upgradeGiftBox); + const auto channel = peer->isChannel(); const auto content = box->verticalLayout(); - AddWearGiftCover(content, gift, show->session().user()); + AddWearGiftCover(content, gift, peer); AddSkip(content, st::defaultVerticalListSkip * 2); @@ -2356,7 +2367,9 @@ void ShowUniqueGiftWearBox( st::settingsPremiumRowAboutPadding); infoRow( tr::lng_gift_wear_badge_title(), - tr::lng_gift_wear_badge_about(), + (channel + ? tr::lng_gift_wear_badge_about_channel() + : tr::lng_gift_wear_badge_about()), st.radiantIcon ? st.radiantIcon : &st::menuIconUnique); //infoRow( // tr::lng_gift_wear_design_title(), @@ -2364,16 +2377,42 @@ void ShowUniqueGiftWearBox( // &st::menuIconUniqueProfile); infoRow( tr::lng_gift_wear_proof_title(), - tr::lng_gift_wear_proof_about(), + (channel + ? tr::lng_gift_wear_proof_about_channel() + : tr::lng_gift_wear_proof_about()), st.proofIcon ? st.proofIcon : &st::menuIconFactcheck); const auto session = &show->session(); + const auto checking = std::make_shared(); const auto button = box->addButton(rpl::single(QString()), [=] { - if (session->premium()) { + const auto emojiStatuses = &session->data().emojiStatuses(); + const auto id = emojiStatuses->fromUniqueGift(gift); + if (!peer->isSelf()) { + if (*checking) { + return; + } + *checking = true; + const auto weak = Ui::MakeWeak(box); + CheckBoostLevel(show, peer, [=](int level) { + const auto limits = Data::LevelLimits(&peer->session()); + const auto wanted = limits.channelEmojiStatusLevelMin(); + if (level >= wanted) { + if (const auto strong = weak.data()) { + strong->closeBox(); + } + emojiStatuses->set(peer, id); + return std::optional(); + } + const auto reason = [&]() -> Ui::AskBoostReason { + return { Ui::AskBoostWearCollectible{ + wanted + } }; + }(); + return std::make_optional(reason); + }, [=] { *checking = false; }); + } else if (session->premium()) { box->closeBox(); - session->data().emojiStatuses().set( - session->user(), - session->data().emojiStatuses().fromUniqueGift(gift)); + emojiStatuses->set(peer, id); } else { const auto link = Ui::Text::Bold( tr::lng_send_as_premium_required_link(tr::now)); @@ -2397,7 +2436,7 @@ void ShowUniqueGiftWearBox( Data::AmPremiumValue(&show->session()) ) | rpl::map([=](const QString &text, bool premium) { auto result = TextWithEntities(); - if (!premium) { + if (!premium && peer->isSelf()) { result.append(lock); } result.append(text); diff --git a/Telegram/SourceFiles/boxes/star_gift_box.h b/Telegram/SourceFiles/boxes/star_gift_box.h index f161186ff..08391f657 100644 --- a/Telegram/SourceFiles/boxes/star_gift_box.h +++ b/Telegram/SourceFiles/boxes/star_gift_box.h @@ -60,6 +60,7 @@ void AddWearGiftCover( void ShowUniqueGiftWearBox( std::shared_ptr show, + not_null peer, const Data::UniqueGift &gift, Settings::GiftWearBoxStyleOverride st); diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp index 427ba2ec9..9394aeeda 100644 --- a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_widget.cpp @@ -462,11 +462,15 @@ void Widget::setupNotifyCheckbox(bool enabled) { notify->checkedChanges() | rpl::start_with_next([=](bool checked) { const auto api = &controller()->session().api(); + const auto show = controller()->uiShow(); using Flag = MTPpayments_ToggleChatStarGiftNotifications::Flag; api->request(MTPpayments_ToggleChatStarGiftNotifications( MTP_flags(checked ? Flag::f_enabled : Flag()), _inner->peer()->input )).send(); + if (checked) { + show->showToast(tr::lng_peer_gifts_notify_enabled(tr::now)); + } }, notify->lifetime()); const auto &checkSt = st::defaultCheckbox; diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 5029219b3..72c75bd11 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -190,14 +190,19 @@ void ToggleStarGiftSaved( Fn done) { using Flag = MTPpayments_SaveStarGift::Flag; const auto api = &show->session().api(); + const auto channelGift = savedId.chat(); api->request(MTPpayments_SaveStarGift( MTP_flags(save ? Flag(0) : Flag::f_unsave), Api::InputSavedStarGiftId(savedId) )).done([=] { done(true); show->showToast((save - ? tr::lng_gift_display_done - : tr::lng_gift_display_done_hide)(tr::now)); + ? (channelGift + ? tr::lng_gift_display_done_channel + : tr::lng_gift_display_done) + : (channelGift + ? tr::lng_gift_display_done_hide_channel + : tr::lng_gift_display_done_hide))(tr::now)); }).fail([=](const MTP::Error &error) { done(false); show->showToast(error.type()); @@ -860,9 +865,8 @@ void FillUniqueGiftMenu( }, st.share ? st.share : &st::menuIconShare); const auto savedId = EntryToSavedStarGiftId(&show->session(), e); - const auto transfer = e.in - && savedId - && (savedId.isUser() ? e.in : savedId.chat()->canManageGifts()) + const auto transfer = savedId + && (savedId.isUser() ? e.in : savedId.chat()->canTransferGifts()) && (unique->starsForTransfer >= 0); if (transfer) { menu->addAction(tr::lng_gift_transfer_button(tr::now), [=] { @@ -871,19 +875,20 @@ void FillUniqueGiftMenu( } }, st.transfer ? st.transfer : &st::menuIconReplace); } - const auto wear = e.in - && (unique->ownerId == show->session().userPeerId()); + const auto owner = show->session().data().peer(unique->ownerId); + const auto wear = owner->isSelf() + ? e.in + : (owner->isChannel() && owner->asChannel()->canEditEmoji()); if (wear) { - const auto peer = show->session().user(); const auto name = UniqueGiftName(*unique); - const auto now = peer->emojiStatusId().collectible; + const auto now = owner->emojiStatusId().collectible; if (now && unique->slug == now->slug) { menu->addAction(tr::lng_gift_transfer_take_off(tr::now), [=] { - show->session().data().emojiStatuses().set(peer, {}); + show->session().data().emojiStatuses().set(owner, {}); }, st.takeoff ? st.takeoff : &st::menuIconNftTakeOff); } else { menu->addAction(tr::lng_gift_transfer_wear(tr::now), [=] { - ShowUniqueGiftWearBox(show, *unique, st.giftWearBox + ShowUniqueGiftWearBox(show, owner, *unique, st.giftWearBox ? *st.giftWearBox : GiftWearBoxStyleOverride()); }, st.wear ? st.wear : &st::menuIconNftWear); @@ -948,9 +953,14 @@ void GenericCreditsEntryBox( const auto giftToChannel = (giftChannel != nullptr); const auto giftToChannelCanManage = giftToChannel && giftChannel->canManageGifts(); - const auto gotStarGift = isStarGift + const auto giftToChannelCanTransfer = giftToChannel + && giftChannel->canTransferGifts(); + const auto starGiftCanManage = isStarGift && !creditsHistoryStarGift && (e.in || giftToChannelCanManage); + const auto starGiftCanTransfer = isStarGift + && !creditsHistoryStarGift + && (e.in || giftToChannelCanTransfer); const auto starGiftSender = (isStarGift && item) ? item->history()->peer->asUser() : (isStarGift && e.in) @@ -963,13 +973,11 @@ void GenericCreditsEntryBox( const auto timeLeft = int64(convertLast) - int64(base::unixtime::now()); const auto timeExceeded = (timeLeft <= 0); const auto uniqueGift = e.uniqueGift.get(); - const auto forConvert = gotStarGift + const auto forConvert = starGiftCanTransfer && e.starsConverted && !e.converted && starGiftSender; const auto canConvert = forConvert && !timeExceeded; - const auto couldConvert = forConvert && timeExceeded; - const auto nonConvertible = (gotStarGift && !e.starsConverted); box->setStyle(st.box ? *st.box : st::giveawayGiftCodeBox); box->setWidth(st::boxWideWidth); @@ -1151,7 +1159,7 @@ void GenericCreditsEntryBox( ? tr::lng_credits_box_history_entry_gift_sent(tr::now) : convertedStarGift ? tr::lng_credits_box_history_entry_gift_converted(tr::now) - : (isStarGift && !gotStarGift) + : (isStarGift && !starGiftCanManage) ? tr::lng_gift_link_label_gift(tr::now) : giftToSelf ? tr::lng_action_gift_self_subtitle(tr::now) @@ -1294,7 +1302,7 @@ void GenericCreditsEntryBox( rpl::single(e.description), st::creditsBoxAbout))); } - if (!uniqueGift && gotStarGift) { + if (!uniqueGift && starGiftCanManage) { Ui::AddSkip(content); const auto about = box->addRow( object_ptr>( @@ -1309,28 +1317,20 @@ void GenericCreditsEntryBox( Ui::Text::RichLangValue) : (e.starsToUpgrade && giftToSelf - && !(couldConvert || nonConvertible)) + && !e.giftTransferred) ? tr::lng_action_gift_self_about_unique( Ui::Text::WithEntities) : (e.starsToUpgrade && giftToChannelCanManage - && !(couldConvert || nonConvertible)) + && !e.giftTransferred) ? tr::lng_action_gift_channel_about_unique( Ui::Text::WithEntities) - : ((couldConvert || nonConvertible) - ? (e.savedToProfile - ? (giftToChannel - ? tr::lng_action_gift_can_remove_channel - : tr::lng_action_gift_can_remove_text) - : (giftToChannel - ? tr::lng_action_gift_got_gift_channel - : tr::lng_action_gift_got_gift_text))( - Ui::Text::WithEntities) - : rpl::combine( + : ((canConvert || e.converted) + ? rpl::combine( (canConvert ? (giftToSelf ? tr::lng_action_gift_self_about - : giftToChannelCanManage + : giftToChannelCanTransfer ? tr::lng_action_gift_channel_about : tr::lng_action_gift_got_stars_text) : tr::lng_gift_got_stars)( @@ -1343,7 +1343,15 @@ void GenericCreditsEntryBox( QString link) { return text.append(' ').append( Ui::Text::Link(link)); - }))), + }) + : (e.savedToProfile + ? (giftToChannel + ? tr::lng_action_gift_can_remove_channel + : tr::lng_action_gift_can_remove_text) + : (giftToChannel + ? tr::lng_action_gift_got_gift_channel + : tr::lng_action_gift_got_gift_text))( + Ui::Text::WithEntities))), st::creditsBoxAbout)))->entity(); about->setClickHandlerFilter([=](const auto &...) { Core::App().iv().openWithIvPreferred( @@ -1399,7 +1407,7 @@ void GenericCreditsEntryBox( }; const auto state = box->lifetime().make_state(); - const auto canToggle = (canConvert || couldConvert || nonConvertible) + const auto canToggle = starGiftCanManage && !e.giftTransferred && !e.giftRefunded; const auto toggleVisibility = [=, weak = Ui::MakeWeak(box)](bool save) { @@ -1541,7 +1549,7 @@ void GenericCreditsEntryBox( tr::lng_credits_box_out_about_link(tr::now)), Ui::Text::WithEntities), st::creditsBoxAboutDivider))); - } else if (gotStarGift || giftToChannelCanManage) { + } else if (starGiftCanManage) { const auto hiddenPhrase = giftToChannelCanManage ? tr::lng_gift_hidden_hint_channel : tr::lng_gift_hidden_hint;