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 000000000..cce4957b3 Binary files /dev/null and b/Telegram/Resources/icons/statistics/mini_currency_graph.png differ 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 000000000..b48cf0572 Binary files /dev/null and b/Telegram/Resources/icons/statistics/mini_currency_graph@2x.png differ 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 000000000..14cdf7ca3 Binary files /dev/null and b/Telegram/Resources/icons/statistics/mini_currency_graph@3x.png differ 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();