From 09643aef8239019f9c7bd1baf74391a63d36c40e Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 11 Nov 2024 02:23:19 +0300 Subject: [PATCH] Added support of credits subscriptions to send credits box. --- Telegram/Resources/langs/lang.strings | 4 + .../SourceFiles/boxes/send_credits_box.cpp | 150 ++++++++++++++++-- Telegram/SourceFiles/dialogs/dialogs_entry.h | 2 +- Telegram/SourceFiles/ui/effects/credits.style | 2 + 4 files changed, 148 insertions(+), 10 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 846e92e63..d0988628c 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2445,6 +2445,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_credits_box_out_media#other" = "Do you want to unlock {media} in {chat} for **{count} Stars**?"; "lng_credits_box_out_media_user#one" = "Do you want to unlock {media} from {user} for **{count} Star**?"; "lng_credits_box_out_media_user#other" = "Do you want to unlock {media} from {user} for **{count} Stars**?"; +"lng_credits_box_out_subscription#one" = "Do you want to subscribe to **{title}** in **{bot}** for **{count}** star per month?"; +"lng_credits_box_out_subscription#other" = "Do you want to subscribe to **{title}** in **{bot}** for **{count}** stars per month?"; +"lng_credits_box_out_subscription_confirm#one" = "Subscribe for {emoji} {count} / month"; +"lng_credits_box_out_subscription_confirm#other" = "Subscribe for {emoji} {count} / month"; "lng_credits_box_out_photo" = "a photo"; "lng_credits_box_out_photos#one" = "{count} photo"; "lng_credits_box_out_photos#other" = "{count} photos"; diff --git a/Telegram/SourceFiles/boxes/send_credits_box.cpp b/Telegram/SourceFiles/boxes/send_credits_box.cpp index 3ac13da20..d00a95f1f 100644 --- a/Telegram/SourceFiles/boxes/send_credits_box.cpp +++ b/Telegram/SourceFiles/boxes/send_credits_box.cpp @@ -26,17 +26,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_credits_graphics.h" #include "ui/boxes/confirm_box.h" #include "ui/controls/userpic_button.h" +#include "ui/effects/credits_graphics.h" #include "ui/effects/premium_graphics.h" #include "ui/effects/premium_top_bar.h" // Ui::Premium::ColorizedSvg. #include "ui/image/image_prepare.h" #include "ui/layers/generic_box.h" +#include "ui/painter.h" #include "ui/rect.h" #include "ui/text/text_utilities.h" #include "ui/vertical_list.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/peer_bubble.h" #include "styles/style_boxes.h" +#include "styles/style_chat.h" #include "styles/style_credits.h" #include "styles/style_giveaway.h" +#include "styles/style_info.h" // inviteLinkSubscribeBoxTerms #include "styles/style_layers.h" #include "styles/style_premium.h" #include "styles/style_settings.h" @@ -92,6 +97,44 @@ struct PaidMediaData { }; } +void AddTerms( + not_null box, + not_null button, + const style::Box &stBox) { + const auto terms = Ui::CreateChild( + button->parentWidget(), + tr::lng_channel_invite_subscription_terms( + lt_link, + rpl::combine( + tr::lng_paid_react_agree_link(), + tr::lng_group_invite_subscription_about_url() + ) | rpl::map([](const QString &text, const QString &url) { + return Ui::Text::Link(text, url); + }), + Ui::Text::RichLangValue), + st::inviteLinkSubscribeBoxTerms); + const auto &buttonPadding = stBox.buttonPadding; + const auto style = box->lifetime().make_state(style::Box{ + .buttonPadding = buttonPadding + QMargins(0, 0, 0, terms->height()), + .buttonHeight = stBox.buttonHeight, + .button = stBox.button, + .margin = stBox.margin, + .title = stBox.title, + .bg = stBox.bg, + .titleAdditionalFg = stBox.titleAdditionalFg, + .shadowIgnoreTopSkip = stBox.shadowIgnoreTopSkip, + .shadowIgnoreBottomSkip = stBox.shadowIgnoreBottomSkip, + }); + button->geometryValue() | rpl::start_with_next([=](const QRect &rect) { + terms->resizeToWidth(box->width() + - rect::m::sum::h(st::boxRowPadding)); + terms->moveToLeft( + rect.x() + (rect.width() - terms->width()) / 2, + rect::bottom(rect) + buttonPadding.bottom() / 2); + }, terms->lifetime()); + box->setStyle(*style); +} + [[nodiscard]] rpl::producer SendCreditsConfirmText( not_null session, not_null form) { @@ -150,6 +193,16 @@ struct PaidMediaData { } const auto bot = session->data().user(form->botId); + if (form->invoice.subscriptionPeriod) { + return tr::lng_credits_box_out_subscription( + lt_count, + rpl::single(form->invoice.amount) | tr::to_count(), + lt_title, + rpl::single(TextWithEntities{ form->title }), + lt_bot, + rpl::single(TextWithEntities{ bot->name() }), + Ui::Text::RichLangValue); + } return tr::lng_credits_box_out_sure( lt_count, rpl::single(form->invoice.amount) | tr::to_count(), @@ -190,6 +243,57 @@ struct PaidMediaData { st::defaultUserpicButton); } +[[nodiscard]] not_null SendCreditsBadge( + not_null parent, + int credits) { + const auto widget = Ui::CreateChild(parent); + const auto &font = st::chatGiveawayBadgeFont; + const auto text = QString::number(credits); + const auto iconHeight = font->ascent - font->descent; + const auto iconWidth = iconHeight + st::lineWidth; + const auto width = font->width(text) + iconWidth + st::lineWidth; + const auto inner = QRect(0, 0, width, font->height); + const auto rect = inner + st::subscriptionCreditsBadgePadding; + const auto size = rect.size(); + const auto svg = widget->lifetime().make_state( + Ui::Premium::Svg()); + const auto half = st::chatGiveawayBadgeStroke / 2.; + const auto left = st::subscriptionCreditsBadgePadding.left(); + const auto smaller = QRectF(rect.translated(-rect.topLeft())) + - Margins(half); + const auto radius = smaller.height() / 2.; + widget->resize(size); + + widget->paintRequest() | rpl::start_with_next([=] { + auto p = QPainter(widget); + auto hq = PainterHighQualityEnabler(p); + p.setPen(QPen(st::premiumButtonFg, st::chatGiveawayBadgeStroke * 1.)); + p.setBrush(st::creditsBg3); + p.drawRoundedRect(smaller, radius, radius); + + p.translate(0, font->descent / 2); + + p.setPen(st::premiumButtonFg); + p.setBrush(st::premiumButtonFg); + svg->render( + &p, + QRect( + left, + half + (inner.height() - iconHeight) / 2, + iconHeight, + iconHeight)); + + p.setFont(font); + p.drawText( + left + iconWidth, + st::subscriptionCreditsBadgePadding.top() + font->ascent, + text); + + }, widget->lifetime()); + + return widget; +} + } // namespace void SendCreditsBox( @@ -203,7 +307,8 @@ void SendCreditsBox( rpl::variable confirmButtonBusy = false; }; const auto state = box->lifetime().make_state(); - box->setStyle(st::giveawayGiftCodeBox); + const auto &stBox = st::giveawayGiftCodeBox; + box->setStyle(stBox); box->setNoContentMargin(true); const auto session = form->invoice.session; @@ -245,14 +350,36 @@ void SendCreditsBox( content, SendCreditsThumbnail(content, session, form.get(), photoSize))); thumb->setAttribute(Qt::WA_TransparentForMouseEvents); + if (form->invoice.subscriptionPeriod) { + const auto badge = SendCreditsBadge(content, form->invoice.amount); + thumb->geometryValue() | rpl::start_with_next([=](const QRect &r) { + badge->moveToLeft( + r.x() + (r.width() - badge->width()) / 2, + rect::bottom(r) - badge->height() / 2); + }, badge->lifetime()); + Ui::AddSkip(content); + Ui::AddSkip(content); + } Ui::AddSkip(content); box->addRow(object_ptr>( box, object_ptr( box, - tr::lng_credits_box_out_title(), + form->invoice.subscriptionPeriod + ? rpl::single(form->title) + : tr::lng_credits_box_out_title(), st::settingsPremiumUserTitle))); + if (form->invoice.subscriptionPeriod && form->botId && form->photo) { + Ui::AddSkip(content); + Ui::AddSkip(content); + const auto bot = session->data().user(form->botId); + box->addRow( + object_ptr>( + box, + Ui::CreatePeerBubble(box, bot))); + Ui::AddSkip(content); + } Ui::AddSkip(content); box->addRow(object_ptr>( box, @@ -306,6 +433,9 @@ void SendCreditsBox( } }).send(); }); + if (form->invoice.subscriptionPeriod) { + AddTerms(box, button, stBox); + } { using namespace Info::Statistics; const auto loadingAnimation = InfiniteRadialAnimationWidget( @@ -317,12 +447,14 @@ void SendCreditsBox( SetButtonMarkedLabel( button, rpl::combine( - tr::lng_credits_box_out_confirm( - lt_count, - rpl::single(form->invoice.amount) | tr::to_count(), - lt_emoji, - rpl::single(CreditsEmojiSmall(session)), - Ui::Text::RichLangValue), + (form->invoice.subscriptionPeriod + ? tr::lng_credits_box_out_subscription_confirm + : tr::lng_credits_box_out_confirm)( + lt_count, + rpl::single(form->invoice.amount) | tr::to_count(), + lt_emoji, + rpl::single(CreditsEmojiSmall(session)), + Ui::Text::RichLangValue), state->confirmButtonBusy.value() ) | rpl::map([](TextWithEntities &&text, bool busy) { return busy ? TextWithEntities() : std::move(text); @@ -332,7 +464,7 @@ void SendCreditsBox( box->getDelegate()->style().button.textFg->c); const auto buttonWidth = st::boxWidth - - rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding); + - rect::m::sum::h(stBox.buttonPadding); button->widthValue() | rpl::filter([=] { return (button->widthNoMargins() != buttonWidth); }) | rpl::start_with_next([=] { diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.h b/Telegram/SourceFiles/dialogs/dialogs_entry.h index 56e84f48c..3a19eec68 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.h +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.h @@ -295,7 +295,7 @@ auto Entry::unreadStateChangeNotifier(bool required) { _flags |= Flag::InUnreadChangeBlock; const auto notify = required && inChatList(); const auto wasState = notify ? chatListUnreadState() : UnreadState(); - return gsl::finally([=] { + return gsl::finally([=, this] { _flags &= ~Flag::InUnreadChangeBlock; if (notify) { Assert(inChatList()); diff --git a/Telegram/SourceFiles/ui/effects/credits.style b/Telegram/SourceFiles/ui/effects/credits.style index 734b2a2f0..1b82fe030 100644 --- a/Telegram/SourceFiles/ui/effects/credits.style +++ b/Telegram/SourceFiles/ui/effects/credits.style @@ -156,3 +156,5 @@ giftListAbout: FlatLabel(defaultFlatLabel) { giftListAboutMargin: margins(12px, 24px, 12px, 24px); giftBoxEmojiToggleTop: 7px; giftBoxLimitTop: 28px; + +subscriptionCreditsBadgePadding: margins(10px, 1px, 8px, 3px);