Merge commit '4efe0468c5b051df8706d83cc1f219add75a1a7d'

This commit is contained in:
Ali 2021-05-22 16:29:25 +04:00
commit 3ae13c31df
9 changed files with 232 additions and 155 deletions

View File

@ -114,6 +114,9 @@ public final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDeco
if !immediately {
mediumBlob.layer.animateScale(from: 0.75, to: 1, duration: 0.35, removeOnCompletion: false)
bigBlob.layer.animateScale(from: 0.75, to: 1, duration: 0.35, removeOnCompletion: false)
} else {
mediumBlob.layer.removeAllAnimations()
bigBlob.layer.removeAllAnimations()
}
updateBlobsState()

View File

@ -623,11 +623,13 @@ public final class AvatarNode: ASDisplayNode {
}
}
public func drawPeerAvatarLetters(context: CGContext, size: CGSize, font: UIFont, letters: [String], peerId: PeerId) {
public func drawPeerAvatarLetters(context: CGContext, size: CGSize, round: Bool = true, font: UIFont, letters: [String], peerId: PeerId) {
if round {
context.beginPath()
context.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height:
size.height))
context.clip()
}
let colorIndex: Int
if peerId.namespace == .max {

View File

@ -87,9 +87,9 @@ public func peerAvatarImageData(account: Account, peerReference: PeerReference?,
}
}
public func peerAvatarCompleteImage(account: Account, peer: Peer, size: CGSize, font: UIFont = avatarPlaceholderFont(size: 13.0), fullSize: Bool = false) -> Signal<UIImage?, NoError> {
public func peerAvatarCompleteImage(account: Account, peer: Peer, size: CGSize, round: Bool = true, font: UIFont = avatarPlaceholderFont(size: 13.0), drawLetters: Bool = true, fullSize: Bool = false) -> Signal<UIImage?, NoError> {
let iconSignal: Signal<UIImage?, NoError>
if let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer), authorOfMessage: nil, representation: peer.profileImageRepresentations.first, displayDimensions: size, inset: 0.0, emptyColor: nil, synchronousLoad: fullSize) {
if let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer), authorOfMessage: nil, representation: peer.profileImageRepresentations.first, displayDimensions: size, round: round, inset: 0.0, emptyColor: nil, synchronousLoad: fullSize) {
if fullSize, let fullSizeSignal = peerAvatarImage(account: account, peerReference: PeerReference(peer), authorOfMessage: nil, representation: peer.profileImageRepresentations.last, displayDimensions: size, emptyColor: nil, synchronousLoad: true) {
iconSignal = combineLatest(.single(nil) |> then(signal), .single(nil) |> then(fullSizeSignal))
|> mapToSignal { thumbnailImage, fullSizeImage -> Signal<UIImage?, NoError> in
@ -113,10 +113,13 @@ public func peerAvatarCompleteImage(account: Account, peer: Peer, size: CGSize,
if displayLetters.count == 2 && displayLetters[0].isSingleEmoji && displayLetters[1].isSingleEmoji {
displayLetters = [displayLetters[0]]
}
if !drawLetters {
displayLetters = []
}
iconSignal = Signal { subscriber in
let image = generateImage(size, rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width, height: size.height), font: font, letters: displayLetters, peerId: peerId)
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width, height: size.height), round: round, font: font, letters: displayLetters, peerId: peerId)
})?.withRenderingMode(.alwaysOriginal)
subscriber.putNext(image)

View File

@ -132,6 +132,14 @@ private final class CallVideoNode: ASDisplayNode {
self.isReadyTimer?.invalidate()
}
override func didLoad() {
super.didLoad()
if #available(iOS 13.0, *) {
self.layer.cornerCurve = .continuous
}
}
func animateRadialMask(from fromRect: CGRect, to toRect: CGRect) {
let maskLayer = CAShapeLayer()
maskLayer.frame = fromRect

View File

@ -222,9 +222,6 @@ private class CallControllerToastItemNode: ASDisplayNode {
self.clipNode = ASDisplayNode()
self.clipNode.clipsToBounds = true
self.clipNode.layer.cornerRadius = 14.0
if #available(iOS 13.0, *) {
self.clipNode.layer.cornerCurve = .continuous
}
self.effectView = UIVisualEffectView()
self.effectView.effect = UIBlurEffect(style: .light)
@ -248,6 +245,14 @@ private class CallControllerToastItemNode: ASDisplayNode {
self.clipNode.addSubnode(self.textNode)
}
override func didLoad() {
super.didLoad()
if #available(iOS 13.0, *) {
self.clipNode.layer.cornerCurve = .continuous
}
}
func update(width: CGFloat, content: Content, transition: ContainedViewLayoutTransition) -> CGFloat {
let inset: CGFloat = 30.0
let isNarrowScreen = width <= 320.0

View File

@ -723,6 +723,7 @@ public final class VoiceChatController: ViewController {
private var animatingAppearance = false
private var animatingButtonsSwap = false
private var panGestureArguments: (topInset: CGFloat, offset: CGFloat)?
private var isPanning = false
private var peer: Peer?
private var currentTitle: String = ""
@ -827,10 +828,6 @@ public final class VoiceChatController: ViewController {
}
}
private var effectiveDisplayMode: DisplayMode {
return self.displayMode
}
private var isExpanded: Bool {
switch self.displayMode {
case .modal(true, _), .fullscreen:
@ -1060,7 +1057,7 @@ public final class VoiceChatController: ViewController {
if peerId != strongSelf.currentDominantSpeaker?.0 {
strongSelf.currentDominantSpeaker = (peerId, CACurrentMediaTime())
}
strongSelf.updateMainVideo(waitForFullSize: false, updateMembers: true, force: true)
strongSelf.updateMainVideo(waitForFullSize: true, updateMembers: true, force: true)
}
}
}, openInvite: { [weak self] in
@ -1628,6 +1625,9 @@ public final class VoiceChatController: ViewController {
if ignore {
return nil
}
if !strongSelf.readyVideoNodes.contains(endpointId) {
return nil
}
for (listEndpointId, videoNode) in strongSelf.videoNodes {
if listEndpointId == endpointId {
return videoNode
@ -2005,18 +2005,6 @@ 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()
// })
}
}
@ -3111,7 +3099,7 @@ public final class VoiceChatController: ViewController {
}
private var isFullscreen: Bool {
switch self.effectiveDisplayMode {
switch self.displayMode {
case .fullscreen(_), .modal(_, true):
return true
default:
@ -3137,7 +3125,7 @@ public final class VoiceChatController: ViewController {
let layoutTopInset: CGFloat = max(layout.statusBarHeight ?? 0.0, layout.safeInsets.top)
let listTopInset = isLandscape ? topPanelHeight : layoutTopInset + topPanelHeight
let bottomPanelHeight = isLandscape ? layout.intrinsicInsets.bottom : self.effectiveBottomAreaHeight + layout.intrinsicInsets.bottom
let bottomPanelHeight = isLandscape ? layout.intrinsicInsets.bottom : bottomAreaHeight + layout.intrinsicInsets.bottom
var size = layout.size
if case .regular = layout.metrics.widthClass {
@ -3218,12 +3206,10 @@ public final class VoiceChatController: ViewController {
}
var bottomInset: CGFloat = 0.0
var bottomEdgeInset: CGFloat = 0.0
if case let .fullscreen(controlsHidden) = self.effectiveDisplayMode {
if case let .fullscreen(controlsHidden) = self.displayMode {
if !controlsHidden {
bottomInset = 80.0
}
bottomEdgeInset = 154.0
}
transition.updateAlpha(node: self.bottomGradientNode, alpha: self.isLandscape ? 0.0 : 1.0)
@ -3277,14 +3263,13 @@ 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
let bottomOffset: CGFloat = min(0.0, bottomEdge - listMaxY) + layout.size.height - bottomPanelHeight
let bottomDelta = bottomGradientHeight - bottomEdgeInset
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 + 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 + bottomGradientHeight), size: CGSize(width: contentWidth - sideInset * 2.0, height: 50.0))
let previousBottomCornersFrame = self.bottomCornersNode.frame
if !bottomCornersFrame.equalTo(previousBottomCornersFrame) {
self.bottomCornersNode.frame = bottomCornersFrame
self.bottomPanelBackgroundNode.frame = CGRect(x: 0.0, y: bottomOffset + bottomDelta, width: size.width, height: 2000.0)
self.bottomPanelBackgroundNode.frame = CGRect(x: 0.0, y: bottomOffset + bottomGradientHeight, width: size.width, height: 2000.0)
let positionDelta = CGPoint(x: 0.0, y: previousBottomCornersFrame.minY - bottomCornersFrame.minY)
transition.animatePositionAdditive(node: self.bottomCornersNode, offset: positionDelta)
@ -3299,8 +3284,7 @@ public final class VoiceChatController: ViewController {
}
let isFullscreen = self.isFullscreen
let isLandscape = self.isLandscape
let effectiveDisplayMode = self.effectiveDisplayMode
let effectiveDisplayMode = self.displayMode
self.controller?.statusBar.updateStatusBarStyle(isFullscreen ? .White : .Ignore, animated: true)
@ -3324,11 +3308,7 @@ public final class VoiceChatController: ViewController {
let backgroundColor: UIColor
if case .fullscreen = effectiveDisplayMode {
if isLandscape {
backgroundColor = isFullscreen ? panelBackgroundColor : secondaryPanelBackgroundColor
} else {
backgroundColor = fullscreenBackgroundColor
}
} else if self.isScheduling || self.callState?.scheduleTimestamp != nil {
backgroundColor = panelBackgroundColor
} else {
@ -3578,7 +3558,7 @@ public final class VoiceChatController: ViewController {
if previousIsLandscape != isLandscape {
if case .modal = self.effectiveDisplayMode {
if case .modal = self.displayMode {
self.displayMode = .modal(isExpanded: true, isFilled: true)
}
self.updateDecorationsColors()
@ -3586,7 +3566,7 @@ public final class VoiceChatController: ViewController {
self.updateMembers()
}
let effectiveDisplayMode = self.effectiveDisplayMode
let effectiveDisplayMode = self.displayMode
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - contentWidth) / 2.0), y: 10.0), size: CGSize(width: contentWidth, height: 44.0)))
self.updateTitle(transition: transition)
@ -4440,6 +4420,22 @@ public final class VoiceChatController: ViewController {
strongSelf.wideVideoNodes.insert(channel.endpointId)
}
strongSelf.updateMembers()
if let interaction = strongSelf.itemInteraction {
loop: for i in 0 ..< strongSelf.currentFullscreenEntries.count {
let entry = strongSelf.currentFullscreenEntries[i]
switch entry {
case let .peer(peerEntry, _):
if peerEntry.effectiveVideoEndpointId == channel.endpointId {
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)
break loop
}
default:
break
}
}
}
}
}
}), forKey: channel.endpointId)
@ -4595,7 +4591,9 @@ public final class VoiceChatController: ViewController {
self.controller?.dismissAllTooltips()
if case .fullscreen = self.effectiveDisplayMode {
if case .fullscreen = self.displayMode {
self.isPanning = true
self.mainStageBackgroundNode.alpha = 0.0
self.mainStageBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.4)
self.mainStageNode.setControlsHidden(true, animated: true)
@ -4613,7 +4611,7 @@ public final class VoiceChatController: ViewController {
return
}
switch self.effectiveDisplayMode {
switch self.displayMode {
case let .modal(isExpanded, previousIsFilled):
var topInset: CGFloat = 0.0
if let (currentTopInset, currentPanOffset) = self.panGestureArguments {
@ -4699,13 +4697,14 @@ public final class VoiceChatController: ViewController {
topInset = self.listNode.frame.height
}
if case .fullscreen = self.effectiveDisplayMode {
if case .fullscreen = self.displayMode {
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.isPanning = false
self.mainStageBackgroundNode.alpha = 1.0
self.mainStageBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, completion: { [weak self] _ in
self?.attachFullscreenVideos()
@ -4725,7 +4724,7 @@ public final class VoiceChatController: ViewController {
}
})
}
} else if case .modal(true, _) = self.effectiveDisplayMode {
} else if case .modal(true, _) = self.displayMode {
self.panGestureArguments = nil
if velocity.y > 300.0 || offset > topInset / 2.0 {
self.displayMode = .modal(isExpanded: false, isFilled: false)
@ -4758,7 +4757,7 @@ public final class VoiceChatController: ViewController {
if self.isScheduling {
self.dismissScheduled()
} else {
if case .fullscreen = self.effectiveDisplayMode {
if case .fullscreen = self.displayMode {
} else {
self.controller?.dismiss(closing: false, manual: true)
dismissing = true
@ -4771,7 +4770,7 @@ public final class VoiceChatController: ViewController {
}
}
if case .modal = self.effectiveDisplayMode {
if case .modal = self.displayMode {
self.displayMode = .modal(isExpanded: true, isFilled: true)
}
self.updateDecorationsColors()
@ -4818,6 +4817,28 @@ public final class VoiceChatController: ViewController {
self.updateDecorationsLayout(transition: .animated(duration: 0.3, curve: .easeInOut), completion: {
self.animatingExpansion = false
})
if case .fullscreen = self.displayMode {
self.isPanning = false
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)
}
})
}
default:
break
}
@ -5277,13 +5298,17 @@ public final class VoiceChatController: ViewController {
}
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: transition, animate: item.peer.id != effectiveSpeakerPeerId)
if let itemNode = itemNode as? VoiceChatFullscreenParticipantItemNode, let item = itemNode.item {
itemNode.animateTransitionIn(from: verticalItemNodes[item.peer.id], containerNode: self.transitionContainerNode, transition: transition, animate: item.peer.id != effectiveSpeakerPeerId)
}
}
if self.isLandscape {
self.transitionMaskTopFillLayer.opacity = 1.0
}
self.transitionMaskBottomFillLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, removeOnCompletion: false, completion: { [weak self] _ in
Queue.mainQueue().after(0.2) {
self?.transitionMaskTopFillLayer.opacity = 0.0
self?.transitionMaskBottomFillLayer.removeAllAnimations()
}
})
@ -5378,6 +5403,8 @@ public final class VoiceChatController: ViewController {
strongSelf.mainStageContainerNode.bounds = bounds
strongSelf.contentContainer.insertSubnode(strongSelf.mainStageContainerNode, belowSubnode: strongSelf.transitionContainerNode)
strongSelf.isPanning = false
})
self.transitionMaskTopFillLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15)

View File

@ -188,9 +188,6 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
let videoContainerNode: ASDisplayNode
private let videoFadeNode: ASDisplayNode
var videoNode: GroupVideoNode?
private let videoReadyDisposable = MetaDisposable()
private var videoReadyDelayed = false
private var videoReady = false
private var profileNode: VoiceChatPeerProfileNode?
@ -289,7 +286,6 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
}
deinit {
self.videoReadyDisposable.dispose()
self.audioLevelDisposable.dispose()
self.raiseHandTimer?.invalidate()
self.silenceTimer?.invalidate()
@ -300,7 +296,7 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
self.layoutParams?.0.action?(self.contextSourceNode)
}
func animateTransitionIn(from sourceNode: ASDisplayNode, containerNode: ASDisplayNode, transition: ContainedViewLayoutTransition, animate: Bool = true) {
func animateTransitionIn(from sourceNode: ASDisplayNode?, containerNode: ASDisplayNode, transition: ContainedViewLayoutTransition, animate: Bool = true) {
guard let item = self.item else {
return
}
@ -372,7 +368,7 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
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)
} else if !initialAnimate {
if case .animated = transition {
if transition.isAnimated {
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)
}
@ -380,7 +376,7 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
} else if let sourceNode = sourceNode as? VoiceChatParticipantItemNode, let _ = sourceNode.item {
var startContainerPosition = sourceNode.avatarNode.view.convert(sourceNode.avatarNode.bounds, to: containerNode.view).center
var animate = true
if startContainerPosition.y > containerNode.frame.height {
if startContainerPosition.y < -tileHeight || startContainerPosition.y > containerNode.frame.height + tileHeight {
animate = false
}
startContainerPosition = startContainerPosition.offsetBy(dx: 0.0, dy: 9.0)
@ -416,6 +412,11 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
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)
}
} else {
if transition.isAnimated {
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)
}
}
}
private func updateIsExtracted(_ isExtracted: Bool, transition: ContainedViewLayoutTransition) {
@ -539,10 +540,30 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
strongSelf.layoutParams = (item, params, first, last)
strongSelf.wavesColor = wavesColor
let videoContainerScale = tileSize.width / videoSize.width
let videoNode = item.getVideo()
if let current = strongSelf.videoNode, current !== videoNode {
current.removeFromSupernode()
strongSelf.videoReadyDisposable.set(nil)
if let currentVideoNode = strongSelf.videoNode, currentVideoNode !== videoNode {
if videoNode == nil {
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
if strongSelf.avatarNode.alpha.isZero {
strongSelf.animatingSelection = true
strongSelf.videoContainerNode.layer.animateScale(from: videoContainerScale, to: 0.001, duration: 0.2)
strongSelf.avatarNode.layer.animateScale(from: 0.0, to: 1.0, duration: 0.2, completion: { [weak self] _ in
self?.animatingSelection = false
})
strongSelf.videoContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -9.0), duration: 0.2, additive: true)
strongSelf.audioLevelView?.layer.animateScale(from: 0.0, to: 1.0, duration: 0.2)
}
transition.updateAlpha(node: currentVideoNode, alpha: 0.0)
transition.updateAlpha(node: strongSelf.videoFadeNode, alpha: 0.0)
transition.updateAlpha(node: strongSelf.avatarNode, alpha: 1.0)
if let audioLevelView = strongSelf.audioLevelView {
transition.updateAlpha(layer: audioLevelView.layer, alpha: 1.0)
}
} else {
currentVideoNode.removeFromSupernode()
}
}
let videoNodeUpdated = strongSelf.videoNode !== videoNode
@ -667,7 +688,7 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
strongSelf.audioLevelView = audioLevelView
strongSelf.offsetContainerNode.view.insertSubview(audioLevelView, at: 0)
if let item = strongSelf.item, strongSelf.videoNode != nil || item.active {
if let item = strongSelf.item, strongSelf.videoNode != nil && !item.active {
audioLevelView.alpha = 0.0
}
}
@ -816,8 +837,6 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
node.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2)
}
let videoContainerScale = tileSize.width / videoSize.width
if !strongSelf.isExtracted && !strongSelf.animatingExtraction {
strongSelf.videoFadeNode.frame = CGRect(x: 0.0, y: videoSize.height - fadeHeight, width: videoSize.width, height: fadeHeight)
strongSelf.videoContainerNode.bounds = CGRect(origin: CGPoint(), size: videoSize)
@ -842,8 +861,11 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
if currentItem != nil {
if item.active {
if strongSelf.avatarNode.alpha.isZero {
strongSelf.animatingSelection = true
strongSelf.videoContainerNode.layer.animateScale(from: videoContainerScale, to: 0.001, duration: 0.2)
strongSelf.avatarNode.layer.animateScale(from: 0.0, to: 1.0, duration: 0.2)
strongSelf.avatarNode.layer.animateScale(from: 0.0, to: 1.0, duration: 0.2, completion: { [weak self] _ in
self?.animatingSelection = false
})
strongSelf.videoContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -9.0), duration: 0.2, additive: true)
strongSelf.audioLevelView?.layer.animateScale(from: 0.0, to: 1.0, duration: 0.2)
}
@ -873,7 +895,7 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
if canUpdateAvatarVisibility {
strongSelf.avatarNode.alpha = 1.0
}
} else if strongSelf.videoReady {
} else {
videoNode.alpha = 1.0
strongSelf.avatarNode.alpha = 0.0
}
@ -891,22 +913,8 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
videoNode.bounds = CGRect(origin: CGPoint(), size: videoSize)
}
if videoNodeUpdated {
strongSelf.videoReadyDelayed = false
strongSelf.videoReadyDisposable.set((videoNode.ready
|> deliverOnMainQueue).start(next: { [weak self] ready in
if let strongSelf = self {
if !ready {
strongSelf.videoReadyDelayed = true
}
strongSelf.videoReady = ready
if let videoNode = strongSelf.videoNode, ready {
if strongSelf.videoReadyDelayed {
Queue.mainQueue().after(0.15) {
guard let currentItem = strongSelf.item else {
return
}
if currentItem.active {
if let _ = currentItem, videoNodeUpdated {
if item.active {
if canUpdateAvatarVisibility {
strongSelf.avatarNode.alpha = 1.0
}
@ -917,7 +925,6 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
videoNode.layer.animateScale(from: 0.01, to: 1.0, duration: 0.2)
videoNode.alpha = 1.0
}
}
} else {
if item.active {
if canUpdateAvatarVisibility {
@ -929,10 +936,6 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
videoNode.alpha = 1.0
}
}
}
}
}))
}
} else if canUpdateAvatarVisibility {
strongSelf.avatarNode.alpha = 1.0
}

View File

@ -48,12 +48,12 @@ final class VoiceChatMainStageNode: ASDisplayNode {
private let audioLevelDisposable = MetaDisposable()
private let speakingPeerDisposable = MetaDisposable()
private let speakingAudioLevelDisposable = MetaDisposable()
private var avatarNode: ASImageNode
private var backdropAvatarNode: ImageNode
private var backdropEffectView: UIVisualEffectView?
private var avatarNode: ImageNode
private let titleNode: ImmediateTextNode
private let microphoneNode: VoiceChatMicrophoneNode
private let avatarDisposable = MetaDisposable()
private let speakingContainerNode: ASDisplayNode
private var speakingEffectView: UIVisualEffectView?
private let speakingAvatarNode: AvatarNode
@ -129,7 +129,12 @@ final class VoiceChatMainStageNode: ASDisplayNode {
self.pinButtonTitleNode.attributedText = NSAttributedString(string: "Unpin", font: Font.regular(17.0), textColor: .white)
self.pinButtonNode = HighlightableButtonNode()
self.avatarNode = ASImageNode()
self.backdropAvatarNode = ImageNode()
self.backdropAvatarNode.contentMode = .scaleAspectFill
self.backdropAvatarNode.displaysAsynchronously = false
self.backdropAvatarNode.isHidden = true
self.avatarNode = ImageNode()
self.avatarNode.displaysAsynchronously = false
self.avatarNode.isHidden = true
@ -152,14 +157,12 @@ final class VoiceChatMainStageNode: ASDisplayNode {
self.clipsToBounds = true
self.cornerRadius = backgroundCornerRadius
if #available(iOS 13.0, *) {
self.layer.cornerCurve = .continuous
}
self.addSubnode(self.backgroundNode)
self.addSubnode(self.topFadeNode)
self.addSubnode(self.bottomFadeNode)
self.addSubnode(self.bottomFillNode)
self.addSubnode(self.backdropAvatarNode)
self.addSubnode(self.avatarNode)
self.addSubnode(self.titleNode)
self.addSubnode(self.microphoneNode)
@ -215,7 +218,6 @@ final class VoiceChatMainStageNode: ASDisplayNode {
}
deinit {
self.avatarDisposable.dispose()
self.videoReadyDisposable.dispose()
self.audioLevelDisposable.dispose()
self.speakingPeerDisposable.dispose()
@ -226,10 +228,25 @@ final class VoiceChatMainStageNode: ASDisplayNode {
override func didLoad() {
super.didLoad()
if #available(iOS 13.0, *) {
self.layer.cornerCurve = .continuous
}
let speakingEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
self.speakingContainerNode.view.insertSubview(speakingEffectView, at: 0)
self.speakingEffectView = speakingEffectView
let effect: UIVisualEffect
if #available(iOS 13.0, *) {
effect = UIBlurEffect(style: .systemMaterialDark)
} else {
effect = UIBlurEffect(style: .dark)
}
let backdropEffectView = UIVisualEffectView(effect: effect)
backdropEffectView.isHidden = true
self.view.insertSubview(backdropEffectView, aboveSubview: self.backdropAvatarNode.view)
self.backdropEffectView = backdropEffectView
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tap)))
self.speakingContainerNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.speakingTap)))
@ -476,12 +493,8 @@ final class VoiceChatMainStageNode: ASDisplayNode {
if !arePeersEqual(previousPeerEntry?.peer, peerEntry.peer) {
let peer = peerEntry.peer
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
self.avatarDisposable.set((peerAvatarCompleteImage(account: self.context.account, peer: peer, size: CGSize(width: 180.0, height: 180.0), font: avatarPlaceholderFont(size: 78.0), fullSize: true)
|> deliverOnMainQueue).start(next: { [weak self] image in
if let strongSelf = self {
strongSelf.avatarNode.image = image
}
}))
self.backdropAvatarNode.setSignal(peerAvatarCompleteImage(account: self.context.account, peer: peer, size: CGSize(width: 180.0, height: 180.0), round: false, font: avatarPlaceholderFont(size: 78.0), drawLetters: false))
self.avatarNode.setSignal(peerAvatarCompleteImage(account: self.context.account, peer: peer, size: CGSize(width: 180.0, height: 180.0), font: avatarPlaceholderFont(size: 78.0), fullSize: true))
self.titleNode.attributedText = NSAttributedString(string: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), font: Font.semibold(15.0), textColor: .white)
if let (size, sideInset, bottomInset, isLandscape) = self.validLayout {
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, isLandscape: isLandscape, transition: .immediate)
@ -491,6 +504,7 @@ final class VoiceChatMainStageNode: ASDisplayNode {
self.pinButtonTitleNode.isHidden = !pinned
self.pinButtonIconNode.image = !pinned ? generateTintedImage(image: UIImage(bundleImageName: "Call/Pin"), color: .white) : generateTintedImage(image: UIImage(bundleImageName: "Call/Unpin"), color: .white)
var firstTime = true
var wavesColor = UIColor(rgb: 0x34c759)
if let getAudioLevel = self.getAudioLevel, previousPeerEntry?.peer.id != peerEntry.peer.id {
if let audioLevelView = self.audioLevelView {
@ -528,7 +542,7 @@ final class VoiceChatMainStageNode: ASDisplayNode {
let avatarScale: CGFloat
if value > 0.02 {
audioLevelView.startAnimating(immediately: true)
audioLevelView.startAnimating(immediately: firstTime)
avatarScale = 1.03 + level * 0.13
audioLevelView.setColor(wavesColor, animated: true)
@ -551,6 +565,7 @@ final class VoiceChatMainStageNode: ASDisplayNode {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.15, curve: .easeInOut)
transition.updateTransformScale(node: strongSelf.avatarNode, scale: avatarScale, beginWithCurrentState: true)
}
firstTime = false
}))
}
@ -579,6 +594,13 @@ final class VoiceChatMainStageNode: ASDisplayNode {
self.microphoneNode.update(state: VoiceChatMicrophoneNode.State(muted: muted, filled: true, color: .white), animated: true)
}
private func setAvatarHidden(_ hidden: Bool) {
self.backdropAvatarNode.isHidden = hidden
self.backdropEffectView?.isHidden = hidden
self.avatarNode.isHidden = hidden
self.audioLevelView?.isHidden = hidden
}
func update(peer: (peer: PeerId, endpointId: String?)?, waitForFullSize: Bool, completion: (() -> Void)? = nil) {
let previousPeer = self.currentPeer
if previousPeer?.0 == peer?.0 && previousPeer?.1 == peer?.1 {
@ -592,8 +614,7 @@ final class VoiceChatMainStageNode: ASDisplayNode {
if let (_, endpointId) = peer {
if endpointId != previousPeer?.1 {
if let endpointId = endpointId {
self.avatarNode.isHidden = true
self.audioLevelView?.isHidden = true
self.setAvatarHidden(true)
self.call.makeIncomingVideoView(endpointId: endpointId, completion: { [weak self] videoView in
Queue.mainQueue().async {
@ -607,28 +628,32 @@ final class VoiceChatMainStageNode: ASDisplayNode {
let previousVideoNode = strongSelf.currentVideoNode
strongSelf.currentVideoNode = videoNode
strongSelf.insertSubnode(videoNode, aboveSubnode: strongSelf.backgroundNode)
if let previousVideoNode = previousVideoNode {
Queue.mainQueue().after(0.03) {
previousVideoNode.removeFromSupernode()
}
// currentVideoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak currentVideoNode] _ in
// currentVideoNode?.removeFromSupernode()
// })
}
if let (size, sideInset, bottomInset, isLandscape) = strongSelf.validLayout {
strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, isLandscape: isLandscape, transition: .immediate)
}
if waitForFullSize {
strongSelf.videoReadyDisposable.set((videoNode.ready
|> filter { $0 }
|> take(1)
|> deliverOnMainQueue).start(next: { _ in
|> deliverOnMainQueue).start(next: { [weak self] _ in
Queue.mainQueue().after(0.07) {
completion?()
if let strongSelf = self {
if let (size, sideInset, bottomInset, isLandscape) = strongSelf.validLayout {
strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, isLandscape: isLandscape, transition: .immediate)
}
}
if let previousVideoNode = previousVideoNode {
previousVideoNode.removeFromSupernode()
}
}
}))
} else {
if let (size, sideInset, bottomInset, isLandscape) = strongSelf.validLayout {
strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, isLandscape: isLandscape, transition: .immediate)
}
if let previousVideoNode = previousVideoNode {
previousVideoNode.removeFromSupernode()
}
strongSelf.videoReadyDisposable.set(nil)
completion?()
}
@ -637,15 +662,14 @@ final class VoiceChatMainStageNode: ASDisplayNode {
}
})
} else {
self.avatarNode.isHidden = false
self.audioLevelView?.isHidden = false
self.setAvatarHidden(false)
if let currentVideoNode = self.currentVideoNode {
currentVideoNode.removeFromSupernode()
self.currentVideoNode = nil
}
}
} else {
self.audioLevelView?.isHidden = self.currentPeer?.1 != nil
self.setAvatarHidden(endpointId != nil)
completion?()
}
} else {
@ -699,6 +723,10 @@ final class VoiceChatMainStageNode: ASDisplayNode {
}
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
transition.updateFrame(node: self.backdropAvatarNode, frame: CGRect(origin: CGPoint(), size: size))
if let backdropEffectView = self.backdropEffectView {
transition.updateFrame(view: backdropEffectView, frame: CGRect(origin: CGPoint(), size: size))
}
let avatarSize = CGSize(width: 180.0, height: 180.0)
let avatarFrame = CGRect(origin: CGPoint(x: (size.width - avatarSize.width) / 2.0, y: (size.height - avatarSize.height) / 2.0), size: avatarSize)

View File

@ -129,9 +129,7 @@ final class VoiceChatTileItemNode: ASDisplayNode {
self.contentNode = ASDisplayNode()
self.contentNode.clipsToBounds = true
self.contentNode.cornerRadius = backgroundCornerRadius
if #available(iOS 13.0, *) {
self.contentNode.layer.cornerCurve = .continuous
}
self.backgroundNode = ASDisplayNode()
self.backgroundNode.backgroundColor = panelBackgroundColor
@ -203,6 +201,10 @@ final class VoiceChatTileItemNode: ASDisplayNode {
override func didLoad() {
super.didLoad()
if #available(iOS 13.0, *) {
self.contentNode.layer.cornerCurve = .continuous
}
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tap)))
}
@ -405,12 +407,6 @@ final class VoiceChatTileItemNode: ASDisplayNode {
videoNode.alpha = 1.0
self.videoNode = videoNode
self.videoContainerNode.addSubnode(videoNode)
if animate {
// self.videoContainerNode.layer.animateScale(from: sourceNode.bounds.width / videoSize.width, to: tileSize.width / videoSize.width, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
// self.videoContainerNode.layer.animate(from: (tileSize.width / 2.0) as NSNumber, to: videoCornerRadius as NSNumber, keyPath: "cornerRadius", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2, removeOnCompletion: false, completion: { _ in
// })
}
}
if animate {
@ -445,9 +441,11 @@ final class VoiceChatTileItemNode: ASDisplayNode {
sourceNode.layer.animateScale(from: 1.0, to: 0.0, duration: duration, timingFunction: timingFunction)
}
if transition.isAnimated {
self.fadeNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
}
}
}
}
private let blue = UIColor(rgb: 0x007fff)