Swiftgram/submodules/LiveLocationTimerNode/Sources/ChatMessageLiveLocationTimerNode.swift
2024-04-16 18:42:21 +04:00

154 lines
6.3 KiB
Swift

import Foundation
import UIKit
import AsyncDisplayKit
import Display
import TelegramPresentationData
private let infinityFont = Font.with(size: 15.0, design: .round, weight: .bold)
private let textFont = Font.with(size: 13.0, design: .round, weight: .bold)
private let smallTextFont = Font.with(size: 11.0, design: .round, weight: .bold)
private class ChatMessageLiveLocationTimerNodeParams: NSObject {
let backgroundColor: UIColor
let foregroundColor: UIColor
let textColor: UIColor
let value: CGFloat
let string: String
init(backgroundColor: UIColor, foregroundColor: UIColor, textColor: UIColor, value: CGFloat, string: String) {
self.backgroundColor = backgroundColor
self.foregroundColor = foregroundColor
self.textColor = textColor
self.value = value
self.string = string
super.init()
}
}
private final class RadialTimeoutNodeTimer: NSObject {
let action: () -> Void
init(_ action: @escaping () -> Void) {
self.action = action
super.init()
}
@objc func event() {
self.action()
}
}
public final class ChatMessageLiveLocationTimerNode: ASDisplayNode {
private var timeoutAndColors: (UIColor, UIColor, UIColor, Double, Double, PresentationStrings)?
private var animationTimer: Timer?
override public init() {
super.init()
self.isOpaque = false
}
deinit {
self.animationTimer?.invalidate()
}
public func update(backgroundColor: UIColor, foregroundColor: UIColor, textColor: UIColor, beginTimestamp: Double, timeout: Double, strings: PresentationStrings) {
if self.timeoutAndColors?.3 != beginTimestamp || self.timeoutAndColors?.4 != timeout {
self.animationTimer?.invalidate()
self.timeoutAndColors = (backgroundColor, foregroundColor, textColor, beginTimestamp, timeout, strings)
let animationTimer = Timer(timeInterval: 10.0, target: RadialTimeoutNodeTimer({ [weak self] in
self?.setNeedsDisplay()
}), selector: #selector(RadialTimeoutNodeTimer.event), userInfo: nil, repeats: true)
self.animationTimer = animationTimer
RunLoop.main.add(animationTimer, forMode: .common)
}
}
public override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
var value: CGFloat = 0.0
if let (backgroundColor, foregroundColor, textColor, beginTimestamp, timeout, strings) = self.timeoutAndColors {
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
let remaining = beginTimestamp + timeout - timestamp
value = CGFloat(max(0.0, 1.0 - min(1.0, remaining / timeout)))
let string: String
if timeout < 0.0 {
value = 0.0
string = ""
} else {
let intRemaining = Int32(remaining)
if intRemaining > 60 * 60 {
let hours = Int32(round(remaining / (60.0 * 60.0)))
string = strings.Map_LiveLocationShortHour("\(hours)").string
} else {
let minutes = Int32(round(remaining / (60.0)))
string = "\(minutes)"
}
}
return ChatMessageLiveLocationTimerNodeParams(backgroundColor: backgroundColor, foregroundColor: foregroundColor, textColor: textColor, value: value, string: string)
} else {
return nil
}
}
@objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
let context = UIGraphicsGetCurrentContext()!
if !isRasterizing {
context.setBlendMode(.copy)
context.setFillColor(UIColor.clear.cgColor)
context.fill(bounds)
}
if let parameters = parameters as? ChatMessageLiveLocationTimerNodeParams {
let lineWidth: CGFloat = 1.5
context.setBlendMode(.copy)
context.setFillColor(parameters.backgroundColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: bounds.size.width, height: bounds.size.height)))
context.setFillColor(UIColor.clear.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: lineWidth, y: lineWidth), size: CGSize(width: bounds.size.width - lineWidth * 2.0, height: bounds.size.height - lineWidth * 2.0)))
context.setBlendMode(.normal)
context.setStrokeColor(parameters.foregroundColor.cgColor)
let progress = 1.0 - parameters.value
let startAngle = -CGFloat(progress) * 2.0 * CGFloat.pi - CGFloat.pi / 2.0
let endAngle = CGFloat(progress) * 2.0 * CGFloat.pi + startAngle
let pathDiameter = bounds.size.width - lineWidth
let path = UIBezierPath(arcCenter: CGPoint(x: bounds.size.width / 2.0, y: bounds.size.height / 2.0), radius: pathDiameter / 2.0, startAngle: startAngle, endAngle: endAngle, clockwise:true)
path.lineWidth = lineWidth
path.lineCapStyle = .round
path.stroke()
let font: UIFont
if parameters.string == "" {
font = infinityFont
} else if parameters.string.count > 2 {
font = smallTextFont
} else {
font = textFont
}
let attributes: [NSAttributedString.Key: Any] = [.font: font, .foregroundColor: parameters.foregroundColor]
let nsString = parameters.string as NSString
let size = nsString.size(withAttributes: attributes)
var offset = CGPoint()
if parameters.string == "" {
offset = CGPoint(x: 1.0, y: -1.0)
} else if parameters.string.count > 2 {
offset = CGPoint(x: 0.0, y: UIScreenPixel)
}
nsString.draw(at: CGPoint(x: floor((bounds.size.width - size.width) / 2.0) + offset.x, y: floor((bounds.size.height - size.height) / 2.0) + offset.y), withAttributes: attributes)
}
}
}