mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
Bot forums
This commit is contained in:
@@ -113,6 +113,35 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
private var appliedExpandedBlockIds: Set<Int>?
|
||||
private var displayContentsUnderSpoilers: (value: Bool, location: CGPoint?) = (false, nil)
|
||||
|
||||
private final class TextRevealAnimationState {
|
||||
let fromCount: Int
|
||||
let toCount: Int
|
||||
let startTimestamp: Double
|
||||
let duration: Double
|
||||
|
||||
init(fromCount: Int, toCount: Int, startTimestamp: Double, duration: Double) {
|
||||
self.fromCount = fromCount
|
||||
self.toCount = toCount
|
||||
self.startTimestamp = startTimestamp
|
||||
self.duration = duration
|
||||
}
|
||||
|
||||
func fraction(timestamp: Double) -> CGFloat {
|
||||
var animationFraction = (timestamp - self.startTimestamp) / self.duration
|
||||
animationFraction = max(0.0, min(1.0, animationFraction))
|
||||
return animationFraction
|
||||
}
|
||||
|
||||
func glyphCount(timestamp: Double) -> Int {
|
||||
let animationFraction = self.fraction(timestamp: timestamp)
|
||||
let glyphCount = (1.0 - animationFraction) * Double(self.fromCount) + animationFraction * Double(self.toCount)
|
||||
return Int(glyphCount)
|
||||
}
|
||||
}
|
||||
|
||||
private var textRevealLink: SharedDisplayLinkDriver.Link?
|
||||
private var textRevealAnimationState: TextRevealAnimationState?
|
||||
|
||||
override public var visibility: ListViewItemNodeVisibility {
|
||||
didSet {
|
||||
if oldValue != self.visibility {
|
||||
@@ -131,6 +160,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
required public init() {
|
||||
self.containerNode = ASDisplayNode()
|
||||
self.containerNode.clipsToBounds = true
|
||||
|
||||
self.textNode = InteractiveTextNodeWithEntities()
|
||||
|
||||
@@ -200,12 +230,22 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
|
||||
override public func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, CGSize?, CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) {
|
||||
let previousItem = self.item
|
||||
|
||||
let textLayout = InteractiveTextNodeWithEntities.asyncLayout(self.textNode)
|
||||
let statusLayout = ChatMessageDateAndStatusNode.asyncLayout(self.statusNode)
|
||||
|
||||
let currentCachedChatMessageText = self.cachedChatMessageText
|
||||
let expandedBlockIds = self.expandedBlockIds
|
||||
let displayContentsUnderSpoilers = self.displayContentsUnderSpoilers
|
||||
let currentMaxGlyphCount: Int?
|
||||
if let textRevealAnimationState = self.textRevealAnimationState {
|
||||
currentMaxGlyphCount = textRevealAnimationState.glyphCount(timestamp: CACurrentMediaTime())
|
||||
//print("currentMaxGlyphCount(\(textRevealAnimationState.fromCount) -> \(textRevealAnimationState.toCount)) fraction: \(textRevealAnimationState.fraction(timestamp: CACurrentMediaTime()))")
|
||||
} else {
|
||||
currentMaxGlyphCount = nil
|
||||
}
|
||||
let previousGlyphCount = self.textNode.textNode.getGlyphCount()
|
||||
|
||||
return { item, layoutConstants, _, _, _, _ in
|
||||
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)
|
||||
@@ -673,9 +713,32 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
|
||||
var textFrame = CGRect(origin: CGPoint(x: -textInsets.left, y: -textInsets.top), size: textLayout.size)
|
||||
|
||||
var textFrameWithoutInsets = CGRect(origin: CGPoint(x: textFrame.origin.x + textInsets.left, y: textFrame.origin.y + textInsets.top), size: CGSize(width: textFrame.width - textInsets.left - textInsets.right, height: textFrame.height - textInsets.top - textInsets.bottom))
|
||||
|
||||
textFrame = textFrame.offsetBy(dx: layoutConstants.text.bubbleInsets.left, dy: topInset)
|
||||
let realTextFrame = textFrame
|
||||
|
||||
var hasDraft = false
|
||||
if item.message.attributes.contains(where: { $0 is TypingDraftMessageAttribute }) {
|
||||
hasDraft = true
|
||||
}
|
||||
var hadDraft = false
|
||||
if let previousItem, previousItem.message.attributes.contains(where: { $0 is TypingDraftMessageAttribute }) {
|
||||
hadDraft = true
|
||||
}
|
||||
|
||||
var maxGlyphCount = currentMaxGlyphCount
|
||||
if maxGlyphCount == nil && (hasDraft || hadDraft) {
|
||||
maxGlyphCount = previousGlyphCount
|
||||
}
|
||||
|
||||
if let maxGlyphCount {
|
||||
textFrame.size = textLayout.sizeForGlyphCount(glyphCount: maxGlyphCount)
|
||||
//print("currentMaxGlyphCount: \(currentMaxGlyphCount), size: \(textFrame.size.height)")
|
||||
textFrameWithoutInsets.size = CGSize(width: textFrame.width - textInsets.left - textInsets.right, height: textFrame.height - textInsets.top - textInsets.bottom)
|
||||
}
|
||||
|
||||
textFrameWithoutInsets = textFrameWithoutInsets.offsetBy(dx: layoutConstants.text.bubbleInsets.left, dy: topInset)
|
||||
|
||||
var suggestedBoundingWidth: CGFloat = textFrameWithoutInsets.width
|
||||
@@ -706,8 +769,13 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
strongSelf.cachedChatMessageText = updatedCachedChatMessageText
|
||||
}
|
||||
|
||||
var previousAnimateGlyphCount: Int?
|
||||
if hasDraft || hadDraft {
|
||||
previousAnimateGlyphCount = strongSelf.textNode.textNode.getGlyphCount()
|
||||
}
|
||||
|
||||
strongSelf.textNode.textNode.displaysAsynchronously = !item.presentationData.isPreview
|
||||
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: boundingSize)
|
||||
animation.animator.updateFrame(layer: strongSelf.containerNode.layer, frame: CGRect(origin: CGPoint(), size: boundingSize), completion: nil)
|
||||
|
||||
if strongSelf.appliedExpandedBlockIds != nil && strongSelf.appliedExpandedBlockIds != strongSelf.expandedBlockIds {
|
||||
itemApply?.setInvertOffsetDirection()
|
||||
@@ -764,7 +832,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
)
|
||||
))
|
||||
animation.animator.updateFrame(layer: strongSelf.textNode.textNode.layer, frame: textFrame, completion: nil)
|
||||
animation.animator.updateFrame(layer: strongSelf.textNode.textNode.layer, frame: realTextFrame, completion: nil)
|
||||
|
||||
switch strongSelf.visibility {
|
||||
case .none:
|
||||
@@ -785,8 +853,6 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
}
|
||||
strongSelf.textAccessibilityOverlayNode.frame = textFrame
|
||||
//TODO:release
|
||||
//strongSelf.textAccessibilityOverlayNode.cachedLayout = textLayout
|
||||
|
||||
strongSelf.updateIsTranslating(isTranslating)
|
||||
|
||||
@@ -897,6 +963,10 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
strongSelf.codeHighlightState = nil
|
||||
codeHighlightState.disposable.dispose()
|
||||
}
|
||||
|
||||
if previousAnimateGlyphCount != nil || strongSelf.textRevealAnimationState != nil || hadDraft {
|
||||
strongSelf.updateTextRevealAnimation(previousGlyphCount: previousAnimateGlyphCount ?? 0)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -904,6 +974,74 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
private func updateTextRevealAnimation(previousGlyphCount: Int) {
|
||||
var fromCount = previousGlyphCount
|
||||
let toCount = self.textNode.textNode.getGlyphCount()
|
||||
let timestamp = CACurrentMediaTime()
|
||||
if let textRevealAnimationState = self.textRevealAnimationState {
|
||||
if textRevealAnimationState.toCount == toCount {
|
||||
return
|
||||
}
|
||||
fromCount = textRevealAnimationState.glyphCount(timestamp: timestamp)
|
||||
}
|
||||
if fromCount == toCount {
|
||||
if self.textRevealAnimationState != nil {
|
||||
self.textRevealAnimationState = nil
|
||||
self.textRevealLink = nil
|
||||
self.textNode.textNode.updateRevealGlyphCount(count: nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var duration: Double = Double(toCount - fromCount) / 20.0
|
||||
duration = max(0.1, min(duration, 5.0))
|
||||
|
||||
self.textRevealAnimationState = TextRevealAnimationState(
|
||||
fromCount: fromCount,
|
||||
toCount: toCount,
|
||||
startTimestamp: timestamp,
|
||||
duration: duration
|
||||
)
|
||||
if self.textRevealLink == nil, self.textRevealAnimationState != nil {
|
||||
var lastLineUpdateTimestamp = timestamp
|
||||
self.textRevealLink = SharedDisplayLinkDriver.shared.add { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard let textRevealAnimationState = self.textRevealAnimationState else {
|
||||
self.textRevealLink = nil
|
||||
return
|
||||
}
|
||||
let timestamp = CACurrentMediaTime()
|
||||
if textRevealAnimationState.fraction(timestamp: timestamp) >= 1.0 {
|
||||
self.textRevealAnimationState = nil
|
||||
self.textRevealLink = nil
|
||||
|
||||
self.textNode.textNode.updateRevealGlyphCount(count: nil)
|
||||
self.requestFullUpdate?()
|
||||
} else {
|
||||
let lineUpdateTimeout = timestamp - lastLineUpdateTimestamp
|
||||
|
||||
var requestUpdate = false
|
||||
let glyphCount = textRevealAnimationState.glyphCount(timestamp: timestamp)
|
||||
if let revealGlyphCount = self.textNode.textNode.revealGlyphCount, let cachedLayout = self.textNode.textNode.cachedLayout {
|
||||
if cachedLayout.sizeForGlyphCount(glyphCount: revealGlyphCount).height != cachedLayout.sizeForGlyphCount(glyphCount: glyphCount).height {
|
||||
if lineUpdateTimeout >= 0.1 {
|
||||
lastLineUpdateTimestamp = timestamp
|
||||
requestUpdate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
self.textNode.textNode.updateRevealGlyphCount(count: glyphCount)
|
||||
|
||||
if requestUpdate {
|
||||
self.requestFullUpdate?()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func animateInsertion(_ currentTimestamp: Double, duration: Double) {
|
||||
self.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.statusNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
@@ -1569,7 +1707,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
guard let self, completed else {
|
||||
return
|
||||
}
|
||||
self.containerNode.clipsToBounds = false
|
||||
self.containerNode.clipsToBounds = true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user