From 925f4b66660e2a32f4e9c150fd406860e547f745 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 10 Jun 2021 16:04:00 +0400 Subject: [PATCH 1/2] Add more blur --- .../PresentationThemeEssentialGraphics.swift | 3 +- .../ChatMessageAnimatedStickerItemNode.swift | 6 +- .../Sources/ChatMessageBubbleItemNode.swift | 77 +++++-------------- 3 files changed, 21 insertions(+), 65 deletions(-) diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift index 3a26a65157..eed7b1ff17 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift @@ -500,7 +500,7 @@ public final class PrincipalThemeAdditionalGraphics { public let chatEmptyItemBackgroundImage: UIImage public let chatLoadingIndicatorBackgroundImage: UIImage - public let chatBubbleShareButtonImage: UIImage + public let chatBubbleNavigateButtonImage: UIImage public let chatBubbleActionButtonIncomingMiddleImage: UIImage public let chatBubbleActionButtonIncomingBottomLeftImage: UIImage @@ -547,7 +547,6 @@ public final class PrincipalThemeAdditionalGraphics { self.chatEmptyItemBackgroundImage = generateStretchableFilledCircleImage(radius: 14.0, color: serviceColor.fill)! self.chatLoadingIndicatorBackgroundImage = generateStretchableFilledCircleImage(diameter: 30.0, color: serviceColor.fill)! - self.chatBubbleShareButtonImage = chatBubbleActionButtonImage(fillColor: bubbleVariableColor(variableColor: theme.message.shareButtonFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.shareButtonStrokeColor, wallpaper: wallpaper), foregroundColor: bubbleVariableColor(variableColor: theme.message.shareButtonForegroundColor, wallpaper: wallpaper), image: UIImage(bundleImageName: "Chat/Message/ShareIcon"))! self.chatBubbleNavigateButtonImage = chatBubbleActionButtonImage(fillColor: bubbleVariableColor(variableColor: theme.message.shareButtonFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.shareButtonStrokeColor, wallpaper: wallpaper), foregroundColor: bubbleVariableColor(variableColor: theme.message.shareButtonForegroundColor, wallpaper: wallpaper), image: UIImage(bundleImageName: "Chat/Message/NavigateToMessageIcon"), iconOffset: CGPoint(x: 0.0, y: 1.0))! self.chatBubbleActionButtonIncomingMiddleImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsStrokeColor, wallpaper: wallpaper), position: .middle, bubbleCorners: bubbleCorners) self.chatBubbleActionButtonIncomingBottomLeftImage = messageBubbleActionButtonImage(color: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsFillColor, wallpaper: wallpaper), strokeColor: bubbleVariableColor(variableColor: theme.message.incoming.actionButtonsStrokeColor, wallpaper: wallpaper), position: .bottomLeft, bubbleCorners: bubbleCorners) diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index f6d3a3ba48..b46d3957ce 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -95,20 +95,16 @@ class ChatMessageShareButton: HighlightableButtonNode { self.isReplies = isReplies let graphics = PresentationResourcesChat.additionalGraphics(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners) - //var updatedShareButtonBackground: UIImage? + var updatedIconImage: UIImage? if case .pinnedMessages = subject { updatedIconImage = PresentationResourcesChat.chatFreeNavigateButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) - //updatedShareButtonBackground = graphics.chatBubbleNavigateButtonImage } else if isReplies { - //updatedShareButtonBackground = PresentationResourcesChat.chatFreeCommentButtonBackground(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) updatedIconImage = PresentationResourcesChat.chatFreeCommentButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) } else if message.id.peerId.isRepliesOrSavedMessages(accountPeerId: account.peerId) { updatedIconImage = PresentationResourcesChat.chatFreeNavigateButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) - //updatedShareButtonBackground = graphics.chatBubbleNavigateButtonImage } else { updatedIconImage = PresentationResourcesChat.chatFreeShareButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) - //updatedShareButtonBackground = graphics.chatBubbleShareButtonImage } self.backgroundNode.color = presentationData.theme.theme.chat.serviceMessage.components.withDefaultWallpaper.dateFillStatic self.iconNode.image = updatedIconImage diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index abdaedc7f4..65f0138233 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -414,7 +414,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode private var mosaicStatusNode: ChatMessageDateAndStatusNode? private var actionButtonsNode: ChatMessageActionButtonsNode? - private var shareButtonNode: HighlightableButtonNode? + private var shareButtonNode: ChatMessageShareButton? private let messageAccessibilityArea: AccessibilityAreaNode @@ -1068,8 +1068,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode let mosaicStatusLayout = ChatMessageDateAndStatusNode.asyncLayout(self.mosaicStatusNode) - let currentShareButtonNode = self.shareButtonNode - let layoutConstants = self.layoutConstants let currentItem = self.appliedItem @@ -1089,7 +1087,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode replyInfoLayout: replyInfoLayout, actionButtonsLayout: actionButtonsLayout, mosaicStatusLayout: mosaicStatusLayout, - currentShareButtonNode: currentShareButtonNode, layoutConstants: layoutConstants, currentItem: currentItem, currentForwardInfo: currentForwardInfo, @@ -1106,7 +1103,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode replyInfoLayout: (ChatPresentationData, PresentationStrings, AccountContext, ChatMessageReplyInfoType, Message, CGSize) -> (CGSize, () -> ChatMessageReplyInfoNode), actionButtonsLayout: (AccountContext, ChatPresentationThemeData, PresentationChatBubbleCorners, PresentationStrings, ReplyMarkupMessageAttribute, Message, CGFloat) -> (minWidth: CGFloat, layout: (CGFloat) -> (CGSize, (Bool) -> ChatMessageActionButtonsNode)), mosaicStatusLayout: (AccountContext, ChatPresentationData, Bool, Int?, String, ChatMessageDateAndStatusType, CGSize, [MessageReaction], Int, Bool, Bool) -> (CGSize, (Bool) -> ChatMessageDateAndStatusNode), - currentShareButtonNode: HighlightableButtonNode?, layoutConstants: ChatMessageItemLayoutConstants, currentItem: ChatMessageItem?, currentForwardInfo: (Peer?, String?)?, @@ -2155,38 +2151,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode layoutInsets.top += layoutConstants.timestampHeaderHeight } - var updatedShareButtonBackground: UIImage? - - var updatedShareButtonNode: HighlightableButtonNode? - if needShareButton { - if currentShareButtonNode != nil { - updatedShareButtonNode = currentShareButtonNode - if item.presentationData.theme !== currentItem?.presentationData.theme { - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) - if case .pinnedMessages = item.associatedData.subject { - updatedShareButtonBackground = graphics.chatBubbleNavigateButtonImage - } else if item.message.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { - updatedShareButtonBackground = graphics.chatBubbleNavigateButtonImage - } else { - updatedShareButtonBackground = graphics.chatBubbleShareButtonImage - } - } - } else { - let buttonNode = HighlightableButtonNode() - let buttonIcon: UIImage? - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) - if case .pinnedMessages = item.associatedData.subject { - buttonIcon = graphics.chatBubbleNavigateButtonImage - } else if item.message.id.peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { - buttonIcon = graphics.chatBubbleNavigateButtonImage - } else { - buttonIcon = graphics.chatBubbleShareButtonImage - } - buttonNode.setBackgroundImage(buttonIcon, for: [.normal]) - updatedShareButtonNode = buttonNode - } - } - let layout = ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets) let graphics = PresentationResourcesChat.principalGraphics(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) @@ -2241,8 +2205,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode contentContainerNodeFrames: contentContainerNodeFrames, mosaicStatusOrigin: mosaicStatusOrigin, mosaicStatusSizeAndApply: mosaicStatusSizeAndApply, - updatedShareButtonNode: updatedShareButtonNode, - updatedShareButtonBackground: updatedShareButtonBackground + needsShareButton: needShareButton ) }) } @@ -2282,8 +2245,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode contentContainerNodeFrames: [(UInt32, CGRect, Bool?, CGFloat)], mosaicStatusOrigin: CGPoint?, mosaicStatusSizeAndApply: (CGSize, (Bool) -> ChatMessageDateAndStatusNode)?, - updatedShareButtonNode: HighlightableButtonNode?, - updatedShareButtonBackground: UIImage? + needsShareButton: Bool ) -> Void { guard let strongSelf = selfReference.value else { return @@ -2737,22 +2699,18 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode strongSelf.mosaicStatusNode = nil mosaicStatusNode.removeFromSupernode() } - - if let updatedShareButtonNode = updatedShareButtonNode { - if updatedShareButtonNode !== strongSelf.shareButtonNode { - if let shareButtonNode = strongSelf.shareButtonNode { - shareButtonNode.removeFromSupernode() - } - strongSelf.shareButtonNode = updatedShareButtonNode - strongSelf.insertSubnode(updatedShareButtonNode, belowSubnode: strongSelf.messageAccessibilityArea) - updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside) - } - if let updatedShareButtonBackground = updatedShareButtonBackground { - strongSelf.shareButtonNode?.setBackgroundImage(updatedShareButtonBackground, for: [.normal]) + + if needsShareButton { + if strongSelf.shareButtonNode == nil { + let shareButtonNode = ChatMessageShareButton() + strongSelf.shareButtonNode = shareButtonNode + strongSelf.insertSubnode(shareButtonNode, belowSubnode: strongSelf.messageAccessibilityArea) + shareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside) } + } else if let shareButtonNode = strongSelf.shareButtonNode { - shareButtonNode.removeFromSupernode() strongSelf.shareButtonNode = nil + shareButtonNode.removeFromSupernode() } if case .System = animation, !strongSelf.mainContextSourceNode.isExtractedToContextPreview { @@ -2761,7 +2719,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } if let shareButtonNode = strongSelf.shareButtonNode { let currentBackgroundFrame = strongSelf.backgroundNode.frame - shareButtonNode.frame = CGRect(origin: CGPoint(x: currentBackgroundFrame.maxX + 8.0, y: currentBackgroundFrame.maxY - 30.0), size: CGSize(width: 29.0, height: 29.0)) + let buttonSize = shareButtonNode.update(presentationData: item.presentationData, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account) + shareButtonNode.frame = CGRect(origin: CGPoint(x: currentBackgroundFrame.maxX + 8.0, y: currentBackgroundFrame.maxY - buttonSize.width - 1.0), size: buttonSize) } } else { if let _ = strongSelf.backgroundFrameTransition { @@ -2770,7 +2729,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } strongSelf.messageAccessibilityArea.frame = backgroundFrame if let shareButtonNode = strongSelf.shareButtonNode { - shareButtonNode.frame = CGRect(origin: CGPoint(x: backgroundFrame.maxX + 8.0, y: backgroundFrame.maxY - 30.0), size: CGSize(width: 29.0, height: 29.0)) + let buttonSize = shareButtonNode.update(presentationData: item.presentationData, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account) + shareButtonNode.frame = CGRect(origin: CGPoint(x: backgroundFrame.maxX + 8.0, y: backgroundFrame.maxY - buttonSize.width - 1.0), size: buttonSize) } if case .System = animation, strongSelf.mainContextSourceNode.isExtractedToContextPreview { @@ -2963,8 +2923,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } self.messageAccessibilityArea.frame = backgroundFrame - if let shareButtonNode = self.shareButtonNode { - shareButtonNode.frame = CGRect(origin: CGPoint(x: backgroundFrame.maxX + 8.0, y: backgroundFrame.maxY - 30.0), size: CGSize(width: 29.0, height: 29.0)) + if let item = self.item, let shareButtonNode = self.shareButtonNode { + let buttonSize = shareButtonNode.update(presentationData: item.presentationData, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account) + shareButtonNode.frame = CGRect(origin: CGPoint(x: backgroundFrame.maxX + 8.0, y: backgroundFrame.maxY - buttonSize.width - 1.0), size: buttonSize) } if CGFloat(1.0).isLessThanOrEqualTo(progress) { From a34bb55b7ec936857b1014268d8489f4f097aab5 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 10 Jun 2021 15:34:33 +0300 Subject: [PATCH 2/2] Video Chat Improvements --- .../Sources/GroupVideoNode.swift | 5 +- .../Sources/PresentationGroupCall.swift | 1 + .../Sources/VoiceChatController.swift | 54 ++++++++++-- .../VoiceChatFullscreenParticipantItem.swift | 8 +- .../Sources/VoiceChatMainStageNode.swift | 84 ++++++++++++++----- 5 files changed, 124 insertions(+), 28 deletions(-) diff --git a/submodules/TelegramCallsUI/Sources/GroupVideoNode.swift b/submodules/TelegramCallsUI/Sources/GroupVideoNode.swift index ead7b595e2..1f5163643e 100644 --- a/submodules/TelegramCallsUI/Sources/GroupVideoNode.swift +++ b/submodules/TelegramCallsUI/Sources/GroupVideoNode.swift @@ -40,7 +40,9 @@ final class GroupVideoNode: ASDisplayNode { return self.readyPromise.get() } - init(videoView: PresentationCallVideoView, backdropVideoView: PresentationCallVideoView?, disabledText: String? = nil) { + public var isMainstageExclusive = false + + init(videoView: PresentationCallVideoView, backdropVideoView: PresentationCallVideoView?) { self.sourceContainerNode = PinchSourceContainerNode() self.containerNode = ASDisplayNode() self.videoViewContainer = UIView() @@ -193,7 +195,6 @@ final class GroupVideoNode: ASDisplayNode { return rotatedAspect } - var keepBackdropSize = false func updateLayout(size: CGSize, layoutMode: LayoutMode, transition: ContainedViewLayoutTransition) { self.validLayout = (size, layoutMode) let bounds = CGRect(origin: CGPoint(), size: size) diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 149966e709..2017da3b47 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -2556,6 +2556,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { public func disableVideo() { self.hasVideo = false + self.useFrontCamera = true; if let _ = self.videoCapturer { self.videoCapturer = nil self.isVideoMutedDisposable.set(nil) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 7519c30a3c..cda47d637f 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -901,8 +901,10 @@ public final class VoiceChatController: ViewController { private var wideVideoNodes = Set() private var videoOrder: [String] = [] private var readyVideoEndpointIds = Set() + private var readyVideoEndpointIdsPromise = ValuePromise>(Set()) private var timeoutedEndpointIds = Set() private var readyVideoDisposables = DisposableDict() + private var myPeerVideoReadyDisposable = MetaDisposable() private var peerIdToEndpointId: [PeerId: String] = [:] @@ -1761,7 +1763,9 @@ public final class VoiceChatController: ViewController { return nil } var ignore = false - if case .fullscreen = strongSelf.displayMode, !strongSelf.isPanning { + if case .mainstage = position { + ignore = false + } else if case .fullscreen = strongSelf.displayMode, !strongSelf.isPanning { ignore = ![.mainstage, .list].contains(position) } else { ignore = position != .tile @@ -1774,6 +1778,9 @@ public final class VoiceChatController: ViewController { } for (listEndpointId, videoNode) in strongSelf.videoNodes { if listEndpointId == endpointId { + if position != .mainstage && videoNode.isMainstageExclusive { + return nil + } return videoNode } } @@ -2276,6 +2283,33 @@ public final class VoiceChatController: ViewController { return self?.itemInteraction?.getAudioLevel(peerId) ?? .single(0.0) } + self.mainStageNode.getVideo = { [weak self] endpointId, isMyPeer, completion in + if let strongSelf = self { + if isMyPeer { + if strongSelf.readyVideoEndpointIds.contains(endpointId) { + completion(strongSelf.itemInteraction?.getPeerVideo(endpointId, .mainstage)) + } else { + strongSelf.myPeerVideoReadyDisposable.set((strongSelf.readyVideoEndpointIdsPromise.get() + |> filter { $0.contains(endpointId) } + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] _ in + if let strongSelf = self { + completion(strongSelf.itemInteraction?.getPeerVideo(endpointId, .mainstage)) + } + })) + } + } else { + strongSelf.call.makeIncomingVideoView(endpointId: endpointId, requestClone: true, completion: { videoView, backdropVideoView in + if let videoView = videoView { + completion(GroupVideoNode(videoView: videoView, backdropVideoView: backdropVideoView)) + } else { + completion(nil) + } + }) + } + } + } + self.applicationStateDisposable = (self.context.sharedContext.applicationBindings.applicationIsActive |> deliverOnMainQueue).start(next: { [weak self] active in guard let strongSelf = self else { @@ -2305,6 +2339,7 @@ public final class VoiceChatController: ViewController { self.ignoreConnectingTimer?.invalidate() self.readyVideoDisposables.dispose() self.applicationStateDisposable?.dispose() + self.myPeerVideoReadyDisposable.dispose() } private func openSettingsMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) { @@ -3621,7 +3656,7 @@ public final class VoiceChatController: ViewController { if !self.mainStageNode.animating { transition.updateFrame(node: self.mainStageNode, frame: videoFrame) } - self.mainStageNode.update(size: videoFrame.size, sideInset: layout.safeInsets.left, bottomInset: bottomInset, isLandscape: self.isLandscape, isTablet: isTablet, transition: transition) + self.mainStageNode.update(size: videoFrame.size, sideInset: layout.safeInsets.left, bottomInset: bottomInset, isLandscape: videoFrame.width > videoFrame.height, isTablet: isTablet, transition: transition) let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelFrame.maxY), size: CGSize(width: size.width, height: layout.size.height)) @@ -3633,8 +3668,13 @@ public final class VoiceChatController: ViewController { leftBorderFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelFrame.maxY - additionalInset), size: CGSize(width: (size.width - contentWidth) / 2.0 + sideInset, height: layout.size.height)) rightBorderFrame = CGRect(origin: CGPoint(x: size.width - (size.width - contentWidth) / 2.0 - sideInset, y: topPanelFrame.maxY - additionalInset), size: CGSize(width: layout.safeInsets.right + (size.width - contentWidth) / 2.0 + sideInset, height: layout.size.height)) } else { - leftBorderFrame = CGRect(origin: CGPoint(x: -additionalInset, y: topPanelFrame.maxY - additionalInset * 0.6), size: CGSize(width: sideInset + additionalInset + (contentLeftInset.isZero ? additionalSideInset : contentLeftInset), height: layout.size.height)) - rightBorderFrame = CGRect(origin: CGPoint(x: size.width - sideInset - (contentLeftInset.isZero ? additionalSideInset : 0.0), y: topPanelFrame.maxY - additionalInset * 0.6), size: CGSize(width: sideInset + additionalInset + additionalSideInset, height: layout.size.height)) + var isFullscreen = false + if case .fullscreen = self.displayMode { + isFullscreen = true + forceUpdate = true + } + leftBorderFrame = CGRect(origin: CGPoint(x: -additionalInset, y: topPanelFrame.maxY - additionalInset * (isFullscreen ? 0.95 : 0.8)), size: CGSize(width: sideInset + additionalInset + (contentLeftInset.isZero ? additionalSideInset : contentLeftInset), height: layout.size.height)) + rightBorderFrame = CGRect(origin: CGPoint(x: size.width - sideInset - (contentLeftInset.isZero ? additionalSideInset : 0.0), y: topPanelFrame.maxY - additionalInset * (isFullscreen ? 0.95 : 0.8)), size: CGSize(width: sideInset + additionalInset + additionalSideInset, height: layout.size.height)) } let topCornersFrame = CGRect(x: sideInset + (contentLeftInset.isZero ? floorToScreenPixels((size.width - contentWidth) / 2.0) : contentLeftInset), y: topPanelFrame.maxY - 60.0, width: contentWidth - sideInset * 2.0, height: 50.0 + 60.0) @@ -5079,11 +5119,10 @@ public final class VoiceChatController: ViewController { self.requestedVideoSources.insert(channel.endpointId) self.call.makeIncomingVideoView(endpointId: channel.endpointId, requestClone: true, completion: { [weak self] videoView, backdropVideoView in Queue.mainQueue().async { - print("create video \(channel.endpointId)") guard let strongSelf = self, let videoView = videoView else { return } - let videoNode = GroupVideoNode(videoView: videoView, backdropVideoView: backdropVideoView, disabledText: strongSelf.presentationData.strings.VoiceChat_VideoPaused) + let videoNode = GroupVideoNode(videoView: videoView, backdropVideoView: backdropVideoView) strongSelf.readyVideoDisposables.set((combineLatest(videoNode.ready, .single(false) |> then(.single(true) |> delay(10.0, queue: Queue.mainQueue()))) |> deliverOnMainQueue @@ -5093,11 +5132,13 @@ public final class VoiceChatController: ViewController { if timeouted && !ready { strongSelf.timeoutedEndpointIds.insert(channel.endpointId) strongSelf.readyVideoEndpointIds.remove(channel.endpointId) + strongSelf.readyVideoEndpointIdsPromise.set(strongSelf.readyVideoEndpointIds) strongSelf.wideVideoNodes.remove(channel.endpointId) strongSelf.updateMembers() } else if ready { strongSelf.readyVideoEndpointIds.insert(channel.endpointId) + strongSelf.readyVideoEndpointIdsPromise.set(strongSelf.readyVideoEndpointIds) strongSelf.timeoutedEndpointIds.remove(channel.endpointId) if videoNode.aspectRatio <= 0.77 { strongSelf.wideVideoNodes.insert(channel.endpointId) @@ -5152,6 +5193,7 @@ public final class VoiceChatController: ViewController { self.videoNodes[videoEndpointId] = nil self.videoOrder.removeAll(where: { $0 == videoEndpointId }) self.readyVideoEndpointIds.remove(videoEndpointId) + self.readyVideoEndpointIdsPromise.set(self.readyVideoEndpointIds) self.readyVideoDisposables.set(nil, forKey: videoEndpointId) } } diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift index dc0c995f1a..e1574a3f2c 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift @@ -315,6 +315,10 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode { videoNode = item.getVideo() } + if videoNode?.isMainstageExclusive == true && active { + videoNode = nil + } + if let videoNode = videoNode { if active { self.avatarNode.alpha = 1.0 @@ -488,7 +492,9 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode { strongSelf.videoContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -9.0), duration: appearanceDuration, additive: true) strongSelf.audioLevelView?.layer.animateScale(from: 0.0, to: 1.0, duration: appearanceDuration) } - apperanceTransition.updateAlpha(node: currentVideoNode, alpha: 0.0) + if currentVideoNode.supernode === strongSelf.videoContainerNode { + apperanceTransition.updateAlpha(node: currentVideoNode, alpha: 0.0) + } apperanceTransition.updateAlpha(node: strongSelf.videoFadeNode, alpha: 0.0) apperanceTransition.updateAlpha(node: strongSelf.avatarNode, alpha: 1.0) if let audioLevelView = strongSelf.audioLevelView { diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatMainStageNode.swift b/submodules/TelegramCallsUI/Sources/VoiceChatMainStageNode.swift index 1468745aab..d3972732fc 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatMainStageNode.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatMainStageNode.swift @@ -78,6 +78,7 @@ final class VoiceChatMainStageNode: ASDisplayNode { var controlsHidden: ((Bool) -> Void)? var getAudioLevel: ((PeerId) -> Signal)? + var getVideo: ((String, Bool, @escaping (GroupVideoNode?) -> Void) -> Void)? private let videoReadyDisposable = MetaDisposable() private var silenceTimer: SwiftSignalKit.Timer? @@ -116,8 +117,6 @@ final class VoiceChatMainStageNode: ASDisplayNode { context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions()) }) { self.bottomFadeNode.backgroundColor = UIColor(patternImage: image) - self.bottomFadeNode.view.layer.rasterizationScale = UIScreen.main.scale - self.bottomFadeNode.view.layer.shouldRasterize = true } self.bottomFillNode = ASDisplayNode() @@ -288,6 +287,11 @@ final class VoiceChatMainStageNode: ASDisplayNode { self.layer.cornerCurve = .continuous } + self.topFadeNode.view.layer.rasterizationScale = UIScreen.main.scale + self.topFadeNode.view.layer.shouldRasterize = true + self.bottomFadeNode.view.layer.rasterizationScale = UIScreen.main.scale + self.bottomFadeNode.view.layer.shouldRasterize = true + let speakingEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark)) speakingEffectView.layer.cornerRadius = 19.0 speakingEffectView.clipsToBounds = true @@ -479,7 +483,7 @@ final class VoiceChatMainStageNode: ASDisplayNode { self.animatingOut = true let targetFrame = targetNode.view.convert(targetNode.bounds, to: self.supernode?.view) - self.currentVideoNode?.keepBackdropSize = true + let currentVideoNode = self.currentVideoNode var infoView: UIView? if let snapshotView = targetNode.infoNode.view.snapshotView(afterScreenUpdates: false) { @@ -525,6 +529,8 @@ final class VoiceChatMainStageNode: ASDisplayNode { infoView?.removeFromSuperview() textView?.removeFromSuperview() + currentVideoNode?.isMainstageExclusive = false + targetNode.transitionIn(from: nil) targetNode.alpha = 1.0 targetNode.highlightNode.layer.animateAlpha(from: 0.0, to: targetNode.highlightNode.alpha, duration: 0.2) strongSelf.animatingOut = false @@ -812,13 +818,19 @@ final class VoiceChatMainStageNode: ASDisplayNode { if !delayTransition { self.setAvatarHidden(true) } - self.call.makeIncomingVideoView(endpointId: endpointId, requestClone: true, completion: { [weak self] videoView, backdropVideoView in + + var waitForFullSize = waitForFullSize + if isMyPeer && !isPresentation && isReady && !self.appeared { + waitForFullSize = false + } + + self.getVideo?(endpointId, isMyPeer && !isPresentation, { [weak self] videoNode in Queue.mainQueue().async { - guard let strongSelf = self, let videoView = videoView else { + guard let strongSelf = self, let videoNode = videoNode else { return } - - let videoNode = GroupVideoNode(videoView: videoView, backdropVideoView: backdropVideoView, disabledText: presentationData.strings.VoiceChat_VideoPaused) + + videoNode.isMainstageExclusive = isMyPeer videoNode.tapped = { [weak self] in guard let strongSelf = self else { return @@ -846,20 +858,31 @@ final class VoiceChatMainStageNode: ASDisplayNode { videoNode.updateIsBlurred(isBlurred: isPaused, light: true, animated: false) videoNode.isUserInteractionEnabled = true let previousVideoNode = strongSelf.currentVideoNode + var previousVideoNodeSnapshot: UIView? + if let previousVideoNode = previousVideoNode, previousVideoNode.isMainstageExclusive, let snapshotView = previousVideoNode.view.snapshotView(afterScreenUpdates: false) { + previousVideoNodeSnapshot = snapshotView + snapshotView.frame = previousVideoNode.frame + previousVideoNode.view.superview?.insertSubview(snapshotView, aboveSubview: previousVideoNode.view) + } strongSelf.currentVideoNode = videoNode strongSelf.insertSubnode(videoNode, aboveSubnode: strongSelf.backdropAvatarNode) - if !isReady { + if delayTransition { + videoNode.alpha = 0.0 + } else if !isReady { videoNode.alpha = 0.0 strongSelf.topFadeNode.isHidden = true strongSelf.bottomFadeNode.isHidden = true strongSelf.bottomFillNode.isHidden = true - } else if delayTransition { - videoNode.alpha = 0.0 + } else if isMyPeer { + videoNode.layer.removeAnimation(forKey: "opacity") + videoNode.alpha = 1.0 } if waitForFullSize { + previousVideoNode?.isMainstageExclusive = false Queue.mainQueue().after(2.0) { - if let previousVideoNode = previousVideoNode { + previousVideoNodeSnapshot?.removeFromSuperview() + if let previousVideoNode = previousVideoNode, previousVideoNode.supernode === strongSelf && !previousVideoNode.isMainstageExclusive { previousVideoNode.removeFromSupernode() } } @@ -881,23 +904,36 @@ final class VoiceChatMainStageNode: ASDisplayNode { } if videoNode.alpha.isZero { - strongSelf.topFadeNode.isHidden = true - strongSelf.bottomFadeNode.isHidden = true - strongSelf.bottomFillNode.isHidden = true + if delayTransition { + strongSelf.topFadeNode.isHidden = false + strongSelf.bottomFadeNode.isHidden = false + strongSelf.bottomFillNode.isHidden = false + strongSelf.topFadeNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + strongSelf.bottomFadeNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + strongSelf.bottomFillNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + + strongSelf.avatarNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) + strongSelf.audioLevelNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) + } if let videoNode = strongSelf.currentVideoNode { videoNode.alpha = 1.0 videoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, completion: { [weak self] _ in if let strongSelf = self { strongSelf.setAvatarHidden(true) - if let previousVideoNode = previousVideoNode { + strongSelf.avatarNode.layer.removeAllAnimations() + strongSelf.audioLevelNode.layer.removeAllAnimations() + previousVideoNodeSnapshot?.removeFromSuperview() + if let previousVideoNode = previousVideoNode, previousVideoNode.supernode === strongSelf { previousVideoNode.removeFromSupernode() } } }) } } else { + previousVideoNodeSnapshot?.removeFromSuperview() + previousVideoNode?.isMainstageExclusive = false Queue.mainQueue().after(0.07) { - if let previousVideoNode = previousVideoNode { + if let previousVideoNode = previousVideoNode, previousVideoNode.supernode === strongSelf { previousVideoNode.removeFromSupernode() } } @@ -909,7 +945,11 @@ final class VoiceChatMainStageNode: ASDisplayNode { strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, isLandscape: isLandscape, isTablet: isTablet, transition: .immediate) } if let previousVideoNode = previousVideoNode { - previousVideoNode.removeFromSupernode() + previousVideoNodeSnapshot?.removeFromSuperview() + previousVideoNode.isMainstageExclusive = false + if previousVideoNode.supernode === strongSelf { + previousVideoNode.removeFromSupernode() + } } strongSelf.videoReadyDisposable.set(nil) completion?() @@ -918,7 +958,10 @@ final class VoiceChatMainStageNode: ASDisplayNode { }) } else { if let currentVideoNode = self.currentVideoNode { - currentVideoNode.removeFromSupernode() + currentVideoNode.isMainstageExclusive = false + if currentVideoNode.supernode === self { + currentVideoNode.removeFromSupernode() + } self.currentVideoNode = nil } self.setAvatarHidden(false) @@ -970,7 +1013,10 @@ final class VoiceChatMainStageNode: ASDisplayNode { } else { self.videoReadyDisposable.set(nil) if let currentVideoNode = self.currentVideoNode { - currentVideoNode.removeFromSupernode() + currentVideoNode.isMainstageExclusive = false + if currentVideoNode.supernode === self { + currentVideoNode.removeFromSupernode() + } self.currentVideoNode = nil } completion?()