Swiftgram/submodules/MediaPickerUI/Sources/AvatarEditorPreviewView.swift
2024-12-22 17:16:14 +04:00

192 lines
7.6 KiB
Swift

import Foundation
import UIKit
import Display
import SwiftSignalKit
import TelegramCore
import Postbox
import AvatarBackground
import AccountContext
import EmojiTextAttachmentView
import TextFormat
import ComponentFlow
import MultilineTextComponent
final class AvatarEditorPreviewView: UIView {
private let context: AccountContext
private var disposable: Disposable?
private var files: [TelegramMediaFile] = []
private var currentIndex = 0
private var currentBackgroundIndex = 0
private var switchingToNext = false
private let backgroundView = UIImageView()
private let label = ComponentView<Empty>()
private var animationLayer: InlineStickerItemLayer?
private var preloadDisposableSet = DisposableSet()
private var timer: SwiftSignalKit.Timer?
private var currentSize: CGSize?
var tapped: () -> Void = {}
init(context: AccountContext) {
self.context = context
super.init(frame: .zero)
self.addSubview(self.backgroundView)
let stickersKey: PostboxViewKey = .orderedItemList(id: Namespaces.OrderedItemList.CloudFeaturedProfilePhotoEmoji)
self.disposable = (context.account.postbox.combinedView(keys: [stickersKey])
|> runOn(Queue.concurrentDefaultQueue())
|> deliverOnMainQueue).start(next: { [weak self] views in
guard let self else {
return
}
if let view = views.views[stickersKey] as? OrderedItemListView {
var files: [TelegramMediaFile] = []
for item in view.items.prefix(8) {
if let mediaItem = item.contents.get(RecentMediaItem.self) {
let file = mediaItem.media
files.append(file)
self.preloadDisposableSet.add(freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start())
}
}
self.files = files
if let size = self.currentSize {
self.updateLayout(size: size)
}
}
})
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleTap))
self.addGestureRecognizer(tapRecognizer)
}
required init?(coder: NSCoder) {
preconditionFailure()
}
deinit {
self.disposable?.dispose()
self.preloadDisposableSet.dispose()
self.timer?.invalidate()
}
@objc private func handleTap() {
self.tapped()
}
func updateLayout(size: CGSize) {
self.currentSize = size
self.backgroundView.frame = CGRect(origin: .zero, size: size)
//TODO:localize
let labelSize = self.label.update(
transition: .immediate,
component: AnyComponent(
MultilineTextComponent(
text: .plain(NSAttributedString(
string: "Use an Emoji",
font: Font.semibold(14.0),
textColor: .white
)),
textShadowColor: UIColor(white: 0.0, alpha: 0.4),
textShadowBlur: 4.0
)
),
environment: {},
containerSize: size
)
if let view = self.label.view {
if view.superview == nil {
self.addSubview(view)
}
view.frame = CGRect(origin: CGPoint(x: floor((size.width - labelSize.width) / 2.0), y: size.height - labelSize.height - 20.0), size: labelSize)
}
guard !self.files.isEmpty else {
if self.backgroundView.image == nil {
self.backgroundView.image = AvatarBackground.defaultBackgrounds[self.currentBackgroundIndex].generateImage(size: size)
}
return
}
if self.timer == nil {
self.timer = SwiftSignalKit.Timer(timeout: 2.0, repeat: true, completion: { [weak self] in
guard let self else {
return
}
self.switchingToNext = true
if let size = self.currentSize {
self.updateLayout(size: size)
}
}, queue: Queue.mainQueue())
self.timer?.start()
}
let iconSize = CGSize(width: 64.0, height: 64.0)
let animationLayer: InlineStickerItemLayer
var disappearingAnimationLayer: InlineStickerItemLayer?
if let current = self.animationLayer, !self.switchingToNext {
animationLayer = current
} else {
if self.switchingToNext {
self.currentIndex = (self.currentIndex + 1) % self.files.count
self.currentBackgroundIndex = (self.currentBackgroundIndex + 1) % AvatarBackground.defaultBackgrounds.count
disappearingAnimationLayer = self.animationLayer
self.switchingToNext = false
}
if let image = self.backgroundView.image {
let snapshotView = UIImageView(image: image)
self.insertSubview(snapshotView, aboveSubview: self.backgroundView)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
snapshotView.removeFromSuperview()
})
}
self.backgroundView.image = AvatarBackground.defaultBackgrounds[self.currentBackgroundIndex].generateImage(size: size)
let file = self.files[self.currentIndex]
let emoji = ChatTextInputTextCustomEmojiAttribute(
interactivelySelectedFromPackId: nil,
fileId: file.fileId.id,
file: file
)
animationLayer = InlineStickerItemLayer(
context: .account(self.context),
userLocation: .other,
attemptSynchronousLoad: false,
emoji: emoji,
file: file,
cache: self.context.animationCache,
renderer: self.context.animationRenderer,
unique: true,
placeholderColor: UIColor(white: 1.0, alpha: 0.1),
pointSize: iconSize,
loopCount: 1
)
animationLayer.isVisibleForAnimations = true
self.layer.addSublayer(animationLayer)
self.animationLayer = animationLayer
animationLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
animationLayer.animatePosition(from: CGPoint(x: 0.0, y: 10.0), to: .zero, duration: 0.2, additive: true)
animationLayer.animateScale(from: 0.01, to: 1.0, duration: 0.2)
}
animationLayer.frame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0) - 10.0), size: iconSize)
if let disappearingAnimationLayer {
disappearingAnimationLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
disappearingAnimationLayer.removeFromSuperlayer()
})
disappearingAnimationLayer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: -10.0), duration: 0.2, removeOnCompletion: false, additive: true)
disappearingAnimationLayer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false)
}
}
}