mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-24 07:05:35 +00:00
Various improvements
This commit is contained in:
@@ -175,6 +175,7 @@ final class DataCategoriesComponent: Component {
|
||||
}
|
||||
|
||||
self.backgroundColor = component.theme.list.itemBlocksBackgroundColor
|
||||
self.containerView.backgroundColor = component.theme.list.itemBlocksBackgroundColor
|
||||
|
||||
self.containerView.frame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: contentHeight))
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ final class DataUsageScreenComponent: Component {
|
||||
init(stats: NetworkUsageStats) {
|
||||
self.wifi = Stats(stats: stats, isWifi: true)
|
||||
self.cellular = Stats(stats: stats, isWifi: false)
|
||||
self.resetTimestamp = stats.resetWifiTimestamp
|
||||
self.resetTimestamp = max(stats.resetWifiTimestamp, stats.resetCellularTimestamp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,7 +293,8 @@ final class DataUsageScreenComponent: Component {
|
||||
private let headerOffsetContainer: UIView
|
||||
private let headerDescriptionView = ComponentView<Empty>()
|
||||
|
||||
private var doneStatusNode: RadialStatusNode?
|
||||
private var doneLabel: ComponentView<Empty>?
|
||||
private var doneSupLabel: ComponentView<Empty>?
|
||||
|
||||
private let scrollContainerView: UIView
|
||||
|
||||
@@ -566,8 +567,17 @@ final class DataUsageScreenComponent: Component {
|
||||
chartItems.append(PieChartComponent.ChartData.Item(id: AnyHashable(listCategory.key), displayValue: listCategory.sizeFraction, displaySize: listCategory.size, value: categoryChartFraction, color: listCategory.color, particle: nil, title: listCategory.key.title(strings: environment.strings), mergeable: false, mergeFactor: 1.0))
|
||||
}
|
||||
|
||||
var emptyValue: CGFloat = 0.0
|
||||
if totalSize == 0 {
|
||||
for i in 0 ..< chartItems.count {
|
||||
chartItems[i].value = 0.0
|
||||
}
|
||||
emptyValue = 1.0
|
||||
}
|
||||
if let allStats = self.allStats, allStats.wifi.isEmpty && allStats.cellular.isEmpty {
|
||||
chartItems.removeAll()
|
||||
} else {
|
||||
chartItems.append(PieChartComponent.ChartData.Item(id: "empty", displayValue: 0.0, displaySize: 0, value: emptyValue, color: UIColor(rgb: 0xC4C4C6), particle: nil, title: "", mergeable: false, mergeFactor: 1.0))
|
||||
}
|
||||
|
||||
let totalCategories: [DataCategoriesComponent.CategoryData] = [
|
||||
@@ -611,6 +621,7 @@ final class DataUsageScreenComponent: Component {
|
||||
component: AnyComponent(PieChartComponent(
|
||||
theme: environment.theme,
|
||||
strings: environment.strings,
|
||||
emptyColor: environment.theme.list.itemAccentColor,
|
||||
chartData: chartData
|
||||
)),
|
||||
environment: {},
|
||||
@@ -625,42 +636,75 @@ final class DataUsageScreenComponent: Component {
|
||||
pieChartTransition.setFrame(view: pieChartComponentView, frame: pieChartFrame)
|
||||
}
|
||||
if let allStats = self.allStats, allStats.wifi.isEmpty && allStats.cellular.isEmpty {
|
||||
let checkColor = UIColor(rgb: 0x34C759)
|
||||
let checkColor = environment.theme.list.itemAccentColor
|
||||
|
||||
let doneStatusNode: RadialStatusNode
|
||||
var animateIn = false
|
||||
if let current = self.doneStatusNode {
|
||||
doneStatusNode = current
|
||||
var doneLabelTransition = transition
|
||||
let doneLabel: ComponentView<Empty>
|
||||
if let current = self.doneLabel {
|
||||
doneLabel = current
|
||||
} else {
|
||||
doneStatusNode = RadialStatusNode(backgroundNodeColor: .clear)
|
||||
self.doneStatusNode = doneStatusNode
|
||||
self.scrollView.addSubnode(doneStatusNode)
|
||||
animateIn = true
|
||||
}
|
||||
let doneSize = CGSize(width: 100.0, height: 100.0)
|
||||
doneStatusNode.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - doneSize.width) / 2.0), y: contentHeight), size: doneSize)
|
||||
|
||||
if animateIn {
|
||||
Queue.mainQueue().after(0.18, {
|
||||
doneStatusNode.transitionToState(.check(checkColor), animated: true)
|
||||
})
|
||||
doneLabelTransition = .immediate
|
||||
doneLabel = ComponentView()
|
||||
self.doneLabel = doneLabel
|
||||
}
|
||||
|
||||
contentHeight += doneSize.height
|
||||
let doneSupLabel: ComponentView<Empty>
|
||||
if let current = self.doneSupLabel {
|
||||
doneSupLabel = current
|
||||
} else {
|
||||
doneSupLabel = ComponentView()
|
||||
self.doneSupLabel = doneSupLabel
|
||||
}
|
||||
|
||||
let doneLabelSize = doneLabel.update(transition: doneLabelTransition, component: AnyComponent(Text(text: "0", font: UIFont.systemFont(ofSize: 50.0, weight: UIFont.Weight(0.25)), color: checkColor)), environment: {}, containerSize: CGSize(width: 100.0, height: 100.0))
|
||||
let doneLabelFrame = CGRect(origin: CGPoint(x: pieChartFrame.minX + floor((pieChartFrame.width - doneLabelSize.width) * 0.5), y: pieChartFrame.minY + 16.0), size: doneLabelSize)
|
||||
if let doneLabelView = doneLabel.view {
|
||||
var animateIn = false
|
||||
if doneLabelView.superview == nil {
|
||||
self.scrollView.addSubview(doneLabelView)
|
||||
animateIn = true
|
||||
}
|
||||
doneLabelTransition.setFrame(view: doneLabelView, frame: doneLabelFrame)
|
||||
|
||||
if animateIn {
|
||||
doneLabelView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: 0.2)
|
||||
}
|
||||
}
|
||||
|
||||
let doneSupLabelSize = doneSupLabel.update(transition: doneLabelTransition, component: AnyComponent(Text(text: "KB", font: avatarPlaceholderFont(size: 12.0), color: checkColor)), environment: {}, containerSize: CGSize(width: 100.0, height: 100.0))
|
||||
let doneSupLabelFrame = CGRect(origin: CGPoint(x: doneLabelFrame.maxX + 1.0, y: doneLabelFrame.minY + 10.0), size: doneSupLabelSize)
|
||||
if let doneSupLabelView = doneSupLabel.view {
|
||||
var animateIn = false
|
||||
if doneSupLabelView.superview == nil {
|
||||
self.scrollView.addSubview(doneSupLabelView)
|
||||
animateIn = true
|
||||
}
|
||||
doneLabelTransition.setFrame(view: doneSupLabelView, frame: doneSupLabelFrame)
|
||||
|
||||
if animateIn {
|
||||
doneSupLabelView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: 0.2)
|
||||
}
|
||||
}
|
||||
|
||||
contentHeight += 100.0
|
||||
} else {
|
||||
contentHeight += pieChartSize.height
|
||||
|
||||
if let doneStatusNode = self.doneStatusNode {
|
||||
self.doneStatusNode = nil
|
||||
doneStatusNode.removeFromSupernode()
|
||||
if let doneLabel = self.doneLabel {
|
||||
self.doneLabel = nil
|
||||
doneLabel.view?.removeFromSuperview()
|
||||
}
|
||||
if let doneSupLabel = self.doneSupLabel {
|
||||
self.doneSupLabel = nil
|
||||
doneSupLabel.view?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
contentHeight += 23.0
|
||||
|
||||
let headerText: String
|
||||
if listCategories.isEmpty {
|
||||
headerText = "Data Usage Reset"
|
||||
if totalSize == 0 {
|
||||
headerText = "No Data Used"
|
||||
} else {
|
||||
headerText = "Data Usage"
|
||||
}
|
||||
@@ -686,15 +730,19 @@ final class DataUsageScreenComponent: Component {
|
||||
let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: environment.theme.list.freeTextColor)
|
||||
|
||||
//TODO:localize
|
||||
|
||||
let timestampString: String
|
||||
if let allStats = self.allStats, allStats.resetTimestamp != 0 {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "E, d MMM yyyy HH:mm"
|
||||
let dateStringPlain = formatter.string(from: Date(timeIntervalSince1970: Double(allStats.resetTimestamp)))
|
||||
timestampString = "Your network usage since \(dateStringPlain)"
|
||||
let dateStringPlain = stringForFullDate(timestamp: allStats.resetTimestamp, strings: environment.strings, dateTimeFormat: PresentationDateTimeFormat())
|
||||
switch self.selectedStats {
|
||||
case .all:
|
||||
timestampString = "Your data usage since \(dateStringPlain)"
|
||||
case .mobile:
|
||||
timestampString = "Your mobile data usage since \(dateStringPlain)"
|
||||
case .wifi:
|
||||
timestampString = "Your Wi-Fi data usage since \(dateStringPlain)"
|
||||
}
|
||||
} else {
|
||||
timestampString = "Your network usage"
|
||||
timestampString = ""
|
||||
}
|
||||
|
||||
let totalUsageText: String = timestampString
|
||||
@@ -743,8 +791,13 @@ final class DataUsageScreenComponent: Component {
|
||||
animatedTextItems.append(AnimatedTextComponent.Item(id: "rest", isUnbreakable: true, content: .text(remainingSizeText)))
|
||||
}
|
||||
|
||||
var labelTransition = transition
|
||||
if labelTransition.animation.isImmediate, let animationHint, animationHint.value == .modeChanged {
|
||||
labelTransition = Transition(animation: .curve(duration: 0.3, curve: .easeInOut))
|
||||
}
|
||||
|
||||
let chartTotalLabelSize = self.chartTotalLabel.update(
|
||||
transition: transition,
|
||||
transition: labelTransition,
|
||||
component: AnyComponent(AnimatedTextComponent(
|
||||
font: Font.with(size: 20.0, design: .round, weight: .bold),
|
||||
color: environment.theme.list.itemPrimaryTextColor,
|
||||
@@ -758,8 +811,8 @@ final class DataUsageScreenComponent: Component {
|
||||
self.scrollContainerView.addSubview(chartTotalLabelView)
|
||||
}
|
||||
let totalLabelFrame = CGRect(origin: CGPoint(x: pieChartFrame.minX + floor((pieChartFrame.width - chartTotalLabelSize.width) / 2.0), y: pieChartFrame.minY + floor((pieChartFrame.height - chartTotalLabelSize.height) / 2.0)), size: chartTotalLabelSize)
|
||||
transition.setFrame(view: chartTotalLabelView, frame: totalLabelFrame)
|
||||
transition.setAlpha(view: chartTotalLabelView, alpha: listCategories.isEmpty ? 0.0 : 1.0)
|
||||
labelTransition.setFrame(view: chartTotalLabelView, frame: totalLabelFrame)
|
||||
labelTransition.setAlpha(view: chartTotalLabelView, alpha: listCategories.isEmpty ? 0.0 : 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -848,9 +901,18 @@ final class DataUsageScreenComponent: Component {
|
||||
contentHeight += 40.0
|
||||
|
||||
//TODO:localize
|
||||
let totalTitle: String
|
||||
switch self.selectedStats {
|
||||
case .all:
|
||||
totalTitle = "TOTAL NETWORK USAGE"
|
||||
case .mobile:
|
||||
totalTitle = "MOBILE NETWORK USAGE"
|
||||
case .wifi:
|
||||
totalTitle = "WI-FI NETWORK USAGE"
|
||||
}
|
||||
let totalCategoriesTitleSize = self.totalCategoriesTitleView.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(MultilineTextComponent(text: .markdown(text: "TOTAL NETWORK USAGE", attributes: MarkdownAttributes(
|
||||
component: AnyComponent(MultilineTextComponent(text: .markdown(text: totalTitle, attributes: MarkdownAttributes(
|
||||
body: body,
|
||||
bold: bold,
|
||||
link: body,
|
||||
|
||||
@@ -242,15 +242,18 @@ final class PieChartComponent: Component {
|
||||
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let emptyColor: UIColor
|
||||
let chartData: ChartData
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
emptyColor: UIColor,
|
||||
chartData: ChartData
|
||||
) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.emptyColor = emptyColor
|
||||
self.chartData = chartData
|
||||
}
|
||||
|
||||
@@ -261,6 +264,9 @@ final class PieChartComponent: Component {
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.emptyColor != rhs.emptyColor {
|
||||
return false
|
||||
}
|
||||
if lhs.chartData != rhs.chartData {
|
||||
return false
|
||||
}
|
||||
@@ -366,6 +372,8 @@ final class PieChartComponent: Component {
|
||||
var label: CalculatedLabel?
|
||||
if let leftLabel = left.label, let rightLabel = right.label {
|
||||
label = leftLabel.interpolateTo(rightLabel, amount: progress)
|
||||
} else {
|
||||
label = right.label
|
||||
}
|
||||
|
||||
self.sections.append(CalculatedSection(
|
||||
@@ -385,7 +393,7 @@ final class PieChartComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
init(size: CGSize, items: [ChartData.Item], selectedKey: AnyHashable?, isEmpty: Bool) {
|
||||
init(size: CGSize, items: [ChartData.Item], selectedKey: AnyHashable?, isEmpty: Bool, emptyColor: UIColor) {
|
||||
self.size = size
|
||||
self.sections = []
|
||||
self.isEmpty = isEmpty
|
||||
@@ -447,7 +455,7 @@ final class PieChartComponent: Component {
|
||||
var arcOuterEndAngle = startAngle + angleValue - angleSpacing * 0.5 * afterSpacingFraction
|
||||
arcOuterEndAngle = max(arcOuterEndAngle, arcOuterStartAngle)
|
||||
|
||||
let itemColor: UIColor = isEmpty ? UIColor(rgb: 0x34C759) : item.color
|
||||
let itemColor: UIColor = isEmpty ? emptyColor : item.color
|
||||
|
||||
self.sections.append(CalculatedSection(
|
||||
id: item.id,
|
||||
@@ -504,15 +512,17 @@ final class PieChartComponent: Component {
|
||||
|
||||
let fractionValue: Double = floor(displayValue * 100.0 * 10.0) / 10.0
|
||||
let fractionString: String
|
||||
if fractionValue < 0.1 {
|
||||
fractionString = "<0.1"
|
||||
if fractionValue == 0.0 {
|
||||
fractionString = ""
|
||||
} else if fractionValue < 0.1 {
|
||||
fractionString = "<0.1%"
|
||||
} else if abs(Double(Int(fractionValue)) - fractionValue) < 0.001 {
|
||||
fractionString = "\(Int(fractionValue))"
|
||||
fractionString = "\(Int(fractionValue))%"
|
||||
} else {
|
||||
fractionString = "\(fractionValue)"
|
||||
fractionString = "\(fractionValue)%"
|
||||
}
|
||||
|
||||
let labelString = NSAttributedString(string: "\(fractionString)%", font: chartLabelFont, textColor: .white)
|
||||
let labelString = NSAttributedString(string: fractionString, font: chartLabelFont, textColor: .white)
|
||||
let labelBounds = labelString.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: [.usesLineFragmentOrigin], context: nil)
|
||||
let labelSize = CGSize(width: ceil(labelBounds.width), height: ceil(labelBounds.height))
|
||||
guard let labelImage = generateImage(labelSize, rotatedContext: { size, context in
|
||||
@@ -922,12 +932,15 @@ final class PieChartComponent: Component {
|
||||
}
|
||||
|
||||
private final class DoneLayer: SimpleLayer {
|
||||
private let particleColor: UIColor
|
||||
private let maskShapeLayer: CAShapeLayer
|
||||
private var particleImage: UIImage?
|
||||
private var particleSet: ParticleSet?
|
||||
private var particleLayers: [SimpleLayer] = []
|
||||
|
||||
override init() {
|
||||
init(particleColor: UIColor) {
|
||||
self.particleColor = particleColor
|
||||
|
||||
self.maskShapeLayer = CAShapeLayer()
|
||||
self.maskShapeLayer.fillColor = UIColor.black.cgColor
|
||||
self.maskShapeLayer.fillRule = .evenOdd
|
||||
@@ -948,6 +961,7 @@ final class PieChartComponent: Component {
|
||||
}
|
||||
|
||||
override init(layer: Any) {
|
||||
self.particleColor = .white
|
||||
self.maskShapeLayer = CAShapeLayer()
|
||||
|
||||
super.init(layer: layer)
|
||||
@@ -982,7 +996,7 @@ final class PieChartComponent: Component {
|
||||
self.particleLayers.append(particleLayer)
|
||||
self.addSublayer(particleLayer)
|
||||
|
||||
particleLayer.layerTintColor = UIColor(rgb: 0x34C759).cgColor
|
||||
particleLayer.layerTintColor = self.particleColor.cgColor
|
||||
}
|
||||
|
||||
particleLayer.position = particle.position
|
||||
@@ -1010,6 +1024,7 @@ final class PieChartComponent: Component {
|
||||
private final class ChartDataView: UIView {
|
||||
private(set) var theme: PresentationTheme?
|
||||
private(set) var data: ChartData?
|
||||
private var emptyColor: UIColor?
|
||||
private(set) var selectedKey: AnyHashable?
|
||||
|
||||
private var currentAnimation: (start: CalculatedLayout, startTime: Double, duration: Double)?
|
||||
@@ -1077,7 +1092,9 @@ final class PieChartComponent: Component {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setItems(theme: PresentationTheme, data: ChartData, selectedKey: AnyHashable?, animated: Bool) {
|
||||
func setItems(theme: PresentationTheme, emptyColor: UIColor, data: ChartData, selectedKey: AnyHashable?, animated: Bool) {
|
||||
self.emptyColor = emptyColor
|
||||
|
||||
let data = processChartData(data: data)
|
||||
|
||||
if self.theme !== theme || self.data != data || self.selectedKey != selectedKey {
|
||||
@@ -1099,14 +1116,16 @@ final class PieChartComponent: Component {
|
||||
size: CGSize(width: 200.0, height: 200.0),
|
||||
items: previousData.items,
|
||||
selectedKey: self.selectedKey,
|
||||
isEmpty: true
|
||||
isEmpty: true,
|
||||
emptyColor: emptyColor
|
||||
)
|
||||
} else {
|
||||
targetLayout = CalculatedLayout(
|
||||
size: CGSize(width: 200.0, height: 200.0),
|
||||
items: data.items,
|
||||
selectedKey: self.selectedKey,
|
||||
isEmpty: false
|
||||
isEmpty: false,
|
||||
emptyColor: emptyColor
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1118,14 +1137,16 @@ final class PieChartComponent: Component {
|
||||
size: CGSize(width: 200.0, height: 200.0),
|
||||
items: [.init(id: AnyHashable(StorageUsageScreenComponent.Category.other), displayValue: 0.0, displaySize: 0, value: 1.0, color: .green, particle: "Settings/Storage/ParticleOther", title: "", mergeable: false, mergeFactor: 1.0)],
|
||||
selectedKey: self.selectedKey,
|
||||
isEmpty: true
|
||||
isEmpty: true,
|
||||
emptyColor: emptyColor
|
||||
)
|
||||
} else {
|
||||
self.currentLayout = CalculatedLayout(
|
||||
size: CGSize(width: 200.0, height: 200.0),
|
||||
items: data.items,
|
||||
selectedKey: self.selectedKey,
|
||||
isEmpty: data.items.isEmpty
|
||||
isEmpty: data.items.isEmpty,
|
||||
emptyColor: emptyColor
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1140,7 +1161,7 @@ final class PieChartComponent: Component {
|
||||
self.particleSet.update(deltaTime: deltaTime)
|
||||
|
||||
var validIds: [AnyHashable] = []
|
||||
if let currentLayout = self.currentLayout {
|
||||
if let currentLayout = self.currentLayout, let emptyColor = self.emptyColor {
|
||||
var effectiveLayout = currentLayout
|
||||
var verticalOffset: CGFloat = 0.0
|
||||
var particleAlpha: CGFloat = 1.0
|
||||
@@ -1195,7 +1216,7 @@ final class PieChartComponent: Component {
|
||||
if let current = self.doneLayer {
|
||||
doneLayer = current
|
||||
} else {
|
||||
doneLayer = DoneLayer()
|
||||
doneLayer = DoneLayer(particleColor: emptyColor)
|
||||
self.doneLayer = doneLayer
|
||||
self.layer.insertSublayer(doneLayer, at: 0)
|
||||
}
|
||||
@@ -1269,7 +1290,7 @@ final class PieChartComponent: Component {
|
||||
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
let point = recognizer.location(in: self.dataView)
|
||||
if let key = self.dataView.sectionKey(at: point) {
|
||||
if let key = self.dataView.sectionKey(at: point), key != AnyHashable("empty") {
|
||||
if self.selectedKey == key {
|
||||
self.selectedKey = nil
|
||||
} else {
|
||||
@@ -1293,7 +1314,7 @@ final class PieChartComponent: Component {
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.dataView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - 200.0) / 2.0), y: 0.0), size: CGSize(width: 200.0, height: 200.0)))
|
||||
self.dataView.setItems(theme: component.theme, data: component.chartData, selectedKey: self.selectedKey, animated: !transition.animation.isImmediate)
|
||||
self.dataView.setItems(theme: component.theme, emptyColor: component.emptyColor, data: component.chartData, selectedKey: self.selectedKey, animated: !transition.animation.isImmediate)
|
||||
|
||||
if let selectedKey = self.selectedKey, let item = component.chartData.items.first(where: { $0.id == selectedKey }) {
|
||||
let tooltip: ComponentView<Empty>
|
||||
|
||||
@@ -1392,6 +1392,7 @@ final class StorageUsageScreenComponent: Component {
|
||||
component: AnyComponent(PieChartComponent(
|
||||
theme: environment.theme,
|
||||
strings: environment.strings,
|
||||
emptyColor: UIColor(rgb: 0x34C759),
|
||||
chartData: chartData
|
||||
)),
|
||||
environment: {},
|
||||
@@ -1404,7 +1405,6 @@ final class StorageUsageScreenComponent: Component {
|
||||
}
|
||||
|
||||
pieChartTransition.setFrame(view: pieChartComponentView, frame: pieChartFrame)
|
||||
//transition.setAlpha(view: pieChartComponentView, alpha: listCategories.isEmpty ? 0.0 : 1.0)
|
||||
}
|
||||
if let _ = self.aggregatedData, listCategories.isEmpty {
|
||||
let checkColor = UIColor(rgb: 0x34C759)
|
||||
|
||||
Reference in New Issue
Block a user