From acafa2bcade07e7313e26c847b1c60a11af0ff53 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 26 Sep 2025 22:17:08 +0400 Subject: [PATCH] Allow buy-and-set color from collectible. --- Telegram/Resources/langs/lang.strings | 1 + .../boxes/peers/edit_peer_color_box.cpp | 115 +++++++++++++++--- .../SourceFiles/boxes/transfer_gift_box.cpp | 6 +- .../SourceFiles/boxes/transfer_gift_box.h | 2 +- .../history/view/history_view_reply.cpp | 16 +-- .../view/media/history_view_contact.cpp | 16 +-- .../view/media/history_view_web_page.cpp | 16 +-- .../settings/settings_credits_graphics.cpp | 2 +- Telegram/SourceFiles/ui/chat/chat_style.cpp | 4 +- Telegram/SourceFiles/ui/chat/chat_style.h | 3 +- 10 files changed, 130 insertions(+), 51 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index dc8c2e8f29..cd35e615f1 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -985,6 +985,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_color_subscribe" = "Subscribe to {link} to choose a custom color for your name."; "lng_settings_color_changed" = "Your name color has been updated!"; "lng_settings_color_changed_channel" = "Your channel color has been updated!"; +"lng_settings_color_apply" = "Apply Style"; "lng_suggest_hide_new_title" = "Hide new chats?"; "lng_suggest_hide_new_about" = "You are receiving lots of new chats from users who are not in your Contact List.\n\nDo you want to have such chats **automatically muted** and **archived**?"; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp index 614b43c7fe..9134a0dc58 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_color_box.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/background_box.h" #include "boxes/star_gift_box.h" #include "boxes/stickers_box.h" +#include "boxes/transfer_gift_box.h" #include "chat_helpers/compose/compose_show.h" #include "core/ui_integration.h" #include "data/stickers/data_custom_emoji.h" @@ -25,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_emoji_statuses.h" #include "data/data_file_origin.h" #include "data/data_peer.h" +#include "data/data_peer_values.h" #include "data/data_premium_limits.h" #include "data/data_session.h" #include "data/data_user.h" @@ -46,6 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/boxes/boost_box.h" #include "ui/chat/chat_style.h" #include "ui/chat/chat_theme.h" +#include "ui/controls/button_labels.h" #include "ui/controls/sub_tabs.h" #include "ui/effects/path_shift_gradient.h" #include "ui/effects/premium_graphics.h" @@ -631,29 +634,35 @@ void Set( } } +bool ShowPremiumToast( + std::shared_ptr show, + not_null peer) { + if (!peer->isSelf() || show->session().premium()) { + return false; + } + Settings::ShowPremiumPromoToast( + show, + tr::lng_settings_color_subscribe( + tr::now, + lt_link, + Ui::Text::Link( + Ui::Text::Bold( + tr::lng_send_as_premium_required_link(tr::now))), + Ui::Text::WithEntities), + u"name_color"_q); + return true; +} + void Apply( std::shared_ptr show, not_null peer, SetValues values, Fn close, Fn cancel) { - const auto session = &peer->session(); if (peer->colorIndex() == values.colorIndex && peer->backgroundEmojiId() == values.backgroundEmojiId && !values.statusChanged) { close(); - } else if (peer->isSelf() && !session->premium()) { - Settings::ShowPremiumPromoToast( - show, - tr::lng_settings_color_subscribe( - tr::now, - lt_link, - Ui::Text::Link( - Ui::Text::Bold( - tr::lng_send_as_premium_required_link(tr::now))), - Ui::Text::WithEntities), - u"name_color"_q); - cancel(); } else if (peer->isSelf()) { Set(show, peer, values); close(); @@ -1546,6 +1555,10 @@ void EditPeerColorBox( ? tr::lng_settings_color_title() : tr::lng_edit_channel_color()); box->setWidth(st::boxWideWidth); + box->setStyle(st::giftBox); + box->addTopButton(st::boxTitleClose, [=] { + box->closeBox(); + }); struct State { rpl::variable index; @@ -1553,6 +1566,7 @@ void EditPeerColorBox( rpl::variable statusId; rpl::variable> collectible; rpl::variable showingGiftId; + std::shared_ptr buyCollectible; TimeId statusUntil = 0; bool statusChanged = false; bool changing = false; @@ -1596,6 +1610,7 @@ void EditPeerColorBox( state->index.value(), [=](uint8 index) { if (state->collectible.current()) { + state->buyCollectible = nullptr; state->collectible = std::nullopt; state->emojiId = 0; } @@ -1739,33 +1754,93 @@ void EditPeerColorBox( state->emojiId = selected->peerColor ? selected->peerColor->backgroundEmojiId : 0; + state->buyCollectible = (selected->peerColor + && (selected->ownerId != session->userPeerId()) + && selected->starsForResale > 0) + ? selected + : nullptr; state->collectible = selected->peerColor ? *selected->peerColor : std::optional(); }, state->collectible.value()); } - box->addButton(tr::lng_settings_apply(), [=] { + const auto button = box->addButton(tr::lng_settings_color_apply(), [=] { if (state->applying) { return; + } else if (ShowPremiumToast(show, peer)) { + return; } - state->applying = true; - Apply(show, peer, { + const auto values = SetValues{ state->index.current(), state->emojiId.current(), state->collectible.current(), state->statusId.current(), state->statusUntil, state->statusChanged, - }, crl::guard(box, [=] { + }; + if (const auto buy = state->buyCollectible) { + const auto done = [=, weak = base::make_weak(box)](bool ok) { + if (ok) { + if (const auto strong = weak.get()) { + strong->closeBox(); + } + Apply(show, peer, values, [] {}, [] {}); + } + }; + const auto to = peer->session().user(); + ShowBuyResaleGiftBox(show, buy, false, to, done); + return; + } + state->applying = true; + Apply(show, peer, values, crl::guard(box, [=] { box->closeBox(); }), crl::guard(box, [=] { state->applying = false; })); }); - box->addButton(tr::lng_cancel(), [=] { - box->closeBox(); - }); + state->collectible.value( + ) | rpl::start_with_next([=] { + const auto buy = state->buyCollectible.get(); + while (!button->children().isEmpty()) { + delete button->children().first(); + } + if (!buy) { + button->setText(rpl::combine( + tr::lng_settings_color_apply(), + Data::AmPremiumValue(&peer->session()) + ) | rpl::map([=](const QString &text, bool premium) { + auto result = TextWithEntities(); + if (!premium && peer->isSelf()) { + result.append(Ui::Text::IconEmoji(&st::giftBoxLock)); + } + result.append(text); + return result; + })); + } else if (buy->onlyAcceptTon) { + button->setText(rpl::single(QString())); + Ui::SetButtonTwoLabels( + button, + tr::lng_gift_buy_resale_button( + lt_cost, + rpl::single(Data::FormatGiftResaleTon(*buy)), + Ui::Text::WithEntities), + tr::lng_gift_buy_resale_equals( + lt_cost, + rpl::single(Ui::Text::IconEmoji( + &st::starIconEmojiSmall + ).append(Lang::FormatCountDecimal(buy->starsForResale))), + Ui::Text::WithEntities), + st::resaleButtonTitle, + st::resaleButtonSubtitle); + } else { + button->setText(tr::lng_gift_buy_resale_button( + lt_cost, + rpl::single(Ui::Text::IconEmoji(&st::starIconEmoji).append( + Lang::FormatCountDecimal(buy->starsForResale))), + Ui::Text::WithEntities)); + } + }, button->lifetime()); } void SetupPeerColorSample( diff --git a/Telegram/SourceFiles/boxes/transfer_gift_box.cpp b/Telegram/SourceFiles/boxes/transfer_gift_box.cpp index 1435ea3b95..d92477bec9 100644 --- a/Telegram/SourceFiles/boxes/transfer_gift_box.cpp +++ b/Telegram/SourceFiles/boxes/transfer_gift_box.cpp @@ -790,7 +790,7 @@ void ShowBuyResaleGiftBox( std::shared_ptr gift, bool forceTon, not_null to, - Fn closeParentBox) { + Fn closeParentBox) { show->show(Box([=](not_null box) { struct State { rpl::variable ton; @@ -856,12 +856,12 @@ void ShowBuyResaleGiftBox( const auto weak = base::make_weak(box); const auto done = [=](Payments::CheckoutResult result) { if (result == Payments::CheckoutResult::Cancelled) { - closeParentBox(); + closeParentBox(false); close(); } else if (result != Payments::CheckoutResult::Paid) { state->sent = false; } else { - closeParentBox(); + closeParentBox(true); close(); } }; diff --git a/Telegram/SourceFiles/boxes/transfer_gift_box.h b/Telegram/SourceFiles/boxes/transfer_gift_box.h index 285c2149fb..a1253e0247 100644 --- a/Telegram/SourceFiles/boxes/transfer_gift_box.h +++ b/Telegram/SourceFiles/boxes/transfer_gift_box.h @@ -41,7 +41,7 @@ void ShowBuyResaleGiftBox( std::shared_ptr gift, bool forceTon, not_null to, - Fn closeParentBox); + Fn closeParentBox); bool ShowResaleGiftLater( std::shared_ptr show, diff --git a/Telegram/SourceFiles/history/view/history_view_reply.cpp b/Telegram/SourceFiles/history/view/history_view_reply.cpp index de3e7e5e5e..ee4f18ed21 100644 --- a/Telegram/SourceFiles/history/view/history_view_reply.cpp +++ b/Telegram/SourceFiles/history/view/history_view_reply.cpp @@ -779,14 +779,14 @@ void Reply::paint( const auto "eSt = _hasQuoteIcon ? st::messageTextStyle.blockquote : st::messageQuoteStyle; - const auto backgroundEmoji = backgroundEmojiId - ? st->backgroundEmojiData(backgroundEmojiId).get() + const auto backgroundEmojiData = backgroundEmojiId + ? st->backgroundEmojiData(backgroundEmojiId, colorCollectible).get() : nullptr; - const auto backgroundEmojiCache = !backgroundEmoji + const auto backgroundEmojiCache = !backgroundEmojiData ? nullptr : useColorCollectible - ? &backgroundEmoji->collectibleCaches[colorCollectible] - : &backgroundEmoji->caches[Ui::BackgroundEmojiData::CacheIndex( + ? &backgroundEmojiData->collectibleCaches[colorCollectible] + : &backgroundEmojiData->caches[Ui::BackgroundEmojiData::CacheIndex( selected, context.outbg, inBubble, @@ -797,11 +797,11 @@ void Reply::paint( } Ui::Text::ValidateQuotePaintCache(*cache, quoteSt); Ui::Text::FillQuotePaint(p, rect, *cache, quoteSt); - if (backgroundEmoji) { + if (backgroundEmojiData) { ValidateBackgroundEmoji( backgroundEmojiId, colorCollectible, - backgroundEmoji, + backgroundEmojiData, backgroundEmojiCache, cache, view); @@ -811,7 +811,7 @@ void Reply::paint( rect, _hasQuoteIcon, *backgroundEmojiCache, - backgroundEmoji->firstGiftFrame); + backgroundEmojiData->firstGiftFrame); } } if (!inBubble) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp index de892a06ba..82d61b73e2 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp @@ -376,25 +376,25 @@ void Contact::draw(Painter &p, const PaintContext &context) const { const auto backgroundEmojiId = _contact ? _contact->backgroundEmojiId() : DocumentId(); - const auto backgroundEmoji = backgroundEmojiId - ? st->backgroundEmojiData(backgroundEmojiId).get() + const auto backgroundEmojiData = backgroundEmojiId + ? st->backgroundEmojiData(backgroundEmojiId, colorCollectible).get() : nullptr; - const auto backgroundEmojiCache = !backgroundEmoji + const auto backgroundEmojiCache = !backgroundEmojiData ? nullptr : useColorCollectible - ? &backgroundEmoji->collectibleCaches[colorCollectible] - : &backgroundEmoji->caches[Ui::BackgroundEmojiData::CacheIndex( + ? &backgroundEmojiData->collectibleCaches[colorCollectible] + : &backgroundEmojiData->caches[Ui::BackgroundEmojiData::CacheIndex( selected, context.outbg, true, useColorIndex ? (colorIndex + 1) : 0)]; Ui::Text::ValidateQuotePaintCache(*cache, _st); Ui::Text::FillQuotePaint(p, outer, *cache, _st); - if (backgroundEmoji) { + if (backgroundEmojiData) { ValidateBackgroundEmoji( backgroundEmojiId, colorCollectible, - backgroundEmoji, + backgroundEmojiData, backgroundEmojiCache, cache, view); @@ -407,7 +407,7 @@ void Contact::draw(Painter &p, const PaintContext &context) const { r, false, *backgroundEmojiCache, - backgroundEmoji->firstGiftFrame); + backgroundEmojiData->firstGiftFrame); } } diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp index c7217d5feb..0b08c3ff55 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp @@ -920,25 +920,25 @@ void WebPage::draw(Painter &p, const PaintContext &context) const { : (sponsored && sponsored->backgroundEmojiId) ? sponsored->backgroundEmojiId : view->contentBackgroundEmojiId(); - const auto backgroundEmoji = backgroundEmojiId - ? st->backgroundEmojiData(backgroundEmojiId).get() + const auto backgroundEmojiData = backgroundEmojiId + ? st->backgroundEmojiData(backgroundEmojiId, colorCollectible).get() : nullptr; - const auto backgroundEmojiCache = !backgroundEmoji + const auto backgroundEmojiCache = !backgroundEmojiData ? nullptr : useColorCollectible - ? &backgroundEmoji->collectibleCaches[colorCollectible] - : &backgroundEmoji->caches[Ui::BackgroundEmojiData::CacheIndex( + ? &backgroundEmojiData->collectibleCaches[colorCollectible] + : &backgroundEmojiData->caches[Ui::BackgroundEmojiData::CacheIndex( selected, context.outbg, true, useColorIndex ? (colorIndex + 1) : 0)]; Ui::Text::ValidateQuotePaintCache(*cache, _st); Ui::Text::FillQuotePaint(p, outer, *cache, _st); - if (backgroundEmoji) { + if (backgroundEmojiData) { ValidateBackgroundEmoji( backgroundEmojiId, colorCollectible, - backgroundEmoji, + backgroundEmojiData, backgroundEmojiCache, cache, view); @@ -948,7 +948,7 @@ void WebPage::draw(Painter &p, const PaintContext &context) const { outer, false, *backgroundEmojiCache, - backgroundEmoji->firstGiftFrame); + backgroundEmojiData->firstGiftFrame); } } else if (factcheck && factcheck->expandable) { const auto &icon = factcheck->expanded ? _st.collapse : _st.expand; diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index e0a27fd228..770078a94d 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -2212,7 +2212,7 @@ void GenericCreditsEntryBox( e.uniqueGift, e.giftResaleForceTon, to, - crl::guard(box, [=] { box->closeBox(); })); + crl::guard(box, [=](bool) { box->closeBox(); })); } else if (canUpgrade || canGiftUpgrade) { upgrade(); } else if (canToggle && !e.savedToProfile) { diff --git a/Telegram/SourceFiles/ui/chat/chat_style.cpp b/Telegram/SourceFiles/ui/chat/chat_style.cpp index e48c41d71e..974b85f192 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.cpp +++ b/Telegram/SourceFiles/ui/chat/chat_style.cpp @@ -954,7 +954,9 @@ const style::TextPalette &ChatStyle::collectibleTextPalette( } not_null ChatStyle::backgroundEmojiData( - uint64 id) const { + uint64 emojiId, + const std::shared_ptr &collectible) const { + const auto id = collectible ? collectible->collectibleId : emojiId; return &_backgroundEmojis[id]; } diff --git a/Telegram/SourceFiles/ui/chat/chat_style.h b/Telegram/SourceFiles/ui/chat/chat_style.h index 3957d55b97..f8718f7484 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.h +++ b/Telegram/SourceFiles/ui/chat/chat_style.h @@ -379,7 +379,8 @@ public: const std::shared_ptr &collectible) const; [[nodiscard]] not_null backgroundEmojiData( - uint64 id) const; + uint64 emojiId, + const std::shared_ptr &collectible) const; [[nodiscard]] const CornersPixmaps &msgBotKbOverBgAddCornersSmall() const; [[nodiscard]] const CornersPixmaps &msgBotKbOverBgAddCornersLarge() const;