mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
Voice Chat Improvements
This commit is contained in:
@@ -212,7 +212,9 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
var feedbackTap: (() -> Void)?
|
||||
|
||||
var blurBackground = true
|
||||
if case let .extracted(extractedSource) = source, !extractedSource.blurBackground {
|
||||
if case .reference = source {
|
||||
blurBackground = false
|
||||
} else if case let .extracted(extractedSource) = source, !extractedSource.blurBackground {
|
||||
blurBackground = false
|
||||
}
|
||||
self.blurBackground = blurBackground
|
||||
@@ -441,7 +443,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}))
|
||||
|
||||
switch source {
|
||||
case .extracted:
|
||||
case .reference, .extracted:
|
||||
self.contentReady.set(.single(true))
|
||||
case let .controller(source):
|
||||
self.contentReady.set(source.controller.ready.get())
|
||||
@@ -478,6 +480,15 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
|
||||
private func initializeContent() {
|
||||
switch self.source {
|
||||
case let .reference(source):
|
||||
let transitionInfo = source.transitionInfo()
|
||||
if let transitionInfo = transitionInfo {
|
||||
let referenceNode = transitionInfo.referenceNode
|
||||
self.contentContainerNode.contentNode = .reference(node: referenceNode)
|
||||
self.contentAreaInScreenSpace = transitionInfo.contentAreaInScreenSpace
|
||||
let projectedFrame = convertFrame(referenceNode.view.bounds, from: referenceNode.view, to: self.view)
|
||||
self.originalProjectedContentViewFrame = (projectedFrame, projectedFrame)
|
||||
}
|
||||
case let .extracted(source):
|
||||
let takenViewInfo = source.takeView()
|
||||
|
||||
@@ -553,6 +564,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.hapticFeedback.impact()
|
||||
|
||||
switch self.source {
|
||||
case .reference:
|
||||
break
|
||||
case .extracted:
|
||||
if let contentAreaInScreenSpace = self.contentAreaInScreenSpace, let maybeContentNode = self.contentContainerNode.contentNode, case .extracted = maybeContentNode {
|
||||
var updatedContentAreaInScreenSpace = contentAreaInScreenSpace
|
||||
@@ -619,6 +632,22 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
|
||||
if let contentNode = self.contentContainerNode.contentNode {
|
||||
switch contentNode {
|
||||
case let .reference(referenceNode):
|
||||
let springDuration: Double = 0.42 * animationDurationFactor
|
||||
let springDamping: CGFloat = 104.0
|
||||
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor)
|
||||
self.actionsContainerNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
|
||||
|
||||
if let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
|
||||
let localSourceFrame = self.view.convert(originalProjectedContentViewFrame.1, to: self.scrollNode.view)
|
||||
|
||||
let localContentSourceFrame = self.view.convert(originalProjectedContentViewFrame.1, to: self.contentContainerNode.view.superview)
|
||||
|
||||
self.actionsContainerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
let contentContainerOffset = CGPoint(x: localContentSourceFrame.center.x - self.contentContainerNode.frame.center.x, y: localContentSourceFrame.center.y - self.contentContainerNode.frame.center.y)
|
||||
self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: contentContainerOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
}
|
||||
case let .extracted(extracted, keepInPlace):
|
||||
let springDuration: Double = 0.42 * animationDurationFactor
|
||||
let springDamping: CGFloat = 104.0
|
||||
@@ -701,6 +730,74 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
var result = initialResult
|
||||
|
||||
switch self.source {
|
||||
case let .reference(source):
|
||||
guard let maybeContentNode = self.contentContainerNode.contentNode, case let .reference(referenceNode) = maybeContentNode else {
|
||||
return
|
||||
}
|
||||
|
||||
let transitionInfo = source.transitionInfo()
|
||||
if transitionInfo == nil {
|
||||
result = .dismissWithoutContent
|
||||
}
|
||||
|
||||
switch result {
|
||||
case let .custom(value):
|
||||
switch value {
|
||||
case let .animated(duration, curve):
|
||||
transitionDuration = duration
|
||||
transitionCurve = curve
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
self.isUserInteractionEnabled = false
|
||||
self.isAnimatingOut = true
|
||||
|
||||
self.scrollNode.view.setContentOffset(self.scrollNode.view.contentOffset, animated: false)
|
||||
|
||||
var completedEffect = false
|
||||
var completedContentNode = false
|
||||
var completedActionsNode = false
|
||||
|
||||
if let transitionInfo = transitionInfo, let parentSupernode = referenceNode.supernode {
|
||||
self.originalProjectedContentViewFrame = (convertFrame(referenceNode.frame, from: parentSupernode.view, to: self.view), convertFrame(referenceNode.bounds, from: referenceNode.view, to: self.view))
|
||||
|
||||
var updatedContentAreaInScreenSpace = transitionInfo.contentAreaInScreenSpace
|
||||
updatedContentAreaInScreenSpace.origin.x = 0.0
|
||||
updatedContentAreaInScreenSpace.size.width = self.bounds.width
|
||||
|
||||
self.clippingNode.layer.animateFrame(from: self.clippingNode.frame, to: updatedContentAreaInScreenSpace, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: updatedContentAreaInScreenSpace.minY, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
if !self.dimNode.isHidden {
|
||||
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false)
|
||||
} else {
|
||||
self.withoutBlurDimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15 * animationDurationFactor, removeOnCompletion: false, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
self.actionsContainerNode.layer.animateScale(from: 1.0, to: 0.1, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
||||
|
||||
let animateOutToItem: Bool
|
||||
switch result {
|
||||
case .default, .custom:
|
||||
animateOutToItem = true
|
||||
case .dismissWithoutContent:
|
||||
animateOutToItem = false
|
||||
}
|
||||
|
||||
if animateOutToItem, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
|
||||
let localSourceFrame = self.view.convert(originalProjectedContentViewFrame.1, to: self.scrollNode.view)
|
||||
let localContentSourceFrame = self.view.convert(originalProjectedContentViewFrame.1, to: self.contentContainerNode.view.superview)
|
||||
|
||||
self.actionsContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y), duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true)
|
||||
}
|
||||
case let .extracted(source):
|
||||
guard let maybeContentNode = self.contentContainerNode.contentNode, case let .extracted(contentParentNode, keepInPlace) = maybeContentNode else {
|
||||
return
|
||||
@@ -1111,7 +1208,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
|
||||
switch layout.metrics.widthClass {
|
||||
case .compact:
|
||||
if case let .extracted(extractedSource) = self.source, !extractedSource.blurBackground {
|
||||
if case .reference = self.source {
|
||||
} else 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, *) {
|
||||
@@ -1126,7 +1224,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.dimNode.isHidden = false
|
||||
self.withoutBlurDimNode.isHidden = true
|
||||
case .regular:
|
||||
if case let .extracted(extractedSource) = self.source, !extractedSource.blurBackground {
|
||||
if case .reference = self.source {
|
||||
} else if case let .extracted(extractedSource) = self.source, !extractedSource.blurBackground {
|
||||
} else if self.effectView.superview != nil {
|
||||
self.effectView.removeFromSuperview()
|
||||
self.withoutBlurDimNode.alpha = 1.0
|
||||
@@ -1147,6 +1246,83 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
|
||||
if let contentNode = self.contentContainerNode.contentNode {
|
||||
switch contentNode {
|
||||
case let .reference(referenceNode):
|
||||
let contentActionsSpacing: CGFloat = 16.0
|
||||
if let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
|
||||
let isInitialLayout = self.actionsContainerNode.frame.size.width.isZero
|
||||
let previousContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
||||
|
||||
let actionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition)
|
||||
self.actionsContainerNode.updateSize(containerSize: actionsSize, contentSize: actionsSize)
|
||||
let contentSize = originalProjectedContentViewFrame.1.size
|
||||
self.contentContainerNode.updateLayout(size: contentSize, scaledSize: contentSize, transition: transition)
|
||||
|
||||
let maximumActionsFrameOrigin = max(60.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - actionsSize.height)
|
||||
|
||||
let originalActionsY = min(originalProjectedContentViewFrame.1.maxY + contentActionsSpacing, maximumActionsFrameOrigin)
|
||||
let preferredActionsX = originalProjectedContentViewFrame.1.minX
|
||||
|
||||
var originalActionsFrame = CGRect(origin: CGPoint(x: max(actionsSideInset, min(layout.size.width - actionsSize.width - actionsSideInset, preferredActionsX)), y: originalActionsY), size: actionsSize)
|
||||
let originalContentX: CGFloat = originalProjectedContentViewFrame.1.minX
|
||||
let originalContentY = originalProjectedContentViewFrame.1.minY
|
||||
|
||||
var originalContentFrame = CGRect(origin: CGPoint(x: originalContentX, y: originalContentY), size: originalProjectedContentViewFrame.1.size)
|
||||
let topEdge = max(contentTopInset, self.contentAreaInScreenSpace?.minY ?? 0.0)
|
||||
if originalContentFrame.minY < topEdge {
|
||||
let requiredOffset = topEdge - originalContentFrame.minY
|
||||
let availableOffset = max(0.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - originalActionsFrame.maxY)
|
||||
let offset = min(requiredOffset, availableOffset)
|
||||
originalActionsFrame = originalActionsFrame.offsetBy(dx: 0.0, dy: offset)
|
||||
originalContentFrame = originalContentFrame.offsetBy(dx: 0.0, dy: offset)
|
||||
}
|
||||
|
||||
var contentHeight = max(layout.size.height, max(layout.size.height, originalActionsFrame.maxY + actionsBottomInset) - originalContentFrame.minY + contentTopInset)
|
||||
|
||||
var overflowOffset: CGFloat
|
||||
var contentContainerFrame: CGRect
|
||||
// if keepInPlace {
|
||||
overflowOffset = min(0.0, originalActionsFrame.minY - contentTopInset)
|
||||
let contentParentNode = referenceNode
|
||||
contentContainerFrame = originalContentFrame
|
||||
if !overflowOffset.isZero {
|
||||
let offsetDelta = contentParentNode.frame.height + 4.0
|
||||
overflowOffset += offsetDelta
|
||||
overflowOffset = min(0.0, overflowOffset)
|
||||
|
||||
originalActionsFrame.origin.x -= contentParentNode.frame.width + 14.0
|
||||
originalActionsFrame.origin.x = max(actionsSideInset, originalActionsFrame.origin.x)
|
||||
//originalActionsFrame.origin.y += contentParentNode.contentRect.height
|
||||
if originalActionsFrame.minX < contentContainerFrame.minX {
|
||||
contentContainerFrame.origin.x = min(originalActionsFrame.maxX + 14.0, layout.size.width - actionsSideInset)
|
||||
}
|
||||
originalActionsFrame.origin.y += offsetDelta
|
||||
if originalActionsFrame.maxY < originalContentFrame.maxY {
|
||||
originalActionsFrame.origin.y += contentParentNode.frame.height
|
||||
originalActionsFrame.origin.y = min(originalActionsFrame.origin.y, layout.size.height - originalActionsFrame.height - actionsBottomInset)
|
||||
}
|
||||
contentHeight -= offsetDelta
|
||||
}
|
||||
// } else {
|
||||
// overflowOffset = min(0.0, originalContentFrame.minY - contentTopInset)
|
||||
// contentContainerFrame = originalContentFrame.offsetBy(dx: -contentParentNode.contentRect.minX, dy: -overflowOffset - contentParentNode.contentRect.minY)
|
||||
// }
|
||||
|
||||
let scrollContentSize = CGSize(width: layout.size.width, height: contentHeight)
|
||||
if self.scrollNode.view.contentSize != scrollContentSize {
|
||||
self.scrollNode.view.contentSize = scrollContentSize
|
||||
}
|
||||
self.actionsContainerNode.panSelectionGestureEnabled = scrollContentSize.height <= layout.size.height
|
||||
|
||||
transition.updateFrame(node: self.contentContainerNode, frame: contentContainerFrame)
|
||||
actionsContainerTransition.updateFrame(node: self.actionsContainerNode, frame: originalActionsFrame.offsetBy(dx: 0.0, dy: -overflowOffset))
|
||||
|
||||
if isInitialLayout {
|
||||
let currentContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
||||
if overflowOffset < 0.0 {
|
||||
transition.animateOffsetAdditive(node: self.scrollNode, offset: currentContainerFrame.minY - previousContainerFrame.minY)
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .extracted(contentParentNode, keepInPlace):
|
||||
let contentActionsSpacing: CGFloat = keepInPlace ? 16.0 : 8.0
|
||||
if let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
|
||||
@@ -1198,7 +1374,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
if keepInPlace {
|
||||
overflowOffset = min(0.0, originalActionsFrame.minY - contentTopInset)
|
||||
contentContainerFrame = originalContentFrame.offsetBy(dx: -contentParentNode.contentRect.minX, dy: -contentParentNode.contentRect.minY)
|
||||
if keepInPlace && !overflowOffset.isZero {
|
||||
if !overflowOffset.isZero {
|
||||
let offsetDelta = contentParentNode.contentRect.height + 4.0
|
||||
overflowOffset += offsetDelta
|
||||
overflowOffset = min(0.0, overflowOffset)
|
||||
@@ -1452,6 +1628,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
let mappedPoint = self.view.convert(point, to: self.scrollNode.view)
|
||||
if let maybeContentNode = self.contentContainerNode.contentNode {
|
||||
switch maybeContentNode {
|
||||
case .reference:
|
||||
break
|
||||
case let .extracted(contentParentNode, _):
|
||||
if case let .extracted(source) = self.source {
|
||||
if !source.ignoreContentTouches {
|
||||
@@ -1493,6 +1671,20 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
}
|
||||
|
||||
public final class ContextControllerReferenceViewInfo {
|
||||
public let referenceNode: ContextReferenceContentNode
|
||||
public let contentAreaInScreenSpace: CGRect
|
||||
|
||||
public init(referenceNode: ContextReferenceContentNode, contentAreaInScreenSpace: CGRect) {
|
||||
self.referenceNode = referenceNode
|
||||
self.contentAreaInScreenSpace = contentAreaInScreenSpace
|
||||
}
|
||||
}
|
||||
|
||||
public protocol ContextReferenceContentSource: class {
|
||||
func transitionInfo() -> ContextControllerReferenceViewInfo?
|
||||
}
|
||||
|
||||
public final class ContextControllerTakeViewInfo {
|
||||
public let contentContainingNode: ContextExtractedContentContainingNode
|
||||
public let contentAreaInScreenSpace: CGRect
|
||||
@@ -1503,16 +1695,6 @@ public final class ContextControllerTakeViewInfo {
|
||||
}
|
||||
}
|
||||
|
||||
public final class ContextControllerTakeControllerInfo {
|
||||
public let contentAreaInScreenSpace: CGRect
|
||||
public let sourceNode: () -> (ASDisplayNode, CGRect)?
|
||||
|
||||
public init(contentAreaInScreenSpace: CGRect, sourceNode: @escaping () -> (ASDisplayNode, CGRect)?) {
|
||||
self.contentAreaInScreenSpace = contentAreaInScreenSpace
|
||||
self.sourceNode = sourceNode
|
||||
}
|
||||
}
|
||||
|
||||
public final class ContextControllerPutBackViewInfo {
|
||||
public let contentAreaInScreenSpace: CGRect
|
||||
|
||||
@@ -1537,6 +1719,16 @@ public extension ContextExtractedContentSource {
|
||||
}
|
||||
}
|
||||
|
||||
public final class ContextControllerTakeControllerInfo {
|
||||
public let contentAreaInScreenSpace: CGRect
|
||||
public let sourceNode: () -> (ASDisplayNode, CGRect)?
|
||||
|
||||
public init(contentAreaInScreenSpace: CGRect, sourceNode: @escaping () -> (ASDisplayNode, CGRect)?) {
|
||||
self.contentAreaInScreenSpace = contentAreaInScreenSpace
|
||||
self.sourceNode = sourceNode
|
||||
}
|
||||
}
|
||||
|
||||
public protocol ContextControllerContentSource: class {
|
||||
var controller: ViewController { get }
|
||||
var navigationController: NavigationController? { get }
|
||||
@@ -1548,6 +1740,7 @@ public protocol ContextControllerContentSource: class {
|
||||
}
|
||||
|
||||
public enum ContextContentSource {
|
||||
case reference(ContextReferenceContentSource)
|
||||
case extracted(ContextExtractedContentSource)
|
||||
case controller(ContextControllerContentSource)
|
||||
}
|
||||
@@ -1576,6 +1769,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
}
|
||||
|
||||
public var reactionSelected: ((ReactionContextItem.Reaction) -> Void)?
|
||||
public var dismissed: (() -> Void)?
|
||||
|
||||
private var shouldBeDismissedDisposable: Disposable?
|
||||
|
||||
@@ -1590,23 +1784,29 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
self.displayTextSelectionTip = displayTextSelectionTip
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
if case let .extracted(extractedSource) = source {
|
||||
if !extractedSource.blurBackground {
|
||||
|
||||
switch source {
|
||||
case .reference:
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
}
|
||||
self.shouldBeDismissedDisposable = (extractedSource.shouldBeDismissed
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
case let .extracted(extractedSource):
|
||||
if extractedSource.blurBackground {
|
||||
self.statusBar.statusBarStyle = .Hide
|
||||
} else {
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
}
|
||||
strongSelf.dismiss(result: .default, completion: {})
|
||||
})
|
||||
} else {
|
||||
self.statusBar.statusBarStyle = .Hide
|
||||
self.shouldBeDismissedDisposable = (extractedSource.shouldBeDismissed
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.dismiss(result: .default, completion: {})
|
||||
})
|
||||
case .controller:
|
||||
self.statusBar.statusBarStyle = .Hide
|
||||
}
|
||||
|
||||
self.lockOrientation = true
|
||||
self.blocksBackgroundWhenInOverlay = true
|
||||
}
|
||||
@@ -1691,6 +1891,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
completion?()
|
||||
})
|
||||
self.dismissed?()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1705,6 +1906,7 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
completion?()
|
||||
})
|
||||
self.dismissed?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user