From 0897f59b5b5d7713b9e835a177ca611f6aca173b Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 4 Dec 2020 10:09:19 +0400 Subject: [PATCH] Voice Chat UI improvements --- submodules/AudioBlob/Sources/BlobView.swift | 2 - .../Sources/ChatListController.swift | 2 + .../Sources/ChatListSearchContainerNode.swift | 1 + .../ContextUI/Sources/ContextController.swift | 22 +- submodules/Display/Source/ListView.swift | 23 +- .../Sources/PeerOnlineMarkerNode.swift | 4 +- .../Sources/TelegramBaseController.swift | 38 +- submodules/TelegramCallsUI/BUILD | 1 + .../Sources/CallControllerButtonsNode.swift | 3 - .../Sources/CallStatusBarNode.swift | 23 +- .../GroupCallNavigationAccessoryPanel.swift | 22 +- .../Sources/VoiceChatActionButton.swift | 57 ++- .../Sources/VoiceChatController.swift | 468 +++++++++++++----- .../Sources/VoiceChatOptionsButton.swift | 41 +- .../Sources/VoiceChatParticipantItem.swift | 29 +- ...essageContextControllerContentSource.swift | 1 + .../Sources/PeerInfo/PeerInfoScreen.swift | 2 + 17 files changed, 479 insertions(+), 260 deletions(-) diff --git a/submodules/AudioBlob/Sources/BlobView.swift b/submodules/AudioBlob/Sources/BlobView.swift index 1269b97cf5..a0ef246af7 100644 --- a/submodules/AudioBlob/Sources/BlobView.swift +++ b/submodules/AudioBlob/Sources/BlobView.swift @@ -153,7 +153,6 @@ public final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDeco } final class BlobView: UIView { - let pointsCount: Int let smoothness: CGFloat @@ -169,7 +168,6 @@ final class BlobView: UIView { var scaleLevelsToBalance = [CGFloat]() - // If true ignores randomness and pointsCount let isCircle: Bool var level: CGFloat = 0 { diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index f2c47e8b9c..ae2d56b5dd 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -2618,6 +2618,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController private final class ChatListTabBarContextExtractedContentSource: ContextExtractedContentSource { let keepInPlace: Bool = true let ignoreContentTouches: Bool = true + let blurBackground: Bool = true private let controller: ChatListController private let sourceNode: ContextExtractedContentContainingNode @@ -2639,6 +2640,7 @@ private final class ChatListTabBarContextExtractedContentSource: ContextExtracte private final class ChatListHeaderBarContextExtractedContentSource: ContextExtractedContentSource { let keepInPlace: Bool let ignoreContentTouches: Bool = true + let blurBackground: Bool = true private let controller: ChatListController private let sourceNode: ContextExtractedContentContainingNode diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 4143af93ad..145bd76645 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -956,6 +956,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo private final class MessageContextExtractedContentSource: ContextExtractedContentSource { let keepInPlace: Bool = false let ignoreContentTouches: Bool = true + let blurBackground: Bool = true private let sourceNode: ContextExtractedContentContainingNode diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index 3c21edd6b1..70a9c484de 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -211,9 +211,12 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi self.scrollNode.view.delegate = self - self.view.addSubview(self.effectView) - self.addSubnode(self.dimNode) - self.addSubnode(self.withoutBlurDimNode) + if case let .extracted(extractedSource) = source, !extractedSource.blurBackground { + } else { + self.view.addSubview(self.effectView) + self.addSubnode(self.dimNode) + self.addSubnode(self.withoutBlurDimNode) + } self.addSubnode(self.clippingNode) @@ -1074,7 +1077,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi switch layout.metrics.widthClass { case .compact: - if self.effectView.superview == nil { + if case let .extracted(extractedSource) = self.source, !extractedSource.blurBackground { + } else if self.effectView.superview == nil { self.view.insertSubview(self.effectView, at: 0) if #available(iOS 10.0, *) { if let propertyAnimator = self.propertyAnimator { @@ -1088,7 +1092,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi self.dimNode.isHidden = false self.withoutBlurDimNode.isHidden = true case .regular: - if self.effectView.superview != nil { + if case let .extracted(extractedSource) = self.source, !extractedSource.blurBackground { + } else if self.effectView.superview != nil { self.effectView.removeFromSuperview() self.withoutBlurDimNode.alpha = 1.0 } @@ -1471,6 +1476,7 @@ public final class ContextControllerPutBackViewInfo { public protocol ContextExtractedContentSource: class { var keepInPlace: Bool { get } var ignoreContentTouches: Bool { get } + var blurBackground: Bool { get } func takeView() -> ContextControllerTakeViewInfo? func putBack() -> ContextControllerPutBackViewInfo? @@ -1528,7 +1534,11 @@ public final class ContextController: ViewController, StandalonePresentableContr super.init(navigationBarPresentationData: nil) - self.statusBar.statusBarStyle = .Hide + if case let .extracted(extractedSource) = source, !extractedSource.blurBackground { + self.statusBar.statusBarStyle = .Ignore + } else { + self.statusBar.statusBarStyle = .Hide + } self.lockOrientation = true } diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index 0a29ea8295..240eb010e8 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -1009,8 +1009,14 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } if let keepMinimalScrollHeightWithTopInset = self.keepMinimalScrollHeightWithTopInset, topItemFound { - completeHeight = max(completeHeight, self.visibleSize.height + keepMinimalScrollHeightWithTopInset - effectiveInsets.bottom - effectiveInsets.top) - bottomItemEdge = max(bottomItemEdge, topItemEdge + completeHeight) + if !self.stackFromBottom { + completeHeight = max(completeHeight, self.visibleSize.height + keepMinimalScrollHeightWithTopInset - effectiveInsets.bottom - effectiveInsets.top) + bottomItemEdge = max(bottomItemEdge, topItemEdge + completeHeight) + } else { + effectiveInsets.top = self.visibleSize.height - completeHeight + completeHeight = max(completeHeight, self.visibleSize.height) + bottomItemEdge = max(bottomItemEdge, topItemEdge + completeHeight) + } } var transition: ContainedViewLayoutTransition = .immediate @@ -1094,6 +1100,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } } + print("off \(offset)") if abs(offset) > CGFloat.ulpOfOne { self.didScrollWithOffset?(-offset, .immediate, nil) @@ -1413,8 +1420,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } if let keepMinimalScrollHeightWithTopInset = self.keepMinimalScrollHeightWithTopInset { - completeHeight = max(completeHeight, self.visibleSize.height + keepMinimalScrollHeightWithTopInset) - bottomItemEdge = max(bottomItemEdge, topItemEdge + completeHeight) + if !self.stackFromBottom { + completeHeight = max(completeHeight, self.visibleSize.height + keepMinimalScrollHeightWithTopInset) + bottomItemEdge = max(bottomItemEdge, topItemEdge + completeHeight) + } } if self.stackFromBottom { @@ -1435,11 +1444,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture self.ignoreScrollingEvents = true if topItemFound && bottomItemFound { self.scroller.contentSize = CGSize(width: self.visibleSize.width, height: completeHeight) - if self.stackFromBottom { - self.lastContentOffset = CGPoint(x: 0.0, y: -topItemEdge) - } else { - self.lastContentOffset = CGPoint(x: 0.0, y: -topItemEdge) - } + self.lastContentOffset = CGPoint(x: 0.0, y: -topItemEdge) self.scroller.contentOffset = self.lastContentOffset } else if topItemFound { self.scroller.contentSize = CGSize(width: self.visibleSize.width, height: infiniteScrollSize * 2.0) diff --git a/submodules/PeerOnlineMarkerNode/Sources/PeerOnlineMarkerNode.swift b/submodules/PeerOnlineMarkerNode/Sources/PeerOnlineMarkerNode.swift index 4fa87a9f0d..740b1154bf 100644 --- a/submodules/PeerOnlineMarkerNode/Sources/PeerOnlineMarkerNode.swift +++ b/submodules/PeerOnlineMarkerNode/Sources/PeerOnlineMarkerNode.swift @@ -8,7 +8,7 @@ private final class VoiceChatIndicatorNode: ASDisplayNode { private let centerLine: ASDisplayNode private let rightLine: ASDisplayNode - private var isCurrentlyInHierarchy = false + private var isCurrentlyInHierarchy = true private var shouldBeAnimating = false var color: UIColor = UIColor(rgb: 0xffffff) { @@ -45,6 +45,8 @@ private final class VoiceChatIndicatorNode: ASDisplayNode { self.addSubnode(self.leftLine) self.addSubnode(self.centerLine) self.addSubnode(self.rightLine) + + self.updateAnimation() } override func didEnterHierarchy() { diff --git a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift index 0d25c3e4e3..76ce9b5ad5 100644 --- a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift +++ b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift @@ -265,36 +265,15 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { case .none, .all: break case let .peer(peerId): - let currentGroupCall: Signal = callManager.currentGroupCallSignal + let currentGroupCall: Signal = callManager.currentGroupCallSignal |> distinctUntilChanged(isEqual: { lhs, rhs in return lhs?.internalId == rhs?.internalId }) - |> mapToSignal { call -> Signal in + |> map { call -> PresentationGroupCall? in guard let call = call, call.peerId == peerId else { - return .single(nil) + return nil } - return call.summaryState - |> filter { $0 != nil } - |> map { summary -> GroupCallPanelData? in - guard let summary = summary else { - return nil - } - return GroupCallPanelData( - peerId: call.peerId, - info: summary.info, - topParticipants: summary.topParticipants, - participantCount: summary.participantCount, - numberOfActiveSpeakers: summary.numberOfActiveSpeakers, - groupCall: call - ) - } - |> take(until: { summary in - if summary != nil { - return SignalTakeAction(passthrough: true, complete: true) - } else { - return SignalTakeAction(passthrough: true, complete: false) - } - }) + return call } let availableGroupCall: Signal @@ -373,12 +352,17 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { self.currentGroupCallDisposable = combineLatest(queue: .mainQueue(), currentGroupCall, availableGroupCall - ).start(next: { [weak self] currentState, availableState in + ).start(next: { [weak self] currentGroupCall, availableState in guard let strongSelf = self else { return } - let panelData = currentState != nil ? nil : availableState + let panelData: GroupCallPanelData? + if let _ = currentGroupCall { + panelData = nil + } else { + panelData = availableState + } let wasEmpty = strongSelf.groupCallPanelData == nil strongSelf.groupCallPanelData = panelData diff --git a/submodules/TelegramCallsUI/BUILD b/submodules/TelegramCallsUI/BUILD index 8863e82a35..d8b4485c55 100644 --- a/submodules/TelegramCallsUI/BUILD +++ b/submodules/TelegramCallsUI/BUILD @@ -36,6 +36,7 @@ swift_library( "//submodules/AudioBlob:AudioBlob", "//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode", "//submodules/AlertUI:AlertUI", + "//submodules/DirectionalPanGesture:DirectionalPanGesture", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift b/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift index 466d3f63ec..1612f1cc91 100644 --- a/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift +++ b/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift @@ -219,15 +219,12 @@ final class CallControllerButtonsNode: ASDisplayNode { if videoState.isAvailable { let isCameraActive: Bool - let isCameraEnabled: Bool let isCameraInitializing: Bool if videoState.hasVideo { isCameraActive = videoState.isCameraActive - isCameraEnabled = videoState.canChangeStatus isCameraInitializing = videoState.isInitializingCamera } else { isCameraActive = false - isCameraEnabled = videoState.canChangeStatus isCameraInitializing = videoState.isInitializingCamera } topButtons.append(.enableCamera(isCameraActive, false, isCameraInitializing)) diff --git a/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift b/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift index f7a3b71250..a1d5375a81 100644 --- a/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift +++ b/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift @@ -42,7 +42,7 @@ private class CallStatusBarBackgroundNode: ASDisplayNode { } private let hierarchyTrackingNode: HierarchyTrackingNode - private var isCurrentlyInHierarchy = false + private var isCurrentlyInHierarchy = true override init() { self.foregroundView = UIView() @@ -89,9 +89,11 @@ private class CallStatusBarBackgroundNode: ASDisplayNode { CATransaction.begin() CATransaction.setDisableActions(true) - self.foregroundView.frame = self.bounds - self.foregroundGradientLayer.frame = self.bounds - self.maskCurveView.frame = self.bounds + if self.maskCurveView.frame != self.bounds { + self.foregroundView.frame = self.bounds + self.foregroundGradientLayer.frame = self.bounds + self.maskCurveView.frame = self.bounds + } CATransaction.commit() } @@ -324,7 +326,7 @@ private final class VoiceCurveView: UIView { minRandomness: 1, maxRandomness: 1.3, minSpeed: 0.9, - maxSpeed: 3.5, + maxSpeed: 3.2, minOffset: smallCurveRange.min, maxOffset: smallCurveRange.max ) @@ -333,7 +335,7 @@ private final class VoiceCurveView: UIView { minRandomness: 1.2, maxRandomness: 1.5, minSpeed: 1.0, - maxSpeed: 4.5, + maxSpeed: 4.4, minOffset: mediumCurveRange.min, maxOffset: mediumCurveRange.max ) @@ -342,7 +344,7 @@ private final class VoiceCurveView: UIView { minRandomness: 1.2, maxRandomness: 1.7, minSpeed: 1.0, - maxSpeed: 6.0, + maxSpeed: 5.8, minOffset: bigCurveRange.min, maxOffset: bigCurveRange.max ) @@ -476,7 +478,7 @@ final class CurveView: UIView { override var frame: CGRect { didSet { - if self.frame != oldValue { + if self.frame.size != oldValue.size { self.fromPoints = nil self.toPoints = nil self.animateToNewShape() @@ -534,7 +536,7 @@ final class CurveView: UIView { func updateSpeedLevel(to newSpeedLevel: CGFloat) { speedLevel = max(speedLevel, newSpeedLevel) - if abs(lastSpeedLevel - newSpeedLevel) > 0.3 { + if abs(lastSpeedLevel - newSpeedLevel) > 0.45 { animateToNewShape() } } @@ -642,7 +644,8 @@ final class CurveView: UIView { CATransaction.begin() CATransaction.setDisableActions(true) - shapeLayer.frame = self.bounds + shapeLayer.position = CGPoint(x: self.bounds.width / 2.0, y: self.bounds.height / 2.0) + shapeLayer.bounds = self.bounds CATransaction.commit() } } diff --git a/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift b/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift index 346dc68745..75e7cd9923 100644 --- a/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift +++ b/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift @@ -280,20 +280,13 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { } let membersText: String - let membersTextIsActive: Bool if summaryState.participantCount == 0 { membersText = strongSelf.strings.VoiceChat_Panel_TapToJoin } else { membersText = strongSelf.strings.VoiceChat_Panel_Members(Int32(summaryState.participantCount)) } - membersTextIsActive = false - - if strongSelf.textIsActive != membersTextIsActive { - strongSelf.textIsActive = membersTextIsActive - strongSelf.animateTextChange() - } - - strongSelf.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: membersTextIsActive ? strongSelf.theme.chat.inputPanel.panelControlAccentColor : strongSelf.theme.chat.inputPanel.secondaryTextColor) + + strongSelf.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: strongSelf.theme.chat.inputPanel.secondaryTextColor) strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.map { $0.peer }, animated: false) @@ -366,22 +359,15 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { self.audioLevelDisposable.set(nil) let membersText: String - let membersTextIsActive: Bool if data.participantCount == 0 { membersText = self.strings.VoiceChat_Panel_TapToJoin } else { membersText = self.strings.VoiceChat_Panel_Members(Int32(data.participantCount)) } - membersTextIsActive = false - if self.textIsActive != membersTextIsActive { - self.textIsActive = membersTextIsActive - self.animateTextChange() - } + self.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor) - self.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: membersTextIsActive ? self.theme.chat.inputPanel.panelControlAccentColor : self.theme.chat.inputPanel.secondaryTextColor) - - self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }, animated: false) + self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }.filter { $0.id != self.context.account.peerId }, animated: false) } if let (size, leftInset, rightInset) = self.validLayout { diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatActionButton.swift b/submodules/TelegramCallsUI/Sources/VoiceChatActionButton.swift index 5a6ab01307..129e0636ce 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatActionButton.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatActionButton.swift @@ -9,12 +9,13 @@ private let titleFont = Font.regular(17.0) private let subtitleFont = Font.regular(13.0) private let white = UIColor(rgb: 0xffffff) -private let greyColor = UIColor(rgb: 0x1c1c1e) +private let greyColor = UIColor(rgb: 0x2c2c2e) +private let secondaryGreyColor = UIColor(rgb: 0x1c1c1e) private let blue = UIColor(rgb: 0x0078ff) private let lightBlue = UIColor(rgb: 0x59c7f8) private let green = UIColor(rgb: 0x33c659) -private let areaSize = CGSize(width: 370.0, height: 370.0) +private let areaSize = CGSize(width: 440.0, height: 440.0) private let blobSize = CGSize(width: 244.0, height: 244.0) final class VoiceChatActionButton: HighlightTrackingButtonNode { @@ -35,7 +36,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode { private let titleLabel: ImmediateTextNode private let subtitleLabel: ImmediateTextNode - private var currentParams: (size: CGSize, buttonSize: CGSize, state: VoiceChatActionButton.State, small: Bool, title: String, subtitle: String)? + private var currentParams: (size: CGSize, buttonSize: CGSize, state: VoiceChatActionButton.State, dark: Bool, small: Bool, title: String, subtitle: String)? private var activePromise = ValuePromise(false) private var outerColorPromise = ValuePromise(nil) @@ -48,10 +49,10 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode { didSet { if self.pressing { let transition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .spring) - transition.updateTransformScale(node: self.containerNode, scale: 0.9) + transition.updateTransformScale(node: self.iconNode, scale: 0.9) } else { let transition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .spring) - transition.updateTransformScale(node: self.containerNode, scale: 1.0) + transition.updateTransformScale(node: self.iconNode, scale: 1.0) } } } @@ -77,10 +78,10 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode { if let strongSelf = self { if highlighted { let transition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .spring) - transition.updateTransformScale(node: strongSelf.containerNode, scale: 0.9) + transition.updateTransformScale(node: strongSelf.iconNode, scale: 0.9) } else if !strongSelf.pressing { let transition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .spring) - transition.updateTransformScale(node: strongSelf.containerNode, scale: 1.0) + transition.updateTransformScale(node: strongSelf.iconNode, scale: 1.0) } } } @@ -103,7 +104,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode { } func applyParams(animated: Bool) { - guard let (size, _, _, small, title, subtitle) = self.currentParams else { + guard let (size, _, _, _, small, title, subtitle) = self.currentParams else { return } @@ -136,7 +137,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode { let subtitleSize = self.subtitleLabel.updateLayout(CGSize(width: size.width, height: .greatestFiniteMagnitude)) let totalHeight = titleSize.height + subtitleSize.height + 1.0 - self.titleLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: floor(size.height - totalHeight / 2.0) - 75.0), size: titleSize) + self.titleLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: floor(size.height - totalHeight / 2.0) - 110.0), size: titleSize) self.subtitleLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - subtitleSize.width) / 2.0), y: self.titleLabel.frame.maxY + 1.0), size: subtitleSize) self.containerNode.frame = CGRect(origin: CGPoint(), size: size) @@ -153,9 +154,9 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode { self.iconNode.frame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize) } - func update(size: CGSize, buttonSize: CGSize, state: VoiceChatActionButton.State, title: String, subtitle: String, small: Bool, animated: Bool = false) { + func update(size: CGSize, buttonSize: CGSize, state: VoiceChatActionButton.State, title: String, subtitle: String, dark: Bool, small: Bool, animated: Bool = false) { let previousState = self.currentParams?.state - self.currentParams = (size, buttonSize, state, small, title, subtitle) + self.currentParams = (size, buttonSize, state, dark, small, title, subtitle) var iconMuted = true var iconColor: UIColor = .white @@ -175,6 +176,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode { case .connecting: backgroundState = .connecting } + self.backgroundNode.updateColor(dark: dark) self.backgroundNode.update(state: backgroundState, animated: true) self.iconNode.update(state: VoiceChatMicrophoneNode.State(muted: iconMuted, color: iconColor), animated: true) @@ -193,7 +195,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { var hitRect = self.bounds - if let (_, buttonSize, _, _, _, _) = self.currentParams { + if let (_, buttonSize, _, _, _, _, _) = self.currentParams { hitRect = self.bounds.insetBy(dx: (self.bounds.width - buttonSize.width) / 2.0, dy: (self.bounds.height - buttonSize.height) / 2.0) } let result = super.hitTest(point, with: event) @@ -334,7 +336,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode { override init() { self.state = .connecting - self.maskBlobView = VoiceBlobView(frame: CGRect(origin: CGPoint(x: (areaSize.width - blobSize.width) / 2.0, y: (areaSize.height - blobSize.height) / 2.0), size: blobSize), maxLevel: 2.5, mediumBlobRange: (0.69, 0.87), bigBlobRange: (0.71, 1.0)) + self.maskBlobView = VoiceBlobView(frame: CGRect(origin: CGPoint(x: (areaSize.width - blobSize.width) / 2.0, y: (areaSize.height - blobSize.height) / 2.0), size: blobSize), maxLevel: 2.0, mediumBlobRange: (0.69, 0.87), bigBlobRange: (0.71, 1.0)) self.maskBlobView.setColor(white) var updateInHierarchy: ((Bool) -> Void)? @@ -364,7 +366,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode { self.maskView.backgroundColor = .clear self.maskGradientLayer.type = .radial - self.maskGradientLayer.colors = [UIColor(rgb: 0xffffff, alpha: 0.7).cgColor, UIColor(rgb: 0xffffff, alpha: 0.0).cgColor] + self.maskGradientLayer.colors = [UIColor(rgb: 0xffffff, alpha: 0.4).cgColor, UIColor(rgb: 0xffffff, alpha: 0.0).cgColor] self.maskGradientLayer.startPoint = CGPoint(x: 0.5, y: 0.5) self.maskGradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0) self.maskGradientLayer.transform = CATransform3DMakeScale(0.3, 0.3, 1.0) @@ -446,7 +448,7 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode { } else { let animation = CABasicAnimation(keyPath: "transform.rotation.z") animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) - animation.duration = 1.5 + animation.duration = 1.0 animation.fromValue = NSNumber(value: Float(0.0)) animation.toValue = NSNumber(value: Float.pi * 2.0) animation.repeatCount = Float.infinity @@ -493,16 +495,16 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode { if let active = active { if active { targetColors = [blue.cgColor, green.cgColor] - targetScale = 0.95 + targetScale = 0.89 outerColor = UIColor(rgb: 0x005720) } else { targetColors = [lightBlue.cgColor, blue.cgColor] - targetScale = 0.8 + targetScale = 0.85 outerColor = UIColor(rgb: 0x00274d) } } else { targetColors = [lightBlue.cgColor, blue.cgColor] - targetScale = 0.35 + targetScale = 0.3 outerColor = nil } self.updatedOuterColor?(outerColor) @@ -606,9 +608,10 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode { self?.transition = nil } } else if transition == .disabled { - + self.transition = nil } else if case let .blob(previousActive) = transition { updateGlowAndGradientAnimations(active: newActive, previousActive: previousActive) + self.transition = nil } } else { self.maskBlobView.startAnimating() @@ -618,6 +621,20 @@ private final class VoiceChatActionButtonBackgroundNode: ASDisplayNode { } } + func updateColor(dark: Bool) { + let previousColor: CGColor = self.backgroundCircleLayer.fillColor ?? greyColor.cgColor + let targetColor: CGColor + if dark { + targetColor = secondaryGreyColor.cgColor + } else { + targetColor = greyColor.cgColor + } + self.backgroundCircleLayer.fillColor = targetColor + self.foregroundCircleLayer.fillColor = targetColor + self.backgroundCircleLayer.animate(from: previousColor, to: targetColor, keyPath: "fillColor", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.3) + self.foregroundCircleLayer.animate(from: previousColor, to: targetColor, keyPath: "fillColor", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.3) + } + func update(state: State, animated: Bool) { var animated = animated var hadState = true @@ -868,7 +885,7 @@ final class BlobView: UIView { func updateSpeedLevel(to newSpeedLevel: CGFloat) { speedLevel = max(speedLevel, newSpeedLevel) - if abs(lastSpeedLevel - newSpeedLevel) > 0.3 { + if abs(lastSpeedLevel - newSpeedLevel) > 0.45 { animateToNewShape() } } diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 6bae26899a..6c29438237 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -20,6 +20,38 @@ import DeleteChatPeerActionSheetItem import UndoUI import AlertUI import PresentationDataUtils +import DirectionalPanGesture + +private let panelBackgroundColor = UIColor(rgb: 0x1c1c1e) +private let secondaryPanelBackgroundColor = UIColor(rgb: 0x2c2c2e) +private let fullscreenBackgroundColor = UIColor(rgb: 0x000000) +private let dimColor = UIColor(white: 0.0, alpha: 0.5) + +private func cornersImage(top: Bool, bottom: Bool, dark: Bool) -> UIImage? { + if !top && !bottom { + return nil + } + return generateImage(CGSize(width: 50.0, height: 50.0), rotatedContext: { (size, context) in + let bounds = CGRect(origin: CGPoint(), size: size) + context.setFillColor((dark ? fullscreenBackgroundColor : panelBackgroundColor).cgColor) + context.fill(bounds) + + context.setBlendMode(.clear) + + var corners: UIRectCorner = [] + if top { + corners.insert(.topLeft) + corners.insert(.topRight) + } + if bottom { + corners.insert(.bottomLeft) + corners.insert(.bottomRight) + } + let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: 11.0, height: 11.0)) + context.addPath(path.cgPath) + context.fillPath() + })?.stretchableImage(withLeftCapWidth: 25, topCapHeight: 25) +} private final class VoiceChatControllerTitleView: UIView { private var theme: PresentationTheme @@ -57,12 +89,6 @@ private final class VoiceChatControllerTitleView: UIView { self.infoNode.attributedText = NSAttributedString(string: subtitle, font: Font.regular(13.0), textColor: UIColor.white.withAlphaComponent(0.5)) } - func animateIn(duration: Double) { - self.titleNode.layer.animatePosition(from: CGPoint(x: 0.0, y: 49.0), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true) - self.infoNode.layer.animatePosition(from: CGPoint(x: 0.0, y: 49.0), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true) - self.titleNode.layer.animateScale(from: 0.882, to: 1.0, duration: duration, timingFunction: kCAMediaTimingFunctionSpring) - } - override func layoutSubviews() { super.layoutSubviews() @@ -340,21 +366,26 @@ public final class VoiceChatController: ViewController { private var presentationData: PresentationData private var darkTheme: PresentationTheme - private let optionsButton: VoiceChatOptionsButton - private let closeButton: HighlightableButtonNode - private let dimNode: ASDisplayNode private let contentContainer: ASDisplayNode private let backgroundNode: ASDisplayNode private let listNode: ListView + private let topPanelNode: ASDisplayNode + private let optionsButton: VoiceChatHeaderButton + private let closeButton: VoiceChatHeaderButton + private let topCornersNode: ASImageNode + private let bottomPanelNode: ASDisplayNode + private let bottomCornersNode: ASImageNode private let audioOutputNode: CallControllerButtonItemNode private let leaveNode: CallControllerButtonItemNode private let actionButton: VoiceChatActionButton + private let leftBorderNode: ASDisplayNode + private let rightBorderNode: ASDisplayNode private let titleView: VoiceChatControllerTitleView private var enqueuedTransitions: [ListTransition] = [] - private var maxListHeight: CGFloat? + private var floatingHeaderOffset: CGFloat? private var validLayout: (ContainerViewLayout, CGFloat)? private var didSetContentsReady: Bool = false @@ -403,31 +434,60 @@ public final class VoiceChatController: ViewController { self.presentationData = sharedContext.currentPresentationData.with { $0 } self.darkTheme = defaultDarkColorPresentationTheme - - self.optionsButton = VoiceChatOptionsButton() - self.closeButton = HighlightableButtonNode() - + self.dimNode = ASDisplayNode() - self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5) + self.dimNode.backgroundColor = dimColor self.contentContainer = ASDisplayNode() self.backgroundNode = ASDisplayNode() - self.backgroundNode.backgroundColor = UIColor(rgb: 0x000000) - self.backgroundNode.cornerRadius = 12.0 + self.backgroundNode.backgroundColor = secondaryPanelBackgroundColor self.listNode = ListView() - self.listNode.backgroundColor = self.darkTheme.list.itemBlocksBackgroundColor self.listNode.verticalScrollIndicatorColor = UIColor(white: 1.0, alpha: 0.3) self.listNode.clipsToBounds = true - self.listNode.cornerRadius = 12.0 + self.listNode.stackFromBottom = true + self.listNode.keepMinimalScrollHeightWithTopInset = 0 + + self.topPanelNode = ASDisplayNode() + self.topPanelNode.backgroundColor = panelBackgroundColor + self.topPanelNode.clipsToBounds = false + self.topPanelNode.layer.cornerRadius = 12.0 + + self.optionsButton = VoiceChatHeaderButton() + self.optionsButton.setImage(optionsButtonImage(dark: false)) + self.closeButton = VoiceChatHeaderButton() + self.closeButton.setImage(closeButtonImage(dark: false)) + + self.titleView = VoiceChatControllerTitleView(theme: self.presentationData.theme) + self.titleView.set(title: self.presentationData.strings.VoiceChat_Title, subtitle: self.presentationData.strings.SocksProxySetup_ProxyStatusConnecting) + self.titleView.isUserInteractionEnabled = false + + self.topCornersNode = ASImageNode() + self.topCornersNode.displaysAsynchronously = false + self.topCornersNode.displayWithoutProcessing = true + self.topCornersNode.image = cornersImage(top: true, bottom: false, dark: false) + + self.bottomPanelNode = ASDisplayNode() + self.bottomPanelNode.backgroundColor = panelBackgroundColor + self.bottomPanelNode.clipsToBounds = false + + self.bottomCornersNode = ASImageNode() + self.bottomCornersNode.displaysAsynchronously = false + self.bottomCornersNode.displayWithoutProcessing = true + self.bottomCornersNode.image = cornersImage(top: false, bottom: true, dark: false) self.audioOutputNode = CallControllerButtonItemNode() self.leaveNode = CallControllerButtonItemNode() self.actionButton = VoiceChatActionButton() - - self.titleView = VoiceChatControllerTitleView(theme: self.presentationData.theme) - self.titleView.set(title: self.presentationData.strings.VoiceChat_Title, subtitle: self.presentationData.strings.SocksProxySetup_ProxyStatusConnecting) + + self.leftBorderNode = ASDisplayNode() + self.leftBorderNode.backgroundColor = panelBackgroundColor + self.leftBorderNode.isUserInteractionEnabled = false + + self.rightBorderNode = ASDisplayNode() + self.rightBorderNode.backgroundColor = panelBackgroundColor + self.rightBorderNode.isUserInteractionEnabled = false super.init() @@ -521,7 +581,7 @@ public final class VoiceChatController: ViewController { return } - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme), source: .extracted(VoiceChatContextExtractedContentSource(controller: controller, sourceNode: sourceNode, keepInPlace: false)), items: .single(items), reactionItems: [], gesture: gesture) + let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme), source: .extracted(VoiceChatContextExtractedContentSource(controller: controller, sourceNode: sourceNode, keepInPlace: false, blurBackground: true)), items: .single(items), reactionItems: [], gesture: gesture) strongSelf.controller?.presentInGlobalOverlay(contextController) }, setPeerIdWithRevealedOptions: { peerId, _ in updateState { state in @@ -531,18 +591,25 @@ public final class VoiceChatController: ViewController { } }) - self.backgroundNode.addSubnode(self.listNode) - self.backgroundNode.addSubnode(self.audioOutputNode) - self.backgroundNode.addSubnode(self.leaveNode) - self.backgroundNode.addSubnode(self.actionButton) - self.backgroundNode.view.addSubview(self.titleView) - self.backgroundNode.addSubnode(self.optionsButton) - self.backgroundNode.addSubnode(self.closeButton) + self.topPanelNode.view.addSubview(self.titleView) + self.topPanelNode.addSubnode(self.optionsButton) + self.topPanelNode.addSubnode(self.closeButton) + self.topPanelNode.addSubnode(self.topCornersNode) + + self.bottomPanelNode.addSubnode(self.bottomCornersNode) + self.bottomPanelNode.addSubnode(self.audioOutputNode) + self.bottomPanelNode.addSubnode(self.leaveNode) + self.bottomPanelNode.addSubnode(self.actionButton) self.addSubnode(self.dimNode) self.addSubnode(self.contentContainer) self.contentContainer.addSubnode(self.backgroundNode) - + self.contentContainer.addSubnode(self.listNode) + self.contentContainer.addSubnode(self.topPanelNode) + self.contentContainer.addSubnode(self.leftBorderNode) + self.contentContainer.addSubnode(self.rightBorderNode) + self.contentContainer.addSubnode(self.bottomPanelNode) + self.memberStatesDisposable = (self.call.members |> deliverOnMainQueue).start(next: { [weak self] callMembers in guard let strongSelf = self, let callMembers = callMembers else { @@ -558,14 +625,6 @@ public final class VoiceChatController: ViewController { strongSelf.titleView.set(title: strongSelf.presentationData.strings.VoiceChat_Title, subtitle: subtitle) }) - self.listNode.visibleBottomContentOffsetChanged = { [weak self] offset in - guard let strongSelf = self else { - return - } - if case let .known(value) = offset, value < 40.0 { - } - } - self.peerViewDisposable = (combineLatest(self.context.account.viewTracker.peerView(self.call.peerId), self.context.account.postbox.loadedPeerWithId(self.context.account.peerId)) |> deliverOnMainQueue).start(next: { [weak self] view, accountPeer in guard let strongSelf = self else { @@ -762,14 +821,32 @@ public final class VoiceChatController: ViewController { }))) } - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme), source: .extracted(VoiceChatContextExtractedContentSource(controller: controller, sourceNode: strongOptionsButton.extractedContainerNode, keepInPlace: true)), items: .single(items), reactionItems: [], gesture: gesture) + let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme), source: .extracted(VoiceChatContextExtractedContentSource(controller: controller, sourceNode: strongOptionsButton.extractedContainerNode, keepInPlace: true, blurBackground: false)), items: .single(items), reactionItems: [], gesture: gesture) strongSelf.controller?.presentInGlobalOverlay(contextController) } - self.closeButton.setImage(closeButtonImage(), for: [.normal]) + self.optionsButton.addTarget(self, action: #selector(self.optionsPressed), forControlEvents: .touchUpInside) self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: .touchUpInside) - self.optionsButton.addTarget(self, action: #selector(self.optionsPressed), forControlEvents: .touchUpInside) + self.listNode.updateFloatingHeaderOffset = { [weak self] offset, transition in + if let strongSelf = self { + strongSelf.updateFloatingHeaderOffset(offset: offset, transition: transition) + } + } + + self.listNode.endedInteractiveDragging = { [weak self] in + guard let strongSelf = self else { + return + } + switch strongSelf.listNode.visibleContentOffset() { + case let .known(value): + if value <= -10.0 { +// strongSelf.controller?.dismiss() + } + default: + break + } + } } deinit { @@ -793,14 +870,22 @@ public final class VoiceChatController: ViewController { longTapRecognizer.delegate = self self.actionButton.view.addGestureRecognizer(longTapRecognizer) - let panRecognizer = CallPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))) - panRecognizer.shouldBegin = { [weak self] _ in - guard let _ = self else { - return false - } - return true - } - self.backgroundNode.view.addGestureRecognizer(panRecognizer) + let panRecognizer = DirectionalPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))) + panRecognizer.delegate = self + panRecognizer.delaysTouchesBegan = false + panRecognizer.cancelsTouchesInView = true +// panRecognizer.shouldBegin = { [weak self] point in +// guard let strongSelf = self else { +// return false +// } +// if strongSelf.topPanelNode.bounds.contains(strongSelf.view.convert(point, to: strongSelf.topPanelNode.view)) { +// if strongSelf.topPanelNode.frame.maxY <= strongSelf.listNode.frame.minY { +// return true +// } +// } +// return false +// } + self.view.addGestureRecognizer(panRecognizer) } @objc private func optionsPressed() { @@ -829,15 +914,6 @@ public final class VoiceChatController: ViewController { } private var actionButtonPressGestureStartTime: Double = 0.0 - - override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { - if let callState = self.callState, case .connected = callState.networkState, let muteState = callState.muteState, !muteState.canUnmute { - return false - } else { - return true - } - } - @objc private func actionButtonPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer) { guard let callState = self.callState else { return @@ -955,6 +1031,101 @@ public final class VoiceChatController: ViewController { } } + private func updateFloatingHeaderOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) { + guard let (validLayout, _) = self.validLayout else { + return + } + + self.floatingHeaderOffset = offset + + let layoutTopInset: CGFloat = max(validLayout.statusBarHeight ?? 0.0, validLayout.safeInsets.top) + + let topPanelHeight: CGFloat = 63.0 + let listTopInset = layoutTopInset + topPanelHeight + + let rawPanelOffset = offset + listTopInset - topPanelHeight + let panelOffset = max(layoutTopInset, rawPanelOffset) + let topPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: panelOffset), size: CGSize(width: validLayout.size.width, height: topPanelHeight)) + + let previousFrame = self.topPanelNode.frame + if !topPanelFrame.equalTo(previousFrame) { + self.topPanelNode.frame = topPanelFrame + + let positionDelta = CGPoint(x: topPanelFrame.minX - previousFrame.minX, y: topPanelFrame.minY - previousFrame.minY) + transition.animateOffsetAdditive(node: self.topPanelNode, offset: positionDelta.y) + } + + let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelFrame.maxY), size: CGSize(width: validLayout.size.width, height: validLayout.size.height)) + + let sideInset: CGFloat = 16.0 + let leftBorderFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelFrame.maxY - 16.0), size: CGSize(width: sideInset, height: validLayout.size.height)) + let rightBorderFrame = CGRect(origin: CGPoint(x: validLayout.size.width - sideInset, y: topPanelFrame.maxY - 16.0), size: CGSize(width: sideInset, height: validLayout.size.height)) + + let previousBackgroundFrame = self.backgroundNode.frame + let previousLeftBorderFrame = self.leftBorderNode.frame + let previousRightBorderFrame = self.rightBorderNode.frame + + self.updateColors(fullscreen: panelOffset == layoutTopInset) + + if !backgroundFrame.equalTo(previousBackgroundFrame) { + self.backgroundNode.frame = backgroundFrame + self.leftBorderNode.frame = leftBorderFrame + self.rightBorderNode.frame = rightBorderFrame + + let backgroundPositionDelta = CGPoint(x: backgroundFrame.minX - previousBackgroundFrame.minX, y: backgroundFrame.minY - previousBackgroundFrame.minY) + transition.animateOffsetAdditive(node: self.backgroundNode, offset: backgroundPositionDelta.y) + + let leftBorderPositionDelta = CGPoint(x: leftBorderFrame.minX - previousLeftBorderFrame.minX, y: leftBorderFrame.minY - previousLeftBorderFrame.minY) + transition.animateOffsetAdditive(node: self.leftBorderNode, offset: leftBorderPositionDelta.y) + + let rightBorderPositionDelta = CGPoint(x: rightBorderFrame.minX - previousRightBorderFrame.minX, y: rightBorderFrame.minY - previousRightBorderFrame.minY) + transition.animateOffsetAdditive(node: self.rightBorderNode, offset: rightBorderPositionDelta.y) + } + } + + var isFullscreen = false + func updateColors(fullscreen: Bool) { + guard self.isFullscreen != fullscreen else { + return + } + self.isFullscreen = fullscreen + + self.controller?.statusBar.statusBarStyle = fullscreen ? .White : .Ignore + + let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .linear) + + transition.updateBackgroundColor(node: self.dimNode, color: fullscreen ? fullscreenBackgroundColor : dimColor) + transition.updateBackgroundColor(node: self.backgroundNode, color: fullscreen ? panelBackgroundColor : secondaryPanelBackgroundColor) + transition.updateBackgroundColor(node: self.topPanelNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor) + transition.updateBackgroundColor(node: self.bottomPanelNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor) + transition.updateBackgroundColor(node: self.leftBorderNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor) + transition.updateBackgroundColor(node: self.rightBorderNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor) + transition.updateBackgroundColor(node: self.rightBorderNode, color: fullscreen ? fullscreenBackgroundColor : panelBackgroundColor) + + if let snapshotView = self.topCornersNode.view.snapshotContentTree() { + snapshotView.frame = self.topCornersNode.frame + self.topPanelNode.view.addSubview(snapshotView) + + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + self.topCornersNode.image = cornersImage(top: true, bottom: false, dark: fullscreen) + + if let snapshotView = self.bottomCornersNode.view.snapshotContentTree() { + snapshotView.frame = self.bottomCornersNode.frame + self.bottomPanelNode.view.addSubview(snapshotView) + + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + self.bottomCornersNode.image = cornersImage(top: false, bottom: true, dark: fullscreen) + + self.optionsButton.setImage(optionsButtonImage(dark: fullscreen), animated: transition.isAnimated) + self.closeButton.setImage(closeButtonImage(dark: fullscreen), animated: transition.isAnimated) + } + func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) { let isFirstTime = self.validLayout == nil self.validLayout = (layout, navigationHeight) @@ -965,31 +1136,41 @@ public final class VoiceChatController: ViewController { transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size)) - let contentHeight: CGFloat = layout.size.height - 240.0 - transition.updateFrame(node: self.contentContainer, frame: CGRect(origin: CGPoint(), size: layout.size)) - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - contentHeight), size: CGSize(width: layout.size.width, height: contentHeight + 1000.0))) - let bottomAreaHeight: CGFloat = 290.0 - let listOrigin = CGPoint(x: 16.0, y: 64.0) + let bottomAreaHeight: CGFloat = 268.0 + let layoutTopInset: CGFloat = max(layout.statusBarHeight ?? 0.0, layout.safeInsets.top) - var listHeight: CGFloat = 44.0 + 56.0 - if let maxListHeight = self.maxListHeight { - listHeight = min(max(1.0, contentHeight - bottomAreaHeight - listOrigin.y - layout.intrinsicInsets.bottom + 25.0), maxListHeight + 44.0) - } + let sideInset: CGFloat = 16.0 + var insets = UIEdgeInsets() + insets.left = layout.safeInsets.left + sideInset + insets.right = layout.safeInsets.right + sideInset - let listFrame = CGRect(origin: listOrigin, size: CGSize(width: layout.size.width - 16.0 * 2.0, height: listHeight)) - transition.updateFrame(node: self.listNode, frame: listFrame) + let bottomPanelHeight = bottomAreaHeight + layout.intrinsicInsets.bottom + let listTopInset = layoutTopInset + 63.0 + let listSize = CGSize(width: layout.size.width, height: layout.size.height - listTopInset - bottomPanelHeight) + + insets.top = max(0.0, listSize.height - 44.0 - floor(56.0 * 3.5)) + + transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(x: 0.0, y: listTopInset), size: listSize)) let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition) - let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listFrame.size, insets: UIEdgeInsets(top: -1.0, left: -6.0, bottom: -1.0, right: -6.0), scrollIndicatorInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0), duration: duration, curve: curve) + let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listSize, insets: insets, duration: duration, curve: curve) + +// let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listFrame.size, insets: UIEdgeInsets(top: -1.0, left: -6.0, bottom: -1.0, right: -6.0), scrollIndicatorInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0), duration: duration, curve: curve) self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + transition.updateFrame(node: self.topCornersNode, frame: CGRect(origin: CGPoint(x: sideInset, y: 63.0), size: CGSize(width: layout.size.width - sideInset * 2.0, height: 50.0))) + + let bottomPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - bottomPanelHeight), size: CGSize(width: layout.size.width, height: bottomPanelHeight)) + transition.updateFrame(node: self.bottomPanelNode, frame: bottomPanelFrame) + transition.updateFrame(node: self.bottomCornersNode, frame: CGRect(origin: CGPoint(x: sideInset, y: -50.0), size: CGSize(width: layout.size.width - sideInset * 2.0, height: 50.0))) + let sideButtonSize = CGSize(width: 60.0, height: 60.0) - let centralButtonSize = CGSize(width: 370.0, height: 370.0) + let centralButtonSize = CGSize(width: 440.0, height: 440.0) - let actionButtonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - centralButtonSize.width) / 2.0), y: contentHeight - bottomAreaHeight - layout.intrinsicInsets.bottom + floorToScreenPixels((bottomAreaHeight - centralButtonSize.height) / 2.0)), size: centralButtonSize) + let actionButtonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - centralButtonSize.width) / 2.0), y: floorToScreenPixels((bottomAreaHeight - centralButtonSize.height) / 2.0)), size: centralButtonSize) let actionButtonState: VoiceChatActionButton.State let actionButtonTitle: String @@ -1036,7 +1217,7 @@ public final class VoiceChatController: ViewController { } self.actionButton.isUserInteractionEnabled = actionButtonEnabled - self.actionButton.update(size: centralButtonSize, buttonSize: CGSize(width: 144.0, height: 144.0), state: actionButtonState, title: actionButtonTitle, subtitle: actionButtonSubtitle, small: layout.size.width < 330.0, animated: true) + self.actionButton.update(size: centralButtonSize, buttonSize: CGSize(width: 144.0, height: 144.0), state: actionButtonState, title: actionButtonTitle, subtitle: actionButtonSubtitle, dark: false, small: layout.size.width < 330.0, animated: true) transition.updateFrame(node: self.actionButton, frame: actionButtonFrame) var audioMode: CallControllerButtonsSpeakerMode = .none @@ -1094,8 +1275,8 @@ public final class VoiceChatController: ViewController { let sideButtonOffset = min(36.0, floor((((layout.size.width - 144.0) / 2.0) - sideButtonSize.width) / 2.0)) let sideButtonOrigin = max(sideButtonMinimalInset, floor((layout.size.width - 144.0) / 2.0) - sideButtonOffset - sideButtonSize.width) - transition.updateFrame(node: self.audioOutputNode, frame: CGRect(origin: CGPoint(x: sideButtonOrigin, y: contentHeight - bottomAreaHeight - layout.intrinsicInsets.bottom + floor((bottomAreaHeight - sideButtonSize.height) / 2.0)), size: sideButtonSize)) - transition.updateFrame(node: self.leaveNode, frame: CGRect(origin: CGPoint(x: layout.size.width - sideButtonOrigin - sideButtonSize.width, y: contentHeight - bottomAreaHeight - layout.intrinsicInsets.bottom + floor((bottomAreaHeight - sideButtonSize.height) / 2.0)), size: sideButtonSize)) + transition.updateFrame(node: self.audioOutputNode, frame: CGRect(origin: CGPoint(x: sideButtonOrigin, y: floor((bottomAreaHeight - sideButtonSize.height) / 2.0)), size: sideButtonSize)) + transition.updateFrame(node: self.leaveNode, frame: CGRect(origin: CGPoint(x: layout.size.width - sideButtonOrigin - sideButtonSize.width, y: floor((bottomAreaHeight - sideButtonSize.height) / 2.0)), size: sideButtonSize)) if isFirstTime { while !self.enqueuedTransitions.isEmpty { @@ -1105,45 +1286,31 @@ public final class VoiceChatController: ViewController { } func animateIn() { - guard let (layout, _) = self.validLayout else { - return - } - - self.isHidden = false - - self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) - - let offset: CGFloat = layout.size.height - 240.0 - self.contentContainer.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, additive: true, completion: { _ in - }) + self.layer.animateBoundsOriginYAdditive(from: -self.bounds.size.height, to: 0.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) + self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.dimNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -self.bounds.size.height), to: CGPoint(), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true, additive: true) } func animateOut(completion: (() -> Void)?) { - guard let (layout, _) = self.validLayout else { - return - } - var dimCompleted = false var offsetCompleted = false - let internalCompletion: () -> Void = { [weak self] in if dimCompleted && offsetCompleted { if let strongSelf = self { + strongSelf.layer.removeAllAnimations() strongSelf.dimNode.layer.removeAllAnimations() - strongSelf.contentContainer.layer.removeAllAnimations() } completion?() } } - self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in - dimCompleted = true + self.layer.animateBoundsOriginYAdditive(from: self.bounds.origin.y, to: -self.bounds.size.height, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in + offsetCompleted = true internalCompletion() }) - - let offset: CGFloat = layout.size.height - 240.0 - self.contentContainer.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: offset), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { _ in - offsetCompleted = true + self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) + self.dimNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -self.bounds.size.height), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { _ in + dimCompleted = true internalCompletion() }) } @@ -1180,14 +1347,13 @@ public final class VoiceChatController: ViewController { strongSelf.controller?.contentsReady.set(true) } - if !transition.deletions.isEmpty || !transition.insertions.isEmpty { + if false, !transition.deletions.isEmpty || !transition.insertions.isEmpty { var itemHeight: CGFloat = 56.0 strongSelf.listNode.forEachVisibleItemNode { node in if node.frame.height > 0 { itemHeight = node.frame.height } } - strongSelf.maxListHeight = CGFloat(transition.count - 1) * itemHeight if let (layout, navigationHeight) = strongSelf.validLayout { strongSelf.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring)) } @@ -1272,39 +1438,87 @@ public final class VoiceChatController: ViewController { self.enqueueTransition(transition) } - @objc private func panGesture(_ recognizer: CallPanGestureRecognizer) { + override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { +// if let callState = self.callState, case .connected = callState.networkState, let muteState = callState.muteState, !muteState.canUnmute { +// return false +// } + if let recognizer = gestureRecognizer as? UIPanGestureRecognizer { + let location = recognizer.location(in: self.view) + if let view = super.hitTest(location, with: nil) { + if let gestureRecognizers = view.gestureRecognizers, view != self.view { + for gestureRecognizer in gestureRecognizers { + if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer, gestureRecognizer.isEnabled { + print(view) + if panGestureRecognizer.state != .began { + panGestureRecognizer.isEnabled = false + panGestureRecognizer.isEnabled = true + } + } + } + } + } + } + return true + } + + @objc func panGesture(_ recognizer: UIPanGestureRecognizer) { switch recognizer.state { case .began: - self.contentContainer.clipsToBounds = true + break case .changed: - let offset = recognizer.translation(in: self.view).y + let translation = recognizer.translation(in: self.contentContainer.view) var bounds = self.contentContainer.bounds - bounds.origin.y = -offset - self.contentContainer.bounds = bounds - case .cancelled, .ended: - let velocity = recognizer.velocity(in: self.view).y - if velocity < 200.0 { - var bounds = self.contentContainer.bounds - let previous = bounds - bounds.origin = CGPoint() - self.contentContainer.bounds = bounds - self.contentContainer.layer.animateBounds(from: previous, to: bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) - } else { - var bounds = self.contentContainer.bounds - let previous = bounds - bounds.origin = CGPoint(x: 0.0, y: velocity > 0.0 ? -bounds.height: bounds.height) - self.contentContainer.bounds = bounds - self.contentContainer.layer.animateBounds(from: previous, to: bounds, duration: 0.15, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, completion: { [weak self] _ in - self?.controller?.dismissInteractively() - var initialBounds = bounds - initialBounds.origin = CGPoint() - self?.contentContainer.bounds = initialBounds - }) + bounds.origin.y = -translation.y + bounds.origin.y = min(0.0, bounds.origin.y) + if bounds.origin.y < 0.0 { + //let delta = -bounds.origin.y + //bounds.origin.y = -((1.0 - (1.0 / (((delta) * 0.55 / (50.0)) + 1.0))) * 50.0) } + + self.contentContainer.bounds = bounds + case .ended: + let translation = recognizer.translation(in: self.contentContainer.view) + var bounds = self.contentContainer.bounds + bounds.origin.y = -translation.y + + let velocity = recognizer.velocity(in: self.contentContainer.view) + + if (bounds.minY < -60.0 || velocity.y > 300.0) { + self.controller?.dismiss() + } else { + let previousBounds = self.bounds + var bounds = self.bounds + bounds.origin.y = 0.0 + self.contentContainer.bounds = bounds + self.contentContainer.layer.animateBounds(from: previousBounds, to: self.contentContainer.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue) + } + case .cancelled: + let previousBounds = self.contentContainer.bounds + var bounds = self.contentContainer.bounds + bounds.origin.y = 0.0 + self.contentContainer.bounds = bounds + self.contentContainer.layer.animateBounds(from: previousBounds, to: self.contentContainer.bounds, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue) default: break } } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + let result = super.hitTest(point, with: event) + + print("actually hitting") + if result === self.topPanelNode.view || result === self.bottomPanelNode.view { + return self.view + } + + if !self.bounds.contains(point) { + return nil + } + if point.y < self.topPanelNode.frame.minY { + return self.dimNode.view + } + return result + } } private let sharedContext: SharedAccountContext @@ -1420,14 +1634,16 @@ public final class VoiceChatController: ViewController { private final class VoiceChatContextExtractedContentSource: ContextExtractedContentSource { var keepInPlace: Bool let ignoreContentTouches: Bool = true + let blurBackground: Bool private let controller: ViewController private let sourceNode: ContextExtractedContentContainingNode - init(controller: ViewController, sourceNode: ContextExtractedContentContainingNode, keepInPlace: Bool) { + init(controller: ViewController, sourceNode: ContextExtractedContentContainingNode, keepInPlace: Bool, blurBackground: Bool) { self.controller = controller self.sourceNode = sourceNode self.keepInPlace = keepInPlace + self.blurBackground = blurBackground } func takeView() -> ContextControllerTakeViewInfo? { diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatOptionsButton.swift b/submodules/TelegramCallsUI/Sources/VoiceChatOptionsButton.swift index ee053e1d37..60c77867ba 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatOptionsButton.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatOptionsButton.swift @@ -3,10 +3,13 @@ import UIKit import AsyncDisplayKit import Display -func optionsButtonImage() -> UIImage? { +func optionsButtonImage(dark: Bool) -> UIImage? { return generateImage(CGSize(width: 28.0, height: 28.0), contextGenerator: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor(rgb: dark ? 0x1c1c1e : 0x2c2c2e).cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor.white.cgColor) context.fillEllipse(in: CGRect(x: 6.0, y: 12.0, width: 4.0, height: 4.0)) context.fillEllipse(in: CGRect(x: 12.0, y: 12.0, width: 4.0, height: 4.0)) @@ -14,11 +17,11 @@ func optionsButtonImage() -> UIImage? { }) } -func closeButtonImage() -> UIImage? { +func closeButtonImage(dark: Bool) -> UIImage? { return generateImage(CGSize(width: 28.0, height: 28.0), contextGenerator: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) - context.setFillColor(UIColor(rgb: 0x1c1c1e).cgColor) + context.setFillColor(UIColor(rgb: dark ? 0x1c1c1e : 0x2c2c2e).cgColor) context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) context.setLineWidth(2.0) @@ -35,7 +38,7 @@ func closeButtonImage() -> UIImage? { }) } -final class VoiceChatOptionsButton: HighlightableButtonNode { +final class VoiceChatHeaderButton: HighlightableButtonNode { let extractedContainerNode: ContextExtractedContentContainingNode let containerNode: ContextControllerSourceNode private let iconNode: ASImageNode @@ -57,6 +60,12 @@ final class VoiceChatOptionsButton: HighlightableButtonNode { self.containerNode.targetNodeForActivationProgress = self.extractedContainerNode.contentNode self.addSubnode(self.containerNode) + self.containerNode.shouldBegin = { [weak self] location in + guard let strongSelf = self, let _ = strongSelf.contextAction else { + return false + } + return true + } self.containerNode.activated = { [weak self] gesture, _ in guard let strongSelf = self else { return @@ -64,17 +73,7 @@ final class VoiceChatOptionsButton: HighlightableButtonNode { strongSelf.contextAction?(strongSelf.containerNode, gesture) } - self.iconNode.image = generateImage(CGSize(width: 28.0, height: 28.0), contextGenerator: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - - context.setFillColor(UIColor(rgb: 0x1c1c1e).cgColor) - context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) - - context.setFillColor(UIColor.white.cgColor) - context.fillEllipse(in: CGRect(x: 6.0, y: 12.0, width: 4.0, height: 4.0)) - context.fillEllipse(in: CGRect(x: 12.0, y: 12.0, width: 4.0, height: 4.0)) - context.fillEllipse(in: CGRect(x: 18.0, y: 12.0, width: 4.0, height: 4.0)) - }) + self.iconNode.image = optionsButtonImage(dark: false) self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 28.0, height: 28.0)) self.extractedContainerNode.frame = self.containerNode.bounds @@ -82,6 +81,18 @@ final class VoiceChatOptionsButton: HighlightableButtonNode { self.iconNode.frame = self.containerNode.bounds } + func setImage(_ image: UIImage?, animated: Bool = false) { + if animated, let snapshotView = self.iconNode.view.snapshotContentTree() { + snapshotView.frame = self.iconNode.frame + self.view.addSubview(snapshotView) + + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + self.iconNode.image = image + } + override func didLoad() { super.didLoad() self.view.isOpaque = false diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift index a669db6df1..107bfcb020 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift @@ -139,7 +139,6 @@ public final class VoiceChatParticipantItem: ListViewItem { private let avatarFont = avatarPlaceholderFont(size: floor(40.0 * 16.0 / 37.0)) public class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode { - private let backgroundNode: ASDisplayNode private let topStripeNode: ASDisplayNode private let bottomStripeNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode @@ -172,9 +171,6 @@ public class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode { private var layoutParams: (VoiceChatParticipantItem, ListViewItemLayoutParams, Bool, Bool)? public init() { - self.backgroundNode = ASDisplayNode() - self.backgroundNode.isLayerBacked = true - self.topStripeNode = ASDisplayNode() self.topStripeNode.isLayerBacked = true @@ -470,7 +466,6 @@ public class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode { if let _ = updatedTheme { strongSelf.topStripeNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor strongSelf.bottomStripeNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor - strongSelf.backgroundNode.backgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor } @@ -517,20 +512,16 @@ public class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode { let _ = titleApply() let _ = statusApply() - if strongSelf.backgroundNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) - } if strongSelf.topStripeNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1) + strongSelf.insertSubnode(strongSelf.topStripeNode, at: 0) } if strongSelf.bottomStripeNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) + strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 1) } strongSelf.topStripeNode.isHidden = first strongSelf.bottomStripeNode.isHidden = last - - strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) + transition.updateFrame(node: strongSelf.topStripeNode, frame: CGRect(origin: CGPoint(x: leftInset, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))) transition.updateFrame(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: leftInset, y: contentSize.height + -separatorHeight), size: CGSize(width: layoutSize.width - leftInset, height: separatorHeight))) @@ -665,7 +656,7 @@ public class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode { strongSelf.iconNode?.frame = CGRect(origin: CGPoint(), size: animationSize) strongSelf.animationNode?.frame = CGRect(origin: CGPoint(), size: animationSize) - strongSelf.actionButtonNode.frame = CGRect(x: params.width - animationSize.width - 6.0, y: floor((layout.contentSize.height - animationSize.height) / 2.0) + 1.0, width: animationSize.width, height: animationSize.height) + strongSelf.actionButtonNode.frame = CGRect(x: params.width - animationSize.width - 6.0 - params.rightInset, y: floor((layout.contentSize.height - animationSize.height) / 2.0) + 1.0, width: animationSize.width, height: animationSize.height) if let presence = item.presence as? TelegramUserPresence { strongSelf.peerPresenceManager?.reset(presence: presence) @@ -681,14 +672,8 @@ public class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode { } var isHighlighted = false - - var reallyHighlighted: Bool { - var reallyHighlighted = self.isHighlighted - return reallyHighlighted - } - func updateIsHighlighted(transition: ContainedViewLayoutTransition) { - if self.reallyHighlighted { + if self.isHighlighted { self.highlightedBackgroundNode.alpha = 1.0 if self.highlightedBackgroundNode.supernode == nil { var anchorNode: ASDisplayNode? @@ -696,8 +681,6 @@ public class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode { anchorNode = self.bottomStripeNode } else if self.topStripeNode.supernode != nil { anchorNode = self.topStripeNode - } else if self.backgroundNode.supernode != nil { - anchorNode = self.backgroundNode } if let anchorNode = anchorNode { self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: anchorNode) @@ -760,7 +743,7 @@ public class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode { super.updateRevealOffset(offset: offset, transition: transition) if let item = self.layoutParams?.0, let params = self.layoutParams?.1 { - var leftInset: CGFloat = 65.0 + params.leftInset + let leftInset: CGFloat = 65.0 + params.leftInset var avatarFrame = self.avatarNode.frame avatarFrame.origin.x = offset + leftInset - 50.0 diff --git a/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift b/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift index 5f6196fcbe..9740f7e645 100644 --- a/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift +++ b/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift @@ -7,6 +7,7 @@ import Postbox final class ChatMessageContextExtractedContentSource: ContextExtractedContentSource { let keepInPlace: Bool = false let ignoreContentTouches: Bool = false + let blurBackground: Bool = true private weak var chatNode: ChatControllerNode? private let message: Message diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 4873d55021..97d0e13db3 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -6017,6 +6017,7 @@ public final class PeerInfoScreen: ViewController { private final class SettingsTabBarContextExtractedContentSource: ContextExtractedContentSource { let keepInPlace: Bool = true let ignoreContentTouches: Bool = true + let blurBackground: Bool = true private let controller: ViewController private let sourceNode: ContextExtractedContentContainingNode @@ -6261,6 +6262,7 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent private final class MessageContextExtractedContentSource: ContextExtractedContentSource { let keepInPlace: Bool = false let ignoreContentTouches: Bool = true + let blurBackground: Bool = true private let sourceNode: ContextExtractedContentContainingNode