From da0f47b6555ee2175403bde04bb986efdbf260cb Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 26 May 2021 14:49:22 +0400 Subject: [PATCH] Video Chat Improvements --- .../Sources/VoiceChatActionItem.swift | 47 +++++++++++++------ .../Sources/VoiceChatController.swift | 31 +++++++++--- .../Sources/VoiceChatTileItemNode.swift | 6 ++- .../TelegramCore/Sources/GroupCalls.swift | 22 ++++++++- 4 files changed, 82 insertions(+), 24 deletions(-) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatActionItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatActionItem.swift index 433638a50c..29dea03ed1 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatActionItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatActionItem.swift @@ -55,7 +55,7 @@ class VoiceChatActionItem: ListViewItem { func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { async { let node = VoiceChatActionItemNode() - let (layout, apply) = node.asyncLayout()(self, params, false, nextItem == nil) + let (layout, apply) = node.asyncLayout()(self, params, previousItem == nil || previousItem is VoiceChatTilesGridItem, nextItem == nil) node.contentSize = layout.contentSize node.insets = layout.insets @@ -74,7 +74,7 @@ class VoiceChatActionItem: ListViewItem { let makeLayout = nodeValue.asyncLayout() async { - let (layout, apply) = makeLayout(self, params, false, nextItem == nil) + let (layout, apply) = makeLayout(self, params, previousItem == nil || previousItem is VoiceChatTilesGridItem, nextItem == nil) Queue.mainQueue().async { completion(layout, { _ in apply() @@ -95,6 +95,7 @@ class VoiceChatActionItem: ListViewItem { class VoiceChatActionItemNode: ListViewItemNode { private let topStripeNode: ASDisplayNode private let bottomStripeNode: ASDisplayNode + private let highlightContainerNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode private let iconNode: ASImageNode @@ -121,13 +122,17 @@ class VoiceChatActionItemNode: ListViewItemNode { self.iconNode.displayWithoutProcessing = true self.iconNode.displaysAsynchronously = false + self.highlightContainerNode = ASDisplayNode() + self.highlightContainerNode.clipsToBounds = true + self.highlightedBackgroundNode = ASDisplayNode() - self.highlightedBackgroundNode.isLayerBacked = true self.activateArea = AccessibilityAreaNode() super.init(layerBacked: false, dynamicBounce: false) + self.highlightContainerNode.addSubnode(self.highlightedBackgroundNode) + self.addSubnode(self.iconNode) self.addSubnode(self.titleNode) self.addSubnode(self.activateArea) @@ -138,11 +143,19 @@ class VoiceChatActionItemNode: ListViewItemNode { } } - func asyncLayout() -> (_ item: VoiceChatActionItem, _ params: ListViewItemLayoutParams, _ firstWithHeader: Bool, _ last: Bool) -> (ListViewItemNodeLayout, () -> Void) { + override func didLoad() { + super.didLoad() + + if #available(iOS 13.0, *) { + self.highlightContainerNode.layer.cornerCurve = .continuous + } + } + + func asyncLayout() -> (_ item: VoiceChatActionItem, _ params: ListViewItemLayoutParams, _ first: Bool, _ last: Bool) -> (ListViewItemNodeLayout, () -> Void) { let makeTitleLayout = TextNode.asyncLayout(self.titleNode) let currentItem = self.item - return { item, params, firstWithHeader, last in + return { item, params, first, last in var updatedTheme: PresentationTheme? var updatedContent = false @@ -165,7 +178,7 @@ class VoiceChatActionItemNode: ListViewItemNode { let contentHeight: CGFloat = 12.0 * 2.0 + titleLayout.size.height let contentSize = CGSize(width: params.width, height: contentHeight) - let insets = UIEdgeInsets(top: firstWithHeader ? 29.0 : 0.0, left: 0.0, bottom: 0.0, right: 0.0) + let insets = UIEdgeInsets() let separatorHeight = UIScreenPixel let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) @@ -214,6 +227,10 @@ class VoiceChatActionItemNode: ListViewItemNode { strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: titleOffset + 1.0, y: floor((contentSize.height - titleLayout.size.height) / 2.0)), size: titleLayout.size) + strongSelf.highlightContainerNode.frame = CGRect(origin: CGPoint(x: params.leftInset, y: -UIScreenPixel), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height + UIScreenPixel + UIScreenPixel + 11.0)) + + strongSelf.highlightContainerNode.cornerRadius = first ? 11.0 : 0.0 + strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: contentSize.height + UIScreenPixel + UIScreenPixel)) } }) @@ -224,8 +241,8 @@ class VoiceChatActionItemNode: ListViewItemNode { super.setHighlighted(highlighted, at: point, animated: animated) if highlighted { - self.highlightedBackgroundNode.alpha = 1.0 - if self.highlightedBackgroundNode.supernode == nil { + self.highlightContainerNode.alpha = 1.0 + if self.highlightContainerNode.supernode == nil { var anchorNode: ASDisplayNode? if self.bottomStripeNode.supernode != nil { anchorNode = self.bottomStripeNode @@ -233,24 +250,24 @@ class VoiceChatActionItemNode: ListViewItemNode { anchorNode = self.topStripeNode } if let anchorNode = anchorNode { - self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: anchorNode) + self.insertSubnode(self.highlightContainerNode, aboveSubnode: anchorNode) } else { - self.addSubnode(self.highlightedBackgroundNode) + self.addSubnode(self.highlightContainerNode) } } } else { - if self.highlightedBackgroundNode.supernode != nil { + if self.highlightContainerNode.supernode != nil { if animated { - self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in + self.highlightContainerNode.layer.animateAlpha(from: self.highlightContainerNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in if let strongSelf = self { if completed { - strongSelf.highlightedBackgroundNode.removeFromSupernode() + strongSelf.highlightContainerNode.removeFromSupernode() } } }) - self.highlightedBackgroundNode.alpha = 0.0 + self.highlightContainerNode.alpha = 0.0 } else { - self.highlightedBackgroundNode.removeFromSupernode() + self.highlightContainerNode.removeFromSupernode() } } } diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 86d47e34f5..4fbd70eb58 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -96,8 +96,8 @@ struct VoiceChatPeerEntry: Identifiable { var peer: Peer var about: String? var isMyPeer: Bool - var videoEndpointId: String? - var presentationEndpointId: String? + var _videoEndpointId: String? + var _presentationEndpointId: String? var state: State var muteState: GroupCallParticipantsContext.Participant.MuteState? var canManageCall: Bool @@ -107,6 +107,22 @@ struct VoiceChatPeerEntry: Identifiable { var active: Bool var isLandscape: Bool + var videoEndpointId: String? { + if let muteState = self.muteState, !muteState.canUnmute || muteState.mutedByYou { + return nil + } else { + return self._videoEndpointId + } + } + + var presentationEndpointId: String? { + if let muteState = self.muteState, !muteState.canUnmute || muteState.mutedByYou { + return nil + } else { + return self._presentationEndpointId + } + } + var effectiveVideoEndpointId: String? { return self.presentationEndpointId ?? self.videoEndpointId } @@ -129,8 +145,8 @@ struct VoiceChatPeerEntry: Identifiable { self.peer = peer self.about = about self.isMyPeer = isMyPeer - self.videoEndpointId = videoEndpointId - self.presentationEndpointId = presentationEndpointId + self._videoEndpointId = videoEndpointId + self._presentationEndpointId = presentationEndpointId self.state = state self.muteState = muteState self.canManageCall = canManageCall @@ -155,10 +171,10 @@ struct VoiceChatPeerEntry: Identifiable { if lhs.isMyPeer != rhs.isMyPeer { return false } - if lhs.videoEndpointId != rhs.videoEndpointId { + if lhs._videoEndpointId != rhs._videoEndpointId { return false } - if lhs.presentationEndpointId != rhs.presentationEndpointId { + if lhs._presentationEndpointId != rhs._presentationEndpointId { return false } if lhs.state != rhs.state { @@ -5587,6 +5603,9 @@ public final class VoiceChatController: ViewController { }) self.transitionMaskTopFillLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15) + if !transitionOffset.isZero { + self.transitionMaskBottomFillLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3) + } if let (layout, navigationHeight) = self.validLayout { self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: transition) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatTileItemNode.swift b/submodules/TelegramCallsUI/Sources/VoiceChatTileItemNode.swift index 95e635ed57..9d0d120d2c 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatTileItemNode.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatTileItemNode.swift @@ -191,7 +191,9 @@ final class VoiceChatTileItemNode: ASDisplayNode { gesture.cancel() return } - contextAction(strongSelf.contextSourceNode, gesture) + if item.videoReady { + contextAction(strongSelf.contextSourceNode, gesture) + } } self.contextSourceNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, transition in guard let strongSelf = self, let _ = strongSelf.item else { @@ -275,6 +277,8 @@ final class VoiceChatTileItemNode: ASDisplayNode { let previousItem = self.item self.item = item + self.containerNode.isGestureEnabled = item.videoReady + if !item.videoReady { let shimmerNode: VoiceChatTileShimmeringNode if let current = self.shimmerNode { diff --git a/submodules/TelegramCore/Sources/GroupCalls.swift b/submodules/TelegramCore/Sources/GroupCalls.swift index f5751171ce..4842b8b4b4 100644 --- a/submodules/TelegramCore/Sources/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/GroupCalls.swift @@ -156,6 +156,10 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int presentationJsonDescription = data } } + if muteState?.canUnmute == false { + videoJsonDescription = nil + presentationJsonDescription = nil + } parsedParticipants.append(GroupCallParticipantsContext.Participant( peer: peer, ssrc: ssrc, @@ -481,6 +485,10 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash presentationJsonDescription = data } } + if muteState?.canUnmute == false { + videoJsonDescription = nil + presentationJsonDescription = nil + } parsedParticipants.append(GroupCallParticipantsContext.Participant( peer: peer, ssrc: ssrc, @@ -741,6 +749,10 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal presentationJsonDescription = data } } + if muteState?.canUnmute == false { + videoJsonDescription = nil + presentationJsonDescription = nil + } if !state.participants.contains(where: { $0.peer.id == peer.id }) { state.participants.append(GroupCallParticipantsContext.Participant( peer: peer, @@ -2150,7 +2162,10 @@ extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate { presentationJsonDescription = data } } - + if muteState?.canUnmute == false { + videoJsonDescription = nil + presentationJsonDescription = nil + } self.init( peerId: peerId, ssrc: ssrc, @@ -2213,7 +2228,10 @@ extension GroupCallParticipantsContext.Update.StateUpdate { presentationJsonDescription = data } } - + if muteState?.canUnmute == false { + videoJsonDescription = nil + presentationJsonDescription = nil + } participantUpdates.append(GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate( peerId: peerId, ssrc: ssrc,