Files
2025-12-01 22:13:33 +08:00

156 lines
7.1 KiB
Swift

import Foundation
import UIKit
import Display
import AsyncDisplayKit
import Postbox
import TelegramCore
import SwiftSignalKit
import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
import TelegramStringFormatting
import TextFormat
import TextNodeWithEntities
final class ChatFeePanelNode: ASDisplayNode {
private let context: AccountContext
private let removeFee: () -> Void
private let contextContainer: ContextControllerSourceNode
private let clippingContainer: ASDisplayNode
private let contentContainer: ASDisplayNode
private let textContainer: ASDisplayNode
private let textNode: ImmediateTextNodeWithEntities
private let removeButtonNode: HighlightTrackingButtonNode
private let removeTextNode: ImmediateTextNode
private var currentLayout: (CGFloat, CGFloat, CGFloat)?
init(context: AccountContext, removeFee: @escaping () -> Void) {
self.context = context
self.removeFee = removeFee
self.contextContainer = ContextControllerSourceNode()
self.clippingContainer = ASDisplayNode()
self.clippingContainer.clipsToBounds = true
self.contentContainer = ASDisplayNode()
self.contextContainer.isGestureEnabled = false
self.textContainer = ASDisplayNode()
self.textNode = ImmediateTextNodeWithEntities()
self.textNode.anchorPoint = CGPoint()
self.textNode.displaysAsynchronously = false
self.textNode.isUserInteractionEnabled = false
self.textNode.maximumNumberOfLines = 2
self.textNode.textAlignment = .center
self.removeButtonNode = HighlightTrackingButtonNode()
self.removeTextNode = ImmediateTextNode()
self.removeTextNode.anchorPoint = CGPoint()
self.removeTextNode.displaysAsynchronously = false
self.removeTextNode.isUserInteractionEnabled = false
super.init()
self.addSubnode(self.contextContainer)
self.contextContainer.addSubnode(self.clippingContainer)
self.clippingContainer.addSubnode(self.contentContainer)
self.contextContainer.addSubnode(self.textContainer)
self.textContainer.addSubnode(self.textNode)
self.contextContainer.addSubnode(self.removeTextNode)
self.contextContainer.addSubnode(self.removeButtonNode)
self.removeButtonNode.addTarget(self, action: #selector(self.removePressed), forControlEvents: [.touchUpInside])
self.removeButtonNode.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if highlighted {
strongSelf.removeTextNode.layer.removeAnimation(forKey: "opacity")
strongSelf.removeTextNode.alpha = 0.4
} else {
strongSelf.removeTextNode.alpha = 1.0
strongSelf.removeTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
}
}
}
}
private var theme: PresentationTheme?
func updateLayout(width: CGFloat, theme: PresentationTheme, strings: PresentationStrings, info: MessageFeeHeaderPanelComponent.Info, transition: ContainedViewLayoutTransition) -> CGFloat {
let leftInset: CGFloat = 0.0
let rightInset: CGFloat = 0.0
if self.theme !== theme {
self.theme = theme
self.removeTextNode.attributedText = NSAttributedString(string: strings.Chat_PaidMessageFee_RemoveFee, font: Font.regular(17.0), textColor: theme.chat.inputPanel.panelControlColor)
}
let attributedText = NSMutableAttributedString(string: strings.Chat_PaidMessageFee_Text(info.peer.compactDisplayTitle, "⭐️\(info.value)").string, font: Font.regular(12.0), textColor: theme.rootController.navigationBar.secondaryTextColor)
let range = (attributedText.string as NSString).range(of: "⭐️")
if range.location != NSNotFound {
attributedText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: true)), range: range)
attributedText.addAttribute(.baselineOffset, value: 0.0, range: range)
}
self.textNode.attributedText = attributedText
self.textNode.visibility = true
self.textNode.arguments = TextNodeWithEntities.Arguments(
context: self.context,
cache: self.context.animationCache,
renderer: self.context.animationRenderer,
placeholderColor: UIColor(white: 1.0, alpha: 0.1),
attemptSynchronous: false
)
let sideInset = 12.0
let textSize = self.textNode.updateLayout(CGSize(width: width - leftInset - rightInset - sideInset * 2.0, height: .greatestFiniteMagnitude))
let textFrame = CGRect(origin: CGPoint(x: leftInset + floorToScreenPixels((width - leftInset - rightInset - textSize.width) / 2.0), y: 9.0), size: textSize)
transition.updateFrame(node: self.textContainer, frame: textFrame)
if self.textNode.bounds.size.width != 0.0, transition.isAnimated {
if let snapshotLayer = self.textNode.layer.snapshotContentTree() {
self.textContainer.layer.addSublayer(snapshotLayer)
snapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak snapshotLayer] _ in
snapshotLayer?.removeFromSuperlayer()
})
self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
}
}
transition.updatePosition(node: self.textNode, position: CGPoint())
self.textNode.bounds = CGRect(origin: CGPoint(), size: textFrame.size)
let panelHeight: CGFloat = 48.0 + textSize.height
let removeSize = self.removeTextNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude))
let removeFrame = CGRect(origin: CGPoint(x: leftInset + floorToScreenPixels((width - leftInset - rightInset - removeSize.width) / 2.0), y: panelHeight - removeSize.height - 9.0), size: removeSize)
transition.updatePosition(node: self.removeTextNode, position: removeFrame.origin)
self.removeTextNode.bounds = CGRect(origin: CGPoint(), size: removeFrame.size)
transition.updateFrame(node: self.removeButtonNode, frame: removeFrame.insetBy(dx: -8.0, dy: -4.0))
self.contextContainer.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight))
self.clippingContainer.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight))
self.contentContainer.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight))
self.currentLayout = (width, leftInset, rightInset)
return panelHeight
}
@objc func removePressed() {
self.removeFee()
}
}