Ilya Laktyushin 74cd2b6093 Various fixes
2023-01-27 19:53:44 +04:00

226 lines
10 KiB
Swift

import Foundation
import UIKit
import Display
import AsyncDisplayKit
import ComponentFlow
import SwiftSignalKit
import ViewControllerComponent
import ComponentDisplayAdapters
import TelegramPresentationData
import AccountContext
import TelegramCore
import MultilineTextComponent
import EmojiStatusComponent
import Postbox
import AnimatedStickerNode
import TelegramAnimatedStickerNode
import StickerResources
final class AvatarPreviewComponent: Component {
typealias EnvironmentType = Empty
let context: AccountContext
let background: AvatarBackground
let file: TelegramMediaFile?
let tapped: () -> Void
init(
context: AccountContext,
background: AvatarBackground,
file: TelegramMediaFile?,
tapped: @escaping () -> Void
) {
self.context = context
self.background = background
self.file = file
self.tapped = tapped
}
static func ==(lhs: AvatarPreviewComponent, rhs: AvatarPreviewComponent) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.background != rhs.background {
return false
}
if lhs.file != rhs.file {
return false
}
return true
}
final class View: UIView, UITextFieldDelegate {
private let imageView: UIImageView
private let imageNode: TransformImageNode
private var animationNode: AnimatedStickerNode?
private var component: AvatarPreviewComponent?
private weak var state: EmptyComponentState?
private let stickerFetchedDisposable = MetaDisposable()
private let cachedDisposable = MetaDisposable()
override init(frame: CGRect) {
self.imageView = UIImageView()
self.imageView.isUserInteractionEnabled = false
self.imageNode = TransformImageNode()
super.init(frame: frame)
self.disablesInteractiveModalDismiss = true
self.disablesInteractiveKeyboardGestureRecognizer = true
self.addSubview(self.imageView)
self.addSubnode(self.imageNode)
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapped)))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.stickerFetchedDisposable.dispose()
self.cachedDisposable.dispose()
}
@objc func tapped() {
self.animationNode?.playOnce()
self.component?.tapped()
}
func update(component: AvatarPreviewComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
let previousBackground = self.component?.background
let hadFile = self.component?.file != nil
var fileUpdated = false
if self.component?.file?.fileId != component.file?.fileId {
fileUpdated = true
}
self.component = component
self.state = state
let size = CGSize(width: availableSize.width * 0.66, height: availableSize.width * 0.66)
var dimensions: CGSize?
if let file = component.file, fileUpdated, let fileDimensions = file.dimensions?.cgSize {
dimensions = fileDimensions
if !self.imageNode.isHidden && hadFile, let snapshotView = self.imageNode.view.snapshotContentTree() {
self.imageNode.view.superview?.addSubview(snapshotView)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
snapshotView.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
if let animationNode = self.animationNode {
self.animationNode = nil
animationNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
animationNode.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false, completion: { [weak animationNode] _ in
animationNode?.removeFromSupernode()
})
}
self.imageNode.isHidden = false
if file.isAnimatedSticker || file.isVideoSticker || file.mimeType == "video/webm" {
if self.animationNode == nil {
let animationNode = DefaultAnimatedStickerNodeImpl()
animationNode.autoplay = false
self.animationNode = animationNode
animationNode.started = { [weak self] in
self?.imageNode.isHidden = true
}
self.addSubnode(animationNode)
}
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: component.context.account.postbox, userLocation: .other, file: file, small: false, size: fileDimensions.aspectFitted(CGSize(width: 256.0, height: 256.0))))
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: component.context.account, userLocation: .other, fileReference: stickerPackFileReference(file), resource: file.resource).start())
} else {
if let animationNode = self.animationNode {
animationNode.visibility = false
self.animationNode = nil
animationNode.removeFromSupernode()
}
self.imageNode.setSignal(chatMessageSticker(account: component.context.account, userLocation: .other, file: file, small: false, synchronousLoad: false))
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: component.context.account, userLocation: .other, fileReference: stickerPackFileReference(file), resource: chatMessageStickerResource(file: file, small: false)).start())
}
if fileUpdated && hadFile {
self.imageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.imageNode.layer.animateScale(from: 0.01, to: 1.0, duration: 0.2)
if let animationNode = self.animationNode {
animationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
animationNode.layer.animateScale(from: 0.01, to: 1.0, duration: 0.2)
}
}
}
if let dimensions {
let imageSize = dimensions.aspectFitted(size)
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))()
self.imageNode.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - imageSize.width) / 2.0), y: (availableSize.height - imageSize.height) / 2.0), size: imageSize)
if let animationNode = self.animationNode {
animationNode.frame = CGRect(origin: CGPoint(x: floor((availableSize.width - imageSize.width) / 2.0), y: (availableSize.height - imageSize.height) / 2.0), size: imageSize)
animationNode.updateLayout(size: imageSize)
}
if fileUpdated {
self.updateVisibility()
}
}
self.imageView.frame = CGRect(origin: .zero, size: availableSize)
if previousBackground != component.background {
if let _ = previousBackground, !transition.animation.isImmediate, let snapshotView = self.imageView.snapshotContentTree() {
self.insertSubview(snapshotView, aboveSubview: self.imageView)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
self.imageView.image = component.background.generateImage(size: availableSize)
}
return availableSize
}
private func updateVisibility() {
guard let component = self.component, let file = component.file else {
return
}
let dimensions = file.dimensions ?? PixelDimensions(width: 512, height: 512)
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 384.0, height: 384.0))
let source = AnimatedStickerResourceSource(account: component.context.account, resource: file.resource, isVideo: file.isVideoSticker || file.mimeType == "video/webm")
self.animationNode?.setup(source: source, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .count(1), mode: .direct(cachePathPrefix: nil))
self.animationNode?.visibility = true
if let animationNode = self.animationNode as? DefaultAnimatedStickerNodeImpl {
if file.isCustomTemplateEmoji {
animationNode.dynamicColor = .white
} else {
animationNode.dynamicColor = nil
}
}
self.cachedDisposable.set((source.cachedDataPath(width: 384, height: 384)
|> deliverOn(Queue.concurrentDefaultQueue())).start())
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: State, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}