Video Chat Improvements

This commit is contained in:
Ilya Laktyushin 2021-05-21 20:21:41 +04:00
parent 659ea466c9
commit 43dd5c7b12
9 changed files with 4568 additions and 4348 deletions

View File

@ -6472,5 +6472,6 @@ Sorry for the inconvenience.";
"VoiceChat.ShareScreen" = "Share Screen"; "VoiceChat.ShareScreen" = "Share Screen";
"VoiceChat.StopScreenSharing" = "Stop Screen Sharing"; "VoiceChat.StopScreenSharing" = "Stop Screen Sharing";
"VoiceChat.ParticipantIsSpeaking" = "%1$@ is speaking";
"WallpaperPreview.WallpaperColors" = "Colors"; "WallpaperPreview.WallpaperColors" = "Colors";

View File

@ -45,6 +45,8 @@ swift_library(
"//submodules/PeerInfoAvatarListNode:PeerInfoAvatarListNode", "//submodules/PeerInfoAvatarListNode:PeerInfoAvatarListNode",
"//submodules/WebSearchUI:WebSearchUI", "//submodules/WebSearchUI:WebSearchUI",
"//submodules/MapResourceToAvatarSizes:MapResourceToAvatarSizes", "//submodules/MapResourceToAvatarSizes:MapResourceToAvatarSizes",
"//submodules/TextFormat:TextFormat",
"//submodules/Markdown:Markdown",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -206,7 +206,6 @@ public final class VoiceChatController: ViewController {
private final class Interaction { private final class Interaction {
let updateIsMuted: (PeerId, Bool) -> Void let updateIsMuted: (PeerId, Bool) -> Void
let switchToPeer: (PeerId, String?, Bool) -> Void let switchToPeer: (PeerId, String?, Bool) -> Void
let togglePeerVideo: (PeerId) -> Void
let openInvite: () -> Void let openInvite: () -> Void
let peerContextAction: (VoiceChatPeerEntry, ASDisplayNode, ContextGesture?) -> Void let peerContextAction: (VoiceChatPeerEntry, ASDisplayNode, ContextGesture?) -> Void
let getPeerVideo: (String, GroupVideoNode.Position) -> GroupVideoNode? let getPeerVideo: (String, GroupVideoNode.Position) -> GroupVideoNode?
@ -219,14 +218,12 @@ public final class VoiceChatController: ViewController {
init( init(
updateIsMuted: @escaping (PeerId, Bool) -> Void, updateIsMuted: @escaping (PeerId, Bool) -> Void,
switchToPeer: @escaping (PeerId, String?, Bool) -> Void, switchToPeer: @escaping (PeerId, String?, Bool) -> Void,
togglePeerVideo: @escaping (PeerId) -> Void,
openInvite: @escaping () -> Void, openInvite: @escaping () -> Void,
peerContextAction: @escaping (VoiceChatPeerEntry, ASDisplayNode, ContextGesture?) -> Void, peerContextAction: @escaping (VoiceChatPeerEntry, ASDisplayNode, ContextGesture?) -> Void,
getPeerVideo: @escaping (String, GroupVideoNode.Position) -> GroupVideoNode? getPeerVideo: @escaping (String, GroupVideoNode.Position) -> GroupVideoNode?
) { ) {
self.updateIsMuted = updateIsMuted self.updateIsMuted = updateIsMuted
self.switchToPeer = switchToPeer self.switchToPeer = switchToPeer
self.togglePeerVideo = togglePeerVideo
self.openInvite = openInvite self.openInvite = openInvite
self.peerContextAction = peerContextAction self.peerContextAction = peerContextAction
self.getPeerVideo = getPeerVideo self.getPeerVideo = getPeerVideo
@ -803,6 +800,7 @@ public final class VoiceChatController: ViewController {
private var endpointToPeerId: [String: PeerId] = [:] private var endpointToPeerId: [String: PeerId] = [:]
private var peerIdToEndpoint: [PeerId: String] = [:] private var peerIdToEndpoint: [PeerId: String] = [:]
private var currentSpeakers: [PeerId] = []
private var currentDominantSpeaker: (PeerId, Double)? private var currentDominantSpeaker: (PeerId, Double)?
private var currentForcedSpeaker: PeerId? private var currentForcedSpeaker: PeerId?
private var effectiveSpeaker: (PeerId, String?)? private var effectiveSpeaker: (PeerId, String?)?
@ -1000,6 +998,7 @@ public final class VoiceChatController: ViewController {
self.transitionContainerNode.clipsToBounds = true self.transitionContainerNode.clipsToBounds = true
self.transitionContainerNode.isUserInteractionEnabled = false self.transitionContainerNode.isUserInteractionEnabled = false
self.transitionContainerNode.view.mask = self.transitionMaskView self.transitionContainerNode.view.mask = self.transitionMaskView
// self.transitionContainerNode.view.addSubview(self.transitionMaskView)
self.scheduleTextNode = ImmediateTextNode() self.scheduleTextNode = ImmediateTextNode()
self.scheduleTextNode.isHidden = !self.isScheduling self.scheduleTextNode.isHidden = !self.isScheduling
@ -1064,13 +1063,6 @@ public final class VoiceChatController: ViewController {
strongSelf.updateMainVideo(waitForFullSize: false, updateMembers: true, force: true) strongSelf.updateMainVideo(waitForFullSize: false, updateMembers: true, force: true)
} }
} }
}, togglePeerVideo: { [weak self] peerId in
guard let strongSelf = self else {
return
}
if let strongSelf = self {
}
}, openInvite: { [weak self] in }, openInvite: { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
@ -1882,6 +1874,20 @@ public final class VoiceChatController: ViewController {
} }
}) })
self.fullscreenListNode.updateFloatingHeaderOffset = { [weak self] _, _ in
guard let strongSelf = self else {
return
}
var visiblePeerIds = Set<PeerId>()
strongSelf.fullscreenListNode.forEachVisibleItemNode { itemNode in
if let itemNode = itemNode as? VoiceChatFullscreenParticipantItemNode, let item = itemNode.item {
visiblePeerIds.insert(item.peer.id)
}
}
strongSelf.mainStageNode.update(visiblePeerIds: visiblePeerIds)
}
self.listNode.updateFloatingHeaderOffset = { [weak self] offset, transition in self.listNode.updateFloatingHeaderOffset = { [weak self] offset, transition in
if let strongSelf = self { if let strongSelf = self {
strongSelf.currentContentOffset = offset strongSelf.currentContentOffset = offset
@ -1996,6 +2002,24 @@ public final class VoiceChatController: ViewController {
} }
} }
self.mainStageNode.switchTo = { [weak self] peerId in
if let strongSelf = self, let interaction = strongSelf.itemInteraction {
interaction.switchToPeer(peerId, nil, false)
// let position: ListViewScrollPosition
// var index: Int = 0
// if index > strongSelf.currentFullscreenEntries.count - 3 {
// index = strongSelf.currentFullscreenEntries.count - 1
// position = .bottom(0.0)
// } else {
// position = .center(.bottom)
// }
// strongSelf.fullscreenListNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: index, position: position, animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in
// completion()
// })
}
}
self.mainStageNode.getAudioLevel = { [weak self] peerId in self.mainStageNode.getAudioLevel = { [weak self] peerId in
return self?.itemInteraction?.getAudioLevel(peerId) ?? .single(0.0) return self?.itemInteraction?.getAudioLevel(peerId) ?? .single(0.0)
} }
@ -3172,18 +3196,14 @@ public final class VoiceChatController: ViewController {
let bottomPanelCoverHeight = bottomAreaHeight + layout.intrinsicInsets.bottom let bottomPanelCoverHeight = bottomAreaHeight + layout.intrinsicInsets.bottom
let bottomGradientFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - bottomPanelCoverHeight), size: CGSize(width: size.width, height: bottomGradientHeight)) let bottomGradientFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - bottomPanelCoverHeight), size: CGSize(width: size.width, height: bottomGradientHeight))
let additionalMaskHeight: CGFloat = 62.0 let transitionContainerFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
let topEdgeY = isLandscape ? 0.0 : layoutTopInset
let bottomEdgeY = (self.isLandscape ? layout.size.height + bottomGradientHeight : bottomGradientFrame.maxY) + additionalMaskHeight
let transitionContainerFrame = CGRect(x: 0.0, y: topEdgeY, width: layout.size.width, height: max(0.0, bottomEdgeY - topEdgeY))
transition.updateFrame(node: self.transitionContainerNode, frame: transitionContainerFrame) transition.updateFrame(node: self.transitionContainerNode, frame: transitionContainerFrame)
transition.updateFrame(view: self.transitionMaskView, frame: CGRect(x: 0.0, y: 0.0, width: transitionContainerFrame.width, height: transitionContainerFrame.height)) transition.updateFrame(view: self.transitionMaskView, frame: CGRect(x: 0.0, y: 0.0, width: transitionContainerFrame.width, height: transitionContainerFrame.height))
let updateMaskLayers = { let updateMaskLayers = {
transition.updateFrame(layer: self.transitionMaskTopFillLayer, frame: CGRect(x: 0.0, y: 0.0, width: transitionContainerFrame.width, height: topPanelFrame.height)) transition.updateFrame(layer: self.transitionMaskTopFillLayer, frame: CGRect(x: 0.0, y: 0.0, width: transitionContainerFrame.width, height: topPanelFrame.maxY))
transition.updateFrame(layer: self.transitionMaskFillLayer, frame: CGRect(x: 0.0, y: topPanelFrame.height, width: transitionContainerFrame.width, height: transitionContainerFrame.height - bottomGradientHeight - additionalMaskHeight - topPanelFrame.height)) transition.updateFrame(layer: self.transitionMaskFillLayer, frame: CGRect(x: 0.0, y: topPanelFrame.maxY, width: transitionContainerFrame.width, height: bottomGradientFrame.minY - topPanelFrame.maxY))
transition.updateFrame(layer: self.transitionMaskGradientLayer, frame: CGRect(x: 0.0, y: transitionContainerFrame.height - bottomGradientHeight - additionalMaskHeight, width: transitionContainerFrame.width, height: bottomGradientHeight)) transition.updateFrame(layer: self.transitionMaskGradientLayer, frame: CGRect(x: 0.0, y: bottomGradientFrame.minY, width: transitionContainerFrame.width, height: bottomGradientFrame.height))
transition.updateFrame(layer: self.transitionMaskBottomFillLayer, frame: CGRect(x: 0.0, y: transitionContainerFrame.height - bottomGradientHeight - additionalMaskHeight, width: transitionContainerFrame.width, height: bottomGradientHeight + additionalMaskHeight)) transition.updateFrame(layer: self.transitionMaskBottomFillLayer, frame: CGRect(x: 0.0, y: bottomGradientFrame.minY, width: transitionContainerFrame.width, height: transitionContainerFrame.height - bottomGradientFrame.minY))
} }
if transition.isAnimated { if transition.isAnimated {
updateMaskLayers() updateMaskLayers()
@ -3204,15 +3224,15 @@ public final class VoiceChatController: ViewController {
} }
bottomEdgeInset = 154.0 bottomEdgeInset = 154.0
} }
transition.updateAlpha(node: self.bottomGradientNode, alpha: isFullscreen || self.isLandscape ? 0.0 : 1.0) transition.updateAlpha(node: self.bottomGradientNode, alpha: self.isLandscape ? 0.0 : 1.0)
let videoTopEdgeY = isLandscape ? 0.0 : layoutTopInset let videoTopEdgeY = isLandscape ? 0.0 : layoutTopInset
let videoBottomEdgeY = self.isLandscape ? layout.size.height : layout.size.height - layout.intrinsicInsets.bottom - 84.0 let videoBottomEdgeY = self.isLandscape ? layout.size.height : layout.size.height - layout.intrinsicInsets.bottom - 84.0
let videoFrame = CGRect(x: 0.0, y: videoTopEdgeY, width: isLandscape ? layout.size.width - layout.safeInsets.right - 84.0 : layout.size.width, height: videoBottomEdgeY - videoTopEdgeY) let videoFrame = CGRect(x: 0.0, y: videoTopEdgeY, width: isLandscape ? layout.size.width - layout.safeInsets.right - 84.0 : layout.size.width, height: videoBottomEdgeY - videoTopEdgeY)
transition.updateFrame(node: self.mainStageContainerNode, frame: videoFrame) transition.updateFrame(node: self.mainStageContainerNode, frame: CGRect(origin: CGPoint(), size: layout.size))
transition.updateFrame(node: self.mainStageBackgroundNode, frame: CGRect(origin: CGPoint(), size: videoFrame.size)) transition.updateFrame(node: self.mainStageBackgroundNode, frame: videoFrame)
if !self.mainStageNode.animating { if !self.mainStageNode.animating {
transition.updateFrame(node: self.mainStageNode, frame: CGRect(origin: CGPoint(), size: videoFrame.size)) transition.updateFrame(node: self.mainStageNode, frame: videoFrame)
} }
self.mainStageNode.update(size: videoFrame.size, sideInset: layout.safeInsets.left, bottomInset: bottomInset, isLandscape: self.isLandscape, transition: transition) self.mainStageNode.update(size: videoFrame.size, sideInset: layout.safeInsets.left, bottomInset: bottomInset, isLandscape: self.isLandscape, transition: transition)
@ -4030,7 +4050,7 @@ public final class VoiceChatController: ViewController {
size.width = floor(min(size.width, size.height) * 0.5) size.width = floor(min(size.width, size.height) * 0.5)
} }
let bottomPanelHeight = self.isLandscape ? layout.intrinsicInsets.bottom : self.effectiveBottomAreaHeight + layout.intrinsicInsets.bottom let bottomPanelHeight = self.isLandscape ? layout.intrinsicInsets.bottom : bottomAreaHeight + layout.intrinsicInsets.bottom
let layoutTopInset: CGFloat = max(layout.statusBarHeight ?? 0.0, layout.safeInsets.top) let layoutTopInset: CGFloat = max(layout.statusBarHeight ?? 0.0, layout.safeInsets.top)
let listTopInset = layoutTopInset + topPanelHeight let listTopInset = layoutTopInset + topPanelHeight
let listSize = CGSize(width: size.width, height: layout.size.height - listTopInset - bottomPanelHeight + bottomGradientHeight) let listSize = CGSize(width: size.width, height: layout.size.height - listTopInset - bottomPanelHeight + bottomGradientHeight)
@ -4085,6 +4105,7 @@ public final class VoiceChatController: ViewController {
disableAnimation = true disableAnimation = true
} }
let speakingPeersUpdated = self.currentSpeakingPeers != speakingPeers
self.currentCallMembers = callMembers self.currentCallMembers = callMembers
self.currentSpeakingPeers = speakingPeers self.currentSpeakingPeers = speakingPeers
self.currentInvitedPeers = invitedPeers self.currentInvitedPeers = invitedPeers
@ -4253,6 +4274,9 @@ public final class VoiceChatController: ViewController {
if let tileItem = tileByVideoEndpoint[tileVideoEndpoint] { if let tileItem = tileByVideoEndpoint[tileVideoEndpoint] {
tileItems.append(tileItem) tileItems.append(tileItem)
if let fullscreenEntry = entryByPeerId[tileItem.peer.id] { if let fullscreenEntry = entryByPeerId[tileItem.peer.id] {
if processedFullscreenPeerIds.contains(tileItem.peer.id) {
continue
}
fullscreenEntries.append(.peer(fullscreenEntry, fullscreenIndex)) fullscreenEntries.append(.peer(fullscreenEntry, fullscreenIndex))
processedFullscreenPeerIds.insert(fullscreenEntry.peer.id) processedFullscreenPeerIds.insert(fullscreenEntry.peer.id)
fullscreenIndex += 1 fullscreenIndex += 1
@ -4360,6 +4384,26 @@ public final class VoiceChatController: ViewController {
let fullscreenTransition = self.preparedFullscreenTransition(from: previousFullscreenEntries, to: fullscreenEntries, isLoading: false, isEmpty: false, canInvite: canInvite, crossFade: false, animated: true, context: self.context, presentationData: presentationData, interaction: self.itemInteraction!) let fullscreenTransition = self.preparedFullscreenTransition(from: previousFullscreenEntries, to: fullscreenEntries, isLoading: false, isEmpty: false, canInvite: canInvite, crossFade: false, animated: true, context: self.context, presentationData: presentationData, interaction: self.itemInteraction!)
self.enqueueFullscreenTransition(fullscreenTransition) self.enqueueFullscreenTransition(fullscreenTransition)
if case .fullscreen = self.displayMode, !self.mainStageNode.animating {
if speakingPeersUpdated {
var speakingPeers = speakingPeers
var updatedSpeakers: [PeerId] = []
for peerId in self.currentSpeakers {
if speakingPeers.contains(peerId) {
updatedSpeakers.append(peerId)
speakingPeers.remove(peerId)
}
}
for peerId in Array(speakingPeers) {
updatedSpeakers.append(peerId)
}
self.currentSpeakers = updatedSpeakers
self.mainStageNode.update(speakingPeerId: updatedSpeakers.first)
}
} else {
self.mainStageNode.update(speakingPeerId: nil)
}
} }
private func callStateDidReset() { private func callStateDidReset() {
@ -4549,6 +4593,19 @@ public final class VoiceChatController: ViewController {
self.panGestureArguments = (topInset, 0.0) self.panGestureArguments = (topInset, 0.0)
self.controller?.dismissAllTooltips() self.controller?.dismissAllTooltips()
if case .fullscreen = self.effectiveDisplayMode {
self.mainStageBackgroundNode.alpha = 0.0
self.mainStageBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.4)
self.mainStageNode.setControlsHidden(true, animated: true)
self.fullscreenListNode.alpha = 0.0
self.fullscreenListNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, completion: { [weak self] _ in
self?.attachTileVideos()
})
self.contentContainer.insertSubnode(self.mainStageContainerNode, aboveSubnode: self.bottomPanelNode)
}
case .changed: case .changed:
var translation = recognizer.translation(in: self.contentContainer.view).y var translation = recognizer.translation(in: self.contentContainer.view).y
if self.isScheduled && translation < 0.0 { if self.isScheduled && translation < 0.0 {
@ -4591,9 +4648,14 @@ public final class VoiceChatController: ViewController {
self.listNode.scroller.panGestureRecognizer.setTranslation(CGPoint(), in: self.listNode.scroller) self.listNode.scroller.panGestureRecognizer.setTranslation(CGPoint(), in: self.listNode.scroller)
} }
} }
case let .fullscreen(controlsHidden): case .fullscreen:
break var bounds = self.mainStageContainerNode.bounds
bounds.origin.y = -translation
self.mainStageContainerNode.bounds = bounds
var backgroundFrame = self.mainStageNode.frame
backgroundFrame.origin.y += -translation
self.mainStageBackgroundNode.frame = backgroundFrame
} }
if let (layout, navigationHeight) = self.validLayout { if let (layout, navigationHeight) = self.validLayout {
@ -4636,7 +4698,33 @@ public final class VoiceChatController: ViewController {
topInset = self.listNode.frame.height topInset = self.listNode.frame.height
} }
if case .modal(true, _) = self.effectiveDisplayMode { if case .fullscreen = self.effectiveDisplayMode {
self.panGestureArguments = nil
if abs(translation.y) > 100.0 || abs(velocity.y) > 300.0 {
self.currentForcedSpeaker = nil
self.updateDisplayMode(.modal(isExpanded: true, isFilled: true), fromPan: true)
self.effectiveSpeaker = nil
} else {
self.mainStageBackgroundNode.alpha = 1.0
self.mainStageBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, completion: { [weak self] _ in
self?.attachFullscreenVideos()
})
self.mainStageNode.setControlsHidden(false, animated: true)
self.fullscreenListNode.alpha = 1.0
self.fullscreenListNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: 0.15)
var bounds = self.mainStageContainerNode.bounds
let previousBounds = bounds
bounds.origin.y = 0.0
self.mainStageContainerNode.bounds = bounds
self.mainStageContainerNode.layer.animateBounds(from: previousBounds, to: self.mainStageContainerNode.bounds, duration: 0.2, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak self] _ in
if let strongSelf = self {
strongSelf.contentContainer.insertSubnode(strongSelf.mainStageContainerNode, belowSubnode: strongSelf.transitionContainerNode)
}
})
}
} else if case .modal(true, _) = self.effectiveDisplayMode {
self.panGestureArguments = nil self.panGestureArguments = nil
if velocity.y > 300.0 || offset > topInset / 2.0 { if velocity.y > 300.0 || offset > topInset / 2.0 {
self.displayMode = .modal(isExpanded: false, isFilled: false) self.displayMode = .modal(isExpanded: false, isFilled: false)
@ -5070,7 +5158,52 @@ public final class VoiceChatController: ViewController {
return self.isScheduling || self.callState?.scheduleTimestamp != nil return self.isScheduling || self.callState?.scheduleTimestamp != nil
} }
private func updateDisplayMode(_ displayMode: DisplayMode) { private func attachFullscreenVideos() {
var verticalItemNodes: [PeerId: ASDisplayNode] = [:]
self.listNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? VoiceChatTilesGridItemNode {
for tileNode in itemNode.tileNodes {
if let item = tileNode.item {
verticalItemNodes[item.peer.id] = tileNode
}
if tileNode.item?.peer.id == self.effectiveSpeaker?.0 {
tileNode.isHidden = false
}
}
}
}
self.fullscreenListNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? VoiceChatFullscreenParticipantItemNode, let item = itemNode.item, let otherItemNode = verticalItemNodes[item.peer.id] {
itemNode.animateTransitionIn(from: otherItemNode, containerNode: self.transitionContainerNode, transition: .immediate, animate: false)
}
}
}
private func attachTileVideos() {
var fullscreenItemNodes: [PeerId: VoiceChatFullscreenParticipantItemNode] = [:]
self.fullscreenListNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? VoiceChatFullscreenParticipantItemNode, let item = itemNode.item {
fullscreenItemNodes[item.peer.id] = itemNode
}
}
self.listNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? VoiceChatTilesGridItemNode {
for tileNode in itemNode.tileNodes {
if let item = tileNode.item, let otherItemNode = fullscreenItemNodes[item.peer.id] {
tileNode.animateTransitionIn(from: otherItemNode, containerNode: self.transitionContainerNode, transition: .immediate, animate: false)
if tileNode.item?.peer.id == self.effectiveSpeaker?.0 {
tileNode.isHidden = true
}
}
}
}
}
}
private func updateDisplayMode(_ displayMode: DisplayMode, fromPan: Bool = false) {
guard !self.animatingExpansion else { guard !self.animatingExpansion else {
return return
} }
@ -5093,6 +5226,7 @@ public final class VoiceChatController: ViewController {
if case .modal = previousDisplayMode, case .fullscreen = self.displayMode { if case .modal = previousDisplayMode, case .fullscreen = self.displayMode {
self.fullscreenListNode.isHidden = false self.fullscreenListNode.isHidden = false
self.fullscreenListNode.alpha = 1.0
self.updateDecorationsLayout(transition: .immediate) self.updateDecorationsLayout(transition: .immediate)
@ -5158,29 +5292,25 @@ public final class VoiceChatController: ViewController {
self.updateDecorationsLayout(transition: transition) self.updateDecorationsLayout(transition: transition)
} }
} }
// if let (peerId, _) = minimalVisiblePeerid { let effectiveSpeakerPeerId = self.effectiveSpeaker?.0
let effectiveSpeakerPeerId = self.effectiveSpeaker?.0 var index = 0
var index = 0 for item in self.currentFullscreenEntries {
for item in self.currentFullscreenEntries { if case let .peer(entry, _) = item, entry.peer.id == effectiveSpeakerPeerId {
if case let .peer(entry, _) = item, entry.peer.id == effectiveSpeakerPeerId { break
break
} else {
index += 1
}
}
let position: ListViewScrollPosition
if index > self.currentFullscreenEntries.count - 3 {
index = self.currentFullscreenEntries.count - 1
position = .bottom(0.0)
} else { } else {
position = .center(.bottom) index += 1
} }
self.fullscreenListNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: index, position: position, animated: false, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }
completion() let position: ListViewScrollPosition
}) if index > self.currentFullscreenEntries.count - 3 {
// } else { index = self.currentFullscreenEntries.count - 1
// completion() position = .bottom(0.0)
// } } else {
position = .center(.bottom)
}
self.fullscreenListNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: index, position: position, animated: false, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in
completion()
})
} else if case .fullscreen = previousDisplayMode, case .modal = self.displayMode { } else if case .fullscreen = previousDisplayMode, case .modal = self.displayMode {
var minimalVisiblePeerid: (PeerId, CGFloat)? var minimalVisiblePeerid: (PeerId, CGFloat)?
var fullscreenItemNodes: [PeerId: VoiceChatFullscreenParticipantItemNode] = [:] var fullscreenItemNodes: [PeerId: VoiceChatFullscreenParticipantItemNode] = [:]
@ -5210,7 +5340,9 @@ public final class VoiceChatController: ViewController {
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, let otherItemNode = fullscreenItemNodes[item.peer.id] { if let item = tileNode.item, let otherItemNode = fullscreenItemNodes[item.peer.id] {
tileNode.animateTransitionIn(from: otherItemNode, containerNode: self.transitionContainerNode, transition: transition, animate: item.peer.id != effectiveSpeakerPeerId) 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 { if item.peer.id == effectiveSpeakerPeerId {
targetTileNode = tileNode targetTileNode = tileNode
@ -5218,29 +5350,36 @@ public final class VoiceChatController: ViewController {
} }
} }
} else if let itemNode = itemNode as? VoiceChatParticipantItemNode, let item = itemNode.item, let otherItemNode = fullscreenItemNodes[item.peer.id] { } else if let itemNode = itemNode as? VoiceChatParticipantItemNode, let item = itemNode.item, let otherItemNode = fullscreenItemNodes[item.peer.id] {
itemNode.animateTransitionIn(from: otherItemNode, containerNode: self.transitionContainerNode, transition: transition) if !fromPan {
itemNode.animateTransitionIn(from: otherItemNode, containerNode: self.transitionContainerNode, transition: transition)
}
} }
} }
self.mainStageNode.animateTransitionOut(to: targetTileNode, transition: transition, completion: { [weak self] in let transitionOffset = -self.mainStageContainerNode.bounds.minY
if transitionOffset.isZero {
self.mainStageBackgroundNode.alpha = 0.0
self.mainStageBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
}
self.mainStageNode.animateTransitionOut(to: targetTileNode, offset: transitionOffset, transition: transition, completion: { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
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.fullscreenListNode.isHidden = true strongSelf.fullscreenListNode.isHidden = true
strongSelf.mainStageContainerNode.isHidden = true strongSelf.mainStageContainerNode.isHidden = true
strongSelf.mainStageContainerNode.addSubnode(strongSelf.mainStageNode) strongSelf.mainStageContainerNode.addSubnode(strongSelf.mainStageNode)
var bounds = strongSelf.mainStageContainerNode.bounds
bounds.origin.y = 0.0
strongSelf.mainStageContainerNode.bounds = bounds
strongSelf.contentContainer.insertSubnode(strongSelf.mainStageContainerNode, belowSubnode: strongSelf.transitionContainerNode)
}) })
self.mainStageBackgroundNode.alpha = 0.0 self.transitionMaskTopFillLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15)
self.mainStageBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
self.transitionMaskTopFillLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak self] _ in
Queue.mainQueue().after(0.2) {
self?.transitionMaskTopFillLayer.removeAllAnimations()
}
})
if let (layout, navigationHeight) = self.validLayout { if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: transition) self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: transition)

View File

@ -372,8 +372,10 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
self.contentWrapperNode.layer.animateScale(from: 0.001, to: 1.0, duration: duration, timingFunction: timingFunction) self.contentWrapperNode.layer.animateScale(from: 0.001, to: 1.0, duration: duration, timingFunction: timingFunction)
self.contentWrapperNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration, timingFunction: timingFunction) self.contentWrapperNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration, timingFunction: timingFunction)
} else if !initialAnimate { } else if !initialAnimate {
self.contextSourceNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration, timingFunction: timingFunction) if case .animated = transition {
self.contextSourceNode.layer.animateScale(from: 0.0, to: 1.0, duration: duration, timingFunction: timingFunction) self.contextSourceNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration, timingFunction: timingFunction)
self.contextSourceNode.layer.animateScale(from: 0.0, to: 1.0, duration: duration, timingFunction: timingFunction)
}
} }
} else if let sourceNode = sourceNode as? VoiceChatParticipantItemNode, let _ = sourceNode.item { } else if let sourceNode = sourceNode as? VoiceChatParticipantItemNode, let _ = sourceNode.item {
var startContainerPosition = sourceNode.avatarNode.view.convert(sourceNode.avatarNode.bounds, to: containerNode.view).center var startContainerPosition = sourceNode.avatarNode.view.convert(sourceNode.avatarNode.bounds, to: containerNode.view).center

View File

@ -16,6 +16,8 @@ import AppBundle
import PresentationDataUtils import PresentationDataUtils
import AvatarNode import AvatarNode
import AudioBlob import AudioBlob
import TextFormat
import Markdown
private let backArrowImage = NavigationBarTheme.generateBackArrowImage(color: .white) private let backArrowImage = NavigationBarTheme.generateBackArrowImage(color: .white)
private let backgroundCornerRadius: CGFloat = 11.0 private let backgroundCornerRadius: CGFloat = 11.0
@ -63,6 +65,7 @@ final class VoiceChatMainStageNode: ASDisplayNode {
var tapped: (() -> Void)? var tapped: (() -> Void)?
var back: (() -> Void)? var back: (() -> Void)?
var togglePin: (() -> Void)? var togglePin: (() -> Void)?
var switchTo: ((PeerId) -> Void)?
var getAudioLevel: ((PeerId) -> Signal<Float, NoError>)? var getAudioLevel: ((PeerId) -> Signal<Float, NoError>)?
private let videoReadyDisposable = MetaDisposable() private let videoReadyDisposable = MetaDisposable()
@ -138,7 +141,9 @@ final class VoiceChatMainStageNode: ASDisplayNode {
self.microphoneNode.alpha = 0.0 self.microphoneNode.alpha = 0.0
self.speakingContainerNode = ASDisplayNode() self.speakingContainerNode = ASDisplayNode()
self.speakingContainerNode.alpha = 0.0
self.speakingContainerNode.cornerRadius = 19.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()
@ -166,6 +171,11 @@ final class VoiceChatMainStageNode: ASDisplayNode {
self.headerNode.addSubnode(self.pinButtonTitleNode) self.headerNode.addSubnode(self.pinButtonTitleNode)
self.headerNode.addSubnode(self.pinButtonNode) self.headerNode.addSubnode(self.pinButtonNode)
self.addSubnode(self.speakingContainerNode)
self.speakingContainerNode.addSubnode(self.speakingAvatarNode)
self.speakingContainerNode.addSubnode(self.speakingTitleNode)
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.backButtonNode.setTitle(presentationData.strings.Common_Back, with: Font.regular(17.0), with: .white, for: []) self.backButtonNode.setTitle(presentationData.strings.Common_Back, with: Font.regular(17.0), with: .white, for: [])
self.backButtonNode.hitTestSlop = UIEdgeInsets(top: -8.0, left: -20.0, bottom: -8.0, right: -8.0) self.backButtonNode.hitTestSlop = UIEdgeInsets(top: -8.0, left: -20.0, bottom: -8.0, right: -8.0)
@ -217,16 +227,25 @@ final class VoiceChatMainStageNode: ASDisplayNode {
super.didLoad() super.didLoad()
let speakingEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark)) let speakingEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
self.speakingContainerNode.view.addSubview(speakingEffectView) self.speakingContainerNode.view.insertSubview(speakingEffectView, at: 0)
self.speakingEffectView = speakingEffectView self.speakingEffectView = speakingEffectView
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tap))) self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tap)))
self.speakingContainerNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.speakingTap)))
} }
@objc private func tap() { @objc private func tap() {
self.tapped?() self.tapped?()
} }
@objc private func speakingTap() {
if let peerId = self.effectiveSpeakingPeerId {
self.switchTo?(peerId)
self.update(speakingPeerId: nil)
}
}
@objc private func backPressed() { @objc private func backPressed() {
self.back?() self.back?()
} }
@ -278,7 +297,7 @@ final class VoiceChatMainStageNode: ASDisplayNode {
}) })
} }
func animateTransitionOut(to targetNode: ASDisplayNode?, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) { func animateTransitionOut(to targetNode: ASDisplayNode?, offset: CGFloat, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
guard let (_, sideInset, bottomInset, isLandscape) = self.validLayout else { guard let (_, sideInset, bottomInset, isLandscape) = self.validLayout else {
return return
} }
@ -289,6 +308,7 @@ final class VoiceChatMainStageNode: ASDisplayNode {
alphaTransition.updateAlpha(node: self.titleNode, alpha: 0.0) alphaTransition.updateAlpha(node: self.titleNode, alpha: 0.0)
alphaTransition.updateAlpha(node: self.microphoneNode, alpha: 0.0) alphaTransition.updateAlpha(node: self.microphoneNode, alpha: 0.0)
alphaTransition.updateAlpha(node: self.headerNode, alpha: 0.0) alphaTransition.updateAlpha(node: self.headerNode, alpha: 0.0)
alphaTransition.updateAlpha(node: self.bottomFadeNode, alpha: 1.0)
guard let targetNode = targetNode as? VoiceChatTileItemNode, let _ = targetNode.item else { guard let targetNode = targetNode as? VoiceChatTileItemNode, let _ = targetNode.item else {
completion() completion()
@ -296,10 +316,13 @@ final class VoiceChatMainStageNode: ASDisplayNode {
} }
targetNode.isHidden = false targetNode.isHidden = false
targetNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) if offset.isZero {
targetNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
}
self.animatingOut = true self.animatingOut = true
let initialFrame = self.frame let originalFrame = self.frame
let initialFrame = originalFrame.offsetBy(dx: 0.0, dy: offset)
let targetFrame = targetNode.view.convert(targetNode.bounds, to: self.supernode?.view) let targetFrame = targetNode.view.convert(targetNode.bounds, to: self.supernode?.view)
self.currentVideoNode?.keepBackdropSize = true self.currentVideoNode?.keepBackdropSize = true
@ -317,6 +340,7 @@ final class VoiceChatMainStageNode: ASDisplayNode {
targetNode.alpha = 0.0 targetNode.alpha = 0.0
self.frame = initialFrame
self.update(size: targetFrame.size, sideInset: sideInset, bottomInset: bottomInset, isLandscape: isLandscape, force: true, transition: transition) self.update(size: targetFrame.size, sideInset: sideInset, bottomInset: bottomInset, isLandscape: isLandscape, force: true, transition: transition)
transition.updateFrame(node: self, frame: targetFrame, completion: { [weak self] _ in transition.updateFrame(node: self, frame: targetFrame, completion: { [weak self] _ in
if let strongSelf = self { if let strongSelf = self {
@ -325,20 +349,25 @@ final class VoiceChatMainStageNode: ASDisplayNode {
infoView?.removeFromSuperview() infoView?.removeFromSuperview()
targetNode.alpha = 1.0 targetNode.alpha = 1.0
strongSelf.animatingOut = false strongSelf.animatingOut = false
strongSelf.frame = initialFrame strongSelf.frame = originalFrame
strongSelf.update(size: initialFrame.size, sideInset: sideInset, bottomInset: bottomInset, isLandscape: isLandscape, transition: .immediate) strongSelf.update(size: initialFrame.size, sideInset: sideInset, bottomInset: bottomInset, isLandscape: isLandscape, transition: .immediate)
} }
}) })
self.update(speakingPeerId: nil)
} }
private var effectiveSpeakingPeerId: PeerId?
private var speakingPeerId: PeerId? private func updateSpeakingPeer() {
func update(speakingPeerId: PeerId?) { var effectiveSpeakingPeerId = self.speakingPeerId
guard self.speakingPeerId != speakingPeerId else { if let peerId = effectiveSpeakingPeerId, self.visiblePeerIds.contains(peerId) || self.currentPeer?.0 == peerId || self.callState?.myPeerId == peerId {
effectiveSpeakingPeerId = nil
}
guard self.effectiveSpeakingPeerId != effectiveSpeakingPeerId else {
return return
} }
self.effectiveSpeakingPeerId = effectiveSpeakingPeerId
if let getAudioLevel = self.getAudioLevel, let peerId = speakingPeerId { if let getAudioLevel = self.getAudioLevel, let peerId = effectiveSpeakingPeerId {
let wavesColor = UIColor(rgb: 0x34c759) let wavesColor = UIColor(rgb: 0x34c759)
if let speakingAudioLevelView = self.speakingAudioLevelView { if let speakingAudioLevelView = self.speakingAudioLevelView {
speakingAudioLevelView.removeFromSuperview() speakingAudioLevelView.removeFromSuperview()
@ -353,7 +382,11 @@ final class VoiceChatMainStageNode: ASDisplayNode {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
strongSelf.speakingAvatarNode.setPeer(context: strongSelf.context, theme: presentationData.theme, peer: peer) strongSelf.speakingAvatarNode.setPeer(context: strongSelf.context, theme: presentationData.theme, peer: peer)
strongSelf.speakingTitleNode.attributedText = NSAttributedString(string: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), font: Font.regular(14.0), textColor: .white)
let bodyAttributes = MarkdownAttributeSet(font: Font.regular(15.0), textColor: .white, additionalAttributes: [:])
let boldAttributes = MarkdownAttributeSet(font: Font.semibold(15.0), textColor: .white, additionalAttributes: [:])
let attributedText = addAttributesToStringWithRanges(presentationData.strings.VoiceChat_ParticipantIsSpeaking(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)), body: bodyAttributes, argumentAttributes: [0: boldAttributes])
strongSelf.speakingTitleNode.attributedText = attributedText
strongSelf.speakingContainerNode.alpha = 0.0 strongSelf.speakingContainerNode.alpha = 0.0
@ -364,48 +397,46 @@ final class VoiceChatMainStageNode: ASDisplayNode {
strongSelf.speakingContainerNode.alpha = 1.0 strongSelf.speakingContainerNode.alpha = 1.0
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 = self.speakingAvatarNode.frame.insetBy(dx: -14.0, dy: -14.0) let blobFrame = strongSelf.speakingAvatarNode.frame.insetBy(dx: -7.0, dy: -7.0)
self.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 {
return return
}
if strongSelf.speakingAudioLevelView == nil, value > 0.0 {
let audioLevelView = VoiceBlobView(
frame: blobFrame,
maxLevel: 1.5,
smallBlobRange: (0, 0),
mediumBlobRange: (0.69, 0.87),
bigBlobRange: (0.71, 1.0)
)
audioLevelView.isHidden = strongSelf.currentPeer?.1 != nil
audioLevelView.setColor(wavesColor)
audioLevelView.alpha = 1.0
strongSelf.speakingAudioLevelView = audioLevelView
strongSelf.speakingContainerNode.view.insertSubview(audioLevelView, belowSubview: strongSelf.speakingAvatarNode.view)
}
let level = min(1.5, max(0.0, CGFloat(value)))
if let audioLevelView = strongSelf.speakingAudioLevelView {
audioLevelView.updateLevel(CGFloat(value))
let avatarScale: CGFloat
if value > 0.02 {
audioLevelView.startAnimating()
avatarScale = 1.03 + level * 0.13
audioLevelView.setColor(wavesColor, animated: true)
} else {
avatarScale = 1.0
} }
let transition: ContainedViewLayoutTransition = .animated(duration: 0.15, curve: .easeInOut) if strongSelf.speakingAudioLevelView == nil, value > 0.0 {
transition.updateTransformScale(node: strongSelf.avatarNode, scale: avatarScale, beginWithCurrentState: true) let audioLevelView = VoiceBlobView(
} frame: blobFrame,
maxLevel: 1.5,
smallBlobRange: (0, 0),
mediumBlobRange: (0.69, 0.87),
bigBlobRange: (0.71, 1.0)
)
audioLevelView.setColor(wavesColor)
audioLevelView.alpha = 1.0
strongSelf.speakingAudioLevelView = audioLevelView
strongSelf.speakingContainerNode.view.insertSubview(audioLevelView, belowSubview: strongSelf.speakingAvatarNode.view)
}
let level = min(1.5, max(0.0, CGFloat(value)))
if let audioLevelView = strongSelf.speakingAudioLevelView {
audioLevelView.updateLevel(CGFloat(value))
let avatarScale: CGFloat
if value > 0.02 {
audioLevelView.startAnimating()
avatarScale = 1.03 + level * 0.13
audioLevelView.setColor(wavesColor, animated: true)
} else {
avatarScale = 1.0
}
let transition: ContainedViewLayoutTransition = .animated(duration: 0.15, curve: .easeInOut)
transition.updateTransformScale(node: strongSelf.speakingAvatarNode, scale: avatarScale, beginWithCurrentState: true)
}
}))
})) }))
} else { } else {
self.speakingPeerDisposable.set(nil) self.speakingPeerDisposable.set(nil)
@ -426,6 +457,18 @@ final class VoiceChatMainStageNode: ASDisplayNode {
} }
} }
private var visiblePeerIds = Set<PeerId>()
func update(visiblePeerIds: Set<PeerId>) {
self.visiblePeerIds = visiblePeerIds
self.updateSpeakingPeer()
}
private var speakingPeerId: PeerId?
func update(speakingPeerId: PeerId?) {
self.speakingPeerId = speakingPeerId
self.updateSpeakingPeer()
}
func update(peerEntry: VoiceChatPeerEntry, pinned: Bool) { func update(peerEntry: VoiceChatPeerEntry, pinned: Bool) {
let previousPeerEntry = self.currentPeerEntry let previousPeerEntry = self.currentPeerEntry
self.currentPeerEntry = peerEntry self.currentPeerEntry = peerEntry
@ -543,6 +586,8 @@ final class VoiceChatMainStageNode: ASDisplayNode {
} }
self.currentPeer = peer self.currentPeer = peer
self.updateSpeakingPeer()
if let (_, endpointId) = peer { if let (_, endpointId) = peer {
if endpointId != previousPeer?.1 { if endpointId != previousPeer?.1 {
if let endpointId = endpointId { if let endpointId = endpointId {
@ -612,6 +657,17 @@ final class VoiceChatMainStageNode: ASDisplayNode {
} }
} }
func setControlsHidden(_ hidden: Bool, animated: Bool) {
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.topFadeNode, alpha: hidden ? 0.0 : 1.0)
transition.updateAlpha(node: self.titleNode, alpha: hidden ? 0.0 : 1.0)
transition.updateAlpha(node: self.microphoneNode, alpha: hidden ? 0.0 : 1.0)
transition.updateAlpha(node: self.bottomFadeNode, alpha: hidden ? 0.0 : 1.0)
transition.updateAlpha(node: self.bottomFillNode, alpha: hidden ? 0.0 : 1.0)
}
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) {
self.validLayout = (size, sideInset, bottomInset, isLandscape) self.validLayout = (size, sideInset, bottomInset, isLandscape)
@ -679,6 +735,15 @@ final class VoiceChatMainStageNode: ASDisplayNode {
} }
transition.updateFrame(node: self.headerNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: 64.0))) transition.updateFrame(node: self.headerNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: 64.0)))
let speakingInset: CGFloat = 16.0
let speakingAvatarSize = CGSize(width: 30.0, height: 30.0)
let speakingTitleSize = self.speakingTitleNode.updateLayout(CGSize(width: 220.0, height: CGFloat.greatestFiniteMagnitude))
let speakingContainerSize = CGSize(width: speakingTitleSize.width + speakingInset * 2.0 + speakingAvatarSize.width, height: 38.0)
self.speakingEffectView?.frame = CGRect(origin: CGPoint(), size: speakingContainerSize)
self.speakingAvatarNode.frame = CGRect(origin: CGPoint(x: 4.0, y: 4.0), size: speakingAvatarSize)
self.speakingTitleNode.frame = CGRect(origin: CGPoint(x: 4.0 + speakingAvatarSize.width + 14.0, y: floorToScreenPixels((38.0 - speakingTitleSize.height) / 2.0)), size: speakingTitleSize)
transition.updateFrame(node: self.speakingContainerNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - speakingContainerSize.width) / 2.0), y: size.height - bottomInset - speakingContainerSize.height - 44.0), size: speakingContainerSize))
} }
func flipVideoIfNeeded() { func flipVideoIfNeeded() {

View File

@ -682,8 +682,14 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
let startContainerAvatarPosition = sourceNode.avatarNode.view.convert(sourceNode.avatarNode.bounds, to: containerNode.view).center let startContainerAvatarPosition = sourceNode.avatarNode.view.convert(sourceNode.avatarNode.bounds, to: containerNode.view).center
var animate = true var animate = true
if startContainerAvatarPosition.x < -tileSize.width || startContainerAvatarPosition.x > containerNode.frame.width + tileSize.width { if containerNode.frame.width > containerNode.frame.height {
animate = false if startContainerAvatarPosition.y < -tileSize.height || startContainerAvatarPosition.y > containerNode.frame.height + tileSize.height {
animate = false
}
} else {
if startContainerAvatarPosition.x < -tileSize.width || startContainerAvatarPosition.x > containerNode.frame.width + tileSize.width {
animate = false
}
} }
if animate { if animate {
sourceNode.avatarNode.alpha = 0.0 sourceNode.avatarNode.alpha = 0.0

View File

@ -231,6 +231,7 @@ final class VoiceChatPeerProfileNode: ASDisplayNode {
if let snapshotView = sourceNode.infoNode.view.snapshotView(afterScreenUpdates: false) { if let snapshotView = sourceNode.infoNode.view.snapshotView(afterScreenUpdates: false) {
self.videoFadeNode.image = tileFadeImage self.videoFadeNode.image = tileFadeImage
self.videoFadeNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
self.videoFadeNode.frame = CGRect(x: 0.0, y: sourceRect.height - sourceNode.fadeNode.frame.height, width: sourceRect.width, height: sourceNode.fadeNode.frame.height) self.videoFadeNode.frame = CGRect(x: 0.0, y: sourceRect.height - sourceNode.fadeNode.frame.height, width: sourceRect.width, height: sourceNode.fadeNode.frame.height)
self.insertSubnode(self.videoFadeNode, aboveSubnode: sourceNode.videoContainerNode) self.insertSubnode(self.videoFadeNode, aboveSubnode: sourceNode.videoContainerNode)