mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 14:45:21 +00:00
Various improvements
This commit is contained in:
@@ -2,11 +2,16 @@ import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import TelegramPresentationData
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import AvatarNode
|
||||
import TelegramCore
|
||||
import TelegramUniversalVideoContent
|
||||
import UniversalMediaPlayer
|
||||
import GalleryUI
|
||||
import HierarchyTrackingLayer
|
||||
|
||||
private let timezoneOffset: Int32 = {
|
||||
let nowTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||
@@ -345,7 +350,7 @@ final class ChatMessageAvatarHeader: ListViewItemHeader {
|
||||
self.controllerInteraction = controllerInteraction
|
||||
self.id = ListViewItemNode.HeaderId(space: 1, id: Id(peerId: peerId, timestampId: dateHeaderTimestampId(timestamp: timestamp)))
|
||||
}
|
||||
|
||||
|
||||
let stickDirection: ListViewItemHeaderStickDirection = .top
|
||||
let stickOverInsets: Bool = false
|
||||
|
||||
@@ -376,17 +381,40 @@ final class ChatMessageAvatarHeader: ListViewItemHeader {
|
||||
|
||||
private let avatarFont = avatarPlaceholderFont(size: 16.0)
|
||||
|
||||
private let maxVideoLoopCount = 3
|
||||
|
||||
final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
|
||||
private let context: AccountContext
|
||||
private var presentationData: ChatPresentationData
|
||||
private let controllerInteraction: ChatControllerInteraction
|
||||
|
||||
private let peerId: PeerId
|
||||
private let messageReference: MessageReference?
|
||||
private let peer: Peer?
|
||||
|
||||
private let containerNode: ContextControllerSourceNode
|
||||
private let avatarNode: AvatarNode
|
||||
private var presentationData: ChatPresentationData
|
||||
private let context: AccountContext
|
||||
private let controllerInteraction: ChatControllerInteraction
|
||||
|
||||
private var videoNode: UniversalVideoNode?
|
||||
|
||||
private var videoContent: NativeVideoContent?
|
||||
private let playbackStartDisposable = MetaDisposable()
|
||||
private var cachedDataDisposable = MetaDisposable()
|
||||
private var hierarchyTrackingLayer: HierarchyTrackingLayer?
|
||||
private var videoLoopCount = 0
|
||||
|
||||
private var trackingIsInHierarchy: Bool = false {
|
||||
didSet {
|
||||
if self.trackingIsInHierarchy != oldValue {
|
||||
Queue.mainQueue().justDispatch {
|
||||
if self.trackingIsInHierarchy {
|
||||
self.videoLoopCount = 0
|
||||
}
|
||||
self.updateVideoVisibility()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(peerId: PeerId, peer: Peer?, messageReference: MessageReference?, presentationData: ChatPresentationData, context: AccountContext, controllerInteraction: ChatControllerInteraction, synchronousLoad: Bool) {
|
||||
self.peerId = peerId
|
||||
self.peer = peer
|
||||
@@ -423,6 +451,11 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
|
||||
|
||||
self.updateSelectionState(animated: false)
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.cachedDataDisposable.dispose()
|
||||
self.playbackStartDisposable.dispose()
|
||||
}
|
||||
|
||||
func setCustomLetters(context: AccountContext, theme: PresentationTheme, synchronousLoad: Bool, letters: [String], emptyColor: UIColor) {
|
||||
self.containerNode.isGestureEnabled = false
|
||||
@@ -438,6 +471,64 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
|
||||
overrideImage = .deletedIcon
|
||||
}
|
||||
self.avatarNode.setPeer(context: context, theme: theme, peer: EnginePeer(peer), authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, synchronousLoad: synchronousLoad, displayDimensions: CGSize(width: 38.0, height: 38.0))
|
||||
|
||||
if peer.isPremium {
|
||||
self.cachedDataDisposable.set((context.account.postbox.peerView(id: peer.id)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peerView in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let cachedPeerData = peerView.cachedData
|
||||
if let cachedPeerData = cachedPeerData as? CachedUserData {
|
||||
if let photo = cachedPeerData.photo, let video = photo.videoRepresentations.last, let peerReference = PeerReference(peer) {
|
||||
let videoId = photo.id?.id ?? peer.id.id._internalGetInt64Value()
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: photo.representations, videoThumbnails: [], immediateThumbnailData: photo.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
|
||||
let videoContent = NativeVideoContent(id: .profileVideo(videoId, nil), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: false)
|
||||
if videoContent.id != strongSelf.videoContent?.id {
|
||||
strongSelf.videoNode?.removeFromSupernode()
|
||||
strongSelf.videoContent = videoContent
|
||||
}
|
||||
|
||||
if strongSelf.hierarchyTrackingLayer == nil {
|
||||
let hierarchyTrackingLayer = HierarchyTrackingLayer()
|
||||
hierarchyTrackingLayer.didEnterHierarchy = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.trackingIsInHierarchy = true
|
||||
}
|
||||
|
||||
hierarchyTrackingLayer.didExitHierarchy = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.trackingIsInHierarchy = false
|
||||
}
|
||||
strongSelf.hierarchyTrackingLayer = hierarchyTrackingLayer
|
||||
strongSelf.layer.addSublayer(hierarchyTrackingLayer)
|
||||
}
|
||||
} else {
|
||||
strongSelf.videoContent = nil
|
||||
|
||||
strongSelf.hierarchyTrackingLayer?.removeFromSuperlayer()
|
||||
strongSelf.hierarchyTrackingLayer = nil
|
||||
}
|
||||
|
||||
strongSelf.updateVideoVisibility()
|
||||
} else {
|
||||
let _ = context.engine.peers.fetchAndUpdateCachedPeerData(peerId: peer.id).start()
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
self.cachedDataDisposable.set(nil)
|
||||
self.videoContent = nil
|
||||
|
||||
self.videoNode?.removeFromSupernode()
|
||||
self.videoNode = nil
|
||||
|
||||
self.hierarchyTrackingLayer?.removeFromSuperlayer()
|
||||
self.hierarchyTrackingLayer = nil
|
||||
}
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
@@ -509,4 +600,74 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateVideoVisibility() {
|
||||
let context = self.context
|
||||
let isVisible = self.trackingIsInHierarchy
|
||||
if isVisible, let videoContent = self.videoContent, self.videoLoopCount != maxVideoLoopCount {
|
||||
if self.videoNode == nil {
|
||||
let mediaManager = context.sharedContext.mediaManager
|
||||
let videoNode = UniversalVideoNode(postbox: context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
|
||||
videoNode.clipsToBounds = true
|
||||
videoNode.isUserInteractionEnabled = false
|
||||
videoNode.isHidden = true
|
||||
videoNode.playbackCompleted = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.videoLoopCount += 1
|
||||
if strongSelf.videoLoopCount == maxVideoLoopCount {
|
||||
if let videoNode = strongSelf.videoNode {
|
||||
strongSelf.videoNode = nil
|
||||
videoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak videoNode] _ in
|
||||
videoNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let _ = videoContent.startTimestamp {
|
||||
self.playbackStartDisposable.set((videoNode.status
|
||||
|> map { status -> Bool in
|
||||
if let status = status, case .playing = status.status {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|> filter { playing in
|
||||
return playing
|
||||
}
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
Queue.mainQueue().after(0.15) {
|
||||
strongSelf.videoNode?.isHidden = false
|
||||
}
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
self.playbackStartDisposable.set(nil)
|
||||
videoNode.isHidden = false
|
||||
}
|
||||
videoNode.layer.cornerRadius = self.avatarNode.frame.size.width / 2.0
|
||||
if #available(iOS 13.0, *) {
|
||||
videoNode.layer.cornerCurve = .circular
|
||||
}
|
||||
|
||||
videoNode.canAttachContent = true
|
||||
videoNode.play()
|
||||
|
||||
self.containerNode.insertSubnode(videoNode, aboveSubnode: self.avatarNode)
|
||||
self.videoNode = videoNode
|
||||
}
|
||||
} else if let videoNode = self.videoNode {
|
||||
self.videoNode = nil
|
||||
videoNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if let videoNode = self.videoNode {
|
||||
videoNode.updateLayout(size: self.avatarNode.frame.size, transition: .immediate)
|
||||
videoNode.frame = self.avatarNode.frame
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user