mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Added all chart widgets for statistic of channels.
This commit is contained in:
parent
a3fd4f3fac
commit
df53ddf837
4 changed files with 181 additions and 106 deletions
|
@ -871,14 +871,17 @@ int ChartWidget::resizeGetHeight(int newWidth) {
|
||||||
|
|
||||||
_filterButtons->fillButtons(texts, colors, ids, newWidth);
|
_filterButtons->fillButtons(texts, colors, ids, newWidth);
|
||||||
}
|
}
|
||||||
|
const auto filtersTopSkip = st::statisticsFilterButtonsPadding.top();
|
||||||
const auto filtersHeight = _filterButtons
|
const auto filtersHeight = _filterButtons
|
||||||
? _filterButtons->height()
|
? (_filterButtons->height()
|
||||||
|
+ st::statisticsFilterButtonsPadding.bottom())
|
||||||
: 0;
|
: 0;
|
||||||
const auto resultHeight = st::statisticsChartHeight
|
const auto resultHeight = st::statisticsChartHeaderHeight
|
||||||
|
+ st::statisticsChartHeight
|
||||||
+ st::statisticsChartFooterHeight
|
+ st::statisticsChartFooterHeight
|
||||||
+ st::statisticsChartFooterSkip
|
+ st::statisticsChartFooterSkip
|
||||||
+ filtersHeight
|
+ filtersTopSkip
|
||||||
+ st::statisticsChartHeaderHeight;
|
+ filtersHeight;
|
||||||
{
|
{
|
||||||
_header->setGeometry(
|
_header->setGeometry(
|
||||||
0,
|
0,
|
||||||
|
@ -887,7 +890,10 @@ int ChartWidget::resizeGetHeight(int newWidth) {
|
||||||
st::statisticsChartHeaderHeight);
|
st::statisticsChartHeaderHeight);
|
||||||
_footer->setGeometry(
|
_footer->setGeometry(
|
||||||
0,
|
0,
|
||||||
resultHeight - st::statisticsChartFooterHeight - filtersHeight,
|
resultHeight
|
||||||
|
- st::statisticsChartFooterHeight
|
||||||
|
- filtersTopSkip
|
||||||
|
- filtersHeight,
|
||||||
newWidth,
|
newWidth,
|
||||||
st::statisticsChartFooterHeight);
|
st::statisticsChartFooterHeight);
|
||||||
if (_filterButtons) {
|
if (_filterButtons) {
|
||||||
|
@ -899,6 +905,7 @@ int ChartWidget::resizeGetHeight(int newWidth) {
|
||||||
newWidth,
|
newWidth,
|
||||||
resultHeight
|
resultHeight
|
||||||
- st::statisticsChartFooterHeight
|
- st::statisticsChartFooterHeight
|
||||||
|
- filtersTopSkip
|
||||||
- filtersHeight
|
- filtersHeight
|
||||||
- st::statisticsChartFooterSkip);
|
- st::statisticsChartFooterSkip);
|
||||||
|
|
||||||
|
@ -1403,6 +1410,16 @@ void ChartWidget::setupFilterButtons() {
|
||||||
void ChartWidget::setChartData(
|
void ChartWidget::setChartData(
|
||||||
Data::StatisticalChart chartData,
|
Data::StatisticalChart chartData,
|
||||||
ChartViewType type) {
|
ChartViewType type) {
|
||||||
|
if (width() < st::statisticsChartHeight) {
|
||||||
|
sizeValue(
|
||||||
|
) | rpl::start_with_next([=](const QSize &s) {
|
||||||
|
if (s.width() > st::statisticsChartHeight) {
|
||||||
|
setChartData(chartData, type);
|
||||||
|
_waitingSizeLifetime.destroy();
|
||||||
|
}
|
||||||
|
}, _waitingSizeLifetime);
|
||||||
|
return;
|
||||||
|
}
|
||||||
_chartData = std::move(chartData);
|
_chartData = std::move(chartData);
|
||||||
FillLineColorsByKey(_chartData);
|
FillLineColorsByKey(_chartData);
|
||||||
|
|
||||||
|
|
|
@ -175,6 +175,8 @@ private:
|
||||||
bool _zoomEnabled = false;
|
bool _zoomEnabled = false;
|
||||||
rpl::event_stream<float64> _zoomRequests;
|
rpl::event_stream<float64> _zoomRequests;
|
||||||
|
|
||||||
|
rpl::lifetime _waitingSizeLifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Statistic
|
} // namespace Statistic
|
||||||
|
|
|
@ -12,6 +12,8 @@ using "ui/widgets/widgets.style";
|
||||||
|
|
||||||
statisticsChartHeight: 150px;
|
statisticsChartHeight: 150px;
|
||||||
|
|
||||||
|
statisticsChartEntryPadding: margins(0px, 8px, 0px, 8px);
|
||||||
|
|
||||||
statisticsDetailsArrowShift: 2px;
|
statisticsDetailsArrowShift: 2px;
|
||||||
statisticsDetailsArrowStroke: 1.5;
|
statisticsDetailsArrowStroke: 1.5;
|
||||||
statisticsDetailsPopupMargins: margins(8px, 8px, 8px, 8px);
|
statisticsDetailsPopupMargins: margins(8px, 8px, 8px, 8px);
|
||||||
|
@ -37,6 +39,8 @@ statisticsChartFooterHeight: 52px;
|
||||||
statisticsChartFlatCheckboxMargins: margins(4px, 4px, 4px, 4px);
|
statisticsChartFlatCheckboxMargins: margins(4px, 4px, 4px, 4px);
|
||||||
statisticsChartFlatCheckboxCheckWidth: 4px;
|
statisticsChartFlatCheckboxCheckWidth: 4px;
|
||||||
|
|
||||||
|
statisticsFilterButtonsPadding: margins(0px, 6px, 0px, 0px);
|
||||||
|
|
||||||
statisticsDetailsPopupHeaderStyle: TextStyle(defaultTextStyle) {
|
statisticsDetailsPopupHeaderStyle: TextStyle(defaultTextStyle) {
|
||||||
font: font(9px semibold);
|
font: font(9px semibold);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,149 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
struct Descriptor final {
|
||||||
|
not_null<PeerData*> peer;
|
||||||
|
not_null<Api::Statistics*> api;
|
||||||
|
not_null<QWidget*> toastParent;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ProcessZoom(
|
||||||
|
const Descriptor &d,
|
||||||
|
not_null<Statistic::ChartWidget*> widget,
|
||||||
|
const QString &zoomToken,
|
||||||
|
Statistic::ChartViewType type) {
|
||||||
|
if (zoomToken.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
widget->zoomRequests(
|
||||||
|
) | rpl::start_with_next([=](float64 x) {
|
||||||
|
d.api->requestZoom(
|
||||||
|
d.peer,
|
||||||
|
zoomToken,
|
||||||
|
x
|
||||||
|
) | rpl::start_with_next_error_done([=](
|
||||||
|
const Data::StatisticalGraph &graph) {
|
||||||
|
if (graph.chart) {
|
||||||
|
widget->setZoomedChartData(graph.chart, x, type);
|
||||||
|
} else if (!graph.error.isEmpty()) {
|
||||||
|
Ui::Toast::Show(d.toastParent, graph.error);
|
||||||
|
}
|
||||||
|
}, [=](const QString &error) {
|
||||||
|
}, [=] {
|
||||||
|
}, widget->lifetime());
|
||||||
|
}, widget->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessChart(
|
||||||
|
const Descriptor &d,
|
||||||
|
not_null<Statistic::ChartWidget*> widget,
|
||||||
|
const Data::StatisticalGraph &graphData,
|
||||||
|
rpl::producer<QString> &&title,
|
||||||
|
Statistic::ChartViewType type) {
|
||||||
|
if (graphData.chart) {
|
||||||
|
widget->setChartData(graphData.chart, type);
|
||||||
|
ProcessZoom(d, widget, graphData.zoomToken, type);
|
||||||
|
widget->setTitle(std::move(title));
|
||||||
|
} else if (!graphData.zoomToken.isEmpty()) {
|
||||||
|
d.api->requestZoom(
|
||||||
|
d.peer,
|
||||||
|
graphData.zoomToken,
|
||||||
|
0
|
||||||
|
) | rpl::start_with_next_error_done([=](
|
||||||
|
const Data::StatisticalGraph &graph) {
|
||||||
|
if (graph.chart) {
|
||||||
|
widget->setChartData(graph.chart, type);
|
||||||
|
ProcessZoom(d, widget, graph.zoomToken, type);
|
||||||
|
widget->setTitle(rpl::duplicate(title));
|
||||||
|
} else if (!graph.error.isEmpty()) {
|
||||||
|
Ui::Toast::Show(d.toastParent, graph.error);
|
||||||
|
}
|
||||||
|
}, [=](const QString &error) {
|
||||||
|
}, [=] {
|
||||||
|
}, widget->lifetime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillChannelStatistic(
|
||||||
|
not_null<Ui::GenericBox*> box,
|
||||||
|
const Descriptor &descriptor,
|
||||||
|
const Data::ChannelStatistics &stats) {
|
||||||
|
using Type = Statistic::ChartViewType;
|
||||||
|
const auto &padding = st::statisticsChartEntryPadding;
|
||||||
|
const auto addSkip = [&] {
|
||||||
|
Settings::AddSkip(box->verticalLayout(), padding.bottom());
|
||||||
|
Settings::AddDivider(box->verticalLayout());
|
||||||
|
Settings::AddSkip(box->verticalLayout(), padding.top());
|
||||||
|
};
|
||||||
|
Settings::AddSkip(box->verticalLayout(), padding.top());
|
||||||
|
ProcessChart(
|
||||||
|
descriptor,
|
||||||
|
box->addRow(object_ptr<Statistic::ChartWidget>(box)),
|
||||||
|
stats.memberCountGraph,
|
||||||
|
tr::lng_chart_title_member_count(),
|
||||||
|
Type::Linear);
|
||||||
|
addSkip();
|
||||||
|
ProcessChart(
|
||||||
|
descriptor,
|
||||||
|
box->addRow(object_ptr<Statistic::ChartWidget>(box)),
|
||||||
|
stats.joinGraph,
|
||||||
|
tr::lng_chart_title_join(),
|
||||||
|
Type::Linear);
|
||||||
|
addSkip();
|
||||||
|
ProcessChart(
|
||||||
|
descriptor,
|
||||||
|
box->addRow(object_ptr<Statistic::ChartWidget>(box)),
|
||||||
|
stats.muteGraph,
|
||||||
|
tr::lng_chart_title_mute(),
|
||||||
|
Type::Linear);
|
||||||
|
addSkip();
|
||||||
|
ProcessChart(
|
||||||
|
descriptor,
|
||||||
|
box->addRow(object_ptr<Statistic::ChartWidget>(box)),
|
||||||
|
stats.viewCountByHourGraph,
|
||||||
|
tr::lng_chart_title_view_count_by_hour(),
|
||||||
|
Type::Linear);
|
||||||
|
addSkip();
|
||||||
|
ProcessChart(
|
||||||
|
descriptor,
|
||||||
|
box->addRow(object_ptr<Statistic::ChartWidget>(box)),
|
||||||
|
stats.viewCountBySourceGraph,
|
||||||
|
tr::lng_chart_title_view_count_by_source(),
|
||||||
|
Type::Stack);
|
||||||
|
addSkip();
|
||||||
|
ProcessChart(
|
||||||
|
descriptor,
|
||||||
|
box->addRow(object_ptr<Statistic::ChartWidget>(box)),
|
||||||
|
stats.joinBySourceGraph,
|
||||||
|
tr::lng_chart_title_join_by_source(),
|
||||||
|
Type::Stack);
|
||||||
|
addSkip();
|
||||||
|
ProcessChart(
|
||||||
|
descriptor,
|
||||||
|
box->addRow(object_ptr<Statistic::ChartWidget>(box)),
|
||||||
|
stats.languageGraph,
|
||||||
|
tr::lng_chart_title_language(),
|
||||||
|
Type::StackLinear);
|
||||||
|
addSkip();
|
||||||
|
ProcessChart(
|
||||||
|
descriptor,
|
||||||
|
box->addRow(object_ptr<Statistic::ChartWidget>(box)),
|
||||||
|
stats.messageInteractionGraph,
|
||||||
|
tr::lng_chart_title_message_interaction(),
|
||||||
|
Type::DoubleLinear);
|
||||||
|
addSkip();
|
||||||
|
ProcessChart(
|
||||||
|
descriptor,
|
||||||
|
box->addRow(object_ptr<Statistic::ChartWidget>(box)),
|
||||||
|
stats.instantViewInteractionGraph,
|
||||||
|
tr::lng_chart_title_instant_view_interaction(),
|
||||||
|
Type::DoubleLinear);
|
||||||
|
addSkip();
|
||||||
|
|
||||||
|
box->verticalLayout()->resizeToWidth(box->width());
|
||||||
|
box->showChildren();
|
||||||
|
}
|
||||||
|
|
||||||
void FillLoading(
|
void FillLoading(
|
||||||
not_null<Ui::GenericBox*> box,
|
not_null<Ui::GenericBox*> box,
|
||||||
rpl::producer<bool> toggleOn) {
|
rpl::producer<bool> toggleOn) {
|
||||||
|
@ -69,117 +212,26 @@ void FillLoading(
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void StatisticsBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
|
void StatisticsBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
|
||||||
|
box->setTitle(tr::lng_stats_title());
|
||||||
const auto loaded = box->lifetime().make_state<rpl::event_stream<bool>>();
|
const auto loaded = box->lifetime().make_state<rpl::event_stream<bool>>();
|
||||||
FillLoading(
|
FillLoading(
|
||||||
box,
|
box,
|
||||||
loaded->events_starting_with(false) | rpl::map(!rpl::mappers::_1));
|
loaded->events_starting_with(false) | rpl::map(!rpl::mappers::_1));
|
||||||
|
|
||||||
const auto chartWidget = box->addRow(
|
const auto descriptor = Descriptor{
|
||||||
object_ptr<Statistic::ChartWidget>(box));
|
peer,
|
||||||
const auto chartWidget2 = box->addRow(
|
box->lifetime().make_state<Api::Statistics>(&peer->session().api()),
|
||||||
object_ptr<Statistic::ChartWidget>(box));
|
box->uiShow()->toastParent(),
|
||||||
const auto chartWidget3 = box->addRow(
|
|
||||||
object_ptr<Statistic::ChartWidget>(box));
|
|
||||||
const auto chartWidget4 = box->addRow(
|
|
||||||
object_ptr<Statistic::ChartWidget>(box));
|
|
||||||
const auto chartWidget5 = box->addRow(
|
|
||||||
object_ptr<Statistic::ChartWidget>(box));
|
|
||||||
const auto api = chartWidget->lifetime().make_state<Api::Statistics>(
|
|
||||||
&peer->session().api());
|
|
||||||
|
|
||||||
const auto processZoom = [=](
|
|
||||||
not_null<Statistic::ChartWidget*> widget,
|
|
||||||
const QString &zoomToken,
|
|
||||||
Statistic::ChartViewType type) {
|
|
||||||
if (!zoomToken.isEmpty()) {
|
|
||||||
widget->zoomRequests(
|
|
||||||
) | rpl::start_with_next([=](float64 x) {
|
|
||||||
api->requestZoom(
|
|
||||||
peer,
|
|
||||||
zoomToken,
|
|
||||||
x
|
|
||||||
) | rpl::start_with_next_error_done([=](
|
|
||||||
const Data::StatisticalGraph &graph) {
|
|
||||||
if (graph.chart) {
|
|
||||||
widget->setZoomedChartData(graph.chart, x, type);
|
|
||||||
} else if (!graph.error.isEmpty()) {
|
|
||||||
Ui::Toast::Show(
|
|
||||||
box->uiShow()->toastParent(),
|
|
||||||
graph.error);
|
|
||||||
}
|
|
||||||
}, [=](const QString &error) {
|
|
||||||
}, [=] {
|
|
||||||
}, widget->lifetime());
|
|
||||||
}, widget->lifetime());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto processChart = [=](
|
descriptor.api->request(
|
||||||
not_null<Statistic::ChartWidget*> widget,
|
descriptor.peer
|
||||||
const Data::StatisticalGraph &graphData,
|
|
||||||
rpl::producer<QString> &&title,
|
|
||||||
Statistic::ChartViewType type) {
|
|
||||||
if (graphData.chart) {
|
|
||||||
widget->setChartData(graphData.chart, type);
|
|
||||||
processZoom(widget, graphData.zoomToken, type);
|
|
||||||
widget->setTitle(std::move(title));
|
|
||||||
} else if (!graphData.zoomToken.isEmpty()) {
|
|
||||||
api->requestZoom(
|
|
||||||
peer,
|
|
||||||
graphData.zoomToken,
|
|
||||||
0
|
|
||||||
) | rpl::start_with_next_error_done([=](
|
|
||||||
const Data::StatisticalGraph &graph) {
|
|
||||||
if (graph.chart) {
|
|
||||||
widget->setChartData(graph.chart, type);
|
|
||||||
processZoom(widget, graph.zoomToken, type);
|
|
||||||
widget->setTitle(rpl::duplicate(title));
|
|
||||||
} else if (!graph.error.isEmpty()) {
|
|
||||||
Ui::Toast::Show(
|
|
||||||
box->uiShow()->toastParent(),
|
|
||||||
graph.error);
|
|
||||||
}
|
|
||||||
}, [=](const QString &error) {
|
|
||||||
}, [=] {
|
|
||||||
}, widget->lifetime());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
api->request(
|
|
||||||
peer
|
|
||||||
) | rpl::start_with_done([=] {
|
) | rpl::start_with_done([=] {
|
||||||
const auto stats = api->channelStats();
|
const auto stats = descriptor.api->channelStats();
|
||||||
if (!stats) {
|
if (!stats) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
FillChannelStatistic(box, descriptor, stats);
|
||||||
loaded->fire(true);
|
loaded->fire(true);
|
||||||
using Type = Statistic::ChartViewType;
|
}, box->lifetime());
|
||||||
processChart(
|
|
||||||
chartWidget,
|
|
||||||
stats.memberCountGraph,
|
|
||||||
tr::lng_chart_title_member_count(),
|
|
||||||
Type::Linear);
|
|
||||||
processChart(
|
|
||||||
chartWidget2,
|
|
||||||
stats.joinGraph,
|
|
||||||
tr::lng_chart_title_join(),
|
|
||||||
Type::Linear);
|
|
||||||
processChart(
|
|
||||||
chartWidget3,
|
|
||||||
stats.muteGraph,
|
|
||||||
tr::lng_chart_title_mute(),
|
|
||||||
Type::Linear);
|
|
||||||
processChart(
|
|
||||||
chartWidget4,
|
|
||||||
stats.viewCountBySourceGraph,
|
|
||||||
tr::lng_chart_title_view_count_by_source(),
|
|
||||||
Type::Stack);
|
|
||||||
processChart(
|
|
||||||
chartWidget5,
|
|
||||||
stats.joinBySourceGraph,
|
|
||||||
tr::lng_chart_title_join_by_source(),
|
|
||||||
Type::Stack);
|
|
||||||
}, chartWidget->lifetime());
|
|
||||||
box->setTitle(tr::lng_stats_title());
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue