Swiftgram/submodules/TelegramCallsUI/Sources/VoiceChatPeerProfileNode.swift
2021-06-10 01:15:00 +04:00

490 lines
33 KiB
Swift

import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import Postbox
import TelegramCore
import SyncCore
import TelegramPresentationData
import TelegramUIPreferences
import PresentationDataUtils
import AvatarNode
import TelegramStringFormatting
import ContextUI
import AccountContext
import LegacyComponents
import PeerInfoAvatarListNode
private let backgroundCornerRadius: CGFloat = 14.0
final class VoiceChatPeerProfileNode: ASDisplayNode {
private let context: AccountContext
private let size: CGSize
private var peer: Peer
private var text: VoiceChatParticipantItem.ParticipantText
private let customNode: ASDisplayNode?
private let additionalEntry: Signal<(TelegramMediaImageRepresentation, Float)?, NoError>
private let backgroundImageNode: ASImageNode
private let avatarListContainerNode: ASDisplayNode
let avatarListWrapperNode: PinchSourceContainerNode
let avatarListNode: PeerInfoAvatarListContainerNode
private var videoFadeNode: ASImageNode
private let infoNode: ASDisplayNode
private let titleNode: ImmediateTextNode
private let statusNode: VoiceChatParticipantStatusNode
private var videoNode: GroupVideoNode?
private var appeared = false
init(context: AccountContext, size: CGSize, peer: Peer, text: VoiceChatParticipantItem.ParticipantText, customNode: ASDisplayNode? = nil, additionalEntry: Signal<(TelegramMediaImageRepresentation, Float)?, NoError>, requestDismiss: (() -> Void)?) {
self.context = context
self.size = size
self.peer = peer
self.text = text
self.customNode = customNode
self.additionalEntry = additionalEntry
self.backgroundImageNode = ASImageNode()
self.backgroundImageNode.clipsToBounds = true
self.backgroundImageNode.displaysAsynchronously = false
self.backgroundImageNode.displayWithoutProcessing = true
self.videoFadeNode = ASImageNode()
self.videoFadeNode.displaysAsynchronously = false
self.videoFadeNode.contentMode = .scaleToFill
self.avatarListContainerNode = ASDisplayNode()
self.avatarListContainerNode.clipsToBounds = true
self.avatarListWrapperNode = PinchSourceContainerNode()
self.avatarListWrapperNode.clipsToBounds = true
self.avatarListWrapperNode.cornerRadius = backgroundCornerRadius
self.avatarListNode = PeerInfoAvatarListContainerNode(context: context)
self.avatarListNode.backgroundColor = .clear
self.avatarListNode.peer = peer
self.avatarListNode.firstFullSizeOnly = true
self.avatarListNode.offsetLocation = true
self.avatarListNode.customCenterTapAction = {
requestDismiss?()
}
self.infoNode = ASDisplayNode()
self.titleNode = ImmediateTextNode()
self.titleNode.isUserInteractionEnabled = false
self.titleNode.contentMode = .left
self.titleNode.contentsScale = UIScreen.main.scale
self.statusNode = VoiceChatParticipantStatusNode()
self.statusNode.isUserInteractionEnabled = false
super.init()
self.clipsToBounds = true
self.addSubnode(self.backgroundImageNode)
self.addSubnode(self.infoNode)
self.addSubnode(self.videoFadeNode)
self.addSubnode(self.avatarListWrapperNode)
self.infoNode.addSubnode(self.titleNode)
self.infoNode.addSubnode(self.statusNode)
self.avatarListContainerNode.addSubnode(self.avatarListNode)
self.avatarListContainerNode.addSubnode(self.avatarListNode.controlsClippingOffsetNode)
self.avatarListWrapperNode.contentNode.addSubnode(self.avatarListContainerNode)
self.avatarListWrapperNode.activate = { [weak self] sourceNode in
guard let strongSelf = self else {
return
}
strongSelf.avatarListNode.controlsContainerNode.alpha = 0.0
let pinchController = PinchController(sourceNode: sourceNode, getContentAreaInScreenSpace: {
return UIScreen.main.bounds
})
context.sharedContext.mainWindow?.presentInGlobalOverlay(pinchController)
}
self.avatarListWrapperNode.deactivated = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.avatarListWrapperNode.contentNode.layer.animate(from: 0.0 as NSNumber, to: backgroundCornerRadius as NSNumber, keyPath: "cornerRadius", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.3, completion: { _ in
})
}
self.avatarListWrapperNode.animatedOut = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.avatarListNode.controlsContainerNode.alpha = 1.0
strongSelf.avatarListNode.controlsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
}
self.updateInfo(size: size, animate: false)
}
func updateInfo(size: CGSize, animate: Bool) {
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
let titleFont = Font.regular(17.0)
let titleColor = UIColor.white
var titleAttributedString: NSAttributedString?
if let user = self.peer as? TelegramUser {
if let firstName = user.firstName, let lastName = user.lastName, !firstName.isEmpty, !lastName.isEmpty {
let string = NSMutableAttributedString()
switch presentationData.nameDisplayOrder {
case .firstLast:
string.append(NSAttributedString(string: firstName, font: titleFont, textColor: titleColor))
string.append(NSAttributedString(string: " ", font: titleFont, textColor: titleColor))
string.append(NSAttributedString(string: lastName, font: titleFont, textColor: titleColor))
case .lastFirst:
string.append(NSAttributedString(string: lastName, font: titleFont, textColor: titleColor))
string.append(NSAttributedString(string: " ", font: titleFont, textColor: titleColor))
string.append(NSAttributedString(string: firstName, font: titleFont, textColor: titleColor))
}
titleAttributedString = string
} else if let firstName = user.firstName, !firstName.isEmpty {
titleAttributedString = NSAttributedString(string: firstName, font: titleFont, textColor: titleColor)
} else if let lastName = user.lastName, !lastName.isEmpty {
titleAttributedString = NSAttributedString(string: lastName, font: titleFont, textColor: titleColor)
} else {
titleAttributedString = NSAttributedString(string: presentationData.strings.User_DeletedAccount, font: titleFont, textColor: titleColor)
}
} else if let group = peer as? TelegramGroup {
titleAttributedString = NSAttributedString(string: group.title, font: titleFont, textColor: titleColor)
} else if let channel = peer as? TelegramChannel {
titleAttributedString = NSAttributedString(string: channel.title, font: titleFont, textColor: titleColor)
}
self.titleNode.attributedText = titleAttributedString
let titleSize = self.titleNode.updateLayout(CGSize(width: self.size.width - 24.0, height: size.height))
let makeStatusLayout = self.statusNode.asyncLayout()
let (statusLayout, statusApply) = makeStatusLayout(CGSize(width: self.size.width - 24.0, height: CGFloat.greatestFiniteMagnitude), self.text, true)
let _ = statusApply()
self.titleNode.frame = CGRect(origin: CGPoint(x: 14.0, y: 0.0), size: titleSize)
self.statusNode.frame = CGRect(origin: CGPoint(x: 14.0, y: titleSize.height + 3.0), size: statusLayout)
let totalHeight = titleSize.height + statusLayout.height + 3.0 + 8.0
let infoFrame = CGRect(x: 0.0, y: size.height - totalHeight, width: self.size.width, height: totalHeight)
if animate {
let springDuration: Double = !self.appeared ? 0.42 : 0.3
let springDamping: CGFloat = !self.appeared ? 124.0 : 1000.0
let initialInfoPosition = self.infoNode.position
self.infoNode.layer.position = infoFrame.center
let initialInfoBounds = self.infoNode.bounds
self.infoNode.layer.bounds = CGRect(origin: CGPoint(), size: infoFrame.size)
self.infoNode.layer.animateSpring(from: NSValue(cgPoint: initialInfoPosition), to: NSValue(cgPoint: self.infoNode.position), keyPath: "position", duration: springDuration, delay: 0.0, initialVelocity: 0.0, damping: springDamping)
self.infoNode.layer.animateSpring(from: NSValue(cgRect: initialInfoBounds), to: NSValue(cgRect: self.infoNode.bounds), keyPath: "bounds", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
} else {
self.infoNode.frame = infoFrame
}
}
func animateIn(from sourceNode: ASDisplayNode, targetRect: CGRect, transition: ContainedViewLayoutTransition) {
let radiusTransition = ContainedViewLayoutTransition.animated(duration: 0.15, curve: .easeInOut)
let springDuration: Double = 0.42
let springDamping: CGFloat = 124.0
if let sourceNode = sourceNode as? VoiceChatTileItemNode {
let sourceRect = sourceNode.bounds
self.backgroundImageNode.frame = sourceNode.bounds
self.updateInfo(size: sourceNode.bounds.size, animate: false)
self.updateInfo(size: targetRect.size, animate: true)
self.backgroundImageNode.image = generateImage(CGSize(width: backgroundCornerRadius * 2.0, height: backgroundCornerRadius * 2.0), rotatedContext: { (size, context) in
let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds)
context.setFillColor(UIColor(rgb: 0x1c1c1e).cgColor)
context.fillEllipse(in: bounds)
context.fill(CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height / 2.0))
})?.stretchableImage(withLeftCapWidth: Int(backgroundCornerRadius), topCapHeight: Int(backgroundCornerRadius))
self.backgroundImageNode.cornerRadius = backgroundCornerRadius
transition.updateCornerRadius(node: self.backgroundImageNode, cornerRadius: 0.0)
let initialRect = sourceRect
let initialScale: CGFloat = sourceRect.width / targetRect.width
let targetSize = CGSize(width: targetRect.size.width, height: targetRect.size.width)
self.avatarListWrapperNode.update(size: targetSize, transition: .immediate)
self.avatarListWrapperNode.frame = CGRect(x: targetRect.minX, y: targetRect.minY, width: targetRect.width, height: targetRect.width + backgroundCornerRadius)
self.avatarListContainerNode.frame = CGRect(origin: CGPoint(), size: targetSize)
self.avatarListContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.avatarListContainerNode.cornerRadius = targetRect.width / 2.0
var appearenceTransition = transition
if transition.isAnimated {
appearenceTransition = .animated(duration: springDuration, curve: .customSpring(damping: springDamping, initialVelocity: 0.0))
}
if let videoNode = sourceNode.videoNode {
videoNode.updateLayout(size: targetSize, layoutMode: .fillOrFitToSquare, transition: appearenceTransition)
appearenceTransition.updateFrame(node: videoNode, frame: CGRect(origin: CGPoint(), size: targetSize))
appearenceTransition.updateFrame(node: sourceNode.videoContainerNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: targetSize.width, height: targetSize.height + backgroundCornerRadius)))
sourceNode.videoContainerNode.cornerRadius = backgroundCornerRadius
}
self.insertSubnode(sourceNode.videoContainerNode, belowSubnode: self.avatarListWrapperNode)
if let snapshotView = sourceNode.infoNode.view.snapshotView(afterScreenUpdates: false) {
self.videoFadeNode.image = tileFadeImage
self.videoFadeNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
self.videoFadeNode.frame = CGRect(x: 0.0, y: sourceRect.height - sourceNode.fadeNode.frame.height, width: sourceRect.width, height: sourceNode.fadeNode.frame.height)
self.insertSubnode(self.videoFadeNode, aboveSubnode: sourceNode.videoContainerNode)
self.view.insertSubview(snapshotView, aboveSubview: sourceNode.videoContainerNode.view)
snapshotView.frame = sourceRect
appearenceTransition.updateFrame(view: snapshotView, frame: CGRect(origin: CGPoint(x: 0.0, y: targetSize.height - snapshotView.frame.size.height), size: snapshotView.frame.size))
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
snapshotView.removeFromSuperview()
})
appearenceTransition.updateFrame(node: self.videoFadeNode, frame: CGRect(origin: CGPoint(x: 0.0, y: targetSize.height - self.videoFadeNode.frame.size.height), size: CGSize(width: targetSize.width, height: self.videoFadeNode.frame.height)))
self.videoFadeNode.alpha = 0.0
self.videoFadeNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
}
self.avatarListWrapperNode.layer.animateSpring(from: initialScale as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
self.avatarListWrapperNode.layer.animateSpring(from: NSValue(cgPoint: initialRect.center), to: NSValue(cgPoint: self.avatarListWrapperNode.position), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, completion: { [weak self] _ in
if let strongSelf = self {
strongSelf.avatarListNode.updateCustomItemsOnlySynchronously = false
strongSelf.avatarListNode.currentItemNode?.addSubnode(sourceNode.videoContainerNode)
}
})
radiusTransition.updateCornerRadius(node: self.avatarListContainerNode, cornerRadius: 0.0)
self.avatarListWrapperNode.contentNode.clipsToBounds = true
self.avatarListNode.frame = CGRect(x: targetRect.width / 2.0, y: targetRect.width / 2.0, width: targetRect.width, height: targetRect.width)
self.avatarListNode.controlsClippingNode.frame = CGRect(x: -targetRect.width / 2.0, y: -targetRect.width / 2.0, width: targetRect.width, height: targetRect.width)
self.avatarListNode.controlsClippingOffsetNode.frame = CGRect(origin: CGPoint(x: targetRect.width / 2.0, y: targetRect.width / 2.0), size: CGSize())
self.avatarListNode.stripContainerNode.frame = CGRect(x: 0.0, y: 13.0, width: targetRect.width, height: 2.0)
self.avatarListNode.shadowNode.frame = CGRect(x: 0.0, y: 0.0, width: targetRect.width, height: 44.0)
self.avatarListNode.updateCustomItemsOnlySynchronously = true
self.avatarListNode.update(size: targetSize, peer: self.peer, customNode: self.customNode, additionalEntry: self.additionalEntry, isExpanded: true, transition: .immediate)
let backgroundTargetRect = CGRect(x: 0.0, y: targetSize.height - backgroundCornerRadius * 2.0, width: targetRect.width, height: targetRect.height - targetSize.height + backgroundCornerRadius * 2.0)
let initialBackgroundPosition = self.backgroundImageNode.position
self.backgroundImageNode.layer.position = backgroundTargetRect.center
let initialBackgroundBounds = self.backgroundImageNode.bounds
self.backgroundImageNode.layer.bounds = CGRect(origin: CGPoint(), size: backgroundTargetRect.size)
self.backgroundImageNode.layer.animateSpring(from: NSValue(cgPoint: initialBackgroundPosition), to: NSValue(cgPoint: self.backgroundImageNode.position), keyPath: "position", duration: springDuration, delay: 0.0, initialVelocity: 0.0, damping: springDamping)
self.backgroundImageNode.layer.animateSpring(from: NSValue(cgRect: initialBackgroundBounds), to: NSValue(cgRect: self.backgroundImageNode.bounds), keyPath: "bounds", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
} else if let sourceNode = sourceNode as? VoiceChatFullscreenParticipantItemNode {
let sourceRect = sourceNode.bounds
self.backgroundImageNode.frame = sourceNode.bounds
self.updateInfo(size: sourceNode.bounds.size, animate: false)
self.updateInfo(size: targetRect.size, animate: true)
self.backgroundImageNode.image = generateImage(CGSize(width: backgroundCornerRadius * 2.0, height: backgroundCornerRadius * 2.0), rotatedContext: { (size, context) in
let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds)
context.setFillColor(UIColor(rgb: 0x1c1c1e).cgColor)
context.fillEllipse(in: bounds)
context.fill(CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height / 2.0))
})?.stretchableImage(withLeftCapWidth: Int(backgroundCornerRadius), topCapHeight: Int(backgroundCornerRadius))
self.backgroundImageNode.cornerRadius = backgroundCornerRadius
transition.updateCornerRadius(node: self.backgroundImageNode, cornerRadius: 0.0)
let initialRect = sourceNode.frame
let initialScale: CGFloat = sourceRect.width / targetRect.width
let targetSize = CGSize(width: targetRect.size.width, height: targetRect.size.width)
self.avatarListWrapperNode.update(size: targetSize, transition: .immediate)
self.avatarListWrapperNode.frame = CGRect(x: targetRect.minX, y: targetRect.minY, width: targetRect.width, height: targetRect.width + backgroundCornerRadius)
self.avatarListContainerNode.frame = CGRect(origin: CGPoint(), size: targetSize)
self.avatarListContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.avatarListContainerNode.cornerRadius = targetRect.width / 2.0
if false, let videoNode = sourceNode.videoNode {
videoNode.updateLayout(size: targetSize, layoutMode: .fillOrFitToSquare, transition: transition)
transition.updateFrame(node: videoNode, frame: CGRect(origin: CGPoint(), size: targetSize))
transition.updateFrame(node: sourceNode.videoContainerNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: targetSize.width, height: targetSize.height + backgroundCornerRadius)))
sourceNode.videoContainerNode.cornerRadius = backgroundCornerRadius
}
// self.insertSubnode(sourceNode.videoContainerNode, belowSubnode: self.avatarListWrapperNode)
// if let snapshotView = sourceNode.infoNode.view.snapshotView(afterScreenUpdates: false) {
// self.videoFadeNode.image = sourceNode.fadeNode.image
// self.videoFadeNode.frame = CGRect(x: 0.0, y: sourceRect.height - sourceNode.fadeNode.frame.height, width: sourceRect.width, height: sourceNode.fadeNode.frame.height)
//
// self.insertSubnode(self.videoFadeNode, aboveSubnode: sourceNode.videoContainerNode)
// self.view.insertSubview(snapshotView, aboveSubview: sourceNode.videoContainerNode.view)
// snapshotView.frame = sourceRect
// transition.updateFrame(view: snapshotView, frame: CGRect(origin: CGPoint(x: 0.0, y: targetSize.height - snapshotView.frame.size.height), size: snapshotView.frame.size))
// snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
// snapshotView.removeFromSuperview()
// })
// transition.updateFrame(node: self.videoFadeNode, frame: CGRect(origin: CGPoint(x: 0.0, y: targetSize.height - self.videoFadeNode.frame.size.height), size: CGSize(width: targetSize.width, height: self.videoFadeNode.frame.height)))
// self.videoFadeNode.alpha = 0.0
// self.videoFadeNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
// }
self.avatarListWrapperNode.layer.animateSpring(from: initialScale as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
self.avatarListWrapperNode.layer.animateSpring(from: NSValue(cgPoint: initialRect.center), to: NSValue(cgPoint: self.avatarListWrapperNode.position), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, completion: { [weak self] _ in
if let strongSelf = self {
strongSelf.avatarListNode.updateCustomItemsOnlySynchronously = false
// strongSelf.avatarListNode.currentItemNode?.addSubnode(sourceNode.videoContainerNode)
}
})
radiusTransition.updateCornerRadius(node: self.avatarListContainerNode, cornerRadius: 0.0)
self.avatarListWrapperNode.contentNode.clipsToBounds = true
self.avatarListNode.frame = CGRect(x: targetRect.width / 2.0, y: targetRect.width / 2.0, width: targetRect.width, height: targetRect.width)
self.avatarListNode.controlsClippingNode.frame = CGRect(x: -targetRect.width / 2.0, y: -targetRect.width / 2.0, width: targetRect.width, height: targetRect.width)
self.avatarListNode.controlsClippingOffsetNode.frame = CGRect(origin: CGPoint(x: targetRect.width / 2.0, y: targetRect.width / 2.0), size: CGSize())
self.avatarListNode.stripContainerNode.frame = CGRect(x: 0.0, y: 13.0, width: targetRect.width, height: 2.0)
self.avatarListNode.shadowNode.frame = CGRect(x: 0.0, y: 0.0, width: targetRect.width, height: 44.0)
self.avatarListNode.updateCustomItemsOnlySynchronously = true
self.avatarListNode.update(size: targetSize, peer: self.peer, customNode: nil, additionalEntry: self.additionalEntry, isExpanded: true, transition: .immediate)
let backgroundTargetRect = CGRect(x: 0.0, y: targetSize.height - backgroundCornerRadius * 2.0, width: targetRect.width, height: targetRect.height - targetSize.height + backgroundCornerRadius * 2.0)
let initialBackgroundPosition = self.backgroundImageNode.position
self.backgroundImageNode.layer.position = backgroundTargetRect.center
let initialBackgroundBounds = self.backgroundImageNode.bounds
self.backgroundImageNode.layer.bounds = CGRect(origin: CGPoint(), size: backgroundTargetRect.size)
self.backgroundImageNode.layer.animateSpring(from: NSValue(cgPoint: initialBackgroundPosition), to: NSValue(cgPoint: self.backgroundImageNode.position), keyPath: "position", duration: springDuration, delay: 0.0, initialVelocity: 0.0, damping: springDamping)
self.backgroundImageNode.layer.animateSpring(from: NSValue(cgRect: initialBackgroundBounds), to: NSValue(cgRect: self.backgroundImageNode.bounds), keyPath: "bounds", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
}
self.appeared = true
}
func animateOut(to targetNode: ASDisplayNode, targetRect: CGRect, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void = {}) {
let radiusTransition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
let springDuration: Double = 0.3
let springDamping: CGFloat = 1000.0
if let targetNode = targetNode as? VoiceChatTileItemNode {
let initialSize = self.bounds
self.updateInfo(size: targetRect.size, animate: true)
transition.updateCornerRadius(node: self.backgroundImageNode, cornerRadius: backgroundCornerRadius)
let targetScale = targetRect.width / avatarListContainerNode.frame.width
self.insertSubnode(targetNode.videoContainerNode, belowSubnode: self.avatarListWrapperNode)
self.insertSubnode(self.videoFadeNode, aboveSubnode: targetNode.videoContainerNode)
self.avatarListWrapperNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.avatarListWrapperNode.layer.animate(from: 1.0 as NSNumber, to: targetScale as NSNumber, keyPath: "transform.scale", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2, removeOnCompletion: false)
self.avatarListWrapperNode.layer.animate(from: NSValue(cgPoint: self.avatarListWrapperNode.position), to: NSValue(cgPoint: targetRect.center), keyPath: "position", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2, removeOnCompletion: false, completion: { [weak self, weak targetNode] _ in
if let targetNode = targetNode {
targetNode.contentNode.insertSubnode(targetNode.videoContainerNode, aboveSubnode: targetNode.backgroundNode)
}
completion()
self?.removeFromSupernode()
})
radiusTransition.updateCornerRadius(node: self.avatarListContainerNode, cornerRadius: backgroundCornerRadius)
if let snapshotView = targetNode.infoNode.view.snapshotView(afterScreenUpdates: true) {
self.view.insertSubview(snapshotView, aboveSubview: targetNode.videoContainerNode.view)
let snapshotFrame = snapshotView.frame
snapshotView.frame = CGRect(origin: CGPoint(x: 0.0, y: initialSize.width - snapshotView.frame.size.height), size: snapshotView.frame.size)
transition.updateFrame(view: snapshotView, frame: snapshotFrame)
snapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
transition.updateFrame(node: self.videoFadeNode, frame: CGRect(origin: CGPoint(x: 0.0, y: targetRect.height - self.videoFadeNode.frame.size.height), size: CGSize(width: targetRect.width, height: self.videoFadeNode.frame.height)))
self.videoFadeNode.alpha = 1.0
self.videoFadeNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
if let videoNode = targetNode.videoNode {
videoNode.updateLayout(size: targetRect.size, layoutMode: .fillOrFitToSquare, transition: transition)
transition.updateFrame(node: videoNode, frame: targetRect)
transition.updateFrame(node: targetNode.videoContainerNode, frame: targetRect)
}
let backgroundTargetRect = targetRect
let initialBackgroundPosition = self.backgroundImageNode.position
self.backgroundImageNode.layer.position = backgroundTargetRect.center
let initialBackgroundBounds = self.backgroundImageNode.bounds
self.backgroundImageNode.layer.bounds = CGRect(origin: CGPoint(), size: backgroundTargetRect.size)
self.backgroundImageNode.layer.animateSpring(from: NSValue(cgPoint: initialBackgroundPosition), to: NSValue(cgPoint: self.backgroundImageNode.position), keyPath: "position", duration: springDuration, delay: 0.0, initialVelocity: 0.0, damping: springDamping)
self.backgroundImageNode.layer.animateSpring(from: NSValue(cgRect: initialBackgroundBounds), to: NSValue(cgRect: self.backgroundImageNode.bounds), keyPath: "bounds", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
self.avatarListNode.stripContainerNode.alpha = 0.0
self.avatarListNode.stripContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
self.avatarListNode.shadowNode.alpha = 0.0
self.avatarListNode.shadowNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
self.infoNode.alpha = 0.0
self.infoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
} else if let targetNode = targetNode as? VoiceChatFullscreenParticipantItemNode {
let initialSize = self.bounds
self.updateInfo(size: targetRect.size, animate: true)
transition.updateCornerRadius(node: self.backgroundImageNode, cornerRadius: backgroundCornerRadius)
let targetScale = targetRect.width / avatarListContainerNode.frame.width
self.insertSubnode(targetNode.videoContainerNode, belowSubnode: self.avatarListWrapperNode)
self.insertSubnode(self.videoFadeNode, aboveSubnode: targetNode.videoContainerNode)
self.avatarListWrapperNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.avatarListWrapperNode.layer.animate(from: 1.0 as NSNumber, to: targetScale as NSNumber, keyPath: "transform.scale", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2, removeOnCompletion: false)
self.avatarListWrapperNode.layer.animate(from: NSValue(cgPoint: self.avatarListWrapperNode.position), to: NSValue(cgPoint: targetRect.center), keyPath: "position", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2, removeOnCompletion: false, completion: { [weak self, weak targetNode] _ in
if let targetNode = targetNode {
targetNode.offsetContainerNode.insertSubnode(targetNode.videoContainerNode, at: 0)
}
completion()
self?.removeFromSupernode()
})
radiusTransition.updateCornerRadius(node: self.avatarListContainerNode, cornerRadius: backgroundCornerRadius)
// if let snapshotView = targetNode.infoNode.view.snapshotView(afterScreenUpdates: false) {
// self.view.insertSubview(snapshotView, aboveSubview: targetNode.videoContainerNode.view)
// let snapshotFrame = snapshotView.frame
// snapshotView.frame = CGRect(origin: CGPoint(x: 0.0, y: initialSize.width - snapshotView.frame.size.height), size: snapshotView.frame.size)
// transition.updateFrame(view: snapshotView, frame: snapshotFrame)
// snapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
// transition.updateFrame(node: self.videoFadeNode, frame: CGRect(origin: CGPoint(x: 0.0, y: targetRect.height - self.videoFadeNode.frame.size.height), size: CGSize(width: targetRect.width, height: self.videoFadeNode.frame.height)))
// self.videoFadeNode.alpha = 1.0
// self.videoFadeNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
// }
if false, let videoNode = targetNode.videoNode {
videoNode.updateLayout(size: targetRect.size, layoutMode: .fillOrFitToSquare, transition: transition)
transition.updateFrame(node: videoNode, frame: targetRect)
transition.updateFrame(node: targetNode.videoContainerNode, frame: targetRect)
}
let backgroundTargetRect = targetRect
let initialBackgroundPosition = self.backgroundImageNode.position
self.backgroundImageNode.layer.position = backgroundTargetRect.center
let initialBackgroundBounds = self.backgroundImageNode.bounds
self.backgroundImageNode.layer.bounds = CGRect(origin: CGPoint(), size: backgroundTargetRect.size)
self.backgroundImageNode.layer.animateSpring(from: NSValue(cgPoint: initialBackgroundPosition), to: NSValue(cgPoint: self.backgroundImageNode.position), keyPath: "position", duration: springDuration, delay: 0.0, initialVelocity: 0.0, damping: springDamping)
self.backgroundImageNode.layer.animateSpring(from: NSValue(cgRect: initialBackgroundBounds), to: NSValue(cgRect: self.backgroundImageNode.bounds), keyPath: "bounds", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
self.avatarListNode.stripContainerNode.alpha = 0.0
self.avatarListNode.stripContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
self.avatarListNode.shadowNode.alpha = 0.0
self.avatarListNode.shadowNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
self.infoNode.alpha = 0.0
self.infoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
}
}
}