mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 11:23:48 +00:00
Video Chat Improvements
This commit is contained in:
parent
a8f2bf17a4
commit
385b035ed0
@ -372,6 +372,7 @@ public protocol PresentationGroupCall: class {
|
||||
var incomingVideoSources: Signal<Set<String>, NoError> { get }
|
||||
|
||||
func makeIncomingVideoView(endpointId: String, completion: @escaping (PresentationCallVideoView?) -> Void)
|
||||
func makeOutgoingVideoView(completion: @escaping (PresentationCallVideoView?) -> Void)
|
||||
|
||||
func loadMoreMembers(token: String)
|
||||
}
|
||||
|
@ -165,8 +165,11 @@ private final class InnerActionsContainerNode: ASDisplayNode {
|
||||
gesture.isEnabled = self.panSelectionGestureEnabled
|
||||
}
|
||||
|
||||
func updateLayout(widthClass: ContainerViewLayoutSizeClass, constrainedWidth: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
func updateLayout(widthClass: ContainerViewLayoutSizeClass, constrainedWidth: CGFloat, minimalWidth: CGFloat?, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
var minActionsWidth: CGFloat = 250.0
|
||||
if let minimalWidth = minimalWidth, minimalWidth > minActionsWidth {
|
||||
minActionsWidth = minimalWidth
|
||||
}
|
||||
|
||||
switch widthClass {
|
||||
case .compact:
|
||||
@ -517,10 +520,10 @@ final class ContextActionsContainerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
var contentSize = CGSize()
|
||||
let actionsSize = self.actionsNode.updateLayout(widthClass: widthClass, constrainedWidth: constrainedWidth, transition: transition)
|
||||
let actionsSize = self.actionsNode.updateLayout(widthClass: widthClass, constrainedWidth: constrainedWidth, minimalWidth: nil, transition: transition)
|
||||
|
||||
if let additionalActionsNode = self.additionalActionsNode, let additionalShadowNode = self.additionalShadowNode {
|
||||
let additionalActionsSize = additionalActionsNode.updateLayout(widthClass: widthClass, constrainedWidth: actionsSize.width, transition: transition)
|
||||
let additionalActionsSize = additionalActionsNode.updateLayout(widthClass: widthClass, constrainedWidth: actionsSize.width, minimalWidth: actionsSize.width, transition: transition)
|
||||
contentSize = additionalActionsSize
|
||||
|
||||
let bounds = CGRect(origin: CGPoint(), size: additionalActionsSize)
|
||||
|
@ -5,6 +5,7 @@ import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import AppBundle
|
||||
import SemanticStatusNode
|
||||
import AnimationUI
|
||||
|
||||
private let labelFont = Font.regular(13.0)
|
||||
|
||||
@ -30,6 +31,8 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
|
||||
}
|
||||
|
||||
enum Image {
|
||||
case cameraOff
|
||||
case cameraOn
|
||||
case camera
|
||||
case mute
|
||||
case flipCamera
|
||||
@ -63,6 +66,7 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
|
||||
private let effectView: UIVisualEffectView
|
||||
private let contentBackgroundNode: ASImageNode
|
||||
private let contentNode: ASImageNode
|
||||
private var animationNode: AnimationNode?
|
||||
private let overlayHighlightNode: ASImageNode
|
||||
private var statusNode: SemanticStatusNode?
|
||||
let textNode: ImmediateTextNode
|
||||
@ -179,6 +183,33 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
|
||||
|
||||
let contentBackgroundImage: UIImage? = nil
|
||||
|
||||
var animationName: String?
|
||||
switch content.image {
|
||||
case .cameraOff:
|
||||
animationName = "anim_cameraoff"
|
||||
case .cameraOn:
|
||||
animationName = "anim_cameraon"
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if let animationName = animationName {
|
||||
let animationFrame = CGRect(origin: CGPoint(), size: CGSize(width: self.largeButtonSize, height: self.largeButtonSize))
|
||||
if self.animationNode == nil {
|
||||
let animationNode = AnimationNode(animation: animationName, colors: nil, scale: 1.0)
|
||||
self.animationNode = animationNode
|
||||
self.contentContainer.insertSubnode(animationNode, aboveSubnode: self.contentNode)
|
||||
}
|
||||
if let animationNode = self.animationNode {
|
||||
animationNode.frame = animationFrame
|
||||
if previousContent == nil {
|
||||
animationNode.seekToEnd()
|
||||
} else if previousContent?.image != content.image {
|
||||
animationNode.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let contentImage = generateImage(CGSize(width: self.largeButtonSize, height: self.largeButtonSize), contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
@ -219,6 +250,8 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
|
||||
var image: UIImage?
|
||||
|
||||
switch content.image {
|
||||
case .cameraOff, .cameraOn:
|
||||
image = nil
|
||||
case .camera:
|
||||
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallCameraButton"), color: imageColor)
|
||||
case .mute:
|
||||
|
@ -2384,6 +2384,76 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
self.participantsContext?.lowerHand()
|
||||
}
|
||||
|
||||
public func makeOutgoingVideoView(completion: @escaping (PresentationCallVideoView?) -> Void) {
|
||||
if self.videoCapturer == nil {
|
||||
let videoCapturer = OngoingCallVideoCapturer()
|
||||
self.videoCapturer = videoCapturer
|
||||
}
|
||||
|
||||
self.videoCapturer?.makeOutgoingVideoView(completion: { view in
|
||||
if let view = view {
|
||||
let setOnFirstFrameReceived = view.setOnFirstFrameReceived
|
||||
let setOnOrientationUpdated = view.setOnOrientationUpdated
|
||||
let setOnIsMirroredUpdated = view.setOnIsMirroredUpdated
|
||||
completion(PresentationCallVideoView(
|
||||
holder: view,
|
||||
view: view.view,
|
||||
setOnFirstFrameReceived: { f in
|
||||
setOnFirstFrameReceived(f)
|
||||
},
|
||||
getOrientation: { [weak view] in
|
||||
if let view = view {
|
||||
let mappedValue: PresentationCallVideoView.Orientation
|
||||
switch view.getOrientation() {
|
||||
case .rotation0:
|
||||
mappedValue = .rotation0
|
||||
case .rotation90:
|
||||
mappedValue = .rotation90
|
||||
case .rotation180:
|
||||
mappedValue = .rotation180
|
||||
case .rotation270:
|
||||
mappedValue = .rotation270
|
||||
}
|
||||
return mappedValue
|
||||
} else {
|
||||
return .rotation0
|
||||
}
|
||||
},
|
||||
getAspect: { [weak view] in
|
||||
if let view = view {
|
||||
return view.getAspect()
|
||||
} else {
|
||||
return 0.0
|
||||
}
|
||||
},
|
||||
setOnOrientationUpdated: { f in
|
||||
setOnOrientationUpdated { value, aspect in
|
||||
let mappedValue: PresentationCallVideoView.Orientation
|
||||
switch value {
|
||||
case .rotation0:
|
||||
mappedValue = .rotation0
|
||||
case .rotation90:
|
||||
mappedValue = .rotation90
|
||||
case .rotation180:
|
||||
mappedValue = .rotation180
|
||||
case .rotation270:
|
||||
mappedValue = .rotation270
|
||||
}
|
||||
f?(mappedValue, aspect)
|
||||
}
|
||||
},
|
||||
setOnIsMirroredUpdated: { f in
|
||||
setOnIsMirroredUpdated { value in
|
||||
f?(value)
|
||||
}
|
||||
}
|
||||
))
|
||||
} else {
|
||||
completion(nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func requestVideo() {
|
||||
if self.videoCapturer == nil {
|
||||
let videoCapturer = OngoingCallVideoCapturer()
|
||||
|
@ -21,14 +21,16 @@ final class VoiceChatCameraPreviewController: ViewController {
|
||||
|
||||
private var animatedIn = false
|
||||
|
||||
private let shareCamera: () -> Void
|
||||
private let cameraNode: GroupVideoNode
|
||||
private let shareCamera: (ASDisplayNode) -> Void
|
||||
private let switchCamera: () -> Void
|
||||
private let shareScreen: () -> Void
|
||||
|
||||
private var presentationDataDisposable: Disposable?
|
||||
|
||||
init(context: AccountContext, shareCamera: @escaping () -> Void, switchCamera: @escaping () -> Void, shareScreen: @escaping () -> Void) {
|
||||
init(context: AccountContext, cameraNode: GroupVideoNode, shareCamera: @escaping (ASDisplayNode) -> Void, switchCamera: @escaping () -> Void, shareScreen: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.cameraNode = cameraNode
|
||||
self.shareCamera = shareCamera
|
||||
self.switchCamera = switchCamera
|
||||
self.shareScreen = shareScreen
|
||||
@ -58,10 +60,12 @@ final class VoiceChatCameraPreviewController: ViewController {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = VoiceChatCameraPreviewControllerNode(context: self.context)
|
||||
self.displayNode = VoiceChatCameraPreviewControllerNode(context: self.context, cameraNode: self.cameraNode)
|
||||
self.controllerNode.shareCamera = { [weak self] in
|
||||
self?.shareCamera()
|
||||
self?.dismiss()
|
||||
if let strongSelf = self {
|
||||
strongSelf.shareCamera(strongSelf.cameraNode)
|
||||
strongSelf.dismiss()
|
||||
}
|
||||
}
|
||||
self.controllerNode.switchCamera = { [weak self] in
|
||||
self?.switchCamera()
|
||||
@ -106,6 +110,7 @@ private class VoiceChatCameraPreviewControllerNode: ViewControllerTracingNode, U
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
|
||||
private let cameraNode: GroupVideoNode
|
||||
private let dimNode: ASDisplayNode
|
||||
private let wrappingScrollNode: ASScrollNode
|
||||
private let contentContainerNode: ASDisplayNode
|
||||
@ -130,10 +135,12 @@ private class VoiceChatCameraPreviewControllerNode: ViewControllerTracingNode, U
|
||||
var dismiss: (() -> Void)?
|
||||
var cancel: (() -> Void)?
|
||||
|
||||
init(context: AccountContext) {
|
||||
init(context: AccountContext, cameraNode: GroupVideoNode) {
|
||||
self.context = context
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
self.cameraNode = cameraNode
|
||||
|
||||
self.wrappingScrollNode = ASScrollNode()
|
||||
self.wrappingScrollNode.view.alwaysBounceVertical = true
|
||||
self.wrappingScrollNode.view.delaysContentTouches = false
|
||||
@ -214,6 +221,7 @@ private class VoiceChatCameraPreviewControllerNode: ViewControllerTracingNode, U
|
||||
|
||||
self.contentContainerNode.addSubnode(self.previewContainerNode)
|
||||
|
||||
self.previewContainerNode.addSubnode(self.cameraNode)
|
||||
self.previewContainerNode.addSubnode(self.switchCameraButton)
|
||||
self.switchCameraButton.view.addSubview(self.switchCameraEffectView)
|
||||
self.switchCameraButton.addSubnode(self.switchCameraIconNode)
|
||||
@ -368,6 +376,9 @@ private class VoiceChatCameraPreviewControllerNode: ViewControllerTracingNode, U
|
||||
let previewSize = CGSize(width: contentFrame.width - previewInset * 2.0, height: contentHeight - 243.0 - bottomInset)
|
||||
transition.updateFrame(node: self.previewContainerNode, frame: CGRect(origin: CGPoint(x: previewInset, y: 56.0), size: previewSize))
|
||||
|
||||
self.cameraNode.frame = CGRect(origin: CGPoint(), size: previewSize)
|
||||
self.cameraNode.updateLayout(size: previewSize, isLandscape: false, transition: .immediate)
|
||||
|
||||
let switchCameraFrame = CGRect(x: previewSize.width - 48.0 - 16.0, y: previewSize.height - 48.0 - 16.0, width: 48.0, height: 48.0)
|
||||
transition.updateFrame(node: self.switchCameraButton, frame: switchCameraFrame)
|
||||
transition.updateFrame(view: self.switchCameraEffectView, frame: CGRect(origin: CGPoint(), size: switchCameraFrame.size))
|
||||
|
@ -177,8 +177,6 @@ final class GroupVideoNode: ASDisplayNode {
|
||||
rotatedVideoFrame.size.height = ceil(rotatedVideoFrame.size.height)
|
||||
|
||||
var videoSize = rotatedVideoFrame.size
|
||||
// CGSize(width: 1203, height: 677)
|
||||
|
||||
transition.updatePosition(layer: self.videoView.view.layer, position: rotatedVideoFrame.center)
|
||||
transition.updateBounds(layer: self.videoView.view.layer, bounds: CGRect(origin: CGPoint(), size: videoSize))
|
||||
|
||||
@ -194,6 +192,8 @@ private final class MainVideoContainerNode: ASDisplayNode {
|
||||
private let context: AccountContext
|
||||
private let call: PresentationGroupCall
|
||||
|
||||
private var backdropVideoNode: GroupVideoNode?
|
||||
|
||||
private var currentVideoNode: GroupVideoNode?
|
||||
private var candidateVideoNode: GroupVideoNode?
|
||||
private let topCornersNode: ASImageNode
|
||||
@ -291,7 +291,6 @@ private final class MainVideoContainerNode: ASDisplayNode {
|
||||
strongSelf.candidateVideoNode = nil
|
||||
|
||||
let videoNode = GroupVideoNode(videoView: videoView)
|
||||
|
||||
if let currentVideoNode = strongSelf.currentVideoNode {
|
||||
currentVideoNode.removeFromSupernode()
|
||||
strongSelf.currentVideoNode = nil
|
||||
@ -500,6 +499,12 @@ public final class VoiceChatController: ViewController {
|
||||
if lhs.effectiveVideoEndpointId != rhs.effectiveVideoEndpointId {
|
||||
return false
|
||||
}
|
||||
if lhs.hasVideo != rhs.hasVideo {
|
||||
return false
|
||||
}
|
||||
if lhs.hasScreencast != rhs.hasScreencast {
|
||||
return false
|
||||
}
|
||||
if lhs.activityTimestamp != rhs.activityTimestamp {
|
||||
return false
|
||||
}
|
||||
@ -639,10 +644,6 @@ public final class VoiceChatController: ViewController {
|
||||
if peerEntry.hasScreencast {
|
||||
textIcon.insert(.screen)
|
||||
}
|
||||
if peerEntry.volume != nil {
|
||||
textIcon.insert(.volume)
|
||||
} else {
|
||||
}
|
||||
let yourText: String
|
||||
if (peerEntry.about?.isEmpty ?? true) && peer.smallProfileImage == nil {
|
||||
yourText = presentationData.strings.VoiceChat_TapToAddPhotoOrBio
|
||||
@ -676,6 +677,9 @@ public final class VoiceChatController: ViewController {
|
||||
text = .text(presentationData.strings.VoiceChat_StatusMutedForYou, textIcon, .destructive)
|
||||
icon = .microphone(true, UIColor(rgb: 0xff3b30))
|
||||
} else {
|
||||
if peerEntry.volume != nil {
|
||||
textIcon.insert(.volume)
|
||||
}
|
||||
let volumeValue = peerEntry.volume.flatMap { $0 / 100 }
|
||||
if let volume = volumeValue, volume != 100 {
|
||||
text = .text( presentationData.strings.VoiceChat_StatusSpeakingVolume("\(volume)%").0, textIcon, .constructive)
|
||||
@ -875,8 +879,8 @@ public final class VoiceChatController: ViewController {
|
||||
private var requestedVideoSources = Set<String>()
|
||||
private var videoNodes: [(String, GroupVideoNode)] = []
|
||||
|
||||
private var currentDominantSpeakerWithVideo: (PeerId, String)?
|
||||
private var currentForcedSpeakerWithVideo: (PeerId, String)?
|
||||
private var currentDominantSpeakerWithVideo: PeerId?
|
||||
private var currentForcedSpeakerWithVideo: PeerId?
|
||||
private var effectiveSpeakerWithVideo: (PeerId, String)?
|
||||
|
||||
private var updateAvatarDisposable = MetaDisposable()
|
||||
@ -982,11 +986,13 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
self.bottomPanelBackgroundNode = ASDisplayNode()
|
||||
self.bottomPanelBackgroundNode.backgroundColor = panelBackgroundColor
|
||||
self.bottomPanelBackgroundNode.isUserInteractionEnabled = false
|
||||
|
||||
self.bottomCornersNode = ASImageNode()
|
||||
self.bottomCornersNode.displaysAsynchronously = false
|
||||
self.bottomCornersNode.displayWithoutProcessing = true
|
||||
self.bottomCornersNode.image = cornersImage(top: false, bottom: true, dark: false)
|
||||
self.bottomCornersNode.isUserInteractionEnabled = false
|
||||
|
||||
self.audioButton = CallControllerButtonItemNode()
|
||||
self.cameraButton = CallControllerButtonItemNode()
|
||||
@ -1076,12 +1082,12 @@ public final class VoiceChatController: ViewController {
|
||||
let _ = self?.call.updateMuteState(peerId: peerId, isMuted: isMuted)
|
||||
}, pinPeer: { [weak self] peerId, endpointId in
|
||||
if let strongSelf = self {
|
||||
if peerId != strongSelf.currentForcedSpeakerWithVideo?.0, let endpointId = endpointId {
|
||||
strongSelf.currentForcedSpeakerWithVideo = (peerId, endpointId)
|
||||
if peerId != strongSelf.currentForcedSpeakerWithVideo {
|
||||
strongSelf.currentForcedSpeakerWithVideo = peerId
|
||||
} else {
|
||||
strongSelf.currentForcedSpeakerWithVideo = nil
|
||||
}
|
||||
strongSelf.updatePinnedParticipant()
|
||||
strongSelf.updatePinnedParticipant(waitForFullSize: false)
|
||||
|
||||
var updateLayout = false
|
||||
if strongSelf.effectiveSpeakerWithVideo != nil && !strongSelf.isExpanded {
|
||||
@ -1415,7 +1421,7 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
for (endpointId, _) in strongSelf.videoNodes {
|
||||
if entry.effectiveVideoEndpointId == endpointId {
|
||||
items.append(.action(ContextMenuActionItem(text: strongSelf.currentForcedSpeakerWithVideo?.0 == peer.id ? strongSelf.presentationData.strings.VoiceChat_UnpinVideo : strongSelf.presentationData.strings.VoiceChat_PinVideo, icon: { theme in
|
||||
items.append(.action(ContextMenuActionItem(text: strongSelf.currentForcedSpeakerWithVideo == peer.id ? strongSelf.presentationData.strings.VoiceChat_UnpinVideo : strongSelf.presentationData.strings.VoiceChat_PinVideo, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Pin"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
guard let strongSelf = self else {
|
||||
@ -1891,12 +1897,12 @@ public final class VoiceChatController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
if let (peerId, endpointId, _) = maxLevelWithVideo {
|
||||
/*if strongSelf.currentDominantSpeakerWithVideo?.0 != peerId || strongSelf.currentDominantSpeakerWithVideo?.1 != endpointId {
|
||||
strongSelf.currentDominantSpeakerWithVideo = (peerId, endpointId)
|
||||
strongSelf.call.setFullSizeVideo(endpointId: endpointId)
|
||||
strongSelf.mainVideoContainerNode?.updatePeer(peer: (peerId: peerId, source: endpointId), waitForFullSize: true)
|
||||
}*/
|
||||
if let (peerId, _, _) = maxLevelWithVideo {
|
||||
if strongSelf.currentDominantSpeakerWithVideo != peerId {
|
||||
strongSelf.currentDominantSpeakerWithVideo = peerId
|
||||
|
||||
strongSelf.updatePinnedParticipant(waitForFullSize: true)
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.itemInteraction?.updateAudioLevels(levels)
|
||||
@ -2056,13 +2062,13 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
if let (peerId, endpointId) = strongSelf.effectiveSpeakerWithVideo {
|
||||
if !validSources.contains(endpointId) {
|
||||
if peerId == strongSelf.currentForcedSpeakerWithVideo?.0 {
|
||||
if peerId == strongSelf.currentForcedSpeakerWithVideo {
|
||||
strongSelf.currentForcedSpeakerWithVideo = nil
|
||||
}
|
||||
if peerId == strongSelf.currentDominantSpeakerWithVideo?.0 {
|
||||
if peerId == strongSelf.currentDominantSpeakerWithVideo {
|
||||
strongSelf.currentDominantSpeakerWithVideo = nil
|
||||
}
|
||||
strongSelf.updatePinnedParticipant()
|
||||
strongSelf.updatePinnedParticipant(waitForFullSize: false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -3240,14 +3246,22 @@ public final class VoiceChatController: ViewController {
|
||||
self.call.disableVideo()
|
||||
self.call.disableScreencast()
|
||||
} else {
|
||||
let controller = VoiceChatCameraPreviewController(context: self.context, shareCamera: { [weak self] in
|
||||
self?.call.requestVideo()
|
||||
self.call.makeOutgoingVideoView { [weak self] view in
|
||||
guard let strongSelf = self, let view = view else {
|
||||
return
|
||||
}
|
||||
let cameraNode = GroupVideoNode(videoView: view)
|
||||
let controller = VoiceChatCameraPreviewController(context: strongSelf.context, cameraNode: cameraNode, shareCamera: { [weak self] videoNode in
|
||||
if let strongSelf = self {
|
||||
strongSelf.call.requestVideo()
|
||||
}
|
||||
}, switchCamera: { [weak self] in
|
||||
self?.call.switchVideoCamera()
|
||||
}, shareScreen: { [weak self] in
|
||||
self?.call.requestScreencast()
|
||||
})
|
||||
self.controller?.present(controller, in: .window(.root))
|
||||
strongSelf.controller?.present(controller, in: .window(.root))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3467,15 +3481,10 @@ public final class VoiceChatController: ViewController {
|
||||
self.topPanelBackgroundNode.frame = CGRect(x: 0.0, y: topPanelHeight - 24.0, width: size.width, height: min(topPanelFrame.height, 24.0))
|
||||
|
||||
let listMaxY = listTopInset + listSize.height
|
||||
var bottomOffset: CGFloat = min(0.0, bottomEdge - listMaxY)
|
||||
let bottomOffset: CGFloat = min(0.0, bottomEdge - listMaxY) + layout.size.height - bottomPanelHeight
|
||||
let bottomDelta = self.effectiveBottomAreaHeight - bottomAreaHeight
|
||||
|
||||
bottomOffset += layout.size.height - bottomPanelHeight
|
||||
|
||||
var bottomCornersFrame = CGRect(origin: CGPoint(x: sideInset + floorToScreenPixels((size.width - contentWidth) / 2.0), y: -50.0 + bottomOffset + bottomDelta), 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 + bottomDelta), size: CGSize(width: contentWidth - sideInset * 2.0, height: 50.0))
|
||||
let previousBottomCornersFrame = self.bottomCornersNode.frame
|
||||
if !bottomCornersFrame.equalTo(previousBottomCornersFrame) {
|
||||
self.bottomCornersNode.frame = bottomCornersFrame
|
||||
@ -3703,7 +3712,7 @@ public final class VoiceChatController: ViewController {
|
||||
}
|
||||
|
||||
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .linear) : .immediate
|
||||
self.cameraButton.update(size: videoButtonSize, content: CallControllerButtonItemNode.Content(appearance: normalButtonAppearance, image: .camera), text: self.presentationData.strings.VoiceChat_Video, transition: transition)
|
||||
self.cameraButton.update(size: videoButtonSize, content: CallControllerButtonItemNode.Content(appearance: normalButtonAppearance, image: .cameraOff), text: self.presentationData.strings.VoiceChat_Video, transition: transition)
|
||||
|
||||
self.switchCameraButton.update(size: videoButtonSize, content: CallControllerButtonItemNode.Content(appearance: normalButtonAppearance, image: .flipCamera), text: "", transition: transition)
|
||||
|
||||
@ -4498,20 +4507,20 @@ public final class VoiceChatController: ViewController {
|
||||
self.enqueueTileTransition(tileTransition)
|
||||
}
|
||||
|
||||
private func updatePinnedParticipant() {
|
||||
private func updatePinnedParticipant(waitForFullSize: Bool) {
|
||||
let effectivePinnedParticipant = self.currentForcedSpeakerWithVideo ?? self.currentDominantSpeakerWithVideo
|
||||
guard effectivePinnedParticipant?.0 != self.effectiveSpeakerWithVideo?.0 || effectivePinnedParticipant?.1 != self.effectiveSpeakerWithVideo?.1 else {
|
||||
guard effectivePinnedParticipant != self.effectiveSpeakerWithVideo?.0 else {
|
||||
return
|
||||
}
|
||||
|
||||
if let (peerId, _) = effectivePinnedParticipant {
|
||||
if let peerId = effectivePinnedParticipant {
|
||||
for entry in self.currentEntries {
|
||||
switch entry {
|
||||
case let .peer(peer):
|
||||
if peer.peer.id == peerId, let endpointId = peer.effectiveVideoEndpointId {
|
||||
self.effectiveSpeakerWithVideo = (peerId, endpointId)
|
||||
self.call.setFullSizeVideo(endpointId: endpointId)
|
||||
self.mainVideoContainerNode?.updatePeer(peer: (peerId: peerId, endpointId: endpointId), waitForFullSize: false)
|
||||
self.mainVideoContainerNode?.updatePeer(peer: (peerId: peerId, endpointId: endpointId), waitForFullSize: waitForFullSize)
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
@ -261,11 +261,9 @@ private class VoiceChatParticipantStatusNode: ASDisplayNode {
|
||||
break
|
||||
}
|
||||
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: size.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let iconSize = CGSize(width: 16.0, height: 16.0)
|
||||
let spacing: CGFloat = 3.0
|
||||
var contentSize = textLayout.size
|
||||
|
||||
var icons: [UIImage] = []
|
||||
if hasVolume, let image = generateTintedImage(image: UIImage(bundleImageName: "Call/StatusVolume"), color: color) {
|
||||
icons.append(image)
|
||||
@ -276,6 +274,9 @@ private class VoiceChatParticipantStatusNode: ASDisplayNode {
|
||||
if hasScreen, let image = generateTintedImage(image: UIImage(bundleImageName: "Call/StatusScreen"), color: color) {
|
||||
icons.append(image)
|
||||
}
|
||||
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: size.width - (iconSize.width + spacing) * CGFloat(icons.count), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
var contentSize = textLayout.size
|
||||
contentSize.width += (iconSize.width + spacing) * CGFloat(icons.count)
|
||||
|
||||
return (contentSize, { [weak self] in
|
||||
@ -1162,6 +1163,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
return (layout, { [weak self] synchronousLoad, animated in
|
||||
if let strongSelf = self {
|
||||
var hadItem = strongSelf.layoutParams?.0 != nil
|
||||
strongSelf.layoutParams = (item, params, first, last)
|
||||
strongSelf.currentTitle = titleAttributedString?.string
|
||||
strongSelf.wavesColor = wavesColor
|
||||
@ -1262,8 +1264,8 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
let transition: ContainedViewLayoutTransition
|
||||
if animated {
|
||||
transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring)
|
||||
if animated && hadItem {
|
||||
transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .easeInOut)
|
||||
} else {
|
||||
transition = .immediate
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit ae6291030c8484492423c8272ad41db02509d9f7
|
||||
Subproject commit 94a9c7b4e49c943d1ca108e35779739ad99d695a
|
Loading…
x
Reference in New Issue
Block a user