From 4da23aac27aa24aa91e77167534318d3458862b8 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 26 Feb 2020 15:04:05 +0400 Subject: [PATCH] Update channel statistics [skip ci] --- .../Chart Screen/ChartDetailsView.swift | 7 +-- .../Chart Screen/ChartStackSection.swift | 2 +- .../Charts Reader/ChartsCollection.swift | 3 +- .../Sources/StatsController.swift | 43 ++++++++++++------ .../StatisticsUI/Sources/StatsGraphItem.swift | 13 ++++-- .../Sources/ChannelStatistics.swift | 37 +++++++++++++-- .../Chart/arrow_left.imageset/Contents.json | 2 +- .../arrow_right.pdf | Bin .../Chart/arrow_right.imageset/Contents.json | 2 +- .../arrow_left.pdf | Bin 10 files changed, 77 insertions(+), 32 deletions(-) rename submodules/TelegramUI/Images.xcassets/Chart/{arrow_right.imageset => arrow_left.imageset}/arrow_right.pdf (100%) rename submodules/TelegramUI/Images.xcassets/Chart/{arrow_left.imageset => arrow_right.imageset}/arrow_left.pdf (100%) diff --git a/submodules/Charts/Sources/Chart Screen/ChartDetailsView.swift b/submodules/Charts/Sources/Chart Screen/ChartDetailsView.swift index 7232c594c3..ecf685aafb 100644 --- a/submodules/Charts/Sources/Chart Screen/ChartDetailsView.swift +++ b/submodules/Charts/Sources/Chart Screen/ChartDetailsView.swift @@ -38,6 +38,7 @@ struct ChartDetailsViewModel { class ChartDetailsView: UIControl { let titleLabel = UILabel() let arrowView = UIImageView() + let activityIndicator = UIActivityIndicatorView() var prefixViews: [UILabel] = [] var labelsViews: [UILabel] = [] @@ -45,11 +46,7 @@ class ChartDetailsView: UIControl { private var viewModel: ChartDetailsViewModel? private var colorMode: ColorMode = .day - - static func fromNib() -> ChartDetailsView { - return Bundle.main.loadNibNamed("ChartDetailsView", owner: nil, options: nil)?.first as! ChartDetailsView - } - + override init(frame: CGRect) { super.init(frame: frame) diff --git a/submodules/Charts/Sources/Chart Screen/ChartStackSection.swift b/submodules/Charts/Sources/Chart Screen/ChartStackSection.swift index 2d28d7b6cc..333bdf1132 100644 --- a/submodules/Charts/Sources/Chart Screen/ChartStackSection.swift +++ b/submodules/Charts/Sources/Chart Screen/ChartStackSection.swift @@ -189,7 +189,7 @@ class ChartStackSection: UIView, ColorModeContainer { controller.chartRangeUpdatedClosure = { [unowned self] (range, animated) in self.rangeView.setRange(range, animated: animated) } - controller.chartRangePagingClosure = { [unowned self] (isEnabled, pageSize) in + controller.chartRangePagingClosure = { [unowned self] (isEnabled, pageSize) in self.rangeView.setRangePaging(enabled: isEnabled, minimumSize: pageSize) } diff --git a/submodules/Charts/Sources/Charts Reader/ChartsCollection.swift b/submodules/Charts/Sources/Charts Reader/ChartsCollection.swift index c91b9536ed..f0348f7ab6 100644 --- a/submodules/Charts/Sources/Charts Reader/ChartsCollection.swift +++ b/submodules/Charts/Sources/Charts Reader/ChartsCollection.swift @@ -58,7 +58,7 @@ extension ChartsCollection { switch type { case .axix: axixValuesToSetup = try column.dropFirst().map { Date(timeIntervalSince1970: try Convert.doubleFrom($0) / 1000) } - case .chart, .bar, .area: + case .chart, .bar, .area, .step: guard let colorString = colors[columnId], let color = UIColor(hexString: colorString) else { throw ChartsError.generalConversion("Unable to get color name from: \(colors) - \(columnId)") @@ -88,4 +88,5 @@ private enum ColumnType: String { case chart = "line" case area = "area" case bar = "bar" + case step = "step" } diff --git a/submodules/StatisticsUI/Sources/StatsController.swift b/submodules/StatisticsUI/Sources/StatsController.swift index eab8417515..87b59f7d15 100644 --- a/submodules/StatisticsUI/Sources/StatsController.swift +++ b/submodules/StatisticsUI/Sources/StatsController.swift @@ -8,6 +8,7 @@ import SyncCore import MapKit import TelegramPresentationData import TelegramUIPreferences +import TelegramStringFormatting import ItemListUI import PresentationDataUtils import AccountContext @@ -15,8 +16,11 @@ import PresentationDataUtils import AppBundle import Charts -private final class StatsArguments { - init() { +private final class StatsControllerArguments { + let loadDetailedGraph: (ChannelStatsGraph, Int64) -> Signal + + init(loadDetailedGraph: @escaping (ChannelStatsGraph, Int64) -> Signal) { + self.loadDetailedGraph = loadDetailedGraph } } @@ -33,7 +37,7 @@ private enum StatsSection: Int32 { } private enum StatsEntry: ItemListNodeEntry { - case overviewHeader(PresentationTheme, String) + case overviewHeader(PresentationTheme, String, String) case overview(PresentationTheme, ChannelStats) case growthTitle(PresentationTheme, String) @@ -126,8 +130,8 @@ private enum StatsEntry: ItemListNodeEntry { static func ==(lhs: StatsEntry, rhs: StatsEntry) -> Bool { switch lhs { - case let .overviewHeader(lhsTheme, lhsText): - if case let .overviewHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + case let .overviewHeader(lhsTheme, lhsText, lhsDates): + if case let .overviewHeader(rhsTheme, rhsText, rhsDates) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsDates == rhsDates { return true } else { return false @@ -242,9 +246,11 @@ private enum StatsEntry: ItemListNodeEntry { } func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { + let arguments = arguments as! StatsControllerArguments switch self { - case let .overviewHeader(theme, text), - let .growthTitle(theme, text), + case let .overviewHeader(theme, text, dates): + return ItemListSectionHeaderItem(presentationData: presentationData, text: text, accessoryText: ItemListSectionHeaderAccessoryText(value: dates, color: .generic), sectionId: self.section) + case let .growthTitle(theme, text), let .followersTitle(theme, text), let .notificationsTitle(theme, text), let .viewsByHourTitle(theme, text), @@ -258,19 +264,21 @@ private enum StatsEntry: ItemListNodeEntry { 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) + 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) + 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) + 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) + return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, getDetailsData: { date, completion in + let _ = arguments.loadDetailedGraph(graph, Int64(date.timeIntervalSince1970)) + }, 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) + 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) + return StatsGraphItem(presentationData: presentationData, graph: graph, type: type, height: 100.0, sectionId: self.section, style: .blocks) } } } @@ -279,7 +287,10 @@ private func statsControllerEntries(data: ChannelStats?, presentationData: Prese var entries: [StatsEntry] = [] if let data = data { - entries.append(.overviewHeader(presentationData.theme, presentationData.strings.Stats_Overview)) + let minDate = stringForDate(timestamp: data.period.minDate, strings: presentationData.strings) + let maxDate = stringForDate(timestamp: data.period.maxDate, strings: presentationData.strings) + + entries.append(.overviewHeader(presentationData.theme, presentationData.strings.Stats_Overview, "\(minDate) – \(maxDate)")) entries.append(.overview(presentationData.theme, data)) entries.append(.growthTitle(presentationData.theme, presentationData.strings.Stats_GrowthTitle)) @@ -339,7 +350,9 @@ public func channelStatsController(context: AccountContext, peer: Peer, cachedPe }) dataPromise.set(.single(nil) |> then(dataSignal)) - let arguments = StatsArguments() + let arguments = StatsControllerArguments(loadDetailedGraph: { graph, x -> Signal in + return statsContext.loadDetailedGraph(graph, x: x) + }) let signal = combineLatest(context.sharedContext.presentationData, dataPromise.get()) |> deliverOnMainQueue diff --git a/submodules/StatisticsUI/Sources/StatsGraphItem.swift b/submodules/StatisticsUI/Sources/StatsGraphItem.swift index f713c89f6e..d8da3b40d1 100644 --- a/submodules/StatisticsUI/Sources/StatsGraphItem.swift +++ b/submodules/StatisticsUI/Sources/StatsGraphItem.swift @@ -15,14 +15,16 @@ class StatsGraphItem: ListViewItem, ItemListItem { let graph: ChannelStatsGraph let type: ChartType let height: CGFloat + let getDetailsData: ((Date, (String?) -> Void) -> Void)? let sectionId: ItemListSectionId let style: ItemListStyle - init(presentationData: ItemListPresentationData, graph: ChannelStatsGraph, type: ChartType, height: CGFloat = 0.0, sectionId: ItemListSectionId, style: ItemListStyle) { + init(presentationData: ItemListPresentationData, graph: ChannelStatsGraph, type: ChartType, height: CGFloat = 0.0, getDetailsData: ((Date, (String?) -> Void) -> Void)? = nil, sectionId: ItemListSectionId, style: ItemListStyle) { self.presentationData = presentationData self.graph = graph self.type = type self.height = height + self.getDetailsData = getDetailsData self.sectionId = sectionId self.style = style } @@ -184,10 +186,11 @@ class StatsGraphItemNode: ListViewItemNode { strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight)) } - if let updatedGraph = updatedGraph, case let .Loaded(data) = updatedGraph { - var data = data.replacingOccurrences(of: "step", with: "bar") - strongSelf.chartNode.setup(data, type: item.type, getDetailsData: { date, completion in - + if let updatedGraph = updatedGraph, case let .Loaded(_, data) = updatedGraph { + strongSelf.chartNode.setup(data, type: item.type, getDetailsData: { [weak self] date, completion in + if let strongSelf = self, let item = strongSelf.item { + item.getDetailsData?(date, completion) + } }) } } diff --git a/submodules/TelegramCore/Sources/ChannelStatistics.swift b/submodules/TelegramCore/Sources/ChannelStatistics.swift index 287bb176de..18b49c67de 100644 --- a/submodules/TelegramCore/Sources/ChannelStatistics.swift +++ b/submodules/TelegramCore/Sources/ChannelStatistics.swift @@ -23,7 +23,18 @@ public struct ChannelStatsPercentValue: Equatable { public enum ChannelStatsGraph: Equatable { case OnDemand(token: String) case Failed(error: String) - case Loaded(data: String) + case Loaded(token: String?, data: String) + + var token: String? { + switch self { + case let .OnDemand(token): + return token + case let .Loaded(token, data): + return token + default: + return nil + } + } } public final class ChannelStats: Equatable { @@ -367,6 +378,14 @@ private final class ChannelStatsContextImpl { }), forKey: token) } } + + func loadDetailedGraph(_ graph: ChannelStatsGraph, x: Int64) -> Signal { + if let token = graph.token { + return requestGraph(network: self.network, datacenterId: self.datacenterId, token: token, x: x) + } else { + return .single(nil) + } + } } public final class ChannelStatsContext { @@ -437,14 +456,26 @@ public final class ChannelStatsContext { impl.loadLanguagesGraph() } } + + public func loadDetailedGraph(_ graph: ChannelStatsGraph, x: Int64) -> Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + self.impl.with { impl in + disposable.set(impl.loadDetailedGraph(graph, x: x).start(next: { value in + subscriber.putNext(value) + })) + } + return disposable + } + } } extension ChannelStatsGraph { init(apiStatsGraph: Api.StatsGraph) { switch apiStatsGraph { - case let .statsGraph(_, json, _): + case let .statsGraph(_, json, zoomToken): if case let .dataJSON(string) = json { - self = .Loaded(data: string) + self = .Loaded(token: zoomToken, data: string) } else { self = .Failed(error: "") } diff --git a/submodules/TelegramUI/Images.xcassets/Chart/arrow_left.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chart/arrow_left.imageset/Contents.json index 78b05b5e2a..147027fa6d 100644 --- a/submodules/TelegramUI/Images.xcassets/Chart/arrow_left.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chart/arrow_left.imageset/Contents.json @@ -2,7 +2,7 @@ "images" : [ { "idiom" : "universal", - "filename" : "arrow_left.pdf" + "filename" : "arrow_right.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chart/arrow_right.imageset/arrow_right.pdf b/submodules/TelegramUI/Images.xcassets/Chart/arrow_left.imageset/arrow_right.pdf similarity index 100% rename from submodules/TelegramUI/Images.xcassets/Chart/arrow_right.imageset/arrow_right.pdf rename to submodules/TelegramUI/Images.xcassets/Chart/arrow_left.imageset/arrow_right.pdf diff --git a/submodules/TelegramUI/Images.xcassets/Chart/arrow_right.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chart/arrow_right.imageset/Contents.json index 147027fa6d..78b05b5e2a 100644 --- a/submodules/TelegramUI/Images.xcassets/Chart/arrow_right.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chart/arrow_right.imageset/Contents.json @@ -2,7 +2,7 @@ "images" : [ { "idiom" : "universal", - "filename" : "arrow_right.pdf" + "filename" : "arrow_left.pdf" } ], "info" : { diff --git a/submodules/TelegramUI/Images.xcassets/Chart/arrow_left.imageset/arrow_left.pdf b/submodules/TelegramUI/Images.xcassets/Chart/arrow_right.imageset/arrow_left.pdf similarity index 100% rename from submodules/TelegramUI/Images.xcassets/Chart/arrow_left.imageset/arrow_left.pdf rename to submodules/TelegramUI/Images.xcassets/Chart/arrow_right.imageset/arrow_left.pdf