From 9c52f245ac8075dbadf8506e39e9379974031a46 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 26 Mar 2024 05:49:31 +0300 Subject: [PATCH] Added initial support of statistical charts in earn channel section. --- .../icons/statistics/mini_currency_graph.png | Bin 0 -> 388 bytes .../statistics/mini_currency_graph@2x.png | Bin 0 -> 609 bytes .../statistics/mini_currency_graph@3x.png | Bin 0 -> 893 bytes Telegram/Resources/langs/lang.strings | 2 ++ .../SourceFiles/data/data_statistics_chart.h | 1 + .../earn/info_earn_inner_widget.cpp | 28 ++++++++++++++++++ .../statistics/chart_rulers_data.cpp | 14 +++++++-- .../statistics/chart_rulers_data.h | 3 +- .../SourceFiles/statistics/statistics.style | 2 ++ .../statistics_data_deserialize.cpp | 9 +++++- .../statistics/view/chart_rulers_view.cpp | 15 ++++++++-- .../statistics/view/chart_rulers_view.h | 1 + .../widgets/point_details_widget.cpp | 16 ++++++++-- .../statistics/widgets/point_details_widget.h | 1 + 14 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 Telegram/Resources/icons/statistics/mini_currency_graph.png create mode 100644 Telegram/Resources/icons/statistics/mini_currency_graph@2x.png create mode 100644 Telegram/Resources/icons/statistics/mini_currency_graph@3x.png diff --git a/Telegram/Resources/icons/statistics/mini_currency_graph.png b/Telegram/Resources/icons/statistics/mini_currency_graph.png new file mode 100644 index 0000000000000000000000000000000000000000..cce4957b3b1ff168be1dc003ef7280fbafeafc5c GIT binary patch literal 388 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uuz(rC1}QWNE&K$e-g&w>hG>Z1 z?YHN12@p7T|DBl-ORJl@(?bTX)+3B1GEO0z%ma4zxGcIF7^v|_Q-x1)QQ(HjTPsz* zG0dKu^y%#T+4}i%PWIvEHG;mEcdc9hy6dFgu|l0Lro+ksp;L8E2Z}Tn%BYOg(tGT%D^|aKTxwBIv=7?Equc&|3A>?G}lZTT)A>iri=d#Wz Gp$P!a28JpC literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/statistics/mini_currency_graph@2x.png b/Telegram/Resources/icons/statistics/mini_currency_graph@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b48cf0572e41540bc1b6fdc973a96ebf7ef9ff4d GIT binary patch literal 609 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`xTHpSruq6Z zXaU(A42G$TlC0TWzSVF5FO4N|zKE$K5**=bJ~#}E(R zx1qW#4mrq#|Itv2ViojdYgU`>X)yCjl8{)8fKJ$I$L@n>CLC(r8NxkHCzm|va%A%l zG}NqHxbJ>-bHog(zJL7f{<~*WjM^?X*579|E^a@VU@+J3_S>>&tEBea zfB(0x-fsTt=@J|@s-9JuGQa=c%i6l?t%QJw-*Vx9GRGa=vTnUKTedT%kHuE5UxCA6 z-g))T{i{zuHDY@8)ynnPFIkZrIcAP}Q@up~<-IDfxc2(%*Q&3-|El=Q3h=ed_a86X z`R7mTU&&84{k5f5bH6h#zR0n~N6qj)jO8;&MYp&YZiPo^~WRM_ZgpKNei*r2+qeHL@lMv0hZmsj5WQ>-$T zVfyKQ{RKE!^roLq z+kCL{%HtK+wnXW^*&d;Dit%oq_~jbA^=YqM_QdJm+Iu&z@K-_Qfm*ZKkvd{?%X}iY zMzy|wS+eVHo`z5-%j9M9CA;sgzRGn?VsG60V&!lD8&)3Q+mipkz`{hT_wmOQPb3yx fWL(U=hOdVC{&mAhHf^=1py>5<^>bP0l+XkKeKYh* literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/statistics/mini_currency_graph@3x.png b/Telegram/Resources/icons/statistics/mini_currency_graph@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..14cdf7ca3d2872946ffefcd8c3ce62c5185da8d6 GIT binary patch literal 893 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@HeP1VMAt!4KlM~~@ zh5(0&Oj40wzI`iG5HOJ7+2moi;9|y{xpQS@Wj7`ot)4x5cKoWi*RNl{nPT;A)9I&5 z4!iHZE89IOVXmLKv2pX#u+?8bepKwd`18|-t%kM#5hZeij1@p zl9QVa4BQgety{;Ty!wJhTYcS~+La+p*PlOse(BOAm8fkCrcM=I_3Yup#G<00NiL1W zxw*YB)~#Q!diVxw=GI%wm#ZgzsIZyD(>3>J%{+-(yZZ$OyB7p#xNHO(aCl>K)tx-^ ziFS5&eA(wTPGp#btqwi1NO4V=wtv++^{VRX*{7d6q;J1%$Wc+jSzJ(HU~kWFJ=N=B z>=XVe?zeKb#jS6zuC1+I%J%MfPf$i;;=-FbN=xF_&p!LiZeDJICQBF4k`gOk=FGD4 z@|#PxNOd30uyb8}F>38G;YB>j8zWxy9n3SD`Rm{$E=w6cAFdO@defILTlOmE) zVDH|&szRI+n>TO%{rh*$s|uTS7ysPVP@d?q{BmOR@%UDQ_wV2D$bU0Mpr@lv^pFJ` z+x5G5@BTPxq5S>ZH;tPgKWQkb;>ubel?M{EYjA!zQ zKYs6cFkynrskF`ZTu#-CPj2kGF8}w(@0b`KiEBrbCT1P_nG&!t#mMvD;ls{)dRrap z_Rkl(fB*h>Zm#yjH34To^w@dDuM9b{ zAYR6-;7NetL?$g6Wp;1H>dMMU#@1aIjvjTLvzR@5*NzC>C>n0 z+-Z4KFVdQ&MBb@0E8lg0ssI2 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index eaffa6251..45a22a71b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4988,6 +4988,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_channel_earn_learn_coin_title" = "What is {emoji} TON?"; "lng_channel_earn_learn_coin_about" = "TON is a blockchain platform and cryptocurrency that Telegram uses for its record scalability and ultra low commissions on transactions. {link}"; "lng_channel_earn_learn_close" = "Got it"; +"lng_channel_earn_chart_top_hours" = "Ad impressions"; +"lng_channel_earn_chart_revenue" = "Ad revenue"; "lng_contact_add" = "Add"; "lng_contact_send_message" = "message"; diff --git a/Telegram/SourceFiles/data/data_statistics_chart.h b/Telegram/SourceFiles/data/data_statistics_chart.h index d7448e273..48309e49f 100644 --- a/Telegram/SourceFiles/data/data_statistics_chart.h +++ b/Telegram/SourceFiles/data/data_statistics_chart.h @@ -65,6 +65,7 @@ struct StatisticalChart { bool isFooterHidden = false; bool hasPercentages = false; bool weekFormat = false; + bool isCurrency = false; // View data. int dayStringMaxWidth = 0; diff --git a/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp b/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp index 0a7714baf..95dc86e4e 100644 --- a/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp +++ b/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/statistics/info_statistics_inner_widget.h" // FillLoading. #include "lang/lang_keys.h" #include "main/main_session.h" +#include "statistics/chart_widget.h" #include "ui/controls/userpic_button.h" #include "ui/effects/animation_value_f.h" #include "ui/layers/generic_box.h" @@ -223,6 +224,33 @@ void InnerWidget::fill() { makeContext(label)); }; + { + using Type = Statistic::ChartViewType; + Ui::AddSkip(container); + Ui::AddSkip(container); + if (data.topHoursGraph.chart) { + const auto widget = container->add( + object_ptr(container), + st::statisticsLayerMargins); + + widget->setChartData(data.topHoursGraph.chart, Type::Linear); + widget->setTitle(tr::lng_channel_earn_chart_top_hours()); + } + if (data.revenueGraph.chart) { + Ui::AddSkip(container); + Ui::AddDivider(container); + Ui::AddSkip(container); + Ui::AddSkip(container); + const auto widget = container->add( + object_ptr(container), + st::statisticsLayerMargins); + + widget->setChartData(data.revenueGraph.chart, Type::StackBar); + widget->setTitle(tr::lng_channel_earn_chart_revenue()); + } + Ui::AddSkip(container); + } + const auto arrow = Ui::Text::SingleCustomEmoji( session->data().customEmojiManager().registerInternalEmoji( st::topicButtonArrow, diff --git a/Telegram/SourceFiles/statistics/chart_rulers_data.cpp b/Telegram/SourceFiles/statistics/chart_rulers_data.cpp index f4a8f9edd..99aec0198 100644 --- a/Telegram/SourceFiles/statistics/chart_rulers_data.cpp +++ b/Telegram/SourceFiles/statistics/chart_rulers_data.cpp @@ -28,13 +28,21 @@ constexpr auto kStep = 5.; : QString::number(absoluteValue); } +[[nodiscard]] QString FormatF(float64 absoluteValue) { + constexpr auto kTooMuch = int(10'000); + return (absoluteValue >= kTooMuch) + ? Lang::FormatCountToShort(absoluteValue).string + : QString::number(absoluteValue); +} + } // namespace ChartRulersData::ChartRulersData( int newMaxHeight, int newMinHeight, bool useMinHeight, - float64 rightRatio) { + float64 rightRatio, + int valueDivider) { if (!useMinHeight) { const auto v = (newMaxHeight > 100) ? Round(newMaxHeight) @@ -92,7 +100,9 @@ ChartRulersData::ChartRulersData( const auto value = int(i * step); line.absoluteValue = newMinHeight + value; line.relativeValue = 1. - value / float64(diffAbsoluteValue); - line.caption = Format(line.absoluteValue); + line.caption = valueDivider + ? FormatF(line.absoluteValue / float64(valueDivider)) + : Format(line.absoluteValue); if (rightRatio > 0) { const auto v = (newMinHeight + i * step) / rightRatio; line.scaledLineCaption = (!skipFloatValues) diff --git a/Telegram/SourceFiles/statistics/chart_rulers_data.h b/Telegram/SourceFiles/statistics/chart_rulers_data.h index 41833ce67..4df719ac0 100644 --- a/Telegram/SourceFiles/statistics/chart_rulers_data.h +++ b/Telegram/SourceFiles/statistics/chart_rulers_data.h @@ -15,7 +15,8 @@ public: int newMaxHeight, int newMinHeight, bool useMinHeight, - float64 rightRatio); + float64 rightRatio, + int valueDivider); void computeRelative( int newMaxHeight, diff --git a/Telegram/SourceFiles/statistics/statistics.style b/Telegram/SourceFiles/statistics/statistics.style index 395d458ca..3ba67a2c8 100644 --- a/Telegram/SourceFiles/statistics/statistics.style +++ b/Telegram/SourceFiles/statistics/statistics.style @@ -172,3 +172,5 @@ boostsListGiftMiniIcon: icon{{ "boosts/mini_gift", historyPeer8UserpicBg2 }}; boostsListGiveawayMiniIcon: icon{{ "boosts/mini_giveaway", historyPeer4UserpicBg2 }}; boostsListUnclaimedIcon: icon{{ "boosts/boost_unknown", premiumButtonFg }}; boostsListUnknownIcon: icon{{ "boosts/boost_unclaimed", premiumButtonFg }}; + +statisticsCurrencyIcon: icon {{ "statistics/mini_currency_graph", windowSubTextFg }}; diff --git a/Telegram/SourceFiles/statistics/statistics_data_deserialize.cpp b/Telegram/SourceFiles/statistics/statistics_data_deserialize.cpp index 355c500c1..df7345d75 100644 --- a/Telegram/SourceFiles/statistics/statistics_data_deserialize.cpp +++ b/Telegram/SourceFiles/statistics/statistics_data_deserialize.cpp @@ -61,7 +61,7 @@ Data::StatisticalChart StatisticalChartFromJSON(const QByteArray &json) { line.isHiddenOnStart = ranges::contains(hiddenLines, columnId); line.y.resize(length); for (auto i = 0; i < length; i++) { - const auto value = array.at(i + 1).toInt(); + const auto value = array.at(i + 1).toInteger(); line.y[i] = value; if (value > line.maxValue) { line.maxValue = value; @@ -132,6 +132,13 @@ Data::StatisticalChart StatisticalChartFromJSON(const QByteArray &json) { result.weekFormat = tooltipFormat.contains(u"'week'"_q); } } + { + const auto tickFormatIt = root.constFind(u"yTickFormatter"_q); + if (tickFormatIt != root.constEnd()) { + const auto tickFormat = tickFormatIt->toString(); + result.isCurrency = tickFormat.contains(u"TON"_q); + } + } const auto colors = root.value(u"colors"_q).toObject(); const auto names = root.value(u"names"_q).toObject(); diff --git a/Telegram/SourceFiles/statistics/view/chart_rulers_view.cpp b/Telegram/SourceFiles/statistics/view/chart_rulers_view.cpp index b77211168..89864dec9 100644 --- a/Telegram/SourceFiles/statistics/view/chart_rulers_view.cpp +++ b/Telegram/SourceFiles/statistics/view/chart_rulers_view.cpp @@ -23,6 +23,9 @@ void ChartRulersView::setChartData( std::shared_ptr linesFilter) { _rulers.clear(); _isDouble = (type == ChartViewType::DoubleLinear); + _currencyIcon = chartData.isCurrency + ? &st::statisticsCurrencyIcon + : nullptr; if (_isDouble && (chartData.lines.size() == 2)) { _linesFilter = std::move(linesFilter); _leftPen = QPen(chartData.lines.front().color); @@ -69,6 +72,7 @@ void ChartRulersView::paintCaptionsToRulers( for (auto &ruler : _rulers) { const auto rulerAlpha = alpha * ruler.alpha; p.setOpacity(rulerAlpha); + const auto left = _currencyIcon ? _currencyIcon->width() : 0; for (const auto &line : ruler.lines) { const auto y = offset + r.height() * line.relativeValue; const auto hasLinesFilter = _isDouble && _linesFilter; @@ -78,8 +82,14 @@ void ChartRulersView::paintCaptionsToRulers( } else { p.setPen(st::windowSubTextFg); } + if (_currencyIcon) { + const auto iconTop = y + - _currencyIcon->height() + + st::statisticsChartRulerCaptionSkip; + _currencyIcon->paint(p, 0, iconTop, r.width()); + } p.drawText( - 0, + left, y, (!_isDouble) ? line.caption @@ -131,7 +141,8 @@ void ChartRulersView::add(Limits newHeight, bool animated) { newHeight.max, newHeight.min, true, - _isDouble ? _scaledLineRatio : 0.); + _isDouble ? _scaledLineRatio : 0., + _currencyIcon ? 1000000000 : 0); if (_isDouble) { const auto &font = st::statisticsDetailsBottomCaptionStyle.font; for (auto &line : newLinesData.lines) { diff --git a/Telegram/SourceFiles/statistics/view/chart_rulers_view.h b/Telegram/SourceFiles/statistics/view/chart_rulers_view.h index 7fdfb7d29..ba355fd50 100644 --- a/Telegram/SourceFiles/statistics/view/chart_rulers_view.h +++ b/Telegram/SourceFiles/statistics/view/chart_rulers_view.h @@ -42,6 +42,7 @@ private: QPen _rightPen; int _leftLineId = 0; int _rightLineId = 0; + const style::icon *_currencyIcon = nullptr; std::vector _rulers; diff --git a/Telegram/SourceFiles/statistics/widgets/point_details_widget.cpp b/Telegram/SourceFiles/statistics/widgets/point_details_widget.cpp index c820aab97..e31ecb99c 100644 --- a/Telegram/SourceFiles/statistics/widgets/point_details_widget.cpp +++ b/Telegram/SourceFiles/statistics/widgets/point_details_widget.cpp @@ -132,7 +132,8 @@ PointDetailsWidget::PointDetailsWidget( , _zoomEnabled(zoomEnabled) , _chartData(chartData) , _textStyle(st::statisticsDetailsPopupStyle) -, _headerStyle(st::statisticsDetailsPopupHeaderStyle) { +, _headerStyle(st::statisticsDetailsPopupHeaderStyle) +, _valueIcon(chartData.isCurrency ? &st::statisticsCurrencyIcon : nullptr) { if (zoomEnabled) { rpl::single(rpl::empty_value()) | rpl::then( @@ -201,6 +202,7 @@ PointDetailsWidget::PointDetailsWidget( + rect::m::sum::h(st::statisticsDetailsPopupPadding) + st::statisticsDetailsPopupPadding.left() // Between strings. + maxNameTextWidth + + (_valueIcon ? _valueIcon->width() : 0) + _maxPercentageWidth; }(); sizeValue( @@ -278,7 +280,9 @@ void PointDetailsWidget::setXIndex(int xIndex) { textLine.name.setText(_textStyle, dataLine.name); textLine.value.setText( _textStyle, - QString("%L1").arg(dataLine.y[xIndex])); + _chartData.isCurrency + ? QString::number(dataLine.y[xIndex] / float64(1000000000)) + : QString("%L1").arg(dataLine.y[xIndex])); hasPositiveValues |= (dataLine.y[xIndex] > 0); textLine.valueColor = QColor(dataLine.color); _lines.push_back(std::move(textLine)); @@ -381,6 +385,14 @@ void PointDetailsWidget::paintEvent(QPaintEvent *e) { .outerWidth = _textRect.width(), .availableWidth = valueWidth, }; + if (_valueIcon) { + _valueIcon->paint( + p, + valueContext.position.x() - _valueIcon->width(), + lineY, + valueContext.outerWidth, + line.valueColor); + } const auto nameContext = Ui::Text::PaintContext{ .position = QPoint( _textRect.x() + _maxPercentageWidth, diff --git a/Telegram/SourceFiles/statistics/widgets/point_details_widget.h b/Telegram/SourceFiles/statistics/widgets/point_details_widget.h index 52ed7dd34..a1e3dcccc 100644 --- a/Telegram/SourceFiles/statistics/widgets/point_details_widget.h +++ b/Telegram/SourceFiles/statistics/widgets/point_details_widget.h @@ -47,6 +47,7 @@ private: const style::TextStyle &_textStyle; const style::TextStyle &_headerStyle; Ui::Text::String _header; + const style::icon *_valueIcon = nullptr; void invalidateCache();