mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-04 10:30:42 +00:00
Merge commit '21632c6dc023684b84ba189e360a922cb2829b38'
This commit is contained in:
commit
382bd7a165
@ -6477,3 +6477,5 @@ Sorry for the inconvenience.";
|
|||||||
"WallpaperPreview.WallpaperColors" = "Colors";
|
"WallpaperPreview.WallpaperColors" = "Colors";
|
||||||
|
|
||||||
"VoiceChat.UnmuteSuggestion" = "You are on mute. Tap here to speak.";
|
"VoiceChat.UnmuteSuggestion" = "You are on mute. Tap here to speak.";
|
||||||
|
|
||||||
|
"VoiceChat.ContextAudio" = "Audio";
|
||||||
|
@ -31,6 +31,7 @@ import UIKitRuntimeUtils
|
|||||||
private let completionKey = "CAAnimationUtils_completion"
|
private let completionKey = "CAAnimationUtils_completion"
|
||||||
|
|
||||||
public let kCAMediaTimingFunctionSpring = "CAAnimationUtilsSpringCurve"
|
public let kCAMediaTimingFunctionSpring = "CAAnimationUtilsSpringCurve"
|
||||||
|
public let kCAMediaTimingFunctionCustomSpringPrefix = "CAAnimationUtilsSpringCustomCurve"
|
||||||
|
|
||||||
public extension CAAnimation {
|
public extension CAAnimation {
|
||||||
var completion: ((Bool) -> Void)? {
|
var completion: ((Bool) -> Void)? {
|
||||||
@ -52,7 +53,38 @@ public extension CAAnimation {
|
|||||||
|
|
||||||
public extension CALayer {
|
public extension CALayer {
|
||||||
func makeAnimation(from: AnyObject, to: AnyObject, keyPath: String, timingFunction: String, duration: Double, delay: Double = 0.0, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) -> CAAnimation {
|
func makeAnimation(from: AnyObject, to: AnyObject, keyPath: String, timingFunction: String, duration: Double, delay: Double = 0.0, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) -> CAAnimation {
|
||||||
if timingFunction == kCAMediaTimingFunctionSpring {
|
if timingFunction.hasPrefix(kCAMediaTimingFunctionCustomSpringPrefix) {
|
||||||
|
let components = timingFunction.components(separatedBy: "_")
|
||||||
|
let damping = Float(components[1]) ?? 100.0
|
||||||
|
let initialVelocity = Float(components[2]) ?? 0.0
|
||||||
|
|
||||||
|
let animation = CASpringAnimation(keyPath: keyPath)
|
||||||
|
animation.fromValue = from
|
||||||
|
animation.toValue = to
|
||||||
|
animation.isRemovedOnCompletion = removeOnCompletion
|
||||||
|
animation.fillMode = .forwards
|
||||||
|
if let completion = completion {
|
||||||
|
animation.delegate = CALayerAnimationDelegate(animation: animation, completion: completion)
|
||||||
|
}
|
||||||
|
animation.damping = CGFloat(damping)
|
||||||
|
animation.initialVelocity = CGFloat(initialVelocity)
|
||||||
|
animation.mass = 5.0
|
||||||
|
animation.stiffness = 900.0
|
||||||
|
animation.duration = animation.settlingDuration
|
||||||
|
animation.timingFunction = CAMediaTimingFunction.init(name: .linear)
|
||||||
|
let k = Float(UIView.animationDurationFactor())
|
||||||
|
var speed: Float = 1.0
|
||||||
|
if k != 0 && k != 1 {
|
||||||
|
speed = Float(1.0) / k
|
||||||
|
}
|
||||||
|
animation.speed = speed * Float(animation.duration / duration)
|
||||||
|
animation.isAdditive = additive
|
||||||
|
if !delay.isZero {
|
||||||
|
animation.beginTime = self.convertTime(CACurrentMediaTime(), from: nil) + delay * UIView.animationDurationFactor()
|
||||||
|
animation.fillMode = .both
|
||||||
|
}
|
||||||
|
return animation
|
||||||
|
} else if timingFunction == kCAMediaTimingFunctionSpring {
|
||||||
let animation = makeSpringAnimation(keyPath)
|
let animation = makeSpringAnimation(keyPath)
|
||||||
animation.fromValue = from
|
animation.fromValue = from
|
||||||
animation.toValue = to
|
animation.toValue = to
|
||||||
|
@ -6,6 +6,7 @@ public enum ContainedViewLayoutTransitionCurve: Equatable, Hashable {
|
|||||||
case linear
|
case linear
|
||||||
case easeInOut
|
case easeInOut
|
||||||
case spring
|
case spring
|
||||||
|
case customSpring(damping: CGFloat, initialVelocity: CGFloat)
|
||||||
case custom(Float, Float, Float, Float)
|
case custom(Float, Float, Float, Float)
|
||||||
|
|
||||||
public static var slide: ContainedViewLayoutTransitionCurve {
|
public static var slide: ContainedViewLayoutTransitionCurve {
|
||||||
@ -22,6 +23,8 @@ public extension ContainedViewLayoutTransitionCurve {
|
|||||||
return listViewAnimationCurveEaseInOut(offset)
|
return listViewAnimationCurveEaseInOut(offset)
|
||||||
case .spring:
|
case .spring:
|
||||||
return listViewAnimationCurveSystem(offset)
|
return listViewAnimationCurveSystem(offset)
|
||||||
|
case .customSpring:
|
||||||
|
return listViewAnimationCurveSystem(offset)
|
||||||
case let .custom(c1x, c1y, c2x, c2y):
|
case let .custom(c1x, c1y, c2x, c2y):
|
||||||
return bezierPoint(CGFloat(c1x), CGFloat(c1y), CGFloat(c2x), CGFloat(c2y), offset)
|
return bezierPoint(CGFloat(c1x), CGFloat(c1y), CGFloat(c2x), CGFloat(c2y), offset)
|
||||||
}
|
}
|
||||||
@ -37,6 +40,8 @@ public extension ContainedViewLayoutTransitionCurve {
|
|||||||
return CAMediaTimingFunctionName.easeInEaseOut.rawValue
|
return CAMediaTimingFunctionName.easeInEaseOut.rawValue
|
||||||
case .spring:
|
case .spring:
|
||||||
return kCAMediaTimingFunctionSpring
|
return kCAMediaTimingFunctionSpring
|
||||||
|
case let .customSpring(damping, initialVelocity):
|
||||||
|
return "\(kCAMediaTimingFunctionCustomSpringPrefix)_\(damping)_\(initialVelocity)"
|
||||||
case .custom:
|
case .custom:
|
||||||
return CAMediaTimingFunctionName.easeInEaseOut.rawValue
|
return CAMediaTimingFunctionName.easeInEaseOut.rawValue
|
||||||
}
|
}
|
||||||
@ -50,6 +55,8 @@ public extension ContainedViewLayoutTransitionCurve {
|
|||||||
return nil
|
return nil
|
||||||
case .spring:
|
case .spring:
|
||||||
return nil
|
return nil
|
||||||
|
case .customSpring:
|
||||||
|
return nil
|
||||||
case let .custom(p1, p2, p3, p4):
|
case let .custom(p1, p2, p3, p4):
|
||||||
return CAMediaTimingFunction(controlPoints: p1, p2, p3, p4)
|
return CAMediaTimingFunction(controlPoints: p1, p2, p3, p4)
|
||||||
}
|
}
|
||||||
@ -64,6 +71,8 @@ public extension ContainedViewLayoutTransitionCurve {
|
|||||||
return [.curveEaseInOut]
|
return [.curveEaseInOut]
|
||||||
case .spring:
|
case .spring:
|
||||||
return UIView.AnimationOptions(rawValue: 7 << 16)
|
return UIView.AnimationOptions(rawValue: 7 << 16)
|
||||||
|
case .customSpring:
|
||||||
|
return UIView.AnimationOptions(rawValue: 7 << 16)
|
||||||
case .custom:
|
case .custom:
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -3307,7 +3307,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
|||||||
switch curve {
|
switch curve {
|
||||||
case .linear:
|
case .linear:
|
||||||
headerNode.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, mediaTimingFunction: CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear))
|
headerNode.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, mediaTimingFunction: CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear))
|
||||||
case .spring:
|
case .spring, .customSpring:
|
||||||
transition.0.animateOffsetAdditive(node: headerNode, offset: offset)
|
transition.0.animateOffsetAdditive(node: headerNode, offset: offset)
|
||||||
case let .custom(p1, p2, p3, p4):
|
case let .custom(p1, p2, p3, p4):
|
||||||
headerNode.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, mediaTimingFunction: CAMediaTimingFunction(controlPoints: p1, p2, p3, p4))
|
headerNode.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, mediaTimingFunction: CAMediaTimingFunction(controlPoints: p1, p2, p3, p4))
|
||||||
|
@ -192,7 +192,7 @@ public func listViewAnimationDurationAndCurve(transition: ContainedViewLayoutTra
|
|||||||
return (animationDuration, .Default(duration: animationDuration))
|
return (animationDuration, .Default(duration: animationDuration))
|
||||||
case .easeInOut:
|
case .easeInOut:
|
||||||
return (animationDuration, .Default(duration: animationDuration))
|
return (animationDuration, .Default(duration: animationDuration))
|
||||||
case .spring:
|
case .spring, .customSpring:
|
||||||
return (animationDuration, .Spring(duration: animationDuration))
|
return (animationDuration, .Spring(duration: animationDuration))
|
||||||
case let .custom(c1x, c1y, c2x, c2y):
|
case let .custom(c1x, c1y, c2x, c2y):
|
||||||
return (animationDuration, .Custom(duration: animationDuration, c1x, c1y, c2x, c2y))
|
return (animationDuration, .Custom(duration: animationDuration, c1x, c1y, c2x, c2y))
|
||||||
|
@ -69,6 +69,42 @@ func decorationCornersImage(top: Bool, bottom: Bool, dark: Bool) -> UIImage? {
|
|||||||
})?.stretchableImage(withLeftCapWidth: 25, topCapHeight: 25)
|
})?.stretchableImage(withLeftCapWidth: 25, topCapHeight: 25)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decorationTopCornersImage(dark: Bool) -> UIImage? {
|
||||||
|
return generateImage(CGSize(width: 50.0, height: 110.0), rotatedContext: { (size, context) in
|
||||||
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||||
|
context.setFillColor((dark ? fullscreenBackgroundColor : panelBackgroundColor).cgColor)
|
||||||
|
context.fill(bounds)
|
||||||
|
|
||||||
|
context.setBlendMode(.clear)
|
||||||
|
|
||||||
|
var corners: UIRectCorner = []
|
||||||
|
corners.insert(.topLeft)
|
||||||
|
corners.insert(.topRight)
|
||||||
|
|
||||||
|
let path = UIBezierPath(roundedRect: CGRect(x: 0.0, y: 60.0, width: 50.0, height: 50.0), byRoundingCorners: corners, cornerRadii: CGSize(width: 11.0, height: 11.0))
|
||||||
|
context.addPath(path.cgPath)
|
||||||
|
context.fillPath()
|
||||||
|
})?.stretchableImage(withLeftCapWidth: 25, topCapHeight: 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decorationBottomCornersImage(dark: Bool) -> UIImage? {
|
||||||
|
return generateImage(CGSize(width: 50.0, height: 110.0), rotatedContext: { (size, context) in
|
||||||
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||||
|
context.setFillColor((dark ? fullscreenBackgroundColor : panelBackgroundColor).cgColor)
|
||||||
|
context.fill(bounds)
|
||||||
|
|
||||||
|
context.setBlendMode(.clear)
|
||||||
|
|
||||||
|
var corners: UIRectCorner = []
|
||||||
|
corners.insert(.bottomLeft)
|
||||||
|
corners.insert(.bottomRight)
|
||||||
|
|
||||||
|
let path = UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: 50.0, height: 50.0), byRoundingCorners: corners, cornerRadii: CGSize(width: 11.0, height: 11.0))
|
||||||
|
context.addPath(path.cgPath)
|
||||||
|
context.fillPath()
|
||||||
|
})?.resizableImage(withCapInsets: UIEdgeInsets(top: 25.0, left: 25.0, bottom: 0.0, right: 25.0), resizingMode: .stretch)
|
||||||
|
}
|
||||||
|
|
||||||
private func decorationBottomGradientImage(dark: Bool) -> UIImage? {
|
private func decorationBottomGradientImage(dark: Bool) -> UIImage? {
|
||||||
return generateImage(CGSize(width: 24.0, height: bottomGradientHeight), rotatedContext: { size, context in
|
return generateImage(CGSize(width: 24.0, height: bottomGradientHeight), rotatedContext: { size, context in
|
||||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||||
@ -79,9 +115,6 @@ private func decorationBottomGradientImage(dark: Bool) -> UIImage? {
|
|||||||
var locations: [CGFloat] = [1.0, 0.0]
|
var locations: [CGFloat] = [1.0, 0.0]
|
||||||
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray, locations: &locations)!
|
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray, locations: &locations)!
|
||||||
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
|
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
|
||||||
//
|
|
||||||
// context.setFillColor(UIColor.red.cgColor)
|
|
||||||
// context.fill(bounds)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -697,7 +730,9 @@ public final class VoiceChatController: ViewController {
|
|||||||
private let dimNode: ASDisplayNode
|
private let dimNode: ASDisplayNode
|
||||||
private let contentContainer: ASDisplayNode
|
private let contentContainer: ASDisplayNode
|
||||||
private let backgroundNode: ASDisplayNode
|
private let backgroundNode: ASDisplayNode
|
||||||
|
private let listContainer: ASDisplayNode
|
||||||
private let listNode: ListView
|
private let listNode: ListView
|
||||||
|
private let fullscreenListContainer: ASDisplayNode
|
||||||
private let fullscreenListNode: ListView
|
private let fullscreenListNode: ListView
|
||||||
private let topPanelNode: ASDisplayNode
|
private let topPanelNode: ASDisplayNode
|
||||||
private let topPanelEdgeNode: ASDisplayNode
|
private let topPanelEdgeNode: ASDisplayNode
|
||||||
@ -705,7 +740,6 @@ public final class VoiceChatController: ViewController {
|
|||||||
private let optionsButton: VoiceChatHeaderButton
|
private let optionsButton: VoiceChatHeaderButton
|
||||||
private let closeButton: VoiceChatHeaderButton
|
private let closeButton: VoiceChatHeaderButton
|
||||||
private let topCornersNode: ASImageNode
|
private let topCornersNode: ASImageNode
|
||||||
private let videoBottomCornersNode: ASImageNode
|
|
||||||
fileprivate let bottomPanelNode: ASDisplayNode
|
fileprivate let bottomPanelNode: ASDisplayNode
|
||||||
private let bottomGradientNode: ASDisplayNode
|
private let bottomGradientNode: ASDisplayNode
|
||||||
private let bottomPanelBackgroundNode: ASDisplayNode
|
private let bottomPanelBackgroundNode: ASDisplayNode
|
||||||
@ -827,7 +861,8 @@ public final class VoiceChatController: ViewController {
|
|||||||
private var videoNodes: [String: GroupVideoNode] = [:]
|
private var videoNodes: [String: GroupVideoNode] = [:]
|
||||||
private var wideVideoNodes = Set<String>()
|
private var wideVideoNodes = Set<String>()
|
||||||
private var videoOrder: [String] = []
|
private var videoOrder: [String] = []
|
||||||
private var readyVideoNodes = Set<String>()
|
private var readyVideoEndpointIds = Set<String>()
|
||||||
|
private var timeoutedEndpointIds = Set<String>()
|
||||||
private var readyVideoDisposables = DisposableDict<String>()
|
private var readyVideoDisposables = DisposableDict<String>()
|
||||||
|
|
||||||
private var endpointToPeerId: [String: PeerId] = [:]
|
private var endpointToPeerId: [String: PeerId] = [:]
|
||||||
@ -892,6 +927,8 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.backgroundNode.backgroundColor = self.isScheduling ? panelBackgroundColor : secondaryPanelBackgroundColor
|
self.backgroundNode.backgroundColor = self.isScheduling ? panelBackgroundColor : secondaryPanelBackgroundColor
|
||||||
self.backgroundNode.clipsToBounds = false
|
self.backgroundNode.clipsToBounds = false
|
||||||
|
|
||||||
|
self.listContainer = ASDisplayNode()
|
||||||
|
|
||||||
self.listNode = ListView()
|
self.listNode = ListView()
|
||||||
self.listNode.alpha = self.isScheduling ? 0.0 : 1.0
|
self.listNode.alpha = self.isScheduling ? 0.0 : 1.0
|
||||||
self.listNode.isUserInteractionEnabled = !self.isScheduling
|
self.listNode.isUserInteractionEnabled = !self.isScheduling
|
||||||
@ -902,10 +939,12 @@ public final class VoiceChatController: ViewController {
|
|||||||
return presentationData.strings.VoiceOver_ScrollStatus(row, count).0
|
return presentationData.strings.VoiceOver_ScrollStatus(row, count).0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.fullscreenListContainer = ASDisplayNode()
|
||||||
|
self.fullscreenListContainer.isHidden = true
|
||||||
|
|
||||||
self.fullscreenListNode = ListView()
|
self.fullscreenListNode = ListView()
|
||||||
self.fullscreenListNode.transform = CATransform3DMakeRotation(-CGFloat(CGFloat.pi / 2.0), 0.0, 0.0, 1.0)
|
self.fullscreenListNode.transform = CATransform3DMakeRotation(-CGFloat(CGFloat.pi / 2.0), 0.0, 0.0, 1.0)
|
||||||
self.fullscreenListNode.clipsToBounds = true
|
self.fullscreenListNode.clipsToBounds = true
|
||||||
self.fullscreenListNode.isHidden = true
|
|
||||||
self.fullscreenListNode.accessibilityPageScrolledString = { row, count in
|
self.fullscreenListNode.accessibilityPageScrolledString = { row, count in
|
||||||
return presentationData.strings.VoiceOver_ScrollStatus(row, count).0
|
return presentationData.strings.VoiceOver_ScrollStatus(row, count).0
|
||||||
}
|
}
|
||||||
@ -935,7 +974,8 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.topCornersNode = ASImageNode()
|
self.topCornersNode = ASImageNode()
|
||||||
self.topCornersNode.displaysAsynchronously = false
|
self.topCornersNode.displaysAsynchronously = false
|
||||||
self.topCornersNode.displayWithoutProcessing = true
|
self.topCornersNode.displayWithoutProcessing = true
|
||||||
self.topCornersNode.image = decorationCornersImage(top: true, bottom: false, dark: false)
|
self.topCornersNode.image = decorationTopCornersImage(dark: false)
|
||||||
|
self.topCornersNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
self.bottomPanelNode = ASDisplayNode()
|
self.bottomPanelNode = ASDisplayNode()
|
||||||
self.bottomPanelNode.clipsToBounds = false
|
self.bottomPanelNode.clipsToBounds = false
|
||||||
@ -951,15 +991,9 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.bottomCornersNode = ASImageNode()
|
self.bottomCornersNode = ASImageNode()
|
||||||
self.bottomCornersNode.displaysAsynchronously = false
|
self.bottomCornersNode.displaysAsynchronously = false
|
||||||
self.bottomCornersNode.displayWithoutProcessing = true
|
self.bottomCornersNode.displayWithoutProcessing = true
|
||||||
self.bottomCornersNode.image = decorationCornersImage(top: false, bottom: true, dark: false)
|
self.bottomCornersNode.image = decorationBottomCornersImage(dark: false)
|
||||||
self.bottomCornersNode.isUserInteractionEnabled = false
|
self.bottomCornersNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
self.videoBottomCornersNode = ASImageNode()
|
|
||||||
self.videoBottomCornersNode.displaysAsynchronously = false
|
|
||||||
self.videoBottomCornersNode.displayWithoutProcessing = true
|
|
||||||
self.videoBottomCornersNode.image = decorationCornersImage(top: false, bottom: true, dark: false)
|
|
||||||
self.videoBottomCornersNode.isUserInteractionEnabled = false
|
|
||||||
|
|
||||||
self.audioButton = CallControllerButtonItemNode()
|
self.audioButton = CallControllerButtonItemNode()
|
||||||
self.cameraButton = CallControllerButtonItemNode(largeButtonSize: sideButtonSize.width)
|
self.cameraButton = CallControllerButtonItemNode(largeButtonSize: sideButtonSize.width)
|
||||||
self.switchCameraButton = CallControllerButtonItemNode()
|
self.switchCameraButton = CallControllerButtonItemNode()
|
||||||
@ -1655,7 +1689,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
if ignore {
|
if ignore {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !strongSelf.readyVideoNodes.contains(endpointId) {
|
if !strongSelf.readyVideoEndpointIds.contains(endpointId) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for (listEndpointId, videoNode) in strongSelf.videoNodes {
|
for (listEndpointId, videoNode) in strongSelf.videoNodes {
|
||||||
@ -1672,7 +1706,6 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.topPanelNode.addSubnode(self.titleNode)
|
self.topPanelNode.addSubnode(self.titleNode)
|
||||||
self.topPanelNode.addSubnode(self.optionsButton)
|
self.topPanelNode.addSubnode(self.optionsButton)
|
||||||
self.topPanelNode.addSubnode(self.closeButton)
|
self.topPanelNode.addSubnode(self.closeButton)
|
||||||
self.topPanelNode.addSubnode(self.topCornersNode)
|
|
||||||
|
|
||||||
self.bottomPanelNode.addSubnode(self.cameraButton)
|
self.bottomPanelNode.addSubnode(self.cameraButton)
|
||||||
self.bottomPanelNode.addSubnode(self.audioButton)
|
self.bottomPanelNode.addSubnode(self.audioButton)
|
||||||
@ -1685,11 +1718,13 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.addSubnode(self.contentContainer)
|
self.addSubnode(self.contentContainer)
|
||||||
self.contentContainer.addSubnode(self.backgroundNode)
|
self.contentContainer.addSubnode(self.backgroundNode)
|
||||||
|
|
||||||
self.contentContainer.addSubnode(self.listNode)
|
self.contentContainer.addSubnode(self.listContainer)
|
||||||
self.contentContainer.addSubnode(self.topPanelNode)
|
self.contentContainer.addSubnode(self.topPanelNode)
|
||||||
self.contentContainer.addSubnode(self.leftBorderNode)
|
self.listContainer.addSubnode(self.listNode)
|
||||||
self.contentContainer.addSubnode(self.rightBorderNode)
|
self.listContainer.addSubnode(self.leftBorderNode)
|
||||||
self.contentContainer.addSubnode(self.bottomCornersNode)
|
self.listContainer.addSubnode(self.rightBorderNode)
|
||||||
|
self.listContainer.addSubnode(self.bottomCornersNode)
|
||||||
|
self.listContainer.addSubnode(self.topCornersNode)
|
||||||
self.contentContainer.addSubnode(self.bottomGradientNode)
|
self.contentContainer.addSubnode(self.bottomGradientNode)
|
||||||
self.contentContainer.addSubnode(self.bottomPanelBackgroundNode)
|
self.contentContainer.addSubnode(self.bottomPanelBackgroundNode)
|
||||||
self.contentContainer.addSubnode(self.mainStageContainerNode)
|
self.contentContainer.addSubnode(self.mainStageContainerNode)
|
||||||
@ -1697,7 +1732,8 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.contentContainer.addSubnode(self.bottomPanelNode)
|
self.contentContainer.addSubnode(self.bottomPanelNode)
|
||||||
self.contentContainer.addSubnode(self.timerNode)
|
self.contentContainer.addSubnode(self.timerNode)
|
||||||
self.contentContainer.addSubnode(self.scheduleTextNode)
|
self.contentContainer.addSubnode(self.scheduleTextNode)
|
||||||
self.contentContainer.addSubnode(self.fullscreenListNode)
|
self.contentContainer.addSubnode(self.fullscreenListContainer)
|
||||||
|
self.fullscreenListContainer.addSubnode(self.fullscreenListNode)
|
||||||
|
|
||||||
self.mainStageContainerNode.addSubnode(self.mainStageBackgroundNode)
|
self.mainStageContainerNode.addSubnode(self.mainStageBackgroundNode)
|
||||||
self.mainStageContainerNode.addSubnode(self.mainStageNode)
|
self.mainStageContainerNode.addSubnode(self.mainStageNode)
|
||||||
@ -1874,7 +1910,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if case .fullscreen = strongSelf.displayMode, !strongSelf.mainStageNode.animating {
|
if case .fullscreen = strongSelf.displayMode, !strongSelf.mainStageNode.animating && !strongSelf.animatingExpansion {
|
||||||
if let (peerId, _) = maxLevelWithVideo {
|
if let (peerId, _) = maxLevelWithVideo {
|
||||||
if let (currentPeerId, _, timestamp) = strongSelf.currentDominantSpeaker {
|
if let (currentPeerId, _, timestamp) = strongSelf.currentDominantSpeaker {
|
||||||
if CACurrentMediaTime() - timestamp > 2.5 && peerId != currentPeerId {
|
if CACurrentMediaTime() - timestamp > 2.5 && peerId != currentPeerId {
|
||||||
@ -2077,7 +2113,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.mainStageNode.back = { [weak self] in
|
self.mainStageNode.back = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.currentForcedSpeaker = nil
|
strongSelf.currentForcedSpeaker = nil
|
||||||
strongSelf.updateDisplayMode(.modal(isExpanded: true, isFilled: true))
|
strongSelf.updateDisplayMode(.modal(isExpanded: true, isFilled: true), fromPan: true)
|
||||||
strongSelf.effectiveSpeaker = nil
|
strongSelf.effectiveSpeaker = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2168,6 +2204,35 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let (availableOutputs, currentOutput) = strongSelf.audioOutputState {
|
||||||
|
var currentOutputTitle = ""
|
||||||
|
for output in availableOutputs {
|
||||||
|
if output == currentOutput {
|
||||||
|
let title: String
|
||||||
|
switch output {
|
||||||
|
case .builtin:
|
||||||
|
title = UIDevice.current.model
|
||||||
|
case .speaker:
|
||||||
|
title = strongSelf.presentationData.strings.Call_AudioRouteSpeaker
|
||||||
|
case .headphones:
|
||||||
|
title = strongSelf.presentationData.strings.Call_AudioRouteHeadphones
|
||||||
|
case let .port(port):
|
||||||
|
title = port.name
|
||||||
|
}
|
||||||
|
currentOutputTitle = title
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_ContextAudio, textLayout: .secondLineWithValue(currentOutputTitle), icon: { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Call/Context Menu/Audio"), color: theme.actionSheet.primaryTextColor)
|
||||||
|
}, action: { c, _ in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.setItems(strongSelf.contextMenuAudioItems())
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
if canManageCall {
|
if canManageCall {
|
||||||
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_EditTitle, icon: { theme -> UIImage? in
|
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_EditTitle, icon: { theme -> UIImage? in
|
||||||
@ -2347,6 +2412,47 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func contextMenuAudioItems() -> Signal<[ContextMenuItem], NoError> {
|
||||||
|
guard let (availableOutputs, currentOutput) = self.audioOutputState else {
|
||||||
|
return .single([])
|
||||||
|
}
|
||||||
|
|
||||||
|
var items: [ContextMenuItem] = []
|
||||||
|
for output in availableOutputs {
|
||||||
|
let title: String
|
||||||
|
switch output {
|
||||||
|
case .builtin:
|
||||||
|
title = UIDevice.current.model
|
||||||
|
case .speaker:
|
||||||
|
title = self.presentationData.strings.Call_AudioRouteSpeaker
|
||||||
|
case .headphones:
|
||||||
|
title = self.presentationData.strings.Call_AudioRouteHeadphones
|
||||||
|
case let .port(port):
|
||||||
|
title = port.name
|
||||||
|
}
|
||||||
|
items.append(.action(ContextMenuActionItem(text: title, icon: { theme in
|
||||||
|
if output == currentOutput {
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.actionSheet.primaryTextColor)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}, action: { [weak self] _, f in
|
||||||
|
f(.default)
|
||||||
|
self?.call.setCurrentAudioOutput(output)
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
items.append(.separator)
|
||||||
|
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Common_Back, icon: { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.actionSheet.primaryTextColor)
|
||||||
|
}, action: { [weak self] (c, _) in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.setItems(strongSelf.contextMenuMainItems())
|
||||||
|
})))
|
||||||
|
return .single(items)
|
||||||
|
}
|
||||||
|
|
||||||
private func contextMenuDisplayAsItems() -> Signal<[ContextMenuItem], NoError> {
|
private func contextMenuDisplayAsItems() -> Signal<[ContextMenuItem], NoError> {
|
||||||
guard let myPeerId = self.callState?.myPeerId else {
|
guard let myPeerId = self.callState?.myPeerId else {
|
||||||
return .single([])
|
return .single([])
|
||||||
@ -3221,7 +3327,6 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var bringVideoToBackOnCompletion = false
|
|
||||||
private func updateDecorationsLayout(transition: ContainedViewLayoutTransition, completion: (() -> Void)? = nil) {
|
private func updateDecorationsLayout(transition: ContainedViewLayoutTransition, completion: (() -> Void)? = nil) {
|
||||||
guard let (layout, _) = self.validLayout else {
|
guard let (layout, _) = self.validLayout else {
|
||||||
return
|
return
|
||||||
@ -3341,14 +3446,17 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
let leftBorderFrame: CGRect
|
let leftBorderFrame: CGRect
|
||||||
let rightBorderFrame: CGRect
|
let rightBorderFrame: CGRect
|
||||||
|
let additionalInset: CGFloat = 60.0
|
||||||
if isLandscape {
|
if isLandscape {
|
||||||
leftBorderFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelFrame.maxY - 16.0), size: CGSize(width: (size.width - contentWidth) / 2.0 + sideInset, height: layout.size.height))
|
leftBorderFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelFrame.maxY - additionalInset), size: CGSize(width: (size.width - contentWidth) / 2.0 + sideInset, height: layout.size.height))
|
||||||
rightBorderFrame = CGRect(origin: CGPoint(x: size.width - (size.width - contentWidth) / 2.0 - sideInset, y: topPanelFrame.maxY - 16.0), size: CGSize(width: layout.safeInsets.right + (size.width - contentWidth) / 2.0 + sideInset, height: layout.size.height))
|
rightBorderFrame = CGRect(origin: CGPoint(x: size.width - (size.width - contentWidth) / 2.0 - sideInset, y: topPanelFrame.maxY - additionalInset), size: CGSize(width: layout.safeInsets.right + (size.width - contentWidth) / 2.0 + sideInset, height: layout.size.height))
|
||||||
} else {
|
} else {
|
||||||
leftBorderFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelFrame.maxY - 16.0), size: CGSize(width: sideInset, height: layout.size.height))
|
leftBorderFrame = CGRect(origin: CGPoint(x: -additionalInset, y: topPanelFrame.maxY - additionalInset), size: CGSize(width: sideInset + additionalInset, height: layout.size.height))
|
||||||
rightBorderFrame = CGRect(origin: CGPoint(x: size.width - sideInset, y: topPanelFrame.maxY - 16.0), size: CGSize(width: sideInset, height: layout.size.height))
|
rightBorderFrame = CGRect(origin: CGPoint(x: size.width - sideInset, y: topPanelFrame.maxY - additionalInset), size: CGSize(width: sideInset + additionalInset, height: layout.size.height))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let topCornersFrame = CGRect(x: sideInset + floorToScreenPixels((size.width - contentWidth) / 2.0), y: topPanelFrame.maxY - 60.0, width: contentWidth - sideInset * 2.0, height: 50.0 + 60.0)
|
||||||
|
|
||||||
let previousTopPanelFrame = self.topPanelNode.frame
|
let previousTopPanelFrame = self.topPanelNode.frame
|
||||||
let previousBackgroundFrame = self.backgroundNode.frame
|
let previousBackgroundFrame = self.backgroundNode.frame
|
||||||
let previousLeftBorderFrame = self.leftBorderNode.frame
|
let previousLeftBorderFrame = self.leftBorderNode.frame
|
||||||
@ -3359,6 +3467,8 @@ public final class VoiceChatController: ViewController {
|
|||||||
let positionDelta = CGPoint(x: 0.0, y: topPanelFrame.minY - previousTopPanelFrame.minY)
|
let positionDelta = CGPoint(x: 0.0, y: topPanelFrame.minY - previousTopPanelFrame.minY)
|
||||||
transition.animateOffsetAdditive(layer: self.topPanelNode.layer, offset: positionDelta.y, completion: completion)
|
transition.animateOffsetAdditive(layer: self.topPanelNode.layer, offset: positionDelta.y, completion: completion)
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.topCornersNode, frame: topCornersFrame)
|
||||||
|
|
||||||
self.backgroundNode.frame = backgroundFrame
|
self.backgroundNode.frame = backgroundFrame
|
||||||
let backgroundPositionDelta = CGPoint(x: 0.0, y: previousBackgroundFrame.minY - backgroundFrame.minY)
|
let backgroundPositionDelta = CGPoint(x: 0.0, y: previousBackgroundFrame.minY - backgroundFrame.minY)
|
||||||
transition.animatePositionAdditive(node: self.backgroundNode, offset: backgroundPositionDelta)
|
transition.animatePositionAdditive(node: self.backgroundNode, offset: backgroundPositionDelta)
|
||||||
@ -3379,7 +3489,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
let listMaxY = listTopInset + listSize.height
|
let listMaxY = listTopInset + listSize.height
|
||||||
let bottomOffset = min(0.0, bottomEdge - listMaxY) + layout.size.height - bottomPanelHeight
|
let bottomOffset = min(0.0, bottomEdge - listMaxY) + layout.size.height - bottomPanelHeight
|
||||||
|
|
||||||
let bottomCornersFrame = CGRect(origin: CGPoint(x: sideInset + floorToScreenPixels((size.width - contentWidth) / 2.0), y: -50.0 + bottomOffset + bottomGradientHeight), size: CGSize(width: contentWidth - sideInset * 2.0, height: 50.0))
|
let bottomCornersFrame = CGRect(origin: CGPoint(x: sideInset + floorToScreenPixels((size.width - contentWidth) / 2.0), y: -50.0 + bottomOffset + bottomGradientHeight), size: CGSize(width: contentWidth - sideInset * 2.0, height: 50.0 + 40.0))
|
||||||
let previousBottomCornersFrame = self.bottomCornersNode.frame
|
let previousBottomCornersFrame = self.bottomCornersNode.frame
|
||||||
if !bottomCornersFrame.equalTo(previousBottomCornersFrame) {
|
if !bottomCornersFrame.equalTo(previousBottomCornersFrame) {
|
||||||
self.bottomCornersNode.frame = bottomCornersFrame
|
self.bottomCornersNode.frame = bottomCornersFrame
|
||||||
@ -3453,14 +3563,14 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.decorationsAreDark = isFullscreen
|
self.decorationsAreDark = isFullscreen
|
||||||
if previousDark != self.decorationsAreDark {
|
if previousDark != self.decorationsAreDark {
|
||||||
if let snapshotView = self.topCornersNode.view.snapshotContentTree() {
|
if let snapshotView = self.topCornersNode.view.snapshotContentTree() {
|
||||||
snapshotView.frame = self.topCornersNode.frame
|
snapshotView.frame = self.topCornersNode.bounds
|
||||||
self.topPanelNode.view.addSubview(snapshotView)
|
self.topCornersNode.view.addSubview(snapshotView)
|
||||||
|
|
||||||
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||||
snapshotView?.removeFromSuperview()
|
snapshotView?.removeFromSuperview()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
self.topCornersNode.image = decorationCornersImage(top: true, bottom: false, dark: isFullscreen)
|
self.topCornersNode.image = decorationTopCornersImage(dark: isFullscreen)
|
||||||
|
|
||||||
if let snapshotView = self.bottomCornersNode.view.snapshotContentTree() {
|
if let snapshotView = self.bottomCornersNode.view.snapshotContentTree() {
|
||||||
snapshotView.frame = self.bottomCornersNode.bounds
|
snapshotView.frame = self.bottomCornersNode.bounds
|
||||||
@ -3470,8 +3580,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
snapshotView?.removeFromSuperview()
|
snapshotView?.removeFromSuperview()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
self.bottomCornersNode.image = decorationCornersImage(top: false, bottom: true, dark: isFullscreen)
|
self.bottomCornersNode.image = decorationBottomCornersImage(dark: isFullscreen)
|
||||||
|
|
||||||
|
|
||||||
if let gridNode = gridNode {
|
if let gridNode = gridNode {
|
||||||
if let snapshotView = gridNode.cornersNode.view.snapshotContentTree() {
|
if let snapshotView = gridNode.cornersNode.view.snapshotContentTree() {
|
||||||
@ -3492,10 +3601,9 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.closeButton.setContent(.image(closeButtonImage(dark: isFullscreen)), animated: transition.isAnimated)
|
self.closeButton.setContent(.image(closeButtonImage(dark: isFullscreen)), animated: transition.isAnimated)
|
||||||
|
self.optionsButton.setContent(.more(optionsCircleImage(dark: isFullscreen)), animated: transition.isAnimated)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.optionsButton.setContent(.more(optionsCircleImage(dark: isFullscreen)), animated: transition.isAnimated)
|
|
||||||
|
|
||||||
self.updateTitle(transition: transition)
|
self.updateTitle(transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3638,21 +3746,17 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
transition.updateTransformScale(node: self.cameraButton, scale: hasCameraButton ? 1.0 : 0.0)
|
transition.updateTransformScale(node: self.cameraButton, scale: hasCameraButton ? 1.0 : 0.0)
|
||||||
|
|
||||||
transition.updateAlpha(node: self.audioButton, alpha: hasVideo ? 0.0 : 1.0)
|
transition.updateAlpha(node: self.audioButton, alpha: hasCameraButton ? 0.0 : 1.0)
|
||||||
transition.updateTransformScale(node: self.audioButton, scale: hasVideo ? 0.0 : 1.0)
|
transition.updateTransformScale(node: self.audioButton, scale: hasCameraButton ? 0.0 : 1.0)
|
||||||
|
|
||||||
self.audioButton.update(size: audioButtonSize, content: CallControllerButtonItemNode.Content(appearance: soundAppearance, image: soundImage, isEnabled: isSoundEnabled), text: soundTitle, transition: transition)
|
self.audioButton.update(size: audioButtonSize, content: CallControllerButtonItemNode.Content(appearance: soundAppearance, image: soundImage, isEnabled: isSoundEnabled), text: soundTitle, transition: transition)
|
||||||
self.audioButton.isUserInteractionEnabled = isSoundEnabled
|
self.audioButton.isUserInteractionEnabled = isSoundEnabled
|
||||||
|
|
||||||
self.leaveButton.update(size: sideButtonSize, content: CallControllerButtonItemNode.Content(appearance: .color(.custom(0xff3b30, 0.3)), image: .cancel), text: self.presentationData.strings.VoiceChat_Leave, transition: .immediate)
|
self.leaveButton.update(size: sideButtonSize, content: CallControllerButtonItemNode.Content(appearance: .color(.custom(0xff3b30, 0.3)), image: .cancel), text: self.presentationData.strings.VoiceChat_Leave, transition: .immediate)
|
||||||
|
|
||||||
transition.updateAlpha(node: self.cameraButton.textNode, alpha: hasCameraButton ? buttonsTitleAlpha : 0.0)
|
transition.updateAlpha(node: self.cameraButton.textNode, alpha: buttonsTitleAlpha)
|
||||||
transition.updateAlpha(node: self.switchCameraButton.textNode, alpha: buttonsTitleAlpha)
|
transition.updateAlpha(node: self.switchCameraButton.textNode, alpha: buttonsTitleAlpha)
|
||||||
var audioButtonTransition = transition
|
transition.updateAlpha(node: self.audioButton.textNode, alpha: buttonsTitleAlpha)
|
||||||
if hasCameraButton, transition.isAnimated {
|
|
||||||
audioButtonTransition = .animated(duration: 0.15, curve: .easeInOut)
|
|
||||||
}
|
|
||||||
transition.updateAlpha(node: self.audioButton.textNode, alpha: hasCameraButton ? 0.0 : buttonsTitleAlpha)
|
|
||||||
transition.updateAlpha(node: self.leaveButton.textNode, alpha: buttonsTitleAlpha)
|
transition.updateAlpha(node: self.leaveButton.textNode, alpha: buttonsTitleAlpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3727,7 +3831,6 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
let bottomPanelHeight = self.effectiveBottomAreaHeight + layout.intrinsicInsets.bottom
|
let bottomPanelHeight = self.effectiveBottomAreaHeight + layout.intrinsicInsets.bottom
|
||||||
var listTopInset = layoutTopInset + topPanelHeight
|
var listTopInset = layoutTopInset + topPanelHeight
|
||||||
let topCornersY = topPanelHeight
|
|
||||||
if isLandscape {
|
if isLandscape {
|
||||||
listTopInset = topPanelHeight
|
listTopInset = topPanelHeight
|
||||||
}
|
}
|
||||||
@ -3746,6 +3849,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
topInset = listSize.height - 46.0 - floor(56.0 * 3.5) - bottomGradientHeight
|
topInset = listSize.height - 46.0 - floor(56.0 * 3.5) - bottomGradientHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transition.updateFrameAsPositionAndBounds(node: self.listContainer, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - contentWidth) / 2.0), y: listTopInset + topInset), size: listSize))
|
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - contentWidth) / 2.0), y: listTopInset + topInset), size: listSize))
|
||||||
|
|
||||||
listInsets.bottom = bottomGradientHeight
|
listInsets.bottom = bottomGradientHeight
|
||||||
@ -3759,6 +3863,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
let fullscreenListTransform: CATransform3D
|
let fullscreenListTransform: CATransform3D
|
||||||
let fullscreenListInset: CGFloat = 14.0
|
let fullscreenListInset: CGFloat = 14.0
|
||||||
let fullscreenListUpdateSizeAndInsets: ListViewUpdateSizeAndInsets
|
let fullscreenListUpdateSizeAndInsets: ListViewUpdateSizeAndInsets
|
||||||
|
let fullscreenListContainerFrame: CGRect
|
||||||
if isLandscape {
|
if isLandscape {
|
||||||
fullscreenListWidth = layout.size.height
|
fullscreenListWidth = layout.size.height
|
||||||
fullscreenListPosition = CGPoint(
|
fullscreenListPosition = CGPoint(
|
||||||
@ -3767,17 +3872,23 @@ public final class VoiceChatController: ViewController {
|
|||||||
)
|
)
|
||||||
fullscreenListTransform = CATransform3DIdentity
|
fullscreenListTransform = CATransform3DIdentity
|
||||||
fullscreenListUpdateSizeAndInsets = ListViewUpdateSizeAndInsets(size: CGSize(width: fullscreenListHeight, height: layout.size.height), insets: UIEdgeInsets(top: fullscreenListInset, left: 0.0, bottom: fullscreenListInset, right: 0.0), duration: duration, curve: curve)
|
fullscreenListUpdateSizeAndInsets = ListViewUpdateSizeAndInsets(size: CGSize(width: fullscreenListHeight, height: layout.size.height), insets: UIEdgeInsets(top: fullscreenListInset, left: 0.0, bottom: fullscreenListInset, right: 0.0), duration: duration, curve: curve)
|
||||||
|
fullscreenListContainerFrame = CGRect(x: layout.size.width - min(self.effectiveBottomAreaHeight, fullscreenBottomAreaHeight) - layout.safeInsets.right - fullscreenListHeight, y: layout.size.height / 2.0, width: layout.size.width, height: fullscreenListHeight)
|
||||||
} else {
|
} else {
|
||||||
fullscreenListWidth = layout.size.width
|
fullscreenListWidth = layout.size.width
|
||||||
fullscreenListPosition = CGPoint(
|
fullscreenListPosition = CGPoint(
|
||||||
x: layout.safeInsets.left + layout.size.width / 2.0,
|
x: layout.size.width / 2.0,
|
||||||
y: layout.size.height - min(bottomPanelHeight, fullscreenBottomAreaHeight + layout.intrinsicInsets.bottom) - fullscreenListHeight / 2.0 + 4.0
|
y: layout.size.height - min(bottomPanelHeight, fullscreenBottomAreaHeight + layout.intrinsicInsets.bottom) - fullscreenListHeight / 2.0 + 4.0
|
||||||
)
|
)
|
||||||
fullscreenListTransform = CATransform3DMakeRotation(-CGFloat(CGFloat.pi / 2.0), 0.0, 0.0, 1.0)
|
fullscreenListTransform = CATransform3DMakeRotation(-CGFloat(CGFloat.pi / 2.0), 0.0, 0.0, 1.0)
|
||||||
fullscreenListUpdateSizeAndInsets = ListViewUpdateSizeAndInsets(size: CGSize(width: fullscreenListHeight, height: layout.size.width), insets: UIEdgeInsets(top: fullscreenListInset + layout.safeInsets.left, left: 0.0, bottom: fullscreenListInset + layout.safeInsets.left, right: 0.0), duration: duration, curve: curve)
|
fullscreenListUpdateSizeAndInsets = ListViewUpdateSizeAndInsets(size: CGSize(width: fullscreenListHeight, height: layout.size.width), insets: UIEdgeInsets(top: fullscreenListInset + layout.safeInsets.left, left: 0.0, bottom: fullscreenListInset + layout.safeInsets.left, right: 0.0), duration: duration, curve: curve)
|
||||||
|
fullscreenListContainerFrame = CGRect(x: 0.0, y: layout.size.height - min(bottomPanelHeight, fullscreenBottomAreaHeight + layout.intrinsicInsets.bottom) - fullscreenListHeight + 4.0, width: layout.size.width, height: fullscreenListHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.fullscreenListContainer, frame: fullscreenListContainerFrame)
|
||||||
|
|
||||||
self.fullscreenListNode.bounds = CGRect(x: 0.0, y: 0.0, width: fullscreenListHeight, height: fullscreenListWidth)
|
self.fullscreenListNode.bounds = CGRect(x: 0.0, y: 0.0, width: fullscreenListHeight, height: fullscreenListWidth)
|
||||||
transition.updatePosition(node: self.fullscreenListNode, position: fullscreenListPosition)
|
transition.updatePosition(node: self.fullscreenListNode, position: CGPoint(x: fullscreenListContainerFrame.width / 2.0, y: fullscreenListContainerFrame.height / 2.0))
|
||||||
|
|
||||||
self.fullscreenListNode.transform = fullscreenListTransform
|
self.fullscreenListNode.transform = fullscreenListTransform
|
||||||
self.fullscreenListNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: fullscreenListUpdateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
self.fullscreenListNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: fullscreenListUpdateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||||
|
|
||||||
@ -3789,9 +3900,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
childrenLayout.intrinsicInsets = childrenInsets
|
childrenLayout.intrinsicInsets = childrenInsets
|
||||||
self.controller?.presentationContext.containerLayoutUpdated(childrenLayout, transition: transition)
|
self.controller?.presentationContext.containerLayoutUpdated(childrenLayout, transition: transition)
|
||||||
|
|
||||||
transition.updateFrame(node: self.topCornersNode, frame: CGRect(origin: CGPoint(x: sideInset + floorToScreenPixels((size.width - contentWidth) / 2.0), y: topCornersY), size: CGSize(width: contentWidth - sideInset * 2.0, height: 50.0)))
|
|
||||||
|
|
||||||
var bottomPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - bottomPanelHeight), size: CGSize(width: size.width, height: bottomPanelHeight))
|
var bottomPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - bottomPanelHeight), size: CGSize(width: size.width, height: bottomPanelHeight))
|
||||||
let bottomPanelCoverHeight = bottomAreaHeight + layout.intrinsicInsets.bottom
|
let bottomPanelCoverHeight = bottomAreaHeight + layout.intrinsicInsets.bottom
|
||||||
if isLandscape {
|
if isLandscape {
|
||||||
@ -3926,7 +4035,6 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.actionButton.update(size: centralButtonSize, buttonSize: CGSize(width: 112.0, height: 112.0), state: actionButtonState, title: actionButtonTitle, subtitle: actionButtonSubtitle, dark: self.isFullscreen, small: smallButtons, animated: true)
|
self.actionButton.update(size: centralButtonSize, buttonSize: CGSize(width: 112.0, height: 112.0), state: actionButtonState, title: actionButtonTitle, subtitle: actionButtonSubtitle, dark: self.isFullscreen, small: smallButtons, animated: true)
|
||||||
|
|
||||||
var hasCameraButton = self.callState?.isVideoEnabled ?? false
|
var hasCameraButton = self.callState?.isVideoEnabled ?? false
|
||||||
|
|
||||||
switch actionButtonState {
|
switch actionButtonState {
|
||||||
case let .active(state):
|
case let .active(state):
|
||||||
switch state {
|
switch state {
|
||||||
@ -3942,6 +4050,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
case .scheduled, .button:
|
case .scheduled, .button:
|
||||||
hasCameraButton = false
|
hasCameraButton = false
|
||||||
}
|
}
|
||||||
|
let hasVideo = hasCameraButton && self.call.hasVideo
|
||||||
|
|
||||||
let upperButtonDistance: CGFloat = 12.0
|
let upperButtonDistance: CGFloat = 12.0
|
||||||
let firstButtonFrame: CGRect
|
let firstButtonFrame: CGRect
|
||||||
@ -3950,7 +4059,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
let forthButtonFrame: CGRect
|
let forthButtonFrame: CGRect
|
||||||
|
|
||||||
let leftButtonFrame: CGRect
|
let leftButtonFrame: CGRect
|
||||||
if self.isScheduled || !hasCameraButton {
|
if self.isScheduled || !hasVideo {
|
||||||
leftButtonFrame = CGRect(origin: CGPoint(x: sideButtonOrigin, y: floor((self.effectiveBottomAreaHeight - sideButtonSize.height) / 2.0)), size: sideButtonSize)
|
leftButtonFrame = CGRect(origin: CGPoint(x: sideButtonOrigin, y: floor((self.effectiveBottomAreaHeight - sideButtonSize.height) / 2.0)), size: sideButtonSize)
|
||||||
} else {
|
} else {
|
||||||
leftButtonFrame = CGRect(origin: CGPoint(x: sideButtonOrigin, y: floor((self.effectiveBottomAreaHeight - sideButtonSize.height - upperButtonDistance - cameraButtonSize.height) / 2.0) + upperButtonDistance + cameraButtonSize.height), size: sideButtonSize)
|
leftButtonFrame = CGRect(origin: CGPoint(x: sideButtonOrigin, y: floor((self.effectiveBottomAreaHeight - sideButtonSize.height - upperButtonDistance - cameraButtonSize.height) / 2.0) + upperButtonDistance + cameraButtonSize.height), size: sideButtonSize)
|
||||||
@ -3962,7 +4071,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
if isLandscape {
|
if isLandscape {
|
||||||
let sideInset: CGFloat
|
let sideInset: CGFloat
|
||||||
let buttonsCount: Int
|
let buttonsCount: Int
|
||||||
if hasCameraButton {
|
if hasVideo {
|
||||||
sideInset = 26.0
|
sideInset = 26.0
|
||||||
buttonsCount = 4
|
buttonsCount = 4
|
||||||
} else {
|
} else {
|
||||||
@ -3994,7 +4103,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
if isLandscape {
|
if isLandscape {
|
||||||
let sideInset: CGFloat
|
let sideInset: CGFloat
|
||||||
let buttonsCount: Int
|
let buttonsCount: Int
|
||||||
if hasCameraButton {
|
if hasVideo {
|
||||||
sideInset = 26.0
|
sideInset = 26.0
|
||||||
buttonsCount = 4
|
buttonsCount = 4
|
||||||
} else {
|
} else {
|
||||||
@ -4007,7 +4116,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
let thirdButtonPreFrame = CGRect(origin: CGPoint(x: x, y: sideInset + sideButtonSize.height + spacing), size: sideButtonSize)
|
let thirdButtonPreFrame = CGRect(origin: CGPoint(x: x, y: sideInset + sideButtonSize.height + spacing), size: sideButtonSize)
|
||||||
thirdButtonFrame = CGRect(origin: CGPoint(x: floor(thirdButtonPreFrame.midX - centralButtonSize.width / 2.0), y: floor(thirdButtonPreFrame.midY - centralButtonSize.height / 2.0)), size: centralButtonSize)
|
thirdButtonFrame = CGRect(origin: CGPoint(x: floor(thirdButtonPreFrame.midX - centralButtonSize.width / 2.0), y: floor(thirdButtonPreFrame.midY - centralButtonSize.height / 2.0)), size: centralButtonSize)
|
||||||
secondButtonFrame = CGRect(origin: CGPoint(x: x, y: thirdButtonPreFrame.maxY + spacing), size: sideButtonSize)
|
secondButtonFrame = CGRect(origin: CGPoint(x: x, y: thirdButtonPreFrame.maxY + spacing), size: sideButtonSize)
|
||||||
if hasCameraButton {
|
if hasVideo {
|
||||||
firstButtonFrame = CGRect(origin: CGPoint(x: x, y: layout.size.height - sideInset - sideButtonSize.height), size: sideButtonSize)
|
firstButtonFrame = CGRect(origin: CGPoint(x: x, y: layout.size.height - sideInset - sideButtonSize.height), size: sideButtonSize)
|
||||||
} else {
|
} else {
|
||||||
firstButtonFrame = secondButtonFrame
|
firstButtonFrame = secondButtonFrame
|
||||||
@ -4015,7 +4124,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
} else {
|
} else {
|
||||||
let sideInset: CGFloat
|
let sideInset: CGFloat
|
||||||
let buttonsCount: Int
|
let buttonsCount: Int
|
||||||
if hasCameraButton {
|
if hasVideo {
|
||||||
sideInset = 26.0
|
sideInset = 26.0
|
||||||
buttonsCount = 4
|
buttonsCount = 4
|
||||||
} else {
|
} else {
|
||||||
@ -4024,7 +4133,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
let spacing = floor((layout.size.width - sideInset * 2.0 - sideButtonSize.width * CGFloat(buttonsCount)) / (CGFloat(buttonsCount - 1)))
|
let spacing = floor((layout.size.width - sideInset * 2.0 - sideButtonSize.width * CGFloat(buttonsCount)) / (CGFloat(buttonsCount - 1)))
|
||||||
let y = controlsHidden ? self.effectiveBottomAreaHeight + layout.intrinsicInsets.bottom + 30.0: floor((self.effectiveBottomAreaHeight - sideButtonSize.height) / 2.0)
|
let y = controlsHidden ? self.effectiveBottomAreaHeight + layout.intrinsicInsets.bottom + 30.0: floor((self.effectiveBottomAreaHeight - sideButtonSize.height) / 2.0)
|
||||||
if hasCameraButton {
|
if hasVideo {
|
||||||
firstButtonFrame = CGRect(origin: CGPoint(x: sideInset, y: y), size: sideButtonSize)
|
firstButtonFrame = CGRect(origin: CGPoint(x: sideInset, y: y), size: sideButtonSize)
|
||||||
secondButtonFrame = CGRect(origin: CGPoint(x: firstButtonFrame.maxX + spacing, y: y), size: sideButtonSize)
|
secondButtonFrame = CGRect(origin: CGPoint(x: firstButtonFrame.maxX + spacing, y: y), size: sideButtonSize)
|
||||||
} else {
|
} else {
|
||||||
@ -4063,15 +4172,9 @@ public final class VoiceChatController: ViewController {
|
|||||||
transition.updateFrameAsPositionAndBounds(node: self.switchCameraButton, frame: firstButtonFrame)
|
transition.updateFrameAsPositionAndBounds(node: self.switchCameraButton, frame: firstButtonFrame)
|
||||||
|
|
||||||
if !self.animatingButtonsSwap || transition.isAnimated {
|
if !self.animatingButtonsSwap || transition.isAnimated {
|
||||||
if hasCameraButton {
|
transition.updateFrameAsPositionAndBounds(node: self.audioButton, frame: secondButtonFrame, completion: { [weak self] _ in
|
||||||
transition.updateFrameAsPositionAndBounds(node: self.audioButton, frame: firstButtonFrame, completion: { [weak self] _ in
|
self?.animatingButtonsSwap = false
|
||||||
self?.animatingButtonsSwap = false
|
})
|
||||||
})
|
|
||||||
} else {
|
|
||||||
transition.updateFrameAsPositionAndBounds(node: self.audioButton, frame: secondButtonFrame, completion: { [weak self] _ in
|
|
||||||
self?.animatingButtonsSwap = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
transition.updateFrameAsPositionAndBounds(node: self.cameraButton, frame: secondButtonFrame)
|
transition.updateFrameAsPositionAndBounds(node: self.cameraButton, frame: secondButtonFrame)
|
||||||
}
|
}
|
||||||
transition.updateFrameAsPositionAndBounds(node: self.leaveButton, frame: forthButtonFrame)
|
transition.updateFrameAsPositionAndBounds(node: self.leaveButton, frame: forthButtonFrame)
|
||||||
@ -4374,11 +4477,15 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
var isTile = false
|
var isTile = false
|
||||||
if let interaction = self.itemInteraction {
|
if let interaction = self.itemInteraction {
|
||||||
if let videoEndpointId = member.presentationEndpointId {
|
if let videoEndpointId = member.presentationEndpointId, !self.timeoutedEndpointIds.contains(videoEndpointId) {
|
||||||
if !self.videoOrder.contains(videoEndpointId) {
|
if !self.videoOrder.contains(videoEndpointId) {
|
||||||
self.videoOrder.append(videoEndpointId)
|
if peerEntry.isMyPeer {
|
||||||
|
self.videoOrder.insert(videoEndpointId, at: 0)
|
||||||
|
} else {
|
||||||
|
self.videoOrder.append(videoEndpointId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let tileItem = ListEntry.peer(peerEntry, 0).tileItem(context: self.context, presentationData: self.presentationData, interaction: interaction, videoEndpointId: videoEndpointId, videoReady: self.readyVideoNodes.contains(videoEndpointId)) {
|
if let tileItem = ListEntry.peer(peerEntry, 0).tileItem(context: self.context, presentationData: self.presentationData, interaction: interaction, videoEndpointId: videoEndpointId, videoReady: self.readyVideoEndpointIds.contains(videoEndpointId)) {
|
||||||
isTile = true
|
isTile = true
|
||||||
tileByVideoEndpoint[videoEndpointId] = tileItem
|
tileByVideoEndpoint[videoEndpointId] = tileItem
|
||||||
}
|
}
|
||||||
@ -4386,11 +4493,15 @@ public final class VoiceChatController: ViewController {
|
|||||||
latestWideVideo = videoEndpointId
|
latestWideVideo = videoEndpointId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let videoEndpointId = member.videoEndpointId {
|
if let videoEndpointId = member.videoEndpointId, !self.timeoutedEndpointIds.contains(videoEndpointId) {
|
||||||
if !self.videoOrder.contains(videoEndpointId) {
|
if !self.videoOrder.contains(videoEndpointId) {
|
||||||
self.videoOrder.append(videoEndpointId)
|
if peerEntry.isMyPeer {
|
||||||
|
self.videoOrder.insert(videoEndpointId, at: 0)
|
||||||
|
} else {
|
||||||
|
self.videoOrder.append(videoEndpointId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let tileItem = ListEntry.peer(peerEntry, 0).tileItem(context: self.context, presentationData: self.presentationData, interaction: interaction, videoEndpointId: videoEndpointId, videoReady: self.readyVideoNodes.contains(videoEndpointId)) {
|
if let tileItem = ListEntry.peer(peerEntry, 0).tileItem(context: self.context, presentationData: self.presentationData, interaction: interaction, videoEndpointId: videoEndpointId, videoReady: self.readyVideoEndpointIds.contains(videoEndpointId)) {
|
||||||
isTile = true
|
isTile = true
|
||||||
tileByVideoEndpoint[videoEndpointId] = tileItem
|
tileByVideoEndpoint[videoEndpointId] = tileItem
|
||||||
}
|
}
|
||||||
@ -4486,7 +4597,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
self.requestedVideoChannels = requestedVideoChannels
|
self.requestedVideoChannels = requestedVideoChannels
|
||||||
|
|
||||||
guard self.didSetDataReady && !self.isPanning else {
|
guard self.didSetDataReady && !self.isPanning && !self.animatingExpansion else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4598,6 +4709,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
for channel in channels {
|
for channel in channels {
|
||||||
validSources.insert(channel.endpointId)
|
validSources.insert(channel.endpointId)
|
||||||
|
|
||||||
|
|
||||||
if !self.requestedVideoSources.contains(channel.endpointId) {
|
if !self.requestedVideoSources.contains(channel.endpointId) {
|
||||||
self.requestedVideoSources.insert(channel.endpointId)
|
self.requestedVideoSources.insert(channel.endpointId)
|
||||||
self.call.makeIncomingVideoView(endpointId: channel.endpointId, requestClone: true, completion: { [weak self] videoView, backdropVideoView in
|
self.call.makeIncomingVideoView(endpointId: channel.endpointId, requestClone: true, completion: { [weak self] videoView, backdropVideoView in
|
||||||
@ -4610,31 +4722,42 @@ public final class VoiceChatController: ViewController {
|
|||||||
strongSelf.readyVideoDisposables.set((videoNode.ready
|
strongSelf.readyVideoDisposables.set((videoNode.ready
|
||||||
|> filter { $0 }
|
|> filter { $0 }
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|
|> timeout(10.0, queue: Queue.mainQueue(), alternate: .single(false))
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
).start(next: { [weak self, weak videoNode] _ in
|
).start(next: { [weak self, weak videoNode] ready in
|
||||||
if let strongSelf = self, let videoNode = videoNode {
|
if let strongSelf = self, let videoNode = videoNode {
|
||||||
print("video ready \(channel.endpointId)")
|
|
||||||
Queue.mainQueue().after(0.1) {
|
Queue.mainQueue().after(0.1) {
|
||||||
strongSelf.readyVideoNodes.insert(channel.endpointId)
|
if ready {
|
||||||
if videoNode.aspectRatio <= 0.77 {
|
strongSelf.readyVideoEndpointIds.insert(channel.endpointId)
|
||||||
strongSelf.wideVideoNodes.insert(channel.endpointId)
|
strongSelf.timeoutedEndpointIds.remove(channel.endpointId)
|
||||||
}
|
if videoNode.aspectRatio <= 0.77 {
|
||||||
strongSelf.updateMembers()
|
strongSelf.wideVideoNodes.insert(channel.endpointId)
|
||||||
|
} else {
|
||||||
|
strongSelf.wideVideoNodes.remove(channel.endpointId)
|
||||||
|
}
|
||||||
|
strongSelf.updateMembers()
|
||||||
|
|
||||||
if let interaction = strongSelf.itemInteraction {
|
if let interaction = strongSelf.itemInteraction {
|
||||||
loop: for i in 0 ..< strongSelf.currentFullscreenEntries.count {
|
loop: for i in 0 ..< strongSelf.currentFullscreenEntries.count {
|
||||||
let entry = strongSelf.currentFullscreenEntries[i]
|
let entry = strongSelf.currentFullscreenEntries[i]
|
||||||
switch entry {
|
switch entry {
|
||||||
case let .peer(peerEntry, _):
|
case let .peer(peerEntry, _):
|
||||||
if peerEntry.effectiveVideoEndpointId == channel.endpointId {
|
if peerEntry.effectiveVideoEndpointId == channel.endpointId {
|
||||||
let presentationData = strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme)
|
let presentationData = strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme)
|
||||||
strongSelf.fullscreenListNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [ListViewUpdateItem(index: i, previousIndex: i, item: entry.fullscreenItem(context: strongSelf.context, presentationData: presentationData, interaction: interaction), directionHint: nil)], options: [.Synchronous], updateOpaqueState: nil)
|
strongSelf.fullscreenListNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [ListViewUpdateItem(index: i, previousIndex: i, item: entry.fullscreenItem(context: strongSelf.context, presentationData: presentationData, interaction: interaction), directionHint: nil)], options: [.Synchronous], updateOpaqueState: nil)
|
||||||
break loop
|
break loop
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
strongSelf.timeoutedEndpointIds.insert(channel.endpointId)
|
||||||
|
strongSelf.readyVideoEndpointIds.remove(channel.endpointId)
|
||||||
|
strongSelf.wideVideoNodes.remove(channel.endpointId)
|
||||||
|
|
||||||
|
strongSelf.updateMembers()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4663,7 +4786,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
if !validSources.contains(videoEndpointId) {
|
if !validSources.contains(videoEndpointId) {
|
||||||
self.videoNodes[videoEndpointId] = nil
|
self.videoNodes[videoEndpointId] = nil
|
||||||
self.videoOrder.removeAll(where: { $0 == videoEndpointId })
|
self.videoOrder.removeAll(where: { $0 == videoEndpointId })
|
||||||
self.readyVideoNodes.remove(videoEndpointId)
|
self.readyVideoEndpointIds.remove(videoEndpointId)
|
||||||
self.readyVideoDisposables.set(nil, forKey: videoEndpointId)
|
self.readyVideoDisposables.set(nil, forKey: videoEndpointId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4730,10 +4853,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
if gestureRecognizer is UILongPressGestureRecognizer {
|
if gestureRecognizer is UILongPressGestureRecognizer {
|
||||||
return !self.isScheduling
|
return !self.isScheduling
|
||||||
} else if gestureRecognizer is DirectionalPanGestureRecognizer {
|
} else if gestureRecognizer is DirectionalPanGestureRecognizer {
|
||||||
if let (layout, _) = self.validLayout, layout.size.width > layout.size.height, case .compact = layout.metrics.widthClass {
|
if self.mainStageNode.animating {
|
||||||
return false
|
|
||||||
}
|
|
||||||
if self.isLandscape {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4779,8 +4899,12 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.fullscreenListNode.alpha = 0.0
|
self.fullscreenListNode.alpha = 0.0
|
||||||
self.fullscreenListNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, completion: { [weak self] finished in
|
self.fullscreenListNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, completion: { [weak self] finished in
|
||||||
self?.attachTileVideos()
|
self?.attachTileVideos()
|
||||||
|
|
||||||
|
self?.fullscreenListContainer.subnodeTransform = CATransform3DIdentity
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.listContainer.transform = CATransform3DMakeScale(0.86, 0.86, 1.0)
|
||||||
|
|
||||||
self.contentContainer.insertSubnode(self.mainStageContainerNode, aboveSubnode: self.bottomPanelNode)
|
self.contentContainer.insertSubnode(self.mainStageContainerNode, aboveSubnode: self.bottomPanelNode)
|
||||||
}
|
}
|
||||||
case .changed:
|
case .changed:
|
||||||
@ -4838,6 +4962,8 @@ public final class VoiceChatController: ViewController {
|
|||||||
var backgroundFrame = self.mainStageNode.frame
|
var backgroundFrame = self.mainStageNode.frame
|
||||||
backgroundFrame.origin.y += -translation
|
backgroundFrame.origin.y += -translation
|
||||||
self.mainStageBackgroundNode.frame = backgroundFrame
|
self.mainStageBackgroundNode.frame = backgroundFrame
|
||||||
|
|
||||||
|
self.fullscreenListContainer.subnodeTransform = CATransform3DMakeTranslation(0.0, translation, 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (layout, navigationHeight) = self.validLayout {
|
if let (layout, navigationHeight) = self.validLayout {
|
||||||
@ -4882,6 +5008,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
if case .fullscreen = self.displayMode {
|
if case .fullscreen = self.displayMode {
|
||||||
self.panGestureArguments = nil
|
self.panGestureArguments = nil
|
||||||
|
self.fullscreenListContainer.subnodeTransform = CATransform3DIdentity
|
||||||
if abs(translation.y) > 100.0 || abs(velocity.y) > 300.0 {
|
if abs(translation.y) > 100.0 || abs(velocity.y) > 300.0 {
|
||||||
self.currentForcedSpeaker = nil
|
self.currentForcedSpeaker = nil
|
||||||
self.updateDisplayMode(.modal(isExpanded: true, isFilled: true), fromPan: true)
|
self.updateDisplayMode(.modal(isExpanded: true, isFilled: true), fromPan: true)
|
||||||
@ -4892,7 +5019,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.mainStageBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, completion: { [weak self] _ in
|
self.mainStageBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, completion: { [weak self] _ in
|
||||||
self?.attachFullscreenVideos()
|
self?.attachFullscreenVideos()
|
||||||
})
|
})
|
||||||
self.mainStageNode.setControlsHidden(false, animated: true)
|
self.mainStageNode.setControlsHidden(false, animated: true, delay: 0.15)
|
||||||
|
|
||||||
self.fullscreenListNode.alpha = 1.0
|
self.fullscreenListNode.alpha = 1.0
|
||||||
self.fullscreenListNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: 0.15)
|
self.fullscreenListNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: 0.15)
|
||||||
@ -4903,6 +5030,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
self.mainStageContainerNode.bounds = bounds
|
self.mainStageContainerNode.bounds = bounds
|
||||||
self.mainStageContainerNode.layer.animateBounds(from: previousBounds, to: self.mainStageContainerNode.bounds, duration: 0.2, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak self] _ in
|
self.mainStageContainerNode.layer.animateBounds(from: previousBounds, to: self.mainStageContainerNode.bounds, duration: 0.2, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak self] _ in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
strongSelf.listContainer.transform = CATransform3DIdentity
|
||||||
strongSelf.contentContainer.insertSubnode(strongSelf.mainStageContainerNode, belowSubnode: strongSelf.transitionContainerNode)
|
strongSelf.contentContainer.insertSubnode(strongSelf.mainStageContainerNode, belowSubnode: strongSelf.transitionContainerNode)
|
||||||
strongSelf.updateMembers()
|
strongSelf.updateMembers()
|
||||||
}
|
}
|
||||||
@ -5003,12 +5131,13 @@ public final class VoiceChatController: ViewController {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if case .fullscreen = self.displayMode {
|
if case .fullscreen = self.displayMode {
|
||||||
|
self.fullscreenListContainer.subnodeTransform = CATransform3DIdentity
|
||||||
self.isPanning = false
|
self.isPanning = false
|
||||||
self.mainStageBackgroundNode.alpha = 1.0
|
self.mainStageBackgroundNode.alpha = 1.0
|
||||||
self.mainStageBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, completion: { [weak self] _ in
|
self.mainStageBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, completion: { [weak self] _ in
|
||||||
self?.attachFullscreenVideos()
|
self?.attachFullscreenVideos()
|
||||||
})
|
})
|
||||||
self.mainStageNode.setControlsHidden(false, animated: true)
|
self.mainStageNode.setControlsHidden(false, animated: true, delay: 0.15)
|
||||||
|
|
||||||
self.fullscreenListNode.alpha = 1.0
|
self.fullscreenListNode.alpha = 1.0
|
||||||
self.fullscreenListNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: 0.15)
|
self.fullscreenListNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: 0.15)
|
||||||
@ -5021,6 +5150,8 @@ public final class VoiceChatController: ViewController {
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.contentContainer.insertSubnode(strongSelf.mainStageContainerNode, belowSubnode: strongSelf.transitionContainerNode)
|
strongSelf.contentContainer.insertSubnode(strongSelf.mainStageContainerNode, belowSubnode: strongSelf.transitionContainerNode)
|
||||||
strongSelf.updateMembers()
|
strongSelf.updateMembers()
|
||||||
|
|
||||||
|
strongSelf.listContainer.transform = CATransform3DIdentity
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -5420,7 +5551,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updateDisplayMode(_ displayMode: DisplayMode, fromPan: Bool = false) {
|
private func updateDisplayMode(_ displayMode: DisplayMode, fromPan: Bool = false) {
|
||||||
guard !self.animatingExpansion else {
|
guard !self.animatingExpansion && !self.mainStageNode.animating else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.updateMembers()
|
self.updateMembers()
|
||||||
@ -5441,42 +5572,16 @@ public final class VoiceChatController: ViewController {
|
|||||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.55, curve: .spring)
|
let transition: ContainedViewLayoutTransition = .animated(duration: 0.55, curve: .spring)
|
||||||
|
|
||||||
if case .modal = previousDisplayMode, case .fullscreen = self.displayMode {
|
if case .modal = previousDisplayMode, case .fullscreen = self.displayMode {
|
||||||
self.fullscreenListNode.isHidden = false
|
|
||||||
self.fullscreenListNode.alpha = 1.0
|
|
||||||
|
|
||||||
self.updateDecorationsLayout(transition: .immediate)
|
self.updateDecorationsLayout(transition: .immediate)
|
||||||
|
|
||||||
var minimalVisiblePeerid: (PeerId, CGPoint)?
|
|
||||||
var verticalItemNodes: [String: ASDisplayNode] = [:]
|
var verticalItemNodes: [String: ASDisplayNode] = [:]
|
||||||
self.listNode.forEachItemNode { itemNode in
|
self.listNode.forEachItemNode { itemNode in
|
||||||
if let itemNode = itemNode as? VoiceChatTilesGridItemNode {
|
if let itemNode = itemNode as? VoiceChatTilesGridItemNode {
|
||||||
for tileNode in itemNode.tileNodes {
|
for tileNode in itemNode.tileNodes {
|
||||||
let convertedFrame = tileNode.view.convert(tileNode.bounds, to: self.transitionContainerNode.view)
|
|
||||||
if let item = tileNode.item {
|
if let item = tileNode.item {
|
||||||
if let (_, point) = minimalVisiblePeerid {
|
|
||||||
if convertedFrame.minY >= 0.0 && (convertedFrame.minY < point.y || (convertedFrame.minY == point.y && convertedFrame.minX < point.x)) {
|
|
||||||
minimalVisiblePeerid = (item.peer.id, convertedFrame.origin)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if convertedFrame.minY >= 0.0 {
|
|
||||||
minimalVisiblePeerid = (item.peer.id, convertedFrame.origin)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
verticalItemNodes[String(item.peer.id.toInt64()) + "_" + item.videoEndpointId] = tileNode
|
verticalItemNodes[String(item.peer.id.toInt64()) + "_" + item.videoEndpointId] = tileNode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let itemNode = itemNode as? VoiceChatParticipantItemNode, let item = itemNode.item {
|
|
||||||
let convertedFrame = itemNode.view.convert(itemNode.bounds, to: self.transitionContainerNode.view)
|
|
||||||
if let (_, point) = minimalVisiblePeerid {
|
|
||||||
if convertedFrame.minY >= 0.0 && convertedFrame.minY < point.y {
|
|
||||||
minimalVisiblePeerid = (item.peer.id, convertedFrame.origin)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if convertedFrame.minY >= 0.0 {
|
|
||||||
minimalVisiblePeerid = (item.peer.id, convertedFrame.origin)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
verticalItemNodes[String(item.peer.id.toInt64()) + "_"] = itemNode
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5484,26 +5589,35 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
let completion = {
|
let completion = {
|
||||||
let effectiveSpeakerPeerId = self.effectiveSpeaker?.0
|
let effectiveSpeakerPeerId = self.effectiveSpeaker?.0
|
||||||
|
|
||||||
|
self.fullscreenListContainer.isHidden = false
|
||||||
|
self.fullscreenListNode.alpha = 0.0
|
||||||
|
|
||||||
|
let completion = {
|
||||||
|
self.attachFullscreenVideos()
|
||||||
|
|
||||||
|
self.fullscreenListNode.alpha = 1.0
|
||||||
|
self.fullscreenListNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||||
|
}
|
||||||
if let effectiveSpeakerPeerId = effectiveSpeakerPeerId, let otherItemNode = verticalItemNodes[String(effectiveSpeakerPeerId.toInt64()) + "_" + (self.effectiveSpeaker?.1 ?? "")] {
|
if let effectiveSpeakerPeerId = effectiveSpeakerPeerId, let otherItemNode = verticalItemNodes[String(effectiveSpeakerPeerId.toInt64()) + "_" + (self.effectiveSpeaker?.1 ?? "")] {
|
||||||
|
|
||||||
self.mainStageNode.alpha = 0.0
|
self.mainStageNode.alpha = 0.0
|
||||||
|
|
||||||
Queue.mainQueue().after(0.05) {
|
let transitionStartPosition = otherItemNode.view.convert(CGPoint(x: otherItemNode.frame.width / 2.0, y: otherItemNode.frame.height), to: self.fullscreenListContainer.view.superview)
|
||||||
self.mainStageNode.animateTransitionIn(from: otherItemNode, transition: transition)
|
self.fullscreenListContainer.layer.animatePosition(from: transitionStartPosition, to: self.fullscreenListContainer.position, duration: 0.55, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
self.mainStageNode.alpha = 1.0
|
|
||||||
|
self.mainStageNode.animateTransitionIn(from: otherItemNode, transition: transition)
|
||||||
self.mainStageBackgroundNode.alpha = 1.0
|
self.mainStageNode.alpha = 1.0
|
||||||
self.mainStageBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
|
||||||
}
|
self.mainStageBackgroundNode.alpha = 1.0
|
||||||
|
self.mainStageBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.13, completion: { _ in
|
||||||
|
completion()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
completion()
|
||||||
}
|
}
|
||||||
|
|
||||||
Queue.mainQueue().after(0.1) {
|
self.listContainer.layer.animateScale(from: 1.0, to: 0.86, duration: 0.55, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
self.fullscreenListNode.forEachItemNode { itemNode in
|
|
||||||
if let itemNode = itemNode as? VoiceChatFullscreenParticipantItemNode, let item = itemNode.item {
|
|
||||||
itemNode.animateTransitionIn(from: verticalItemNodes[String(item.peer.id.toInt64()) + "_" + (item.videoEndpointId ?? "")], containerNode: self.transitionContainerNode, transition: transition, animate: item.peer.id != effectiveSpeakerPeerId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.isLandscape {
|
if self.isLandscape {
|
||||||
self.transitionMaskTopFillLayer.opacity = 1.0
|
self.transitionMaskTopFillLayer.opacity = 1.0
|
||||||
@ -5564,32 +5678,36 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
self.transitionContainerNode.addSubnode(self.mainStageNode)
|
self.transitionContainerNode.addSubnode(self.mainStageNode)
|
||||||
|
|
||||||
|
self.listContainer.transform = CATransform3DIdentity
|
||||||
|
|
||||||
self.listNode.forEachItemNode { itemNode in
|
self.listNode.forEachItemNode { itemNode in
|
||||||
if let itemNode = itemNode as? VoiceChatTilesGridItemNode {
|
if let itemNode = itemNode as? VoiceChatTilesGridItemNode {
|
||||||
for tileNode in itemNode.tileNodes {
|
for tileNode in itemNode.tileNodes {
|
||||||
if let item = tileNode.item {
|
if let item = tileNode.item {
|
||||||
if let otherItemNode = fullscreenItemNodes[String(item.peer.id.toInt64()) + "_" + item.videoEndpointId] {
|
|
||||||
if !fromPan || item.peer.id == effectiveSpeakerPeerId {
|
|
||||||
tileNode.animateTransitionIn(from: otherItemNode, containerNode: self.transitionContainerNode, transition: transition, animate: item.peer.id != effectiveSpeakerPeerId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if item.peer.id == effectiveSpeakerPeerId, item.videoEndpointId == self.effectiveSpeaker?.1 {
|
if item.peer.id == effectiveSpeakerPeerId, item.videoEndpointId == self.effectiveSpeaker?.1 {
|
||||||
targetTileNode = tileNode
|
targetTileNode = tileNode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let itemNode = itemNode as? VoiceChatParticipantItemNode, let item = itemNode.item, let otherItemNode = fullscreenItemNodes[String(item.peer.id.toInt64()) + "_"] {
|
|
||||||
if !fromPan {
|
|
||||||
itemNode.animateTransitionIn(from: otherItemNode, containerNode: self.transitionContainerNode, transition: transition)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let transitionOffset = -self.mainStageContainerNode.bounds.minY
|
let transitionOffset = -self.mainStageContainerNode.bounds.minY
|
||||||
if transitionOffset.isZero {
|
if transitionOffset.isZero {
|
||||||
self.mainStageBackgroundNode.alpha = 0.0
|
if let targetTileNode = targetTileNode {
|
||||||
self.mainStageBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
|
let transitionTargetPosition = targetTileNode.view.convert(CGPoint(x: targetTileNode.frame.width / 2.0, y: targetTileNode.frame.height), to: self.fullscreenListContainer.view.superview)
|
||||||
|
self.fullscreenListContainer.layer.animatePosition(from: self.fullscreenListContainer.position, to: transitionTargetPosition, duration: 0.55, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.fullscreenListNode.alpha = 0.0
|
||||||
|
self.fullscreenListNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, completion: { [weak self] _ in
|
||||||
|
self?.fullscreenListContainer.isHidden = true
|
||||||
|
self?.fullscreenListNode.alpha = 1.0
|
||||||
|
self?.attachTileVideos()
|
||||||
|
|
||||||
|
self?.mainStageBackgroundNode.alpha = 0.0
|
||||||
|
self?.mainStageBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
self.mainStageNode.animateTransitionOut(to: targetTileNode, offset: transitionOffset, transition: transition, completion: { [weak self] in
|
self.mainStageNode.animateTransitionOut(to: targetTileNode, offset: transitionOffset, transition: transition, completion: { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -5598,7 +5716,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
strongSelf.effectiveSpeaker = nil
|
strongSelf.effectiveSpeaker = nil
|
||||||
strongSelf.mainStageNode.update(peer: nil, waitForFullSize: false)
|
strongSelf.mainStageNode.update(peer: nil, waitForFullSize: false)
|
||||||
strongSelf.mainStageNode.setControlsHidden(false, animated: false)
|
strongSelf.mainStageNode.setControlsHidden(false, animated: false)
|
||||||
strongSelf.fullscreenListNode.isHidden = true
|
strongSelf.fullscreenListContainer.isHidden = true
|
||||||
strongSelf.mainStageContainerNode.isHidden = true
|
strongSelf.mainStageContainerNode.isHidden = true
|
||||||
strongSelf.mainStageContainerNode.addSubnode(strongSelf.mainStageNode)
|
strongSelf.mainStageContainerNode.addSubnode(strongSelf.mainStageNode)
|
||||||
|
|
||||||
@ -5610,7 +5728,9 @@ public final class VoiceChatController: ViewController {
|
|||||||
|
|
||||||
strongSelf.isPanning = false
|
strongSelf.isPanning = false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.listContainer.layer.animateScale(from: 0.86, to: 1.0, duration: 0.55, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
|
||||||
self.transitionMaskTopFillLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15)
|
self.transitionMaskTopFillLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15)
|
||||||
if !transitionOffset.isZero {
|
if !transitionOffset.isZero {
|
||||||
self.transitionMaskBottomFillLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
|
self.transitionMaskBottomFillLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
|
||||||
|
@ -154,8 +154,6 @@ final class VoiceChatMainStageNode: ASDisplayNode {
|
|||||||
|
|
||||||
self.speakingContainerNode = ASDisplayNode()
|
self.speakingContainerNode = ASDisplayNode()
|
||||||
self.speakingContainerNode.alpha = 0.0
|
self.speakingContainerNode.alpha = 0.0
|
||||||
self.speakingContainerNode.cornerRadius = 19.0
|
|
||||||
self.speakingContainerNode.clipsToBounds = true
|
|
||||||
|
|
||||||
self.speakingAvatarNode = AvatarNode(font: avatarPlaceholderFont(size: 14.0))
|
self.speakingAvatarNode = AvatarNode(font: avatarPlaceholderFont(size: 14.0))
|
||||||
self.speakingTitleNode = ImmediateTextNode()
|
self.speakingTitleNode = ImmediateTextNode()
|
||||||
@ -242,6 +240,11 @@ final class VoiceChatMainStageNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let speakingEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
let speakingEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
||||||
|
speakingEffectView.layer.cornerRadius = 19.0
|
||||||
|
speakingEffectView.clipsToBounds = true
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
speakingEffectView.layer.cornerCurve = .continuous
|
||||||
|
}
|
||||||
self.speakingContainerNode.view.insertSubview(speakingEffectView, at: 0)
|
self.speakingContainerNode.view.insertSubview(speakingEffectView, at: 0)
|
||||||
self.speakingEffectView = speakingEffectView
|
self.speakingEffectView = speakingEffectView
|
||||||
|
|
||||||
@ -292,8 +295,13 @@ final class VoiceChatMainStageNode: ASDisplayNode {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.appeared = true
|
self.appeared = true
|
||||||
|
|
||||||
|
self.topFadeNode.alpha = 0.0
|
||||||
|
self.titleNode.alpha = 0.0
|
||||||
|
self.microphoneNode.alpha = 0.0
|
||||||
|
self.headerNode.alpha = 0.0
|
||||||
|
|
||||||
let alphaTransition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
|
let alphaTransition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .easeInOut)
|
||||||
alphaTransition.updateAlpha(node: self.backgroundNode, alpha: 1.0)
|
alphaTransition.updateAlpha(node: self.backgroundNode, alpha: 1.0)
|
||||||
alphaTransition.updateAlpha(node: self.topFadeNode, alpha: 1.0)
|
alphaTransition.updateAlpha(node: self.topFadeNode, alpha: 1.0)
|
||||||
alphaTransition.updateAlpha(node: self.titleNode, alpha: 1.0)
|
alphaTransition.updateAlpha(node: self.titleNode, alpha: 1.0)
|
||||||
@ -333,7 +341,7 @@ final class VoiceChatMainStageNode: ASDisplayNode {
|
|||||||
|
|
||||||
self.appeared = false
|
self.appeared = false
|
||||||
|
|
||||||
let alphaTransition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
|
let alphaTransition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .easeInOut)
|
||||||
if offset.isZero {
|
if offset.isZero {
|
||||||
alphaTransition.updateAlpha(node: self.backgroundNode, alpha: 0.0)
|
alphaTransition.updateAlpha(node: self.backgroundNode, alpha: 0.0)
|
||||||
} else {
|
} else {
|
||||||
@ -434,7 +442,7 @@ final class VoiceChatMainStageNode: ASDisplayNode {
|
|||||||
strongSelf.speakingContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
strongSelf.speakingContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
strongSelf.speakingContainerNode.layer.animateScale(from: 0.01, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
strongSelf.speakingContainerNode.layer.animateScale(from: 0.01, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
|
||||||
let blobFrame = strongSelf.speakingAvatarNode.frame.insetBy(dx: -14.0, dy: -14.0)
|
let blobFrame = strongSelf.speakingAvatarNode.frame.insetBy(dx: -12.0, dy: -12.0)
|
||||||
strongSelf.speakingAudioLevelDisposable.set((getAudioLevel(peerId)
|
strongSelf.speakingAudioLevelDisposable.set((getAudioLevel(peerId)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -751,15 +759,15 @@ final class VoiceChatMainStageNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setControlsHidden(_ hidden: Bool, animated: Bool) {
|
func setControlsHidden(_ hidden: Bool, animated: Bool, delay: Double = 0.0) {
|
||||||
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate
|
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate
|
||||||
transition.updateAlpha(node: self.headerNode, alpha: hidden ? 0.0 : 1.0)
|
transition.updateAlpha(node: self.headerNode, alpha: hidden ? 0.0 : 1.0, delay: delay)
|
||||||
transition.updateAlpha(node: self.topFadeNode, alpha: hidden ? 0.0 : 1.0)
|
transition.updateAlpha(node: self.topFadeNode, alpha: hidden ? 0.0 : 1.0, delay: delay)
|
||||||
|
|
||||||
transition.updateAlpha(node: self.titleNode, alpha: hidden ? 0.0 : 1.0)
|
transition.updateAlpha(node: self.titleNode, alpha: hidden ? 0.0 : 1.0, delay: delay)
|
||||||
transition.updateAlpha(node: self.microphoneNode, alpha: hidden ? 0.0 : 1.0)
|
transition.updateAlpha(node: self.microphoneNode, alpha: hidden ? 0.0 : 1.0, delay: delay)
|
||||||
transition.updateAlpha(node: self.bottomFadeNode, alpha: hidden ? 0.0 : 1.0)
|
transition.updateAlpha(node: self.bottomFadeNode, alpha: hidden ? 0.0 : 1.0, delay: delay)
|
||||||
transition.updateAlpha(node: self.bottomFillNode, alpha: hidden ? 0.0 : 1.0)
|
transition.updateAlpha(node: self.bottomFillNode, alpha: hidden ? 0.0 : 1.0, delay: delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, isLandscape: Bool, force: Bool = false, transition: ContainedViewLayoutTransition) {
|
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, isLandscape: Bool, force: Bool = false, transition: ContainedViewLayoutTransition) {
|
||||||
|
@ -221,10 +221,15 @@ final class VoiceChatPeerProfileNode: ASDisplayNode {
|
|||||||
self.avatarListContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
self.avatarListContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
self.avatarListContainerNode.cornerRadius = targetRect.width / 2.0
|
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 {
|
if let videoNode = sourceNode.videoNode {
|
||||||
videoNode.updateLayout(size: targetSize, layoutMode: .fillOrFitToSquare, transition: transition)
|
videoNode.updateLayout(size: targetSize, layoutMode: .fillOrFitToSquare, transition: appearenceTransition)
|
||||||
transition.updateFrame(node: videoNode, frame: CGRect(origin: CGPoint(), size: targetSize))
|
appearenceTransition.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)))
|
appearenceTransition.updateFrame(node: sourceNode.videoContainerNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: targetSize.width, height: targetSize.height + backgroundCornerRadius)))
|
||||||
sourceNode.videoContainerNode.cornerRadius = backgroundCornerRadius
|
sourceNode.videoContainerNode.cornerRadius = backgroundCornerRadius
|
||||||
}
|
}
|
||||||
self.insertSubnode(sourceNode.videoContainerNode, belowSubnode: self.avatarListWrapperNode)
|
self.insertSubnode(sourceNode.videoContainerNode, belowSubnode: self.avatarListWrapperNode)
|
||||||
@ -237,11 +242,11 @@ final class VoiceChatPeerProfileNode: ASDisplayNode {
|
|||||||
self.insertSubnode(self.videoFadeNode, aboveSubnode: sourceNode.videoContainerNode)
|
self.insertSubnode(self.videoFadeNode, aboveSubnode: sourceNode.videoContainerNode)
|
||||||
self.view.insertSubview(snapshotView, aboveSubview: sourceNode.videoContainerNode.view)
|
self.view.insertSubview(snapshotView, aboveSubview: sourceNode.videoContainerNode.view)
|
||||||
snapshotView.frame = sourceRect
|
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))
|
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.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||||
snapshotView.removeFromSuperview()
|
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)))
|
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.alpha = 0.0
|
||||||
self.videoFadeNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
|
self.videoFadeNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,8 @@ final class VoiceChatTileGridNode: ASDisplayNode {
|
|||||||
self.isFirstTime = false
|
self.isFirstTime = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let availableWidth = min(size.width, size.height)
|
||||||
|
|
||||||
for i in 0 ..< self.items.count {
|
for i in 0 ..< self.items.count {
|
||||||
let item = self.items[i]
|
let item = self.items[i]
|
||||||
let isLast = i == self.items.count - 1
|
let isLast = i == self.items.count - 1
|
||||||
@ -65,12 +67,12 @@ final class VoiceChatTileGridNode: ASDisplayNode {
|
|||||||
var wasAdded = false
|
var wasAdded = false
|
||||||
if let current = self.itemNodes[item.id] {
|
if let current = self.itemNodes[item.id] {
|
||||||
itemNode = current
|
itemNode = current
|
||||||
current.update(size: itemSize, availableWidth: size.width, item: item, transition: transition)
|
current.update(size: itemSize, availableWidth: availableWidth, item: item, transition: transition)
|
||||||
} else {
|
} else {
|
||||||
wasAdded = true
|
wasAdded = true
|
||||||
let addedItemNode = VoiceChatTileItemNode(context: self.context)
|
let addedItemNode = VoiceChatTileItemNode(context: self.context)
|
||||||
itemNode = addedItemNode
|
itemNode = addedItemNode
|
||||||
addedItemNode.update(size: itemSize, availableWidth: size.width, item: item, transition: .immediate)
|
addedItemNode.update(size: itemSize, availableWidth: availableWidth, item: item, transition: .immediate)
|
||||||
self.itemNodes[self.items[i].id] = addedItemNode
|
self.itemNodes[self.items[i].id] = addedItemNode
|
||||||
self.addSubnode(addedItemNode)
|
self.addSubnode(addedItemNode)
|
||||||
}
|
}
|
||||||
@ -235,7 +237,7 @@ final class VoiceChatTilesGridItemNode: ListViewItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let transition: ContainedViewLayoutTransition = currentItem == nil ? .immediate : .animated(duration: 0.3, curve: .easeInOut)
|
let transition: ContainedViewLayoutTransition = currentItem == nil ? .immediate : .animated(duration: 0.3, curve: .easeInOut)
|
||||||
let tileGridSize = tileGridNode.update(size: CGSize(width: params.width - params.leftInset - params.rightInset, height: CGFloat.greatestFiniteMagnitude), items: item.tiles, transition: transition)
|
let tileGridSize = tileGridNode.update(size: CGSize(width: params.width - params.leftInset - params.rightInset, height: params.availableHeight), items: item.tiles, transition: transition)
|
||||||
if currentItem == nil {
|
if currentItem == nil {
|
||||||
tileGridNode.frame = CGRect(x: params.leftInset, y: 0.0, width: tileGridSize.width, height: tileGridSize.height)
|
tileGridNode.frame = CGRect(x: params.leftInset, y: 0.0, width: tileGridSize.width, height: tileGridSize.height)
|
||||||
strongSelf.backgroundNode.frame = tileGridNode.frame
|
strongSelf.backgroundNode.frame = tileGridNode.frame
|
||||||
|
@ -153,6 +153,8 @@ final class VoiceChatTileItemNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.titleNode = ImmediateTextNode()
|
self.titleNode = ImmediateTextNode()
|
||||||
|
self.titleNode.displaysAsynchronously = false
|
||||||
|
|
||||||
self.statusNode = VoiceChatParticipantStatusNode()
|
self.statusNode = VoiceChatParticipantStatusNode()
|
||||||
|
|
||||||
self.iconNode = ASImageNode()
|
self.iconNode = ASImageNode()
|
||||||
@ -270,33 +272,33 @@ final class VoiceChatTileItemNode: ASDisplayNode {
|
|||||||
|
|
||||||
self.validLayout = (size, availableWidth)
|
self.validLayout = (size, availableWidth)
|
||||||
|
|
||||||
|
if !item.videoReady {
|
||||||
|
let shimmerNode: VoiceChatTileShimmeringNode
|
||||||
|
if let current = self.shimmerNode {
|
||||||
|
shimmerNode = current
|
||||||
|
} else {
|
||||||
|
shimmerNode = VoiceChatTileShimmeringNode(account: item.account, peer: item.peer)
|
||||||
|
self.contentNode.insertSubnode(shimmerNode, aboveSubnode: self.fadeNode)
|
||||||
|
self.shimmerNode = shimmerNode
|
||||||
|
|
||||||
|
if let (rect, containerSize) = self.absoluteLocation {
|
||||||
|
shimmerNode.updateAbsoluteRect(rect, within: containerSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transition.updateFrame(node: shimmerNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
|
shimmerNode.update(shimmeringColor: UIColor.white, size: size, transition: transition)
|
||||||
|
} else if let shimmerNode = self.shimmerNode {
|
||||||
|
self.shimmerNode = nil
|
||||||
|
shimmerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak shimmerNode] _ in
|
||||||
|
shimmerNode?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
var itemTransition = transition
|
var itemTransition = transition
|
||||||
if self.item != item {
|
if self.item != item {
|
||||||
let previousItem = self.item
|
let previousItem = self.item
|
||||||
self.item = item
|
self.item = item
|
||||||
|
|
||||||
if !item.videoReady {
|
|
||||||
let shimmerNode: VoiceChatTileShimmeringNode
|
|
||||||
if let current = self.shimmerNode {
|
|
||||||
shimmerNode = current
|
|
||||||
} else {
|
|
||||||
shimmerNode = VoiceChatTileShimmeringNode(account: item.account, peer: item.peer)
|
|
||||||
self.contentNode.insertSubnode(shimmerNode, aboveSubnode: self.fadeNode)
|
|
||||||
self.shimmerNode = shimmerNode
|
|
||||||
|
|
||||||
if let (rect, containerSize) = self.absoluteLocation {
|
|
||||||
shimmerNode.updateAbsoluteRect(rect, within: containerSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
transition.updateFrame(node: shimmerNode, frame: CGRect(origin: CGPoint(), size: size))
|
|
||||||
shimmerNode.update(shimmeringColor: UIColor.white, size: size, transition: transition)
|
|
||||||
} else if let shimmerNode = self.shimmerNode {
|
|
||||||
self.shimmerNode = nil
|
|
||||||
shimmerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak shimmerNode] _ in
|
|
||||||
shimmerNode?.removeFromSupernode()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if let getAudioLevel = item.getAudioLevel {
|
if let getAudioLevel = item.getAudioLevel {
|
||||||
self.audioLevelDisposable.set((getAudioLevel()
|
self.audioLevelDisposable.set((getAudioLevel()
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||||
@ -354,7 +356,7 @@ final class VoiceChatTileItemNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var microphoneColor = UIColor.white
|
var microphoneColor = UIColor.white
|
||||||
if let additionalText = item.additionalText, case let .text(text, _, color) = additionalText {
|
if let additionalText = item.additionalText, case let .text(_, _, color) = additionalText {
|
||||||
if case .destructive = color {
|
if case .destructive = color {
|
||||||
microphoneColor = destructiveColor
|
microphoneColor = destructiveColor
|
||||||
}
|
}
|
||||||
@ -644,7 +646,7 @@ class VoiceChatTileHighlightNode: ASDisplayNode {
|
|||||||
final class ShimmerEffectForegroundNode: ASDisplayNode {
|
final class ShimmerEffectForegroundNode: ASDisplayNode {
|
||||||
private var currentForegroundColor: UIColor?
|
private var currentForegroundColor: UIColor?
|
||||||
private let imageNodeContainer: ASDisplayNode
|
private let imageNodeContainer: ASDisplayNode
|
||||||
private let imageNode: ASImageNode
|
private let imageNode: ASDisplayNode
|
||||||
|
|
||||||
private var absoluteLocation: (CGRect, CGSize)?
|
private var absoluteLocation: (CGRect, CGSize)?
|
||||||
private var isCurrentlyInHierarchy = false
|
private var isCurrentlyInHierarchy = false
|
||||||
@ -658,11 +660,9 @@ final class ShimmerEffectForegroundNode: ASDisplayNode {
|
|||||||
self.imageNodeContainer = ASDisplayNode()
|
self.imageNodeContainer = ASDisplayNode()
|
||||||
self.imageNodeContainer.isLayerBacked = true
|
self.imageNodeContainer.isLayerBacked = true
|
||||||
|
|
||||||
self.imageNode = ASImageNode()
|
self.imageNode = ASDisplayNode()
|
||||||
self.imageNode.isLayerBacked = true
|
self.imageNode.isLayerBacked = true
|
||||||
self.imageNode.displaysAsynchronously = false
|
self.imageNode.displaysAsynchronously = false
|
||||||
self.imageNode.displayWithoutProcessing = true
|
|
||||||
self.imageNode.contentMode = .scaleToFill
|
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
@ -709,7 +709,9 @@ final class ShimmerEffectForegroundNode: ASDisplayNode {
|
|||||||
|
|
||||||
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions())
|
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions())
|
||||||
})
|
})
|
||||||
self.imageNode.image = image
|
if let image = image {
|
||||||
|
self.imageNode.backgroundColor = UIColor(patternImage: image)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||||
@ -775,7 +777,7 @@ private class VoiceChatTileShimmeringNode: ASDisplayNode {
|
|||||||
self.backgroundNode.displaysAsynchronously = false
|
self.backgroundNode.displaysAsynchronously = false
|
||||||
self.backgroundNode.contentMode = .scaleAspectFill
|
self.backgroundNode.contentMode = .scaleAspectFill
|
||||||
|
|
||||||
self.effectNode = ShimmerEffectForegroundNode(size: 220.0)
|
self.effectNode = ShimmerEffectForegroundNode(size: 240.0)
|
||||||
|
|
||||||
self.borderNode = ASDisplayNode()
|
self.borderNode = ASDisplayNode()
|
||||||
self.borderEffectNode = ShimmerEffectForegroundNode(size: 320.0)
|
self.borderEffectNode = ShimmerEffectForegroundNode(size: 320.0)
|
||||||
@ -835,7 +837,7 @@ private class VoiceChatTileShimmeringNode: ASDisplayNode {
|
|||||||
self.effectNode.update(foregroundColor: shimmeringColor.withAlphaComponent(0.3))
|
self.effectNode.update(foregroundColor: shimmeringColor.withAlphaComponent(0.3))
|
||||||
self.effectNode.frame = bounds
|
self.effectNode.frame = bounds
|
||||||
|
|
||||||
self.borderEffectNode.update(foregroundColor: shimmeringColor.withAlphaComponent(0.5))
|
self.borderEffectNode.update(foregroundColor: shimmeringColor.withAlphaComponent(0.45))
|
||||||
self.borderEffectNode.frame = bounds
|
self.borderEffectNode.frame = bounds
|
||||||
|
|
||||||
transition.updateFrame(node: self.backgroundNode, frame: bounds)
|
transition.updateFrame(node: self.backgroundNode, frame: bounds)
|
||||||
|
File diff suppressed because it is too large
Load Diff
12
submodules/TelegramUI/Images.xcassets/Call/Context Menu/Audio.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Call/Context Menu/Audio.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "ic_menuaudio.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Call/Context Menu/Audio.imageset/ic_menuaudio.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Call/Context Menu/Audio.imageset/ic_menuaudio.pdf
vendored
Normal file
Binary file not shown.
Binary file not shown.
@ -1302,10 +1302,10 @@ private:
|
|||||||
remoteRenderer.videoContentMode = UIViewContentModeScaleToFill;
|
remoteRenderer.videoContentMode = UIViewContentModeScaleToFill;
|
||||||
|
|
||||||
VideoMetalView *cloneRenderer = nil;
|
VideoMetalView *cloneRenderer = nil;
|
||||||
/*if (requestClone) {
|
if (requestClone) {
|
||||||
cloneRenderer = [[VideoMetalView alloc] initWithFrame:CGRectZero];
|
cloneRenderer = [[VideoMetalView alloc] initWithFrame:CGRectZero];
|
||||||
cloneRenderer.videoContentMode = UIViewContentModeScaleToFill;
|
cloneRenderer.videoContentMode = UIViewContentModeScaleToFill;
|
||||||
}*/
|
}
|
||||||
|
|
||||||
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink = [remoteRenderer getSink];
|
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink = [remoteRenderer getSink];
|
||||||
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> cloneSink = [cloneRenderer getSink];
|
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> cloneSink = [cloneRenderer getSink];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user