From 9d7aab4326f490b6a20d4fbadb28804a80454a5d Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 28 Jun 2025 13:51:30 +0300 Subject: [PATCH] Added initial support of personal earn currency history. --- Telegram/SourceFiles/api/api_credits.cpp | 9 ++- Telegram/SourceFiles/api/api_credits.h | 6 +- .../SourceFiles/boxes/gift_premium_box.cpp | 2 +- .../info_statistics_list_controllers.cpp | 58 ++++++++++++++++-- .../SourceFiles/settings/settings_credits.cpp | 7 ++- .../settings/settings_credits_graphics.cpp | 61 ++++++++++++++++--- Telegram/SourceFiles/ui/effects/credits.style | 1 + 7 files changed, 121 insertions(+), 23 deletions(-) diff --git a/Telegram/SourceFiles/api/api_credits.cpp b/Telegram/SourceFiles/api/api_credits.cpp index 6c9147e2b8..2832e1bd53 100644 --- a/Telegram/SourceFiles/api/api_credits.cpp +++ b/Telegram/SourceFiles/api/api_credits.cpp @@ -318,13 +318,18 @@ void CreditsStatus::request( }).send(); } -CreditsHistory::CreditsHistory(not_null peer, bool in, bool out) +CreditsHistory::CreditsHistory( + not_null peer, + bool in, + bool out, + bool currency) : _peer(peer) -, _flags((in == out) +, _flags(((in == out) ? HistoryTL::Flags(0) : HistoryTL::Flags(0) | (in ? HistoryTL::Flag::f_inbound : HistoryTL::Flags(0)) | (out ? HistoryTL::Flag::f_outbound : HistoryTL::Flags(0))) + | (currency ? HistoryTL::Flag::f_ton : HistoryTL::Flags(0))) , _api(&peer->session().api().instance()) { } diff --git a/Telegram/SourceFiles/api/api_credits.h b/Telegram/SourceFiles/api/api_credits.h index 559e84711e..1b5745e42b 100644 --- a/Telegram/SourceFiles/api/api_credits.h +++ b/Telegram/SourceFiles/api/api_credits.h @@ -75,7 +75,11 @@ private: class CreditsHistory final { public: - CreditsHistory(not_null peer, bool in, bool out); + CreditsHistory( + not_null peer, + bool in, + bool out, + bool currency = false); void request( const Data::CreditsStatusSlice::OffsetToken &token, diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp index 0b898a86ee..a807e2f8bd 100644 --- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp +++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp @@ -1729,7 +1729,7 @@ void AddCreditsHistoryEntryTable( (entry.gift ? tr::lng_credits_box_history_entry_peer_in : tr::lng_credits_box_history_entry_via)(), - (entry.gift + ((entry.gift && entry.credits.stars()) ? tr::lng_credits_box_history_entry_anonymous : tr::lng_credits_box_history_entry_fragment)( Ui::Text::RichLangValue)); diff --git a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp index 9c8253b865..8bed42de68 100644 --- a/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp +++ b/Telegram/SourceFiles/info/statistics/info_statistics_list_controllers.cpp @@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peer_list_controllers.h" #include "boxes/peer_list_widgets.h" #include "info/channel_statistics/earn/earn_format.h" +#include "info/channel_statistics/earn/earn_icons.h" +#include "info/channel_statistics/earn/earn_format.h" #include "chat_helpers/stickers_gift_box_pack.h" #include "core/ui_integration.h" // TextContext #include "data/data_channel.h" @@ -42,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/vertical_layout.h" #include "styles/style_boxes.h" #include "styles/style_color_indices.h" +#include "styles/style_channel_earn.h" #include "styles/style_credits.h" #include "styles/style_dialogs.h" // dialogsStoriesFull. #include "styles/style_layers.h" // boxRowPadding. @@ -49,6 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_settings.h" #include "styles/style_statistics.h" #include "styles/style_window.h" +#include "styles/style_chat.h" namespace Info::Statistics { namespace { @@ -830,6 +834,7 @@ private: Ui::Text::String _description; Ui::Text::String _rightText; + Ui::Text::String _rightMinorText; std::shared_ptr _descriptionThumbnail; QImage _descriptionThumbnailCache; @@ -910,6 +915,8 @@ void CreditsRow::init() { : (_entry.peerType == Data::CreditsHistoryEntry::PeerType::PremiumBot) ? tr::lng_credits_box_history_entry_via_premium_bot(tr::now) + : (_entry.peerType == Data::CreditsHistoryEntry::PeerType::Fragment) + ? tr::lng_credits_box_history_entry_fragment(tr::now) : (_entry.gift && isSpecial) ? tr::lng_credits_box_history_entry_anonymous(tr::now) : (_name == name) @@ -960,15 +967,35 @@ void CreditsRow::init() { } if (_entry) { constexpr auto kMinus = QChar(0x2212); + const auto isCurrency = _entry.credits.ton(); _rightText.setMarkedText( - st::creditsHistoryRowRightStyle, + isCurrency + ? st::channelEarnHistoryMajorLabel.style + : st::creditsHistoryRowRightStyle, TextWithEntities() .append(_entry.in ? QChar('+') : kMinus) - .append(Lang::FormatCreditsAmountDecimal(_entry.credits.abs())) + .append(isCurrency + ? Info::ChannelEarn::MajorPart(_entry.credits) + : Lang::FormatCreditsAmountDecimal(_entry.credits.abs())) .append(QChar(' ')) - .append(Ui::MakeCreditsIconEntity()), + .append(isCurrency + ? TextWithEntities() + : Ui::MakeCreditsIconEntity()), kMarkupTextOptions, _context); + if (isCurrency) { + _rightMinorText.setMarkedText( + st::channelEarnHistoryMinorLabel.style, + TextWithEntities() + .append(Info::ChannelEarn::MinorPart(_entry.credits)) + .append(QChar(' ')) + .append( + Ui::Text::SingleCustomEmoji(_entry.in + ? u"ton:in"_q + : u"ton:out"_q)), + kMarkupTextOptions, + _context); + } } if (!_paintUserpicCallback) { _paintUserpicCallback = /*_entry.stargift @@ -1023,7 +1050,9 @@ QSize CreditsRow::rightActionSize() const { return QSize(maxWidth + st::boxRowPadding.right(), _rowHeight); } else if (_subscription || _entry) { return QSize( - _rightText.maxWidth() + st::boxRowPadding.right() / 2, + _rightText.maxWidth() + + _rightMinorText.maxWidth() + + st::boxRowPadding.right() / 2, _rowHeight); } else if (!_entry && !_subscription) { return QSize(); @@ -1046,7 +1075,6 @@ void CreditsRow::rightActionPaint( int outerWidth, bool selected, bool actionSelected) { - const auto &font = _rightText.style()->font; const auto rightSkip = st::boxRowPadding.right(); if (_rightLabel) { return _rightLabel->draw(p, x, y, _rowHeight); @@ -1082,9 +1110,15 @@ void CreditsRow::rightActionPaint( : _entry.in ? st::boxTextFgGood : st::menuIconAttentionColor); + const auto xMinor = outerWidth - _rightMinorText.maxWidth() - rightSkip; + _rightMinorText.draw(p, Ui::Text::PaintContext{ + .position = QPoint(xMinor, y + st::creditsHistoryRowRightMinorTop), + .outerWidth = outerWidth, + .availableWidth = outerWidth, + }); _rightText.draw(p, Ui::Text::PaintContext{ .position = QPoint( - outerWidth - _rightText.maxWidth() - rightSkip, + xMinor - _rightText.maxWidth(), y + st::creditsHistoryRowRightTop), .outerWidth = outerWidth, .availableWidth = outerWidth, @@ -1179,6 +1213,18 @@ CreditsController::CreditsController(CreditsDescriptor d) Ui::MakeCreditsIconEmoji(height, 1), QPoint(-st::lineWidth, st::lineWidth)); } + if (data.startsWith(u"ton"_q)) { + const auto in = data.split(u":"_q)[1].startsWith(u"in"_q); + return std::make_unique( + std::make_unique( + Ui::Earn::IconCurrencyColored( + st::tonFieldIconSize, + in + ? st::boxTextFgGood->c + : st::menuIconAttentionColor->c), + data.toString()), + QPoint(0, st::lineWidth)); + } const auto desc = DeserializeCreditsRowDescriptionData( data.toString()); if (!desc.rowId || !desc.bareGiftStickerId) { diff --git a/Telegram/SourceFiles/settings/settings_credits.cpp b/Telegram/SourceFiles/settings/settings_credits.cpp index 688cca122e..ff144bfa24 100644 --- a/Telegram/SourceFiles/settings/settings_credits.cpp +++ b/Telegram/SourceFiles/settings/settings_credits.cpp @@ -380,9 +380,10 @@ void Credits::setupHistory(not_null container) { const auto apiLifetime = content->lifetime().make_state(); { using Api = Api::CreditsHistory; - const auto apiFull = apiLifetime->make_state(self, true, true); - const auto apiIn = apiLifetime->make_state(self, true, false); - const auto apiOut = apiLifetime->make_state(self, false, true); + const auto c = (_creditsType == CreditsType::Ton); + const auto apiFull = apiLifetime->make_state(self, true, true, c); + const auto apiIn = apiLifetime->make_state(self, true, false, c); + const auto apiOut = apiLifetime->make_state(self, false, true, c); apiFull->request({}, [=](Data::CreditsStatusSlice fullSlice) { apiIn->request({}, [=](Data::CreditsStatusSlice inSlice) { apiOut->request({}, [=](Data::CreditsStatusSlice outSlice) { diff --git a/Telegram/SourceFiles/settings/settings_credits_graphics.cpp b/Telegram/SourceFiles/settings/settings_credits_graphics.cpp index 03a71c8a26..fec6e3673c 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_format.h" #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. @@ -1429,6 +1430,7 @@ void GenericCreditsEntryBox( constexpr auto kMinus = QChar(0x2212); auto &lifetime = content->lifetime(); const auto text = lifetime.make_state(); + auto minorText = (Ui::Text::String*)(nullptr); const auto roundedText = e.refunded ? tr::lng_channel_earn_history_return(tr::now) : e.pending @@ -1466,13 +1468,13 @@ void GenericCreditsEntryBox( Ui::Text::WithEntities), kMarkupTextOptions, context); - } else { + } else if (e.credits.stars()) { auto t = TextWithEntities() .append((e.in && (creditsHistoryStarGift || !isStarGift)) - ? u"+"_q + ? QChar('+') : (e.gift && !creditsHistoryStarGift) - ? QString() - : QString(kMinus)) + ? QChar() + : kMinus) .append(Lang::FormatCreditsAmountDecimal(e.credits.abs())) .append(QChar(' ')) .append(owner->customEmojiManager().creditsEmoji()); @@ -1481,6 +1483,35 @@ void GenericCreditsEntryBox( std::move(t), kMarkupTextOptions, context); + } else if (e.credits.ton()) { + auto t = TextWithEntities() + .append((e.in ? QChar('+') : kMinus)) + .append(Info::ChannelEarn::MajorPart(e.credits.abs())); + text->setMarkedText( + st::channelEarnHistoryMajorLabel.style, + std::move(t), + kMarkupTextOptions, + context); + + auto minor = TextWithEntities() + .append(Info::ChannelEarn::MinorPart(e.credits.abs())) + .append(QChar(' ')) + .append( + Ui::Text::SingleCustomEmoji( + owner->customEmojiManager().registerInternalEmoji( + Ui::Earn::IconCurrencyColored( + st::tonFieldIconSize, + (e.in + ? st::boxTextFgGood->c + : st::menuIconAttentionColor->c)), + QMargins(0, st::lineWidth * 1, 0, 0), + false))); + minorText = lifetime.make_state(); + minorText->setMarkedText( + st::channelEarnHistoryMinorLabel.style, + std::move(minor), + kMarkupTextOptions, + context); } const auto font = text->style()->font; const auto roundedFont = st::defaultTextStyle.font; @@ -1490,7 +1521,9 @@ void GenericCreditsEntryBox( + roundedSkip + roundedFont->height : 0; - const auto fullWidth = text->maxWidth() + roundedWidth; + const auto fullWidth = text->maxWidth() + + roundedWidth + + (minorText ? minorText->maxWidth() : 0); amount->paintRequest( ) | rpl::start_with_next([=] { auto p = Painter(amount); @@ -1506,13 +1539,21 @@ void GenericCreditsEntryBox( ? st::windowBoldFg : st::menuIconAttentionColor); const auto x = (amount->width() - fullWidth) / 2; + const auto y = (amount->height() - font->height) / 2; text->draw(p, Ui::Text::PaintContext{ - .position = QPoint( - x, - (amount->height() - font->height) / 2), + .position = QPoint(x, y), .outerWidth = amount->width(), .availableWidth = amount->width(), }); + if (minorText) { + minorText->draw(p, Ui::Text::PaintContext{ + .position = QPoint( + x + text->maxWidth(), + y + st::lineWidth * 2), + .outerWidth = amount->width(), + .availableWidth = amount->width(), + }); + } if (rounded) { const auto roundedLeft = fullWidth @@ -1620,7 +1661,7 @@ void GenericCreditsEntryBox( about->setTextColorOverride(st::menuIconAttentionColor->c); } } else if (isStarGift) { - } else if (e.gift || isPrize) { + } else if ((e.gift || isPrize) && e.credits.stars()) { Ui::AddSkip(content); auto link = tr::lng_credits_box_history_entry_gift_about_link( lt_emoji, @@ -1805,7 +1846,7 @@ void GenericCreditsEntryBox( Ui::AddSkip(content); - if (!isStarGift) { + if (!isStarGift && e.credits.stars()) { box->addRow(object_ptr>( box, object_ptr( diff --git a/Telegram/SourceFiles/ui/effects/credits.style b/Telegram/SourceFiles/ui/effects/credits.style index 01946d7edd..3c865fead6 100644 --- a/Telegram/SourceFiles/ui/effects/credits.style +++ b/Telegram/SourceFiles/ui/effects/credits.style @@ -309,6 +309,7 @@ creditsHistoryTabsSlider: SettingsSlider(defaultTabsSlider) { creditsHistoryTabsSliderPadding: margins(14px, 0px, 24px, 0px); creditsHistoryRowDescriptionSkip: 20px; creditsHistoryRowRightTop: 16px; +creditsHistoryRowRightMinorTop: 18px; creditsHistoryRowRightStyle: TextStyle(defaultTextStyle) { font: font(fsize); }