From 915a1b105c84126f52bfc6bcea744fcfccb51229 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 27 Jun 2025 20:25:58 +0400 Subject: [PATCH] Add balance of stars / TON. --- .../icons/settings/premium/checklist.svg | 7 ++ Telegram/Resources/langs/lang.strings | 4 +- Telegram/SourceFiles/api/api_chat_invite.cpp | 1 + .../SourceFiles/boxes/send_credits_box.cpp | 1 + .../controls/history_view_suggest_options.cpp | 83 +++++++++++++++---- .../payments/payments_reaction_process.cpp | 1 + .../payments/ui/payments_reaction_box.cpp | 2 + .../payments/ui/payments_reaction_box.h | 5 ++ Telegram/SourceFiles/settings/settings.style | 1 + .../SourceFiles/settings/settings_credits.cpp | 1 + .../settings/settings_credits_graphics.cpp | 58 +++++++------ .../settings/settings_credits_graphics.h | 1 + .../SourceFiles/settings/settings_premium.cpp | 2 +- Telegram/SourceFiles/ui/chat/chat.style | 2 +- 14 files changed, 122 insertions(+), 47 deletions(-) create mode 100644 Telegram/Resources/icons/settings/premium/checklist.svg diff --git a/Telegram/Resources/icons/settings/premium/checklist.svg b/Telegram/Resources/icons/settings/premium/checklist.svg new file mode 100644 index 0000000000..6cb5fac3f9 --- /dev/null +++ b/Telegram/Resources/icons/settings/premium/checklist.svg @@ -0,0 +1,7 @@ + + + Icon / Filled / checklist + + + + \ No newline at end of file diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 7f7aa95438..85872ff6eb 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4497,8 +4497,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_suggest_warn_text_ton" = "You won't receive **TON** for the post if you delete it now. The post must remain visible for at least **24 hours** after it was published."; "lng_suggest_warn_delete_anyway" = "Delete Anyway"; "lng_suggest_low_ton_title" = "{amount} TON Needed"; -"lng_suggest_low_ton_text" = "Buy **TON** to suggest message to {channel} and others."; -"lng_suggest_low_ton_fragment" = "Buy on Fragment"; +"lng_suggest_low_ton_text" = "You can add funds to your balance via the third-party platform Fragment."; +"lng_suggest_low_ton_fragment" = "Add Funds via Fragment"; "lng_suggest_low_ton_fragment_url" = "https://fragment.com/ads/topup"; "lng_reply_in_another_title" = "Reply in..."; diff --git a/Telegram/SourceFiles/api/api_chat_invite.cpp b/Telegram/SourceFiles/api/api_chat_invite.cpp index 18cab335e6..d108a13927 100644 --- a/Telegram/SourceFiles/api/api_chat_invite.cpp +++ b/Telegram/SourceFiles/api/api_chat_invite.cpp @@ -256,6 +256,7 @@ void ConfirmSubscriptionBox( { const auto balance = Settings::AddBalanceWidget( content, + session, session->credits().balanceValue(), true); session->credits().load(true); diff --git a/Telegram/SourceFiles/boxes/send_credits_box.cpp b/Telegram/SourceFiles/boxes/send_credits_box.cpp index 2a4e4f242a..97cf32238b 100644 --- a/Telegram/SourceFiles/boxes/send_credits_box.cpp +++ b/Telegram/SourceFiles/boxes/send_credits_box.cpp @@ -487,6 +487,7 @@ void SendCreditsBox( session->credits().load(true); const auto balance = Settings::AddBalanceWidget( content, + session, session->credits().balanceValue(), false); rpl::combine( diff --git a/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp b/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp index 2a7310ce2d..91e291d6fa 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_suggest_options.cpp @@ -122,13 +122,21 @@ void ChooseSuggestPriceBox( const auto starsMax = session->appConfig().suggestedPostStarsMax(); const auto nanoTonMin = session->appConfig().suggestedPostNanoTonMin(); const auto nanoTonMax = session->appConfig().suggestedPostNanoTonMax(); + const auto container = box->verticalLayout(); box->setStyle(st::suggestPriceBox); - box->setTitle((args.mode == SuggestMode::New) - ? tr::lng_suggest_options_title() - : tr::lng_suggest_options_change()); + box->setNoContentMargin(true); + + Ui::AddSkip(container, st::boxTitleHeight * 1.1); + box->addRow(object_ptr>( + box, + object_ptr( + box, + ((args.mode == SuggestMode::New) + ? tr::lng_suggest_options_title() + : tr::lng_suggest_options_change()), + st::settingsPremiumUserTitle))); - const auto container = box->verticalLayout(); state->buttons.push_back({ .text = Ui::Text::String( st::semiboldTextStyle, @@ -159,7 +167,7 @@ void ChooseSuggestPriceBox( const auto buttons = box->addRow( object_ptr(box), (st::boxRowPadding - - QMargins(padding.left() / 2, 0, padding.right() / 2, 0))); + - QMargins(padding.left() / 2, -st::normalFont->height, padding.right() / 2, 0))); const auto height = y + state->buttons.back().geometry.height() + st::giftBoxTabsMargin.bottom(); @@ -253,7 +261,7 @@ void ChooseSuggestPriceBox( Ui::AddSubsectionTitle( starsInner, tr::lng_suggest_options_stars_price(), - QMargins(added.left(), 0, added.right(), 0)); + QMargins(added.left(), 0, added.right(), -st::defaultSubsectionTitlePadding.bottom())); const auto starsFieldWrap = starsInner->add( object_ptr( @@ -295,7 +303,7 @@ void ChooseSuggestPriceBox( Ui::AddSubsectionTitle( tonInner, tr::lng_suggest_options_ton_price(), - QMargins(added.left(), 0, added.right(), 0)); + QMargins(added.left(), 0, added.right(), -st::defaultSubsectionTitlePadding.bottom())); const auto tonFieldWrap = tonInner->add( object_ptr( @@ -522,6 +530,39 @@ void ChooseSuggestPriceBox( }) | rpl::start_with_next([=] { button->resizeToWidth(buttonWidth); }, button->lifetime()); + + + { + const auto close = Ui::CreateChild( + container, + st::boxTitleClose); + close->setClickedCallback([=] { box->closeBox(); }); + container->widthValue() | rpl::start_with_next([=](int) { + close->moveToRight(0, 0); + }, close->lifetime()); + } + + { + session->credits().load(true); + session->credits().tonLoad(true); + const auto balance = Settings::AddBalanceWidget( + container, + session, + rpl::conditional( + state->ton.value(), + session->credits().tonBalanceValue(), + session->credits().balanceValue()), + false); + rpl::combine( + balance->sizeValue(), + container->sizeValue() + ) | rpl::start_with_next([=](const QSize &, const QSize &) { + balance->moveToLeft( + st::creditsHistoryRightSkip * 2, + st::creditsHistoryRightSkip); + balance->update(); + }, balance->lifetime()); + } } bool CanEditSuggestedMessage(not_null item) { @@ -546,6 +587,11 @@ void InsufficientTonBox( not_null box, not_null peer, CreditsAmount required) { + box->setStyle(st::suggestPriceBox); + box->addTopButton(st::boxTitleClose, [=] { + box->closeBox(); + }); + auto icon = Settings::CreateLottieIcon( box->verticalLayout(), { @@ -571,21 +617,24 @@ void InsufficientTonBox( const auto label = box->addRow( object_ptr( box, - tr::lng_suggest_low_ton_text( - lt_channel, - rpl::single(Ui::Text::Bold(peer->name())), - Ui::Text::RichLangValue), + tr::lng_suggest_low_ton_text(Ui::Text::RichLangValue), st::lowTonText), st::boxRowPadding + st::lowTonTextPadding); label->setTryMakeSimilarLines(true); label->resizeToWidth( st::boxWidth - st::boxRowPadding.left() - st::boxRowPadding.right()); - box->addButton(tr::lng_suggest_low_ton_fragment(), [=] { - UrlClickHandler::Open(tr::lng_suggest_low_ton_fragment_url(tr::now)); - }); - box->addButton(tr::lng_cancel(), [=] { - box->closeBox(); - }); + + const auto url = tr::lng_suggest_low_ton_fragment_url(tr::now); + const auto button = box->addButton( + tr::lng_suggest_low_ton_fragment(), + [=] { UrlClickHandler::Open(url); }); + const auto buttonWidth = st::boxWidth + - rect::m::sum::h(st::suggestPriceBox.buttonPadding); + button->widthValue() | rpl::filter([=] { + return (button->widthNoMargins() != buttonWidth); + }) | rpl::start_with_next([=] { + button->resizeToWidth(buttonWidth); + }, button->lifetime()); } SuggestOptions::SuggestOptions( diff --git a/Telegram/SourceFiles/payments/payments_reaction_process.cpp b/Telegram/SourceFiles/payments/payments_reaction_process.cpp index b585bab604..3e5236fda1 100644 --- a/Telegram/SourceFiles/payments/payments_reaction_process.cpp +++ b/Telegram/SourceFiles/payments/payments_reaction_process.cpp @@ -251,6 +251,7 @@ void ShowPaidReactionDetails( .chosen = chosen, .max = max, .top = std::move(top), + .session = &channel->session(), .channel = channel->name(), .submit = std::move(submitText), .balanceValue = session->credits().balanceValue(), diff --git a/Telegram/SourceFiles/payments/ui/payments_reaction_box.cpp b/Telegram/SourceFiles/payments/ui/payments_reaction_box.cpp index e227c5933b..67ad2c7cc0 100644 --- a/Telegram/SourceFiles/payments/ui/payments_reaction_box.cpp +++ b/Telegram/SourceFiles/payments/ui/payments_reaction_box.cpp @@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Settings { [[nodiscard]] not_null AddBalanceWidget( not_null parent, + not_null session, rpl::producer balanceValue, bool rightAlign, rpl::producer opacityValue = nullptr); @@ -572,6 +573,7 @@ void PaidReactionsBox( { const auto balance = Settings::AddBalanceWidget( content, + args.session, std::move(args.balanceValue), false); rpl::combine( diff --git a/Telegram/SourceFiles/payments/ui/payments_reaction_box.h b/Telegram/SourceFiles/payments/ui/payments_reaction_box.h index bada338249..bf52952ac3 100644 --- a/Telegram/SourceFiles/payments/ui/payments_reaction_box.h +++ b/Telegram/SourceFiles/payments/ui/payments_reaction_box.h @@ -13,6 +13,10 @@ namespace style { struct RoundCheckbox; } // namespace style +namespace Main { +class Session; +} // namespace Main + namespace Ui { class BoxContent; @@ -39,6 +43,7 @@ struct PaidReactionBoxArgs { std::vector top; + not_null session; QString channel; Fn(rpl::producer amount)> submit; rpl::producer balanceValue; diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index 6da9e1117f..76396d8b28 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -103,6 +103,7 @@ settingsPremiumIconLastSeen: icon {{ "settings/premium/lastseen", settingsIconFg settingsPremiumIconPrivacy: icon {{ "settings/premium/privacy", settingsIconFg }}; settingsPremiumIconBusiness: icon {{ "settings/premium/market", settingsIconFg }}; settingsPremiumIconEffects: icon {{ "settings/premium/effects", settingsIconFg }}; +settingsPremiumIconChecklist: icon {{ "settings/premium/checklist", settingsIconFg }}; settingsStoriesIconOrder: icon {{ "settings/premium/stories_order", premiumButtonBg1 }}; settingsStoriesIconStealth: icon {{ "menu/stealth", premiumButtonBg1 }}; diff --git a/Telegram/SourceFiles/settings/settings_credits.cpp b/Telegram/SourceFiles/settings/settings_credits.cpp index d71be42094..c758708953 100644 --- a/Telegram/SourceFiles/settings/settings_credits.cpp +++ b/Telegram/SourceFiles/settings/settings_credits.cpp @@ -607,6 +607,7 @@ QPointer Credits::createPinnedToTop( { const auto balance = AddBalanceWidget( content, + &_controller->session(), _controller->session().credits().balanceValue(), true, content->heightValue() | rpl::map([=](int height) { diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index b2a9a0c3dd..03a71c8a26 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp @@ -43,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/bot/starref/info_bot_starref_common.h" #include "info/channel_statistics/boosts/giveaway/boost_badge.h" // InfiniteRadialAnimationWidget. #include "info/channel_statistics/earn/info_channel_earn_widget.h" // Info::ChannelEarn::Make. +#include "info/channel_statistics/earn/earn_icons.h" #include "info/peer_gifts/info_peer_gifts_common.h" #include "info/settings/info_settings_widget.h" // SectionCustomTopBarData. #include "info/statistics/info_statistics_list_controllers.h" @@ -684,19 +685,17 @@ void FillCreditOptions( not_null AddBalanceWidget( not_null parent, + not_null session, rpl::producer balanceValue, bool rightAlign, rpl::producer opacityValue) { struct State final { - QImage star; float64 opacity = 1.0; Ui::Text::String label; Ui::Text::String count; }; const auto balance = Ui::CreateChild(parent); const auto state = balance->lifetime().make_state(); - state->star = QImage(Ui::GenerateStars(st::creditsBalanceStarHeight, 1)); - const auto starSize = state->star.size() / style::DevicePixelRatio(); state->label = Ui::Text::String( st::defaultTextStyle, tr::lng_credits_summary_balance(tr::now)); @@ -708,22 +707,40 @@ not_null AddBalanceWidget( state->opacity = value; }, balance->lifetime()); } - const auto diffBetweenStarAndCount = state->count.style()->font->spacew; const auto resize = [=] { balance->resize( - std::max( - state->label.maxWidth(), - state->count.maxWidth() - + starSize.width() - + diffBetweenStarAndCount), - state->label.style()->font->height + starSize.height()); + std::max(state->label.maxWidth(), state->count.maxWidth()), + (state->label.style()->font->height + + state->count.style()->font->height)); }; std::move( balanceValue ) | rpl::start_with_next([=](CreditsAmount value) { - state->count.setText( + auto text = TextWithEntities(); + const auto manager = &session->data().customEmojiManager(); + if (value.ton()) { + text.append(Ui::Text::SingleCustomEmoji( + manager->registerInternalEmoji( + Ui::Earn::IconCurrencyColored( + st::tonFieldIconSize, + st::windowActiveTextFg->c), + st::channelEarnCurrencyLearnMargins, + false)) + ).append(' ').append(Lang::FormatCreditsAmountDecimal(value)); + } else { + text.append( + manager->creditsEmoji() + ).append(' ').append( + Lang::FormatCreditsAmountToShort(value).string); + } + state->count.setMarkedText( st::semiboldTextStyle, - Lang::FormatCreditsAmountToShort(value).string); + text, + kMarkupTextOptions, + Core::TextContext({ + .session = session, + .repaint = [=] { balance->update(); }, + })); balance->setBalance(value); resize(); }, balance->lifetime()); @@ -742,22 +759,10 @@ not_null AddBalanceWidget( }); state->count.draw(p, { .position = QPoint( - (rightAlign - ? (balance->width() - state->count.maxWidth()) - : (starSize.width() + diffBetweenStarAndCount)), - state->label.minHeight() - + (starSize.height() - state->count.minHeight()) / 2), + rightAlign ? (balance->width() - state->count.maxWidth()) : 0, + state->label.minHeight()), .availableWidth = balance->width(), }); - p.drawImage( - (rightAlign - ? (balance->width() - - state->count.maxWidth() - - starSize.width() - - diffBetweenStarAndCount) - : 0), - state->label.minHeight(), - state->star); }, balance->lifetime()); return balance; } @@ -2489,6 +2494,7 @@ void SmallBalanceBox( { const auto balance = AddBalanceWidget( content, + &show->session(), show->session().credits().balanceValue(), true); show->session().credits().load(true); diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.h b/Telegram/SourceFiles/settings/settings_credits_graphics.h index 9aedbee770..9789278fac 100644 --- a/Telegram/SourceFiles/settings/settings_credits_graphics.h +++ b/Telegram/SourceFiles/settings/settings_credits_graphics.h @@ -79,6 +79,7 @@ void FillCreditOptions( [[nodiscard]] not_null AddBalanceWidget( not_null parent, + not_null session, rpl::producer balanceValue, bool rightAlign, rpl::producer opacityValue = nullptr); diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp index 83d69ec8c5..76d93b9d5d 100644 --- a/Telegram/SourceFiles/settings/settings_premium.cpp +++ b/Telegram/SourceFiles/settings/settings_premium.cpp @@ -389,7 +389,7 @@ using Order = std::vector; { u"todo"_q, Entry{ - &st::settingsPremiumIconTranslations, + &st::settingsPremiumIconChecklist, tr::lng_premium_summary_subtitle_todo_lists(), tr::lng_premium_summary_about_todo_lists(), PremiumFeature::TodoLists, diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 6f67637eb5..138ef0feae 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -1365,7 +1365,7 @@ chatTabsOutlineHorizontal: ChatTabsOutline { chatTabsOutlineVertical: ChatTabsOutline(chatTabsOutlineHorizontal) { } -suggestPriceTonIconMargins: margins(0px, 1px, 0px, 0px); +suggestPriceTonIconMargins: margins(0px, 2px, 0px, 0px); suggestPriceBox: Box(defaultBox) { buttonPadding: margins(22px, 22px, 22px, 22px); buttonHeight: 42px;