mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
209 lines
12 KiB
Swift
209 lines
12 KiB
Swift
import Foundation
|
|
import Display
|
|
import AsyncDisplayKit
|
|
|
|
enum ChatMessageInteractiveMediaBadgeShape: Equatable {
|
|
case round
|
|
case corners(CGFloat)
|
|
}
|
|
|
|
enum ChatMessageInteractiveMediaDownloadState: Equatable {
|
|
case remote
|
|
case fetching(progress: Float)
|
|
case compactRemote
|
|
case compactFetching(progress: Float)
|
|
}
|
|
|
|
enum ChatMessageInteractiveMediaBadgeContent: Equatable {
|
|
case text(inset: CGFloat, backgroundColor: UIColor, foregroundColor: UIColor, shape: ChatMessageInteractiveMediaBadgeShape, text: NSAttributedString)
|
|
case mediaDownload(backgroundColor: UIColor, foregroundColor: UIColor, duration: String, size: String)
|
|
|
|
static func ==(lhs: ChatMessageInteractiveMediaBadgeContent, rhs: ChatMessageInteractiveMediaBadgeContent) -> Bool {
|
|
switch lhs {
|
|
case let .text(lhsInset, lhsBackgroundColor, lhsForegroundColor, lhsShape, lhsText):
|
|
if case let .text(rhsInset, rhsBackgroundColor, rhsForegroundColor, rhsShape, rhsText) = rhs, lhsInset.isEqual(to: rhsInset), lhsBackgroundColor.isEqual(rhsBackgroundColor), lhsForegroundColor.isEqual(rhsForegroundColor), lhsShape == rhsShape, lhsText.isEqual(to: rhsText) {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .mediaDownload(lhsBackgroundColor, lhsForegroundColor, lhsDuration, lhsSize):
|
|
if case let .mediaDownload(rhsBackgroundColor, rhsForegroundColor, rhsDuration, rhsSize) = rhs, lhsBackgroundColor.isEqual(rhsBackgroundColor), lhsForegroundColor.isEqual(rhsForegroundColor), lhsDuration == rhsDuration, lhsSize == rhsSize {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private let font = Font.regular(11.0)
|
|
private let boldFont = Font.semibold(11.0)
|
|
|
|
private final class ChatMessageInteractiveMediaBadgeParams: NSObject {
|
|
let content: ChatMessageInteractiveMediaBadgeContent?
|
|
|
|
init(content: ChatMessageInteractiveMediaBadgeContent?) {
|
|
self.content = content
|
|
}
|
|
}
|
|
|
|
final class ChatMessageInteractiveMediaBadge: ASDisplayNode {
|
|
var pressed: (() -> Void)?
|
|
|
|
private var content: ChatMessageInteractiveMediaBadgeContent?
|
|
|
|
private var mediaDownloadStatusNode: RadialStatusNode?
|
|
private var mediaDownloadState: ChatMessageInteractiveMediaDownloadState?
|
|
|
|
override init() {
|
|
super.init()
|
|
|
|
self.contentMode = .topLeft
|
|
self.contentsScale = UIScreenScale
|
|
}
|
|
|
|
override func didLoad() {
|
|
super.didLoad()
|
|
|
|
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
|
|
}
|
|
|
|
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
|
if case .ended = recognizer.state {
|
|
self.pressed?()
|
|
}
|
|
}
|
|
|
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
|
if let contents = self.contents, CFGetTypeID(contents as CFTypeRef) == CGImage.typeID {
|
|
let image = contents as! CGImage
|
|
if CGRect(origin: CGPoint(), size: CGSize(width: CGFloat(image.width) / UIScreenScale, height: CGFloat(image.height) / UIScreenScale)).contains(point) {
|
|
return self.view
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func update(theme: PresentationTheme, content: ChatMessageInteractiveMediaBadgeContent?, mediaDownloadState: ChatMessageInteractiveMediaDownloadState?, animated: Bool) {
|
|
if self.content != content {
|
|
self.content = content
|
|
self.setNeedsDisplay()
|
|
}
|
|
if self.mediaDownloadState != mediaDownloadState {
|
|
self.mediaDownloadState = mediaDownloadState
|
|
if let mediaDownloadState = self.mediaDownloadState {
|
|
let mediaDownloadStatusNode: RadialStatusNode
|
|
if let current = self.mediaDownloadStatusNode {
|
|
mediaDownloadStatusNode = current
|
|
} else {
|
|
mediaDownloadStatusNode = RadialStatusNode(backgroundNodeColor: .clear)
|
|
self.mediaDownloadStatusNode = mediaDownloadStatusNode
|
|
self.addSubnode(mediaDownloadStatusNode)
|
|
}
|
|
let state: RadialStatusNodeState
|
|
var isCompact = false
|
|
switch mediaDownloadState {
|
|
case .remote:
|
|
if let image = PresentationResourcesChat.chatBubbleFileCloudFetchMediaIcon(theme) {
|
|
state = .customIcon(image)
|
|
} else {
|
|
state = .none
|
|
}
|
|
case let .fetching(progress):
|
|
state = .cloudProgress(color: .white, strokeBackgroundColor: UIColor(white: 1.0, alpha: 0.3), lineWidth: 2.0, value: CGFloat(progress))
|
|
case .compactRemote:
|
|
state = .download(.white)
|
|
isCompact = true
|
|
case .compactFetching:
|
|
state = .progress(color: .white, lineWidth: nil, value: 0.0, cancelEnabled: true)
|
|
isCompact = true
|
|
}
|
|
let mediaStatusFrame: CGRect
|
|
if isCompact {
|
|
mediaStatusFrame = CGRect(origin: CGPoint(x: 1.0, y: -1.0), size: CGSize(width: 20.0, height: 20.0))
|
|
} else {
|
|
mediaStatusFrame = CGRect(origin: CGPoint(x: 7.0, y: 6.0), size: CGSize(width: 28.0, height: 28.0))
|
|
}
|
|
mediaDownloadStatusNode.frame = mediaStatusFrame
|
|
mediaDownloadStatusNode.transitionToState(state, animated: true, completion: {})
|
|
} else if let mediaDownloadStatusNode = self.mediaDownloadStatusNode {
|
|
self.mediaDownloadStatusNode = nil
|
|
mediaDownloadStatusNode.removeFromSupernode()
|
|
}
|
|
}
|
|
}
|
|
|
|
override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
|
|
return ChatMessageInteractiveMediaBadgeParams(content: self.content)
|
|
}
|
|
|
|
@objc override public class func display(withParameters: Any?, isCancelled: () -> Bool) -> UIImage? {
|
|
if let content = (withParameters as? ChatMessageInteractiveMediaBadgeParams)?.content {
|
|
switch content {
|
|
case let .text(inset, backgroundColor, foregroundColor, shape, text):
|
|
let convertedText = NSMutableAttributedString(string: text.string, attributes: [.font: font, .foregroundColor: foregroundColor])
|
|
text.enumerateAttributes(in: NSRange(location: 0, length: text.length), options: []) { attributes, range, _ in
|
|
if let _ = attributes[ChatTextInputAttributes.bold] {
|
|
convertedText.addAttribute(.font, value: boldFont, range: range)
|
|
}
|
|
}
|
|
let textRect = convertedText.boundingRect(with: CGSize(width: 200.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil)
|
|
let imageSize = CGSize(width: inset + ceil(textRect.size.width) + 10.0, height: 18.0)
|
|
return generateImage(imageSize, rotatedContext: { size, context in
|
|
context.clear(CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size))
|
|
context.setBlendMode(.copy)
|
|
context.setFillColor(backgroundColor.cgColor)
|
|
switch shape {
|
|
case .round:
|
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.height, height: size.height)))
|
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - size.height, y: 0.0), size: CGSize(width: size.height, height: size.height)))
|
|
context.fill(CGRect(origin: CGPoint(x: size.height / 2.0, y: 0.0), size: CGSize(width: size.width - size.height, height: size.height)))
|
|
case let .corners(radius):
|
|
let diameter = radius * 2.0
|
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: diameter, height: diameter)))
|
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: size.height - diameter), size: CGSize(width: diameter, height: diameter)))
|
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - diameter, y: 0.0), size: CGSize(width: diameter, height: diameter)))
|
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - diameter, y: size.height - diameter), size: CGSize(width: diameter, height: diameter)))
|
|
context.fill(CGRect(origin: CGPoint(x: 0.0, y: radius), size: CGSize(width: diameter, height: size.height - diameter)))
|
|
context.fill(CGRect(origin: CGPoint(x: radius, y: 0.0), size: CGSize(width: size.width - diameter, height: size.height)))
|
|
context.fill(CGRect(origin: CGPoint(x: size.width - diameter, y: radius), size: CGSize(width: diameter, height: size.height - diameter)))
|
|
}
|
|
context.setBlendMode(.normal)
|
|
UIGraphicsPushContext(context)
|
|
convertedText.draw(at: CGPoint(x: inset + floor((size.width - inset - textRect.size.width) / 2.0) + textRect.origin.x, y: 2.0 + textRect.origin.y))
|
|
UIGraphicsPopContext()
|
|
})
|
|
case let .mediaDownload(backgroundColor, foregroundColor, duration, size):
|
|
let durationString = NSMutableAttributedString(string: duration, attributes: [.font: font, .foregroundColor: foregroundColor])
|
|
let sizeString = NSMutableAttributedString(string: size, attributes: [.font: font, .foregroundColor: foregroundColor])
|
|
let durationRect = durationString.boundingRect(with: CGSize(width: 200.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil)
|
|
let sizeRect = sizeString.boundingRect(with: CGSize(width: 200.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil)
|
|
let leftInset: CGFloat = 42.0
|
|
let imageSize = CGSize(width: leftInset + max(ceil(durationRect.width), ceil(sizeRect.width)) + 10.0, height: 40.0)
|
|
return generateImage(imageSize, rotatedContext: { size, context in
|
|
context.clear(CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size))
|
|
context.setBlendMode(.copy)
|
|
context.setFillColor(backgroundColor.cgColor)
|
|
|
|
let radius: CGFloat = 12.0
|
|
let diameter = radius * 2.0
|
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: diameter, height: diameter)))
|
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: size.height - diameter), size: CGSize(width: diameter, height: diameter)))
|
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - diameter, y: 0.0), size: CGSize(width: diameter, height: diameter)))
|
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - diameter, y: size.height - diameter), size: CGSize(width: diameter, height: diameter)))
|
|
context.fill(CGRect(origin: CGPoint(x: 0.0, y: radius), size: CGSize(width: diameter, height: size.height - diameter)))
|
|
context.fill(CGRect(origin: CGPoint(x: radius, y: 0.0), size: CGSize(width: size.width - diameter, height: size.height)))
|
|
context.fill(CGRect(origin: CGPoint(x: size.width - diameter, y: radius), size: CGSize(width: diameter, height: size.height - diameter)))
|
|
|
|
context.setBlendMode(.normal)
|
|
UIGraphicsPushContext(context)
|
|
durationString.draw(at: CGPoint(x: leftInset + durationRect.origin.x, y: 7.0 + durationRect.origin.y))
|
|
sizeString.draw(at: CGPoint(x: leftInset + sizeRect.origin.x, y: 21.0 + sizeRect.origin.y))
|
|
UIGraphicsPopContext()
|
|
})
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
}
|