Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2025-02-27 19:01:55 +04:00
commit e12b20be74
18 changed files with 310 additions and 245 deletions

View File

@ -298,7 +298,6 @@ private final class ItemNode: ASDisplayNode {
self.shortTitleActiveNode.visibility = title.enableAnimations
if themeUpdated || titleUpdated {
//TODO:release
self.titleNode.attributedText = title.attributedString(font: Font.medium(14.0), textColor: presentationData.theme.list.itemSecondaryTextColor)
self.titleActiveNode.attributedText = title.attributedString(font: Font.medium(14.0), textColor: presentationData.theme.list.itemAccentColor)

View File

@ -637,7 +637,6 @@ final class VideoChatParticipantsComponent: Component {
private var ignoreScrolling: Bool = false
//TODO:release
private var gridParticipants: [VideoParticipant] = []
private var listParticipants: [GroupCallParticipantsContext.Participant] = []

View File

@ -2420,7 +2420,6 @@ final class VideoChatScreenComponent: Component {
}
videoButtonContent = .audio(audio: buttonAudio, isEnabled: buttonIsEnabled)
} else {
//TODO:release
videoButtonContent = .video(isActive: false)
}
let _ = self.videoButton.update(

View File

@ -285,7 +285,6 @@ extension VideoChatScreenComponent.View {
}
//let isScheduled = strongSelf.isScheduled
//TODO:release
let isScheduled: Bool = !"".isEmpty
let canSpeak: Bool

View File

@ -90,7 +90,7 @@ public final class BatchVideoRenderingContext {
private final class TargetContext {
weak var target: Target?
let file: TelegramMediaFile
let file: FileMediaReference
let userLocation: MediaResourceUserLocation
var readingContext: QueueLocalObject<ReadingContext>?
@ -100,7 +100,7 @@ public final class BatchVideoRenderingContext {
init(
target: Target,
file: TelegramMediaFile,
file: FileMediaReference,
userLocation: MediaResourceUserLocation
) {
self.target = target
@ -128,7 +128,7 @@ public final class BatchVideoRenderingContext {
self.context = context
}
public func add(target: Target, file: TelegramMediaFile, userLocation: MediaResourceUserLocation) -> TargetHandle {
public func add(target: Target, file: FileMediaReference, userLocation: MediaResourceUserLocation) -> TargetHandle {
let id = self.nextId
self.nextId += 1
@ -158,11 +158,11 @@ public final class BatchVideoRenderingContext {
mediaBox: self.context.account.postbox.mediaBox,
userLocation: targetContext.userLocation,
userContentType: .sticker,
reference: .media(media: .standalone(media: targetContext.file), resource: targetContext.file.resource)
reference: targetContext.file.resourceReference(targetContext.file.media.resource)
).startStrict()
}
if targetContext.dataDisposable == nil {
targetContext.dataDisposable = (self.context.account.postbox.mediaBox.resourceData(targetContext.file.resource)
targetContext.dataDisposable = (self.context.account.postbox.mediaBox.resourceData(targetContext.file.media.resource)
|> deliverOnMainQueue).startStrict(next: { [weak self, weak targetContext] data in
guard let self, let targetContext else {
return

View File

@ -40,6 +40,7 @@ swift_library(
"//submodules/TelegramUI/Components/EntityKeyboardGifContent:EntityKeyboardGifContent",
"//submodules/TelegramUI/Components/LegacyMessageInputPanelInputView:LegacyMessageInputPanelInputView",
"//submodules/AttachmentTextInputPanelNode",
"//submodules/TelegramUI/Components/BatchVideoRendering",
],
visibility = [
"//visibility:public",

View File

@ -1912,6 +1912,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
interaction: interaction,
inputNodeInteraction: inputNodeInteraction,
mode: mappedMode,
batchVideoRenderingContext: nil,
trendingGifsPromise: trendingGifsPromise,
cancel: {
},

View File

@ -12,12 +12,15 @@ import ChatControllerInteraction
import MultiplexedVideoNode
import ChatPresentationInterfaceState
import EntityKeyboardGifContent
import BatchVideoRendering
final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode {
private let context: AccountContext
private let interaction: ChatEntityKeyboardInputNode.Interaction
private let inputNodeInteraction: ChatMediaInputNodeInteraction
private let batchVideoRenderingContext: BatchVideoRenderingContext
private var theme: PresentationTheme
private var strings: PresentationStrings
@ -45,10 +48,11 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode {
private var hasInitialText = false
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, interaction: ChatEntityKeyboardInputNode.Interaction, inputNodeInteraction: ChatMediaInputNodeInteraction, trendingPromise: Promise<ChatMediaInputGifPaneTrendingState?>) {
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, interaction: ChatEntityKeyboardInputNode.Interaction, inputNodeInteraction: ChatMediaInputNodeInteraction, batchVideoRenderingContext: BatchVideoRenderingContext, trendingPromise: Promise<ChatMediaInputGifPaneTrendingState?>) {
self.context = context
self.interaction = interaction
self.inputNodeInteraction = inputNodeInteraction
self.batchVideoRenderingContext = batchVideoRenderingContext
self.trendingPromise = trendingPromise
self.theme = theme
@ -217,7 +221,7 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode {
super.willEnterHierarchy()
if self.multiplexedNode == nil {
let multiplexedNode = MultiplexedVideoNode(account: self.context.account, theme: self.theme, strings: self.strings)
let multiplexedNode = MultiplexedVideoNode(context: self.context, theme: self.theme, strings: self.strings, batchVideoRenderingContext: self.batchVideoRenderingContext)
self.multiplexedNode = multiplexedNode
if let layout = self.validLayout {
multiplexedNode.frame = CGRect(origin: CGPoint(), size: layout)

View File

@ -14,6 +14,7 @@ import MultiplexedVideoNode
import FeaturedStickersScreen
import StickerPeekUI
import EntityKeyboardGifContent
import BatchVideoRendering
private let searchBarHeight: CGFloat = 52.0
@ -54,7 +55,7 @@ public final class PaneSearchContainerNode: ASDisplayNode, EntitySearchContainer
return self.contentNode.ready
}
public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, interaction: ChatEntityKeyboardInputNode.Interaction, inputNodeInteraction: ChatMediaInputNodeInteraction, mode: ChatMediaInputSearchMode, stickerActionTitle: String? = nil, trendingGifsPromise: Promise<ChatMediaInputGifPaneTrendingState?>, cancel: @escaping () -> Void, peekBehavior: EmojiContentPeekBehavior?) {
public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, interaction: ChatEntityKeyboardInputNode.Interaction, inputNodeInteraction: ChatMediaInputNodeInteraction, mode: ChatMediaInputSearchMode, batchVideoRenderingContext: BatchVideoRenderingContext?, stickerActionTitle: String? = nil, trendingGifsPromise: Promise<ChatMediaInputGifPaneTrendingState?>, cancel: @escaping () -> Void, peekBehavior: EmojiContentPeekBehavior?) {
self.context = context
self.mode = mode
self.interaction = interaction
@ -62,7 +63,7 @@ public final class PaneSearchContainerNode: ASDisplayNode, EntitySearchContainer
self.peekBehavior = peekBehavior
switch mode {
case .gif:
self.contentNode = GifPaneSearchContentNode(context: context, theme: theme, strings: strings, interaction: interaction, inputNodeInteraction: inputNodeInteraction, trendingPromise: trendingGifsPromise)
self.contentNode = GifPaneSearchContentNode(context: context, theme: theme, strings: strings, interaction: interaction, inputNodeInteraction: inputNodeInteraction, batchVideoRenderingContext: batchVideoRenderingContext ?? BatchVideoRenderingContext(context: context), trendingPromise: trendingGifsPromise)
case .sticker, .trending:
self.contentNode = StickerPaneSearchContentNode(context: context, theme: theme, strings: strings, interaction: interaction, inputNodeInteraction: inputNodeInteraction, stickerActionTitle: stickerActionTitle)
}

View File

@ -51,6 +51,7 @@ swift_library(
"//submodules/TelegramCore/FlatBuffers",
"//submodules/TelegramCore/FlatSerialization",
"//submodules/TelegramUI/Components/BatchVideoRendering",
"//submodules/TelegramUI/Components/GifVideoLayer",
],
visibility = [
"//visibility:public",

View File

@ -20,115 +20,7 @@ import AVFoundation
import PhotoResources
import ShimmerEffect
import BatchVideoRendering
private class GifVideoLayer: AVSampleBufferDisplayLayer, BatchVideoRenderingContext.Target {
private let context: AccountContext
private let batchVideoContext: BatchVideoRenderingContext
private let userLocation: MediaResourceUserLocation
private let file: TelegramMediaFile?
private var batchVideoTargetHandle: BatchVideoRenderingContext.TargetHandle?
var batchVideoRenderingTargetState: BatchVideoRenderingContext.TargetState?
private var thumbnailDisposable: Disposable?
private var isReadyToRender: Bool = false
var started: (() -> Void)?
var shouldBeAnimating: Bool = false {
didSet {
if self.shouldBeAnimating == oldValue {
return
}
self.updateShouldBeRendering()
}
}
init(context: AccountContext, batchVideoContext: BatchVideoRenderingContext, userLocation: MediaResourceUserLocation, file: TelegramMediaFile?, synchronousLoad: Bool) {
self.context = context
self.batchVideoContext = batchVideoContext
self.userLocation = userLocation
self.file = file
super.init()
self.videoGravity = .resizeAspectFill
if let file = self.file {
if let dimensions = file.dimensions {
self.thumbnailDisposable = (mediaGridMessageVideo(postbox: context.account.postbox, userLocation: userLocation, videoReference: .savedGif(media: file), synchronousLoad: synchronousLoad, nilForEmptyResult: true)
|> deliverOnMainQueue).start(next: { [weak self] transform in
guard let strongSelf = self else {
return
}
let boundingSize = CGSize(width: 93.0, height: 93.0)
let imageSize = dimensions.cgSize.aspectFilled(boundingSize)
if let image = transform(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: boundingSize, intrinsicInsets: UIEdgeInsets(), resizeMode: .fill(.clear)))?.generateImage() {
Queue.mainQueue().async {
if let strongSelf = self {
strongSelf.contents = image.cgImage
strongSelf.setupVideo()
strongSelf.started?()
}
}
} else {
strongSelf.setupVideo()
}
})
} else {
self.setupVideo()
}
}
}
override init(layer: Any) {
guard let layer = layer as? GifVideoLayer else {
preconditionFailure()
}
self.context = layer.context
self.batchVideoContext = layer.batchVideoContext
self.userLocation = layer.userLocation
self.file = layer.file
super.init(layer: layer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.thumbnailDisposable?.dispose()
}
private func setupVideo() {
self.isReadyToRender = true
self.updateShouldBeRendering()
}
private func updateShouldBeRendering() {
let shouldBeRendering = self.shouldBeAnimating && self.isReadyToRender
if shouldBeRendering, let file = self.file {
if self.batchVideoTargetHandle == nil {
self.batchVideoTargetHandle = self.batchVideoContext.add(target: self, file: file, userLocation: self.userLocation)
}
} else {
self.batchVideoTargetHandle = nil
}
}
func setSampleBuffer(sampleBuffer: CMSampleBuffer) {
if #available(iOS 17.0, *) {
self.sampleBufferRenderer.enqueue(sampleBuffer)
} else {
self.enqueue(sampleBuffer)
}
}
}
import GifVideoLayer
public final class GifPagerContentComponent: Component {
public typealias EnvironmentType = (EntityKeyboardChildEnvironment, PagerComponentChildEnvironment)
@ -388,7 +280,7 @@ public final class GifPagerContentComponent: Component {
self.item = item
self.onUpdateDisplayPlaceholder = onUpdateDisplayPlaceholder
super.init(context: context, batchVideoContext: batchVideoContext, userLocation: .other, file: item?.file.media, synchronousLoad: attemptSynchronousLoad)
super.init(context: context, batchVideoContext: batchVideoContext, userLocation: .other, file: item?.file, synchronousLoad: attemptSynchronousLoad)
if item == nil {
self.updateDisplayPlaceholder(displayPlaceholder: true, duration: 0.0)

View File

@ -0,0 +1,23 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "GifVideoLayer",
module_name = "GifVideoLayer",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/Display",
"//submodules/TelegramUI/Components/BatchVideoRendering",
"//submodules/AccountContext",
"//submodules/TelegramCore",
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/PhotoResources",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,118 @@
import Foundation
import UIKit
import Display
import BatchVideoRendering
import AccountContext
import TelegramCore
import AVFoundation
import SwiftSignalKit
import PhotoResources
open class GifVideoLayer: AVSampleBufferDisplayLayer, BatchVideoRenderingContext.Target {
private let context: AccountContext
private let batchVideoContext: BatchVideoRenderingContext
private let userLocation: MediaResourceUserLocation
private let file: FileMediaReference?
private var batchVideoTargetHandle: BatchVideoRenderingContext.TargetHandle?
public var batchVideoRenderingTargetState: BatchVideoRenderingContext.TargetState?
private var thumbnailDisposable: Disposable?
private var isReadyToRender: Bool = false
public var started: (() -> Void)?
public var shouldBeAnimating: Bool = false {
didSet {
if self.shouldBeAnimating == oldValue {
return
}
self.updateShouldBeRendering()
}
}
public init(context: AccountContext, batchVideoContext: BatchVideoRenderingContext, userLocation: MediaResourceUserLocation, file: FileMediaReference?, synchronousLoad: Bool) {
self.context = context
self.batchVideoContext = batchVideoContext
self.userLocation = userLocation
self.file = file
super.init()
self.videoGravity = .resizeAspectFill
if let file = self.file {
if let dimensions = file.media.dimensions {
self.thumbnailDisposable = (mediaGridMessageVideo(postbox: context.account.postbox, userLocation: userLocation, videoReference: file, synchronousLoad: synchronousLoad, nilForEmptyResult: true)
|> deliverOnMainQueue).start(next: { [weak self] transform in
guard let strongSelf = self else {
return
}
let boundingSize = CGSize(width: 93.0, height: 93.0)
let imageSize = dimensions.cgSize.aspectFilled(boundingSize)
if let image = transform(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: boundingSize, intrinsicInsets: UIEdgeInsets(), resizeMode: .fill(.clear)))?.generateImage() {
Queue.mainQueue().async {
if let strongSelf = self {
strongSelf.contents = image.cgImage
strongSelf.setupVideo()
strongSelf.started?()
}
}
} else {
strongSelf.setupVideo()
}
})
} else {
self.setupVideo()
}
}
}
override public init(layer: Any) {
guard let layer = layer as? GifVideoLayer else {
preconditionFailure()
}
self.context = layer.context
self.batchVideoContext = layer.batchVideoContext
self.userLocation = layer.userLocation
self.file = layer.file
super.init(layer: layer)
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.thumbnailDisposable?.dispose()
}
private func setupVideo() {
self.isReadyToRender = true
self.updateShouldBeRendering()
}
private func updateShouldBeRendering() {
let shouldBeRendering = self.shouldBeAnimating && self.isReadyToRender
if shouldBeRendering, let file = self.file {
if self.batchVideoTargetHandle == nil {
self.batchVideoTargetHandle = self.batchVideoContext.add(target: self, file: file, userLocation: self.userLocation)
}
} else {
self.batchVideoTargetHandle = nil
}
}
public func setSampleBuffer(sampleBuffer: CMSampleBuffer) {
if #available(iOS 17.0, *) {
self.sampleBufferRenderer.enqueue(sampleBuffer)
} else {
self.enqueue(sampleBuffer)
}
}
}

View File

@ -19,6 +19,8 @@ swift_library(
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/ShimmerEffect:ShimmerEffect",
"//submodules/SoftwareVideo:SoftwareVideo",
"//submodules/TelegramUI/Components/BatchVideoRendering",
"//submodules/TelegramUI/Components/GifVideoLayer",
],
visibility = [
"//visibility:public",

View File

@ -10,6 +10,9 @@ import ContextUI
import TelegramPresentationData
import ShimmerEffect
import SoftwareVideo
import BatchVideoRendering
import GifVideoLayer
import AccountContext
final class MultiplexedVideoPlaceholderNode: ASDisplayNode {
private let effectNode: ShimmerEffectNode
@ -101,9 +104,115 @@ public final class MultiplexedVideoNodeFiles {
}
public final class MultiplexedVideoNode: ASDisplayNode, ASScrollViewDelegate {
private let account: Account
public final class Item: Equatable {
public let file: FileMediaReference
public let contextResult: (ChatContextResultCollection, ChatContextResult)?
public init(file: FileMediaReference, contextResult: (ChatContextResultCollection, ChatContextResult)?) {
self.file = file
self.contextResult = contextResult
}
public static func ==(lhs: Item, rhs: Item) -> Bool {
if lhs === rhs {
return true
}
if lhs.file.media.fileId != rhs.file.media.fileId {
return false
}
if (lhs.contextResult == nil) != (rhs.contextResult != nil) {
return false
}
return true
}
}
fileprivate final class ItemLayer: GifVideoLayer {
let item: Item?
private var disposable: Disposable?
private var fetchDisposable: Disposable?
private var isInHierarchyValue: Bool = false
public var isVisibleForAnimations: Bool = false {
didSet {
if self.isVisibleForAnimations != oldValue {
self.updatePlayback()
}
}
}
private(set) var displayPlaceholder: Bool = false
let onUpdateDisplayPlaceholder: (Bool, Double) -> Void
init(
item: Item?,
context: AccountContext,
batchVideoContext: BatchVideoRenderingContext,
groupId: String,
attemptSynchronousLoad: Bool,
onUpdateDisplayPlaceholder: @escaping (Bool, Double) -> Void
) {
self.item = item
self.onUpdateDisplayPlaceholder = onUpdateDisplayPlaceholder
super.init(context: context, batchVideoContext: batchVideoContext, userLocation: .other, file: item?.file, synchronousLoad: attemptSynchronousLoad)
if item == nil {
self.updateDisplayPlaceholder(displayPlaceholder: true, duration: 0.0)
}
self.started = { [weak self] in
let _ = self
//self?.updateDisplayPlaceholder(displayPlaceholder: false, duration: 0.2)
}
}
override init(layer: Any) {
self.item = nil
self.onUpdateDisplayPlaceholder = { _, _ in }
super.init(layer: layer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.disposable?.dispose()
self.fetchDisposable?.dispose()
}
override func action(forKey event: String) -> CAAction? {
if event == kCAOnOrderIn {
self.isInHierarchyValue = true
} else if event == kCAOnOrderOut {
self.isInHierarchyValue = false
}
self.updatePlayback()
return nullAction
}
private func updatePlayback() {
let shouldBePlaying = self.isInHierarchyValue && self.isVisibleForAnimations
self.shouldBeAnimating = shouldBePlaying
}
func updateDisplayPlaceholder(displayPlaceholder: Bool, duration: Double) {
if self.displayPlaceholder == displayPlaceholder {
return
}
self.displayPlaceholder = displayPlaceholder
self.onUpdateDisplayPlaceholder(displayPlaceholder, duration)
}
}
private let context: AccountContext
private var theme: PresentationTheme
private var strings: PresentationStrings
private let batchVideoRenderingContext: BatchVideoRenderingContext
private let trackingNode: MultiplexedVideoTrackingNode
public var didScroll: ((CGFloat, CGFloat) -> Void)?
@ -142,41 +251,39 @@ public final class MultiplexedVideoNode: ASDisplayNode, ASScrollViewDelegate {
}
private var displayItems: [VisibleVideoItem] = []
private var visibleThumbnailLayers: [VisibleVideoItem.Id: SoftwareVideoThumbnailNode] = [:]
private var visiblePlaceholderNodes: [Int: MultiplexedVideoPlaceholderNode] = [:]
private let contextContainerNode: ContextControllerSourceNode
public let scrollNode: ASScrollNode
private var visibleLayers: [VisibleVideoItem.Id: (SoftwareVideoLayerFrameManager, SampleBufferLayer)] = [:]
private var visibleLayers: [VisibleVideoItem.Id: ItemLayer] = [:]
private let trendingTitleNode: ImmediateTextNode
private var displayLink: CADisplayLink!
private var timeOffset = 0.0
private var pauseTime = 0.0
private let timebase: CMTimebase
public var fileSelected: ((MultiplexedVideoNodeFile, ASDisplayNode, CGRect) -> Void)?
public var fileContextMenu: ((MultiplexedVideoNodeFile, ASDisplayNode, CGRect, ContextGesture, Bool) -> Void)?
public var enableVideoNodes = false
public var enableVideoNodes = false {
didSet {
if self.enableVideoNodes != oldValue {
for (_, itemLayer) in self.visibleLayers {
itemLayer.isVisibleForAnimations = self.enableVideoNodes
}
}
}
}
private var currentActivatingId: VisibleVideoItem.Id?
private var isFinishingActivation = false
public init(account: Account, theme: PresentationTheme, strings: PresentationStrings) {
self.account = account
public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, batchVideoRenderingContext: BatchVideoRenderingContext) {
self.context = context
self.theme = theme
self.strings = strings
self.batchVideoRenderingContext = batchVideoRenderingContext
self.trackingNode = MultiplexedVideoTrackingNode()
self.trackingNode.isLayerBacked = true
var timebase: CMTimebase?
CMTimebaseCreateWithSourceClock(allocator: nil, sourceClock: CMClockGetHostTimeClock(), timebaseOut: &timebase)
CMTimebaseSetRate(timebase!, rate: 0.0)
self.timebase = timebase!
self.contextContainerNode = ContextControllerSourceNode()
self.contextContainerNode.animateScale = false
self.scrollNode = ASScrollNode()
@ -197,35 +304,8 @@ public final class MultiplexedVideoNode: ASDisplayNode, ASScrollViewDelegate {
self.addSubnode(self.contextContainerNode)
self.contextContainerNode.addSubnode(self.scrollNode)
class DisplayLinkProxy: NSObject {
weak var target: MultiplexedVideoNode?
init(target: MultiplexedVideoNode) {
self.target = target
}
@objc func displayLinkEvent() {
self.target?.displayLinkEvent()
}
}
self.displayLink = CADisplayLink(target: DisplayLinkProxy(target: self), selector: #selector(DisplayLinkProxy.displayLinkEvent))
self.displayLink.add(to: RunLoop.main, forMode: .common)
if #available(iOS 10.0, *) {
self.displayLink.preferredFramesPerSecond = 25
} else {
self.displayLink.frameInterval = 2
}
self.displayLink.isPaused = true
self.trackingNode.inHierarchyUpdated = { [weak self] value in
if let strongSelf = self {
if !value {
CMTimebaseSetRate(strongSelf.timebase, rate: 0.0)
} else {
CMTimebaseSetRate(strongSelf.timebase, rate: 1.0)
}
strongSelf.displayLink.isPaused = !value
if value && !strongSelf.enableVideoNodes {
strongSelf.enableVideoNodes = true
strongSelf.validVisibleItemsOffset = nil
@ -261,8 +341,8 @@ public final class MultiplexedVideoNode: ASDisplayNode, ASScrollViewDelegate {
return
}
if let currentActivatingId = strongSelf.currentActivatingId, let (_, layer) = strongSelf.visibleLayers[currentActivatingId] {
let layer = layer.layer
if let currentActivatingId = strongSelf.currentActivatingId, let itemLayer = strongSelf.visibleLayers[currentActivatingId] {
let layer = itemLayer
let targetContentRect: CGRect = layer.bounds
@ -286,21 +366,17 @@ public final class MultiplexedVideoNode: ASDisplayNode, ASScrollViewDelegate {
case .begin:
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
layer.sublayerTransform = sublayerTransform
if let thumbnail = strongSelf.visibleThumbnailLayers[currentActivatingId] {
thumbnail.isHidden = true
}
case .ended:
if !strongSelf.isFinishingActivation {
strongSelf.isFinishingActivation = true
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
let previousTransform = layer.sublayerTransform
layer.sublayerTransform = sublayerTransform
layer.animate(from: NSValue(caTransform3D: previousTransform), to: NSValue(caTransform3D: sublayerTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, completion: { _ in
strongSelf.isFinishingActivation = false
if let thumbnail = strongSelf.visibleThumbnailLayers[currentActivatingId] {
thumbnail.isHidden = false
layer.animate(from: NSValue(caTransform3D: previousTransform), to: NSValue(caTransform3D: sublayerTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, completion: { [weak strongSelf] _ in
guard let strongSelf else {
return
}
strongSelf.isFinishingActivation = false
})
}
}
@ -328,19 +404,6 @@ public final class MultiplexedVideoNode: ASDisplayNode, ASScrollViewDelegate {
}
deinit {
self.displayLink.invalidate()
self.displayLink.isPaused = true
for (_, value) in self.visibleLayers {
value.1.isFreed = true
}
clearSampleBufferLayerPoll()
}
private func displayLinkEvent() {
let timestamp = CMTimebaseGetTime(self.timebase).seconds
for (_, (manager, _)) in self.visibleLayers {
manager.tick(timestamp: timestamp)
}
}
private var validSize: CGSize?
@ -404,7 +467,6 @@ public final class MultiplexedVideoNode: ASDisplayNode, ASScrollViewDelegate {
let minVisibleThumbnailY = visibleThumbnailBounds.minY
let maxVisibleThumbnailY = visibleThumbnailBounds.maxY
var visibleThumbnailIds = Set<VisibleVideoItem.Id>()
var visibleIds = Set<VisibleVideoItem.Id>()
var maxVisibleIndex = -1
@ -421,31 +483,6 @@ public final class MultiplexedVideoNode: ASDisplayNode, ASScrollViewDelegate {
maxVisibleIndex = max(maxVisibleIndex, index)
visibleThumbnailIds.insert(item.id)
let thumbnailLayer: SoftwareVideoThumbnailNode
if let current = self.visibleThumbnailLayers[item.id] {
thumbnailLayer = current
if ensureFrames {
thumbnailLayer.frame = item.frame
}
} else {
var existingPlaceholderNode: MultiplexedVideoPlaceholderNode?
if let placeholderNode = self.visiblePlaceholderNodes[index] {
existingPlaceholderNode = placeholderNode
self.visiblePlaceholderNodes.removeValue(forKey: index)
placeholderNode.removeFromSupernode()
}
thumbnailLayer = SoftwareVideoThumbnailNode(account: self.account, fileReference: item.file.file, synchronousLoad: synchronous, usePlaceholder: true, existingPlaceholder: existingPlaceholderNode)
thumbnailLayer.frame = item.frame
self.scrollNode.addSubnode(thumbnailLayer)
self.visibleThumbnailLayers[item.id] = thumbnailLayer
}
thumbnailLayer.update(theme: self.theme, size: item.frame.size)
thumbnailLayer.updateAbsoluteRect(item.frame.offsetBy(dx: 0.0, dy: absoluteContainerOffset), within: absoluteContainerSize)
if item.frame.maxY < minVisibleY {
continue
}
@ -455,22 +492,25 @@ public final class MultiplexedVideoNode: ASDisplayNode, ASScrollViewDelegate {
visibleIds.insert(item.id)
if let (_, layerHolder) = self.visibleLayers[item.id] {
if let itemLayer = self.visibleLayers[item.id] {
if ensureFrames {
layerHolder.layer.frame = item.frame
itemLayer.frame = item.frame
}
itemLayer.isVisibleForAnimations = self.enableVideoNodes
} else {
let layerHolder = takeSampleBufferLayer()
layerHolder.layer.videoGravity = AVLayerVideoGravity.resizeAspectFill
layerHolder.layer.frame = item.frame
self.scrollNode.layer.addSublayer(layerHolder.layer)
let manager = SoftwareVideoLayerFrameManager(account: self.account, userLocation: .other, userContentType: .other, fileReference: item.file.file, layerHolder: layerHolder)
self.visibleLayers[item.id] = (manager, layerHolder)
self.visibleThumbnailLayers[item.id]?.ready = { [weak self] in
if let strongSelf = self {
strongSelf.visibleLayers[item.id]?.0.start()
let itemLayer = ItemLayer(
item: Item(file: item.file.file, contextResult: item.file.contextResult),
context: self.context,
batchVideoContext: self.batchVideoRenderingContext,
groupId: "",
attemptSynchronousLoad: false,
onUpdateDisplayPlaceholder: { _, _ in
}
}
)
itemLayer.frame = item.frame
self.scrollNode.layer.addSublayer(itemLayer)
self.visibleLayers[item.id] = itemLayer
itemLayer.isVisibleForAnimations = self.enableVideoNodes
}
}
@ -518,13 +558,6 @@ public final class MultiplexedVideoNode: ASDisplayNode, ASScrollViewDelegate {
}
}
var removeThumbnailIds: [VisibleVideoItem.Id] = []
for id in self.visibleThumbnailLayers.keys {
if !visibleThumbnailIds.contains(id) {
removeThumbnailIds.append(id)
}
}
var removePlaceholderIndices: [Int] = []
for index in self.visiblePlaceholderNodes.keys {
if !visiblePlaceholderIndices.contains(index) {
@ -533,17 +566,11 @@ public final class MultiplexedVideoNode: ASDisplayNode, ASScrollViewDelegate {
}
for id in removeIds {
let (_, layerHolder) = self.visibleLayers[id]!
layerHolder.layer.removeFromSuperlayer()
let itemLayer = self.visibleLayers[id]!
itemLayer.removeFromSuperlayer()
self.visibleLayers.removeValue(forKey: id)
}
for id in removeThumbnailIds {
let thumbnailLayer = self.visibleThumbnailLayers[id]!
thumbnailLayer.removeFromSupernode()
self.visibleThumbnailLayers.removeValue(forKey: id)
}
for index in removePlaceholderIndices {
if let placeholderNode = self.visiblePlaceholderNodes[index] {
placeholderNode.removeFromSupernode()

View File

@ -332,6 +332,7 @@ private final class StickerSelectionComponent: Component {
interaction: interaction,
inputNodeInteraction: inputNodeInteraction,
mode: mappedMode,
batchVideoRenderingContext: nil,
stickerActionTitle: presentationData.strings.StickerPack_AddSticker,
trendingGifsPromise: self.component?.getController()?.node.trendingGifsPromise ?? Promise(nil),
cancel: {

View File

@ -445,7 +445,6 @@ extension ChatControllerImpl {
let purchaseScreen = strongSelf.context.sharedContext.makeStarsPurchaseScreen(context: strongSelf.context, starsContext: starsContext, options: options, purpose: .reactions(peerId: message.id.peerId, requiredStars: 1), completion: { result in
let _ = result
//TODO:release
})
strongSelf.push(purchaseScreen)
})

View File

@ -1787,7 +1787,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let purchaseScreen = strongSelf.context.sharedContext.makeStarsPurchaseScreen(context: strongSelf.context, starsContext: starsContext, options: options, purpose: .reactions(peerId: peerId, requiredStars: 1), completion: { result in
let _ = result
//TODO:release
})
strongSelf.push(purchaseScreen)
})