mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Avatar playback improvements
This commit is contained in:
parent
cc0595de4b
commit
394c0d7a26
@ -25,6 +25,9 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/EntityKeyboard:EntityKeyboard",
|
||||
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
|
||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
||||
"//submodules/AnimatedStickerNode",
|
||||
"//submodules/TelegramAnimatedStickerNode",
|
||||
"//submodules/StickerResources:StickerResources",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -13,6 +13,9 @@ import GradientBackground
|
||||
import AnimationCache
|
||||
import MultiAnimationRenderer
|
||||
import EntityKeyboard
|
||||
import AnimatedStickerNode
|
||||
import TelegramAnimatedStickerNode
|
||||
import StickerResources
|
||||
|
||||
private let maxVideoLoopCount = 3
|
||||
|
||||
@ -26,6 +29,8 @@ public final class AvatarVideoNode: ASDisplayNode {
|
||||
private var fileDisposable: Disposable?
|
||||
private var animationFile: TelegramMediaFile?
|
||||
private var itemLayer: EmojiPagerContentComponent.View.ItemLayer?
|
||||
private var useAnimationNode = false
|
||||
private var animationNode: AnimatedStickerNode?
|
||||
|
||||
private var videoNode: UniversalVideoNode?
|
||||
private var videoContent: NativeVideoContent?
|
||||
@ -69,7 +74,23 @@ public final class AvatarVideoNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
|
||||
let itemNativeFitSize = self.internalSize.width > 100.0 ? CGSize(width: 256.0, height: 256.0) : CGSize(width: 128.0, height: 128.0)
|
||||
if self.useAnimationNode {
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.autoplay = false
|
||||
self.animationNode = animationNode
|
||||
animationNode.started = { [weak self] in
|
||||
if let self {
|
||||
if !self.didAppear {
|
||||
self.didAppear = true
|
||||
Queue.mainQueue().after(0.15) {
|
||||
self.backgroundNode.isHidden = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.backgroundNode.addSubnode(animationNode)
|
||||
} else {
|
||||
let itemNativeFitSize = self.internalSize.width > 100.0 ? CGSize(width: 192.0, height: 192.0) : CGSize(width: 64.0, height: 64.0)
|
||||
|
||||
let animationData = EntityKeyboardAnimationData(file: animationFile)
|
||||
let itemLayer = EmojiPagerContentComponent.View.ItemLayer(
|
||||
@ -107,18 +128,20 @@ public final class AvatarVideoNode: ASDisplayNode {
|
||||
itemLayer.isVisibleForAnimations = self.visibility
|
||||
self.itemLayer = itemLayer
|
||||
self.backgroundNode.layer.addSublayer(itemLayer)
|
||||
}
|
||||
|
||||
if let (size, cornerRadius) = self.validLayout {
|
||||
self.updateLayout(size: size, cornerRadius: cornerRadius, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
public func update(markup: TelegramMediaImage.EmojiMarkup, size: CGSize) {
|
||||
public func update(markup: TelegramMediaImage.EmojiMarkup, size: CGSize, useAnimationNode: Bool = true) {
|
||||
guard markup != self.emojiMarkup else {
|
||||
return
|
||||
}
|
||||
self.emojiMarkup = markup
|
||||
self.internalSize = size
|
||||
//self.useAnimationNode = useAnimationNode
|
||||
|
||||
let colors = markup.backgroundColors.map { UInt32(bitPattern: $0) }
|
||||
if colors.count == 1 {
|
||||
@ -160,7 +183,7 @@ public final class AvatarVideoNode: ASDisplayNode {
|
||||
public func update(peer: EnginePeer, photo: TelegramMediaImage, size: CGSize) {
|
||||
self.internalSize = size
|
||||
if let markup = photo.emojiMarkup {
|
||||
self.update(markup: markup, size: size)
|
||||
self.update(markup: markup, size: size, useAnimationNode: false)
|
||||
} else if let video = smallestVideoRepresentation(photo.videoRepresentations), let peerReference = PeerReference(peer._asPeer()) {
|
||||
self.backgroundNode.image = nil
|
||||
|
||||
@ -177,6 +200,14 @@ public final class AvatarVideoNode: ASDisplayNode {
|
||||
private var visibility = false
|
||||
public func updateVisibility(_ isVisible: Bool) {
|
||||
self.visibility = isVisible
|
||||
if isVisible, let animationNode = self.animationNode, let file = self.animationFile {
|
||||
let pathPrefix = self.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id)
|
||||
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: self.context.account, resource: file.resource, isVideo: file.isVideoSticker || file.mimeType == "video/webm")
|
||||
animationNode.setup(source: source, width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .direct(cachePathPrefix: pathPrefix))
|
||||
}
|
||||
self.animationNode?.visibility = isVisible
|
||||
if isVisible, let videoContent = self.videoContent, self.videoLoopCount != maxVideoLoopCount {
|
||||
if self.videoNode == nil {
|
||||
let context = self.context
|
||||
@ -247,9 +278,13 @@ public final class AvatarVideoNode: ASDisplayNode {
|
||||
videoNode.updateLayout(size: size, transition: transition)
|
||||
}
|
||||
|
||||
if let itemLayer = self.itemLayer {
|
||||
let itemSize = CGSize(width: size.width * 0.67, height: size.height * 0.67)
|
||||
let itemFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - itemSize.width) / 2.0), y: floorToScreenPixels((size.height - itemSize.height) / 2.0)), size: itemSize)
|
||||
if let animationNode = self.animationNode {
|
||||
animationNode.frame = itemFrame
|
||||
animationNode.updateLayout(size: itemSize)
|
||||
}
|
||||
if let itemLayer = self.itemLayer {
|
||||
itemLayer.frame = itemFrame
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import UniversalMediaPlayer
|
||||
import RadialStatusNode
|
||||
import TelegramUIPreferences
|
||||
import AvatarNode
|
||||
import AvatarVideoNode
|
||||
|
||||
private class PeerInfoAvatarListLoadingStripNode: ASImageNode {
|
||||
private var currentInHierarchy = false
|
||||
@ -210,6 +211,7 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||
private var videoContent: NativeVideoContent?
|
||||
private var videoStartTimestamp: Double?
|
||||
private let playbackStartDisposable = MetaDisposable()
|
||||
private var markupNode: AvatarVideoNode?
|
||||
private let statusDisposable = MetaDisposable()
|
||||
private let preloadDisposable = MetaDisposable()
|
||||
private let statusNode: RadialStatusNode
|
||||
@ -260,6 +262,7 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||
self.preloadDisposable.set(preloadVideoResource(postbox: self.context.account.postbox, userLocation: .other, userContentType: .video, resourceReference: videoContent.fileReference.resourceReference(videoContent.fileReference.media.resource), duration: duration).start())
|
||||
}
|
||||
}
|
||||
self.markupNode?.updateVisibility(isCentral)
|
||||
}
|
||||
}
|
||||
|
||||
@ -346,6 +349,12 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||
}
|
||||
transition.updateAlpha(node: videoNode, alpha: 1.0 - fraction)
|
||||
}
|
||||
if let markupNode = self.markupNode {
|
||||
if case .immediate = transition, fraction == 1.0 {
|
||||
return
|
||||
}
|
||||
transition.updateAlpha(node: markupNode, alpha: 1.0 - fraction)
|
||||
}
|
||||
}
|
||||
|
||||
private func setupVideoPlayback() {
|
||||
@ -443,24 +452,27 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||
let videoRepresentations: [VideoRepresentationWithReference]
|
||||
let immediateThumbnailData: Data?
|
||||
var id: Int64
|
||||
let markup: TelegramMediaImage.EmojiMarkup?
|
||||
switch item {
|
||||
case let .custom(node):
|
||||
id = 0
|
||||
representations = []
|
||||
videoRepresentations = []
|
||||
immediateThumbnailData = nil
|
||||
id = 0
|
||||
markup = nil
|
||||
if !synchronous {
|
||||
self.addSubnode(node)
|
||||
}
|
||||
case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail):
|
||||
id = self.peer.id.id._internalGetInt64Value()
|
||||
representations = topRepresentations
|
||||
videoRepresentations = videoRepresentationsValue
|
||||
immediateThumbnailData = immediateThumbnail
|
||||
id = self.peer.id.id._internalGetInt64Value()
|
||||
if let resource = videoRepresentations.first?.representation.resource as? CloudPhotoSizeMediaResource {
|
||||
id = id &+ resource.photoId
|
||||
}
|
||||
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail, _, _):
|
||||
markup = nil
|
||||
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail, _, markupValue):
|
||||
representations = imageRepresentations
|
||||
videoRepresentations = videoRepresentationsValue
|
||||
immediateThumbnailData = immediateThumbnail
|
||||
@ -469,10 +481,37 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||
} else {
|
||||
id = self.peer.id.id._internalGetInt64Value()
|
||||
}
|
||||
markup = markupValue
|
||||
}
|
||||
self.imageNode.setSignal(chatAvatarGalleryPhoto(account: self.context.account, representations: representations, immediateThumbnailData: immediateThumbnailData, autoFetchFullSize: true, attemptSynchronously: synchronous, skipThumbnail: fullSizeOnly, skipBlurIfLarge: isMain), attemptSynchronously: synchronous, dispatchOnDisplayLink: false)
|
||||
|
||||
if let video = videoRepresentations.last, let peerReference = PeerReference(self.peer) {
|
||||
if let markup {
|
||||
if let videoNode = self.videoNode {
|
||||
self.videoContent = nil
|
||||
self.videoStartTimestamp = nil
|
||||
self.videoNode = nil
|
||||
|
||||
videoNode.removeFromSupernode()
|
||||
}
|
||||
self.statusPromise.set(.single(nil))
|
||||
self.statusDisposable.set(nil)
|
||||
|
||||
let markupNode: AvatarVideoNode
|
||||
if let current = self.markupNode {
|
||||
markupNode = current
|
||||
} else {
|
||||
markupNode = AvatarVideoNode(context: self.context)
|
||||
self.insertSubnode(markupNode, belowSubnode: self.statusNode)
|
||||
self.markupNode = markupNode
|
||||
}
|
||||
markupNode.update(markup: markup, size: CGSize(width: 320.0, height: 320.0))
|
||||
markupNode.updateVisibility(self.isCentral ?? true)
|
||||
|
||||
if !self.didSetReady {
|
||||
self.didSetReady = true
|
||||
self.isReady.set(.single(true))
|
||||
}
|
||||
} else if let video = videoRepresentations.last, let peerReference = PeerReference(self.peer) {
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])]))
|
||||
let videoContent = NativeVideoContent(id: .profileVideo(id, nil), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: fullSizeOnly, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, storeAfterDownload: nil)
|
||||
|
||||
@ -491,7 +530,6 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
self.statusPromise.set(.single(nil))
|
||||
|
||||
self.statusDisposable.set(nil)
|
||||
|
||||
self.imageNode.imageUpdated = { [weak self] _ in
|
||||
@ -520,6 +558,10 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||
videoNode.updateLayout(size: imageSize, transition: .immediate)
|
||||
videoNode.frame = imageFrame
|
||||
}
|
||||
if let markupNode = self.markupNode {
|
||||
markupNode.updateLayout(size: imageSize, cornerRadius: 0.0, transition: .immediate)
|
||||
markupNode.frame = imageFrame
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ private let phrases = [
|
||||
"Halo"
|
||||
]
|
||||
|
||||
private var activeCount = 13
|
||||
private var simultaneousDisplayCount = 13
|
||||
|
||||
private let referenceWidth: CGFloat = 1180
|
||||
private let positions: [CGPoint] = [
|
||||
@ -69,7 +69,7 @@ final class HelloView: UIView, PhoneDemoDecorationView {
|
||||
let phraseIds = Array(self.availablePhraseIds()).shuffled()
|
||||
let positionIds = Array(self.availablePositionIds()).shuffled()
|
||||
|
||||
for i in 0 ..< activeCount {
|
||||
for i in 0 ..< simultaneousDisplayCount {
|
||||
let delay: Double = Double.random(in: 0.0 ..< 0.8)
|
||||
Queue.mainQueue().after(delay) {
|
||||
self.spawnPhrase(phraseIds[i], positionIndex: positionIds[i])
|
||||
|
@ -838,6 +838,34 @@ public extension TelegramEngine.EngineData.Item {
|
||||
}
|
||||
}
|
||||
|
||||
public struct TranslationHidden: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
|
||||
public typealias Result = Bool
|
||||
|
||||
fileprivate var id: EnginePeer.Id
|
||||
public var mapKey: EnginePeer.Id {
|
||||
return self.id
|
||||
}
|
||||
|
||||
public init(id: EnginePeer.Id) {
|
||||
self.id = id
|
||||
}
|
||||
|
||||
var key: PostboxViewKey {
|
||||
return .cachedPeerData(peerId: self.id)
|
||||
}
|
||||
|
||||
func extract(view: PostboxView) -> Result {
|
||||
guard let view = view as? CachedPeerDataView else {
|
||||
preconditionFailure()
|
||||
}
|
||||
if let cachedData = view.cachedPeerData as? CachedChannelData {
|
||||
return cachedData.flags.contains(.translationHidden)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct LegacyGroupParticipants: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
|
||||
public typealias Result = EnginePeerCachedInfoItem<[EngineLegacyGroupParticipant]>
|
||||
|
||||
|
@ -6737,15 +6737,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return peer?.isPremium ?? false
|
||||
} |> distinctUntilChanged
|
||||
|
||||
let isHidden = self.chatDisplayNode.historyNode.cachedPeerDataAndMessages
|
||||
|> map { cachedDataAndMessages -> Bool in
|
||||
let (cachedData, _) = cachedDataAndMessages
|
||||
var isHidden = false
|
||||
if let cachedData = cachedData as? CachedChannelData, cachedData.flags.contains(.translationHidden) {
|
||||
isHidden = true
|
||||
}
|
||||
return isHidden
|
||||
} |> distinctUntilChanged
|
||||
let isHidden = self.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.TranslationHidden(id: self.context.account.peerId))
|
||||
|> distinctUntilChanged
|
||||
self.translationStateDisposable = (combineLatest(
|
||||
queue: .concurrentDefaultQueue(),
|
||||
isPremium,
|
||||
|
@ -29,6 +29,7 @@ import MultiAnimationRenderer
|
||||
import ComponentDisplayAdapters
|
||||
import ChatTitleView
|
||||
import AppBundle
|
||||
import AvatarVideoNode
|
||||
|
||||
enum PeerInfoHeaderButtonKey: Hashable {
|
||||
case message
|
||||
@ -309,6 +310,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
|
||||
let avatarNode: AvatarNode
|
||||
fileprivate var videoNode: UniversalVideoNode?
|
||||
fileprivate var markupNode: AvatarVideoNode?
|
||||
fileprivate var iconView: ComponentView<Empty>?
|
||||
private var videoContent: NativeVideoContent?
|
||||
private var videoStartTimestamp: Double?
|
||||
@ -380,12 +382,23 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
if fraction > 0.0 {
|
||||
self.videoNode?.pause()
|
||||
videoNode.pause()
|
||||
} else {
|
||||
self.videoNode?.play()
|
||||
videoNode.play()
|
||||
}
|
||||
transition.updateAlpha(node: videoNode, alpha: 1.0 - fraction)
|
||||
}
|
||||
if let markupNode = self.markupNode {
|
||||
if case .immediate = transition, fraction == 1.0 {
|
||||
return
|
||||
}
|
||||
if fraction > 0.0 {
|
||||
markupNode.updateVisibility(false)
|
||||
} else {
|
||||
markupNode.updateVisibility(true)
|
||||
}
|
||||
transition.updateAlpha(node: markupNode, alpha: 1.0 - fraction)
|
||||
}
|
||||
}
|
||||
|
||||
var removedPhotoResourceIds = Set<String>()
|
||||
@ -461,9 +474,11 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
var isForum = false
|
||||
let avatarCornerRadius: CGFloat
|
||||
if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) {
|
||||
avatarCornerRadius = floor(avatarSize * 0.25)
|
||||
isForum = true
|
||||
} else {
|
||||
avatarCornerRadius = avatarSize / 2.0
|
||||
}
|
||||
@ -485,12 +500,14 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
let videoRepresentations: [VideoRepresentationWithReference]
|
||||
let immediateThumbnailData: Data?
|
||||
var videoId: Int64
|
||||
let markup: TelegramMediaImage.EmojiMarkup?
|
||||
switch item {
|
||||
case .custom:
|
||||
representations = []
|
||||
videoRepresentations = []
|
||||
immediateThumbnailData = nil
|
||||
videoId = 0
|
||||
markup = nil
|
||||
case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail):
|
||||
representations = topRepresentations
|
||||
videoRepresentations = videoRepresentationsValue
|
||||
@ -499,7 +516,8 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
if let resource = videoRepresentations.first?.representation.resource as? CloudPhotoSizeMediaResource {
|
||||
videoId = videoId &+ resource.photoId
|
||||
}
|
||||
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail, _, _):
|
||||
markup = nil
|
||||
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail, _, markupValue):
|
||||
representations = imageRepresentations
|
||||
videoRepresentations = videoRepresentationsValue
|
||||
immediateThumbnailData = immediateThumbnail
|
||||
@ -508,11 +526,31 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
} else {
|
||||
videoId = peer.id.id._internalGetInt64Value()
|
||||
}
|
||||
markup = markupValue
|
||||
}
|
||||
|
||||
self.containerNode.isGestureEnabled = !isSettings
|
||||
|
||||
if threadInfo == nil, let video = videoRepresentations.last, let peerReference = PeerReference(peer) {
|
||||
if let markup {
|
||||
if let videoNode = self.videoNode {
|
||||
self.videoContent = nil
|
||||
self.videoStartTimestamp = nil
|
||||
self.videoNode = nil
|
||||
|
||||
videoNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
let markupNode: AvatarVideoNode
|
||||
if let current = self.markupNode {
|
||||
markupNode = current
|
||||
} else {
|
||||
markupNode = AvatarVideoNode(context: self.context)
|
||||
self.containerNode.addSubnode(markupNode)
|
||||
self.markupNode = markupNode
|
||||
}
|
||||
markupNode.update(markup: markup, size: CGSize(width: 320.0, height: 320.0))
|
||||
markupNode.updateVisibility(true)
|
||||
} else if threadInfo == nil, let video = videoRepresentations.last, let peerReference = PeerReference(peer) {
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])]))
|
||||
let videoContent = NativeVideoContent(id: .profileVideo(videoId, nil), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: peer.isCopyProtectionEnabled, storeAfterDownload: nil)
|
||||
if videoContent.id != self.videoContent?.id {
|
||||
@ -553,28 +591,51 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
self.videoContent = videoContent
|
||||
self.videoNode = videoNode
|
||||
|
||||
let maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size))
|
||||
let maskPath: UIBezierPath
|
||||
if isForum {
|
||||
maskPath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size), cornerRadius: avatarCornerRadius)
|
||||
} else {
|
||||
maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size))
|
||||
}
|
||||
let shape = CAShapeLayer()
|
||||
shape.path = maskPath.cgPath
|
||||
videoNode.layer.mask = shape
|
||||
|
||||
self.containerNode.addSubnode(videoNode)
|
||||
}
|
||||
} else if let videoNode = self.videoNode {
|
||||
} else {
|
||||
if let markupNode = self.markupNode {
|
||||
self.markupNode = nil
|
||||
markupNode.removeFromSupernode()
|
||||
}
|
||||
if let videoNode = self.videoNode {
|
||||
self.videoStartTimestamp = nil
|
||||
self.videoContent = nil
|
||||
self.videoNode = nil
|
||||
|
||||
videoNode.removeFromSupernode()
|
||||
}
|
||||
} else if let videoNode = self.videoNode {
|
||||
}
|
||||
} else {
|
||||
if let markupNode = self.markupNode {
|
||||
self.markupNode = nil
|
||||
markupNode.removeFromSupernode()
|
||||
}
|
||||
if let videoNode = self.videoNode {
|
||||
self.videoStartTimestamp = nil
|
||||
self.videoContent = nil
|
||||
self.videoNode = nil
|
||||
|
||||
videoNode.removeFromSupernode()
|
||||
|
||||
}
|
||||
self.containerNode.isGestureEnabled = false
|
||||
}
|
||||
|
||||
if let markupNode = self.markupNode {
|
||||
markupNode.frame = self.avatarNode.frame
|
||||
markupNode.updateLayout(size: self.avatarNode.frame.size, cornerRadius: avatarCornerRadius, transition: .immediate)
|
||||
}
|
||||
|
||||
if let videoNode = self.videoNode {
|
||||
if self.canAttachVideo {
|
||||
videoNode.updateLayout(size: self.avatarNode.frame.size, transition: .immediate)
|
||||
@ -730,6 +791,7 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
|
||||
private let context: AccountContext
|
||||
let avatarNode: AvatarNode
|
||||
fileprivate var videoNode: UniversalVideoNode?
|
||||
fileprivate var markupNode: AvatarVideoNode?
|
||||
private var videoContent: NativeVideoContent?
|
||||
private var videoStartTimestamp: Double?
|
||||
var item: PeerInfoAvatarListItem?
|
||||
@ -799,8 +861,10 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
|
||||
self.avatarNode.setPeer(context: self.context, theme: theme, peer: EnginePeer(peer), overrideImage: overrideImage, clipStyle: .none, synchronousLoad: false, displayDimensions: CGSize(width: avatarSize, height: avatarSize))
|
||||
self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize))
|
||||
|
||||
var isForum = false
|
||||
let avatarCornerRadius: CGFloat
|
||||
if let channel = peer as? TelegramChannel, channel.flags.contains(.isForum) {
|
||||
isForum = true
|
||||
avatarCornerRadius = floor(avatarSize * 0.25)
|
||||
} else {
|
||||
avatarCornerRadius = avatarSize / 2.0
|
||||
@ -816,35 +880,63 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
|
||||
let representations: [ImageRepresentationWithReference]
|
||||
let videoRepresentations: [VideoRepresentationWithReference]
|
||||
let immediateThumbnailData: Data?
|
||||
var id: Int64
|
||||
var videoId: Int64
|
||||
let markup: TelegramMediaImage.EmojiMarkup?
|
||||
switch item {
|
||||
case .custom:
|
||||
representations = []
|
||||
videoRepresentations = []
|
||||
immediateThumbnailData = nil
|
||||
id = 0
|
||||
videoId = 0
|
||||
markup = nil
|
||||
case let .topImage(topRepresentations, videoRepresentationsValue, immediateThumbnail):
|
||||
representations = topRepresentations
|
||||
videoRepresentations = videoRepresentationsValue
|
||||
immediateThumbnailData = immediateThumbnail
|
||||
id = peer.id.id._internalGetInt64Value()
|
||||
videoId = peer.id.id._internalGetInt64Value()
|
||||
if let resource = videoRepresentations.first?.representation.resource as? CloudPhotoSizeMediaResource {
|
||||
id = id &+ resource.photoId
|
||||
videoId = videoId &+ resource.photoId
|
||||
}
|
||||
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail, _, _):
|
||||
markup = nil
|
||||
case let .image(reference, imageRepresentations, videoRepresentationsValue, immediateThumbnail, _, markupValue):
|
||||
representations = imageRepresentations
|
||||
videoRepresentations = videoRepresentationsValue
|
||||
immediateThumbnailData = immediateThumbnail
|
||||
if case let .cloud(imageId, _, _) = reference {
|
||||
id = imageId
|
||||
videoId = imageId
|
||||
} else {
|
||||
id = peer.id.id._internalGetInt64Value()
|
||||
videoId = peer.id.id._internalGetInt64Value()
|
||||
}
|
||||
markup = markupValue
|
||||
}
|
||||
|
||||
if let markup {
|
||||
if let videoNode = self.videoNode {
|
||||
self.videoContent = nil
|
||||
self.videoStartTimestamp = nil
|
||||
self.videoNode = nil
|
||||
|
||||
videoNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
let markupNode: AvatarVideoNode
|
||||
if let current = self.markupNode {
|
||||
markupNode = current
|
||||
} else {
|
||||
markupNode = AvatarVideoNode(context: self.context)
|
||||
self.insertSubnode(markupNode, aboveSubnode: self.avatarNode)
|
||||
self.markupNode = markupNode
|
||||
}
|
||||
markupNode.update(markup: markup, size: CGSize(width: 320.0, height: 320.0))
|
||||
markupNode.updateVisibility(true)
|
||||
} else if threadData == nil, let video = videoRepresentations.last, let peerReference = PeerReference(peer) {
|
||||
if let markupNode = self.markupNode {
|
||||
self.markupNode = nil
|
||||
markupNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if threadData == nil, let video = videoRepresentations.last, let peerReference = PeerReference(peer) {
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [])]))
|
||||
let videoContent = NativeVideoContent(id: .profileVideo(id, nil), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: peer.isCopyProtectionEnabled, storeAfterDownload: nil)
|
||||
let videoContent = NativeVideoContent(id: .profileVideo(videoId, nil), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: peer.isCopyProtectionEnabled, storeAfterDownload: nil)
|
||||
if videoContent.id != self.videoContent?.id {
|
||||
self.videoNode?.removeFromSupernode()
|
||||
|
||||
@ -855,20 +947,31 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
|
||||
self.videoContent = videoContent
|
||||
self.videoNode = videoNode
|
||||
|
||||
let maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size))
|
||||
let maskPath: UIBezierPath
|
||||
if isForum {
|
||||
maskPath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size), cornerRadius: avatarCornerRadius)
|
||||
} else {
|
||||
maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size))
|
||||
}
|
||||
let shape = CAShapeLayer()
|
||||
shape.path = maskPath.cgPath
|
||||
videoNode.layer.mask = shape
|
||||
|
||||
self.insertSubnode(videoNode, aboveSubnode: self.avatarNode)
|
||||
}
|
||||
} else if let videoNode = self.videoNode {
|
||||
} else {
|
||||
if let markupNode = self.markupNode {
|
||||
self.markupNode = nil
|
||||
markupNode.removeFromSupernode()
|
||||
}
|
||||
if let videoNode = self.videoNode {
|
||||
self.videoStartTimestamp = nil
|
||||
self.videoContent = nil
|
||||
self.videoNode = nil
|
||||
|
||||
videoNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
} else if let videoNode = self.videoNode {
|
||||
self.videoStartTimestamp = nil
|
||||
self.videoContent = nil
|
||||
@ -877,6 +980,11 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
|
||||
videoNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if let markupNode = self.markupNode {
|
||||
markupNode.frame = self.avatarNode.frame
|
||||
markupNode.updateLayout(size: self.avatarNode.frame.size, cornerRadius: avatarCornerRadius, transition: .immediate)
|
||||
}
|
||||
|
||||
if let videoNode = self.videoNode {
|
||||
if self.canAttachVideo {
|
||||
videoNode.updateLayout(size: self.avatarNode.frame.size, transition: .immediate)
|
||||
@ -897,6 +1005,8 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
final class PeerInfoAvatarListNode: ASDisplayNode {
|
||||
private let isSettings: Bool
|
||||
let pinchSourceNode: PinchSourceContainerNode
|
||||
@ -1018,6 +1128,8 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
|
||||
if let currentItemNode = self.listContainerNode.currentItemNode, case .animated = transition {
|
||||
if let _ = self.avatarContainerNode.videoNode {
|
||||
|
||||
} else if let _ = self.avatarContainerNode.markupNode {
|
||||
|
||||
} else if let unroundedImage = self.avatarContainerNode.avatarNode.unroundedImage {
|
||||
let avatarCopyView = UIImageView()
|
||||
avatarCopyView.image = unroundedImage
|
||||
|
Loading…
x
Reference in New Issue
Block a user