Swiftgram/submodules/WebSearchUI/Sources/WebSearchVideoGalleryItem.swift
2020-02-11 10:34:00 +04:00

541 lines
30 KiB
Swift

import Foundation
import UIKit
import AsyncDisplayKit
import SwiftSignalKit
import TelegramCore
import SyncCore
import Display
import Postbox
import TelegramPresentationData
import AccountContext
import RadialStatusNode
import GalleryUI
import TelegramUniversalVideoContent
import GalleryUI
class WebSearchVideoGalleryItem: GalleryItem {
let context: AccountContext
let presentationData: PresentationData
let result: ChatContextResult
let content: UniversalVideoContent
let controllerInteraction: WebSearchGalleryControllerInteraction?
init(context: AccountContext, presentationData: PresentationData, result: ChatContextResult, content: UniversalVideoContent, controllerInteraction: WebSearchGalleryControllerInteraction?) {
self.context = context
self.presentationData = presentationData
self.result = result
self.content = content
self.controllerInteraction = controllerInteraction
}
func node() -> GalleryItemNode {
let node = WebSearchVideoGalleryItemNode(context: self.context, presentationData: self.presentationData, controllerInteraction: self.controllerInteraction)
node.setupItem(self)
return node
}
func updateNode(node: GalleryItemNode) {
if let node = node as? WebSearchVideoGalleryItemNode {
node.setupItem(self)
}
}
func thumbnailItem() -> (Int64, GalleryThumbnailItem)? {
return nil
}
}
private struct FetchControls {
let fetch: () -> Void
let cancel: () -> Void
}
final class WebSearchVideoGalleryItemNode: ZoomableContentGalleryItemNode {
private let context: AccountContext
private let strings: PresentationStrings
private let controllerInteraction: WebSearchGalleryControllerInteraction?
fileprivate let _ready = Promise<Void>()
private let footerContentNode: WebSearchGalleryFooterContentNode
private var videoNode: UniversalVideoNode?
private let statusButtonNode: HighlightableButtonNode
private let statusNode: RadialStatusNode
private var isCentral = false
private var validLayout: (ContainerViewLayout, CGFloat)?
private var didPause = false
private var isPaused = true
private var requiresDownload = false
var item: WebSearchVideoGalleryItem?
private let statusDisposable = MetaDisposable()
private let fetchDisposable = MetaDisposable()
private var fetchStatus: MediaResourceStatus?
private var fetchControls: FetchControls?
var playbackCompleted: (() -> Void)?
init(context: AccountContext, presentationData: PresentationData, controllerInteraction: WebSearchGalleryControllerInteraction?) {
self.context = context
self.strings = presentationData.strings
self.controllerInteraction = controllerInteraction
self.footerContentNode = WebSearchGalleryFooterContentNode(context: context, presentationData: presentationData)
self.statusButtonNode = HighlightableButtonNode()
self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.5))
super.init()
self.statusButtonNode.addSubnode(self.statusNode)
self.statusButtonNode.addTarget(self, action: #selector(statusButtonPressed), forControlEvents: .touchUpInside)
self.addSubnode(self.statusButtonNode)
self.footerContentNode.cancel = {
controllerInteraction?.dismiss(true)
}
self.footerContentNode.send = { [weak self] in
if let strongSelf = self, let item = strongSelf.item {
controllerInteraction?.send(item.result)
}
}
}
deinit {
self.statusDisposable.dispose()
}
@objc override func contentTap(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
if recognizer.state == .ended {
if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation {
switch gesture {
case .tap:
if let item = self.item, let selectionState = item.controllerInteraction?.selectionState {
let legacyItem = legacyWebSearchItem(account: item.context.account, result: item.result)
selectionState.toggleItemSelection(legacyItem, success: nil)
}
case .doubleTap:
super.contentTap(recognizer)
default:
break
}
}
}
}
override func ready() -> Signal<Void, NoError> {
return self._ready.get()
}
override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
self.validLayout = (layout, navigationBarHeight)
let statusDiameter: CGFloat = 50.0
let statusFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - statusDiameter) / 2.0), y: floor((layout.size.height - statusDiameter) / 2.0)), size: CGSize(width: statusDiameter, height: statusDiameter))
transition.updateFrame(node: self.statusButtonNode, frame: statusFrame)
transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(), size: statusFrame.size))
}
func setupItem(_ item: WebSearchVideoGalleryItem) {
if self.item?.content.id != item.content.id {
var isAnimated = false
var mediaResource: MediaResource?
if let content = item.content as? NativeVideoContent {
isAnimated = content.fileReference.media.isAnimated
mediaResource = content.fileReference.media.resource
}
if let videoNode = self.videoNode {
videoNode.canAttachContent = false
videoNode.removeFromSupernode()
}
let mediaManager = item.context.sharedContext.mediaManager
let videoNode = UniversalVideoNode(postbox: item.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: item.content, priority: .gallery)
let videoSize = CGSize(width: item.content.dimensions.width * 2.0, height: item.content.dimensions.height * 2.0)
videoNode.updateLayout(size: videoSize, transition: .immediate)
self.videoNode = videoNode
videoNode.isUserInteractionEnabled = false
videoNode.backgroundColor = videoNode.ownsContentNode ? UIColor.black : UIColor(rgb: 0x333335)
videoNode.canAttachContent = true
self.requiresDownload = true
var mediaFileStatus: Signal<MediaResourceStatus?, NoError> = .single(nil)
if let mediaResource = mediaResource {
mediaFileStatus = item.context.account.postbox.mediaBox.resourceStatus(mediaResource)
|> map(Optional.init)
}
self.statusDisposable.set((combineLatest(videoNode.status, mediaFileStatus)
|> deliverOnMainQueue).start(next: { [weak self] value, fetchStatus in
if let strongSelf = self {
var initialBuffering = false
var isPaused = true
if let value = value {
if let zoomableContent = strongSelf.zoomableContent, !value.dimensions.width.isZero && !value.dimensions.height.isZero {
let videoSize = CGSize(width: value.dimensions.width * 2.0, height: value.dimensions.height * 2.0)
if !zoomableContent.0.equalTo(videoSize) {
strongSelf.zoomableContent = (videoSize, zoomableContent.1)
strongSelf.videoNode?.updateLayout(size: videoSize, transition: .immediate)
}
}
switch value.status {
case .playing:
isPaused = false
case let .buffering(_, whilePlaying):
initialBuffering = true
isPaused = !whilePlaying
var isStreaming = false
if let fetchStatus = strongSelf.fetchStatus {
switch fetchStatus {
case .Local:
break
default:
isStreaming = true
}
}
if let content = item.content as? NativeVideoContent, !isStreaming {
initialBuffering = false
if !content.enableSound {
isPaused = false
}
}
default:
if let content = item.content as? NativeVideoContent, !content.streamVideo.enabled {
if !content.enableSound {
isPaused = false
}
}
}
}
var fetching = false
if initialBuffering {
strongSelf.statusNode.transitionToState(.progress(color: .white, lineWidth: nil, value: nil, cancelEnabled: false), animated: false, completion: {})
} else {
var state: RadialStatusNodeState = .none
if let fetchStatus = fetchStatus {
if strongSelf.requiresDownload {
switch fetchStatus {
case let .Fetching(_, progress):
fetching = true
isPaused = true
state = .progress(color: .white, lineWidth: nil, value: CGFloat(max(0.027, progress)), cancelEnabled: false)
default:
break
}
}
}
strongSelf.statusNode.transitionToState(state, animated: false, completion: {})
}
strongSelf.isPaused = isPaused
strongSelf.fetchStatus = fetchStatus
strongSelf.statusButtonNode.isHidden = !initialBuffering && !isPaused && !fetching
}
}))
self.zoomableContent = (videoSize, videoNode)
videoNode.playbackCompleted = { [weak videoNode] in
Queue.mainQueue().async {
if !isAnimated {
videoNode?.seek(0.0)
}
}
}
self._ready.set(videoNode.ready)
}
self.item = item
}
override func centralityUpdated(isCentral: Bool) {
super.centralityUpdated(isCentral: isCentral)
if self.isCentral != isCentral {
self.isCentral = isCentral
if let videoNode = self.videoNode, videoNode.ownsContentNode {
if isCentral {
videoNode.play()
} else {
videoNode.pause()
}
}
}
}
override func activateAsInitial() {
if self.isCentral {
self.videoNode?.play()
}
}
override func animateIn(from node: (ASDisplayNode, CGRect, () -> (UIView?, UIView?)), addToTransitionSurface: (UIView) -> Void) {
guard let videoNode = self.videoNode else {
return
}
if let node = node.0 as? OverlayMediaItemNode {
var transformedFrame = node.view.convert(node.view.bounds, to: videoNode.view)
let transformedSuperFrame = node.view.convert(node.view.bounds, to: videoNode.view.superview)
videoNode.layer.animatePosition(from: CGPoint(x: transformedSuperFrame.midX, y: transformedSuperFrame.midY), to: videoNode.layer.position, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
transformedFrame.origin = CGPoint()
let transform = CATransform3DScale(videoNode.layer.transform, transformedFrame.size.width / videoNode.layer.bounds.size.width, transformedFrame.size.height / videoNode.layer.bounds.size.height, 1.0)
videoNode.layer.animate(from: NSValue(caTransform3D: transform), to: NSValue(caTransform3D: videoNode.layer.transform), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25)
self.context.sharedContext.mediaManager.setOverlayVideoNode(nil)
} else {
var transformedFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view)
let transformedSuperFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view.superview)
let transformedSelfFrame = node.0.view.convert(node.0.view.bounds, to: self.view)
let transformedCopyViewFinalFrame = videoNode.view.convert(videoNode.view.bounds, to: self.view)
let surfaceCopyView = node.2().0!
let copyView = node.2().0!
addToTransitionSurface(surfaceCopyView)
var transformedSurfaceFrame: CGRect?
var transformedSurfaceFinalFrame: CGRect?
if let contentSurface = surfaceCopyView.superview {
transformedSurfaceFrame = node.0.view.convert(node.0.view.bounds, to: contentSurface)
transformedSurfaceFinalFrame = videoNode.view.convert(videoNode.view.bounds, to: contentSurface)
}
if let transformedSurfaceFrame = transformedSurfaceFrame {
surfaceCopyView.frame = transformedSurfaceFrame
}
self.view.insertSubview(copyView, belowSubview: self.scrollNode.view)
copyView.frame = transformedSelfFrame
copyView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, removeOnCompletion: false)
surfaceCopyView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
copyView.layer.animatePosition(from: CGPoint(x: transformedSelfFrame.midX, y: transformedSelfFrame.midY), to: CGPoint(x: transformedCopyViewFinalFrame.midX, y: transformedCopyViewFinalFrame.midY), duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { [weak copyView] _ in
copyView?.removeFromSuperview()
})
let scale = CGSize(width: transformedCopyViewFinalFrame.size.width / transformedSelfFrame.size.width, height: transformedCopyViewFinalFrame.size.height / transformedSelfFrame.size.height)
copyView.layer.animate(from: NSValue(caTransform3D: CATransform3DIdentity), to: NSValue(caTransform3D: CATransform3DMakeScale(scale.width, scale.height, 1.0)), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25, removeOnCompletion: false)
if let transformedSurfaceFrame = transformedSurfaceFrame, let transformedSurfaceFinalFrame = transformedSurfaceFinalFrame {
surfaceCopyView.layer.animatePosition(from: CGPoint(x: transformedSurfaceFrame.midX, y: transformedSurfaceFrame.midY), to: CGPoint(x: transformedCopyViewFinalFrame.midX, y: transformedCopyViewFinalFrame.midY), duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { [weak surfaceCopyView] _ in
surfaceCopyView?.removeFromSuperview()
})
let scale = CGSize(width: transformedSurfaceFinalFrame.size.width / transformedSurfaceFrame.size.width, height: transformedSurfaceFinalFrame.size.height / transformedSurfaceFrame.size.height)
surfaceCopyView.layer.animate(from: NSValue(caTransform3D: CATransform3DIdentity), to: NSValue(caTransform3D: CATransform3DMakeScale(scale.width, scale.height, 1.0)), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25, removeOnCompletion: false)
}
videoNode.allowsGroupOpacity = true
videoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, completion: { [weak videoNode] _ in
videoNode?.allowsGroupOpacity = false
})
videoNode.layer.animatePosition(from: CGPoint(x: transformedSuperFrame.midX, y: transformedSuperFrame.midY), to: videoNode.layer.position, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
transformedFrame.origin = CGPoint()
let transform = CATransform3DScale(videoNode.layer.transform, transformedFrame.size.width / videoNode.layer.bounds.size.width, transformedFrame.size.height / videoNode.layer.bounds.size.height, 1.0)
videoNode.layer.animate(from: NSValue(caTransform3D: transform), to: NSValue(caTransform3D: videoNode.layer.transform), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25)
self.statusButtonNode.layer.animatePosition(from: CGPoint(x: transformedSuperFrame.midX, y: transformedSuperFrame.midY), to: self.statusButtonNode.position, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
self.statusButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
self.statusButtonNode.layer.animateScale(from: 0.5, to: 1.0, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
}
}
override func animateOut(to node: (ASDisplayNode, CGRect, () -> (UIView?, UIView?)), addToTransitionSurface: (UIView) -> Void, completion: @escaping () -> Void) {
guard let videoNode = self.videoNode else {
completion()
return
}
var transformedFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view)
let transformedSuperFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view.superview)
let transformedSelfFrame = node.0.view.convert(node.0.view.bounds, to: self.view)
let transformedCopyViewInitialFrame = videoNode.view.convert(videoNode.view.bounds, to: self.view)
var positionCompleted = false
var boundsCompleted = false
var copyCompleted = false
let copyView = node.2().0!
let surfaceCopyView = node.2().0!
addToTransitionSurface(surfaceCopyView)
var transformedSurfaceFrame: CGRect?
var transformedSurfaceCopyViewInitialFrame: CGRect?
if let contentSurface = surfaceCopyView.superview {
transformedSurfaceFrame = node.0.view.convert(node.0.view.bounds, to: contentSurface)
transformedSurfaceCopyViewInitialFrame = videoNode.view.convert(videoNode.view.bounds, to: contentSurface)
}
self.view.insertSubview(copyView, belowSubview: self.scrollNode.view)
copyView.frame = transformedSelfFrame
let intermediateCompletion = { [weak copyView, weak surfaceCopyView] in
if positionCompleted && boundsCompleted && copyCompleted {
copyView?.removeFromSuperview()
surfaceCopyView?.removeFromSuperview()
completion()
}
}
copyView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false)
surfaceCopyView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, removeOnCompletion: false)
copyView.layer.animatePosition(from: CGPoint(x: transformedCopyViewInitialFrame.midX, y: transformedCopyViewInitialFrame.midY), to: CGPoint(x: transformedSelfFrame.midX, y: transformedSelfFrame.midY), duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
let scale = CGSize(width: transformedCopyViewInitialFrame.size.width / transformedSelfFrame.size.width, height: transformedCopyViewInitialFrame.size.height / transformedSelfFrame.size.height)
copyView.layer.animate(from: NSValue(caTransform3D: CATransform3DMakeScale(scale.width, scale.height, 1.0)), to: NSValue(caTransform3D: CATransform3DIdentity), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25, removeOnCompletion: false, completion: { _ in
copyCompleted = true
intermediateCompletion()
})
if let transformedSurfaceFrame = transformedSurfaceFrame, let transformedCopyViewInitialFrame = transformedSurfaceCopyViewInitialFrame {
surfaceCopyView.layer.animatePosition(from: CGPoint(x: transformedCopyViewInitialFrame.midX, y: transformedCopyViewInitialFrame.midY), to: CGPoint(x: transformedSurfaceFrame.midX, y: transformedSurfaceFrame.midY), duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
let scale = CGSize(width: transformedCopyViewInitialFrame.size.width / transformedSurfaceFrame.size.width, height: transformedCopyViewInitialFrame.size.height / transformedSurfaceFrame.size.height)
surfaceCopyView.layer.animate(from: NSValue(caTransform3D: CATransform3DMakeScale(scale.width, scale.height, 1.0)), to: NSValue(caTransform3D: CATransform3DIdentity), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25, removeOnCompletion: false)
}
videoNode.layer.animatePosition(from: videoNode.layer.position, to: CGPoint(x: transformedSuperFrame.midX, y: transformedSuperFrame.midY), duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
positionCompleted = true
intermediateCompletion()
})
videoNode.allowsGroupOpacity = true
videoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak videoNode] _ in
videoNode?.allowsGroupOpacity = false
})
self.statusButtonNode.layer.animatePosition(from: self.statusButtonNode.layer.position, to: CGPoint(x: transformedSelfFrame.midX, y: transformedSelfFrame.midY), duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
//positionCompleted = true
//intermediateCompletion()
})
self.statusButtonNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
self.statusButtonNode.layer.animateScale(from: 1.0, to: 0.2, duration: 0.25, removeOnCompletion: false)
transformedFrame.origin = CGPoint()
let transform = CATransform3DScale(videoNode.layer.transform, transformedFrame.size.width / videoNode.layer.bounds.size.width, transformedFrame.size.height / videoNode.layer.bounds.size.height, 1.0)
videoNode.layer.animate(from: NSValue(caTransform3D: videoNode.layer.transform), to: NSValue(caTransform3D: transform), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25, removeOnCompletion: false, completion: { _ in
boundsCompleted = true
intermediateCompletion()
})
}
func animateOut(toOverlay node: ASDisplayNode, completion: @escaping () -> Void) {
guard let videoNode = self.videoNode else {
completion()
return
}
var transformedFrame = node.view.convert(node.view.bounds, to: videoNode.view)
let transformedSuperFrame = node.view.convert(node.view.bounds, to: videoNode.view.superview)
let transformedSelfFrame = node.view.convert(node.view.bounds, to: self.view)
let transformedCopyViewInitialFrame = videoNode.view.convert(videoNode.view.bounds, to: self.view)
let transformedSelfTargetSuperFrame = videoNode.view.convert(videoNode.view.bounds, to: node.view.superview)
var positionCompleted = false
var boundsCompleted = false
var copyCompleted = false
var nodeCompleted = false
let copyView = node.view.snapshotContentTree()!
videoNode.isHidden = true
copyView.frame = transformedSelfFrame
let intermediateCompletion = { [weak copyView] in
if positionCompleted && boundsCompleted && copyCompleted && nodeCompleted {
copyView?.removeFromSuperview()
completion()
}
}
copyView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1, removeOnCompletion: false)
copyView.layer.animatePosition(from: CGPoint(x: transformedCopyViewInitialFrame.midX, y: transformedCopyViewInitialFrame.midY), to: CGPoint(x: transformedSelfFrame.midX, y: transformedSelfFrame.midY), duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
let scale = CGSize(width: transformedCopyViewInitialFrame.size.width / transformedSelfFrame.size.width, height: transformedCopyViewInitialFrame.size.height / transformedSelfFrame.size.height)
copyView.layer.animate(from: NSValue(caTransform3D: CATransform3DMakeScale(scale.width, scale.height, 1.0)), to: NSValue(caTransform3D: CATransform3DIdentity), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25, removeOnCompletion: false, completion: { _ in
copyCompleted = true
intermediateCompletion()
})
videoNode.layer.animatePosition(from: videoNode.layer.position, to: CGPoint(x: transformedSuperFrame.midX, y: transformedSuperFrame.midY), duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
positionCompleted = true
intermediateCompletion()
})
videoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
self.statusButtonNode.layer.animatePosition(from: self.statusButtonNode.layer.position, to: CGPoint(x: transformedSelfFrame.midX, y: transformedSelfFrame.midY), duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
//positionCompleted = true
//intermediateCompletion()
})
self.statusButtonNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
self.statusButtonNode.layer.animateScale(from: 1.0, to: 0.2, duration: 0.25, removeOnCompletion: false)
transformedFrame.origin = CGPoint()
let videoTransform = CATransform3DScale(videoNode.layer.transform, transformedFrame.size.width / videoNode.layer.bounds.size.width, transformedFrame.size.height / videoNode.layer.bounds.size.height, 1.0)
videoNode.layer.animate(from: NSValue(caTransform3D: videoNode.layer.transform), to: NSValue(caTransform3D: videoTransform), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25, removeOnCompletion: false, completion: { _ in
boundsCompleted = true
intermediateCompletion()
})
let nodeTransform = CATransform3DScale(node.layer.transform, videoNode.layer.bounds.size.width / transformedFrame.size.width, videoNode.layer.bounds.size.height / transformedFrame.size.height, 1.0)
node.layer.animatePosition(from: CGPoint(x: transformedSelfTargetSuperFrame.midX, y: transformedSelfTargetSuperFrame.midY), to: node.layer.position, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
node.layer.animate(from: NSValue(caTransform3D: nodeTransform), to: NSValue(caTransform3D: node.layer.transform), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25, removeOnCompletion: false, completion: { _ in
nodeCompleted = true
intermediateCompletion()
})
}
@objc func statusButtonPressed() {
if let videoNode = self.videoNode {
if let fetchStatus = self.fetchStatus, case .Local = fetchStatus {
self.toggleControlsVisibility()
}
if let fetchStatus = self.fetchStatus {
switch fetchStatus {
case .Local:
videoNode.togglePlayPause()
case .Remote:
if self.requiresDownload {
self.fetchControls?.fetch()
} else {
videoNode.togglePlayPause()
}
case .Fetching:
self.fetchControls?.cancel()
}
} else {
videoNode.togglePlayPause()
}
}
}
override func footerContent() -> Signal<(GalleryFooterContentNode?, GalleryOverlayContentNode?), NoError> {
return .single((self.footerContentNode, nil))
}
}