Update channel statistics

This commit is contained in:
Ilya Laktyushin 2020-02-25 17:32:37 +04:00
parent 0b9a47aec0
commit 89738dbf28
12 changed files with 3365 additions and 3236 deletions

View File

@ -5363,3 +5363,6 @@ Any member of this group will be able to see messages in the channel.";
"Stats.FollowersTitle" = "FOLLOWERS";
"Stats.NotificationsTitle" = "NOTIFICATIONS";
"Stats.InteractionsTitle" = "INTERACTIONS";
"Stats.ViewsBySourceTitle" = "VIEWS BY SOURCE";
"Stats.FollowersBySourceTitle" = "FOLLOWERS BY SOURCE";
"Stats.LanguagesTitle" = "LANGUAGES";

View File

@ -39,12 +39,18 @@ class ChartStackSection: UIView, ColorModeContainer {
sectionContainerView.addSubview(rangeView)
sectionContainerView.addSubview(visibilityView)
sectionContainerView.addSubview(titleLabel)
sectionContainerView.addSubview(backButton)
titleLabel.font = UIFont.systemFont(ofSize: 14, weight: .bold)
titleLabel.textAlignment = .center
visibilityView.clipsToBounds = true
backButton.isExclusiveTouch = true
backButton.addTarget(self, action: #selector(self.didTapBackButton), for: .touchUpInside)
backButton.setTitle("Zoom Out", for: .normal)
backButton.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .regular)
backButton.setTitleColor(UIColor(rgb: 0x007ee5), for: .normal)
backButton.setVisible(false, animated: false)
}
@ -95,7 +101,7 @@ class ChartStackSection: UIView, ColorModeContainer {
self.titleLabel.setTextColor(colorMode.chartTitleColor, animated: animated && titleLabel.isVisibleInWindow)
}
@IBAction func didTapBackButton() {
@objc private func didTapBackButton() {
controller.didTapZoomOut()
}
@ -127,11 +133,12 @@ class ChartStackSection: UIView, ColorModeContainer {
super.layoutSubviews()
let bounds = self.bounds
self.titleLabel.frame = CGRect(origin: CGPoint(x: 0.0, y: 10.0), size: CGSize(width: bounds.width, height: 48.0))
self.sectionContainerView.frame = CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: 350.0))
self.titleLabel.frame = CGRect(origin: CGPoint(x: backButton.alpha > 0.0 ? 36.0 : 0.0, y: 5.0), size: CGSize(width: bounds.width, height: 28.0))
self.sectionContainerView.frame = CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: 400.0))
self.chartView.frame = CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: 250.0))
self.rangeView.frame = CGRect(origin: CGPoint(x: 0.0, y: 250.0), size: CGSize(width: bounds.width, height: 48.0))
self.visibilityView.frame = CGRect(origin: CGPoint(x: 0.0, y: 308.0), size: CGSize(width: bounds.width, height: 122.0))
self.rangeView.frame = CGRect(origin: CGPoint(x: 0.0, y: 250.0), size: CGSize(width: bounds.width, height: 42.0))
self.visibilityView.frame = CGRect(origin: CGPoint(x: 0.0, y: 308.0), size: CGSize(width: bounds.width, height: 222.0))
self.backButton.frame = CGRect(x: 0.0, y: 0.0, width: 96.0, height: 38.0)
}
func setup(controller: BaseChartController, title: String) {
@ -164,6 +171,7 @@ class ChartStackSection: UIView, ColorModeContainer {
self.titleLabel.setText(title, animated: animated)
}
controller.setBackButtonVisibilityClosure = { [unowned self] visible, animated in
self.setNeedsLayout()
self.setBackButtonVisible(visible, animated: animated)
}
controller.refreshChartToolsClosure = { [unowned self] animated in
@ -193,9 +201,9 @@ class ChartStackSection: UIView, ColorModeContainer {
controller.initializeChart()
updateToolViews(animated: false)
TimeInterval.animationDurationMultipler = 0.0001
rangeView.setRange(0.75...1.0, animated: false)
controller.updateChartRange(0.75...1.0)
rangeView.setRange(0.8...1.0, animated: false)
TimeInterval.animationDurationMultipler = 0.00001
controller.updateChartRange(0.8...1.0, animated: false)
TimeInterval.animationDurationMultipler = 1.0
}
}

View File

@ -4,6 +4,13 @@ import Display
import AsyncDisplayKit
import AppBundle
public enum ChartType {
case lines
case twoAxis
case pie
case bars
}
public final class ChartNode: ASDisplayNode {
private var chartView: ChartStackSection {
return self.view as! ChartStackSection
@ -23,21 +30,32 @@ public final class ChartNode: ASDisplayNode {
self.view.disablesInteractiveTransitionGestureRecognizer = true
}
@objc private func nop() {
}
public func setup(_ data: String, bar: Bool = false) {
var bar = bar
if data.contains("bar") {
bar = true
}
public func setup(_ data: String, type: ChartType, getDetailsData: @escaping (Date, (String?) -> Void) -> Void) {
if let data = data.data(using: .utf8) {
ChartsDataManager().readChart(data: data, extraCopiesCount: 0, sync: true, success: { [weak self] collection in
let controller: BaseChartController
if bar {
controller = TwoAxisLinesChartController(chartsCollection: collection)
} else {
controller = GeneralLinesChartController(chartsCollection: collection)
switch type {
case .lines:
controller = GeneralLinesChartController(chartsCollection: collection)
controller.getDetailsData = { date, completion in
getDetailsData(date, { detailsData in
if let detailsData = detailsData, let data = detailsData.data(using: .utf8) {
ChartsDataManager().readChart(data: data, extraCopiesCount: 0, sync: true, success: { [weak self] collection in
completion(collection)
}) { error in
completion(nil)
}
} else {
completion(nil)
}
})
}
case .twoAxis:
controller = TwoAxisLinesChartController(chartsCollection: collection)
case .pie:
controller = PercentPieChartController(chartsCollection: collection)
case .bars:
controller = StackedBarsChartController(chartsCollection: collection)
}
if let strongSelf = self {
strongSelf.chartView.setup(controller: controller, title: "")

View File

@ -13,6 +13,7 @@ import PresentationDataUtils
import AccountContext
import PresentationDataUtils
import AppBundle
import Charts
private final class StatsArguments {
init() {
@ -36,28 +37,28 @@ private enum StatsEntry: ItemListNodeEntry {
case overview(PresentationTheme, ChannelStats)
case growthTitle(PresentationTheme, String)
case growthGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
case growthGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph, ChartType)
case followersTitle(PresentationTheme, String)
case followersGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
case followersGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph, ChartType)
case notificationsTitle(PresentationTheme, String)
case notificationsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
case notificationsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph, ChartType)
case viewsByHourTitle(PresentationTheme, String)
case viewsByHourGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
case viewsByHourGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph, ChartType)
case postInteractionsTitle(PresentationTheme, String)
case postInteractionsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
case postInteractionsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph, ChartType)
case viewsBySourceTitle(PresentationTheme, String)
case viewsBySourceGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
case viewsBySourceGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph, ChartType)
case followersBySourceTitle(PresentationTheme, String)
case followersBySourceGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
case followersBySourceGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph, ChartType)
case languagesTitle(PresentationTheme, String)
case languagesGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
case languagesGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph, ChartType)
var section: ItemListSectionId {
switch self {
@ -143,8 +144,8 @@ private enum StatsEntry: ItemListNodeEntry {
} else {
return false
}
case let .growthGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
if case let .growthGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
case let .growthGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .growthGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
return true
} else {
return false
@ -155,8 +156,8 @@ private enum StatsEntry: ItemListNodeEntry {
} else {
return false
}
case let .followersGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
if case let .followersGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
case let .followersGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .followersGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
return true
} else {
return false
@ -167,8 +168,8 @@ private enum StatsEntry: ItemListNodeEntry {
} else {
return false
}
case let .notificationsGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
if case let .notificationsGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
case let .notificationsGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .notificationsGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
return true
} else {
return false
@ -179,8 +180,8 @@ private enum StatsEntry: ItemListNodeEntry {
} else {
return false
}
case let .viewsByHourGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
if case let .viewsByHourGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
case let .viewsByHourGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .viewsByHourGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
return true
} else {
return false
@ -191,8 +192,8 @@ private enum StatsEntry: ItemListNodeEntry {
} else {
return false
}
case let .postInteractionsGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
if case let .postInteractionsGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
case let .postInteractionsGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .postInteractionsGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
return true
} else {
return false
@ -203,8 +204,8 @@ private enum StatsEntry: ItemListNodeEntry {
} else {
return false
}
case let .viewsBySourceGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
if case let .viewsBySourceGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
case let .viewsBySourceGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .viewsBySourceGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
return true
} else {
return false
@ -215,8 +216,8 @@ private enum StatsEntry: ItemListNodeEntry {
} else {
return false
}
case let .followersBySourceGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
if case let .followersBySourceGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
case let .followersBySourceGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .followersBySourceGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
return true
} else {
return false
@ -227,8 +228,8 @@ private enum StatsEntry: ItemListNodeEntry {
} else {
return false
}
case let .languagesGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
if case let .languagesGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
case let .languagesGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph, lhsType):
if case let .languagesGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph, rhsType) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph, lhsType == rhsType {
return true
} else {
return false
@ -254,15 +255,22 @@ private enum StatsEntry: ItemListNodeEntry {
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .overview(theme, stats):
return StatsOverviewItem(presentationData: presentationData, stats: stats, sectionId: self.section, style: .blocks)
case let .growthGraph(theme, strings, dateTimeFormat, graph),
let .followersGraph(theme, strings, dateTimeFormat, graph),
let .notificationsGraph(theme, strings, dateTimeFormat, graph),
let .viewsByHourGraph(theme, strings, dateTimeFormat, graph),
let .postInteractionsGraph(theme, strings, dateTimeFormat, graph),
let .viewsBySourceGraph(theme, strings, dateTimeFormat, graph),
let .followersBySourceGraph(theme, strings, dateTimeFormat, graph),
let .languagesGraph(theme, strings, dateTimeFormat, graph):
return StatsGraphItem(presentationData: presentationData, graph: graph, sectionId: self.section, style: .blocks)
case let .growthGraph(theme, strings, dateTimeFormat, graph, type):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, sectionId: self.section, style: .blocks)
case let .followersGraph(theme, strings, dateTimeFormat, graph, type):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, sectionId: self.section, style: .blocks)
case let .notificationsGraph(theme, strings, dateTimeFormat, graph, type):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, sectionId: self.section, style: .blocks)
case let .viewsByHourGraph(theme, strings, dateTimeFormat, graph, type):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, sectionId: self.section, style: .blocks)
case let .postInteractionsGraph(theme, strings, dateTimeFormat, graph, type):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, sectionId: self.section, style: .blocks)
case let .viewsBySourceGraph(theme, strings, dateTimeFormat, graph, type):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, height: 160.0, sectionId: self.section, style: .blocks)
case let .followersBySourceGraph(theme, strings, dateTimeFormat, graph, type):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, height: 160.0, sectionId: self.section, style: .blocks)
case let .languagesGraph(theme, strings, dateTimeFormat, graph, type):
return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, height: 100.0, sectionId: self.section, style: .blocks)
}
}
}
@ -275,16 +283,25 @@ private func statsControllerEntries(data: ChannelStats?, presentationData: Prese
entries.append(.overview(presentationData.theme, data))
entries.append(.growthTitle(presentationData.theme, presentationData.strings.Stats_GrowthTitle))
entries.append(.growthGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.growthGraph))
entries.append(.growthGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.growthGraph, .lines))
entries.append(.followersTitle(presentationData.theme, presentationData.strings.Stats_FollowersTitle))
entries.append(.followersGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.followersGraph))
entries.append(.followersGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.followersGraph, .lines))
entries.append(.notificationsTitle(presentationData.theme, presentationData.strings.Stats_NotificationsTitle))
entries.append(.notificationsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.muteGraph))
entries.append(.notificationsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.muteGraph, .lines))
entries.append(.postInteractionsTitle(presentationData.theme, presentationData.strings.Stats_InteractionsTitle))
entries.append(.postInteractionsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.interactionsGraph))
entries.append(.postInteractionsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.interactionsGraph, .twoAxis))
entries.append(.viewsBySourceTitle(presentationData.theme, presentationData.strings.Stats_ViewsBySourceTitle))
entries.append(.viewsBySourceGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.viewsBySourceGraph, .bars))
entries.append(.followersBySourceTitle(presentationData.theme, presentationData.strings.Stats_FollowersBySourceTitle))
entries.append(.followersBySourceGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.newFollowersBySourceGraph, .bars))
entries.append(.languagesTitle(presentationData.theme, presentationData.strings.Stats_LanguagesTitle))
entries.append(.languagesGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.languagesGraph, .pie))
}
return entries
@ -306,7 +323,7 @@ public func channelStatsController(context: AccountContext, peer: Peer, cachedPe
datacenterId = cachedData.statsDatacenterId
}
let statsContext = ChannelStatsContext(network: context.account.network, datacenterId: datacenterId, peer: peer)
let statsContext = ChannelStatsContext(postbox: context.account.postbox, network: context.account.network, datacenterId: datacenterId, peerId: peer.id)
let dataSignal: Signal<ChannelStats?, NoError> = statsContext.state
|> map { state in
return state.stats
@ -314,6 +331,9 @@ public func channelStatsController(context: AccountContext, peer: Peer, cachedPe
if let w = statsContext, let a = a {
if case .OnDemand = a.interactionsGraph {
w.loadInteractionsGraph()
w.loadNewFollowersBySourceGraph()
w.loadViewsBySourceGraph()
w.loadLanguagesGraph()
}
}
})

View File

@ -13,12 +13,16 @@ import Charts
class StatsGraphItem: ListViewItem, ItemListItem {
let presentationData: ItemListPresentationData
let graph: ChannelStatsGraph
let type: ChartType
let height: CGFloat
let sectionId: ItemListSectionId
let style: ItemListStyle
init(presentationData: ItemListPresentationData, graph: ChannelStatsGraph, sectionId: ItemListSectionId, style: ItemListStyle) {
init(presentationData: ItemListPresentationData, graph: ChannelStatsGraph, type: ChartType, height: CGFloat = 0.0, sectionId: ItemListSectionId, style: ItemListStyle) {
self.presentationData = presentationData
self.graph = graph
self.type = type
self.height = height
self.sectionId = sectionId
self.style = style
}
@ -115,12 +119,12 @@ class StatsGraphItemNode: ListViewItemNode {
case .plain:
itemBackgroundColor = item.presentationData.theme.list.plainBackgroundColor
itemSeparatorColor = item.presentationData.theme.list.itemPlainSeparatorColor
contentSize = CGSize(width: params.width, height: 340.0)
contentSize = CGSize(width: params.width, height: 350.0 + item.height)
insets = itemListNeighborsPlainInsets(neighbors)
case .blocks:
itemBackgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor
itemSeparatorColor = item.presentationData.theme.list.itemBlocksSeparatorColor
contentSize = CGSize(width: params.width, height: 340.0)
contentSize = CGSize(width: params.width, height: 350.0 + item.height)
insets = itemListNeighborsGroupedInsets(neighbors)
}
@ -173,7 +177,7 @@ class StatsGraphItemNode: ListViewItemNode {
bottomStripeInset = 0.0
}
strongSelf.chartNode.frame = CGRect(origin: CGPoint(x: leftInset, y: -10.0), size: CGSize(width: layout.size.width - leftInset - rightInset, height: 350.0))
strongSelf.chartNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: layout.size.width - leftInset - rightInset, height: 400.0))
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight))
@ -182,7 +186,9 @@ class StatsGraphItemNode: ListViewItemNode {
if let updatedGraph = updatedGraph, case let .Loaded(data) = updatedGraph {
var data = data.replacingOccurrences(of: "step", with: "bar")
strongSelf.chartNode.setup(data)
strongSelf.chartNode.setup(data, type: item.type, getDetailsData: { date, completion in
})
}
}
})

View File

@ -418,7 +418,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1269012015] = { return Api.messages.AffectedHistory.parse_affectedHistory($0) }
dict[1244130093] = { return Api.StatsGraph.parse_statsGraphAsync($0) }
dict[-1092839390] = { return Api.StatsGraph.parse_statsGraphError($0) }
dict[-1057809608] = { return Api.StatsGraph.parse_statsGraph($0) }
dict[-1901828938] = { return Api.StatsGraph.parse_statsGraph($0) }
dict[-1036572727] = { return Api.account.PasswordInputSettings.parse_passwordInputSettings($0) }
dict[878078826] = { return Api.PageTableCell.parse_pageTableCell($0) }
dict[-1626209256] = { return Api.ChatBannedRights.parse_chatBannedRights($0) }
@ -529,7 +529,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1837345356] = { return Api.InputChatPhoto.parse_inputChatUploadedPhoto($0) }
dict[-1991004873] = { return Api.InputChatPhoto.parse_inputChatPhoto($0) }
dict[-368917890] = { return Api.PaymentCharge.parse_paymentCharge($0) }
dict[205195937] = { return Api.stats.BroadcastStats.parse_broadcastStats($0) }
dict[-1387279939] = { return Api.MessageInteractionCounters.parse_messageInteractionCounters($0) }
dict[821185690] = { return Api.stats.BroadcastStats.parse_broadcastStats($0) }
dict[-484987010] = { return Api.Updates.parse_updatesTooLong($0) }
dict[-1857044719] = { return Api.Updates.parse_updateShortMessage($0) }
dict[377562760] = { return Api.Updates.parse_updateShortChatMessage($0) }
@ -775,7 +776,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1041346555] = { return Api.updates.ChannelDifference.parse_channelDifferenceEmpty($0) }
dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) }
dict[-1531132162] = { return Api.updates.ChannelDifference.parse_channelDifferenceTooLong($0) }
dict[-581804346] = { return Api.StatsRowAbsValueAndPrev.parse_statsRowAbsValueAndPrev($0) }
dict[-309659827] = { return Api.channels.AdminLogResults.parse_adminLogResults($0) }
dict[-264117680] = { return Api.ChatOnlines.parse_chatOnlines($0) }
dict[488313413] = { return Api.InputAppEvent.parse_inputAppEvent($0) }
@ -1225,6 +1225,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.PaymentCharge:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageInteractionCounters:
_1.serialize(buffer, boxed)
case let _1 as Api.stats.BroadcastStats:
_1.serialize(buffer, boxed)
case let _1 as Api.Updates:
@ -1413,8 +1415,6 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.updates.ChannelDifference:
_1.serialize(buffer, boxed)
case let _1 as Api.StatsRowAbsValueAndPrev:
_1.serialize(buffer, boxed)
case let _1 as Api.channels.AdminLogResults:
_1.serialize(buffer, boxed)
case let _1 as Api.ChatOnlines:

View File

@ -12044,7 +12044,7 @@ public extension Api {
public enum StatsGraph: TypeConstructorDescription {
case statsGraphAsync(token: String)
case statsGraphError(error: String)
case statsGraph(json: Api.DataJSON)
case statsGraph(flags: Int32, json: Api.DataJSON, zoomToken: String?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -12060,11 +12060,13 @@ public extension Api {
}
serializeString(error, buffer: buffer, boxed: false)
break
case .statsGraph(let json):
case .statsGraph(let flags, let json, let zoomToken):
if boxed {
buffer.appendInt32(-1057809608)
buffer.appendInt32(-1901828938)
}
serializeInt32(flags, buffer: buffer, boxed: false)
json.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeString(zoomToken!, buffer: buffer, boxed: false)}
break
}
}
@ -12075,8 +12077,8 @@ public extension Api {
return ("statsGraphAsync", [("token", token)])
case .statsGraphError(let error):
return ("statsGraphError", [("error", error)])
case .statsGraph(let json):
return ("statsGraph", [("json", json)])
case .statsGraph(let flags, let json, let zoomToken):
return ("statsGraph", [("flags", flags), ("json", json), ("zoomToken", zoomToken)])
}
}
@ -12103,13 +12105,19 @@ public extension Api {
}
}
public static func parse_statsGraph(_ reader: BufferReader) -> StatsGraph? {
var _1: Api.DataJSON?
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.DataJSON?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.DataJSON
_2 = Api.parse(reader, signature: signature) as? Api.DataJSON
}
var _3: String?
if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) }
let _c1 = _1 != nil
if _c1 {
return Api.StatsGraph.statsGraph(json: _1!)
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
if _c1 && _c2 && _c3 {
return Api.StatsGraph.statsGraph(flags: _1!, json: _2!, zoomToken: _3)
}
else {
return nil
@ -14978,6 +14986,48 @@ public extension Api {
}
}
}
public enum MessageInteractionCounters: TypeConstructorDescription {
case messageInteractionCounters(msgId: Int32, views: Int32, forwards: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .messageInteractionCounters(let msgId, let views, let forwards):
if boxed {
buffer.appendInt32(-1387279939)
}
serializeInt32(msgId, buffer: buffer, boxed: false)
serializeInt32(views, buffer: buffer, boxed: false)
serializeInt32(forwards, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .messageInteractionCounters(let msgId, let views, let forwards):
return ("messageInteractionCounters", [("msgId", msgId), ("views", views), ("forwards", forwards)])
}
}
public static func parse_messageInteractionCounters(_ reader: BufferReader) -> MessageInteractionCounters? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: Int32?
_3 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.MessageInteractionCounters.messageInteractionCounters(msgId: _1!, views: _2!, forwards: _3!)
}
else {
return nil
}
}
}
public enum Updates: TypeConstructorDescription {
case updatesTooLong
@ -21264,54 +21314,6 @@ public extension Api {
}
}
}
public enum StatsRowAbsValueAndPrev: TypeConstructorDescription {
case statsRowAbsValueAndPrev(id: String, title: String, shortTitle: String, values: Api.StatsAbsValueAndPrev)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .statsRowAbsValueAndPrev(let id, let title, let shortTitle, let values):
if boxed {
buffer.appendInt32(-581804346)
}
serializeString(id, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false)
serializeString(shortTitle, buffer: buffer, boxed: false)
values.serialize(buffer, true)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .statsRowAbsValueAndPrev(let id, let title, let shortTitle, let values):
return ("statsRowAbsValueAndPrev", [("id", id), ("title", title), ("shortTitle", shortTitle), ("values", values)])
}
}
public static func parse_statsRowAbsValueAndPrev(_ reader: BufferReader) -> StatsRowAbsValueAndPrev? {
var _1: String?
_1 = parseString(reader)
var _2: String?
_2 = parseString(reader)
var _3: String?
_3 = parseString(reader)
var _4: Api.StatsAbsValueAndPrev?
if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev
}
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.StatsRowAbsValueAndPrev.statsRowAbsValueAndPrev(id: _1!, title: _2!, shortTitle: _3!, values: _4!)
}
else {
return nil
}
}
}
public enum ChatOnlines: TypeConstructorDescription {
case chatOnlines(onlines: Int32)

View File

@ -539,47 +539,40 @@ public struct payments {
public extension Api {
public struct stats {
public enum BroadcastStats: TypeConstructorDescription {
case broadcastStats(period: Api.StatsDateRangeDays, followers: Api.StatsAbsValueAndPrev, viewsPerPost: Api.StatsAbsValueAndPrev, sharesPerPost: Api.StatsAbsValueAndPrev, enabledNotifications: Api.StatsPercentValue, viewsBySource: [Api.StatsRowAbsValueAndPrev], newFollowersBySource: [Api.StatsRowAbsValueAndPrev], languages: [Api.StatsRowAbsValueAndPrev], growthGraph: Api.StatsGraph, followersGraph: Api.StatsGraph, muteGraph: Api.StatsGraph, topHoursGraph: Api.StatsGraph, interactionsGraph: Api.StatsGraph)
case broadcastStats(period: Api.StatsDateRangeDays, followers: Api.StatsAbsValueAndPrev, viewsPerPost: Api.StatsAbsValueAndPrev, sharesPerPost: Api.StatsAbsValueAndPrev, enabledNotifications: Api.StatsPercentValue, growthGraph: Api.StatsGraph, followersGraph: Api.StatsGraph, muteGraph: Api.StatsGraph, topHoursGraph: Api.StatsGraph, interactionsGraph: Api.StatsGraph, viewsBySourceGraph: Api.StatsGraph, newFollowersBySourceGraph: Api.StatsGraph, languagesGraph: Api.StatsGraph, recentMessageInteractions: [Api.MessageInteractionCounters])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .broadcastStats(let period, let followers, let viewsPerPost, let sharesPerPost, let enabledNotifications, let viewsBySource, let newFollowersBySource, let languages, let growthGraph, let followersGraph, let muteGraph, let topHoursGraph, let interactionsGraph):
case .broadcastStats(let period, let followers, let viewsPerPost, let sharesPerPost, let enabledNotifications, let growthGraph, let followersGraph, let muteGraph, let topHoursGraph, let interactionsGraph, let viewsBySourceGraph, let newFollowersBySourceGraph, let languagesGraph, let recentMessageInteractions):
if boxed {
buffer.appendInt32(205195937)
buffer.appendInt32(821185690)
}
period.serialize(buffer, true)
followers.serialize(buffer, true)
viewsPerPost.serialize(buffer, true)
sharesPerPost.serialize(buffer, true)
enabledNotifications.serialize(buffer, true)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(viewsBySource.count))
for item in viewsBySource {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(newFollowersBySource.count))
for item in newFollowersBySource {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(languages.count))
for item in languages {
item.serialize(buffer, true)
}
growthGraph.serialize(buffer, true)
followersGraph.serialize(buffer, true)
muteGraph.serialize(buffer, true)
topHoursGraph.serialize(buffer, true)
interactionsGraph.serialize(buffer, true)
viewsBySourceGraph.serialize(buffer, true)
newFollowersBySourceGraph.serialize(buffer, true)
languagesGraph.serialize(buffer, true)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(recentMessageInteractions.count))
for item in recentMessageInteractions {
item.serialize(buffer, true)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .broadcastStats(let period, let followers, let viewsPerPost, let sharesPerPost, let enabledNotifications, let viewsBySource, let newFollowersBySource, let languages, let growthGraph, let followersGraph, let muteGraph, let topHoursGraph, let interactionsGraph):
return ("broadcastStats", [("period", period), ("followers", followers), ("viewsPerPost", viewsPerPost), ("sharesPerPost", sharesPerPost), ("enabledNotifications", enabledNotifications), ("viewsBySource", viewsBySource), ("newFollowersBySource", newFollowersBySource), ("languages", languages), ("growthGraph", growthGraph), ("followersGraph", followersGraph), ("muteGraph", muteGraph), ("topHoursGraph", topHoursGraph), ("interactionsGraph", interactionsGraph)])
case .broadcastStats(let period, let followers, let viewsPerPost, let sharesPerPost, let enabledNotifications, let growthGraph, let followersGraph, let muteGraph, let topHoursGraph, let interactionsGraph, let viewsBySourceGraph, let newFollowersBySourceGraph, let languagesGraph, let recentMessageInteractions):
return ("broadcastStats", [("period", period), ("followers", followers), ("viewsPerPost", viewsPerPost), ("sharesPerPost", sharesPerPost), ("enabledNotifications", enabledNotifications), ("growthGraph", growthGraph), ("followersGraph", followersGraph), ("muteGraph", muteGraph), ("topHoursGraph", topHoursGraph), ("interactionsGraph", interactionsGraph), ("viewsBySourceGraph", viewsBySourceGraph), ("newFollowersBySourceGraph", newFollowersBySourceGraph), ("languagesGraph", languagesGraph), ("recentMessageInteractions", recentMessageInteractions)])
}
}
@ -604,17 +597,17 @@ public struct stats {
if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.StatsPercentValue
}
var _6: [Api.StatsRowAbsValueAndPrev]?
if let _ = reader.readInt32() {
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsRowAbsValueAndPrev.self)
var _6: Api.StatsGraph?
if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
var _7: [Api.StatsRowAbsValueAndPrev]?
if let _ = reader.readInt32() {
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsRowAbsValueAndPrev.self)
var _7: Api.StatsGraph?
if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
var _8: [Api.StatsRowAbsValueAndPrev]?
if let _ = reader.readInt32() {
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsRowAbsValueAndPrev.self)
var _8: Api.StatsGraph?
if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
var _9: Api.StatsGraph?
if let signature = reader.readInt32() {
@ -636,6 +629,10 @@ public struct stats {
if let signature = reader.readInt32() {
_13 = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
var _14: [Api.MessageInteractionCounters]?
if let _ = reader.readInt32() {
_14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageInteractionCounters.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
@ -649,8 +646,9 @@ public struct stats {
let _c11 = _11 != nil
let _c12 = _12 != nil
let _c13 = _13 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 {
return Api.stats.BroadcastStats.broadcastStats(period: _1!, followers: _2!, viewsPerPost: _3!, sharesPerPost: _4!, enabledNotifications: _5!, viewsBySource: _6!, newFollowersBySource: _7!, languages: _8!, growthGraph: _9!, followersGraph: _10!, muteGraph: _11!, topHoursGraph: _12!, interactionsGraph: _13!)
let _c14 = _14 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 {
return Api.stats.BroadcastStats.broadcastStats(period: _1!, followers: _2!, viewsPerPost: _3!, sharesPerPost: _4!, enabledNotifications: _5!, growthGraph: _6!, followersGraph: _7!, muteGraph: _8!, topHoursGraph: _9!, interactionsGraph: _10!, viewsBySourceGraph: _11!, newFollowersBySourceGraph: _12!, languagesGraph: _13!, recentMessageInteractions: _14!)
}
else {
return nil

View File

@ -3964,11 +3964,13 @@ public extension Api {
})
}
public static func loadAsyncGraph(token: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.StatsGraph>) {
public static func loadAsyncGraph(flags: Int32, token: String, x: Int64?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.StatsGraph>) {
let buffer = Buffer()
buffer.appendInt32(1749505346)
buffer.appendInt32(1646092192)
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(token, buffer: buffer, boxed: false)
return (FunctionDescription(name: "stats.loadAsyncGraph", parameters: [("token", token)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.StatsGraph? in
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(x!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "stats.loadAsyncGraph", parameters: [("flags", flags), ("token", token), ("x", x)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.StatsGraph? in
let reader = BufferReader(buffer)
var result: Api.StatsGraph?
if let signature = reader.readInt32() {

View File

@ -20,13 +20,6 @@ public struct ChannelStatsPercentValue: Equatable {
public let total: Double
}
public struct ChannelStatsNamedValue: Equatable {
public let id: String
public let title: String
public let shortTitle: String
public let value: ChannelStatsValue
}
public enum ChannelStatsGraph: Equatable {
case OnDemand(token: String)
case Failed(error: String)
@ -39,29 +32,29 @@ public final class ChannelStats: Equatable {
public let viewsPerPost: ChannelStatsValue
public let sharesPerPost: ChannelStatsValue
public let enabledNotifications: ChannelStatsPercentValue
public let viewsBySource: [ChannelStatsNamedValue]
public let newFollowersBySource: [ChannelStatsNamedValue]
public let languages: [ChannelStatsNamedValue]
public let growthGraph: ChannelStatsGraph
public let followersGraph: ChannelStatsGraph
public let muteGraph: ChannelStatsGraph
public let topHoursGraph: ChannelStatsGraph
public let interactionsGraph: ChannelStatsGraph
public init(period: ChannelStatsDateRange, followers: ChannelStatsValue, viewsPerPost: ChannelStatsValue, sharesPerPost: ChannelStatsValue, enabledNotifications: ChannelStatsPercentValue, viewsBySource: [ChannelStatsNamedValue], newFollowersBySource: [ChannelStatsNamedValue], languages: [ChannelStatsNamedValue], growthGraph: ChannelStatsGraph, followersGraph: ChannelStatsGraph, muteGraph: ChannelStatsGraph, topHoursGraph: ChannelStatsGraph, interactionsGraph: ChannelStatsGraph) {
public let viewsBySourceGraph: ChannelStatsGraph
public let newFollowersBySourceGraph: ChannelStatsGraph
public let languagesGraph: ChannelStatsGraph
public init(period: ChannelStatsDateRange, followers: ChannelStatsValue, viewsPerPost: ChannelStatsValue, sharesPerPost: ChannelStatsValue, enabledNotifications: ChannelStatsPercentValue, growthGraph: ChannelStatsGraph, followersGraph: ChannelStatsGraph, muteGraph: ChannelStatsGraph, topHoursGraph: ChannelStatsGraph, interactionsGraph: ChannelStatsGraph, viewsBySourceGraph: ChannelStatsGraph, newFollowersBySourceGraph: ChannelStatsGraph, languagesGraph: ChannelStatsGraph) {
self.period = period
self.followers = followers
self.viewsPerPost = viewsPerPost
self.sharesPerPost = sharesPerPost
self.enabledNotifications = enabledNotifications
self.viewsBySource = viewsBySource
self.newFollowersBySource = newFollowersBySource
self.languages = languages
self.growthGraph = growthGraph
self.followersGraph = followersGraph
self.muteGraph = muteGraph
self.topHoursGraph = topHoursGraph
self.interactionsGraph = interactionsGraph
self.viewsBySourceGraph = viewsBySourceGraph
self.newFollowersBySourceGraph = newFollowersBySourceGraph
self.languagesGraph = languagesGraph
}
public static func == (lhs: ChannelStats, rhs: ChannelStats) -> Bool {
@ -80,15 +73,6 @@ public final class ChannelStats: Equatable {
if lhs.enabledNotifications != rhs.enabledNotifications {
return false
}
if lhs.viewsBySource != rhs.viewsBySource {
return false
}
if lhs.newFollowersBySource != rhs.newFollowersBySource {
return false
}
if lhs.languages != rhs.languages {
return false
}
if lhs.growthGraph != rhs.growthGraph {
return false
}
@ -104,27 +88,48 @@ public final class ChannelStats: Equatable {
if lhs.interactionsGraph != rhs.interactionsGraph {
return false
}
if lhs.viewsBySourceGraph != rhs.viewsBySourceGraph {
return false
}
if lhs.newFollowersBySourceGraph != rhs.newFollowersBySourceGraph {
return false
}
if lhs.languagesGraph != rhs.languagesGraph {
return false
}
return true
}
public func withUpdatedGrowthGraph(_ growthGraph: ChannelStatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, viewsBySource: self.viewsBySource, newFollowersBySource: self.newFollowersBySource, languages: self.languages, growthGraph: growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph)
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph)
}
public func withUpdatedFollowersGraph(_ followersGraph: ChannelStatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, viewsBySource: self.viewsBySource, newFollowersBySource: self.newFollowersBySource, languages: self.languages, growthGraph: self.growthGraph, followersGraph: followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph)
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph)
}
public func withUpdatedMuteGraph(_ muteGraph: ChannelStatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, viewsBySource: self.viewsBySource, newFollowersBySource: self.newFollowersBySource, languages: self.languages, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph)
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph)
}
public func withUpdatedTopHoursGraph(_ viewsByHourGraph: ChannelStatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, viewsBySource: self.viewsBySource, newFollowersBySource: self.newFollowersBySource, languages: self.languages, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: viewsByHourGraph, interactionsGraph: self.interactionsGraph)
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: viewsByHourGraph, interactionsGraph: self.interactionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph)
}
public func withUpdatedInteractionsGraph(_ interactionsGraph: ChannelStatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, viewsBySource: self.viewsBySource, newFollowersBySource: self.newFollowersBySource, languages: self.languages, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: interactionsGraph)
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: interactionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph)
}
public func withUpdatedViewsBySourceGraph(_ viewsBySourceGraph: ChannelStatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, viewsBySourceGraph: viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph)
}
public func withUpdatedNewFollowersBySourceGraph(_ newFollowersBySourceGraph: ChannelStatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: newFollowersBySourceGraph, languagesGraph: self.languagesGraph)
}
public func withUpdatedLanguagesGraph(_ languagesGraph: ChannelStatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: languagesGraph)
}
}
@ -132,46 +137,54 @@ public struct ChannelStatsContextState: Equatable {
public var stats: ChannelStats?
}
private func requestStats(network: Network, datacenterId: Int32, peer: Peer, dark: Bool = false) -> Signal<ChannelStats?, NoError> {
guard let inputChannel = apiInputChannel(peer) else {
return .never()
}
var flags: Int32 = 0
if dark {
flags |= (1 << 1)
}
let signal: Signal<Api.stats.BroadcastStats, MTRpcError>
if network.datacenterId != datacenterId {
signal = network.download(datacenterId: Int(datacenterId), isMedia: false, tag: nil)
|> castError(MTRpcError.self)
|> mapToSignal { worker in
return worker.request(Api.functions.stats.getBroadcastStats(flags: flags, channel: inputChannel))
private func requestStats(postbox: Postbox, network: Network, datacenterId: Int32, peerId: PeerId, dark: Bool = false) -> Signal<ChannelStats?, NoError> {
return postbox.transaction { transaction -> Peer? in
return transaction.getPeer(peerId)
} |> mapToSignal { peer -> Signal<ChannelStats?, NoError> in
guard let peer = peer, let inputChannel = apiInputChannel(peer) else {
return .never()
}
var flags: Int32 = 0
if dark {
flags |= (1 << 1)
}
let signal: Signal<Api.stats.BroadcastStats, MTRpcError>
if network.datacenterId != datacenterId {
signal = network.download(datacenterId: Int(datacenterId), isMedia: false, tag: nil)
|> castError(MTRpcError.self)
|> mapToSignal { worker in
return worker.request(Api.functions.stats.getBroadcastStats(flags: flags, channel: inputChannel))
}
} else {
signal = network.request(Api.functions.stats.getBroadcastStats(flags: flags, channel: inputChannel))
}
return signal
|> map { result -> ChannelStats? in
return ChannelStats(apiBroadcastStats: result)
}
|> `catch` { _ -> Signal<ChannelStats?, NoError> in
return .single(nil)
}
} else {
signal = network.request(Api.functions.stats.getBroadcastStats(flags: flags, channel: inputChannel))
}
return signal
|> map { result -> ChannelStats? in
return ChannelStats(apiBroadcastStats: result)
}
|> `catch` { _ -> Signal<ChannelStats?, NoError> in
return .single(nil)
}
}
private func requestGraph(network: Network, datacenterId: Int32, token: String) -> Signal<ChannelStatsGraph?, NoError> {
private func requestGraph(network: Network, datacenterId: Int32, token: String, x: Int64? = nil) -> Signal<ChannelStatsGraph?, NoError> {
var flags: Int32 = 0
if let _ = x {
flags |= (1 << 0)
}
let signal: Signal<Api.StatsGraph, MTRpcError>
if network.datacenterId != datacenterId {
signal = network.download(datacenterId: Int(datacenterId), isMedia: false, tag: nil)
|> castError(MTRpcError.self)
|> mapToSignal { worker in
return worker.request(Api.functions.stats.loadAsyncGraph(token: token))
return worker.request(Api.functions.stats.loadAsyncGraph(flags: flags, token: token, x: x))
}
} else {
signal = network.request(Api.functions.stats.loadAsyncGraph(token: token))
signal = network.request(Api.functions.stats.loadAsyncGraph(flags: flags, token: token, x: x))
}
return signal
@ -184,9 +197,10 @@ private func requestGraph(network: Network, datacenterId: Int32, token: String)
}
private final class ChannelStatsContextImpl {
private let postbox: Postbox
private let network: Network
private let peer: Peer
private let datacenterId: Int32
private let peerId: PeerId
private var _state: ChannelStatsContextState {
didSet {
@ -203,12 +217,13 @@ private final class ChannelStatsContextImpl {
private let disposable = MetaDisposable()
private let disposables = DisposableDict<String>()
init(network: Network, datacenterId: Int32, peer: Peer) {
init(postbox: Postbox, network: Network, datacenterId: Int32, peerId: PeerId) {
assert(Queue.mainQueue().isCurrent())
self.postbox = postbox
self.network = network
self.peer = peer
self.datacenterId = datacenterId
self.peerId = peerId
self._state = ChannelStatsContextState(stats: nil)
self._statePromise.set(.single(self._state))
@ -224,7 +239,7 @@ private final class ChannelStatsContextImpl {
private func load() {
assert(Queue.mainQueue().isCurrent())
self.disposable.set((requestStats(network: self.network, datacenterId: self.datacenterId, peer: self.peer)
self.disposable.set((requestStats(postbox: self.postbox, network: self.network, datacenterId: self.datacenterId, peerId: self.peerId)
|> deliverOnMainQueue).start(next: { [weak self] stats in
if let strongSelf = self {
strongSelf._state = ChannelStatsContextState(stats: stats)
@ -307,6 +322,51 @@ private final class ChannelStatsContextImpl {
}), forKey: token)
}
}
func loadViewsBySourceGraph() {
guard let stats = self._state.stats else {
return
}
if case let .OnDemand(token) = stats.viewsBySourceGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph {
strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedViewsBySourceGraph(graph))
strongSelf._statePromise.set(.single(strongSelf._state))
}
}), forKey: token)
}
}
func loadNewFollowersBySourceGraph() {
guard let stats = self._state.stats else {
return
}
if case let .OnDemand(token) = stats.newFollowersBySourceGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph {
strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedNewFollowersBySourceGraph(graph))
strongSelf._statePromise.set(.single(strongSelf._state))
}
}), forKey: token)
}
}
func loadLanguagesGraph() {
guard let stats = self._state.stats else {
return
}
if case let .OnDemand(token) = stats.languagesGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph {
strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedLanguagesGraph(graph))
strongSelf._statePromise.set(.single(strongSelf._state))
}
}), forKey: token)
}
}
}
public final class ChannelStatsContext {
@ -324,12 +384,12 @@ public final class ChannelStatsContext {
}
}
public init(network: Network, datacenterId: Int32, peer: Peer) {
public init(postbox: Postbox, network: Network, datacenterId: Int32, peerId: PeerId) {
self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: {
return ChannelStatsContextImpl(network: network, datacenterId: datacenterId, peer: peer)
return ChannelStatsContextImpl(postbox: postbox, network: network, datacenterId: datacenterId, peerId: peerId)
})
}
public func loadGrowthGraph() {
self.impl.with { impl in
impl.loadGrowthGraph()
@ -359,12 +419,30 @@ public final class ChannelStatsContext {
impl.loadInteractionsGraph()
}
}
public func loadViewsBySourceGraph() {
self.impl.with { impl in
impl.loadViewsBySourceGraph()
}
}
public func loadNewFollowersBySourceGraph() {
self.impl.with { impl in
impl.loadNewFollowersBySourceGraph()
}
}
public func loadLanguagesGraph() {
self.impl.with { impl in
impl.loadLanguagesGraph()
}
}
}
extension ChannelStatsGraph {
init(apiStatsGraph: Api.StatsGraph) {
switch apiStatsGraph {
case let .statsGraph(json):
case let .statsGraph(_, json, _):
if case let .dataJSON(string) = json {
self = .Loaded(data: string)
} else {
@ -396,15 +474,6 @@ extension ChannelStatsValue {
}
}
extension ChannelStatsNamedValue {
init(apiStatsRowAbsValueAndPrev: Api.StatsRowAbsValueAndPrev) {
switch apiStatsRowAbsValueAndPrev {
case let .statsRowAbsValueAndPrev(id, title, shortTitle, values):
self = ChannelStatsNamedValue(id: id, title: title, shortTitle: shortTitle, value: ChannelStatsValue(apiStatsAbsValueAndPrev: values))
}
}
}
extension ChannelStatsPercentValue {
init(apiPercentValue: Api.StatsPercentValue) {
switch apiPercentValue {
@ -417,8 +486,8 @@ extension ChannelStatsPercentValue {
extension ChannelStats {
convenience init(apiBroadcastStats: Api.stats.BroadcastStats) {
switch apiBroadcastStats {
case let .broadcastStats(period, followers, viewsPerPost, sharesPerPost, enabledNotifications, viewsBySource, newFollowersBySource, languages, growthGraph, followersGraph, muteGraph, topHoursGraph, interactionsGraph):
self.init(period: ChannelStatsDateRange(apiStatsDateRangeDays: period), followers: ChannelStatsValue(apiStatsAbsValueAndPrev: followers), viewsPerPost: ChannelStatsValue(apiStatsAbsValueAndPrev: viewsPerPost), sharesPerPost: ChannelStatsValue(apiStatsAbsValueAndPrev: sharesPerPost), enabledNotifications: ChannelStatsPercentValue(apiPercentValue: enabledNotifications), viewsBySource: viewsBySource.map { ChannelStatsNamedValue(apiStatsRowAbsValueAndPrev: $0) }, newFollowersBySource: newFollowersBySource.map { ChannelStatsNamedValue(apiStatsRowAbsValueAndPrev: $0) }, languages: languages.map { ChannelStatsNamedValue(apiStatsRowAbsValueAndPrev: $0) }, growthGraph: ChannelStatsGraph(apiStatsGraph: growthGraph), followersGraph: ChannelStatsGraph(apiStatsGraph: followersGraph), muteGraph: ChannelStatsGraph(apiStatsGraph: muteGraph), topHoursGraph: ChannelStatsGraph(apiStatsGraph: topHoursGraph), interactionsGraph: ChannelStatsGraph(apiStatsGraph: interactionsGraph))
case let .broadcastStats(period, followers, viewsPerPost, sharesPerPost, enabledNotifications, growthGraph, followersGraph, muteGraph, topHoursGraph, interactionsGraph, viewsBySourceGraph, newFollowersBySourceGraph, languagesGraph, recentMessageInteractions):
self.init(period: ChannelStatsDateRange(apiStatsDateRangeDays: period), followers: ChannelStatsValue(apiStatsAbsValueAndPrev: followers), viewsPerPost: ChannelStatsValue(apiStatsAbsValueAndPrev: viewsPerPost), sharesPerPost: ChannelStatsValue(apiStatsAbsValueAndPrev: sharesPerPost), enabledNotifications: ChannelStatsPercentValue(apiPercentValue: enabledNotifications), growthGraph: ChannelStatsGraph(apiStatsGraph: growthGraph), followersGraph: ChannelStatsGraph(apiStatsGraph: followersGraph), muteGraph: ChannelStatsGraph(apiStatsGraph: muteGraph), topHoursGraph: ChannelStatsGraph(apiStatsGraph: topHoursGraph), interactionsGraph: ChannelStatsGraph(apiStatsGraph: interactionsGraph), viewsBySourceGraph: ChannelStatsGraph(apiStatsGraph: viewsBySourceGraph), newFollowersBySourceGraph: ChannelStatsGraph(apiStatsGraph: newFollowersBySourceGraph), languagesGraph: ChannelStatsGraph(apiStatsGraph: languagesGraph))
}
}
}