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 {
|
guard let navigationBar = self.navigationBar else {
|
||||||
return nil
|
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? {
|
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||||
|
@ -366,7 +366,7 @@ public final class CallListController: TelegramBaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func takeView() -> ContextControllerTakeViewInfo? {
|
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? {
|
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||||
@ -510,7 +510,7 @@ private final class CallListTabBarContextExtractedContentSource: ContextExtracte
|
|||||||
}
|
}
|
||||||
|
|
||||||
func takeView() -> ContextControllerTakeViewInfo? {
|
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? {
|
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||||
|
@ -3209,7 +3209,7 @@ private final class ChatListTabBarContextExtractedContentSource: ContextExtracte
|
|||||||
}
|
}
|
||||||
|
|
||||||
func takeView() -> ContextControllerTakeViewInfo? {
|
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? {
|
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||||
@ -3232,7 +3232,7 @@ private final class ChatListHeaderBarContextExtractedContentSource: ContextExtra
|
|||||||
}
|
}
|
||||||
|
|
||||||
func takeView() -> ContextControllerTakeViewInfo? {
|
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? {
|
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||||
|
@ -1406,7 +1406,7 @@ private final class MessageContextExtractedContentSource: ContextExtractedConten
|
|||||||
}
|
}
|
||||||
|
|
||||||
func takeView() -> ContextControllerTakeViewInfo? {
|
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? {
|
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||||
|
@ -1782,6 +1782,7 @@ public final class ChatListNode: ListView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var options = transition.options
|
var options = transition.options
|
||||||
|
if self.view.window != nil {
|
||||||
if !options.contains(.AnimateInsertion) {
|
if !options.contains(.AnimateInsertion) {
|
||||||
options.insert(.PreferSynchronousDrawing)
|
options.insert(.PreferSynchronousDrawing)
|
||||||
options.insert(.PreferSynchronousResourceLoading)
|
options.insert(.PreferSynchronousResourceLoading)
|
||||||
@ -1789,6 +1790,7 @@ public final class ChatListNode: ListView {
|
|||||||
if options.contains(.AnimateCrossfade) && !self.isDeceleratingAfterTracking {
|
if options.contains(.AnimateCrossfade) && !self.isDeceleratingAfterTracking {
|
||||||
options.insert(.PreferSynchronousDrawing)
|
options.insert(.PreferSynchronousDrawing)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var scrollToItem = transition.scrollToItem
|
var scrollToItem = transition.scrollToItem
|
||||||
if transition.adjustScrollToFirstItem {
|
if transition.adjustScrollToFirstItem {
|
||||||
|
@ -32,8 +32,25 @@ public final class ReactionIconView: PortalSourceView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
private final class ReactionImageCache {
|
||||||
fileprivate final class ContainerButtonNode: HighlightTrackingButtonNode {
|
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 {
|
struct Colors: Equatable {
|
||||||
var background: UInt32
|
var background: UInt32
|
||||||
var foreground: UInt32
|
var foreground: UInt32
|
||||||
@ -65,8 +82,17 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
|||||||
private var animationState: AnimationState?
|
private var animationState: AnimationState?
|
||||||
private var animator: ConstantDisplayLinkAnimator?
|
private var animator: ConstantDisplayLinkAnimator?
|
||||||
|
|
||||||
init() {
|
override init(frame: CGRect) {
|
||||||
super.init(pointerStyle: nil)
|
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) {
|
func update(layout: Layout) {
|
||||||
@ -130,14 +156,21 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let image = generateImage(layout.size, rotatedContext: { size, context in
|
let isExtracted = self.isExtracted
|
||||||
|
let animationState = self.animationState
|
||||||
|
|
||||||
|
DispatchQueue.global().async { [weak self] in
|
||||||
|
var image: UIImage?
|
||||||
|
|
||||||
|
if true {
|
||||||
|
image = generateImage(layout.size, rotatedContext: { size, context in
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
UIGraphicsPushContext(context)
|
UIGraphicsPushContext(context)
|
||||||
|
|
||||||
func drawContents(colors: Colors) {
|
func drawContents(colors: Colors) {
|
||||||
let backgroundColor: UIColor
|
let backgroundColor: UIColor
|
||||||
let foregroundColor: UIColor
|
let foregroundColor: UIColor
|
||||||
if self.isExtracted {
|
if isExtracted {
|
||||||
backgroundColor = UIColor(argb: colors.extractedBackground)
|
backgroundColor = UIColor(argb: colors.extractedBackground)
|
||||||
foregroundColor = UIColor(argb: colors.extractedForeground)
|
foregroundColor = UIColor(argb: colors.extractedForeground)
|
||||||
} else {
|
} else {
|
||||||
@ -161,7 +194,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
|||||||
var rightTextOrigin = textOrigin + totalComponentWidth
|
var rightTextOrigin = textOrigin + totalComponentWidth
|
||||||
|
|
||||||
let animationFraction: CGFloat
|
let animationFraction: CGFloat
|
||||||
if let animationState = self.animationState, animationState.fromCounter != nil {
|
if let animationState = animationState, animationState.fromCounter != nil {
|
||||||
animationFraction = max(0.0, min(1.0, (CACurrentMediaTime() - animationState.startTime) / animationState.duration))
|
animationFraction = max(0.0, min(1.0, (CACurrentMediaTime() - animationState.startTime) / animationState.duration))
|
||||||
} else {
|
} else {
|
||||||
animationFraction = 1.0
|
animationFraction = 1.0
|
||||||
@ -172,7 +205,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
|||||||
var componentAlpha: CGFloat = 1.0
|
var componentAlpha: CGFloat = 1.0
|
||||||
var componentVerticalOffset: CGFloat = 0.0
|
var componentVerticalOffset: CGFloat = 0.0
|
||||||
|
|
||||||
if let animationState = self.animationState, let fromCounter = animationState.fromCounter {
|
if let animationState = animationState, let fromCounter = animationState.fromCounter {
|
||||||
let reverseIndex = counter.components.count - 1 - i
|
let reverseIndex = counter.components.count - 1 - i
|
||||||
if reverseIndex < fromCounter.components.count {
|
if reverseIndex < fromCounter.components.count {
|
||||||
let previousComponent = fromCounter.components[fromCounter.components.count - 1 - reverseIndex]
|
let previousComponent = fromCounter.components[fromCounter.components.count - 1 - reverseIndex]
|
||||||
@ -219,7 +252,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let animationState = self.animationState, animationState.fromColors.isSelected != layout.colors.isSelected {
|
if let animationState = animationState, animationState.fromColors.isSelected != layout.colors.isSelected {
|
||||||
var animationFraction: CGFloat = max(0.0, min(1.0, (CACurrentMediaTime() - animationState.startTime) / animationState.duration))
|
var animationFraction: CGFloat = max(0.0, min(1.0, (CACurrentMediaTime() - animationState.startTime) / animationState.duration))
|
||||||
if !layout.colors.isSelected {
|
if !layout.colors.isSelected {
|
||||||
animationFraction = 1.0 - animationFraction
|
animationFraction = 1.0 - animationFraction
|
||||||
@ -245,14 +278,19 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UIGraphicsPopContext()
|
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)
|
DispatchQueue.main.async {
|
||||||
|
if let strongSelf = self, let image = image {
|
||||||
|
let previousContents = strongSelf.layer.contents
|
||||||
|
|
||||||
|
ASDisplayNodeSetResizableContents(strongSelf.layer, image)
|
||||||
|
|
||||||
if animated, let previousContents = previousContents {
|
if animated, let previousContents = previousContents {
|
||||||
self.layer.animate(from: previousContents as! CGImage, to: image.cgImage!, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.2)
|
strongSelf.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?
|
private var layout: Layout?
|
||||||
|
|
||||||
public let containerNode: ContextExtractedContentContainingNode
|
public let containerView: ContextExtractedContentContainingView
|
||||||
private let buttonNode: ContainerButtonNode
|
private let buttonNode: ContainerButtonNode
|
||||||
public let iconView: ReactionIconView
|
public var iconView: ReactionIconView?
|
||||||
private var avatarsView: AnimatedAvatarSetView?
|
private var avatarsView: AnimatedAvatarSetView?
|
||||||
|
|
||||||
private let iconImageDisposable = MetaDisposable()
|
private let iconImageDisposable = MetaDisposable()
|
||||||
@ -481,42 +519,40 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override init() {
|
override init(frame: CGRect) {
|
||||||
self.containerNode = ContextExtractedContentContainingNode()
|
self.containerView = ContextExtractedContentContainingView()
|
||||||
self.buttonNode = ContainerButtonNode()
|
self.buttonNode = ContainerButtonNode()
|
||||||
|
|
||||||
self.iconView = ReactionIconView()
|
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.addSubview(self.containerView)
|
||||||
self.containerNode.contentNode.addSubnode(self.buttonNode)
|
self.containerView.contentView.addSubview(self.buttonNode)
|
||||||
self.buttonNode.view.addSubview(self.iconView)
|
if let iconView = self.iconView {
|
||||||
|
self.buttonNode.addSubview(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.buttonNode.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||||
|
|
||||||
self.isGestureEnabled = true
|
self.isGestureEnabled = true
|
||||||
self.beginDelay = 0.0
|
self.beginDelay = 0.0
|
||||||
|
|
||||||
self.containerNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, _ in
|
self.containerView.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.buttonNode.updateIsExtracted(isExtracted: isExtracted, animated: true)
|
strongSelf.buttonNode.updateIsExtracted(isExtracted: isExtracted, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.activateAfterCompletion {
|
||||||
|
self.contextGesture?.activatedAfterCompletion = { [weak self] in
|
||||||
|
self?.pressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
required init?(coder aDecoder: NSCoder) {
|
||||||
@ -527,14 +563,11 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
|||||||
self.iconImageDisposable.dispose()
|
self.iconImageDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func didLoad() {
|
func reset() {
|
||||||
super.didLoad()
|
self.iconView?.imageView.image = nil
|
||||||
|
self.layout = nil
|
||||||
|
|
||||||
if self.activateAfterCompletion {
|
self.buttonNode.reset()
|
||||||
self.contextGesture?.activatedAfterCompletion = { [weak self] in
|
|
||||||
self?.pressed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func pressed() {
|
@objc private func pressed() {
|
||||||
@ -545,44 +578,74 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func apply(layout: Layout, animation: ListViewItemUpdateAnimation) {
|
fileprivate func apply(layout: Layout, animation: ListViewItemUpdateAnimation) {
|
||||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
self.containerView.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
self.containerNode.contentNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
self.containerView.contentView.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
self.containerNode.contentRect = 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)
|
animation.animator.updateFrame(layer: self.buttonNode.layer, frame: CGRect(origin: CGPoint(), size: layout.size), completion: nil)
|
||||||
|
|
||||||
self.buttonNode.update(layout: layout.backgroundLayout)
|
self.buttonNode.update(layout: layout.backgroundLayout)
|
||||||
|
|
||||||
animation.animator.updateFrame(layer: self.iconView.layer, frame: layout.imageFrame, completion: nil)
|
if let iconView = self.iconView {
|
||||||
self.iconView.update(size: layout.imageFrame.size, transition: animation.transition)
|
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 self.layout?.spec.component.reaction != layout.spec.component.reaction {
|
||||||
if let file = layout.spec.component.reaction.centerAnimation {
|
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))
|
if let image = ReactionImageCache.shared.get(reaction: layout.spec.component.reaction.value) {
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] data in
|
iconView.imageView.image = image
|
||||||
guard let strongSelf = self else {
|
} else {
|
||||||
return
|
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
|
||||||
}
|
}
|
||||||
|
|> take(1)
|
||||||
|
|> map { data -> UIImage? in
|
||||||
if data.isComplete, let dataValue = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
|
if data.isComplete, let dataValue = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
|
||||||
if let image = UIImage(data: dataValue) {
|
if let image = UIImage(data: dataValue) {
|
||||||
strongSelf.iconView.imageView.image = image
|
return image.precomposed()
|
||||||
|
} else {
|
||||||
|
print("Could not decode image")
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
print("Incomplete data")
|
||||||
}
|
}
|
||||||
}))
|
return nil
|
||||||
} 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] image in
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] data in
|
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if data.complete, let dataValue = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
|
if let image = image {
|
||||||
if let image = WebP.convert(fromWebP: dataValue) {
|
strongSelf.iconView?.imageView.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
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !layout.spec.component.avatarPeers.isEmpty {
|
if !layout.spec.component.avatarPeers.isEmpty {
|
||||||
@ -593,7 +656,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
|||||||
avatarsView = AnimatedAvatarSetView()
|
avatarsView = AnimatedAvatarSetView()
|
||||||
avatarsView.isUserInteractionEnabled = false
|
avatarsView.isUserInteractionEnabled = false
|
||||||
self.avatarsView = avatarsView
|
self.avatarsView = avatarsView
|
||||||
self.buttonNode.view.addSubview(avatarsView)
|
self.buttonNode.addSubview(avatarsView)
|
||||||
}
|
}
|
||||||
let content = AnimatedAvatarSetContext().update(peers: layout.spec.component.avatarPeers, animated: false)
|
let content = AnimatedAvatarSetContext().update(peers: layout.spec.component.avatarPeers, animated: false)
|
||||||
let avatarsSize = avatarsView.update(
|
let avatarsSize = avatarsView.update(
|
||||||
@ -620,8 +683,8 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
|||||||
self.layout = layout
|
self.layout = layout
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func asyncLayout(_ view: ReactionButtonAsyncNode?) -> (ReactionButtonComponent) -> (size: CGSize, apply: (_ animation: ListViewItemUpdateAnimation) -> ReactionButtonAsyncNode) {
|
public static func asyncLayout(_ item: ReactionNodePool.Item?) -> (ReactionButtonComponent) -> (size: CGSize, apply: (_ animation: ListViewItemUpdateAnimation) -> ReactionNodePool.Item) {
|
||||||
let currentLayout = view?.layout
|
let currentLayout = item?.view.layout
|
||||||
|
|
||||||
return { component in
|
return { component in
|
||||||
let spec = Layout.Spec(component: component)
|
let spec = Layout.Spec(component: component)
|
||||||
@ -635,17 +698,17 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
|||||||
|
|
||||||
return (size: layout.size, apply: { animation in
|
return (size: layout.size, apply: { animation in
|
||||||
var animation = animation
|
var animation = animation
|
||||||
let updatedView: ReactionButtonAsyncNode
|
let updatedItem: ReactionNodePool.Item
|
||||||
if let view = view {
|
if let item = item {
|
||||||
updatedView = view
|
updatedItem = item
|
||||||
} else {
|
} else {
|
||||||
updatedView = ReactionButtonAsyncNode()
|
updatedItem = ReactionNodePool.shared.take()
|
||||||
animation = .None
|
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 final class ReactionButtonsAsyncLayoutContainer {
|
||||||
public struct Reaction {
|
public struct Reaction {
|
||||||
public var reaction: ReactionButtonComponent.Reaction
|
public var reaction: ReactionButtonComponent.Reaction
|
||||||
@ -783,15 +879,15 @@ public final class ReactionButtonsAsyncLayoutContainer {
|
|||||||
public struct ApplyResult {
|
public struct ApplyResult {
|
||||||
public struct Item {
|
public struct Item {
|
||||||
public var value: String
|
public var value: String
|
||||||
public var node: ReactionButtonAsyncNode
|
public var node: ReactionNodePool.Item
|
||||||
public var size: CGSize
|
public var size: CGSize
|
||||||
}
|
}
|
||||||
|
|
||||||
public var items: [Item]
|
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() {
|
public init() {
|
||||||
}
|
}
|
||||||
@ -804,7 +900,7 @@ public final class ReactionButtonsAsyncLayoutContainer {
|
|||||||
constrainedWidth: CGFloat
|
constrainedWidth: CGFloat
|
||||||
) -> Result {
|
) -> Result {
|
||||||
var items: [Result.Item] = []
|
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>()
|
var validIds = Set<String>()
|
||||||
for reaction in reactions.sorted(by: { lhs, rhs in
|
for reaction in reactions.sorted(by: { lhs, rhs in
|
||||||
@ -856,10 +952,10 @@ public final class ReactionButtonsAsyncLayoutContainer {
|
|||||||
removeIds.append(id)
|
removeIds.append(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var removedNodes: [ReactionButtonAsyncNode] = []
|
var removedNodes: [ReactionNodePool.Item] = []
|
||||||
for id in removeIds {
|
for id in removeIds {
|
||||||
if let node = self.buttons.removeValue(forKey: id) {
|
if let item = self.buttons.removeValue(forKey: id) {
|
||||||
removedNodes.append(node)
|
removedNodes.append(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -868,13 +964,13 @@ public final class ReactionButtonsAsyncLayoutContainer {
|
|||||||
apply: { animation in
|
apply: { animation in
|
||||||
var items: [ApplyResult.Item] = []
|
var items: [ApplyResult.Item] = []
|
||||||
for (key, size, apply) in applyItems {
|
for (key, size, apply) in applyItems {
|
||||||
let node = apply(animation)
|
let nodeItem = apply(animation)
|
||||||
items.append(ApplyResult.Item(value: key, node: node, size: size))
|
items.append(ApplyResult.Item(value: key, node: nodeItem, size: size))
|
||||||
|
|
||||||
if let current = self.buttons[key] {
|
if let current = self.buttons[key] {
|
||||||
assert(current === node)
|
assert(current === nodeItem)
|
||||||
} else {
|
} else {
|
||||||
self.buttons[key] = node
|
self.buttons[key] = nodeItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -698,7 +698,7 @@ private final class ContactsTabBarContextExtractedContentSource: ContextExtracte
|
|||||||
}
|
}
|
||||||
|
|
||||||
func takeView() -> ContextControllerTakeViewInfo? {
|
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? {
|
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||||
|
@ -2091,12 +2091,17 @@ public extension ContextReferenceContentSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class ContextControllerTakeViewInfo {
|
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 contentAreaInScreenSpace: CGRect
|
||||||
public let maskView: UIView?
|
public let maskView: UIView?
|
||||||
|
|
||||||
public init(contentContainingNode: ContextExtractedContentContainingNode, contentAreaInScreenSpace: CGRect, maskView: UIView? = nil) {
|
public init(containingItem: ContainingItem, contentAreaInScreenSpace: CGRect, maskView: UIView? = nil) {
|
||||||
self.contentContainingNode = contentContainingNode
|
self.containingItem = containingItem
|
||||||
self.contentAreaInScreenSpace = contentAreaInScreenSpace
|
self.contentAreaInScreenSpace = contentAreaInScreenSpace
|
||||||
self.maskView = maskView
|
self.maskView = maskView
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,107 @@ import TelegramCore
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import ReactionSelectionNode
|
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 {
|
final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextControllerPresentationNode, UIScrollViewDelegate {
|
||||||
enum ContentSource {
|
enum ContentSource {
|
||||||
case reference(ContextReferenceContentSource)
|
case reference(ContextReferenceContentSource)
|
||||||
@ -16,14 +117,14 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
|
|
||||||
private final class ContentNode: ASDisplayNode {
|
private final class ContentNode: ASDisplayNode {
|
||||||
let offsetContainerNode: ASDisplayNode
|
let offsetContainerNode: ASDisplayNode
|
||||||
let containingNode: ContextExtractedContentContainingNode
|
var containingItem: ContextControllerTakeViewInfo.ContainingItem
|
||||||
|
|
||||||
var animateClippingFromContentAreaInScreenSpace: CGRect?
|
var animateClippingFromContentAreaInScreenSpace: CGRect?
|
||||||
var storedGlobalFrame: CGRect?
|
var storedGlobalFrame: CGRect?
|
||||||
|
|
||||||
init(containingNode: ContextExtractedContentContainingNode) {
|
init(containingItem: ContextControllerTakeViewInfo.ContainingItem) {
|
||||||
self.offsetContainerNode = ASDisplayNode()
|
self.offsetContainerNode = ASDisplayNode()
|
||||||
self.containingNode = containingNode
|
self.containingItem = containingItem
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
@ -34,8 +135,15 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func takeContainingNode() {
|
func takeContainingNode() {
|
||||||
if self.containingNode.contentNode.supernode !== self.offsetContainerNode {
|
switch self.containingItem {
|
||||||
self.offsetContainerNode.addSubnode(self.containingNode.contentNode)
|
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) {
|
if !self.bounds.contains(point) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !self.containingNode.contentRect.contains(point) {
|
if !self.containingItem.contentRect.contains(point) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return self.view
|
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 {
|
if case let .extracted(source) = self.source, !source.ignoreContentTouches, let contentNode = self.contentNode {
|
||||||
let contentPoint = self.view.convert(point, to: contentNode.containingNode.contentNode.view)
|
let contentPoint = self.view.convert(point, to: contentNode.containingItem.contentView)
|
||||||
if let result = contentNode.containingNode.contentNode.customHitTest?(contentPoint) {
|
if let result = contentNode.containingItem.customHitTest?(contentPoint) {
|
||||||
return result
|
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 {
|
if result is TextSelectionNodeView {
|
||||||
return result
|
return result
|
||||||
} else if contentNode.containingNode.contentRect.contains(contentPoint) {
|
} else if contentNode.containingItem.contentRect.contains(contentPoint) {
|
||||||
return contentNode.containingNode.contentNode.view
|
return contentNode.containingItem.contentView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -291,7 +399,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
guard let takeInfo = source.takeView() else {
|
guard let takeInfo = source.takeView() else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let contentNodeValue = ContentNode(containingNode: takeInfo.contentContainingNode)
|
let contentNodeValue = ContentNode(containingItem: takeInfo.containingItem)
|
||||||
contentNodeValue.animateClippingFromContentAreaInScreenSpace = takeInfo.contentAreaInScreenSpace
|
contentNodeValue.animateClippingFromContentAreaInScreenSpace = takeInfo.contentAreaInScreenSpace
|
||||||
self.scrollNode.insertSubnode(contentNodeValue, aboveSubnode: self.actionsStackNode)
|
self.scrollNode.insertSubnode(contentNodeValue, aboveSubnode: self.actionsStackNode)
|
||||||
self.contentNode = contentNodeValue
|
self.contentNode = contentNodeValue
|
||||||
@ -329,10 +437,10 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
if let contentNode = contentNode {
|
if let contentNode = contentNode {
|
||||||
switch stateTransition {
|
switch stateTransition {
|
||||||
case .animateIn, .animateOut:
|
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:
|
case .none:
|
||||||
if contentNode.storedGlobalFrame == nil {
|
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:
|
case .extracted:
|
||||||
if let contentNode = contentNode {
|
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)
|
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.containingNode.contentRect.size.height), size: contentNode.containingNode.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 {
|
if case .animateOut = stateTransition {
|
||||||
contentRect.origin.y = self.contentRectDebugNode.frame.maxY - contentRect.size.height
|
contentRect.origin.y = self.contentRectDebugNode.frame.maxY - contentRect.size.height
|
||||||
}
|
}
|
||||||
@ -380,7 +488,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
if let contentNode = contentNode {
|
if let contentNode = contentNode {
|
||||||
contentNode.update(
|
contentNode.update(
|
||||||
presentationData: presentationData,
|
presentationData: presentationData,
|
||||||
size: contentNode.containingNode.bounds.size,
|
size: contentNode.containingItem.view.bounds.size,
|
||||||
transition: contentTransition
|
transition: contentTransition
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -485,7 +593,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
transition.updateFrame(node: self.actionsStackNode, frame: actionsFrame, beginWithCurrentState: true)
|
transition.updateFrame(node: self.actionsStackNode, frame: actionsFrame, beginWithCurrentState: true)
|
||||||
|
|
||||||
if let contentNode = contentNode {
|
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
|
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)
|
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)
|
let currentContentLocalFrame = convertFrame(contentRect, from: self.scrollNode.view, to: self.view)
|
||||||
animationInContentDistance = currentContentLocalFrame.maxY - currentContentScreenFrame.maxY
|
animationInContentDistance = currentContentLocalFrame.maxY - currentContentScreenFrame.maxY
|
||||||
|
|
||||||
@ -623,11 +731,11 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
self.actionsStackNode.animateIn()
|
self.actionsStackNode.animateIn()
|
||||||
|
|
||||||
if let contentNode = contentNode {
|
if let contentNode = contentNode {
|
||||||
contentNode.containingNode.isExtractedToContextPreview = true
|
contentNode.containingItem.isExtractedToContextPreview = true
|
||||||
contentNode.containingNode.isExtractedToContextPreviewUpdated?(true)
|
contentNode.containingItem.isExtractedToContextPreviewUpdated?(true)
|
||||||
contentNode.containingNode.willUpdateIsExtractedToContextPreview?(true, transition)
|
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 {
|
guard let strongSelf = self, let _ = strongSelf.contentNode else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -677,7 +785,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let contentNode = contentNode {
|
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 {
|
} else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -719,7 +827,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
let completeWithActionStack = contentNode == nil
|
let completeWithActionStack = contentNode == nil
|
||||||
|
|
||||||
if let contentNode = contentNode {
|
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)
|
contentNode.offsetContainerNode.position = contentNode.offsetContainerNode.position.offsetBy(dx: 0.0, dy: -animationInContentDistance)
|
||||||
let reactionContextNodeIsAnimatingOut = self.reactionContextNodeIsAnimatingOut
|
let reactionContextNodeIsAnimatingOut = self.reactionContextNodeIsAnimatingOut
|
||||||
@ -733,11 +841,16 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
|||||||
additive: true,
|
additive: true,
|
||||||
completion: { [weak self] _ in
|
completion: { [weak self] _ in
|
||||||
Queue.mainQueue().after(reactionContextNodeIsAnimatingOut ? 0.2 * UIView.animationDurationFactor() : 0.0, {
|
Queue.mainQueue().after(reactionContextNodeIsAnimatingOut ? 0.2 * UIView.animationDurationFactor() : 0.0, {
|
||||||
contentNode.containingNode.isExtractedToContextPreview = false
|
contentNode.containingItem.isExtractedToContextPreview = false
|
||||||
contentNode.containingNode.isExtractedToContextPreviewUpdated?(false)
|
contentNode.containingItem.isExtractedToContextPreviewUpdated?(false)
|
||||||
|
|
||||||
if let strongSelf = self, let contentNode = strongSelf.contentNode {
|
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()
|
completion()
|
||||||
|
@ -37,12 +37,76 @@ public final class ContextExtractedContentContainingNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class ContextExtractedContentNode: ASDisplayNode {
|
public final class ContextExtractedContentContainingView: UIView {
|
||||||
public var customHitTest: ((CGPoint) -> 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? {
|
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
let result = self.view.hitTest(point, with: event)
|
if self.contentView.superview === self {
|
||||||
if result === self.view {
|
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
|
return nil
|
||||||
} else {
|
} else {
|
||||||
return result
|
return result
|
||||||
|
@ -126,3 +126,264 @@ open class ContextControllerSourceNode: ContextReferenceContentNode {
|
|||||||
contextGesture.isEnabled = self.isGestureEnabled
|
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 = CADisplayLink(target: DisplayLinkProxy(target: self), selector: #selector(DisplayLinkProxy.displayLinkEvent))
|
||||||
self.displayLink.add(to: RunLoop.main, forMode: RunLoop.Mode.common)
|
self.displayLink.add(to: RunLoop.main, forMode: RunLoop.Mode.common)
|
||||||
|
|
||||||
if #available(iOS 10.0, *) {
|
if #available(iOS 15.0, iOSApplicationExtension 15.0, *) {
|
||||||
self.displayLink.preferredFramesPerSecond = 60
|
self.displayLink?.preferredFrameRateRange = CAFrameRateRange(minimum: 60.0, maximum: 120.0, preferred: 120.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.displayLink.isPaused = true
|
self.displayLink.isPaused = true
|
||||||
@ -522,15 +522,14 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func dispatchOnVSync(forceNext: Bool = false, action: @escaping () -> ()) {
|
private func dispatchOnVSync(forceNext: Bool = false, action: @escaping () -> ()) {
|
||||||
Queue.mainQueue().async {
|
/*Queue.mainQueue().async {
|
||||||
if !forceNext && self.inVSync {
|
if !forceNext && self.inVSync {
|
||||||
action()
|
action()
|
||||||
} else {
|
} else {
|
||||||
action()
|
action()
|
||||||
//self.actionsForVSync.append(action)
|
|
||||||
//self.setNeedsAnimations()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
DispatchQueue.main.async(execute: action)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func beginReordering(itemNode: ListViewItemNode) {
|
private func beginReordering(itemNode: ListViewItemNode) {
|
||||||
@ -1576,6 +1575,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
|||||||
|
|
||||||
private func async(_ f: @escaping () -> Void) {
|
private func async(_ f: @escaping () -> Void) {
|
||||||
DispatchQueue.global(qos: .userInteractive).async(execute: f)
|
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) {
|
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 Foundation
|
||||||
import UIKit
|
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 {
|
public extension CALayer {
|
||||||
func addShakeAnimation(amplitude: CGFloat = 3.0, duration: Double = 0.3, count: Int = 4, decay: Bool = false) {
|
func addShakeAnimation(amplitude: CGFloat = 3.0, duration: Double = 0.3, count: Int = 4, decay: Bool = false) {
|
||||||
let k = Float(UIView.animationDurationFactor())
|
let k = Float(UIView.animationDurationFactor())
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
if (self != nil) {
|
if (self != nil) {
|
||||||
_codec = codec;
|
_codec = codec;
|
||||||
_impl = avcodec_alloc_context3((AVCodec *)[codec impl]);
|
_impl = avcodec_alloc_context3((AVCodec *)[codec impl]);
|
||||||
|
_impl->max_pixels = 4 * 1024 * 4 * 1024;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -976,7 +976,7 @@ final class InviteLinkContextExtractedContentSource: ContextExtractedContentSour
|
|||||||
}
|
}
|
||||||
|
|
||||||
func takeView() -> ContextControllerTakeViewInfo? {
|
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? {
|
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||||
|
@ -425,7 +425,7 @@ final class InviteRequestsContextExtractedContentSource: ContextExtractedContent
|
|||||||
}
|
}
|
||||||
|
|
||||||
func takeView() -> ContextControllerTakeViewInfo? {
|
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? {
|
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||||
|
@ -48,6 +48,7 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
|||||||
|
|
||||||
private let videoFrame: FFMpegAVFrame
|
private let videoFrame: FFMpegAVFrame
|
||||||
private var resetDecoderOnNextFrame = true
|
private var resetDecoderOnNextFrame = true
|
||||||
|
private var isError = false
|
||||||
|
|
||||||
private var defaultDuration: CMTime?
|
private var defaultDuration: CMTime?
|
||||||
private var defaultTimescale: CMTimeScale?
|
private var defaultTimescale: CMTimeScale?
|
||||||
@ -90,6 +91,10 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func receiveFromDecoder(ptsOffset: CMTime?) -> ReceiveResult {
|
public func receiveFromDecoder(ptsOffset: CMTime?) -> ReceiveResult {
|
||||||
|
if self.isError {
|
||||||
|
return .error
|
||||||
|
}
|
||||||
|
|
||||||
guard let defaultTimescale = self.defaultTimescale, let defaultDuration = self.defaultDuration else {
|
guard let defaultTimescale = self.defaultTimescale, let defaultDuration = self.defaultDuration else {
|
||||||
return .error
|
return .error
|
||||||
}
|
}
|
||||||
@ -97,6 +102,11 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
|||||||
let receiveResult = self.codecContext.receive(into: self.videoFrame)
|
let receiveResult = self.codecContext.receive(into: self.videoFrame)
|
||||||
switch receiveResult {
|
switch receiveResult {
|
||||||
case .success:
|
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)
|
var pts = CMTimeMake(value: self.videoFrame.pts, timescale: defaultTimescale)
|
||||||
if let ptsOffset = ptsOffset {
|
if let ptsOffset = ptsOffset {
|
||||||
pts = CMTimeAdd(pts, 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? {
|
public func decode(frame: MediaTrackDecodableFrame, ptsOffset: CMTime?, forceARGB: Bool = false) -> MediaTrackFrame? {
|
||||||
|
if self.isError {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
let status = frame.packet.send(toDecoder: self.codecContext)
|
let status = frame.packet.send(toDecoder: self.codecContext)
|
||||||
if status == 0 {
|
if status == 0 {
|
||||||
self.defaultDuration = frame.duration
|
self.defaultDuration = frame.duration
|
||||||
self.defaultTimescale = frame.pts.timescale
|
self.defaultTimescale = frame.pts.timescale
|
||||||
|
|
||||||
if self.codecContext.receive(into: self.videoFrame) == .success {
|
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)
|
var pts = CMTimeMake(value: self.videoFrame.pts, timescale: frame.pts.timescale)
|
||||||
if let ptsOffset = ptsOffset {
|
if let ptsOffset = ptsOffset {
|
||||||
pts = CMTimeAdd(pts, ptsOffset)
|
pts = CMTimeAdd(pts, ptsOffset)
|
||||||
@ -137,6 +156,9 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
|||||||
guard let defaultTimescale = self.defaultTimescale, let defaultDuration = self.defaultDuration else {
|
guard let defaultTimescale = self.defaultTimescale, let defaultDuration = self.defaultDuration else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
if self.isError {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
var result: [MediaTrackFrame] = []
|
var result: [MediaTrackFrame] = []
|
||||||
result.append(contentsOf: self.delayedFrames)
|
result.append(contentsOf: self.delayedFrames)
|
||||||
@ -144,6 +166,11 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
|||||||
|
|
||||||
while true {
|
while true {
|
||||||
if case .success = self.codecContext.receive(into: self.videoFrame) {
|
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)
|
var pts = CMTimeMake(value: self.videoFrame.pts, timescale: defaultTimescale)
|
||||||
if let ptsOffset = ptsOffset {
|
if let ptsOffset = ptsOffset {
|
||||||
pts = CMTimeAdd(pts, ptsOffset)
|
pts = CMTimeAdd(pts, ptsOffset)
|
||||||
@ -162,6 +189,11 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
|
|||||||
let status = frame.packet.send(toDecoder: self.codecContext)
|
let status = frame.packet.send(toDecoder: self.codecContext)
|
||||||
if status == 0 {
|
if status == 0 {
|
||||||
if case .success = self.codecContext.receive(into: self.videoFrame) {
|
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)
|
return convertVideoFrameToImage(self.videoFrame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -593,6 +593,10 @@ public func chatMessagePhotoInternal(photoData: Signal<Tuple4<Data?, Data?, Chat
|
|||||||
return ({
|
return ({
|
||||||
return nil
|
return nil
|
||||||
}, quality, { arguments in
|
}, quality, { arguments in
|
||||||
|
if !synchronousLoad {
|
||||||
|
assert(!Thread.isMainThread)
|
||||||
|
}
|
||||||
|
|
||||||
let drawingRect = arguments.drawingRect
|
let drawingRect = arguments.drawingRect
|
||||||
var fittedSize = arguments.imageSize
|
var fittedSize = arguments.imageSize
|
||||||
if abs(fittedSize.width - arguments.boundingSize.width).isLessThanOrEqualTo(CGFloat(1.0)) {
|
if abs(fittedSize.width - arguments.boundingSize.width).isLessThanOrEqualTo(CGFloat(1.0)) {
|
||||||
|
@ -544,7 +544,7 @@ private final class ChannelStatsContextExtractedContentSource: ContextExtractedC
|
|||||||
}
|
}
|
||||||
|
|
||||||
func takeView() -> ContextControllerTakeViewInfo? {
|
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? {
|
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||||
|
@ -7087,7 +7087,7 @@ private final class VoiceChatContextExtractedContentSource: ContextExtractedCont
|
|||||||
|
|
||||||
func takeView() -> ContextControllerTakeViewInfo? {
|
func takeView() -> ContextControllerTakeViewInfo? {
|
||||||
self.animateTransitionIn()
|
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? {
|
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||||
|
@ -1139,7 +1139,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
strongSelf.window?.presentInGlobalOverlay(controller)
|
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 {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1164,7 +1164,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
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
|
dismissController = { [weak controller] completion in
|
||||||
controller?.dismiss(completion: {
|
controller?.dismiss(completion: {
|
||||||
@ -8352,7 +8352,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
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
|
contextController.dismissed = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.updateChatPresentationInterfaceState(interactive: true, {
|
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)
|
strongSelf.presentInGlobalOverlay(contextController)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -15846,19 +15846,19 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent
|
|||||||
|
|
||||||
final class ChatControllerContextReferenceContentSource: ContextReferenceContentSource {
|
final class ChatControllerContextReferenceContentSource: ContextReferenceContentSource {
|
||||||
private let controller: ViewController
|
private let controller: ViewController
|
||||||
private let sourceNode: ContextReferenceContentNode
|
private let sourceView: UIView
|
||||||
private let insets: UIEdgeInsets
|
private let insets: UIEdgeInsets
|
||||||
private let contentInsets: 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.controller = controller
|
||||||
self.sourceNode = sourceNode
|
self.sourceView = sourceView
|
||||||
self.insets = insets
|
self.insets = insets
|
||||||
self.contentInsets = contentInsets
|
self.contentInsets = contentInsets
|
||||||
}
|
}
|
||||||
|
|
||||||
func transitionInfo() -> ContextControllerReferenceViewInfo? {
|
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 openPeerMention: (String) -> Void
|
||||||
let openMessageContextMenu: (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void
|
let openMessageContextMenu: (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void
|
||||||
let updateMessageReaction: (Message, ChatControllerInteractionReaction) -> 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 activateMessagePinch: (PinchSourceContainerNode) -> Void
|
||||||
let openMessageContextActions: (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void
|
let openMessageContextActions: (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void
|
||||||
let navigateToMessage: (MessageId, MessageId) -> Void
|
let navigateToMessage: (MessageId, MessageId) -> Void
|
||||||
@ -157,7 +157,7 @@ public final class ChatControllerInteraction {
|
|||||||
openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, MessageReference?, Peer?) -> Void,
|
openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, MessageReference?, Peer?) -> Void,
|
||||||
openPeerMention: @escaping (String) -> Void,
|
openPeerMention: @escaping (String) -> Void,
|
||||||
openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> 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,
|
updateMessageReaction: @escaping (Message, ChatControllerInteractionReaction) -> Void,
|
||||||
activateMessagePinch: @escaping (PinchSourceContainerNode) -> Void,
|
activateMessagePinch: @escaping (PinchSourceContainerNode) -> Void,
|
||||||
openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> 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) {
|
private func processDisplayedItemRangeChanged(displayedRange: ListViewDisplayedItemRange, transactionState: ChatHistoryTransactionOpaqueState) {
|
||||||
|
if "".isEmpty {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let historyView = transactionState.historyView
|
let historyView = transactionState.historyView
|
||||||
var isTopReplyThreadMessageShownValue = false
|
var isTopReplyThreadMessageShownValue = false
|
||||||
var topVisibleMessageRange: ChatTopVisibleMessageRange?
|
var topVisibleMessageRange: ChatTopVisibleMessageRange?
|
||||||
|
@ -1365,13 +1365,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
item.controllerInteraction.updateMessageReaction(item.message, .reaction(value))
|
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 {
|
guard let strongSelf = self, let item = strongSelf.item else {
|
||||||
gesture?.cancel()
|
gesture?.cancel()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
item.controllerInteraction.openMessageReactionContextMenu(item.message, sourceNode, gesture, value)
|
item.controllerInteraction.openMessageReactionContextMenu(item.message, sourceView, gesture, value)
|
||||||
}
|
}
|
||||||
reactionButtonsNode.frame = reactionButtonsFrame
|
reactionButtonsNode.frame = reactionButtonsFrame
|
||||||
if let (rect, containerSize) = strongSelf.absoluteRect {
|
if let (rect, containerSize) = strongSelf.absoluteRect {
|
||||||
|
@ -49,13 +49,13 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
item.controllerInteraction.updateMessageReaction(item.message, .reaction(value))
|
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 {
|
guard let strongSelf = self, let item = strongSelf.item else {
|
||||||
gesture?.cancel()
|
gesture?.cancel()
|
||||||
return
|
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
|
return
|
||||||
}
|
}
|
||||||
if item.content.contains(where: { $0.0.stableId == self.message.stableId }), let contentNode = itemNode.getMessageContextSourceNode(stableId: self.selectAll ? nil : self.message.stableId) {
|
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
|
return result
|
||||||
@ -91,7 +91,7 @@ final class ChatMessageReactionContextExtractedContentSource: ContextExtractedCo
|
|||||||
private weak var chatNode: ChatControllerNode?
|
private weak var chatNode: ChatControllerNode?
|
||||||
private let postbox: Postbox
|
private let postbox: Postbox
|
||||||
private let message: Message
|
private let message: Message
|
||||||
private let contentNode: ContextExtractedContentContainingNode
|
private let contentView: ContextExtractedContentContainingView
|
||||||
|
|
||||||
var shouldBeDismissed: Signal<Bool, NoError> {
|
var shouldBeDismissed: Signal<Bool, NoError> {
|
||||||
if self.message.adAttribute != nil {
|
if self.message.adAttribute != nil {
|
||||||
@ -112,11 +112,11 @@ final class ChatMessageReactionContextExtractedContentSource: ContextExtractedCo
|
|||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
init(chatNode: ChatControllerNode, postbox: Postbox, message: Message, contentNode: ContextExtractedContentContainingNode) {
|
init(chatNode: ChatControllerNode, postbox: Postbox, message: Message, contentView: ContextExtractedContentContainingView) {
|
||||||
self.chatNode = chatNode
|
self.chatNode = chatNode
|
||||||
self.postbox = postbox
|
self.postbox = postbox
|
||||||
self.message = message
|
self.message = message
|
||||||
self.contentNode = contentNode
|
self.contentView = contentView
|
||||||
}
|
}
|
||||||
|
|
||||||
func takeView() -> ContextControllerTakeViewInfo? {
|
func takeView() -> ContextControllerTakeViewInfo? {
|
||||||
@ -133,7 +133,7 @@ final class ChatMessageReactionContextExtractedContentSource: ContextExtractedCo
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if item.content.contains(where: { $0.0.stableId == self.message.stableId }) {
|
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
|
return result
|
||||||
@ -183,7 +183,7 @@ final class ChatMessageNavigationButtonContextExtractedContentSource: ContextExt
|
|||||||
return nil
|
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? {
|
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||||
|
@ -226,7 +226,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var reactionSelected: ((String) -> Void)?
|
var reactionSelected: ((String) -> Void)?
|
||||||
var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingNode, String) -> Void)?
|
var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingView, String) -> Void)?
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
self.dateNode = TextNode()
|
self.dateNode = TextNode()
|
||||||
@ -801,24 +801,24 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
reactionButtonPosition.y += item.size.height + 6.0
|
reactionButtonPosition.y += item.size.height + 6.0
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.node.supernode == nil {
|
if item.node.view.superview == nil {
|
||||||
strongSelf.addSubnode(item.node)
|
strongSelf.view.addSubview(item.node.view)
|
||||||
item.node.frame = CGRect(origin: reactionButtonPosition, size: item.size)
|
item.node.view.frame = CGRect(origin: reactionButtonPosition, size: item.size)
|
||||||
|
|
||||||
if animation.isAnimated {
|
if animation.isAnimated {
|
||||||
item.node.layer.animateScale(from: 0.01, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
item.node.view.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.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
} else {
|
} 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 itemValue = item.value
|
||||||
let itemNode = item.node
|
let itemNode = item.node
|
||||||
item.node.isGestureEnabled = true
|
item.node.view.isGestureEnabled = true
|
||||||
let canViewReactionList = arguments.canViewReactionList
|
let canViewReactionList = arguments.canViewReactionList
|
||||||
item.node.activateAfterCompletion = !canViewReactionList
|
item.node.view.activateAfterCompletion = !canViewReactionList
|
||||||
item.node.activated = { [weak itemNode] gesture, _ in
|
item.node.view.activated = { [weak itemNode] gesture, _ in
|
||||||
guard let strongSelf = self, canViewReactionList else {
|
guard let strongSelf = self, canViewReactionList else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -827,7 +827,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let openReactionPreview = strongSelf.openReactionPreview {
|
if let openReactionPreview = strongSelf.openReactionPreview {
|
||||||
openReactionPreview(gesture, itemNode.containerNode, itemValue)
|
openReactionPreview(gesture, itemNode.view.containerView, itemValue)
|
||||||
} else {
|
} else {
|
||||||
gesture.cancel()
|
gesture.cancel()
|
||||||
}
|
}
|
||||||
@ -838,12 +838,12 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
|
|
||||||
for node in reactionButtons.removedNodes {
|
for node in reactionButtons.removedNodes {
|
||||||
if animation.isAnimated {
|
if animation.isAnimated {
|
||||||
node.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false)
|
node.view.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.view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak node] _ in
|
||||||
node?.removeFromSupernode()
|
node?.view.removeFromSuperview()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
node.removeFromSupernode()
|
node.view.removeFromSuperview()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1195,7 +1195,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
for (key, button) in self.reactionButtonsContainer.buttons {
|
for (key, button) in self.reactionButtonsContainer.buttons {
|
||||||
if key == value {
|
if key == value {
|
||||||
return button.iconView
|
return button.view.iconView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -1203,8 +1203,8 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
|||||||
|
|
||||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
for (_, button) in self.reactionButtonsContainer.buttons {
|
for (_, button) in self.reactionButtonsContainer.buttons {
|
||||||
if button.frame.contains(point) {
|
if button.view.frame.contains(point) {
|
||||||
if let result = button.hitTest(self.view.convert(point, to: button.view), with: event) {
|
if let result = button.view.hitTest(self.view.convert(point, to: button.view), with: event) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ final class MessageReactionButtonsNode: ASDisplayNode {
|
|||||||
private var backgroundMaskButtons: [String: UIView] = [:]
|
private var backgroundMaskButtons: [String: UIView] = [:]
|
||||||
|
|
||||||
var reactionSelected: ((String) -> Void)?
|
var reactionSelected: ((String) -> Void)?
|
||||||
var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingNode, String) -> Void)?
|
var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingView, String) -> Void)?
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
self.container = ReactionButtonsAsyncLayoutContainer()
|
self.container = ReactionButtonsAsyncLayoutContainer()
|
||||||
@ -304,23 +304,23 @@ final class MessageReactionButtonsNode: ASDisplayNode {
|
|||||||
strongSelf.backgroundMaskButtons[item.value] = itemMaskView
|
strongSelf.backgroundMaskButtons[item.value] = itemMaskView
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.node.supernode == nil {
|
if item.node.view.superview == nil {
|
||||||
strongSelf.addSubnode(item.node)
|
strongSelf.view.addSubview(item.node.view)
|
||||||
if animation.isAnimated {
|
if animation.isAnimated {
|
||||||
item.node.layer.animateScale(from: 0.01, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
item.node.view.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.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
item.node.frame = itemFrame
|
item.node.view.frame = itemFrame
|
||||||
} else {
|
} 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 itemValue = item.value
|
||||||
let itemNode = item.node
|
let itemNode = item.node
|
||||||
item.node.isGestureEnabled = true
|
item.node.view.isGestureEnabled = true
|
||||||
let canViewReactionList = canViewMessageReactionList(message: message)
|
let canViewReactionList = canViewMessageReactionList(message: message)
|
||||||
item.node.activateAfterCompletion = !canViewReactionList
|
item.node.view.activateAfterCompletion = !canViewReactionList
|
||||||
item.node.activated = { [weak itemNode] gesture, _ in
|
item.node.view.activated = { [weak itemNode] gesture, _ in
|
||||||
guard let strongSelf = self, let itemNode = itemNode else {
|
guard let strongSelf = self, let itemNode = itemNode else {
|
||||||
gesture.cancel()
|
gesture.cancel()
|
||||||
return
|
return
|
||||||
@ -328,9 +328,9 @@ final class MessageReactionButtonsNode: ASDisplayNode {
|
|||||||
if !canViewReactionList {
|
if !canViewReactionList {
|
||||||
return
|
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 {
|
if itemMaskView.superview == nil {
|
||||||
strongSelf.backgroundMaskView?.addSubview(itemMaskView)
|
strongSelf.backgroundMaskView?.addSubview(itemMaskView)
|
||||||
@ -364,12 +364,12 @@ final class MessageReactionButtonsNode: ASDisplayNode {
|
|||||||
|
|
||||||
for node in reactionButtons.removedNodes {
|
for node in reactionButtons.removedNodes {
|
||||||
if animation.isAnimated {
|
if animation.isAnimated {
|
||||||
node.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false)
|
node.view.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.view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak node] _ in
|
||||||
node?.removeFromSupernode()
|
node?.view.removeFromSuperview()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
node.removeFromSupernode()
|
node.view.removeFromSuperview()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -409,7 +409,7 @@ final class MessageReactionButtonsNode: ASDisplayNode {
|
|||||||
func reactionTargetView(value: String) -> UIView? {
|
func reactionTargetView(value: String) -> UIView? {
|
||||||
for (key, button) in self.container.buttons {
|
for (key, button) in self.container.buttons {
|
||||||
if key == value {
|
if key == value {
|
||||||
return button.iconView
|
return button.view.iconView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -417,20 +417,20 @@ final class MessageReactionButtonsNode: ASDisplayNode {
|
|||||||
|
|
||||||
func animateIn(animation: ListViewItemUpdateAnimation) {
|
func animateIn(animation: ListViewItemUpdateAnimation) {
|
||||||
for (_, button) in self.container.buttons {
|
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) {
|
func animateOut(animation: ListViewItemUpdateAnimation) {
|
||||||
for (_, button) in self.container.buttons {
|
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? {
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
for (_, button) in self.container.buttons {
|
for (_, button) in self.container.buttons {
|
||||||
if button.frame.contains(point) {
|
if button.view.frame.contains(point) {
|
||||||
if let result = button.hitTest(self.view.convert(point, to: button.view), with: event) {
|
if let result = button.view.hitTest(self.view.convert(point, to: button.view), with: event) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -600,7 +600,7 @@ final class ChatMessageReactionButtonsNode: ASDisplayNode {
|
|||||||
private let buttonsNode: MessageReactionButtonsNode
|
private let buttonsNode: MessageReactionButtonsNode
|
||||||
|
|
||||||
var reactionSelected: ((String) -> Void)?
|
var reactionSelected: ((String) -> Void)?
|
||||||
var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingNode, String) -> Void)?
|
var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingView, String) -> Void)?
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
self.buttonsNode = MessageReactionButtonsNode()
|
self.buttonsNode = MessageReactionButtonsNode()
|
||||||
|
@ -62,7 +62,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
self.textNode.isUserInteractionEnabled = false
|
self.textNode.isUserInteractionEnabled = false
|
||||||
self.textNode.contentMode = .topLeft
|
self.textNode.contentMode = .topLeft
|
||||||
self.textNode.contentsScale = UIScreenScale
|
self.textNode.contentsScale = UIScreenScale
|
||||||
self.textNode.displaysAsynchronously = false
|
self.textNode.displaysAsynchronously = true
|
||||||
self.addSubnode(self.textNode)
|
self.addSubnode(self.textNode)
|
||||||
self.addSubnode(self.textAccessibilityOverlayNode)
|
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)
|
strongSelf.presentInGlobalOverlay(contextController)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -8140,7 +8140,7 @@ private final class SettingsTabBarContextExtractedContentSource: ContextExtracte
|
|||||||
}
|
}
|
||||||
|
|
||||||
func takeView() -> ContextControllerTakeViewInfo? {
|
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? {
|
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||||
@ -8415,7 +8415,7 @@ private final class MessageContextExtractedContentSource: ContextExtractedConten
|
|||||||
}
|
}
|
||||||
|
|
||||||
func takeView() -> ContextControllerTakeViewInfo? {
|
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? {
|
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||||
|
@ -8,7 +8,7 @@ import TgVoip
|
|||||||
import TgVoipWebrtc
|
import TgVoipWebrtc
|
||||||
|
|
||||||
private let debugUseLegacyVersionForReflectors: Bool = {
|
private let debugUseLegacyVersionForReflectors: Bool = {
|
||||||
#if DEBUG && false
|
#if DEBUG
|
||||||
return true
|
return true
|
||||||
#else
|
#else
|
||||||
return false
|
return false
|
||||||
@ -827,7 +827,7 @@ public final class OngoingCallContext {
|
|||||||
filteredConnections.append(contentsOf: callConnectionDescriptionsWebrtc(connection, idMapping: reflectorIdMapping))
|
filteredConnections.append(contentsOf: callConnectionDescriptionsWebrtc(connection, idMapping: reflectorIdMapping))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*#if DEBUG
|
if debugUseLegacyVersionForReflectors {
|
||||||
for connection in filteredConnections {
|
for connection in filteredConnections {
|
||||||
if connection.username == "reflector" {
|
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))
|
filteredConnections.append(OngoingCallConnectionDescriptionWebrtc(reflectorId: 0, hasStun: false, hasTurn: true, hasTcp: true, ip: "91.108.12.1", port: 533, username: "reflector", password: connection.password))
|
||||||
@ -835,7 +835,7 @@ public final class OngoingCallContext {
|
|||||||
break
|
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
|
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)
|
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]? {
|
public func addLocallyGeneratedEntities(_ text: String, enabledTypes: EnabledEntityTypes, entities: [MessageTextEntity], mediaDuration: Double? = nil) -> [MessageTextEntity]? {
|
||||||
|
if "".isEmpty {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var resultEntities = entities
|
var resultEntities = entities
|
||||||
|
|
||||||
var hasDigits = false
|
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