Swiftgram/TelegramUI/InstantPagePlayableVideoNode.swift
Ilya Laktyushin 47d146b229 Added online statuses in chat list and share menu
Added recent stickers clearing
Added sending logs via email
Added forward recipient change on forward acccessory panel tap
Tweaked undo panel design
Various UI fixes
2019-04-09 23:49:26 +04:00

161 lines
6.4 KiB
Swift

import Foundation
import AsyncDisplayKit
import Display
import Postbox
import TelegramCore
import SwiftSignalKit
private struct FetchControls {
let fetch: (Bool) -> Void
let cancel: () -> Void
}
final class InstantPagePlayableVideoNode: ASDisplayNode, InstantPageNode {
private let context: AccountContext
let media: InstantPageMedia
private let interactive: Bool
private let openMedia: (InstantPageMedia) -> Void
private var fetchControls: FetchControls?
private let videoNode: UniversalVideoNode
private let statusNode: RadialStatusNode
private var currentSize: CGSize?
private var fetchStatus: MediaResourceStatus?
private var fetchedDisposable = MetaDisposable()
private var statusDisposable = MetaDisposable()
private var localIsVisible = false
init(context: AccountContext, webPage: TelegramMediaWebpage, theme: InstantPageTheme, media: InstantPageMedia, interactive: Bool, openMedia: @escaping (InstantPageMedia) -> Void) {
self.context = context
self.media = media
self.interactive = interactive
self.openMedia = openMedia
var imageReference: ImageMediaReference?
if let file = media.media as? TelegramMediaFile, let presentation = smallestImageRepresentation(file.previewRepresentations) {
let image = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [presentation], immediateThumbnailData: nil, reference: nil, partialReference: nil)
imageReference = ImageMediaReference.webPage(webPage: WebpageReference(webPage), media: image)
}
var streamVideo = false
if let file = media.media as? TelegramMediaFile {
streamVideo = isMediaStreamable(media: file)
}
self.videoNode = UniversalVideoNode(postbox: context.account.postbox, audioSession: context.sharedContext.mediaManager.audioSession, manager: context.sharedContext.mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: NativeVideoContent(id: .instantPage(webPage.webpageId, media.media.id!), fileReference: .webPage(webPage: WebpageReference(webPage), media: media.media as! TelegramMediaFile), imageReference: imageReference, streamVideo: streamVideo ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, placeholderColor: theme.pageBackgroundColor), priority: .embedded, autoplay: true)
self.videoNode.isUserInteractionEnabled = false
self.statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.6))
super.init()
self.addSubnode(self.videoNode)
if let file = media.media as? TelegramMediaFile {
self.fetchedDisposable.set(fetchedMediaResource(postbox: context.account.postbox, reference: AnyMediaReference.webPage(webPage: WebpageReference(webPage), media: file).resourceReference(file.resource)).start())
self.statusDisposable.set((context.account.postbox.mediaBox.resourceStatus(file.resource) |> deliverOnMainQueue).start(next: { [weak self] status in
displayLinkDispatcher.dispatch {
if let strongSelf = self {
strongSelf.fetchStatus = status
strongSelf.updateFetchStatus()
}
}
}))
}
}
deinit {
self.fetchedDisposable.dispose()
}
override func didLoad() {
super.didLoad()
if self.interactive {
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
}
}
func updateIsVisible(_ isVisible: Bool) {
if self.localIsVisible != isVisible {
self.localIsVisible = isVisible
self.videoNode.canAttachContent = isVisible
}
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
}
func update(strings: PresentationStrings, theme: InstantPageTheme) {
}
private func updateFetchStatus() {
var state: RadialStatusNodeState = .none
if let fetchStatus = self.fetchStatus {
switch fetchStatus {
case let .Fetching(_, progress):
let adjustedProgress = max(progress, 0.027)
state = .progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true)
case .Remote:
state = .download(.white)
default:
break
}
}
self.statusNode.transitionToState(state, completion: { [weak statusNode] in
if state == .none {
statusNode?.removeFromSupernode()
}
})
}
override func layout() {
super.layout()
let size = self.bounds.size
if self.currentSize != size {
self.currentSize = size
self.videoNode.frame = CGRect(origin: CGPoint(), size: size)
self.videoNode.updateLayout(size: size, transition: .immediate)
let radialStatusSize: CGFloat = 50.0
self.statusNode.frame = CGRect(x: floorToScreenPixels((size.width - radialStatusSize) / 2.0), y: floorToScreenPixels((size.height - radialStatusSize) / 2.0), width: radialStatusSize, height: radialStatusSize)
}
}
func transitionNode(media: InstantPageMedia) -> (ASDisplayNode, () -> (UIView?, UIView?))? {
if media == self.media {
let videoNode = self.videoNode
return (self.videoNode, { [weak videoNode] in
return (videoNode?.view.snapshotContentTree(unhide: true), nil)
})
} else {
return nil
}
}
func updateHiddenMedia(media: InstantPageMedia?) {
self.videoNode.isHidden = self.media == media
}
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state, let fetchStatus = self.fetchStatus {
switch fetchStatus {
case .Local:
self.openMedia(self.media)
case .Remote:
self.fetchControls?.fetch(true)
case .Fetching:
self.fetchControls?.cancel()
}
}
}
}