mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
159 lines
8.6 KiB
Swift
159 lines
8.6 KiB
Swift
import Foundation
|
|
import AsyncDisplayKit
|
|
import Display
|
|
import Postbox
|
|
import TelegramCore
|
|
import TelegramPresentationData
|
|
import AppBundle
|
|
import AnimatedStickerNode
|
|
import TelegramAnimatedStickerNode
|
|
import SwiftSignalKit
|
|
import StickerResources
|
|
import AccountContext
|
|
|
|
private func generateBubbleImage(foreground: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? {
|
|
return generateImage(CGSize(width: diameter + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in
|
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
|
context.setFillColor(foreground.cgColor)
|
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
|
|
})?.stretchableImage(withLeftCapWidth: Int(diameter / 2.0 + shadowBlur / 2.0), topCapHeight: Int(diameter / 2.0 + shadowBlur / 2.0))
|
|
}
|
|
|
|
private func generateBubbleShadowImage(shadow: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? {
|
|
return generateImage(CGSize(width: diameter + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in
|
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
|
context.setFillColor(UIColor.white.cgColor)
|
|
context.setShadow(offset: CGSize(), blur: shadowBlur, color: shadow.cgColor)
|
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
|
|
context.setShadow(offset: CGSize(), blur: 1.0, color: shadow.cgColor)
|
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
|
|
context.setFillColor(UIColor.clear.cgColor)
|
|
context.setBlendMode(.copy)
|
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
|
|
})?.stretchableImage(withLeftCapWidth: Int(diameter / 2.0 + shadowBlur / 2.0), topCapHeight: Int(diameter / 2.0 + shadowBlur / 2.0))
|
|
}
|
|
|
|
private let font = Font.medium(13.0)
|
|
|
|
final class ReactionNode: ASDisplayNode {
|
|
let context: AccountContext
|
|
let item: ReactionContextItem
|
|
private let staticImageNode: TransformImageNode
|
|
private let stillAnimationNode: AnimatedStickerNode
|
|
private var animationNode: AnimatedStickerNode?
|
|
|
|
private var fetchStickerDisposable: Disposable?
|
|
private var fetchFullAnimationDisposable: Disposable?
|
|
|
|
private var validSize: CGSize?
|
|
|
|
var isExtracted: Bool = false
|
|
|
|
var didSetupStillAnimation: Bool = false
|
|
|
|
init(context: AccountContext, theme: PresentationTheme, item: ReactionContextItem) {
|
|
self.context = context
|
|
self.item = item
|
|
|
|
self.staticImageNode = TransformImageNode()
|
|
self.stillAnimationNode = AnimatedStickerNode()
|
|
|
|
super.init()
|
|
|
|
self.addSubnode(self.staticImageNode)
|
|
|
|
self.addSubnode(self.stillAnimationNode)
|
|
|
|
self.stillAnimationNode.started = { [weak self] in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
strongSelf.staticImageNode.isHidden = true
|
|
}
|
|
|
|
self.fetchStickerDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: .standalone(resource: item.stillAnimation.resource)).start()
|
|
self.fetchStickerDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: .standalone(resource: item.listAnimation.resource)).start()
|
|
self.fetchFullAnimationDisposable = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: .standalone(resource: item.applicationAnimation.resource)).start()
|
|
}
|
|
|
|
deinit {
|
|
self.fetchStickerDisposable?.dispose()
|
|
self.fetchFullAnimationDisposable?.dispose()
|
|
}
|
|
|
|
func updateLayout(size: CGSize, isExpanded: Bool, transition: ContainedViewLayoutTransition) {
|
|
let intrinsicSize = size
|
|
|
|
let animationSize = self.item.stillAnimation.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0)
|
|
var animationDisplaySize = animationSize.aspectFitted(intrinsicSize)
|
|
|
|
let scalingFactor: CGFloat = 1.0
|
|
let offsetFactor: CGFloat = 0.0
|
|
|
|
animationDisplaySize.width = floor(animationDisplaySize.width * scalingFactor)
|
|
animationDisplaySize.height = floor(animationDisplaySize.height * scalingFactor)
|
|
|
|
var animationFrame = CGRect(origin: CGPoint(x: floor((intrinsicSize.width - animationDisplaySize.width) / 2.0), y: floor((intrinsicSize.height - animationDisplaySize.height) / 2.0)), size: animationDisplaySize)
|
|
animationFrame.origin.y = floor(animationFrame.origin.y + animationFrame.height * offsetFactor)
|
|
|
|
if isExpanded, self.animationNode == nil {
|
|
let animationNode = AnimatedStickerNode()
|
|
self.animationNode = animationNode
|
|
self.addSubnode(animationNode)
|
|
|
|
animationNode.started = { [weak self] in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
strongSelf.staticImageNode.isHidden = true
|
|
}
|
|
|
|
animationNode.setup(source: AnimatedStickerResourceSource(account: self.context.account, resource: self.item.listAnimation.resource), width: Int(animationDisplaySize.width * 2.0), height: Int(animationDisplaySize.height * 2.0), playbackMode: .once, mode: .direct(cachePathPrefix: self.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(self.item.listAnimation.resource.id)))
|
|
animationNode.frame = animationFrame
|
|
animationNode.updateLayout(size: animationFrame.size)
|
|
if transition.isAnimated, !self.staticImageNode.frame.isEmpty {
|
|
transition.animateTransformScale(node: animationNode, from: self.staticImageNode.bounds.width / animationFrame.width)
|
|
transition.animatePositionAdditive(node: animationNode, offset: CGPoint(x: self.staticImageNode.frame.midX - animationFrame.midX, y: self.staticImageNode.frame.midY - animationFrame.midY))
|
|
}
|
|
animationNode.visibility = true
|
|
|
|
self.stillAnimationNode.alpha = 0.0
|
|
if transition.isAnimated {
|
|
self.stillAnimationNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak self] _ in
|
|
self?.stillAnimationNode.visibility = false
|
|
})
|
|
animationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
|
} else {
|
|
self.stillAnimationNode.visibility = false
|
|
}
|
|
}
|
|
|
|
if self.validSize != size {
|
|
self.validSize = size
|
|
|
|
self.staticImageNode.setSignal(chatMessageAnimatedSticker(postbox: self.context.account.postbox, file: item.stillAnimation, small: false, size: CGSize(width: animationDisplaySize.width * UIScreenScale, height: animationDisplaySize.height * UIScreenScale), fitzModifier: nil, fetched: false, onlyFullSize: false, thumbnail: false, synchronousLoad: false))
|
|
let imageApply = self.staticImageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: animationDisplaySize, boundingSize: animationDisplaySize, intrinsicInsets: UIEdgeInsets()))
|
|
imageApply()
|
|
transition.updateFrame(node: self.staticImageNode, frame: animationFrame)
|
|
}
|
|
|
|
if !self.didSetupStillAnimation {
|
|
if self.animationNode == nil {
|
|
self.didSetupStillAnimation = true
|
|
|
|
self.stillAnimationNode.setup(source: AnimatedStickerResourceSource(account: self.context.account, resource: self.item.stillAnimation.resource), width: Int(animationDisplaySize.width * 2.0), height: Int(animationDisplaySize.height * 2.0), playbackMode: .loop, mode: .direct(cachePathPrefix: self.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(self.item.stillAnimation.resource.id)))
|
|
self.stillAnimationNode.position = animationFrame.center
|
|
self.stillAnimationNode.bounds = CGRect(origin: CGPoint(), size: animationFrame.size)
|
|
self.stillAnimationNode.updateLayout(size: animationFrame.size)
|
|
self.stillAnimationNode.visibility = true
|
|
}
|
|
} else {
|
|
transition.updatePosition(node: self.stillAnimationNode, position: animationFrame.center)
|
|
transition.updateTransformScale(node: self.stillAnimationNode, scale: animationFrame.size.width / self.stillAnimationNode.bounds.width)
|
|
}
|
|
}
|
|
|
|
func didAppear() {
|
|
}
|
|
}
|