mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
8270f2b029
commit
015528cb3e
@ -27,7 +27,7 @@ private final class InstantPageContextExtractedContentSource: ContextExtractedCo
|
||||
guard let navigationBar = self.navigationBar else {
|
||||
return nil
|
||||
}
|
||||
return ContextControllerTakeViewInfo(contentContainingNode: navigationBar.contextSourceNode, contentAreaInScreenSpace: navigationBar.convert(navigationBar.contextSourceNode.frame.offsetBy(dx: 0.0, dy: 40.0), to: nil))
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(navigationBar.contextSourceNode), contentAreaInScreenSpace: navigationBar.convert(navigationBar.contextSourceNode.frame.offsetBy(dx: 0.0, dy: 40.0), to: nil))
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
|
@ -366,7 +366,7 @@ public final class CallListController: TelegramBaseController {
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(contentContainingNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
@ -510,7 +510,7 @@ private final class CallListTabBarContextExtractedContentSource: ContextExtracte
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(contentContainingNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
|
@ -3209,7 +3209,7 @@ private final class ChatListTabBarContextExtractedContentSource: ContextExtracte
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(contentContainingNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
@ -3232,7 +3232,7 @@ private final class ChatListHeaderBarContextExtractedContentSource: ContextExtra
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(contentContainingNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
|
@ -1406,7 +1406,7 @@ private final class MessageContextExtractedContentSource: ContextExtractedConten
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(contentContainingNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
|
@ -1782,12 +1782,14 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
|
||||
var options = transition.options
|
||||
if !options.contains(.AnimateInsertion) {
|
||||
options.insert(.PreferSynchronousDrawing)
|
||||
options.insert(.PreferSynchronousResourceLoading)
|
||||
}
|
||||
if options.contains(.AnimateCrossfade) && !self.isDeceleratingAfterTracking {
|
||||
options.insert(.PreferSynchronousDrawing)
|
||||
if self.view.window != nil {
|
||||
if !options.contains(.AnimateInsertion) {
|
||||
options.insert(.PreferSynchronousDrawing)
|
||||
options.insert(.PreferSynchronousResourceLoading)
|
||||
}
|
||||
if options.contains(.AnimateCrossfade) && !self.isDeceleratingAfterTracking {
|
||||
options.insert(.PreferSynchronousDrawing)
|
||||
}
|
||||
}
|
||||
|
||||
var scrollToItem = transition.scrollToItem
|
||||
|
@ -32,8 +32,25 @@ public final class ReactionIconView: PortalSourceView {
|
||||
}
|
||||
}
|
||||
|
||||
public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
||||
fileprivate final class ContainerButtonNode: HighlightTrackingButtonNode {
|
||||
private final class ReactionImageCache {
|
||||
static let shared = ReactionImageCache()
|
||||
|
||||
private var images: [String: UIImage] = [:]
|
||||
|
||||
init() {
|
||||
}
|
||||
|
||||
func get(reaction: String) -> UIImage? {
|
||||
return self.images[reaction]
|
||||
}
|
||||
|
||||
func put(reaction: String, image: UIImage) {
|
||||
self.images[reaction] = image
|
||||
}
|
||||
}
|
||||
|
||||
public final class ReactionButtonAsyncNode: ContextControllerSourceView {
|
||||
fileprivate final class ContainerButtonNode: UIButton {
|
||||
struct Colors: Equatable {
|
||||
var background: UInt32
|
||||
var foreground: UInt32
|
||||
@ -65,8 +82,17 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
||||
private var animationState: AnimationState?
|
||||
private var animator: ConstantDisplayLinkAnimator?
|
||||
|
||||
init() {
|
||||
super.init(pointerStyle: nil)
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: CGRect())
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func reset() {
|
||||
self.layer.contents = nil
|
||||
self.currentLayout = nil
|
||||
}
|
||||
|
||||
func update(layout: Layout) {
|
||||
@ -130,130 +156,142 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
||||
}
|
||||
}
|
||||
|
||||
let image = generateImage(layout.size, rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
UIGraphicsPushContext(context)
|
||||
let isExtracted = self.isExtracted
|
||||
let animationState = self.animationState
|
||||
|
||||
DispatchQueue.global().async { [weak self] in
|
||||
var image: UIImage?
|
||||
|
||||
func drawContents(colors: Colors) {
|
||||
let backgroundColor: UIColor
|
||||
let foregroundColor: UIColor
|
||||
if self.isExtracted {
|
||||
backgroundColor = UIColor(argb: colors.extractedBackground)
|
||||
foregroundColor = UIColor(argb: colors.extractedForeground)
|
||||
} else {
|
||||
backgroundColor = UIColor(argb: colors.background)
|
||||
foregroundColor = UIColor(argb: colors.foreground)
|
||||
}
|
||||
|
||||
context.setBlendMode(.copy)
|
||||
|
||||
context.setFillColor(backgroundColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.height, height: size.height)))
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - size.height, y: 0.0), size: CGSize(width: size.height, height: size.height)))
|
||||
context.fill(CGRect(origin: CGPoint(x: size.height / 2.0, y: 0.0), size: CGSize(width: size.width - size.height, height: size.height)))
|
||||
|
||||
if let counter = layout.counter {
|
||||
let isForegroundTransparent = foregroundColor.alpha < 1.0
|
||||
context.setBlendMode(isForegroundTransparent ? .copy : .normal)
|
||||
if true {
|
||||
image = generateImage(layout.size, rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
UIGraphicsPushContext(context)
|
||||
|
||||
let textOrigin: CGFloat = 36.0
|
||||
|
||||
var rightTextOrigin = textOrigin + totalComponentWidth
|
||||
|
||||
let animationFraction: CGFloat
|
||||
if let animationState = self.animationState, animationState.fromCounter != nil {
|
||||
animationFraction = max(0.0, min(1.0, (CACurrentMediaTime() - animationState.startTime) / animationState.duration))
|
||||
} else {
|
||||
animationFraction = 1.0
|
||||
}
|
||||
|
||||
for i in (0 ..< counter.components.count).reversed() {
|
||||
let component = counter.components[i]
|
||||
var componentAlpha: CGFloat = 1.0
|
||||
var componentVerticalOffset: CGFloat = 0.0
|
||||
func drawContents(colors: Colors) {
|
||||
let backgroundColor: UIColor
|
||||
let foregroundColor: UIColor
|
||||
if isExtracted {
|
||||
backgroundColor = UIColor(argb: colors.extractedBackground)
|
||||
foregroundColor = UIColor(argb: colors.extractedForeground)
|
||||
} else {
|
||||
backgroundColor = UIColor(argb: colors.background)
|
||||
foregroundColor = UIColor(argb: colors.foreground)
|
||||
}
|
||||
|
||||
if let animationState = self.animationState, let fromCounter = animationState.fromCounter {
|
||||
let reverseIndex = counter.components.count - 1 - i
|
||||
if reverseIndex < fromCounter.components.count {
|
||||
let previousComponent = fromCounter.components[fromCounter.components.count - 1 - reverseIndex]
|
||||
context.setBlendMode(.copy)
|
||||
|
||||
context.setFillColor(backgroundColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.height, height: size.height)))
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - size.height, y: 0.0), size: CGSize(width: size.height, height: size.height)))
|
||||
context.fill(CGRect(origin: CGPoint(x: size.height / 2.0, y: 0.0), size: CGSize(width: size.width - size.height, height: size.height)))
|
||||
|
||||
if let counter = layout.counter {
|
||||
let isForegroundTransparent = foregroundColor.alpha < 1.0
|
||||
context.setBlendMode(isForegroundTransparent ? .copy : .normal)
|
||||
|
||||
let textOrigin: CGFloat = 36.0
|
||||
|
||||
var rightTextOrigin = textOrigin + totalComponentWidth
|
||||
|
||||
let animationFraction: CGFloat
|
||||
if let animationState = animationState, animationState.fromCounter != nil {
|
||||
animationFraction = max(0.0, min(1.0, (CACurrentMediaTime() - animationState.startTime) / animationState.duration))
|
||||
} else {
|
||||
animationFraction = 1.0
|
||||
}
|
||||
|
||||
for i in (0 ..< counter.components.count).reversed() {
|
||||
let component = counter.components[i]
|
||||
var componentAlpha: CGFloat = 1.0
|
||||
var componentVerticalOffset: CGFloat = 0.0
|
||||
|
||||
if previousComponent != component {
|
||||
componentAlpha = animationFraction
|
||||
componentVerticalOffset = -(1.0 - animationFraction) * 8.0
|
||||
if previousComponent.string < component.string {
|
||||
componentVerticalOffset = -componentVerticalOffset
|
||||
if let animationState = animationState, let fromCounter = animationState.fromCounter {
|
||||
let reverseIndex = counter.components.count - 1 - i
|
||||
if reverseIndex < fromCounter.components.count {
|
||||
let previousComponent = fromCounter.components[fromCounter.components.count - 1 - reverseIndex]
|
||||
|
||||
if previousComponent != component {
|
||||
componentAlpha = animationFraction
|
||||
componentVerticalOffset = -(1.0 - animationFraction) * 8.0
|
||||
if previousComponent.string < component.string {
|
||||
componentVerticalOffset = -componentVerticalOffset
|
||||
}
|
||||
|
||||
let previousComponentAlpha = 1.0 - componentAlpha
|
||||
var previousComponentVerticalOffset = animationFraction * 8.0
|
||||
if previousComponent.string < component.string {
|
||||
previousComponentVerticalOffset = -previousComponentVerticalOffset
|
||||
}
|
||||
|
||||
var componentOrigin = rightTextOrigin - previousComponent.bounds.width
|
||||
componentOrigin = max(componentOrigin, layout.size.height / 2.0 + UIScreenPixel)
|
||||
let previousColor: UIColor
|
||||
if isForegroundTransparent {
|
||||
previousColor = foregroundColor.mixedWith(backgroundColor, alpha: 1.0 - previousComponentAlpha)
|
||||
} else {
|
||||
previousColor = foregroundColor.withMultipliedAlpha(previousComponentAlpha)
|
||||
}
|
||||
let string = NSAttributedString(string: previousComponent.string, font: Font.medium(11.0), textColor: previousColor)
|
||||
string.draw(at: previousComponent.bounds.origin.offsetBy(dx: componentOrigin, dy: floorToScreenPixels(size.height - previousComponent.bounds.height) / 2.0 + previousComponentVerticalOffset))
|
||||
}
|
||||
}
|
||||
|
||||
let previousComponentAlpha = 1.0 - componentAlpha
|
||||
var previousComponentVerticalOffset = animationFraction * 8.0
|
||||
if previousComponent.string < component.string {
|
||||
previousComponentVerticalOffset = -previousComponentVerticalOffset
|
||||
}
|
||||
|
||||
var componentOrigin = rightTextOrigin - previousComponent.bounds.width
|
||||
componentOrigin = max(componentOrigin, layout.size.height / 2.0 + UIScreenPixel)
|
||||
let previousColor: UIColor
|
||||
if isForegroundTransparent {
|
||||
previousColor = foregroundColor.mixedWith(backgroundColor, alpha: 1.0 - previousComponentAlpha)
|
||||
} else {
|
||||
previousColor = foregroundColor.withMultipliedAlpha(previousComponentAlpha)
|
||||
}
|
||||
let string = NSAttributedString(string: previousComponent.string, font: Font.medium(11.0), textColor: previousColor)
|
||||
string.draw(at: previousComponent.bounds.origin.offsetBy(dx: componentOrigin, dy: floorToScreenPixels(size.height - previousComponent.bounds.height) / 2.0 + previousComponentVerticalOffset))
|
||||
}
|
||||
|
||||
let componentOrigin = rightTextOrigin - component.bounds.width
|
||||
let currentColor: UIColor
|
||||
if isForegroundTransparent {
|
||||
currentColor = foregroundColor.mixedWith(backgroundColor, alpha: 1.0 - componentAlpha)
|
||||
} else {
|
||||
currentColor = foregroundColor.withMultipliedAlpha(componentAlpha)
|
||||
}
|
||||
let string = NSAttributedString(string: component.string, font: Font.medium(11.0), textColor: currentColor)
|
||||
string.draw(at: component.bounds.origin.offsetBy(dx: componentOrigin, dy: floorToScreenPixels(size.height - component.bounds.height) / 2.0 + componentVerticalOffset))
|
||||
|
||||
rightTextOrigin -= component.bounds.width
|
||||
}
|
||||
}
|
||||
|
||||
let componentOrigin = rightTextOrigin - component.bounds.width
|
||||
let currentColor: UIColor
|
||||
if isForegroundTransparent {
|
||||
currentColor = foregroundColor.mixedWith(backgroundColor, alpha: 1.0 - componentAlpha)
|
||||
} else {
|
||||
currentColor = foregroundColor.withMultipliedAlpha(componentAlpha)
|
||||
}
|
||||
|
||||
if let animationState = animationState, animationState.fromColors.isSelected != layout.colors.isSelected {
|
||||
var animationFraction: CGFloat = max(0.0, min(1.0, (CACurrentMediaTime() - animationState.startTime) / animationState.duration))
|
||||
if !layout.colors.isSelected {
|
||||
animationFraction = 1.0 - animationFraction
|
||||
}
|
||||
let string = NSAttributedString(string: component.string, font: Font.medium(11.0), textColor: currentColor)
|
||||
string.draw(at: component.bounds.origin.offsetBy(dx: componentOrigin, dy: floorToScreenPixels(size.height - component.bounds.height) / 2.0 + componentVerticalOffset))
|
||||
|
||||
rightTextOrigin -= component.bounds.width
|
||||
let center = CGPoint(x: 21.0, y: size.height / 2.0)
|
||||
let diameter = 0.0 * (1.0 - animationFraction) + (size.width - center.x) * 2.0 * animationFraction
|
||||
|
||||
context.beginPath()
|
||||
context.addEllipse(in: CGRect(origin: CGPoint(x: center.x - diameter / 2.0, y: center.y - diameter / 2.0), size: CGSize(width: diameter, height: diameter)))
|
||||
context.clip(using: .evenOdd)
|
||||
drawContents(colors: layout.colors.isSelected ? layout.colors : animationState.fromColors)
|
||||
|
||||
context.resetClip()
|
||||
|
||||
context.beginPath()
|
||||
context.addRect(CGRect(origin: CGPoint(), size: size))
|
||||
context.addEllipse(in: CGRect(origin: CGPoint(x: center.x - diameter / 2.0, y: center.y - diameter / 2.0), size: CGSize(width: diameter, height: diameter)))
|
||||
context.clip(using: .evenOdd)
|
||||
drawContents(colors: layout.colors.isSelected ? animationState.fromColors : layout.colors)
|
||||
} else {
|
||||
drawContents(colors: layout.colors)
|
||||
}
|
||||
|
||||
UIGraphicsPopContext()
|
||||
})
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
if let strongSelf = self, let image = image {
|
||||
let previousContents = strongSelf.layer.contents
|
||||
|
||||
ASDisplayNodeSetResizableContents(strongSelf.layer, image)
|
||||
|
||||
if animated, let previousContents = previousContents {
|
||||
strongSelf.layer.animate(from: previousContents as! CGImage, to: image.cgImage!, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let animationState = self.animationState, animationState.fromColors.isSelected != layout.colors.isSelected {
|
||||
var animationFraction: CGFloat = max(0.0, min(1.0, (CACurrentMediaTime() - animationState.startTime) / animationState.duration))
|
||||
if !layout.colors.isSelected {
|
||||
animationFraction = 1.0 - animationFraction
|
||||
}
|
||||
|
||||
let center = CGPoint(x: 21.0, y: size.height / 2.0)
|
||||
let diameter = 0.0 * (1.0 - animationFraction) + (size.width - center.x) * 2.0 * animationFraction
|
||||
|
||||
context.beginPath()
|
||||
context.addEllipse(in: CGRect(origin: CGPoint(x: center.x - diameter / 2.0, y: center.y - diameter / 2.0), size: CGSize(width: diameter, height: diameter)))
|
||||
context.clip(using: .evenOdd)
|
||||
drawContents(colors: layout.colors.isSelected ? layout.colors : animationState.fromColors)
|
||||
|
||||
context.resetClip()
|
||||
|
||||
context.beginPath()
|
||||
context.addRect(CGRect(origin: CGPoint(), size: size))
|
||||
context.addEllipse(in: CGRect(origin: CGPoint(x: center.x - diameter / 2.0, y: center.y - diameter / 2.0), size: CGSize(width: diameter, height: diameter)))
|
||||
context.clip(using: .evenOdd)
|
||||
drawContents(colors: layout.colors.isSelected ? animationState.fromColors : layout.colors)
|
||||
} else {
|
||||
drawContents(colors: layout.colors)
|
||||
}
|
||||
|
||||
UIGraphicsPopContext()
|
||||
})//?.stretchableImage(withLeftCapWidth: Int(layout.baseSize.height / 2.0), topCapHeight: Int(layout.baseSize.height / 2.0))
|
||||
if let image = image {
|
||||
let previousContents = self.layer.contents
|
||||
|
||||
ASDisplayNodeSetResizableContents(self.layer, image)
|
||||
|
||||
if animated, let previousContents = previousContents {
|
||||
self.layer.animate(from: previousContents as! CGImage, to: image.cgImage!, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -462,9 +500,9 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
||||
|
||||
private var layout: Layout?
|
||||
|
||||
public let containerNode: ContextExtractedContentContainingNode
|
||||
public let containerView: ContextExtractedContentContainingView
|
||||
private let buttonNode: ContainerButtonNode
|
||||
public let iconView: ReactionIconView
|
||||
public var iconView: ReactionIconView?
|
||||
private var avatarsView: AnimatedAvatarSetView?
|
||||
|
||||
private let iconImageDisposable = MetaDisposable()
|
||||
@ -481,42 +519,40 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
||||
}
|
||||
}
|
||||
|
||||
override init() {
|
||||
self.containerNode = ContextExtractedContentContainingNode()
|
||||
override init(frame: CGRect) {
|
||||
self.containerView = ContextExtractedContentContainingView()
|
||||
self.buttonNode = ContainerButtonNode()
|
||||
|
||||
self.iconView = ReactionIconView()
|
||||
self.iconView.isUserInteractionEnabled = false
|
||||
self.iconView?.isUserInteractionEnabled = false
|
||||
|
||||
super.init()
|
||||
super.init(frame: frame)
|
||||
|
||||
self.targetNodeForActivationProgress = self.containerNode.contentNode
|
||||
self.targetViewForActivationProgress = self.containerView.contentView
|
||||
|
||||
self.addSubnode(self.containerNode)
|
||||
self.containerNode.contentNode.addSubnode(self.buttonNode)
|
||||
self.buttonNode.view.addSubview(self.iconView)
|
||||
|
||||
self.buttonNode.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.buttonNode.highligthedChanged = { [weak self] highlighted in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let _ = strongSelf
|
||||
if highlighted {
|
||||
} else {
|
||||
}
|
||||
self.addSubview(self.containerView)
|
||||
self.containerView.contentView.addSubview(self.buttonNode)
|
||||
if let iconView = self.iconView {
|
||||
self.buttonNode.addSubview(iconView)
|
||||
}
|
||||
|
||||
self.buttonNode.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||
|
||||
self.isGestureEnabled = true
|
||||
self.beginDelay = 0.0
|
||||
|
||||
self.containerNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, _ in
|
||||
self.containerView.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.buttonNode.updateIsExtracted(isExtracted: isExtracted, animated: true)
|
||||
}
|
||||
|
||||
if self.activateAfterCompletion {
|
||||
self.contextGesture?.activatedAfterCompletion = { [weak self] in
|
||||
self?.pressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
@ -527,14 +563,11 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
||||
self.iconImageDisposable.dispose()
|
||||
}
|
||||
|
||||
override public func didLoad() {
|
||||
super.didLoad()
|
||||
func reset() {
|
||||
self.iconView?.imageView.image = nil
|
||||
self.layout = nil
|
||||
|
||||
if self.activateAfterCompletion {
|
||||
self.contextGesture?.activatedAfterCompletion = { [weak self] in
|
||||
self?.pressed()
|
||||
}
|
||||
}
|
||||
self.buttonNode.reset()
|
||||
}
|
||||
|
||||
@objc private func pressed() {
|
||||
@ -545,43 +578,73 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
||||
}
|
||||
|
||||
fileprivate func apply(layout: Layout, animation: ListViewItemUpdateAnimation) {
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.containerNode.contentNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.containerNode.contentRect = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.containerView.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.containerView.contentView.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.containerView.contentRect = CGRect(origin: CGPoint(), size: layout.size)
|
||||
animation.animator.updateFrame(layer: self.buttonNode.layer, frame: CGRect(origin: CGPoint(), size: layout.size), completion: nil)
|
||||
|
||||
self.buttonNode.update(layout: layout.backgroundLayout)
|
||||
|
||||
animation.animator.updateFrame(layer: self.iconView.layer, frame: layout.imageFrame, completion: nil)
|
||||
self.iconView.update(size: layout.imageFrame.size, transition: animation.transition)
|
||||
|
||||
if self.layout?.spec.component.reaction != layout.spec.component.reaction {
|
||||
if let file = layout.spec.component.reaction.centerAnimation {
|
||||
self.iconImageDisposable.set((reactionStaticImage(context: layout.spec.component.context, animation: file, pixelSize: CGSize(width: 64.0 * UIScreenScale, height: 64.0 * UIScreenScale))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] data in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if data.isComplete, let dataValue = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
|
||||
if let image = UIImage(data: dataValue) {
|
||||
strongSelf.iconView.imageView.image = image
|
||||
if let iconView = self.iconView {
|
||||
animation.animator.updateFrame(layer: iconView.layer, frame: layout.imageFrame, completion: nil)
|
||||
iconView.update(size: layout.imageFrame.size, transition: animation.transition)
|
||||
|
||||
if self.layout?.spec.component.reaction != layout.spec.component.reaction {
|
||||
if let file = layout.spec.component.reaction.centerAnimation {
|
||||
if let image = ReactionImageCache.shared.get(reaction: layout.spec.component.reaction.value) {
|
||||
iconView.imageView.image = image
|
||||
} else {
|
||||
self.iconImageDisposable.set((reactionStaticImage(context: layout.spec.component.context, animation: file, pixelSize: CGSize(width: 32.0 * UIScreenScale, height: 32.0 * UIScreenScale))
|
||||
|> filter { data in
|
||||
return data.isComplete
|
||||
}
|
||||
}
|
||||
}))
|
||||
} else if let legacyIcon = layout.spec.component.reaction.legacyIcon {
|
||||
self.iconImageDisposable.set((layout.spec.component.context.account.postbox.mediaBox.resourceData(legacyIcon.resource)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] data in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if data.complete, let dataValue = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
|
||||
if let image = WebP.convert(fromWebP: dataValue) {
|
||||
strongSelf.iconView.imageView.image = image
|
||||
|> take(1)
|
||||
|> map { data -> UIImage? in
|
||||
if data.isComplete, let dataValue = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
|
||||
if let image = UIImage(data: dataValue) {
|
||||
return image.precomposed()
|
||||
} else {
|
||||
print("Could not decode image")
|
||||
}
|
||||
} else {
|
||||
print("Incomplete data")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] image in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if let image = image {
|
||||
strongSelf.iconView?.imageView.image = image
|
||||
ReactionImageCache.shared.put(reaction: layout.spec.component.reaction.value, image: image)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}))
|
||||
} else if let legacyIcon = layout.spec.component.reaction.legacyIcon {
|
||||
self.iconImageDisposable.set((layout.spec.component.context.account.postbox.mediaBox.resourceData(legacyIcon.resource)
|
||||
|> deliverOn(Queue.concurrentDefaultQueue())
|
||||
|> map { data -> UIImage? in
|
||||
if data.complete, let dataValue = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
|
||||
if let image = WebP.convert(fromWebP: dataValue) {
|
||||
if #available(iOS 15.0, iOSApplicationExtension 15.0, *) {
|
||||
return image.preparingForDisplay()
|
||||
} else {
|
||||
return image.precomposed()
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] image in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.iconView?.imageView.image = image
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -593,7 +656,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
||||
avatarsView = AnimatedAvatarSetView()
|
||||
avatarsView.isUserInteractionEnabled = false
|
||||
self.avatarsView = avatarsView
|
||||
self.buttonNode.view.addSubview(avatarsView)
|
||||
self.buttonNode.addSubview(avatarsView)
|
||||
}
|
||||
let content = AnimatedAvatarSetContext().update(peers: layout.spec.component.avatarPeers, animated: false)
|
||||
let avatarsSize = avatarsView.update(
|
||||
@ -620,8 +683,8 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
||||
self.layout = layout
|
||||
}
|
||||
|
||||
public static func asyncLayout(_ view: ReactionButtonAsyncNode?) -> (ReactionButtonComponent) -> (size: CGSize, apply: (_ animation: ListViewItemUpdateAnimation) -> ReactionButtonAsyncNode) {
|
||||
let currentLayout = view?.layout
|
||||
public static func asyncLayout(_ item: ReactionNodePool.Item?) -> (ReactionButtonComponent) -> (size: CGSize, apply: (_ animation: ListViewItemUpdateAnimation) -> ReactionNodePool.Item) {
|
||||
let currentLayout = item?.view.layout
|
||||
|
||||
return { component in
|
||||
let spec = Layout.Spec(component: component)
|
||||
@ -635,17 +698,17 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
||||
|
||||
return (size: layout.size, apply: { animation in
|
||||
var animation = animation
|
||||
let updatedView: ReactionButtonAsyncNode
|
||||
if let view = view {
|
||||
updatedView = view
|
||||
let updatedItem: ReactionNodePool.Item
|
||||
if let item = item {
|
||||
updatedItem = item
|
||||
} else {
|
||||
updatedView = ReactionButtonAsyncNode()
|
||||
updatedItem = ReactionNodePool.shared.take()
|
||||
animation = .None
|
||||
}
|
||||
|
||||
updatedView.apply(layout: layout, animation: animation)
|
||||
updatedItem.view.apply(layout: layout, animation: animation)
|
||||
|
||||
return updatedView
|
||||
return updatedItem
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -751,6 +814,39 @@ public final class ReactionButtonComponent: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
public final class ReactionNodePool {
|
||||
static let shared = ReactionNodePool()
|
||||
|
||||
public final class Item {
|
||||
public let view: ReactionButtonAsyncNode
|
||||
private weak var pool: ReactionNodePool?
|
||||
|
||||
init(view: ReactionButtonAsyncNode, pool: ReactionNodePool) {
|
||||
self.view = view
|
||||
self.pool = pool
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.pool?.putBack(view: self.view)
|
||||
}
|
||||
}
|
||||
|
||||
private var views: [ReactionButtonAsyncNode] = []
|
||||
|
||||
func putBack(view: ReactionButtonAsyncNode) {
|
||||
view.reset()
|
||||
self.views.append(view)
|
||||
}
|
||||
|
||||
func take() -> Item {
|
||||
if !self.views.isEmpty {
|
||||
return Item(view: self.views.removeLast(), pool: self)
|
||||
} else {
|
||||
return Item(view: ReactionButtonAsyncNode(), pool: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class ReactionButtonsAsyncLayoutContainer {
|
||||
public struct Reaction {
|
||||
public var reaction: ReactionButtonComponent.Reaction
|
||||
@ -783,15 +879,15 @@ public final class ReactionButtonsAsyncLayoutContainer {
|
||||
public struct ApplyResult {
|
||||
public struct Item {
|
||||
public var value: String
|
||||
public var node: ReactionButtonAsyncNode
|
||||
public var node: ReactionNodePool.Item
|
||||
public var size: CGSize
|
||||
}
|
||||
|
||||
public var items: [Item]
|
||||
public var removedNodes: [ReactionButtonAsyncNode]
|
||||
public var removedNodes: [ReactionNodePool.Item]
|
||||
}
|
||||
|
||||
public private(set) var buttons: [String: ReactionButtonAsyncNode] = [:]
|
||||
public private(set) var buttons: [String: ReactionNodePool.Item] = [:]
|
||||
|
||||
public init() {
|
||||
}
|
||||
@ -804,7 +900,7 @@ public final class ReactionButtonsAsyncLayoutContainer {
|
||||
constrainedWidth: CGFloat
|
||||
) -> Result {
|
||||
var items: [Result.Item] = []
|
||||
var applyItems: [(key: String, size: CGSize, apply: (_ animation: ListViewItemUpdateAnimation) -> ReactionButtonAsyncNode)] = []
|
||||
var applyItems: [(key: String, size: CGSize, apply: (_ animation: ListViewItemUpdateAnimation) -> ReactionNodePool.Item)] = []
|
||||
|
||||
var validIds = Set<String>()
|
||||
for reaction in reactions.sorted(by: { lhs, rhs in
|
||||
@ -856,10 +952,10 @@ public final class ReactionButtonsAsyncLayoutContainer {
|
||||
removeIds.append(id)
|
||||
}
|
||||
}
|
||||
var removedNodes: [ReactionButtonAsyncNode] = []
|
||||
var removedNodes: [ReactionNodePool.Item] = []
|
||||
for id in removeIds {
|
||||
if let node = self.buttons.removeValue(forKey: id) {
|
||||
removedNodes.append(node)
|
||||
if let item = self.buttons.removeValue(forKey: id) {
|
||||
removedNodes.append(item)
|
||||
}
|
||||
}
|
||||
|
||||
@ -868,13 +964,13 @@ public final class ReactionButtonsAsyncLayoutContainer {
|
||||
apply: { animation in
|
||||
var items: [ApplyResult.Item] = []
|
||||
for (key, size, apply) in applyItems {
|
||||
let node = apply(animation)
|
||||
items.append(ApplyResult.Item(value: key, node: node, size: size))
|
||||
let nodeItem = apply(animation)
|
||||
items.append(ApplyResult.Item(value: key, node: nodeItem, size: size))
|
||||
|
||||
if let current = self.buttons[key] {
|
||||
assert(current === node)
|
||||
assert(current === nodeItem)
|
||||
} else {
|
||||
self.buttons[key] = node
|
||||
self.buttons[key] = nodeItem
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -698,7 +698,7 @@ private final class ContactsTabBarContextExtractedContentSource: ContextExtracte
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(contentContainingNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
|
@ -2091,12 +2091,17 @@ public extension ContextReferenceContentSource {
|
||||
}
|
||||
|
||||
public final class ContextControllerTakeViewInfo {
|
||||
public let contentContainingNode: ContextExtractedContentContainingNode
|
||||
public enum ContainingItem {
|
||||
case node(ContextExtractedContentContainingNode)
|
||||
case view(ContextExtractedContentContainingView)
|
||||
}
|
||||
|
||||
public let containingItem: ContainingItem
|
||||
public let contentAreaInScreenSpace: CGRect
|
||||
public let maskView: UIView?
|
||||
|
||||
public init(contentContainingNode: ContextExtractedContentContainingNode, contentAreaInScreenSpace: CGRect, maskView: UIView? = nil) {
|
||||
self.contentContainingNode = contentContainingNode
|
||||
public init(containingItem: ContainingItem, contentAreaInScreenSpace: CGRect, maskView: UIView? = nil) {
|
||||
self.containingItem = containingItem
|
||||
self.contentAreaInScreenSpace = contentAreaInScreenSpace
|
||||
self.maskView = maskView
|
||||
}
|
||||
|
@ -8,6 +8,107 @@ import TelegramCore
|
||||
import SwiftSignalKit
|
||||
import ReactionSelectionNode
|
||||
|
||||
private extension ContextControllerTakeViewInfo.ContainingItem {
|
||||
var contentRect: CGRect {
|
||||
switch self {
|
||||
case let .node(containingNode):
|
||||
return containingNode.contentRect
|
||||
case let .view(containingView):
|
||||
return containingView.contentRect
|
||||
}
|
||||
}
|
||||
|
||||
var customHitTest: ((CGPoint) -> UIView?)? {
|
||||
switch self {
|
||||
case let .node(containingNode):
|
||||
return containingNode.contentNode.customHitTest
|
||||
case let .view(containingView):
|
||||
return containingView.contentView.customHitTest
|
||||
}
|
||||
}
|
||||
|
||||
var view: UIView {
|
||||
switch self {
|
||||
case let .node(containingNode):
|
||||
return containingNode.view
|
||||
case let .view(containingView):
|
||||
return containingView
|
||||
}
|
||||
}
|
||||
|
||||
var contentView: UIView {
|
||||
switch self {
|
||||
case let .node(containingNode):
|
||||
return containingNode.contentNode.view
|
||||
case let .view(containingView):
|
||||
return containingView.contentView
|
||||
}
|
||||
}
|
||||
|
||||
func contentHitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
switch self {
|
||||
case let .node(containingNode):
|
||||
return containingNode.contentNode.hitTest(point, with: event)
|
||||
case let .view(containingView):
|
||||
return containingView.contentView.hitTest(point, with: event)
|
||||
}
|
||||
}
|
||||
|
||||
var isExtractedToContextPreview: Bool {
|
||||
get {
|
||||
switch self {
|
||||
case let .node(containingNode):
|
||||
return containingNode.isExtractedToContextPreview
|
||||
case let .view(containingView):
|
||||
return containingView.isExtractedToContextPreview
|
||||
}
|
||||
} set(value) {
|
||||
switch self {
|
||||
case let .node(containingNode):
|
||||
containingNode.isExtractedToContextPreview = value
|
||||
case let .view(containingView):
|
||||
containingView.isExtractedToContextPreview = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var willUpdateIsExtractedToContextPreview: ((Bool, ContainedViewLayoutTransition) -> Void)? {
|
||||
switch self {
|
||||
case let .node(containingNode):
|
||||
return containingNode.willUpdateIsExtractedToContextPreview
|
||||
case let .view(containingView):
|
||||
return containingView.willUpdateIsExtractedToContextPreview
|
||||
}
|
||||
}
|
||||
|
||||
var isExtractedToContextPreviewUpdated: ((Bool) -> Void)? {
|
||||
switch self {
|
||||
case let .node(containingNode):
|
||||
return containingNode.isExtractedToContextPreviewUpdated
|
||||
case let .view(containingView):
|
||||
return containingView.isExtractedToContextPreviewUpdated
|
||||
}
|
||||
}
|
||||
|
||||
var layoutUpdated: ((CGSize, ListViewItemUpdateAnimation) -> Void)? {
|
||||
get {
|
||||
switch self {
|
||||
case let .node(containingNode):
|
||||
return containingNode.layoutUpdated
|
||||
case let .view(containingView):
|
||||
return containingView.layoutUpdated
|
||||
}
|
||||
} set(value) {
|
||||
switch self {
|
||||
case let .node(containingNode):
|
||||
containingNode.layoutUpdated = value
|
||||
case let .view(containingView):
|
||||
containingView.layoutUpdated = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextControllerPresentationNode, UIScrollViewDelegate {
|
||||
enum ContentSource {
|
||||
case reference(ContextReferenceContentSource)
|
||||
@ -16,14 +117,14 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
|
||||
private final class ContentNode: ASDisplayNode {
|
||||
let offsetContainerNode: ASDisplayNode
|
||||
let containingNode: ContextExtractedContentContainingNode
|
||||
var containingItem: ContextControllerTakeViewInfo.ContainingItem
|
||||
|
||||
var animateClippingFromContentAreaInScreenSpace: CGRect?
|
||||
var storedGlobalFrame: CGRect?
|
||||
|
||||
init(containingNode: ContextExtractedContentContainingNode) {
|
||||
init(containingItem: ContextControllerTakeViewInfo.ContainingItem) {
|
||||
self.offsetContainerNode = ASDisplayNode()
|
||||
self.containingNode = containingNode
|
||||
self.containingItem = containingItem
|
||||
|
||||
super.init()
|
||||
|
||||
@ -34,8 +135,15 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
}
|
||||
|
||||
func takeContainingNode() {
|
||||
if self.containingNode.contentNode.supernode !== self.offsetContainerNode {
|
||||
self.offsetContainerNode.addSubnode(self.containingNode.contentNode)
|
||||
switch self.containingItem {
|
||||
case let .node(containingNode):
|
||||
if containingNode.contentNode.supernode !== self.offsetContainerNode {
|
||||
self.offsetContainerNode.addSubnode(containingNode.contentNode)
|
||||
}
|
||||
case let .view(containingView):
|
||||
if containingView.contentView.superview !== self.offsetContainerNode.view {
|
||||
self.offsetContainerNode.view.addSubview(containingView.contentView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,7 +151,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
if !self.bounds.contains(point) {
|
||||
return nil
|
||||
}
|
||||
if !self.containingNode.contentRect.contains(point) {
|
||||
if !self.containingItem.contentRect.contains(point) {
|
||||
return nil
|
||||
}
|
||||
return self.view
|
||||
@ -165,14 +273,14 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
}
|
||||
|
||||
if case let .extracted(source) = self.source, !source.ignoreContentTouches, let contentNode = self.contentNode {
|
||||
let contentPoint = self.view.convert(point, to: contentNode.containingNode.contentNode.view)
|
||||
if let result = contentNode.containingNode.contentNode.customHitTest?(contentPoint) {
|
||||
let contentPoint = self.view.convert(point, to: contentNode.containingItem.contentView)
|
||||
if let result = contentNode.containingItem.customHitTest?(contentPoint) {
|
||||
return result
|
||||
} else if let result = contentNode.containingNode.contentNode.hitTest(contentPoint, with: event) {
|
||||
} else if let result = contentNode.containingItem.contentHitTest(contentPoint, with: event) {
|
||||
if result is TextSelectionNodeView {
|
||||
return result
|
||||
} else if contentNode.containingNode.contentRect.contains(contentPoint) {
|
||||
return contentNode.containingNode.contentNode.view
|
||||
} else if contentNode.containingItem.contentRect.contains(contentPoint) {
|
||||
return contentNode.containingItem.contentView
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -291,7 +399,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
guard let takeInfo = source.takeView() else {
|
||||
return
|
||||
}
|
||||
let contentNodeValue = ContentNode(containingNode: takeInfo.contentContainingNode)
|
||||
let contentNodeValue = ContentNode(containingItem: takeInfo.containingItem)
|
||||
contentNodeValue.animateClippingFromContentAreaInScreenSpace = takeInfo.contentAreaInScreenSpace
|
||||
self.scrollNode.insertSubnode(contentNodeValue, aboveSubnode: self.actionsStackNode)
|
||||
self.contentNode = contentNodeValue
|
||||
@ -329,10 +437,10 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
if let contentNode = contentNode {
|
||||
switch stateTransition {
|
||||
case .animateIn, .animateOut:
|
||||
contentNode.storedGlobalFrame = convertFrame(contentNode.containingNode.contentRect, from: contentNode.containingNode.view, to: self.view)
|
||||
contentNode.storedGlobalFrame = convertFrame(contentNode.containingItem.contentRect, from: contentNode.containingItem.view, to: self.view)
|
||||
case .none:
|
||||
if contentNode.storedGlobalFrame == nil {
|
||||
contentNode.storedGlobalFrame = convertFrame(contentNode.containingNode.contentRect, from: contentNode.containingNode.view, to: self.view)
|
||||
contentNode.storedGlobalFrame = convertFrame(contentNode.containingItem.contentRect, from: contentNode.containingItem.view, to: self.view)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -352,10 +460,10 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
}
|
||||
case .extracted:
|
||||
if let contentNode = contentNode {
|
||||
contentParentGlobalFrame = convertFrame(contentNode.containingNode.bounds, from: contentNode.containingNode.view, to: self.view)
|
||||
contentParentGlobalFrame = convertFrame(contentNode.containingItem.view.bounds, from: contentNode.containingItem.view, to: self.view)
|
||||
|
||||
let contentRectGlobalFrame = CGRect(origin: CGPoint(x: contentNode.containingNode.contentRect.minX, y: (contentNode.storedGlobalFrame?.maxY ?? 0.0) - contentNode.containingNode.contentRect.height), size: contentNode.containingNode.contentRect.size)
|
||||
contentRect = CGRect(origin: CGPoint(x: contentRectGlobalFrame.minX, y: contentRectGlobalFrame.maxY - contentNode.containingNode.contentRect.size.height), size: contentNode.containingNode.contentRect.size)
|
||||
let contentRectGlobalFrame = CGRect(origin: CGPoint(x: contentNode.containingItem.contentRect.minX, y: (contentNode.storedGlobalFrame?.maxY ?? 0.0) - contentNode.containingItem.contentRect.height), size: contentNode.containingItem.contentRect.size)
|
||||
contentRect = CGRect(origin: CGPoint(x: contentRectGlobalFrame.minX, y: contentRectGlobalFrame.maxY - contentNode.containingItem.contentRect.size.height), size: contentNode.containingItem.contentRect.size)
|
||||
if case .animateOut = stateTransition {
|
||||
contentRect.origin.y = self.contentRectDebugNode.frame.maxY - contentRect.size.height
|
||||
}
|
||||
@ -380,7 +488,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
if let contentNode = contentNode {
|
||||
contentNode.update(
|
||||
presentationData: presentationData,
|
||||
size: contentNode.containingNode.bounds.size,
|
||||
size: contentNode.containingItem.view.bounds.size,
|
||||
transition: contentTransition
|
||||
)
|
||||
}
|
||||
@ -485,7 +593,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
transition.updateFrame(node: self.actionsStackNode, frame: actionsFrame, beginWithCurrentState: true)
|
||||
|
||||
if let contentNode = contentNode {
|
||||
contentTransition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(x: contentParentGlobalFrame.minX + contentRect.minX - contentNode.containingNode.contentRect.minX, y: contentRect.minY - contentNode.containingNode.contentRect.minY), size: contentNode.containingNode.bounds.size), beginWithCurrentState: true)
|
||||
contentTransition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(x: contentParentGlobalFrame.minX + contentRect.minX - contentNode.containingItem.contentRect.minX, y: contentRect.minY - contentNode.containingItem.contentRect.minY), size: contentNode.containingItem.view.bounds.size), beginWithCurrentState: true)
|
||||
}
|
||||
|
||||
let contentHeight: CGFloat
|
||||
@ -542,7 +650,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: animateClippingFromContentAreaInScreenSpace.minY, to: 0.0, duration: 0.2)
|
||||
}
|
||||
|
||||
currentContentScreenFrame = convertFrame(contentNode.containingNode.contentRect, from: contentNode.containingNode.view, to: self.view)
|
||||
currentContentScreenFrame = convertFrame(contentNode.containingItem.contentRect, from: contentNode.containingItem.view, to: self.view)
|
||||
let currentContentLocalFrame = convertFrame(contentRect, from: self.scrollNode.view, to: self.view)
|
||||
animationInContentDistance = currentContentLocalFrame.maxY - currentContentScreenFrame.maxY
|
||||
|
||||
@ -623,11 +731,11 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
self.actionsStackNode.animateIn()
|
||||
|
||||
if let contentNode = contentNode {
|
||||
contentNode.containingNode.isExtractedToContextPreview = true
|
||||
contentNode.containingNode.isExtractedToContextPreviewUpdated?(true)
|
||||
contentNode.containingNode.willUpdateIsExtractedToContextPreview?(true, transition)
|
||||
contentNode.containingItem.isExtractedToContextPreview = true
|
||||
contentNode.containingItem.isExtractedToContextPreviewUpdated?(true)
|
||||
contentNode.containingItem.willUpdateIsExtractedToContextPreview?(true, transition)
|
||||
|
||||
contentNode.containingNode.layoutUpdated = { [weak self] _, animation in
|
||||
contentNode.containingItem.layoutUpdated = { [weak self] _, animation in
|
||||
guard let strongSelf = self, let _ = strongSelf.contentNode else {
|
||||
return
|
||||
}
|
||||
@ -677,7 +785,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
}
|
||||
|
||||
if let contentNode = contentNode {
|
||||
currentContentScreenFrame = convertFrame(contentNode.containingNode.contentRect, from: contentNode.containingNode.view, to: self.view)
|
||||
currentContentScreenFrame = convertFrame(contentNode.containingItem.contentRect, from: contentNode.containingItem.view, to: self.view)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
@ -719,7 +827,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
let completeWithActionStack = contentNode == nil
|
||||
|
||||
if let contentNode = contentNode {
|
||||
contentNode.containingNode.willUpdateIsExtractedToContextPreview?(false, transition)
|
||||
contentNode.containingItem.willUpdateIsExtractedToContextPreview?(false, transition)
|
||||
|
||||
contentNode.offsetContainerNode.position = contentNode.offsetContainerNode.position.offsetBy(dx: 0.0, dy: -animationInContentDistance)
|
||||
let reactionContextNodeIsAnimatingOut = self.reactionContextNodeIsAnimatingOut
|
||||
@ -733,11 +841,16 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
additive: true,
|
||||
completion: { [weak self] _ in
|
||||
Queue.mainQueue().after(reactionContextNodeIsAnimatingOut ? 0.2 * UIView.animationDurationFactor() : 0.0, {
|
||||
contentNode.containingNode.isExtractedToContextPreview = false
|
||||
contentNode.containingNode.isExtractedToContextPreviewUpdated?(false)
|
||||
contentNode.containingItem.isExtractedToContextPreview = false
|
||||
contentNode.containingItem.isExtractedToContextPreviewUpdated?(false)
|
||||
|
||||
if let strongSelf = self, let contentNode = strongSelf.contentNode {
|
||||
contentNode.containingNode.addSubnode(contentNode.containingNode.contentNode)
|
||||
switch contentNode.containingItem {
|
||||
case let .node(containingNode):
|
||||
containingNode.addSubnode(containingNode.contentNode)
|
||||
case let .view(containingView):
|
||||
containingView.addSubview(containingView.contentView)
|
||||
}
|
||||
}
|
||||
|
||||
completion()
|
||||
|
@ -37,12 +37,76 @@ public final class ContextExtractedContentContainingNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
public final class ContextExtractedContentNode: ASDisplayNode {
|
||||
public var customHitTest: ((CGPoint) -> UIView?)?
|
||||
public final class ContextExtractedContentContainingView: UIView {
|
||||
public let contentView: ContextExtractedContentView
|
||||
public var contentRect: CGRect = CGRect()
|
||||
public var isExtractedToContextPreview: Bool = false
|
||||
public var willUpdateIsExtractedToContextPreview: ((Bool, ContainedViewLayoutTransition) -> Void)?
|
||||
public var isExtractedToContextPreviewUpdated: ((Bool) -> Void)?
|
||||
public var updateAbsoluteRect: ((CGRect, CGSize) -> Void)?
|
||||
public var applyAbsoluteOffset: ((CGPoint, ContainedViewLayoutTransitionCurve, Double) -> Void)?
|
||||
public var applyAbsoluteOffsetSpring: ((CGFloat, Double, CGFloat) -> Void)?
|
||||
public var layoutUpdated: ((CGSize, ListViewItemUpdateAnimation) -> Void)?
|
||||
public var updateDistractionFreeMode: ((Bool) -> Void)?
|
||||
public var requestDismiss: (() -> Void)?
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
self.contentView = ContextExtractedContentView()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.contentView)
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let result = self.view.hitTest(point, with: event)
|
||||
if result === self.view {
|
||||
if self.contentView.superview === self {
|
||||
return self.contentView.hitTest(self.convert(point, to: self.contentView), with: event)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class ContextExtractedContentNode: ASDisplayNode {
|
||||
private var viewImpl: ContextExtractedContentView {
|
||||
return self.view as! ContextExtractedContentView
|
||||
}
|
||||
|
||||
public var customHitTest: ((CGPoint) -> UIView?)? {
|
||||
didSet {
|
||||
if self.isNodeLoaded {
|
||||
self.viewImpl.customHitTest = self.customHitTest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public init() {
|
||||
super.init()
|
||||
|
||||
self.setViewBlock {
|
||||
return ContextExtractedContentView(frame: CGRect())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class ContextExtractedContentView: UIView {
|
||||
public var customHitTest: ((CGPoint) -> UIView?)?
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let result = super.hitTest(point, with: event)
|
||||
if result === self {
|
||||
return nil
|
||||
} else {
|
||||
return result
|
||||
|
@ -126,3 +126,264 @@ open class ContextControllerSourceNode: ContextReferenceContentNode {
|
||||
contextGesture.isEnabled = self.isGestureEnabled
|
||||
}
|
||||
}
|
||||
|
||||
/*open class ContextControllerSourceNode: ASDisplayNode {
|
||||
private var viewImpl: ContextControllerSourceView {
|
||||
return self.view as! ContextControllerSourceView
|
||||
}
|
||||
|
||||
public var contextGesture: ContextGesture? {
|
||||
if self.isNodeLoaded {
|
||||
return self.viewImpl.contextGesture
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public var isGestureEnabled: Bool = true {
|
||||
didSet {
|
||||
if self.isNodeLoaded {
|
||||
self.viewImpl.isGestureEnabled = self.isGestureEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var beginDelay: Double = 0.12 {
|
||||
didSet {
|
||||
if self.isNodeLoaded {
|
||||
self.viewImpl.beginDelay = self.beginDelay
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var animateScale: Bool = true {
|
||||
didSet {
|
||||
if self.isNodeLoaded {
|
||||
self.viewImpl.animateScale = self.animateScale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var activated: ((ContextGesture, CGPoint) -> Void)? {
|
||||
didSet {
|
||||
if self.isNodeLoaded {
|
||||
self.viewImpl.activated = self.activated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var shouldBegin: ((CGPoint) -> Bool)? {
|
||||
didSet {
|
||||
if self.isNodeLoaded {
|
||||
self.viewImpl.shouldBegin = self.shouldBegin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var customActivationProgress: ((CGFloat, ContextGestureTransition) -> Void)? {
|
||||
didSet {
|
||||
if self.isNodeLoaded {
|
||||
self.viewImpl.customActivationProgress = self.customActivationProgress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public weak var additionalActivationProgressLayer: CALayer? {
|
||||
didSet {
|
||||
if self.isNodeLoaded {
|
||||
self.viewImpl.additionalActivationProgressLayer = self.additionalActivationProgressLayer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var targetNodeForActivationProgress: ASDisplayNode? {
|
||||
didSet {
|
||||
if self.isNodeLoaded {
|
||||
self.viewImpl.targetNodeForActivationProgress = self.targetNodeForActivationProgress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var targetViewForActivationProgress: UIView? {
|
||||
didSet {
|
||||
if self.isNodeLoaded {
|
||||
self.viewImpl.targetViewForActivationProgress = self.targetViewForActivationProgress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var targetNodeForActivationProgressContentRect: CGRect? {
|
||||
didSet {
|
||||
if self.isNodeLoaded {
|
||||
self.viewImpl.targetNodeForActivationProgressContentRect = self.targetNodeForActivationProgressContentRect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public init() {
|
||||
super.init()
|
||||
|
||||
self.setViewBlock({
|
||||
return ContextControllerSourceView(frame: CGRect())
|
||||
})
|
||||
}
|
||||
|
||||
override open func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.viewImpl.isGestureEnabled = self.isGestureEnabled
|
||||
self.viewImpl.beginDelay = self.beginDelay
|
||||
self.viewImpl.animateScale = self.animateScale
|
||||
self.viewImpl.activated = self.activated
|
||||
self.viewImpl.shouldBegin = self.shouldBegin
|
||||
self.viewImpl.customActivationProgress = self.customActivationProgress
|
||||
self.viewImpl.additionalActivationProgressLayer = self.additionalActivationProgressLayer
|
||||
self.viewImpl.targetNodeForActivationProgress = self.targetNodeForActivationProgress
|
||||
self.viewImpl.targetViewForActivationProgress = self.targetViewForActivationProgress
|
||||
self.viewImpl.targetNodeForActivationProgressContentRect = self.targetNodeForActivationProgressContentRect
|
||||
}
|
||||
|
||||
public func cancelGesture() {
|
||||
if self.isNodeLoaded {
|
||||
self.viewImpl.cancelGesture()
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
open class ContextControllerSourceView: UIView {
|
||||
public private(set) var contextGesture: ContextGesture?
|
||||
|
||||
public var isGestureEnabled: Bool = true {
|
||||
didSet {
|
||||
self.contextGesture?.isEnabled = self.isGestureEnabled
|
||||
}
|
||||
}
|
||||
public var beginDelay: Double = 0.12 {
|
||||
didSet {
|
||||
self.contextGesture?.beginDelay = self.beginDelay
|
||||
}
|
||||
}
|
||||
public var animateScale: Bool = true
|
||||
|
||||
public var activated: ((ContextGesture, CGPoint) -> Void)?
|
||||
public var shouldBegin: ((CGPoint) -> Bool)?
|
||||
public var customActivationProgress: ((CGFloat, ContextGestureTransition) -> Void)?
|
||||
public weak var additionalActivationProgressLayer: CALayer?
|
||||
public var targetNodeForActivationProgress: ASDisplayNode?
|
||||
public var targetViewForActivationProgress: UIView?
|
||||
public var targetNodeForActivationProgressContentRect: CGRect?
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
let contextGesture = ContextGesture(target: self, action: nil)
|
||||
self.contextGesture = contextGesture
|
||||
self.addGestureRecognizer(contextGesture)
|
||||
|
||||
contextGesture.beginDelay = self.beginDelay
|
||||
contextGesture.isEnabled = self.isGestureEnabled
|
||||
|
||||
contextGesture.shouldBegin = { [weak self] point in
|
||||
guard let strongSelf = self, !strongSelf.bounds.width.isZero else {
|
||||
return false
|
||||
}
|
||||
return strongSelf.shouldBegin?(point) ?? true
|
||||
}
|
||||
|
||||
contextGesture.activationProgress = { [weak self] progress, update in
|
||||
guard let strongSelf = self, !strongSelf.bounds.width.isZero else {
|
||||
return
|
||||
}
|
||||
if let customActivationProgress = strongSelf.customActivationProgress {
|
||||
customActivationProgress(progress, update)
|
||||
} else if strongSelf.animateScale {
|
||||
let targetView: UIView
|
||||
let targetContentRect: CGRect
|
||||
if let targetNodeForActivationProgress = strongSelf.targetNodeForActivationProgress {
|
||||
targetView = targetNodeForActivationProgress.view
|
||||
if let targetNodeForActivationProgressContentRect = strongSelf.targetNodeForActivationProgressContentRect {
|
||||
targetContentRect = targetNodeForActivationProgressContentRect
|
||||
} else {
|
||||
targetContentRect = CGRect(origin: CGPoint(), size: targetView.bounds.size)
|
||||
}
|
||||
} else if let targetViewForActivationProgress = strongSelf.targetViewForActivationProgress {
|
||||
targetView = targetViewForActivationProgress
|
||||
if let targetNodeForActivationProgressContentRect = strongSelf.targetNodeForActivationProgressContentRect {
|
||||
targetContentRect = targetNodeForActivationProgressContentRect
|
||||
} else {
|
||||
targetContentRect = CGRect(origin: CGPoint(), size: targetView.bounds.size)
|
||||
}
|
||||
} else {
|
||||
targetView = strongSelf
|
||||
targetContentRect = CGRect(origin: CGPoint(), size: targetView.bounds.size)
|
||||
}
|
||||
|
||||
let scaleSide = targetContentRect.width
|
||||
let minScale: CGFloat = max(0.7, (scaleSide - 15.0) / scaleSide)
|
||||
let currentScale = 1.0 * (1.0 - progress) + minScale * progress
|
||||
|
||||
let originalCenterOffsetX: CGFloat = targetView.bounds.width / 2.0 - targetContentRect.midX
|
||||
let scaledCenterOffsetX: CGFloat = originalCenterOffsetX * currentScale
|
||||
|
||||
let originalCenterOffsetY: CGFloat = targetView.bounds.height / 2.0 - targetContentRect.midY
|
||||
let scaledCenterOffsetY: CGFloat = originalCenterOffsetY * currentScale
|
||||
|
||||
let scaleMidX: CGFloat = scaledCenterOffsetX - originalCenterOffsetX
|
||||
let scaleMidY: CGFloat = scaledCenterOffsetY - originalCenterOffsetY
|
||||
|
||||
switch update {
|
||||
case .update:
|
||||
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
|
||||
targetView.layer.sublayerTransform = sublayerTransform
|
||||
if let additionalActivationProgressLayer = strongSelf.additionalActivationProgressLayer {
|
||||
additionalActivationProgressLayer.transform = sublayerTransform
|
||||
}
|
||||
case .begin:
|
||||
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
|
||||
targetView.layer.sublayerTransform = sublayerTransform
|
||||
if let additionalActivationProgressLayer = strongSelf.additionalActivationProgressLayer {
|
||||
additionalActivationProgressLayer.transform = sublayerTransform
|
||||
}
|
||||
case .ended:
|
||||
let sublayerTransform = CATransform3DTranslate(CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0), scaleMidX, scaleMidY, 0.0)
|
||||
let previousTransform = targetView.layer.sublayerTransform
|
||||
targetView.layer.sublayerTransform = sublayerTransform
|
||||
|
||||
targetView.layer.animate(from: NSValue(caTransform3D: previousTransform), to: NSValue(caTransform3D: sublayerTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2)
|
||||
|
||||
if let additionalActivationProgressLayer = strongSelf.additionalActivationProgressLayer {
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2, execute: {
|
||||
additionalActivationProgressLayer.transform = sublayerTransform
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
contextGesture.activated = { [weak self] gesture, location in
|
||||
guard let strongSelf = self else {
|
||||
gesture.cancel()
|
||||
return
|
||||
}
|
||||
if let customActivationProgress = strongSelf.customActivationProgress {
|
||||
customActivationProgress(0.0, .ended(0.0))
|
||||
}
|
||||
|
||||
if let activated = strongSelf.activated {
|
||||
activated(gesture, location)
|
||||
} else {
|
||||
gesture.cancel()
|
||||
}
|
||||
}
|
||||
contextGesture.isEnabled = self.isGestureEnabled
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func cancelGesture() {
|
||||
self.contextGesture?.cancel()
|
||||
self.contextGesture?.isEnabled = false
|
||||
self.contextGesture?.isEnabled = self.isGestureEnabled
|
||||
}
|
||||
}
|
||||
|
@ -473,8 +473,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
self.displayLink = CADisplayLink(target: DisplayLinkProxy(target: self), selector: #selector(DisplayLinkProxy.displayLinkEvent))
|
||||
self.displayLink.add(to: RunLoop.main, forMode: RunLoop.Mode.common)
|
||||
|
||||
if #available(iOS 10.0, *) {
|
||||
self.displayLink.preferredFramesPerSecond = 60
|
||||
if #available(iOS 15.0, iOSApplicationExtension 15.0, *) {
|
||||
self.displayLink?.preferredFrameRateRange = CAFrameRateRange(minimum: 60.0, maximum: 120.0, preferred: 120.0)
|
||||
}
|
||||
|
||||
self.displayLink.isPaused = true
|
||||
@ -522,15 +522,14 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
}
|
||||
|
||||
private func dispatchOnVSync(forceNext: Bool = false, action: @escaping () -> ()) {
|
||||
Queue.mainQueue().async {
|
||||
/*Queue.mainQueue().async {
|
||||
if !forceNext && self.inVSync {
|
||||
action()
|
||||
} else {
|
||||
action()
|
||||
//self.actionsForVSync.append(action)
|
||||
//self.setNeedsAnimations()
|
||||
}
|
||||
}
|
||||
}*/
|
||||
DispatchQueue.main.async(execute: action)
|
||||
}
|
||||
|
||||
private func beginReordering(itemNode: ListViewItemNode) {
|
||||
@ -1576,6 +1575,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
|
||||
private func async(_ f: @escaping () -> Void) {
|
||||
DispatchQueue.global(qos: .userInteractive).async(execute: f)
|
||||
//DispatchQueue.main.async(execute: f)
|
||||
}
|
||||
|
||||
private func nodeForItem(synchronous: Bool, synchronousLoads: Bool, item: ListViewItem, previousNode: QueueLocalObject<ListViewItemNode>?, index: Int, previousItem: ListViewItem?, nextItem: ListViewItem?, params: ListViewItemLayoutParams, updateAnimationIsAnimated: Bool, updateAnimationIsCrossfade: Bool, completion: @escaping (QueueLocalObject<ListViewItemNode>, ListViewItemNodeLayout, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
|
@ -1,6 +1,10 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
// Incuding at least one Objective-C class in a swift file ensures that it doesn't get stripped by the linker
|
||||
private final class LinkHelperClass: NSObject {
|
||||
}
|
||||
|
||||
public extension CALayer {
|
||||
func addShakeAnimation(amplitude: CGFloat = 3.0, duration: Double = 0.3, count: Int = 4, decay: Bool = false) {
|
||||
let k = Float(UIView.animationDurationFactor())
|
||||
|
@ -19,6 +19,7 @@
|
||||
if (self != nil) {
|
||||
_codec = codec;
|
||||
_impl = avcodec_alloc_context3((AVCodec *)[codec impl]);
|
||||
_impl->max_pixels = 4 * 1024 * 4 * 1024;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -976,7 +976,7 @@ final class InviteLinkContextExtractedContentSource: ContextExtractedContentSour
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(contentContainingNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
|
@ -425,7 +425,7 @@ final class InviteRequestsContextExtractedContentSource: ContextExtractedContent
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(contentContainingNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
|
@ -48,6 +48,7 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
|
||||
private let videoFrame: FFMpegAVFrame
|
||||
private var resetDecoderOnNextFrame = true
|
||||
private var isError = false
|
||||
|
||||
private var defaultDuration: CMTime?
|
||||
private var defaultTimescale: CMTimeScale?
|
||||
@ -90,6 +91,10 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
}
|
||||
|
||||
public func receiveFromDecoder(ptsOffset: CMTime?) -> ReceiveResult {
|
||||
if self.isError {
|
||||
return .error
|
||||
}
|
||||
|
||||
guard let defaultTimescale = self.defaultTimescale, let defaultDuration = self.defaultDuration else {
|
||||
return .error
|
||||
}
|
||||
@ -97,6 +102,11 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
let receiveResult = self.codecContext.receive(into: self.videoFrame)
|
||||
switch receiveResult {
|
||||
case .success:
|
||||
if self.videoFrame.width * self.videoFrame.height > 4 * 1024 * 4 * 1024 {
|
||||
self.isError = true
|
||||
return .error
|
||||
}
|
||||
|
||||
var pts = CMTimeMake(value: self.videoFrame.pts, timescale: defaultTimescale)
|
||||
if let ptsOffset = ptsOffset {
|
||||
pts = CMTimeAdd(pts, ptsOffset)
|
||||
@ -116,12 +126,21 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
}
|
||||
|
||||
public func decode(frame: MediaTrackDecodableFrame, ptsOffset: CMTime?, forceARGB: Bool = false) -> MediaTrackFrame? {
|
||||
if self.isError {
|
||||
return nil
|
||||
}
|
||||
|
||||
let status = frame.packet.send(toDecoder: self.codecContext)
|
||||
if status == 0 {
|
||||
self.defaultDuration = frame.duration
|
||||
self.defaultTimescale = frame.pts.timescale
|
||||
|
||||
if self.codecContext.receive(into: self.videoFrame) == .success {
|
||||
if self.videoFrame.width * self.videoFrame.height > 4 * 1024 * 4 * 1024 {
|
||||
self.isError = true
|
||||
return nil
|
||||
}
|
||||
|
||||
var pts = CMTimeMake(value: self.videoFrame.pts, timescale: frame.pts.timescale)
|
||||
if let ptsOffset = ptsOffset {
|
||||
pts = CMTimeAdd(pts, ptsOffset)
|
||||
@ -137,6 +156,9 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
guard let defaultTimescale = self.defaultTimescale, let defaultDuration = self.defaultDuration else {
|
||||
return []
|
||||
}
|
||||
if self.isError {
|
||||
return []
|
||||
}
|
||||
|
||||
var result: [MediaTrackFrame] = []
|
||||
result.append(contentsOf: self.delayedFrames)
|
||||
@ -144,6 +166,11 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
|
||||
while true {
|
||||
if case .success = self.codecContext.receive(into: self.videoFrame) {
|
||||
if self.videoFrame.width * self.videoFrame.height > 4 * 1024 * 4 * 1024 {
|
||||
self.isError = true
|
||||
return []
|
||||
}
|
||||
|
||||
var pts = CMTimeMake(value: self.videoFrame.pts, timescale: defaultTimescale)
|
||||
if let ptsOffset = ptsOffset {
|
||||
pts = CMTimeAdd(pts, ptsOffset)
|
||||
@ -162,6 +189,11 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
||||
let status = frame.packet.send(toDecoder: self.codecContext)
|
||||
if status == 0 {
|
||||
if case .success = self.codecContext.receive(into: self.videoFrame) {
|
||||
if self.videoFrame.width * self.videoFrame.height > 4 * 1024 * 4 * 1024 {
|
||||
self.isError = true
|
||||
return nil
|
||||
}
|
||||
|
||||
return convertVideoFrameToImage(self.videoFrame)
|
||||
}
|
||||
}
|
||||
|
@ -593,6 +593,10 @@ public func chatMessagePhotoInternal(photoData: Signal<Tuple4<Data?, Data?, Chat
|
||||
return ({
|
||||
return nil
|
||||
}, quality, { arguments in
|
||||
if !synchronousLoad {
|
||||
assert(!Thread.isMainThread)
|
||||
}
|
||||
|
||||
let drawingRect = arguments.drawingRect
|
||||
var fittedSize = arguments.imageSize
|
||||
if abs(fittedSize.width - arguments.boundingSize.width).isLessThanOrEqualTo(CGFloat(1.0)) {
|
||||
|
@ -544,7 +544,7 @@ private final class ChannelStatsContextExtractedContentSource: ContextExtractedC
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(contentContainingNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
|
@ -7087,7 +7087,7 @@ private final class VoiceChatContextExtractedContentSource: ContextExtractedCont
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
self.animateTransitionIn()
|
||||
return ContextControllerTakeViewInfo(contentContainingNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds, maskView: self.maskView)
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds, maskView: self.maskView)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
|
@ -1139,7 +1139,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.window?.presentInGlobalOverlay(controller)
|
||||
})
|
||||
}
|
||||
}, openMessageReactionContextMenu: { [weak self] message, sourceNode, gesture, value in
|
||||
}, openMessageReactionContextMenu: { [weak self] message, sourceView, gesture, value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -1164,7 +1164,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
|
||||
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageReactionContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: message, contentNode: sourceNode)), items: .single(items), recognizer: nil, gesture: gesture)
|
||||
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageReactionContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: message, contentView: sourceView)), items: .single(items), recognizer: nil, gesture: gesture)
|
||||
|
||||
dismissController = { [weak controller] completion in
|
||||
controller?.dismiss(completion: {
|
||||
@ -8352,7 +8352,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: strongSelf, sourceNode: node, insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomInset, right: 0.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture, workaroundUseLegacyImplementation: true)
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: strongSelf, sourceView: node.view, insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomInset, right: 0.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture, workaroundUseLegacyImplementation: true)
|
||||
contextController.dismissed = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(interactive: true, {
|
||||
@ -8894,7 +8894,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}))
|
||||
})))
|
||||
}
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: strongSelf, sourceNode: backButtonNode, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: strongSelf, sourceView: backButtonNode.view, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
strongSelf.presentInGlobalOverlay(contextController)
|
||||
})
|
||||
}
|
||||
@ -15846,19 +15846,19 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent
|
||||
|
||||
final class ChatControllerContextReferenceContentSource: ContextReferenceContentSource {
|
||||
private let controller: ViewController
|
||||
private let sourceNode: ContextReferenceContentNode
|
||||
private let sourceView: UIView
|
||||
private let insets: UIEdgeInsets
|
||||
private let contentInsets: UIEdgeInsets
|
||||
|
||||
init(controller: ViewController, sourceNode: ContextReferenceContentNode, insets: UIEdgeInsets, contentInsets: UIEdgeInsets = UIEdgeInsets()) {
|
||||
init(controller: ViewController, sourceView: UIView, insets: UIEdgeInsets, contentInsets: UIEdgeInsets = UIEdgeInsets()) {
|
||||
self.controller = controller
|
||||
self.sourceNode = sourceNode
|
||||
self.sourceView = sourceView
|
||||
self.insets = insets
|
||||
self.contentInsets = contentInsets
|
||||
}
|
||||
|
||||
func transitionInfo() -> ContextControllerReferenceViewInfo? {
|
||||
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds.inset(by: self.insets), insets: self.contentInsets)
|
||||
return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds.inset(by: self.insets), insets: self.contentInsets)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ public final class ChatControllerInteraction {
|
||||
let openPeerMention: (String) -> Void
|
||||
let openMessageContextMenu: (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void
|
||||
let updateMessageReaction: (Message, ChatControllerInteractionReaction) -> Void
|
||||
let openMessageReactionContextMenu: (Message, ContextExtractedContentContainingNode, ContextGesture?, String) -> Void
|
||||
let openMessageReactionContextMenu: (Message, ContextExtractedContentContainingView, ContextGesture?, String) -> Void
|
||||
let activateMessagePinch: (PinchSourceContainerNode) -> Void
|
||||
let openMessageContextActions: (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void
|
||||
let navigateToMessage: (MessageId, MessageId) -> Void
|
||||
@ -157,7 +157,7 @@ public final class ChatControllerInteraction {
|
||||
openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, MessageReference?, Peer?) -> Void,
|
||||
openPeerMention: @escaping (String) -> Void,
|
||||
openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void,
|
||||
openMessageReactionContextMenu: @escaping (Message, ContextExtractedContentContainingNode, ContextGesture?, String) -> Void,
|
||||
openMessageReactionContextMenu: @escaping (Message, ContextExtractedContentContainingView, ContextGesture?, String) -> Void,
|
||||
updateMessageReaction: @escaping (Message, ChatControllerInteractionReaction) -> Void,
|
||||
activateMessagePinch: @escaping (PinchSourceContainerNode) -> Void,
|
||||
openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void,
|
||||
|
@ -1806,6 +1806,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
}
|
||||
|
||||
private func processDisplayedItemRangeChanged(displayedRange: ListViewDisplayedItemRange, transactionState: ChatHistoryTransactionOpaqueState) {
|
||||
if "".isEmpty {
|
||||
return
|
||||
}
|
||||
|
||||
let historyView = transactionState.historyView
|
||||
var isTopReplyThreadMessageShownValue = false
|
||||
var topVisibleMessageRange: ChatTopVisibleMessageRange?
|
||||
|
@ -1365,13 +1365,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
item.controllerInteraction.updateMessageReaction(item.message, .reaction(value))
|
||||
}
|
||||
reactionButtonsNode.openReactionPreview = { gesture, sourceNode, value in
|
||||
reactionButtonsNode.openReactionPreview = { gesture, sourceView, value in
|
||||
guard let strongSelf = self, let item = strongSelf.item else {
|
||||
gesture?.cancel()
|
||||
return
|
||||
}
|
||||
|
||||
item.controllerInteraction.openMessageReactionContextMenu(item.message, sourceNode, gesture, value)
|
||||
item.controllerInteraction.openMessageReactionContextMenu(item.message, sourceView, gesture, value)
|
||||
}
|
||||
reactionButtonsNode.frame = reactionButtonsFrame
|
||||
if let (rect, containerSize) = strongSelf.absoluteRect {
|
||||
|
@ -49,13 +49,13 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
item.controllerInteraction.updateMessageReaction(item.message, .reaction(value))
|
||||
}
|
||||
|
||||
self.dateAndStatusNode.openReactionPreview = { [weak self] gesture, sourceNode, value in
|
||||
self.dateAndStatusNode.openReactionPreview = { [weak self] gesture, sourceView, value in
|
||||
guard let strongSelf = self, let item = strongSelf.item else {
|
||||
gesture?.cancel()
|
||||
return
|
||||
}
|
||||
|
||||
item.controllerInteraction.openMessageReactionContextMenu(item.topMessage, sourceNode, gesture, value)
|
||||
item.controllerInteraction.openMessageReactionContextMenu(item.topMessage, sourceView, gesture, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ final class ChatMessageContextExtractedContentSource: ContextExtractedContentSou
|
||||
return
|
||||
}
|
||||
if item.content.contains(where: { $0.0.stableId == self.message.stableId }), let contentNode = itemNode.getMessageContextSourceNode(stableId: self.selectAll ? nil : self.message.stableId) {
|
||||
result = ContextControllerTakeViewInfo(contentContainingNode: contentNode, contentAreaInScreenSpace: chatNode.convert(chatNode.frameForVisibleArea(), to: nil))
|
||||
result = ContextControllerTakeViewInfo(containingItem: .node(contentNode), contentAreaInScreenSpace: chatNode.convert(chatNode.frameForVisibleArea(), to: nil))
|
||||
}
|
||||
}
|
||||
return result
|
||||
@ -91,7 +91,7 @@ final class ChatMessageReactionContextExtractedContentSource: ContextExtractedCo
|
||||
private weak var chatNode: ChatControllerNode?
|
||||
private let postbox: Postbox
|
||||
private let message: Message
|
||||
private let contentNode: ContextExtractedContentContainingNode
|
||||
private let contentView: ContextExtractedContentContainingView
|
||||
|
||||
var shouldBeDismissed: Signal<Bool, NoError> {
|
||||
if self.message.adAttribute != nil {
|
||||
@ -112,11 +112,11 @@ final class ChatMessageReactionContextExtractedContentSource: ContextExtractedCo
|
||||
|> distinctUntilChanged
|
||||
}
|
||||
|
||||
init(chatNode: ChatControllerNode, postbox: Postbox, message: Message, contentNode: ContextExtractedContentContainingNode) {
|
||||
init(chatNode: ChatControllerNode, postbox: Postbox, message: Message, contentView: ContextExtractedContentContainingView) {
|
||||
self.chatNode = chatNode
|
||||
self.postbox = postbox
|
||||
self.message = message
|
||||
self.contentNode = contentNode
|
||||
self.contentView = contentView
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
@ -133,7 +133,7 @@ final class ChatMessageReactionContextExtractedContentSource: ContextExtractedCo
|
||||
return
|
||||
}
|
||||
if item.content.contains(where: { $0.0.stableId == self.message.stableId }) {
|
||||
result = ContextControllerTakeViewInfo(contentContainingNode: self.contentNode, contentAreaInScreenSpace: chatNode.convert(chatNode.frameForVisibleArea(), to: nil))
|
||||
result = ContextControllerTakeViewInfo(containingItem: .view(self.contentView), contentAreaInScreenSpace: chatNode.convert(chatNode.frameForVisibleArea(), to: nil))
|
||||
}
|
||||
}
|
||||
return result
|
||||
@ -183,7 +183,7 @@ final class ChatMessageNavigationButtonContextExtractedContentSource: ContextExt
|
||||
return nil
|
||||
}
|
||||
|
||||
return ContextControllerTakeViewInfo(contentContainingNode: self.contentNode, contentAreaInScreenSpace: chatNode.convert(chatNode.frameForVisibleArea(), to: nil))
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.contentNode), contentAreaInScreenSpace: chatNode.convert(chatNode.frameForVisibleArea(), to: nil))
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
|
@ -226,7 +226,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
var reactionSelected: ((String) -> Void)?
|
||||
var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingNode, String) -> Void)?
|
||||
var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingView, String) -> Void)?
|
||||
|
||||
override init() {
|
||||
self.dateNode = TextNode()
|
||||
@ -801,24 +801,24 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
reactionButtonPosition.y += item.size.height + 6.0
|
||||
}
|
||||
|
||||
if item.node.supernode == nil {
|
||||
strongSelf.addSubnode(item.node)
|
||||
item.node.frame = CGRect(origin: reactionButtonPosition, size: item.size)
|
||||
if item.node.view.superview == nil {
|
||||
strongSelf.view.addSubview(item.node.view)
|
||||
item.node.view.frame = CGRect(origin: reactionButtonPosition, size: item.size)
|
||||
|
||||
if animation.isAnimated {
|
||||
item.node.layer.animateScale(from: 0.01, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
item.node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
item.node.view.layer.animateScale(from: 0.01, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
item.node.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
} else {
|
||||
animation.animator.updateFrame(layer: item.node.layer, frame: CGRect(origin: reactionButtonPosition, size: item.size), completion: nil)
|
||||
animation.animator.updateFrame(layer: item.node.view.layer, frame: CGRect(origin: reactionButtonPosition, size: item.size), completion: nil)
|
||||
}
|
||||
|
||||
let itemValue = item.value
|
||||
let itemNode = item.node
|
||||
item.node.isGestureEnabled = true
|
||||
item.node.view.isGestureEnabled = true
|
||||
let canViewReactionList = arguments.canViewReactionList
|
||||
item.node.activateAfterCompletion = !canViewReactionList
|
||||
item.node.activated = { [weak itemNode] gesture, _ in
|
||||
item.node.view.activateAfterCompletion = !canViewReactionList
|
||||
item.node.view.activated = { [weak itemNode] gesture, _ in
|
||||
guard let strongSelf = self, canViewReactionList else {
|
||||
return
|
||||
}
|
||||
@ -827,7 +827,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
if let openReactionPreview = strongSelf.openReactionPreview {
|
||||
openReactionPreview(gesture, itemNode.containerNode, itemValue)
|
||||
openReactionPreview(gesture, itemNode.view.containerView, itemValue)
|
||||
} else {
|
||||
gesture.cancel()
|
||||
}
|
||||
@ -838,12 +838,12 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
|
||||
for node in reactionButtons.removedNodes {
|
||||
if animation.isAnimated {
|
||||
node.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false)
|
||||
node.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak node] _ in
|
||||
node?.removeFromSupernode()
|
||||
node.view.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false)
|
||||
node.view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak node] _ in
|
||||
node?.view.removeFromSuperview()
|
||||
})
|
||||
} else {
|
||||
node.removeFromSupernode()
|
||||
node.view.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1195,7 +1195,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
}
|
||||
for (key, button) in self.reactionButtonsContainer.buttons {
|
||||
if key == value {
|
||||
return button.iconView
|
||||
return button.view.iconView
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -1203,8 +1203,8 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
for (_, button) in self.reactionButtonsContainer.buttons {
|
||||
if button.frame.contains(point) {
|
||||
if let result = button.hitTest(self.view.convert(point, to: button.view), with: event) {
|
||||
if button.view.frame.contains(point) {
|
||||
if let result = button.view.hitTest(self.view.convert(point, to: button.view), with: event) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ final class MessageReactionButtonsNode: ASDisplayNode {
|
||||
private var backgroundMaskButtons: [String: UIView] = [:]
|
||||
|
||||
var reactionSelected: ((String) -> Void)?
|
||||
var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingNode, String) -> Void)?
|
||||
var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingView, String) -> Void)?
|
||||
|
||||
override init() {
|
||||
self.container = ReactionButtonsAsyncLayoutContainer()
|
||||
@ -304,23 +304,23 @@ final class MessageReactionButtonsNode: ASDisplayNode {
|
||||
strongSelf.backgroundMaskButtons[item.value] = itemMaskView
|
||||
}
|
||||
|
||||
if item.node.supernode == nil {
|
||||
strongSelf.addSubnode(item.node)
|
||||
if item.node.view.superview == nil {
|
||||
strongSelf.view.addSubview(item.node.view)
|
||||
if animation.isAnimated {
|
||||
item.node.layer.animateScale(from: 0.01, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
item.node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
item.node.view.layer.animateScale(from: 0.01, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
item.node.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
item.node.frame = itemFrame
|
||||
item.node.view.frame = itemFrame
|
||||
} else {
|
||||
animation.animator.updateFrame(layer: item.node.layer, frame: itemFrame, completion: nil)
|
||||
animation.animator.updateFrame(layer: item.node.view.layer, frame: itemFrame, completion: nil)
|
||||
}
|
||||
|
||||
let itemValue = item.value
|
||||
let itemNode = item.node
|
||||
item.node.isGestureEnabled = true
|
||||
item.node.view.isGestureEnabled = true
|
||||
let canViewReactionList = canViewMessageReactionList(message: message)
|
||||
item.node.activateAfterCompletion = !canViewReactionList
|
||||
item.node.activated = { [weak itemNode] gesture, _ in
|
||||
item.node.view.activateAfterCompletion = !canViewReactionList
|
||||
item.node.view.activated = { [weak itemNode] gesture, _ in
|
||||
guard let strongSelf = self, let itemNode = itemNode else {
|
||||
gesture.cancel()
|
||||
return
|
||||
@ -328,9 +328,9 @@ final class MessageReactionButtonsNode: ASDisplayNode {
|
||||
if !canViewReactionList {
|
||||
return
|
||||
}
|
||||
strongSelf.openReactionPreview?(gesture, itemNode.containerNode, itemValue)
|
||||
strongSelf.openReactionPreview?(gesture, itemNode.view.containerView, itemValue)
|
||||
}
|
||||
item.node.additionalActivationProgressLayer = itemMaskView.layer
|
||||
item.node.view.additionalActivationProgressLayer = itemMaskView.layer
|
||||
|
||||
if itemMaskView.superview == nil {
|
||||
strongSelf.backgroundMaskView?.addSubview(itemMaskView)
|
||||
@ -364,12 +364,12 @@ final class MessageReactionButtonsNode: ASDisplayNode {
|
||||
|
||||
for node in reactionButtons.removedNodes {
|
||||
if animation.isAnimated {
|
||||
node.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false)
|
||||
node.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak node] _ in
|
||||
node?.removeFromSupernode()
|
||||
node.view.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false)
|
||||
node.view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak node] _ in
|
||||
node?.view.removeFromSuperview()
|
||||
})
|
||||
} else {
|
||||
node.removeFromSupernode()
|
||||
node.view.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -409,7 +409,7 @@ final class MessageReactionButtonsNode: ASDisplayNode {
|
||||
func reactionTargetView(value: String) -> UIView? {
|
||||
for (key, button) in self.container.buttons {
|
||||
if key == value {
|
||||
return button.iconView
|
||||
return button.view.iconView
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -417,20 +417,20 @@ final class MessageReactionButtonsNode: ASDisplayNode {
|
||||
|
||||
func animateIn(animation: ListViewItemUpdateAnimation) {
|
||||
for (_, button) in self.container.buttons {
|
||||
animation.animator.animateScale(layer: button.layer, from: 0.01, to: 1.0, completion: nil)
|
||||
animation.animator.animateScale(layer: button.view.layer, from: 0.01, to: 1.0, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
func animateOut(animation: ListViewItemUpdateAnimation) {
|
||||
for (_, button) in self.container.buttons {
|
||||
animation.animator.updateScale(layer: button.layer, scale: 0.01, completion: nil)
|
||||
animation.animator.updateScale(layer: button.view.layer, scale: 0.01, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
for (_, button) in self.container.buttons {
|
||||
if button.frame.contains(point) {
|
||||
if let result = button.hitTest(self.view.convert(point, to: button.view), with: event) {
|
||||
if button.view.frame.contains(point) {
|
||||
if let result = button.view.hitTest(self.view.convert(point, to: button.view), with: event) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
@ -600,7 +600,7 @@ final class ChatMessageReactionButtonsNode: ASDisplayNode {
|
||||
private let buttonsNode: MessageReactionButtonsNode
|
||||
|
||||
var reactionSelected: ((String) -> Void)?
|
||||
var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingNode, String) -> Void)?
|
||||
var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingView, String) -> Void)?
|
||||
|
||||
override init() {
|
||||
self.buttonsNode = MessageReactionButtonsNode()
|
||||
|
@ -62,7 +62,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
self.textNode.contentMode = .topLeft
|
||||
self.textNode.contentsScale = UIScreenScale
|
||||
self.textNode.displaysAsynchronously = false
|
||||
self.textNode.displaysAsynchronously = true
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.textAccessibilityOverlayNode)
|
||||
|
||||
|
@ -8063,7 +8063,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
|
||||
}))
|
||||
})))
|
||||
}
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: strongSelf, sourceNode: backButtonNode, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: strongSelf, sourceView: backButtonNode.view, insets: UIEdgeInsets(), contentInsets: UIEdgeInsets(top: 0.0, left: -15.0, bottom: 0.0, right: -15.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
strongSelf.presentInGlobalOverlay(contextController)
|
||||
})
|
||||
}
|
||||
@ -8140,7 +8140,7 @@ private final class SettingsTabBarContextExtractedContentSource: ContextExtracte
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(contentContainingNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
@ -8415,7 +8415,7 @@ private final class MessageContextExtractedContentSource: ContextExtractedConten
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(contentContainingNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
|
@ -8,7 +8,7 @@ import TgVoip
|
||||
import TgVoipWebrtc
|
||||
|
||||
private let debugUseLegacyVersionForReflectors: Bool = {
|
||||
#if DEBUG && false
|
||||
#if DEBUG
|
||||
return true
|
||||
#else
|
||||
return false
|
||||
@ -827,15 +827,15 @@ public final class OngoingCallContext {
|
||||
filteredConnections.append(contentsOf: callConnectionDescriptionsWebrtc(connection, idMapping: reflectorIdMapping))
|
||||
}
|
||||
|
||||
/*#if DEBUG
|
||||
for connection in filteredConnections {
|
||||
if connection.username == "reflector" {
|
||||
filteredConnections.append(OngoingCallConnectionDescriptionWebrtc(reflectorId: 0, hasStun: false, hasTurn: true, hasTcp: true, ip: "91.108.12.1", port: 533, username: "reflector", password: connection.password))
|
||||
|
||||
break
|
||||
if debugUseLegacyVersionForReflectors {
|
||||
for connection in filteredConnections {
|
||||
if connection.username == "reflector" {
|
||||
filteredConnections.append(OngoingCallConnectionDescriptionWebrtc(reflectorId: 0, hasStun: false, hasTurn: true, hasTcp: true, ip: "91.108.12.1", port: 533, username: "reflector", password: connection.password))
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif*/
|
||||
|
||||
let context = OngoingCallThreadLocalContextWebrtc(version: version, queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, connections: filteredConnections, maxLayer: maxLayer, allowP2P: allowP2P, allowTCP: enableTCP, enableStunMarking: enableStunMarking, logPath: tempLogPath, statsLogPath: tempStatsLogPath, sendSignalingData: { [weak callSessionManager] data in
|
||||
callSessionManager?.sendSignalingData(internalId: internalId, data: data)
|
||||
|
@ -302,6 +302,10 @@ public func generateTextEntities(_ text: String, enabledTypes: EnabledEntityType
|
||||
}
|
||||
|
||||
public func addLocallyGeneratedEntities(_ text: String, enabledTypes: EnabledEntityTypes, entities: [MessageTextEntity], mediaDuration: Double? = nil) -> [MessageTextEntity]? {
|
||||
if "".isEmpty {
|
||||
return nil
|
||||
}
|
||||
|
||||
var resultEntities = entities
|
||||
|
||||
var hasDigits = false
|
||||
|
2
third-party/webrtc/webrtc
vendored
2
third-party/webrtc/webrtc
vendored
@ -1 +1 @@
|
||||
Subproject commit 422323456699b93e4e5ea96bd8d1b062098ad9e9
|
||||
Subproject commit a206ca345bbbe520e0506ce4caf3ab4844204a58
|
Loading…
x
Reference in New Issue
Block a user