mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Channel Statistics
This commit is contained in:
@@ -5346,3 +5346,14 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"PeerInfo.BioExpand" = "more";
|
||||
|
||||
"External.OpenIn" = "Open in %@";
|
||||
|
||||
"Stats.Overview" = "OVERVIEW";
|
||||
"Stats.Followers" = "Followers";
|
||||
"Stats.EnabledNotifications" = "Enabled Notifications";
|
||||
"Stats.ViewsPerPost" = "Views Per Post";
|
||||
"Stats.SharesPerPost" = "Shares Per Post";
|
||||
|
||||
"Stats.GrowthTitle" = "GROWTH";
|
||||
"Stats.FollowersTitle" = "FOLLOWERS";
|
||||
"Stats.NotificationsTitle" = "NOTIFICATIONS";
|
||||
"Stats.InteractionsTitle" = "INTERACTIONS";
|
||||
|
||||
@@ -19,7 +19,6 @@ class ChartStackSection: UIView, ColorModeContainer {
|
||||
var sectionContainerView: UIView
|
||||
var separators: [UIView] = []
|
||||
|
||||
var headerLabel: UILabel!
|
||||
var titleLabel: UILabel!
|
||||
var backButton: UIButton!
|
||||
|
||||
@@ -30,7 +29,6 @@ class ChartStackSection: UIView, ColorModeContainer {
|
||||
chartView = ChartView()
|
||||
rangeView = RangeChartView()
|
||||
visibilityView = ChartVisibilityView()
|
||||
headerLabel = UILabel()
|
||||
titleLabel = UILabel()
|
||||
backButton = UIButton()
|
||||
|
||||
@@ -40,9 +38,10 @@ class ChartStackSection: UIView, ColorModeContainer {
|
||||
sectionContainerView.addSubview(chartView)
|
||||
sectionContainerView.addSubview(rangeView)
|
||||
sectionContainerView.addSubview(visibilityView)
|
||||
sectionContainerView.addSubview(titleLabel)
|
||||
|
||||
headerLabel.font = UIFont.systemFont(ofSize: 14, weight: .regular)
|
||||
titleLabel.font = UIFont.systemFont(ofSize: 14, weight: .bold)
|
||||
titleLabel.textAlignment = .center
|
||||
visibilityView.clipsToBounds = true
|
||||
backButton.isExclusiveTouch = true
|
||||
|
||||
@@ -56,7 +55,6 @@ class ChartStackSection: UIView, ColorModeContainer {
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
headerLabel.font = UIFont.systemFont(ofSize: 14, weight: .regular)
|
||||
titleLabel.font = UIFont.systemFont(ofSize: 14, weight: .bold)
|
||||
visibilityView.clipsToBounds = true
|
||||
backButton.isExclusiveTouch = true
|
||||
@@ -95,7 +93,6 @@ class ChartStackSection: UIView, ColorModeContainer {
|
||||
}
|
||||
|
||||
self.titleLabel.setTextColor(colorMode.chartTitleColor, animated: animated && titleLabel.isVisibleInWindow)
|
||||
self.headerLabel.setTextColor(colorMode.sectionTitleColor, animated: animated && headerLabel.isVisibleInWindow)
|
||||
}
|
||||
|
||||
@IBAction func didTapBackButton() {
|
||||
@@ -130,6 +127,7 @@ 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.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))
|
||||
@@ -138,7 +136,6 @@ class ChartStackSection: UIView, ColorModeContainer {
|
||||
|
||||
func setup(controller: BaseChartController, title: String) {
|
||||
self.controller = controller
|
||||
self.headerLabel.text = title
|
||||
|
||||
// Chart
|
||||
chartView.renderers = controller.mainChartRenderers
|
||||
@@ -195,5 +192,10 @@ 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)
|
||||
TimeInterval.animationDurationMultipler = 1.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ public protocol ChartViewRenderer: class {
|
||||
func render(context: CGContext, bounds: CGRect, chartFrame: CGRect)
|
||||
}
|
||||
|
||||
class ChartView: UIView {
|
||||
class ChartView: UIControl {
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
@@ -63,11 +63,18 @@ class ChartView: UIView {
|
||||
var userDidSelectCoordinateClosure: ((CGPoint) -> Void)?
|
||||
var userDidDeselectCoordinateClosure: (() -> Void)?
|
||||
|
||||
private var _isTracking: Bool = false
|
||||
private var touchInitialLocation: CGPoint?
|
||||
override var isTracking: Bool {
|
||||
return self._isTracking
|
||||
}
|
||||
|
||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
if let point = touches.first?.location(in: self) {
|
||||
let fractionPoint = CGPoint(x: (point.x - chartFrame.origin.x) / chartFrame.width,
|
||||
y: (point.y - chartFrame.origin.y) / chartFrame.height)
|
||||
userDidSelectCoordinateClosure?(fractionPoint)
|
||||
self.touchInitialLocation = point
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,15 +83,23 @@ class ChartView: UIView {
|
||||
let fractionPoint = CGPoint(x: (point.x - chartFrame.origin.x) / chartFrame.width,
|
||||
y: (point.y - chartFrame.origin.y) / chartFrame.height)
|
||||
userDidSelectCoordinateClosure?(fractionPoint)
|
||||
|
||||
if let initialPosition = self.touchInitialLocation, abs(initialPosition.x - point.x) > 3.0 {
|
||||
self._isTracking = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
userDidDeselectCoordinateClosure?()
|
||||
self.touchInitialLocation = nil
|
||||
self._isTracking = false
|
||||
}
|
||||
|
||||
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
userDidDeselectCoordinateClosure?()
|
||||
self.touchInitialLocation = nil
|
||||
self._isTracking = false
|
||||
}
|
||||
|
||||
// MARK: Details View
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
//
|
||||
// ChartsStackViewController.swift
|
||||
// GraphTest
|
||||
//
|
||||
// Created by Andrei Salavei on 4/13/19.
|
||||
// Copyright © 2019 Andrei Salavei. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class ChartsStackViewController: UIViewController {
|
||||
@IBOutlet private var stackView: UIStackView!
|
||||
@IBOutlet private var scrollView: UIScrollView!
|
||||
@IBOutlet private var psLabel: UILabel!
|
||||
@IBOutlet private var ppsLabel: UILabel!
|
||||
@IBOutlet private var animationButton: ChartVisibilityItemView!
|
||||
|
||||
private var sections: [ChartStackSection] = []
|
||||
|
||||
private var colorMode: ColorMode = .night
|
||||
private var colorModeButton: UIBarButtonItem!
|
||||
private var performFastAnimation: Bool = false
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
title = "Statistics"
|
||||
colorModeButton = UIBarButtonItem(title: colorMode.switchTitle, style: .plain, target: self, action: #selector(didTapSwitchColorMode))
|
||||
navigationItem.rightBarButtonItem = colorModeButton
|
||||
|
||||
apply(colorMode: colorMode, animated: false)
|
||||
|
||||
self.navigationController?.navigationBar.barStyle = .black
|
||||
self.navigationController?.navigationBar.isTranslucent = false
|
||||
|
||||
self.view.isUserInteractionEnabled = false
|
||||
animationButton.backgroundColor = .clear
|
||||
animationButton.tapClosure = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.setSlowAnimationEnabled(!self.animationButton.isChecked)
|
||||
}
|
||||
self.setSlowAnimationEnabled(false)
|
||||
|
||||
loadChart1()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.view.setNeedsUpdateConstraints()
|
||||
self.view.setNeedsLayout()
|
||||
}
|
||||
}
|
||||
|
||||
func loadChart1() {
|
||||
ChartsDataLoader.overviewData(type: .generalLines, sync: true, success: { collection in
|
||||
let generalLinesChartController = GeneralLinesChartController(chartsCollection: collection)
|
||||
self.addSection(controller: generalLinesChartController, title: "FOLLOWERS")
|
||||
generalLinesChartController.getDetailsData = { date, completion in
|
||||
ChartsDataLoader.detaildData(type: .generalLines, date: date, success: { collection in
|
||||
completion(collection)
|
||||
}, failure: { error in
|
||||
completion(nil)
|
||||
})
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.loadChart2()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func loadChart2() {
|
||||
ChartsDataLoader.overviewData(type: .twoAxisLines, success: { collection in
|
||||
let twoAxisLinesChartController = TwoAxisLinesChartController(chartsCollection: collection)
|
||||
self.addSection(controller: twoAxisLinesChartController, title: "INTERACTIONS")
|
||||
twoAxisLinesChartController.getDetailsData = { date, completion in
|
||||
ChartsDataLoader.detaildData(type: .twoAxisLines, date: date, success: { collection in
|
||||
completion(collection)
|
||||
}, failure: { error in
|
||||
completion(nil)
|
||||
})
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.loadChart3()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func loadChart3() {
|
||||
ChartsDataLoader.overviewData(type: .stackedBars, success: { collection in
|
||||
let stackedBarsChartController = StackedBarsChartController(chartsCollection: collection)
|
||||
self.addSection(controller: stackedBarsChartController, title: "FRUITS")
|
||||
stackedBarsChartController.getDetailsData = { date, completion in
|
||||
ChartsDataLoader.detaildData(type: .stackedBars, date: date, success: { collection in
|
||||
completion(collection)
|
||||
}, failure: { error in
|
||||
completion(nil)
|
||||
})
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.loadChart4()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func loadChart4() {
|
||||
ChartsDataLoader.overviewData(type: .dailyBars, success: { collection in
|
||||
let dailyBarsChartController = DailyBarsChartController(chartsCollection: collection)
|
||||
self.addSection(controller: dailyBarsChartController, title: "VIEWS")
|
||||
dailyBarsChartController.getDetailsData = { date, completion in
|
||||
ChartsDataLoader.detaildData(type: .dailyBars, date: date, success: { collection in
|
||||
completion(collection)
|
||||
}, failure: { error in
|
||||
completion(nil)
|
||||
})
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.loadChart5()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func loadChart5() {
|
||||
ChartsDataLoader.overviewData(type: .percentPie, success: { collection in
|
||||
let percentPieChartController = PercentPieChartController(chartsCollection: collection)
|
||||
self.addSection(controller: percentPieChartController, title: "MORE FRUITS")
|
||||
self.finalizeChartsLoading()
|
||||
})
|
||||
}
|
||||
|
||||
func setSlowAnimationEnabled(_ isEnabled: Bool) {
|
||||
animationButton.setChecked(isChecked: isEnabled, animated: true)
|
||||
if isEnabled {
|
||||
TimeInterval.animationDurationMultipler = 5
|
||||
} else {
|
||||
TimeInterval.animationDurationMultipler = 1
|
||||
}
|
||||
}
|
||||
|
||||
func finalizeChartsLoading() {
|
||||
self.view.isUserInteractionEnabled = true
|
||||
}
|
||||
|
||||
func addSection(controller: BaseChartController, title: String) {
|
||||
let section = Bundle.main.loadNibNamed("ChartStackSection", owner: nil, options: nil)?.first as! ChartStackSection
|
||||
section.frame = UIScreen.main.bounds
|
||||
section.layoutIfNeeded()
|
||||
section.setup(controller: controller, title: title)
|
||||
section.apply(colorMode: colorMode, animated: false)
|
||||
stackView.addArrangedSubview(section)
|
||||
sections.append(section)
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return (colorMode == .day) ? .default : .lightContent
|
||||
}
|
||||
|
||||
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
|
||||
return .fade
|
||||
}
|
||||
|
||||
@objc private func didTapSwitchColorMode() {
|
||||
self.colorMode = self.colorMode == .day ? .night : .day
|
||||
apply(colorMode: self.colorMode, animated: !performFastAnimation)
|
||||
colorModeButton.title = colorMode.switchTitle
|
||||
}
|
||||
}
|
||||
|
||||
extension ChartsStackViewController: UIScrollViewDelegate {
|
||||
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||
performFastAnimation = decelerate
|
||||
}
|
||||
|
||||
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||
performFastAnimation = false
|
||||
}
|
||||
}
|
||||
|
||||
extension ChartsStackViewController: ColorModeContainer {
|
||||
func apply(colorMode: ColorMode, animated: Bool) {
|
||||
|
||||
UIView.perform(animated: animated) {
|
||||
self.psLabel.setTextColor(colorMode.sectionTitleColor, animated: animated && self.psLabel.isVisibleInWindow)
|
||||
self.ppsLabel.setTextColor(colorMode.sectionTitleColor, animated: animated && self.ppsLabel.isVisibleInWindow)
|
||||
self.animationButton.item = ChartVisibilityItem(title: "Enable slow animations",
|
||||
color: colorMode.sectionTitleColor)
|
||||
|
||||
self.view.backgroundColor = colorMode.tableBackgroundColor
|
||||
|
||||
if (animated) {
|
||||
let animation = CATransition()
|
||||
animation.timingFunction = CAMediaTimingFunction.init(name: .linear)
|
||||
animation.type = .fade
|
||||
animation.duration = .defaultDuration
|
||||
self.navigationController?.navigationBar.layer.add(animation, forKey: "kCATransitionColorFade")
|
||||
}
|
||||
|
||||
self.navigationController?.navigationBar.tintColor = colorMode.actionButtonColor
|
||||
self.navigationController?.navigationBar.barTintColor = colorMode.chartBackgroundColor
|
||||
self.navigationController?.navigationBar.titleTextAttributes = [.font: UIFont.systemFont(ofSize: 17, weight: .medium),
|
||||
.foregroundColor: colorMode.viewTintColor]
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
self.setNeedsStatusBarAppearanceUpdate()
|
||||
|
||||
for section in sections {
|
||||
section.apply(colorMode: colorMode, animated: animated && section.isVisibleInWindow)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ColorMode {
|
||||
var switchTitle: String {
|
||||
switch self {
|
||||
case .day:
|
||||
return "Night Mode"
|
||||
case .night:
|
||||
return "Day Mode"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,8 @@ class RangeChartView: UIControl {
|
||||
private let cropFrameView = UIImageView()
|
||||
|
||||
private var selectedMarker: Marker?
|
||||
private var selectedMarkerHorizontalOffet: CGFloat = 0
|
||||
private var selectedMarkerHorizontalOffset: CGFloat = 0
|
||||
private var selectedMarkerInitialLocation: CGPoint?
|
||||
private var isBoundCropHighlighted: Bool = false
|
||||
private var isRangePagingEnabled: Bool = false
|
||||
|
||||
@@ -137,18 +138,22 @@ class RangeChartView: UIControl {
|
||||
|
||||
if abs(locationInView(for: upperBound) - point.x + Constants.markerSelectionRange / 2) < Constants.markerSelectionRange {
|
||||
selectedMarker = .upper
|
||||
selectedMarkerHorizontalOffet = point.x - locationInView(for: upperBound)
|
||||
selectedMarkerHorizontalOffset = point.x - locationInView(for: upperBound)
|
||||
selectedMarkerInitialLocation = point
|
||||
isBoundCropHighlighted = true
|
||||
} else if abs(locationInView(for: lowerBound) - point.x - Constants.markerSelectionRange / 2) < Constants.markerSelectionRange {
|
||||
selectedMarker = .lower
|
||||
selectedMarkerHorizontalOffet = point.x - locationInView(for: lowerBound)
|
||||
selectedMarkerHorizontalOffset = point.x - locationInView(for: lowerBound)
|
||||
selectedMarkerInitialLocation = point
|
||||
isBoundCropHighlighted = true
|
||||
} else if point.x > locationInView(for: lowerBound) && point.x < locationInView(for: upperBound) {
|
||||
selectedMarker = .center
|
||||
selectedMarkerHorizontalOffet = point.x - locationInView(for: lowerBound)
|
||||
selectedMarkerHorizontalOffset = point.x - locationInView(for: lowerBound)
|
||||
selectedMarkerInitialLocation = point
|
||||
isBoundCropHighlighted = true
|
||||
} else {
|
||||
selectedMarker = nil
|
||||
selectedMarkerInitialLocation = nil
|
||||
return
|
||||
}
|
||||
|
||||
@@ -160,10 +165,14 @@ class RangeChartView: UIControl {
|
||||
guard let selectedMarker = selectedMarker else { return }
|
||||
guard let point = touches.first?.location(in: self) else { return }
|
||||
|
||||
let horizontalPosition = point.x - selectedMarkerHorizontalOffet
|
||||
let horizontalPosition = point.x - selectedMarkerHorizontalOffset
|
||||
let fraction = fractionFor(offsetX: horizontalPosition)
|
||||
updateMarkerOffset(selectedMarker, fraction: fraction)
|
||||
|
||||
if let initialPosition = selectedMarkerInitialLocation, abs(initialPosition.x - point.x) > 3.0 {
|
||||
self._isTracking = true
|
||||
}
|
||||
|
||||
sendActions(for: .valueChanged)
|
||||
}
|
||||
|
||||
@@ -175,11 +184,12 @@ class RangeChartView: UIControl {
|
||||
}
|
||||
guard let point = touches.first?.location(in: self) else { return }
|
||||
|
||||
let horizontalPosition = point.x - selectedMarkerHorizontalOffet
|
||||
let horizontalPosition = point.x - selectedMarkerHorizontalOffset
|
||||
let fraction = fractionFor(offsetX: horizontalPosition)
|
||||
updateMarkerOffset(selectedMarker, fraction: fraction)
|
||||
|
||||
self.selectedMarker = nil
|
||||
self.selectedMarkerInitialLocation = nil
|
||||
self.isBoundCropHighlighted = false
|
||||
if bounds.contains(point) {
|
||||
sendActions(for: .touchUpInside)
|
||||
@@ -187,13 +197,22 @@ class RangeChartView: UIControl {
|
||||
sendActions(for: .touchUpOutside)
|
||||
}
|
||||
rangeDidChangeClosure?(lowerBound...upperBound)
|
||||
|
||||
self._isTracking = false
|
||||
}
|
||||
|
||||
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
self.selectedMarker = nil
|
||||
self.selectedMarkerInitialLocation = nil
|
||||
self.isBoundCropHighlighted = false
|
||||
self._isTracking = false
|
||||
sendActions(for: .touchCancel)
|
||||
}
|
||||
|
||||
private var _isTracking: Bool = false
|
||||
override var isTracking: Bool {
|
||||
return self._isTracking
|
||||
}
|
||||
}
|
||||
|
||||
private extension RangeChartView {
|
||||
|
||||
@@ -23,12 +23,19 @@ 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
|
||||
}
|
||||
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 = DailyBarsChartController(chartsCollection: collection)
|
||||
controller = TwoAxisLinesChartController(chartsCollection: collection)
|
||||
} else {
|
||||
controller = GeneralLinesChartController(chartsCollection: collection)
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ extension ChartsCollection {
|
||||
guard axixValuesToSetup.isEmpty == false,
|
||||
chartToSetup.isEmpty == false,
|
||||
chartToSetup.firstIndex(where: { $0.values.count != axixValuesToSetup.count }) == nil else {
|
||||
throw ChartsError.generalConversion("Saniazing: Invalid number of items: \(axixValuesToSetup), \(chartToSetup)")
|
||||
throw ChartsError.generalConversion("Sanitazing: Invalid number of items: \(axixValuesToSetup), \(chartToSetup)")
|
||||
}
|
||||
self.axisValues = axixValuesToSetup
|
||||
self.chartValues = chartToSetup
|
||||
|
||||
@@ -128,7 +128,11 @@ class BaseChartController: ColorModeContainer {
|
||||
fatalError("Abstract")
|
||||
}
|
||||
|
||||
func updateChartRange(_ rangeFraction: ClosedRange<CGFloat>) {
|
||||
func updateChartRangeTitle(animated: Bool) {
|
||||
|
||||
}
|
||||
|
||||
func updateChartRange(_ rangeFraction: ClosedRange<CGFloat>, animated: Bool = true) {
|
||||
fatalError("Abstract")
|
||||
}
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ class BaseLinesChartController: BaseChartController {
|
||||
|
||||
func setupChartCollection(chartsCollection: ChartsCollection, animated: Bool, isZoomed: Bool) {
|
||||
if animated {
|
||||
TimeInterval.setDefaultSuration(.expandAnimationDuration)
|
||||
TimeInterval.setDefaultDuration(.expandAnimationDuration)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .expandAnimationDuration) {
|
||||
TimeInterval.setDefaultSuration(.osXDuration)
|
||||
TimeInterval.setDefaultDuration(.osXDuration)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class BaseLinesChartController: BaseChartController {
|
||||
updateChartRangeTitle(animated: animated)
|
||||
}
|
||||
|
||||
func updateChartRangeTitle(animated: Bool) {
|
||||
override func updateChartRangeTitle(animated: Bool) {
|
||||
let fromDate = Date(timeIntervalSince1970: TimeInterval(zoomedChartRange.lowerBound) + .hour)
|
||||
let toDate = Date(timeIntervalSince1970: TimeInterval(zoomedChartRange.upperBound))
|
||||
if Calendar.utc.startOfDay(for: fromDate) == Calendar.utc.startOfDay(for: toDate) {
|
||||
@@ -64,7 +64,7 @@ class BaseLinesChartController: BaseChartController {
|
||||
isChartInteractionBegun = false
|
||||
}
|
||||
|
||||
override func updateChartRange(_ rangeFraction: ClosedRange<CGFloat>) {
|
||||
override func updateChartRange(_ rangeFraction: ClosedRange<CGFloat>, animated: Bool) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ class GeneralLinesChartController: BaseLinesChartController {
|
||||
return visibleCharts
|
||||
}
|
||||
|
||||
override func updateChartRange(_ rangeFraction: ClosedRange<CGFloat>) {
|
||||
override func updateChartRange(_ rangeFraction: ClosedRange<CGFloat>, animated: Bool) {
|
||||
cancelChartInteraction()
|
||||
|
||||
let horizontalRange = ClosedRange(uncheckedBounds:
|
||||
@@ -183,8 +183,8 @@ class GeneralLinesChartController: BaseLinesChartController {
|
||||
updateChartRangeTitle(animated: true)
|
||||
|
||||
updateMainChartHorizontalRange(range: horizontalRange, animated: false)
|
||||
updateHorizontalLimists(horizontalRange: horizontalRange, animated: true)
|
||||
updateVerticalLimitsAndRange(horizontalRange: horizontalRange, animated: true)
|
||||
updateHorizontalLimists(horizontalRange: horizontalRange, animated: animated)
|
||||
updateVerticalLimitsAndRange(horizontalRange: horizontalRange, animated: animated)
|
||||
}
|
||||
|
||||
func updateMainChartHorizontalRange(range: ClosedRange<CGFloat>, animated: Bool) {
|
||||
|
||||
@@ -194,7 +194,7 @@ class TwoAxisLinesChartController: BaseLinesChartController {
|
||||
self.setupChartCollection(chartsCollection: initialChartCollection, animated: true, isZoomed: false)
|
||||
}
|
||||
|
||||
override func updateChartRange(_ rangeFraction: ClosedRange<CGFloat>) {
|
||||
override func updateChartRange(_ rangeFraction: ClosedRange<CGFloat>, animated: Bool) {
|
||||
cancelChartInteraction()
|
||||
|
||||
let horizontalRange = ClosedRange(uncheckedBounds:
|
||||
@@ -205,8 +205,8 @@ class TwoAxisLinesChartController: BaseLinesChartController {
|
||||
updateChartRangeTitle(animated: true)
|
||||
|
||||
updateMainChartHorizontalRange(range: horizontalRange, animated: false)
|
||||
updateHorizontalLimists(horizontalRange: horizontalRange, animated: true)
|
||||
updateVerticalLimitsAndRange(horizontalRange: horizontalRange, animated: true)
|
||||
updateHorizontalLimists(horizontalRange: horizontalRange, animated: animated)
|
||||
updateVerticalLimitsAndRange(horizontalRange: horizontalRange, animated: animated)
|
||||
}
|
||||
|
||||
func updateMainChartHorizontalRange(range: ClosedRange<CGFloat>, animated: Bool) {
|
||||
|
||||
@@ -93,9 +93,9 @@ class PercentPieChartController: BaseChartController {
|
||||
|
||||
func switchToChart(chartsCollection: ChartsCollection, isZoomed: Bool, animated: Bool) {
|
||||
if animated {
|
||||
TimeInterval.setDefaultSuration(.expandAnimationDuration)
|
||||
TimeInterval.setDefaultDuration(.expandAnimationDuration)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .expandAnimationDuration) {
|
||||
TimeInterval.setDefaultSuration(.osXDuration)
|
||||
TimeInterval.setDefaultDuration(.osXDuration)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ class PercentPieChartController: BaseChartController {
|
||||
})
|
||||
}
|
||||
|
||||
override func updateChartRange(_ rangeFraction: ClosedRange<CGFloat>) {
|
||||
override func updateChartRange(_ rangeFraction: ClosedRange<CGFloat>, animated: Bool) {
|
||||
if isZoomed {
|
||||
return pieController.chartRangeFractionDidUpdated(rangeFraction)
|
||||
} else {
|
||||
|
||||
@@ -84,9 +84,9 @@ class DailyBarsChartController: BaseChartController {
|
||||
|
||||
func switchToChart(chartsCollection: ChartsCollection, isZoomed: Bool, animated: Bool) {
|
||||
if animated {
|
||||
TimeInterval.setDefaultSuration(.expandAnimationDuration)
|
||||
TimeInterval.setDefaultDuration(.expandAnimationDuration)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .expandAnimationDuration) {
|
||||
TimeInterval.setDefaultSuration(.osXDuration)
|
||||
TimeInterval.setDefaultDuration(.osXDuration)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ class DailyBarsChartController: BaseChartController {
|
||||
switchToChart(chartsCollection: barsController.chartsCollection, isZoomed: false, animated: true)
|
||||
}
|
||||
|
||||
override func updateChartRange(_ rangeFraction: ClosedRange<CGFloat>) {
|
||||
override func updateChartRange(_ rangeFraction: ClosedRange<CGFloat>, animated: Bool) {
|
||||
if isZoomed {
|
||||
return linesController.chartRangeFractionDidUpdated(rangeFraction)
|
||||
} else {
|
||||
|
||||
@@ -79,9 +79,9 @@ class StackedBarsChartController: BaseChartController {
|
||||
|
||||
func switchToChart(chartsCollection: ChartsCollection, isZoomed: Bool, animated: Bool) {
|
||||
if animated {
|
||||
TimeInterval.setDefaultSuration(.expandAnimationDuration)
|
||||
TimeInterval.setDefaultDuration(.expandAnimationDuration)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .expandAnimationDuration) {
|
||||
TimeInterval.setDefaultSuration(.osXDuration)
|
||||
TimeInterval.setDefaultDuration(.osXDuration)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ class StackedBarsChartController: BaseChartController {
|
||||
switchToChart(chartsCollection: barsController.chartsCollection, isZoomed: false, animated: true)
|
||||
}
|
||||
|
||||
override func updateChartRange(_ rangeFraction: ClosedRange<CGFloat>) {
|
||||
override func updateChartRange(_ rangeFraction: ClosedRange<CGFloat>, animated: Bool) {
|
||||
if isZoomed {
|
||||
return zoomedBarsController.chartRangeFractionDidUpdated(rangeFraction)
|
||||
} else {
|
||||
|
||||
@@ -21,7 +21,7 @@ extension TimeInterval {
|
||||
}
|
||||
private static var innerDefaultDuration: TimeInterval = osXDuration
|
||||
|
||||
static func setDefaultSuration(_ duration: TimeInterval) {
|
||||
static func setDefaultDuration(_ duration: TimeInterval) {
|
||||
innerDefaultDuration = duration
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,6 +354,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
|
||||
let trackingRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.trackingGesture(_:)))
|
||||
trackingRecognizer.delegate = self
|
||||
trackingRecognizer.cancelsTouchesInView = false
|
||||
self.view.addGestureRecognizer(trackingRecognizer)
|
||||
|
||||
self.view.addGestureRecognizer(ListViewReorderingGestureRecognizer(shouldBegin: { [weak self] point in
|
||||
|
||||
@@ -28,6 +28,11 @@ public final class ListViewScroller: UIScrollView, UIGestureRecognizerDelegate {
|
||||
return gestureRecognizer.numberOfTouches < 2
|
||||
}
|
||||
}
|
||||
|
||||
if let view = gestureRecognizer.view?.hitTest(gestureRecognizer.location(in: gestureRecognizer.view), with: nil) as? UIControl {
|
||||
return !view.isTracking
|
||||
}
|
||||
|
||||
return true
|
||||
} else {
|
||||
return true
|
||||
|
||||
@@ -36,28 +36,28 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
case overview(PresentationTheme, ChannelStats)
|
||||
|
||||
case growthTitle(PresentationTheme, String)
|
||||
case growthGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, String, ChannelStatsGraph)
|
||||
case growthGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
|
||||
|
||||
case followersTitle(PresentationTheme, String)
|
||||
case followersGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, String, ChannelStatsGraph)
|
||||
case followersGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
|
||||
|
||||
case notificationsTitle(PresentationTheme, String)
|
||||
case notificationsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, String, ChannelStatsGraph)
|
||||
case notificationsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
|
||||
|
||||
case viewsByHourTitle(PresentationTheme, String)
|
||||
case viewsByHourGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, String, ChannelStatsGraph)
|
||||
case viewsByHourGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
|
||||
|
||||
case postInteractionsTitle(PresentationTheme, String)
|
||||
case postInteractionsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, String, ChannelStatsGraph)
|
||||
case postInteractionsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
|
||||
|
||||
case viewsBySourceTitle(PresentationTheme, String)
|
||||
case viewsBySourceGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, String, ChannelStatsGraph)
|
||||
case viewsBySourceGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
|
||||
|
||||
case followersBySourceTitle(PresentationTheme, String)
|
||||
case followersBySourceGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, String, ChannelStatsGraph)
|
||||
case followersBySourceGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
|
||||
|
||||
case languagesTitle(PresentationTheme, String)
|
||||
case languagesGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, String, ChannelStatsGraph)
|
||||
case languagesGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, ChannelStatsGraph)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
@@ -143,8 +143,8 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .growthGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsText, lhsGraph):
|
||||
if case let .growthGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsText, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsText == rhsText, lhsGraph == rhsGraph {
|
||||
case let .growthGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
|
||||
if case let .growthGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@@ -155,8 +155,8 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .followersGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsText, lhsGraph):
|
||||
if case let .followersGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsText, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsText == rhsText, lhsGraph == rhsGraph {
|
||||
case let .followersGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
|
||||
if case let .followersGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@@ -167,8 +167,8 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .notificationsGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsText, lhsGraph):
|
||||
if case let .notificationsGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsText, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsText == rhsText, lhsGraph == rhsGraph {
|
||||
case let .notificationsGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
|
||||
if case let .notificationsGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@@ -179,8 +179,8 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .viewsByHourGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsText, lhsGraph):
|
||||
if case let .viewsByHourGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsText, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsText == rhsText, lhsGraph == rhsGraph {
|
||||
case let .viewsByHourGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
|
||||
if case let .viewsByHourGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@@ -191,8 +191,8 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .postInteractionsGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsText, lhsGraph):
|
||||
if case let .postInteractionsGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsText, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsText == rhsText, lhsGraph == rhsGraph {
|
||||
case let .postInteractionsGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
|
||||
if case let .postInteractionsGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@@ -203,8 +203,8 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .viewsBySourceGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsText, lhsGraph):
|
||||
if case let .viewsBySourceGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsText, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsText == rhsText, lhsGraph == rhsGraph {
|
||||
case let .viewsBySourceGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
|
||||
if case let .viewsBySourceGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@@ -215,8 +215,8 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .followersBySourceGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsText, lhsGraph):
|
||||
if case let .followersBySourceGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsText, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsText == rhsText, lhsGraph == rhsGraph {
|
||||
case let .followersBySourceGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
|
||||
if case let .followersBySourceGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@@ -227,8 +227,8 @@ private enum StatsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .languagesGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsText, lhsGraph):
|
||||
if case let .languagesGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsText, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsText == rhsText, lhsGraph == rhsGraph {
|
||||
case let .languagesGraph(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsGraph):
|
||||
if case let .languagesGraph(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsGraph) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsGraph == rhsGraph {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@@ -254,15 +254,15 @@ 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, title, graph),
|
||||
let .followersGraph(theme, strings, dateTimeFormat, title, graph),
|
||||
let .notificationsGraph(theme, strings, dateTimeFormat, title, graph),
|
||||
let .viewsByHourGraph(theme, strings, dateTimeFormat, title, graph),
|
||||
let .postInteractionsGraph(theme, strings, dateTimeFormat, title, graph),
|
||||
let .viewsBySourceGraph(theme, strings, dateTimeFormat, title, graph),
|
||||
let .followersBySourceGraph(theme, strings, dateTimeFormat, title, graph),
|
||||
let .languagesGraph(theme, strings, dateTimeFormat, title, graph):
|
||||
return StatsGraphItem(presentationData: presentationData, title: title, graph: graph, 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -271,17 +271,20 @@ private func statsControllerEntries(data: ChannelStats?, presentationData: Prese
|
||||
var entries: [StatsEntry] = []
|
||||
|
||||
if let data = data {
|
||||
entries.append(.overviewHeader(presentationData.theme, "OVERVIEW"))
|
||||
entries.append(.overviewHeader(presentationData.theme, presentationData.strings.Stats_Overview))
|
||||
entries.append(.overview(presentationData.theme, data))
|
||||
|
||||
entries.append(.growthTitle(presentationData.theme, "GROWTH"))
|
||||
entries.append(.growthGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, "Growth", data.growthGraph))
|
||||
entries.append(.growthTitle(presentationData.theme, presentationData.strings.Stats_GrowthTitle))
|
||||
entries.append(.growthGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.growthGraph))
|
||||
|
||||
entries.append(.followersTitle(presentationData.theme, "FOLLOWERS"))
|
||||
entries.append(.followersGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, "Followers", data.followersGraph))
|
||||
entries.append(.followersTitle(presentationData.theme, presentationData.strings.Stats_FollowersTitle))
|
||||
entries.append(.followersGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.followersGraph))
|
||||
|
||||
entries.append(.notificationsTitle(presentationData.theme, "NOTIFICATIONS"))
|
||||
entries.append(.notificationsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, "Notifications", data.muteGraph))
|
||||
entries.append(.notificationsTitle(presentationData.theme, presentationData.strings.Stats_NotificationsTitle))
|
||||
entries.append(.notificationsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.muteGraph))
|
||||
|
||||
entries.append(.postInteractionsTitle(presentationData.theme, presentationData.strings.Stats_InteractionsTitle))
|
||||
entries.append(.postInteractionsGraph(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, data.interactionsGraph))
|
||||
}
|
||||
|
||||
return entries
|
||||
@@ -321,8 +324,13 @@ public func channelStatsController(context: AccountContext, peer: Peer, cachedPe
|
||||
let signal = combineLatest(context.sharedContext.presentationData, dataPromise.get())
|
||||
|> deliverOnMainQueue
|
||||
|> map { presentationData, data -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
var emptyStateItem: ItemListControllerEmptyStateItem?
|
||||
if data == nil {
|
||||
emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme)
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.ChannelInfo_Stats), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: statsControllerEntries(data: data, presentationData: presentationData), style: .blocks, emptyStateItem: nil, crossfadeState: false, animateChanges: false)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: statsControllerEntries(data: data, presentationData: presentationData), style: .blocks, emptyStateItem: emptyStateItem, crossfadeState: false, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
@@ -345,5 +353,6 @@ public func channelStatsController(context: AccountContext, peer: Peer, cachedPe
|
||||
controller.present(c, in: .window(.root), with: a)
|
||||
}
|
||||
}
|
||||
|
||||
return controller
|
||||
}
|
||||
|
||||
@@ -12,14 +12,12 @@ import Charts
|
||||
|
||||
class StatsGraphItem: ListViewItem, ItemListItem {
|
||||
let presentationData: ItemListPresentationData
|
||||
let title: String
|
||||
let graph: ChannelStatsGraph
|
||||
let sectionId: ItemListSectionId
|
||||
let style: ItemListStyle
|
||||
|
||||
init(presentationData: ItemListPresentationData, title: String, graph: ChannelStatsGraph, sectionId: ItemListSectionId, style: ItemListStyle) {
|
||||
init(presentationData: ItemListPresentationData, graph: ChannelStatsGraph, sectionId: ItemListSectionId, style: ItemListStyle) {
|
||||
self.presentationData = presentationData
|
||||
self.title = title
|
||||
self.graph = graph
|
||||
self.sectionId = sectionId
|
||||
self.style = style
|
||||
@@ -117,12 +115,12 @@ class StatsGraphItemNode: ListViewItemNode {
|
||||
case .plain:
|
||||
itemBackgroundColor = item.presentationData.theme.list.plainBackgroundColor
|
||||
itemSeparatorColor = item.presentationData.theme.list.itemPlainSeparatorColor
|
||||
contentSize = CGSize(width: params.width, height: 320.0)
|
||||
contentSize = CGSize(width: params.width, height: 340.0)
|
||||
insets = itemListNeighborsPlainInsets(neighbors)
|
||||
case .blocks:
|
||||
itemBackgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor
|
||||
itemSeparatorColor = item.presentationData.theme.list.itemBlocksSeparatorColor
|
||||
contentSize = CGSize(width: params.width, height: 320.0)
|
||||
contentSize = CGSize(width: params.width, height: 340.0)
|
||||
insets = itemListNeighborsGroupedInsets(neighbors)
|
||||
}
|
||||
|
||||
@@ -138,10 +136,6 @@ class StatsGraphItemNode: ListViewItemNode {
|
||||
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
|
||||
}
|
||||
|
||||
if let updatedGraph = updatedGraph, case let .Loaded(data) = updatedGraph {
|
||||
strongSelf.chartNode.setup(data)
|
||||
}
|
||||
|
||||
switch item.style {
|
||||
case .plain:
|
||||
if strongSelf.backgroundNode.supernode != nil {
|
||||
@@ -179,12 +173,17 @@ class StatsGraphItemNode: ListViewItemNode {
|
||||
bottomStripeInset = 0.0
|
||||
}
|
||||
|
||||
strongSelf.chartNode.frame = CGRect(origin: CGPoint(x: leftInset, y: -30.0), size: CGSize(width: layout.size.width - leftInset - rightInset, height: 350.0))
|
||||
strongSelf.chartNode.frame = CGRect(origin: CGPoint(x: leftInset, y: -10.0), size: CGSize(width: layout.size.width - leftInset - rightInset, height: 350.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))
|
||||
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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -151,32 +151,60 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
updatedTheme = item.presentationData.theme
|
||||
}
|
||||
|
||||
let valueFont = Font.regular(item.presentationData.fontSize.itemListBaseHeaderFontSize)
|
||||
let valueFont = Font.semibold(item.presentationData.fontSize.itemListBaseFontSize)
|
||||
let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseHeaderFontSize)
|
||||
let deltaFont = Font.regular(item.presentationData.fontSize.itemListBaseHeaderFontSize)
|
||||
|
||||
let (followersValueLabelLayout, followersValueLabelApply) = makeFollowersValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "221K", font: valueFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (followersValueLabelLayout, followersValueLabelApply) = makeFollowersValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: compactNumericCountString(Int(item.stats.followers.current)), font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 140.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (viewsPerPostValueLabelLayout, viewsPerPostValueLabelApply) = makeViewsPerPostValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "120K", font: valueFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (viewsPerPostValueLabelLayout, viewsPerPostValueLabelApply) = makeViewsPerPostValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: compactNumericCountString(Int(item.stats.viewsPerPost.current)), font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 140.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (sharesPerPostValueLabelLayout, sharesPerPostValueLabelApply) = makeSharesPerPostValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "350", font: valueFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (sharesPerPostValueLabelLayout, sharesPerPostValueLabelApply) = makeSharesPerPostValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: compactNumericCountString(Int(item.stats.sharesPerPost.current)), font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (enabledNotificationsValueLabelLayout, enabledNotificationsValueLabelApply) = makeEnabledNotificationsValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "22.77%", font: valueFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
var enabledNotifications: Double = 0.0
|
||||
if item.stats.enabledNotifications.total > 0 {
|
||||
enabledNotifications = item.stats.enabledNotifications.value / item.stats.enabledNotifications.total
|
||||
}
|
||||
|
||||
let (enabledNotificationsValueLabelLayout, enabledNotificationsValueLabelApply) = makeEnabledNotificationsValueLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: String(format: "%.02f%%", enabledNotifications * 100.0), font: valueFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (followersTitleLabelLayout, followersTitleLabelApply) = makeFollowersTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Followers", font: valueFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (followersTitleLabelLayout, followersTitleLabelApply) = makeFollowersTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Followers, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 140.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (viewsPerPostTitleLabelLayout, viewsPerPostTitleLabelApply) = makeViewsPerPostTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Views Per Post", font: valueFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (viewsPerPostTitleLabelLayout, viewsPerPostTitleLabelApply) = makeViewsPerPostTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_ViewsPerPost, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 140.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (sharesPerPostTitleLabelLayout, sharesPerPostTitleLabelApply) = makeSharesPerPostTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Shares Per Post", font: valueFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (sharesPerPostTitleLabelLayout, sharesPerPostTitleLabelApply) = makeSharesPerPostTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_SharesPerPost, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 140.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (enabledNotificationsTitleLabelLayout, enabledNotificationsTitleLabelApply) = makeEnabledNotificationsTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Enabled Notifications", font: valueFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (enabledNotificationsTitleLabelLayout, enabledNotificationsTitleLabelApply) = makeEnabledNotificationsTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_EnabledNotifications, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 140.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (followersDeltaLabelLayout, followersDeltaLabelApply) = makeFollowersDeltaLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "+474 (0.21%)", font: valueFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let followersDeltaValue = item.stats.followers.current - item.stats.followers.previous
|
||||
let followersDeltaCompact = compactNumericCountString(abs(Int(followersDeltaValue)))
|
||||
let followersDelta = followersDeltaValue > 0 ? "+\(followersDeltaCompact)" : "-\(followersDeltaCompact)"
|
||||
var followersDeltaPercentage = 0.0
|
||||
if item.stats.followers.previous > 0.0 {
|
||||
followersDeltaPercentage = abs(followersDeltaValue / item.stats.followers.previous)
|
||||
}
|
||||
|
||||
let (viewsPerPostDeltaLabelLayout, viewsPerPostDeltaLabelApply) = makeViewsPerPostDeltaLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "-14K (10.68%)", font: valueFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (followersDeltaLabelLayout, followersDeltaLabelApply) = makeFollowersDeltaLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: String(format: "%@ (%.02f%%)", followersDelta, followersDeltaPercentage * 100.0), font: deltaFont, textColor: followersDeltaValue > 0.0 ? item.presentationData.theme.list.freeTextSuccessColor : item.presentationData.theme.list.freeTextErrorColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 140.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (sharesPerPostDeltaLabelLayout, sharesPerPostDeltaLabelApply) = makeSharesPerPostDeltaLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "-134 (27.68%)", font: valueFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let viewsPerPostDeltaValue = item.stats.viewsPerPost.current - item.stats.viewsPerPost.previous
|
||||
let viewsPerPostDeltaCompact = compactNumericCountString(abs(Int(viewsPerPostDeltaValue)))
|
||||
let viewsPerPostDelta = viewsPerPostDeltaValue > 0 ? "+\(viewsPerPostDeltaCompact)" : "-\(viewsPerPostDeltaCompact)"
|
||||
var viewsPerPostDeltaPercentage = 0.0
|
||||
if item.stats.viewsPerPost.previous > 0.0 {
|
||||
viewsPerPostDeltaPercentage = abs(viewsPerPostDeltaValue / item.stats.viewsPerPost.previous)
|
||||
}
|
||||
|
||||
let (viewsPerPostDeltaLabelLayout, viewsPerPostDeltaLabelApply) = makeViewsPerPostDeltaLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: String(format: "%@ (%.02f%%)", viewsPerPostDelta, viewsPerPostDeltaPercentage * 100.0), font: deltaFont, textColor: viewsPerPostDeltaValue > 0.0 ? item.presentationData.theme.list.freeTextSuccessColor : item.presentationData.theme.list.freeTextErrorColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 140.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let sharesPerPostDeltaValue = item.stats.sharesPerPost.current - item.stats.sharesPerPost.previous
|
||||
let sharesPerPostDeltaCompact = compactNumericCountString(abs(Int(sharesPerPostDeltaValue)))
|
||||
let sharesPerPostDelta = sharesPerPostDeltaValue > 0 ? "+\(sharesPerPostDeltaCompact)" : "-\(sharesPerPostDeltaCompact)"
|
||||
var sharesPerPostDeltaPercentage = 0.0
|
||||
if item.stats.sharesPerPost.previous > 0.0 {
|
||||
sharesPerPostDeltaPercentage = abs(sharesPerPostDeltaValue / item.stats.sharesPerPost.previous)
|
||||
}
|
||||
|
||||
let (sharesPerPostDeltaLabelLayout, sharesPerPostDeltaLabelApply) = makeSharesPerPostDeltaLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: String(format: "%@ (%.02f%%)", sharesPerPostDelta, sharesPerPostDeltaPercentage * 100.0), font: deltaFont, textColor: sharesPerPostDeltaValue > 0.0 ? item.presentationData.theme.list.freeTextSuccessColor : item.presentationData.theme.list.freeTextErrorColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 140.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let contentSize: CGSize
|
||||
let insets: UIEdgeInsets
|
||||
@@ -265,13 +293,28 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight))
|
||||
}
|
||||
|
||||
strongSelf.followersValueLabel.frame = CGRect(origin: CGPoint(x: leftInset, y: 7.0), size: followersValueLabelLayout.size)
|
||||
let horizontalSpacing: CGFloat = 4.0
|
||||
let verticalSpacing: CGFloat = 70.0
|
||||
let topInset: CGFloat = 14.0
|
||||
let sideInset: CGFloat = 16.0
|
||||
|
||||
strongSelf.viewsPerPostValueLabel.frame = CGRect(origin: CGPoint(x: leftInset, y: 44.0), size: viewsPerPostValueLabelLayout.size)
|
||||
strongSelf.followersValueLabel.frame = CGRect(origin: CGPoint(x: sideInset + leftInset, y: topInset), size: followersValueLabelLayout.size)
|
||||
strongSelf.followersTitleLabel.frame = CGRect(origin: CGPoint(x: sideInset + leftInset, y: strongSelf.followersValueLabel.frame.maxY), size: followersTitleLabelLayout.size)
|
||||
strongSelf.followersDeltaLabel.frame = CGRect(origin: CGPoint(x: strongSelf.followersValueLabel.frame.maxX + horizontalSpacing, y: strongSelf.followersValueLabel.frame.minY + 4.0), size: followersDeltaLabelLayout.size)
|
||||
|
||||
strongSelf.sharesPerPostValueLabel.frame = CGRect(origin: CGPoint(x: layout.size.width / 2.0, y: 44.0), size: sharesPerPostValueLabelLayout.size)
|
||||
strongSelf.viewsPerPostValueLabel.frame = CGRect(origin: CGPoint(x: sideInset + leftInset, y: verticalSpacing), size: viewsPerPostValueLabelLayout.size)
|
||||
strongSelf.viewsPerPostTitleLabel.frame = CGRect(origin: CGPoint(x: sideInset + leftInset, y: strongSelf.viewsPerPostValueLabel.frame.maxY), size: viewsPerPostTitleLabelLayout.size)
|
||||
strongSelf.viewsPerPostDeltaLabel.frame = CGRect(origin: CGPoint(x: strongSelf.viewsPerPostValueLabel.frame.maxX + horizontalSpacing, y: strongSelf.viewsPerPostValueLabel.frame.minY + 4.0), size: viewsPerPostDeltaLabelLayout.size)
|
||||
|
||||
strongSelf.enabledNotificationsValueLabel.frame = CGRect(origin: CGPoint(x: layout.size.width / 2.0, y: 7.0), size: enabledNotificationsValueLabelLayout.size)
|
||||
let rightColumnX = max(layout.size.width / 2.0, max(strongSelf.followersDeltaLabel.frame.maxX, strongSelf.viewsPerPostDeltaLabel.frame.maxX) + horizontalSpacing)
|
||||
|
||||
strongSelf.sharesPerPostValueLabel.frame = CGRect(origin: CGPoint(x: rightColumnX, y: verticalSpacing), size: sharesPerPostValueLabelLayout.size)
|
||||
strongSelf.enabledNotificationsValueLabel.frame = CGRect(origin: CGPoint(x: rightColumnX, y: topInset), size: enabledNotificationsValueLabelLayout.size)
|
||||
|
||||
strongSelf.sharesPerPostTitleLabel.frame = CGRect(origin: CGPoint(x: rightColumnX, y: strongSelf.sharesPerPostValueLabel.frame.maxY), size: sharesPerPostTitleLabelLayout.size)
|
||||
strongSelf.enabledNotificationsTitleLabel.frame = CGRect(origin: CGPoint(x: rightColumnX, y: strongSelf.enabledNotificationsValueLabel.frame.maxY), size: enabledNotificationsTitleLabelLayout.size)
|
||||
|
||||
strongSelf.sharesPerPostDeltaLabel.frame = CGRect(origin: CGPoint(x: strongSelf.sharesPerPostValueLabel.frame.maxX + horizontalSpacing, y: strongSelf.sharesPerPostValueLabel.frame.minY + 4.0), size: sharesPerPostDeltaLabelLayout.size)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ public struct ChannelStatsValue: Equatable {
|
||||
}
|
||||
|
||||
public struct ChannelStatsPercentValue: Equatable {
|
||||
public let fraction: Double
|
||||
public let value: Double
|
||||
public let total: Double
|
||||
}
|
||||
|
||||
@@ -133,8 +133,7 @@ public struct ChannelStatsContextState: Equatable {
|
||||
}
|
||||
|
||||
private func requestStats(network: Network, datacenterId: Int32, peer: Peer, dark: Bool = false) -> Signal<ChannelStats?, NoError> {
|
||||
return .never()
|
||||
/*guard let inputChannel = apiInputChannel(peer) else {
|
||||
guard let inputChannel = apiInputChannel(peer) else {
|
||||
return .never()
|
||||
}
|
||||
|
||||
@@ -160,12 +159,11 @@ private func requestStats(network: Network, datacenterId: Int32, peer: Peer, dar
|
||||
}
|
||||
|> `catch` { _ -> Signal<ChannelStats?, NoError> in
|
||||
return .single(nil)
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
private func requestGraph(network: Network, datacenterId: Int32, token: String) -> Signal<ChannelStatsGraph?, NoError> {
|
||||
return .never()
|
||||
/*let signal: Signal<Api.StatsGraph, MTRpcError>
|
||||
let signal: Signal<Api.StatsGraph, MTRpcError>
|
||||
if network.datacenterId != datacenterId {
|
||||
signal = network.download(datacenterId: Int(datacenterId), isMedia: false, tag: nil)
|
||||
|> castError(MTRpcError.self)
|
||||
@@ -182,7 +180,7 @@ private func requestGraph(network: Network, datacenterId: Int32, token: String)
|
||||
}
|
||||
|> `catch` { _ -> Signal<ChannelStatsGraph?, NoError> in
|
||||
return .single(nil)
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
private final class ChannelStatsContextImpl {
|
||||
@@ -363,7 +361,7 @@ public final class ChannelStatsContext {
|
||||
}
|
||||
}
|
||||
|
||||
/*extension ChannelStatsGraph {
|
||||
extension ChannelStatsGraph {
|
||||
init(apiStatsGraph: Api.StatsGraph) {
|
||||
switch apiStatsGraph {
|
||||
case let .statsGraph(json):
|
||||
@@ -411,7 +409,7 @@ extension ChannelStatsPercentValue {
|
||||
init(apiPercentValue: Api.StatsPercentValue) {
|
||||
switch apiPercentValue {
|
||||
case let .statsPercentValue(part, total):
|
||||
self = ChannelStatsPercentValue(fraction: part, total: total)
|
||||
self = ChannelStatsPercentValue(value: part, total: total)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -424,4 +422,3 @@ extension ChannelStats {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -319,7 +319,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
||||
}
|
||||
|
||||
switch fullChat {
|
||||
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, _, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, _, pts):
|
||||
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, _, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, pts):
|
||||
var channelFlags = CachedChannelFlags()
|
||||
if (flags & (1 << 3)) != 0 {
|
||||
channelFlags.insert(.canDisplayParticipants)
|
||||
@@ -450,7 +450,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
||||
.withUpdatedSlowModeTimeout(slowmodeSeconds)
|
||||
.withUpdatedSlowModeValidUntilTimestamp(slowmodeNextSendDate)
|
||||
.withUpdatedHasScheduledMessages(hasScheduledMessages)
|
||||
// .withUpdatedStatsDatacenterId(statsDc ?? 0)
|
||||
.withUpdatedStatsDatacenterId(statsDc ?? 0)
|
||||
})
|
||||
|
||||
if let minAvailableMessageId = minAvailableMessageId, minAvailableMessageIdUpdated {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -202,6 +202,7 @@ framework(
|
||||
"//submodules/SemanticStatusNode:SemanticStatusNode",
|
||||
"//submodules/AccountUtils:AccountUtils",
|
||||
"//submodules/Svg:Svg",
|
||||
"//submodules/StatisticsUI:StatisticsUI",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
||||
@@ -1362,32 +1362,61 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (getBankCardInfo(account: strongSelf.context.account, cardNumber: number)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] info in
|
||||
if let strongSelf = self, let info = info {
|
||||
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
|
||||
var items: [ActionSheetItem] = []
|
||||
items.append(ActionSheetTextItem(title: info.title))
|
||||
for url in info.urls {
|
||||
items.append(ActionSheetButtonItem(title: url.title, color: .accent, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerInteraction?.openUrl(url.url, false, false, message)
|
||||
}
|
||||
}))
|
||||
}
|
||||
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_LinkDialogCopy, color: .accent, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
UIPasteboard.general.string = number
|
||||
}))
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])])
|
||||
strongSelf.present(actionSheet, in: .window(.root))
|
||||
}
|
||||
})
|
||||
// var signal = getBankCardInfo(account: strongSelf.context.account, cardNumber: number)
|
||||
//
|
||||
// var cancelImpl: (() -> Void)?
|
||||
// let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
// let progressSignal = Signal<Never, NoError> { subscriber in
|
||||
// let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
||||
// cancelImpl?()
|
||||
// }))
|
||||
// strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
// return ActionDisposable { [weak controller] in
|
||||
// Queue.mainQueue().async() {
|
||||
// controller?.dismiss()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// |> runOn(Queue.mainQueue())
|
||||
// |> delay(0.15, queue: Queue.mainQueue())
|
||||
// let progressDisposable = progressSignal.start()
|
||||
//
|
||||
// signal = signal
|
||||
// |> afterDisposed {
|
||||
// Queue.mainQueue().async {
|
||||
// progressDisposable.dispose()
|
||||
// }
|
||||
// }
|
||||
// cancelImpl = {
|
||||
// disposable.set(nil)
|
||||
// }
|
||||
// disposable.set((signal
|
||||
// |> deliverOnMainQueue).start(next: { [weak self] info in
|
||||
// if let strongSelf = self, let info = info {
|
||||
// let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
|
||||
// var items: [ActionSheetItem] = []
|
||||
// items.append(ActionSheetTextItem(title: info.title))
|
||||
// for url in info.urls {
|
||||
// items.append(ActionSheetButtonItem(title: url.title, color: .accent, action: { [weak actionSheet] in
|
||||
// actionSheet?.dismissAnimated()
|
||||
// if let strongSelf = self {
|
||||
// strongSelf.controllerInteraction?.openUrl(url.url, false, false, message)
|
||||
// }
|
||||
// }))
|
||||
// }
|
||||
// items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Conversation_LinkDialogCopy, color: .accent, action: { [weak actionSheet] in
|
||||
// actionSheet?.dismissAnimated()
|
||||
// UIPasteboard.general.string = number
|
||||
// }))
|
||||
// actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||
// ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
// actionSheet?.dismissAnimated()
|
||||
// })
|
||||
// ])])
|
||||
// strongSelf.present(actionSheet, in: .window(.root))
|
||||
// }
|
||||
// }))
|
||||
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import LocationResources
|
||||
import LocationUI
|
||||
import Geocoding
|
||||
import TextFormat
|
||||
import StatisticsUI
|
||||
|
||||
protocol PeerInfoScreenItem: class {
|
||||
var id: AnyHashable { get }
|
||||
@@ -2215,6 +2216,13 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
}
|
||||
}
|
||||
} else if let channel = peer as? TelegramChannel {
|
||||
if let cachedData = self.data?.cachedData as? CachedChannelData, cachedData.flags.contains(.canViewStats) {
|
||||
items.append(ActionSheetButtonItem(title: presentationData.strings.ChannelInfo_Stats, color: .accent, action: { [weak self] in
|
||||
dismissAction()
|
||||
self?.openStats()
|
||||
}))
|
||||
}
|
||||
|
||||
var canReport = true
|
||||
if channel.isVerified {
|
||||
canReport = false
|
||||
@@ -2691,6 +2699,15 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
})
|
||||
}
|
||||
|
||||
private func openStats() {
|
||||
guard let controller = self.controller, let data = self.data, let peer = data.peer, let cachedData = data.cachedData else {
|
||||
return
|
||||
}
|
||||
self.view.endEditing(true)
|
||||
|
||||
controller.push(channelStatsController(context: self.context, peer: peer, cachedPeerData: cachedData))
|
||||
}
|
||||
|
||||
private func openReport(user: Bool) {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user