mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-08 08:31:13 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
# Conflicts: # submodules/TelegramCore/Sources/ChannelStatistics.swift
This commit is contained in:
commit
e38a69235d
1
.gitignore
vendored
1
.gitignore
vendored
@ -59,3 +59,4 @@ bazel-telegram-ios/*
|
||||
bazel-testlogs
|
||||
bazel-testlogs/*
|
||||
build-input/data/*
|
||||
build-input/gen/*
|
||||
|
@ -5352,3 +5352,14 @@ Any member of this group will be able to see messages in the channel.";
|
||||
|
||||
"ChatList.EmptyChatListNewMessage" = "New Message";
|
||||
"ChatList.EmptyChatListEditFilter" = "Edit Filter";
|
||||
|
||||
"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
|
||||
|
@ -74,7 +74,7 @@ class BaseChartController: ColorModeContainer {
|
||||
init(chartsCollection: ChartsCollection) {
|
||||
self.initialChartsCollection = chartsCollection
|
||||
}
|
||||
|
||||
|
||||
var mainChartRenderers: [ChartViewRenderer] {
|
||||
fatalError("Abstract")
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -652,6 +652,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
}
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.chatListNode.present = { [weak self] c in
|
||||
if let strongSelf = self {
|
||||
self?.present(c, in: .window(.root))
|
||||
}
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.chatListNode.toggleArchivedFolderHiddenByDefault = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
@ -235,7 +235,9 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
if let requestAddContact = self?.requestAddContact {
|
||||
requestAddContact(phoneNumber)
|
||||
}
|
||||
}, peerContextAction: self.peerContextAction), cancel: { [weak self] in
|
||||
}, peerContextAction: self.peerContextAction, present: { [weak self] c in
|
||||
self?.controller?.present(c, in: .window(.root))
|
||||
}), cancel: { [weak self] in
|
||||
if let requestDeactivateSearch = self?.requestDeactivateSearch {
|
||||
requestDeactivateSearch()
|
||||
}
|
||||
|
@ -627,7 +627,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
|
||||
private let filter: ChatListNodePeersFilter
|
||||
|
||||
public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, openPeer originalOpenPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?) {
|
||||
public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, openPeer originalOpenPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController) -> Void) {
|
||||
self.context = context
|
||||
self.filter = filter
|
||||
self.dimNode = ASDisplayNode()
|
||||
@ -1043,6 +1043,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
case .groupReference:
|
||||
gesture?.cancel()
|
||||
}
|
||||
}, present: { [weak self] c in
|
||||
present(c)
|
||||
})
|
||||
self.interaction = interaction
|
||||
|
||||
@ -1390,8 +1392,24 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
}
|
||||
|
||||
private func clearRecentSearch() {
|
||||
let _ = (clearRecentlySearchedPeers(postbox: self.context.account.postbox)
|
||||
|> deliverOnMainQueue).start()
|
||||
let presentationData = self.presentationData
|
||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: presentationData.strings.WebSearch_RecentSectionClear, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let _ = (clearRecentlySearchedPeers(postbox: strongSelf.context.account.postbox)
|
||||
|> deliverOnMainQueue).start()
|
||||
})
|
||||
]), ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])])
|
||||
self.interaction?.present(actionSheet)
|
||||
}
|
||||
|
||||
override public func scrollToTop() {
|
||||
|
@ -61,11 +61,12 @@ public final class ChatListNodeInteraction {
|
||||
let togglePeerMarkedUnread: (PeerId, Bool) -> Void
|
||||
let toggleArchivedFolderHiddenByDefault: () -> Void
|
||||
let activateChatPreview: (ChatListItem, ASDisplayNode, ContextGesture?) -> Void
|
||||
let present: (ViewController) -> Void
|
||||
|
||||
public var searchTextHighightState: String?
|
||||
var highlightedChatLocation: ChatListHighlightedLocation?
|
||||
|
||||
public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer) -> Void, disabledPeerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> Void, messageSelected: @escaping (Peer, Message, Bool) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void) {
|
||||
public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer) -> Void, disabledPeerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> Void, messageSelected: @escaping (Peer, Message, Bool) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void, present: @escaping (ViewController) -> Void) {
|
||||
self.activateSearch = activateSearch
|
||||
self.peerSelected = peerSelected
|
||||
self.disabledPeerSelected = disabledPeerSelected
|
||||
@ -81,6 +82,7 @@ public final class ChatListNodeInteraction {
|
||||
self.togglePeerMarkedUnread = togglePeerMarkedUnread
|
||||
self.toggleArchivedFolderHiddenByDefault = toggleArchivedFolderHiddenByDefault
|
||||
self.activateChatPreview = activateChatPreview
|
||||
self.present = present
|
||||
}
|
||||
}
|
||||
|
||||
@ -358,6 +360,7 @@ public final class ChatListNode: ListView {
|
||||
public var deletePeerChat: ((PeerId) -> Void)?
|
||||
public var updatePeerGrouping: ((PeerId, Bool) -> Void)?
|
||||
public var presentAlert: ((String) -> Void)?
|
||||
public var present: ((ViewController) -> Void)?
|
||||
public var toggleArchivedFolderHiddenByDefault: (() -> Void)?
|
||||
public var activateChatPreview: ((ChatListItem, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
|
||||
@ -562,6 +565,8 @@ public final class ChatListNode: ListView {
|
||||
} else {
|
||||
gesture?.cancel()
|
||||
}
|
||||
}, present: { [weak self] c in
|
||||
self?.present?(c)
|
||||
})
|
||||
|
||||
let viewProcessingQueue = self.viewProcessingQueue
|
||||
|
@ -354,8 +354,9 @@ 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
|
||||
if let strongSelf = self {
|
||||
if let index = strongSelf.itemIndexAtPoint(point) {
|
||||
|
@ -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
|
||||
|
@ -71,6 +71,7 @@ public final class HashtagSearchController: TelegramBaseController {
|
||||
}, toggleArchivedFolderHiddenByDefault: {
|
||||
}, activateChatPreview: { _, _, gesture in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in
|
||||
})
|
||||
|
||||
let previousSearchItems = Atomic<[ChatListSearchEntry]?>(value: nil)
|
||||
|
@ -215,7 +215,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
||||
|
||||
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, activateChatPreview: { _, _, gesture in
|
||||
gesture?.cancel()
|
||||
})
|
||||
}, present: { _ in })
|
||||
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
|
||||
|
||||
let peers = SimpleDictionary<PeerId, Peer>()
|
||||
|
@ -768,6 +768,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
|
||||
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, activateChatPreview: { _, _, gesture in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in
|
||||
})
|
||||
let chatListPresentationData = ChatListPresentationData(theme: self.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
|
||||
|
||||
|
@ -352,6 +352,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, activateChatPreview: { _, _, gesture in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in
|
||||
})
|
||||
let chatListPresentationData = ChatListPresentationData(theme: self.previewTheme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: 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)
|
||||
}
|
||||
|
||||
@ -137,11 +135,7 @@ class StatsGraphItemNode: ListViewItemNode {
|
||||
strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor
|
||||
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: 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 (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 (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 (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()))
|
||||
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: "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()))
|
||||
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: 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 (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 (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 (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 (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 (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 (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 (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 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 (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 (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 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: "-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 (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 (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 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -16,7 +16,7 @@ public struct ChannelStatsValue: Equatable {
|
||||
}
|
||||
|
||||
public struct ChannelStatsPercentValue: Equatable {
|
||||
public let fraction: Double
|
||||
public let value: Double
|
||||
public let total: Double
|
||||
}
|
||||
|
||||
@ -422,7 +422,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -435,4 +435,3 @@ extension ChannelStats {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
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()
|
||||
}
|
||||
}
|
||||
|
@ -204,6 +204,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
|
||||
default:
|
||||
gesture?.cancel()
|
||||
}
|
||||
}, present: { _ in
|
||||
})
|
||||
interaction.searchTextHighightState = searchQuery
|
||||
self.interaction = interaction
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import SyncCore
|
||||
import AsyncDisplayKit
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
|
@ -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
|
||||
|
@ -223,7 +223,8 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
if let requestOpenMessageFromSearch = self?.requestOpenMessageFromSearch {
|
||||
requestOpenMessageFromSearch(peer, messageId)
|
||||
}
|
||||
}, addContact: nil, peerContextAction: nil), cancel: { [weak self] in
|
||||
}, addContact: nil, peerContextAction: nil, present: { _ in
|
||||
}), cancel: { [weak self] in
|
||||
if let requestDeactivateSearch = self?.requestDeactivateSearch {
|
||||
requestDeactivateSearch()
|
||||
}
|
||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user