mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Pinned message improvements
This commit is contained in:
parent
1620754988
commit
e37edd6319
@ -5874,7 +5874,7 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
|
|
||||||
"Chat.PanelHidePinnedMessages" = "Don't Show Pinned Messages";
|
"Chat.PanelHidePinnedMessages" = "Don't Show Pinned Messages";
|
||||||
"Chat.PanelUnpinAllMessages_1" = "Unpin Message";
|
"Chat.PanelUnpinAllMessages_1" = "Unpin Message";
|
||||||
"Chat.PanelUnpinAllMessages_any" = "Unpin All %@ Messages";
|
"Chat.PanelUnpinAllMessages_any" = "Unpin All Messages";
|
||||||
"Chat.UnpinAllMessagesConfirmation_1" = "Do you want to unpin 1 message in this chat?";
|
"Chat.UnpinAllMessagesConfirmation_1" = "Do you want to unpin 1 message in this chat?";
|
||||||
"Chat.UnpinAllMessagesConfirmation_any" = "Do you want to unpin all %@ messages in this chat?";
|
"Chat.UnpinAllMessagesConfirmation_any" = "Do you want to unpin all %@ messages in this chat?";
|
||||||
|
|
||||||
@ -5883,3 +5883,5 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
|
|
||||||
"Chat.PinnedMessagesHiddenTitle" = "Pinned Messages Hidden";
|
"Chat.PinnedMessagesHiddenTitle" = "Pinned Messages Hidden";
|
||||||
"Chat.PinnedMessagesHiddenText" = "You will see the bar with pinned messages only if a new message is pinned.";
|
"Chat.PinnedMessagesHiddenText" = "You will see the bar with pinned messages only if a new message is pinned.";
|
||||||
|
|
||||||
|
"OpenFile.PotentiallyDangerousContentAlert" = "Previewing this file can potentially expose your IP address to its sender. Continue?";
|
||||||
|
16
submodules/AnimatedNavigationStripeNode/BUCK
Normal file
16
submodules/AnimatedNavigationStripeNode/BUCK
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
load("//Config:buck_rule_macros.bzl", "static_library")
|
||||||
|
|
||||||
|
static_library(
|
||||||
|
name = "AnimatedNavigationStripeNode",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/**/*.swift",
|
||||||
|
]),
|
||||||
|
deps = [
|
||||||
|
"//submodules/Display:Display#shared",
|
||||||
|
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
|
||||||
|
],
|
||||||
|
frameworks = [
|
||||||
|
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||||
|
"$SDKROOT/System/Library/Frameworks/UIKit.framework",
|
||||||
|
],
|
||||||
|
)
|
16
submodules/AnimatedNavigationStripeNode/BUILD
Normal file
16
submodules/AnimatedNavigationStripeNode/BUILD
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "AnimatedNavigationStripeNode",
|
||||||
|
module_name = "AnimatedNavigationStripeNode",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/**/*.swift",
|
||||||
|
]),
|
||||||
|
deps = [
|
||||||
|
"//submodules/Display:Display",
|
||||||
|
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
@ -0,0 +1,281 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import AsyncDisplayKit
|
||||||
|
|
||||||
|
public final class AnimatedNavigationStripeNode: ASDisplayNode {
|
||||||
|
public struct Colors: Equatable {
|
||||||
|
public var foreground: UIColor
|
||||||
|
public var background: UIColor
|
||||||
|
public var clearBackground: UIColor
|
||||||
|
|
||||||
|
public init(
|
||||||
|
foreground: UIColor,
|
||||||
|
background: UIColor,
|
||||||
|
clearBackground: UIColor
|
||||||
|
) {
|
||||||
|
self.foreground = foreground
|
||||||
|
self.background = background
|
||||||
|
self.clearBackground = clearBackground
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: Colors, rhs: Colors) -> Bool {
|
||||||
|
if !lhs.foreground.isEqual(rhs.foreground) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !lhs.background.isEqual(rhs.background) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !lhs.clearBackground.isEqual(rhs.clearBackground) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Configuration: Equatable {
|
||||||
|
public var height: CGFloat
|
||||||
|
public var index: Int
|
||||||
|
public var count: Int
|
||||||
|
|
||||||
|
public init(height: CGFloat, index: Int, count: Int) {
|
||||||
|
self.height = height
|
||||||
|
self.index = index
|
||||||
|
self.count = count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class BackgroundLineNode {
|
||||||
|
let lineNode: ASImageNode
|
||||||
|
let overlayNode: ASImageNode
|
||||||
|
|
||||||
|
init() {
|
||||||
|
self.lineNode = ASImageNode()
|
||||||
|
self.overlayNode = ASImageNode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var currentColors: Colors?
|
||||||
|
private var currentConfiguration: Configuration?
|
||||||
|
|
||||||
|
private let foregroundLineNode: ASImageNode
|
||||||
|
private var backgroundLineNodes: [Int: BackgroundLineNode] = [:]
|
||||||
|
private var removingBackgroundLineNodes: [BackgroundLineNode] = []
|
||||||
|
|
||||||
|
private let topShadowNode: ASImageNode
|
||||||
|
private let bottomShadowNode: ASImageNode
|
||||||
|
|
||||||
|
private var currentForegroundImage: UIImage?
|
||||||
|
private var currentBackgroundImage: UIImage?
|
||||||
|
private var currentClearBackgroundImage: UIImage?
|
||||||
|
|
||||||
|
override public init() {
|
||||||
|
self.foregroundLineNode = ASImageNode()
|
||||||
|
self.topShadowNode = ASImageNode()
|
||||||
|
self.bottomShadowNode = ASImageNode()
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.clipsToBounds = true
|
||||||
|
|
||||||
|
self.addSubnode(self.foregroundLineNode)
|
||||||
|
self.addSubnode(self.topShadowNode)
|
||||||
|
self.addSubnode(self.bottomShadowNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(colors: Colors, configuration: Configuration, transition: ContainedViewLayoutTransition) {
|
||||||
|
var transition = transition
|
||||||
|
|
||||||
|
let segmentSpacing: CGFloat = 2.0
|
||||||
|
|
||||||
|
if self.currentColors != colors {
|
||||||
|
self.currentColors = colors
|
||||||
|
self.currentForegroundImage = generateFilledCircleImage(diameter: 2.0, color: colors.foreground)?.resizableImage(withCapInsets: UIEdgeInsets(top: 1.0, left: 0.0, bottom: 1.0, right: 0.0), resizingMode: .stretch)
|
||||||
|
self.currentBackgroundImage = generateFilledCircleImage(diameter: 2.0, color: colors.background)?.resizableImage(withCapInsets: UIEdgeInsets(top: 1.0, left: 0.0, bottom: 1.0, right: 0.0), resizingMode: .stretch)
|
||||||
|
self.currentClearBackgroundImage = generateImage(CGSize(width: 2.0, height: 4.0 + segmentSpacing * 2.0 + 1.0 * 2.0), contextGenerator: { size, context in
|
||||||
|
context.setFillColor(colors.clearBackground.cgColor)
|
||||||
|
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
context.setFillColor(UIColor.clear.cgColor)
|
||||||
|
context.setBlendMode(.copy)
|
||||||
|
|
||||||
|
let ellipseFudge: CGFloat = 0.02
|
||||||
|
|
||||||
|
let topEllipse = CGRect(origin: CGPoint(x: -ellipseFudge, y: 1.0 + segmentSpacing), size: CGSize(width: 2.0 + ellipseFudge * 2.0, height: 2.0))
|
||||||
|
let bottomEllipse = CGRect(origin: CGPoint(x: -ellipseFudge, y: size.height - (1.0 + segmentSpacing) - 2.0), size: CGSize(width: 2.0 + ellipseFudge * 2.0, height: 2.0))
|
||||||
|
|
||||||
|
context.fillEllipse(in: topEllipse)
|
||||||
|
context.fillEllipse(in: bottomEllipse)
|
||||||
|
|
||||||
|
context.fill(CGRect(origin: CGPoint(x: 0.0, y: topEllipse.midY), size: CGSize(width: 2.0, height: bottomEllipse.midY - topEllipse.midY)))
|
||||||
|
|
||||||
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: -1.0), size: CGSize(width: 2.0, height: 2.0)))
|
||||||
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: size.height - 1.0), size: CGSize(width: 2.0, height: 2.0)))
|
||||||
|
})?.resizableImage(withCapInsets: UIEdgeInsets(top: 1.0 + segmentSpacing + 2.0, left: 1.0, bottom: 1.0 + segmentSpacing + 2.0, right: 1.0), resizingMode: .stretch)
|
||||||
|
|
||||||
|
self.foregroundLineNode.image = self.currentForegroundImage
|
||||||
|
for (_, itemNode) in self.backgroundLineNodes {
|
||||||
|
itemNode.lineNode.image = self.currentBackgroundImage
|
||||||
|
itemNode.overlayNode.image = self.currentClearBackgroundImage
|
||||||
|
}
|
||||||
|
|
||||||
|
self.topShadowNode.image = generateImage(CGSize(width: 2.0, height: 7.0), contextGenerator: { size, context in
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
var locations: [CGFloat] = [1.0, 0.0]
|
||||||
|
let colors: [CGColor] = [colors.clearBackground.cgColor, colors.clearBackground.withAlphaComponent(0.0).cgColor]
|
||||||
|
|
||||||
|
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||||
|
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
|
||||||
|
|
||||||
|
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
|
||||||
|
})
|
||||||
|
|
||||||
|
self.bottomShadowNode.image = generateImage(CGSize(width: 2.0, height: 7.0), rotatedContext: { size, context in
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
var locations: [CGFloat] = [1.0, 0.0]
|
||||||
|
let colors: [CGColor] = [colors.clearBackground.cgColor, colors.clearBackground.withAlphaComponent(0.0).cgColor]
|
||||||
|
|
||||||
|
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||||
|
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
|
||||||
|
|
||||||
|
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.currentConfiguration == nil {
|
||||||
|
transition = .immediate
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.currentConfiguration != configuration {
|
||||||
|
self.currentConfiguration = configuration
|
||||||
|
|
||||||
|
let defaultVerticalInset: CGFloat = 7.0
|
||||||
|
let minSegmentHeight: CGFloat = 8.0
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.topShadowNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: 2.0, height: defaultVerticalInset)))
|
||||||
|
transition.updateFrame(node: self.bottomShadowNode, frame: CGRect(origin: CGPoint(x: 0.0, y: configuration.height - defaultVerticalInset), size: CGSize(width: 2.0, height: defaultVerticalInset)))
|
||||||
|
|
||||||
|
let availableVerticalHeight: CGFloat = configuration.height - defaultVerticalInset * 2.0
|
||||||
|
|
||||||
|
let proposedSegmentHeight: CGFloat = (availableVerticalHeight - segmentSpacing * CGFloat(configuration.count) + segmentSpacing) / CGFloat(configuration.count)
|
||||||
|
let segmentHeight = max(proposedSegmentHeight, minSegmentHeight)
|
||||||
|
|
||||||
|
let allItemsHeight = CGFloat(configuration.count) * segmentHeight + max(0.0, CGFloat(configuration.count - 1)) * segmentSpacing
|
||||||
|
|
||||||
|
var verticalInset = defaultVerticalInset
|
||||||
|
if allItemsHeight > availableVerticalHeight && allItemsHeight - 2.0 <= availableVerticalHeight {
|
||||||
|
verticalInset -= 2.0
|
||||||
|
}
|
||||||
|
|
||||||
|
let topItemsHeight = CGFloat(configuration.index) * (segmentHeight + segmentSpacing)
|
||||||
|
let bottomItemsHeight = allItemsHeight - topItemsHeight - segmentHeight
|
||||||
|
|
||||||
|
var itemScreenOffset = floorToScreenPixels((configuration.height - segmentHeight) / 2.0)
|
||||||
|
|
||||||
|
if itemScreenOffset - topItemsHeight > verticalInset {
|
||||||
|
itemScreenOffset = topItemsHeight + verticalInset
|
||||||
|
}
|
||||||
|
if itemScreenOffset + segmentHeight + bottomItemsHeight < configuration.height - verticalInset {
|
||||||
|
itemScreenOffset = configuration.height - verticalInset - (segmentHeight + bottomItemsHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
var backgroundItemNodesToOffset: [BackgroundLineNode] = []
|
||||||
|
var resolvedOffset: CGFloat = 0.0
|
||||||
|
|
||||||
|
func updateBackgroundLine(index: Int) -> Bool {
|
||||||
|
let indexDifference = index - configuration.index
|
||||||
|
let offsetDistance = CGFloat(indexDifference) * (segmentHeight + segmentSpacing)
|
||||||
|
|
||||||
|
let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: itemScreenOffset + offsetDistance), size: CGSize(width: 2.0, height: segmentHeight))
|
||||||
|
|
||||||
|
if itemFrame.maxY <= 0.0 || itemFrame.minY > configuration.height {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var itemNodeTransition = transition
|
||||||
|
let itemNode: BackgroundLineNode
|
||||||
|
if let current = self.backgroundLineNodes[index] {
|
||||||
|
itemNode = current
|
||||||
|
let offset = itemFrame.minY - itemNode.lineNode.frame.minY
|
||||||
|
if abs(offset) > abs(resolvedOffset) {
|
||||||
|
resolvedOffset = offset
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
itemNodeTransition = .immediate
|
||||||
|
itemNode = BackgroundLineNode()
|
||||||
|
itemNode.lineNode.image = self.currentBackgroundImage
|
||||||
|
itemNode.overlayNode.image = self.currentClearBackgroundImage
|
||||||
|
self.backgroundLineNodes[index] = itemNode
|
||||||
|
self.insertSubnode(itemNode.lineNode, belowSubnode: self.foregroundLineNode)
|
||||||
|
self.insertSubnode(itemNode.overlayNode, belowSubnode: self.topShadowNode)
|
||||||
|
backgroundItemNodesToOffset.append(itemNode)
|
||||||
|
}
|
||||||
|
itemNodeTransition.updateFrame(node: itemNode.lineNode, frame: itemFrame, beginWithCurrentState: true)
|
||||||
|
itemNodeTransition.updateFrame(node: itemNode.overlayNode, frame: itemFrame.insetBy(dx: 0.0, dy: -(1.0 + segmentSpacing)), beginWithCurrentState: true)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var validIndices = Set<Int>()
|
||||||
|
if configuration.index >= 0 {
|
||||||
|
for i in (0 ... configuration.index).reversed() {
|
||||||
|
if updateBackgroundLine(index: i) {
|
||||||
|
validIndices.insert(i)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if configuration.index < configuration.count {
|
||||||
|
for i in configuration.index + 1 ..< configuration.count {
|
||||||
|
if updateBackgroundLine(index: i) {
|
||||||
|
validIndices.insert(i)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !resolvedOffset.isZero {
|
||||||
|
for itemNode in backgroundItemNodesToOffset {
|
||||||
|
transition.animatePositionAdditive(node: itemNode.lineNode, offset: CGPoint(x: 0.0, y: -resolvedOffset))
|
||||||
|
transition.animatePositionAdditive(node: itemNode.overlayNode, offset: CGPoint(x: 0.0, y: -resolvedOffset))
|
||||||
|
}
|
||||||
|
for itemNode in self.removingBackgroundLineNodes {
|
||||||
|
transition.animatePosition(node: itemNode.lineNode, to: CGPoint(x: 0.0, y: resolvedOffset), removeOnCompletion: false, additive: true)
|
||||||
|
transition.animatePosition(node: itemNode.overlayNode, to: CGPoint(x: 0.0, y: resolvedOffset), removeOnCompletion: false, additive: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var removeIndices: [Int] = []
|
||||||
|
for (index, itemNode) in self.backgroundLineNodes {
|
||||||
|
if !validIndices.contains(index) {
|
||||||
|
removeIndices.append(index)
|
||||||
|
|
||||||
|
if transition.isAnimated {
|
||||||
|
removingBackgroundLineNodes.append(itemNode)
|
||||||
|
transition.animatePosition(node: itemNode.overlayNode, to: CGPoint(x: 0.0, y: resolvedOffset), removeOnCompletion: false, additive: true)
|
||||||
|
transition.animatePosition(node: itemNode.lineNode, to: CGPoint(x: 0.0, y: resolvedOffset), removeOnCompletion: false, additive: true, completion: { [weak self, weak itemNode] _ in
|
||||||
|
guard let strongSelf = self, let itemNode = itemNode else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.removingBackgroundLineNodes.removeAll(where: { $0 === itemNode })
|
||||||
|
itemNode.lineNode.removeFromSupernode()
|
||||||
|
itemNode.overlayNode.removeFromSupernode()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
itemNode.lineNode.removeFromSupernode()
|
||||||
|
itemNode.overlayNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for index in removeIndices {
|
||||||
|
self.backgroundLineNodes.removeValue(forKey: index)
|
||||||
|
}
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.foregroundLineNode, frame: CGRect(origin: CGPoint(x: 0.0, y: itemScreenOffset), size: CGSize(width: 2.0, height: segmentHeight)), beginWithCurrentState: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -628,9 +628,11 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
|||||||
self.selectedLineNode.image = generateImage(CGSize(width: 5.0, height: 3.0), rotatedContext: { size, context in
|
self.selectedLineNode.image = generateImage(CGSize(width: 5.0, height: 3.0), rotatedContext: { size, context in
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
context.setFillColor(presentationData.theme.list.itemAccentColor.cgColor)
|
context.setFillColor(presentationData.theme.list.itemAccentColor.cgColor)
|
||||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + 1.0)))
|
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: 3.0, height: 3.0)))
|
||||||
|
context.fillEllipse(in: CGRect(origin: CGPoint(x: size.width - 3.0, y: 0.0), size: CGSize(width: 3.0, height: 3.0)))
|
||||||
|
context.fill(CGRect(x: 1.5, y: 0.0, width: size.width - 3.0, height: 3.0))
|
||||||
context.fill(CGRect(x: 0.0, y: 2.0, width: size.width, height: 2.0))
|
context.fill(CGRect(x: 0.0, y: 2.0, width: size.width, height: 2.0))
|
||||||
})?.stretchableImage(withLeftCapWidth: 2, topCapHeight: 2)
|
})?.resizableImage(withCapInsets: UIEdgeInsets(top: 3.0, left: 2.5, bottom: 0.0, right: 2.5), resizingMode: .stretch)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isReordering {
|
if isReordering {
|
||||||
|
@ -276,8 +276,8 @@ public extension ContainedViewLayoutTransition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func animatePosition(node: ASDisplayNode, to position: CGPoint, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
func animatePosition(node: ASDisplayNode, to position: CGPoint, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||||
if node.position.equalTo(position) {
|
if !additive && node.position.equalTo(position) {
|
||||||
completion?(true)
|
completion?(true)
|
||||||
} else {
|
} else {
|
||||||
switch self {
|
switch self {
|
||||||
@ -286,7 +286,7 @@ public extension ContainedViewLayoutTransition {
|
|||||||
completion(true)
|
completion(true)
|
||||||
}
|
}
|
||||||
case let .animated(duration, curve):
|
case let .animated(duration, curve):
|
||||||
node.layer.animatePosition(from: node.position, to: position, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, completion: { result in
|
node.layer.animatePosition(from: additive ? CGPoint() : node.position, to: position, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: additive, completion: { result in
|
||||||
if let completion = completion {
|
if let completion = completion {
|
||||||
completion(result)
|
completion(result)
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,14 @@ import AsyncDisplayKit
|
|||||||
open class ASImageNode: ASDisplayNode {
|
open class ASImageNode: ASDisplayNode {
|
||||||
public var image: UIImage? {
|
public var image: UIImage? {
|
||||||
didSet {
|
didSet {
|
||||||
|
if self.isNodeLoaded {
|
||||||
if let image = self.image {
|
if let image = self.image {
|
||||||
let capInsets = image.capInsets
|
let capInsets = image.capInsets
|
||||||
if capInsets.left.isZero && capInsets.top.isZero {
|
if capInsets.left.isZero && capInsets.top.isZero {
|
||||||
self.contentsScale = image.scale
|
self.contentsScale = image.scale
|
||||||
self.contents = image.cgImage
|
self.contents = image.cgImage
|
||||||
} else {
|
} else {
|
||||||
ASDisplayNodeSetResizableContents(self, image)
|
ASDisplayNodeSetResizableContents(self.layer, image)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.contents = nil
|
self.contents = nil
|
||||||
@ -21,6 +22,7 @@ open class ASImageNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public var displayWithoutProcessing: Bool = true
|
public var displayWithoutProcessing: Bool = true
|
||||||
|
|
||||||
@ -28,6 +30,20 @@ open class ASImageNode: ASDisplayNode {
|
|||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override open func didLoad() {
|
||||||
|
super.didLoad()
|
||||||
|
|
||||||
|
if let image = self.image {
|
||||||
|
let capInsets = image.capInsets
|
||||||
|
if capInsets.left.isZero && capInsets.top.isZero {
|
||||||
|
self.contentsScale = image.scale
|
||||||
|
self.contents = image.cgImage
|
||||||
|
} else {
|
||||||
|
ASDisplayNodeSetResizableContents(self.layer, image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override public func calculateSizeThatFits(_ contrainedSize: CGSize) -> CGSize {
|
override public func calculateSizeThatFits(_ contrainedSize: CGSize) -> CGSize {
|
||||||
return self.image?.size ?? CGSize()
|
return self.image?.size ?? CGSize()
|
||||||
}
|
}
|
||||||
|
@ -212,6 +212,7 @@ framework(
|
|||||||
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
|
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
|
||||||
"//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode",
|
"//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode",
|
||||||
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
|
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
|
||||||
|
"//submodules/AnimatedNavigationStripeNode:AnimatedNavigationStripeNode",
|
||||||
],
|
],
|
||||||
frameworks = [
|
frameworks = [
|
||||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||||
|
@ -209,6 +209,7 @@ swift_library(
|
|||||||
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
|
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
|
||||||
"//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode",
|
"//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode",
|
||||||
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
|
"//submodules/SlotMachineAnimationNode:SlotMachineAnimationNode",
|
||||||
|
"//submodules/AnimatedNavigationStripeNode:AnimatedNavigationStripeNode",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -4206,8 +4206,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
} else {
|
} else {
|
||||||
if case .known = strongSelf.chatDisplayNode.historyNode.visibleContentOffset() {
|
if case .known = strongSelf.chatDisplayNode.historyNode.visibleContentOffset() {
|
||||||
strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory()
|
strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory()
|
||||||
} else if case let .peer(peerId) = strongSelf.chatLocation {
|
} else if case .peer = strongSelf.chatLocation {
|
||||||
strongSelf.navigateToMessage(messageLocation: .upperBound(peerId), animated: true)
|
strongSelf.scrollToEndOfHistory()
|
||||||
} else if case .replyThread = strongSelf.chatLocation {
|
} else if case .replyThread = strongSelf.chatLocation {
|
||||||
strongSelf.scrollToEndOfHistory()
|
strongSelf.scrollToEndOfHistory()
|
||||||
} else {
|
} else {
|
||||||
@ -4751,8 +4751,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
})
|
})
|
||||||
strongSelf.updateItemNodesSearchTextHighlightStates()
|
strongSelf.updateItemNodesSearchTextHighlightStates()
|
||||||
}
|
}
|
||||||
}, navigateToMessage: { [weak self] messageId, dropStack in
|
}, navigateToMessage: { [weak self] messageId, dropStack, forceInCurrentChat in
|
||||||
self?.navigateToMessage(from: nil, to: .id(messageId), dropStack: dropStack)
|
self?.navigateToMessage(from: nil, to: .id(messageId), forceInCurrentChat: forceInCurrentChat, dropStack: dropStack)
|
||||||
}, navigateToChat: { [weak self] peerId in
|
}, navigateToChat: { [weak self] peerId in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -5294,6 +5294,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
if canManagePin {
|
if canManagePin {
|
||||||
let count = strongSelf.presentationInterfaceState.pinnedMessage?.totalCount ?? 1
|
let count = strongSelf.presentationInterfaceState.pinnedMessage?.totalCount ?? 1
|
||||||
|
|
||||||
|
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.Chat_UnpinAllMessagesConfirmation(Int32(count)), actions: [
|
||||||
|
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||||
|
}),
|
||||||
|
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Conversation_Unpin, action: {
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
let _ = (requestUnpinAllMessages(account: strongSelf.context.account, peerId: strongSelf.chatLocation.peerId)
|
let _ = (requestUnpinAllMessages(account: strongSelf.context.account, peerId: strongSelf.chatLocation.peerId)
|
||||||
|> deliverOnMainQueue).start(error: { _ in
|
|> deliverOnMainQueue).start(error: { _ in
|
||||||
|
|
||||||
@ -5304,6 +5311,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
strongSelf.dismiss()
|
strongSelf.dismiss()
|
||||||
strongSelf.updatedUnpinnedAllMessages?(count)
|
strongSelf.updatedUnpinnedAllMessages?(count)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
], parseMarkdown: true), in: .window(.root))
|
||||||
} else {
|
} else {
|
||||||
let topPinnedMessage: Signal<ChatPinnedMessage?, NoError> = strongSelf.topPinnedMessageSignal(latest: true)
|
let topPinnedMessage: Signal<ChatPinnedMessage?, NoError> = strongSelf.topPinnedMessageSignal(latest: true)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
@ -9337,7 +9346,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedForwardMessageIds(nil) }) })
|
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedForwardMessageIds(nil) }) })
|
||||||
}
|
}
|
||||||
|
|
||||||
if case .peer(peerId) = strongSelf.chatLocation, strongSelf.parentController == nil {
|
var isPinnedMessages = false
|
||||||
|
if case .pinnedMessages = strongSelf.presentationInterfaceState.subject {
|
||||||
|
isPinnedMessages = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if case .peer(peerId) = strongSelf.chatLocation, strongSelf.parentController == nil, !isPinnedMessages {
|
||||||
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedForwardMessageIds(messages.map { $0.id }).withoutSelectionState() }) })
|
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedForwardMessageIds(messages.map { $0.id }).withoutSelectionState() }) })
|
||||||
strongController.dismiss()
|
strongController.dismiss()
|
||||||
} else if peerId == strongSelf.context.account.peerId {
|
} else if peerId == strongSelf.context.account.peerId {
|
||||||
@ -9375,6 +9389,25 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) })
|
strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) })
|
||||||
strongController.dismiss()
|
strongController.dismiss()
|
||||||
} else {
|
} else {
|
||||||
|
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||||
|
for controller in navigationController.viewControllers {
|
||||||
|
if let maybeChat = controller as? ChatControllerImpl {
|
||||||
|
if case .peer(peerId) = maybeChat.chatLocation {
|
||||||
|
var isChatPinnedMessages = false
|
||||||
|
if case .pinnedMessages = maybeChat.presentationInterfaceState.subject {
|
||||||
|
isChatPinnedMessages = true
|
||||||
|
}
|
||||||
|
if !isChatPinnedMessages {
|
||||||
|
maybeChat.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedForwardMessageIds(messages.map { $0.id }).withoutSelectionState() }) })
|
||||||
|
strongSelf.dismiss()
|
||||||
|
strongController.dismiss()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let _ = (strongSelf.context.account.postbox.transaction({ transaction -> Void in
|
let _ = (strongSelf.context.account.postbox.transaction({ transaction -> Void in
|
||||||
transaction.updatePeerChatInterfaceState(peerId, update: { currentState in
|
transaction.updatePeerChatInterfaceState(peerId, update: { currentState in
|
||||||
if let currentState = currentState as? ChatInterfaceState {
|
if let currentState = currentState as? ChatInterfaceState {
|
||||||
|
@ -424,7 +424,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
refineContentImageLayout = refineLayout
|
refineContentImageLayout = refineLayout
|
||||||
} else if file.isInstantVideo {
|
} else if file.isInstantVideo {
|
||||||
let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file)
|
let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file)
|
||||||
let (videoLayout, apply) = contentInstantVideoLayout(ChatMessageBubbleContentItem(context: context, controllerInteraction: controllerInteraction, message: message, read: messageRead, chatLocation: chatLocation, presentationData: presentationData, associatedData: associatedData, attributes: attributes), constrainedSize.width - horizontalInsets.left - horizontalInsets.right, CGSize(width: 212.0, height: 212.0), .bubble, automaticDownload)
|
let (videoLayout, apply) = contentInstantVideoLayout(ChatMessageBubbleContentItem(context: context, controllerInteraction: controllerInteraction, message: message, read: messageRead, chatLocation: chatLocation, presentationData: presentationData, associatedData: associatedData, attributes: attributes, isItemPinned: message.tags.contains(.pinned)), constrainedSize.width - horizontalInsets.left - horizontalInsets.right, CGSize(width: 212.0, height: 212.0), .bubble, automaticDownload)
|
||||||
initialWidth = videoLayout.contentSize.width + videoLayout.overflowLeft + videoLayout.overflowRight
|
initialWidth = videoLayout.contentSize.width + videoLayout.overflowLeft + videoLayout.overflowRight
|
||||||
contentInstantVideoSizeAndApply = (videoLayout, apply)
|
contentInstantVideoSizeAndApply = (videoLayout, apply)
|
||||||
} else if file.isVideo {
|
} else if file.isVideo {
|
||||||
@ -474,7 +474,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_, refineLayout) = contentFileLayout(context, presentationData, message, associatedData, chatLocation, attributes, file, automaticDownload, message.effectivelyIncoming(context.account.peerId), false, associatedData.forcedResourceStatus, statusType, nil, CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height))
|
let (_, refineLayout) = contentFileLayout(context, presentationData, message, associatedData, chatLocation, attributes, message.tags.contains(.pinned) && !associatedData.isInPinnedListMode, file, automaticDownload, message.effectivelyIncoming(context.account.peerId), false, associatedData.forcedResourceStatus, statusType, nil, CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height))
|
||||||
refineContentFileLayout = refineLayout
|
refineContentFileLayout = refineLayout
|
||||||
}
|
}
|
||||||
} else if let image = media as? TelegramMediaImage {
|
} else if let image = media as? TelegramMediaImage {
|
||||||
|
@ -108,8 +108,9 @@ final class ChatMessageBubbleContentItem {
|
|||||||
let presentationData: ChatPresentationData
|
let presentationData: ChatPresentationData
|
||||||
let associatedData: ChatMessageItemAssociatedData
|
let associatedData: ChatMessageItemAssociatedData
|
||||||
let attributes: ChatMessageEntryAttributes
|
let attributes: ChatMessageEntryAttributes
|
||||||
|
let isItemPinned: Bool
|
||||||
|
|
||||||
init(context: AccountContext, controllerInteraction: ChatControllerInteraction, message: Message, read: Bool, chatLocation: ChatLocation, presentationData: ChatPresentationData, associatedData: ChatMessageItemAssociatedData, attributes: ChatMessageEntryAttributes) {
|
init(context: AccountContext, controllerInteraction: ChatControllerInteraction, message: Message, read: Bool, chatLocation: ChatLocation, presentationData: ChatPresentationData, associatedData: ChatMessageItemAssociatedData, attributes: ChatMessageEntryAttributes, isItemPinned: Bool) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.controllerInteraction = controllerInteraction
|
self.controllerInteraction = controllerInteraction
|
||||||
self.message = message
|
self.message = message
|
||||||
@ -118,6 +119,7 @@ final class ChatMessageBubbleContentItem {
|
|||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.associatedData = associatedData
|
self.associatedData = associatedData
|
||||||
self.attributes = attributes
|
self.attributes = attributes
|
||||||
|
self.isItemPinned = isItemPinned
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1208,11 +1208,19 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
let contentNodeCount = contentPropertiesAndPrepareLayouts.count
|
let contentNodeCount = contentPropertiesAndPrepareLayouts.count
|
||||||
|
|
||||||
let read: Bool
|
let read: Bool
|
||||||
|
var isItemPinned = false
|
||||||
|
|
||||||
switch item.content {
|
switch item.content {
|
||||||
case let .message(_, value, _, _):
|
case let .message(message, value, _, _):
|
||||||
read = value
|
read = value
|
||||||
|
isItemPinned = message.tags.contains(.pinned)
|
||||||
case let .group(messages):
|
case let .group(messages):
|
||||||
read = messages[0].1
|
read = messages[0].1
|
||||||
|
for message in messages {
|
||||||
|
if message.0.tags.contains(.pinned) {
|
||||||
|
isItemPinned = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var mosaicStartIndex: Int?
|
var mosaicStartIndex: Int?
|
||||||
@ -1267,7 +1275,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
prepareContentPosition = .linear(top: topPosition, bottom: refinedBottomPosition)
|
prepareContentPosition = .linear(top: topPosition, bottom: refinedBottomPosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
let contentItem = ChatMessageBubbleContentItem(context: item.context, controllerInteraction: item.controllerInteraction, message: message, read: read, chatLocation: item.chatLocation, presentationData: item.presentationData, associatedData: item.associatedData, attributes: attributes)
|
let contentItem = ChatMessageBubbleContentItem(context: item.context, controllerInteraction: item.controllerInteraction, message: message, read: read, chatLocation: item.chatLocation, presentationData: item.presentationData, associatedData: item.associatedData, attributes: attributes, isItemPinned: isItemPinned)
|
||||||
|
|
||||||
var itemSelection: Bool?
|
var itemSelection: Bool?
|
||||||
switch content {
|
switch content {
|
||||||
|
@ -89,7 +89,7 @@ class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
|
|
||||||
let automaticDownload = shouldDownloadMediaAutomatically(settings: item.controllerInteraction.automaticMediaDownloadSettings, peerType: item.associatedData.automaticDownloadPeerType, networkType: item.associatedData.automaticDownloadNetworkType, authorPeerId: item.message.author?.id, contactsPeerIds: item.associatedData.contactsPeerIds, media: selectedFile!)
|
let automaticDownload = shouldDownloadMediaAutomatically(settings: item.controllerInteraction.automaticMediaDownloadSettings, peerType: item.associatedData.automaticDownloadPeerType, networkType: item.associatedData.automaticDownloadNetworkType, authorPeerId: item.message.author?.id, contactsPeerIds: item.associatedData.contactsPeerIds, media: selectedFile!)
|
||||||
|
|
||||||
let (initialWidth, refineLayout) = interactiveFileLayout(item.context, item.presentationData, item.message, item.associatedData, item.chatLocation, item.attributes, selectedFile!, automaticDownload, item.message.effectivelyIncoming(item.context.account.peerId), item.associatedData.isRecentActions, item.associatedData.forcedResourceStatus, statusType, item.message.groupingKey != nil ? selection : nil, CGSize(width: constrainedSize.width - layoutConstants.file.bubbleInsets.left - layoutConstants.file.bubbleInsets.right, height: constrainedSize.height))
|
let (initialWidth, refineLayout) = interactiveFileLayout(item.context, item.presentationData, item.message, item.associatedData, item.chatLocation, item.attributes, item.isItemPinned, selectedFile!, automaticDownload, item.message.effectivelyIncoming(item.context.account.peerId), item.associatedData.isRecentActions, item.associatedData.forcedResourceStatus, statusType, item.message.groupingKey != nil ? selection : nil, CGSize(width: constrainedSize.width - layoutConstants.file.bubbleInsets.left - layoutConstants.file.bubbleInsets.right, height: constrainedSize.height))
|
||||||
|
|
||||||
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)
|
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)
|
||||||
|
|
||||||
|
@ -276,7 +276,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (videoLayout, videoApply) = makeVideoLayout(ChatMessageBubbleContentItem(context: item.context, controllerInteraction: item.controllerInteraction, message: item.message, read: item.read, chatLocation: item.chatLocation, presentationData: item.presentationData, associatedData: item.associatedData, attributes: item.content.firstMessageAttributes), params.width - params.leftInset - params.rightInset - avatarInset, displaySize, .free, automaticDownload)
|
let (videoLayout, videoApply) = makeVideoLayout(ChatMessageBubbleContentItem(context: item.context, controllerInteraction: item.controllerInteraction, message: item.message, read: item.read, chatLocation: item.chatLocation, presentationData: item.presentationData, associatedData: item.associatedData, attributes: item.content.firstMessageAttributes, isItemPinned: item.message.tags.contains(.pinned)), params.width - params.leftInset - params.rightInset - avatarInset, displaySize, .free, automaticDownload)
|
||||||
|
|
||||||
let videoFrame = CGRect(origin: CGPoint(x: (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - videoLayout.contentSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - deliveryFailedInset)), y: 0.0), size: videoLayout.contentSize)
|
let videoFrame = CGRect(origin: CGPoint(x: (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - videoLayout.contentSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - deliveryFailedInset)), y: 0.0), size: videoLayout.contentSize)
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ associatedData: ChatMessageItemAssociatedData, _ chatLocation: ChatLocation, _ attributes: ChatMessageEntryAttributes, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ forcedResourceStatus: FileMediaResourceStatus?, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> Void))) {
|
func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ associatedData: ChatMessageItemAssociatedData, _ chatLocation: ChatLocation, _ attributes: ChatMessageEntryAttributes, _ isPinned: Bool, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ forcedResourceStatus: FileMediaResourceStatus?, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> Void))) {
|
||||||
let currentFile = self.file
|
let currentFile = self.file
|
||||||
|
|
||||||
let titleAsyncLayout = TextNode.asyncLayout(self.titleNode)
|
let titleAsyncLayout = TextNode.asyncLayout(self.titleNode)
|
||||||
@ -218,7 +218,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
|
|
||||||
let currentMessage = self.message
|
let currentMessage = self.message
|
||||||
|
|
||||||
return { context, presentationData, message, associatedData, chatLocation, attributes, file, automaticDownload, incoming, isRecentActions, forcedResourceStatus, dateAndStatusType, messageSelection, constrainedSize in
|
return { context, presentationData, message, associatedData, chatLocation, attributes, isPinned, file, automaticDownload, incoming, isRecentActions, forcedResourceStatus, dateAndStatusType, messageSelection, constrainedSize in
|
||||||
return (CGFloat.greatestFiniteMagnitude, { constrainedSize in
|
return (CGFloat.greatestFiniteMagnitude, { constrainedSize in
|
||||||
let titleFont = Font.regular(floor(presentationData.fontSize.baseDisplaySize * 16.0 / 17.0))
|
let titleFont = Font.regular(floor(presentationData.fontSize.baseDisplaySize * 16.0 / 17.0))
|
||||||
let descriptionFont = Font.regular(floor(presentationData.fontSize.baseDisplaySize * 13.0 / 17.0))
|
let descriptionFont = Font.regular(floor(presentationData.fontSize.baseDisplaySize * 13.0 / 17.0))
|
||||||
@ -327,7 +327,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
|
|
||||||
let dateText = stringForMessageTimestampStatus(accountPeerId: context.account.peerId, message: message, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, strings: presentationData.strings, reactionCount: dateReactionCount)
|
let dateText = stringForMessageTimestampStatus(accountPeerId: context.account.peerId, message: message, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, strings: presentationData.strings, reactionCount: dateReactionCount)
|
||||||
|
|
||||||
let (size, apply) = statusLayout(context, presentationData, edited, viewCount, dateText, statusType, constrainedSize, dateReactions, dateReplies, message.tags.contains(.pinned) && !associatedData.isInPinnedListMode)
|
let (size, apply) = statusLayout(context, presentationData, edited, viewCount, dateText, statusType, constrainedSize, dateReactions, dateReplies, isPinned && !associatedData.isInPinnedListMode)
|
||||||
statusSize = size
|
statusSize = size
|
||||||
statusApply = apply
|
statusApply = apply
|
||||||
}
|
}
|
||||||
@ -1032,12 +1032,12 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
self.fetchingCompactTextNode.frame = CGRect(origin: self.descriptionNode.frame.origin, size: fetchingCompactSize)
|
self.fetchingCompactTextNode.frame = CGRect(origin: self.descriptionNode.frame.origin, size: fetchingCompactSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func asyncLayout(_ node: ChatMessageInteractiveFileNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ associatedData: ChatMessageItemAssociatedData, _ chatLocation: ChatLocation, _ attributes: ChatMessageEntryAttributes, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ forcedResourceStatus: FileMediaResourceStatus?, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> ChatMessageInteractiveFileNode))) {
|
static func asyncLayout(_ node: ChatMessageInteractiveFileNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ associatedData: ChatMessageItemAssociatedData, _ chatLocation: ChatLocation, _ attributes: ChatMessageEntryAttributes, _ isPinned: Bool, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ forcedResourceStatus: FileMediaResourceStatus?, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> ChatMessageInteractiveFileNode))) {
|
||||||
let currentAsyncLayout = node?.asyncLayout()
|
let currentAsyncLayout = node?.asyncLayout()
|
||||||
|
|
||||||
return { context, presentationData, message, associatedData, chatLocation, attributes, file, automaticDownload, incoming, isRecentActions, forcedResourceStatus, dateAndStatusType, messageSelection, constrainedSize in
|
return { context, presentationData, message, associatedData, chatLocation, attributes, isPinned, file, automaticDownload, incoming, isRecentActions, forcedResourceStatus, dateAndStatusType, messageSelection, constrainedSize in
|
||||||
var fileNode: ChatMessageInteractiveFileNode
|
var fileNode: ChatMessageInteractiveFileNode
|
||||||
var fileLayout: (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ associatedData: ChatMessageItemAssociatedData, _ chatLocation: ChatLocation, _ attributes: ChatMessageEntryAttributes, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ forcedResourceStatus: FileMediaResourceStatus?, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> Void)))
|
var fileLayout: (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ associatedData: ChatMessageItemAssociatedData, _ chatLocation: ChatLocation, _ attributes: ChatMessageEntryAttributes, _ isPinned: Bool, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ forcedResourceStatus: FileMediaResourceStatus?, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, (Bool) -> Void)))
|
||||||
|
|
||||||
if let node = node, let currentAsyncLayout = currentAsyncLayout {
|
if let node = node, let currentAsyncLayout = currentAsyncLayout {
|
||||||
fileNode = node
|
fileNode = node
|
||||||
@ -1047,7 +1047,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
fileLayout = fileNode.asyncLayout()
|
fileLayout = fileNode.asyncLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
let (initialWidth, continueLayout) = fileLayout(context, presentationData, message, associatedData, chatLocation, attributes, file, automaticDownload, incoming, isRecentActions, forcedResourceStatus, dateAndStatusType, messageSelection, constrainedSize)
|
let (initialWidth, continueLayout) = fileLayout(context, presentationData, message, associatedData, chatLocation, attributes, isPinned, file, automaticDownload, incoming, isRecentActions, forcedResourceStatus, dateAndStatusType, messageSelection, constrainedSize)
|
||||||
|
|
||||||
return (initialWidth, { constrainedSize in
|
return (initialWidth, { constrainedSize in
|
||||||
let (finalWidth, finalLayout) = continueLayout(constrainedSize)
|
let (finalWidth, finalLayout) = continueLayout(constrainedSize)
|
||||||
|
@ -73,7 +73,7 @@ final class ChatPanelInterfaceInteraction {
|
|||||||
let openSearchResults: () -> Void
|
let openSearchResults: () -> Void
|
||||||
let openCalendarSearch: () -> Void
|
let openCalendarSearch: () -> Void
|
||||||
let toggleMembersSearch: (Bool) -> Void
|
let toggleMembersSearch: (Bool) -> Void
|
||||||
let navigateToMessage: (MessageId, Bool) -> Void
|
let navigateToMessage: (MessageId, Bool, Bool) -> Void
|
||||||
let navigateToChat: (PeerId) -> Void
|
let navigateToChat: (PeerId) -> Void
|
||||||
let navigateToProfile: (PeerId) -> Void
|
let navigateToProfile: (PeerId) -> Void
|
||||||
let openPeerInfo: () -> Void
|
let openPeerInfo: () -> Void
|
||||||
@ -151,7 +151,7 @@ final class ChatPanelInterfaceInteraction {
|
|||||||
navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void,
|
navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void,
|
||||||
openCalendarSearch: @escaping () -> Void,
|
openCalendarSearch: @escaping () -> Void,
|
||||||
toggleMembersSearch: @escaping (Bool) -> Void,
|
toggleMembersSearch: @escaping (Bool) -> Void,
|
||||||
navigateToMessage: @escaping (MessageId, Bool) -> Void,
|
navigateToMessage: @escaping (MessageId, Bool, Bool) -> Void,
|
||||||
navigateToChat: @escaping (PeerId) -> Void,
|
navigateToChat: @escaping (PeerId) -> Void,
|
||||||
navigateToProfile: @escaping (PeerId) -> Void,
|
navigateToProfile: @escaping (PeerId) -> Void,
|
||||||
openPeerInfo: @escaping () -> Void,
|
openPeerInfo: @escaping () -> Void,
|
||||||
|
@ -13,6 +13,7 @@ import StickerResources
|
|||||||
import PhotoResources
|
import PhotoResources
|
||||||
import TelegramStringFormatting
|
import TelegramStringFormatting
|
||||||
import AnimatedCountLabelNode
|
import AnimatedCountLabelNode
|
||||||
|
import AnimatedNavigationStripeNode
|
||||||
|
|
||||||
private enum PinnedMessageAnimation {
|
private enum PinnedMessageAnimation {
|
||||||
case slideToTop
|
case slideToTop
|
||||||
@ -28,7 +29,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
private let clippingContainer: ASDisplayNode
|
private let clippingContainer: ASDisplayNode
|
||||||
private let contentContainer: ASDisplayNode
|
private let contentContainer: ASDisplayNode
|
||||||
private let contentTextContainer: ASDisplayNode
|
private let contentTextContainer: ASDisplayNode
|
||||||
private let lineNode: ASImageNode
|
private let lineNode: AnimatedNavigationStripeNode
|
||||||
private let titleNode: AnimatedCountLabelNode
|
private let titleNode: AnimatedCountLabelNode
|
||||||
private let textNode: TextNode
|
private let textNode: TextNode
|
||||||
|
|
||||||
@ -69,9 +70,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
self.contentContainer = ASDisplayNode()
|
self.contentContainer = ASDisplayNode()
|
||||||
self.contentTextContainer = ASDisplayNode()
|
self.contentTextContainer = ASDisplayNode()
|
||||||
|
|
||||||
self.lineNode = ASImageNode()
|
self.lineNode = AnimatedNavigationStripeNode()
|
||||||
self.lineNode.displayWithoutProcessing = true
|
|
||||||
self.lineNode.displaysAsynchronously = false
|
|
||||||
|
|
||||||
self.titleNode = AnimatedCountLabelNode()
|
self.titleNode = AnimatedCountLabelNode()
|
||||||
self.titleNode.isUserInteractionEnabled = false
|
self.titleNode.isUserInteractionEnabled = false
|
||||||
@ -145,7 +144,6 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
self.theme = interfaceState.theme
|
self.theme = interfaceState.theme
|
||||||
self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(interfaceState.theme), for: [])
|
self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(interfaceState.theme), for: [])
|
||||||
self.listButton.setImage(PresentationResourcesChat.chatInputPanelPinnedListIconImage(interfaceState.theme), for: [])
|
self.listButton.setImage(PresentationResourcesChat.chatInputPanelPinnedListIconImage(interfaceState.theme), for: [])
|
||||||
self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(interfaceState.theme)
|
|
||||||
self.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
|
self.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
|
||||||
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
|
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
|
||||||
}
|
}
|
||||||
@ -180,7 +178,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
self.currentMessage = interfaceState.pinnedMessage
|
self.currentMessage = interfaceState.pinnedMessage
|
||||||
|
|
||||||
if let currentMessage = self.currentMessage, let currentLayout = self.currentLayout {
|
if let currentMessage = self.currentMessage, let currentLayout = self.currentLayout {
|
||||||
self.enqueueTransition(width: currentLayout.0, leftInset: currentLayout.1, rightInset: currentLayout.2, transition: .immediate, animation: messageUpdatedAnimation, pinnedMessage: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, accountPeerId: self.context.account.peerId, firstTime: previousMessageWasNil, isReplyThread: isReplyThread)
|
self.enqueueTransition(width: currentLayout.0, panelHeight: panelHeight, leftInset: currentLayout.1, rightInset: currentLayout.2, transition: .immediate, animation: messageUpdatedAnimation, pinnedMessage: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, accountPeerId: self.context.account.peerId, firstTime: previousMessageWasNil, isReplyThread: isReplyThread)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,11 +198,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
self.closeButton.isHidden = true
|
self.closeButton.isHidden = true
|
||||||
}
|
}
|
||||||
|
|
||||||
let contentLeftInset: CGFloat = 10.0 + leftInset
|
|
||||||
let rightInset: CGFloat = 18.0 + rightInset
|
let rightInset: CGFloat = 18.0 + rightInset
|
||||||
|
|
||||||
transition.updateFrame(node: self.lineNode, frame: CGRect(origin: CGPoint(x: contentLeftInset, y: 7.0), size: CGSize(width: 2.0, height: panelHeight - 14.0)))
|
|
||||||
|
|
||||||
let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0))
|
let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0))
|
||||||
transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: width - rightInset - closeButtonSize.width, y: 19.0), size: closeButtonSize))
|
transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: width - rightInset - closeButtonSize.width, y: 19.0), size: closeButtonSize))
|
||||||
|
|
||||||
@ -221,14 +216,14 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
self.currentLayout = (width, leftInset, rightInset)
|
self.currentLayout = (width, leftInset, rightInset)
|
||||||
|
|
||||||
if let currentMessage = self.currentMessage {
|
if let currentMessage = self.currentMessage {
|
||||||
self.enqueueTransition(width: width, leftInset: leftInset, rightInset: rightInset, transition: .immediate, animation: .none, pinnedMessage: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, accountPeerId: interfaceState.accountPeerId, firstTime: true, isReplyThread: isReplyThread)
|
self.enqueueTransition(width: width, panelHeight: panelHeight, leftInset: leftInset, rightInset: rightInset, transition: .immediate, animation: .none, pinnedMessage: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, accountPeerId: interfaceState.accountPeerId, firstTime: true, isReplyThread: isReplyThread)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return panelHeight
|
return panelHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
private func enqueueTransition(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, animation: PinnedMessageAnimation?, pinnedMessage: ChatPinnedMessage, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, accountPeerId: PeerId, firstTime: Bool, isReplyThread: Bool) {
|
private func enqueueTransition(width: CGFloat, panelHeight: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, animation: PinnedMessageAnimation?, pinnedMessage: ChatPinnedMessage, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, accountPeerId: PeerId, firstTime: Bool, isReplyThread: Bool) {
|
||||||
let message = pinnedMessage.message
|
let message = pinnedMessage.message
|
||||||
|
|
||||||
var animationTransition: ContainedViewLayoutTransition = .immediate
|
var animationTransition: ContainedViewLayoutTransition = .immediate
|
||||||
@ -390,6 +385,22 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 5.0), size: titleLayout.size)
|
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 5.0), size: titleLayout.size)
|
||||||
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 23.0), size: textLayout.size)
|
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 23.0), size: textLayout.size)
|
||||||
|
|
||||||
|
let lineFrame = CGRect(origin: CGPoint(x: contentLeftInset, y: 0.0), size: CGSize(width: 2.0, height: panelHeight))
|
||||||
|
animationTransition.updateFrame(node: strongSelf.lineNode, frame: lineFrame)
|
||||||
|
strongSelf.lineNode.update(
|
||||||
|
colors: AnimatedNavigationStripeNode.Colors(
|
||||||
|
foreground: theme.chat.inputPanel.panelControlAccentColor,
|
||||||
|
background: theme.chat.inputPanel.panelControlAccentColor.withAlphaComponent(0.5),
|
||||||
|
clearBackground: theme.chat.inputPanel.panelBackgroundColor
|
||||||
|
),
|
||||||
|
configuration: AnimatedNavigationStripeNode.Configuration(
|
||||||
|
height: panelHeight,
|
||||||
|
index: pinnedMessage.index,
|
||||||
|
count: pinnedMessage.totalCount
|
||||||
|
),
|
||||||
|
transition: animationTransition
|
||||||
|
)
|
||||||
|
|
||||||
strongSelf.imageNodeContainer.frame = CGRect(origin: CGPoint(x: contentLeftInset + 9.0, y: 7.0), size: CGSize(width: 35.0, height: 35.0))
|
strongSelf.imageNodeContainer.frame = CGRect(origin: CGPoint(x: contentLeftInset + 9.0, y: 7.0), size: CGSize(width: 35.0, height: 35.0))
|
||||||
strongSelf.imageNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 35.0, height: 35.0))
|
strongSelf.imageNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 35.0, height: 35.0))
|
||||||
|
|
||||||
@ -419,7 +430,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
if self.isReplyThread {
|
if self.isReplyThread {
|
||||||
interfaceInteraction.scrollToTop()
|
interfaceInteraction.scrollToTop()
|
||||||
} else {
|
} else {
|
||||||
interfaceInteraction.navigateToMessage(message.message.id, false)
|
interfaceInteraction.navigateToMessage(message.message.id, false, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ final class ChatRecentActionsController: TelegramBaseController {
|
|||||||
}, navigateMessageSearch: { _ in
|
}, navigateMessageSearch: { _ in
|
||||||
}, openCalendarSearch: {
|
}, openCalendarSearch: {
|
||||||
}, toggleMembersSearch: { _ in
|
}, toggleMembersSearch: { _ in
|
||||||
}, navigateToMessage: { _, _ in
|
}, navigateToMessage: { _, _, _ in
|
||||||
}, navigateToChat: { _ in
|
}, navigateToChat: { _ in
|
||||||
}, navigateToProfile: { _ in
|
}, navigateToProfile: { _ in
|
||||||
}, openPeerInfo: {
|
}, openPeerInfo: {
|
||||||
|
@ -347,7 +347,7 @@ final class EditAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
|
|
||||||
@objc func contentTap(_ recognizer: UITapGestureRecognizer) {
|
@objc func contentTap(_ recognizer: UITapGestureRecognizer) {
|
||||||
if case .ended = recognizer.state, let message = self.currentMessage {
|
if case .ended = recognizer.state, let message = self.currentMessage {
|
||||||
self.interfaceInteraction?.navigateToMessage(message.id, false)
|
self.interfaceInteraction?.navigateToMessage(message.id, false, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -382,7 +382,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
|
|||||||
}, navigateMessageSearch: { _ in
|
}, navigateMessageSearch: { _ in
|
||||||
}, openCalendarSearch: {
|
}, openCalendarSearch: {
|
||||||
}, toggleMembersSearch: { _ in
|
}, toggleMembersSearch: { _ in
|
||||||
}, navigateToMessage: { _, _ in
|
}, navigateToMessage: { _, _, _ in
|
||||||
}, navigateToChat: { _ in
|
}, navigateToChat: { _ in
|
||||||
}, navigateToProfile: { _ in
|
}, navigateToProfile: { _ in
|
||||||
}, openPeerInfo: {
|
}, openPeerInfo: {
|
||||||
|
@ -256,7 +256,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
|
|
||||||
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||||
if case .ended = recognizer.state {
|
if case .ended = recognizer.state {
|
||||||
self.interfaceInteraction?.navigateToMessage(self.messageId, false)
|
self.interfaceInteraction?.navigateToMessage(self.messageId, false, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,7 +218,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
displayUndo = undo
|
displayUndo = undo
|
||||||
self.originalRemainingSeconds = undo ? 5 : 3
|
self.originalRemainingSeconds = undo ? 5 : 5
|
||||||
case let .emoji(path, text):
|
case let .emoji(path, text):
|
||||||
self.iconNode = nil
|
self.iconNode = nil
|
||||||
self.iconCheckNode = nil
|
self.iconCheckNode = nil
|
||||||
|
Loading…
x
Reference in New Issue
Block a user