mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
no message
This commit is contained in:
170
TelegramUI/ChatMessageTextBubbleContentNode.swift
Normal file
170
TelegramUI/ChatMessageTextBubbleContentNode.swift
Normal file
@@ -0,0 +1,170 @@
|
||||
import Foundation
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import TelegramCore
|
||||
|
||||
private let messageFont: UIFont = UIFont.systemFont(ofSize: 17.0)
|
||||
private let messageBoldFont: UIFont = UIFont.boldSystemFont(ofSize: 17.0)
|
||||
|
||||
class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
private let textNode: TextNode
|
||||
private let statusNode: ChatMessageDateAndStatusNode
|
||||
|
||||
required init() {
|
||||
self.textNode = TextNode()
|
||||
self.statusNode = ChatMessageDateAndStatusNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.textNode.isLayerBacked = true
|
||||
self.textNode.contentMode = .topLeft
|
||||
self.textNode.contentsScale = UIScreenScale
|
||||
self.textNode.displaysAsynchronously = true
|
||||
self.addSubnode(self.textNode)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func asyncLayoutContent() -> (_ item: ChatMessageItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ position: ChatMessageBubbleContentPosition, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, () -> Void))) {
|
||||
let textLayout = TextNode.asyncLayout(self.textNode)
|
||||
let statusLayout = self.statusNode.asyncLayout()
|
||||
|
||||
return { item, layoutConstants, position, _ in
|
||||
return (CGFloat.greatestFiniteMagnitude, { constrainedSize in
|
||||
let message = item.message
|
||||
|
||||
let incoming = item.account.peerId != message.author?.id
|
||||
|
||||
let horizontalInset = layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right
|
||||
let textConstrainedSize = CGSize(width: constrainedSize.width - horizontalInset, height: constrainedSize.height)
|
||||
|
||||
var t = Int(item.message.timestamp)
|
||||
var timeinfo = tm()
|
||||
localtime_r(&t, &timeinfo)
|
||||
|
||||
let dateText = String(format: "%02d:%02d", arguments: [Int(timeinfo.tm_hour), Int(timeinfo.tm_min)])
|
||||
//let dateText = "\(message.id.id)"
|
||||
|
||||
let statusType: ChatMessageDateAndStatusType?
|
||||
if case .None = position.bottom {
|
||||
if incoming {
|
||||
statusType = .BubbleIncoming
|
||||
} else {
|
||||
if message.flags.contains(.Failed) {
|
||||
statusType = .BubbleOutgoing(.Failed)
|
||||
} else if message.flags.contains(.Unsent) {
|
||||
statusType = .BubbleOutgoing(.Sending)
|
||||
} else {
|
||||
statusType = .BubbleOutgoing(.Sent(read: true))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
statusType = nil
|
||||
}
|
||||
|
||||
var statusSize: CGSize?
|
||||
var statusApply: (() -> Void)?
|
||||
|
||||
if let statusType = statusType {
|
||||
let (size, apply) = statusLayout(dateText, statusType, textConstrainedSize)
|
||||
statusSize = size
|
||||
statusApply = apply
|
||||
}
|
||||
|
||||
let attributedText: NSAttributedString
|
||||
var entities: TextEntitiesMessageAttribute?
|
||||
for attribute in item.message.attributes {
|
||||
if let attribute = attribute as? TextEntitiesMessageAttribute {
|
||||
entities = attribute
|
||||
break
|
||||
}
|
||||
}
|
||||
if let entities = entities {
|
||||
let string = NSMutableAttributedString(string: message.text, attributes: [NSFontAttributeName: messageFont, NSForegroundColorAttributeName: UIColor.black])
|
||||
for entity in entities.entities {
|
||||
switch entity.type {
|
||||
case .Url:
|
||||
string.addAttribute(NSForegroundColorAttributeName, value: UIColor(0x004bad), range: NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound))
|
||||
case .Bold:
|
||||
string.addAttribute(NSFontAttributeName, value: messageBoldFont, range: NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
attributedText = string
|
||||
} else {
|
||||
attributedText = NSAttributedString(string: message.text, font: messageFont, textColor: UIColor.black)
|
||||
}
|
||||
|
||||
let (textLayout, textApply) = textLayout(attributedText, nil, 0, .end, textConstrainedSize, nil)
|
||||
|
||||
var textFrame = CGRect(origin: CGPoint(), size: textLayout.size)
|
||||
let textSize = textLayout.size
|
||||
|
||||
var statusFrame: CGRect?
|
||||
if let statusSize = statusSize {
|
||||
var frame = CGRect(origin: CGPoint(), size: statusSize)
|
||||
|
||||
let trailingLineWidth = textLayout.trailingLineWidth
|
||||
if textSize.width - trailingLineWidth >= statusSize.width {
|
||||
frame.origin = CGPoint(x: textFrame.maxX - statusSize.width, y: textFrame.maxY - statusSize.height)
|
||||
} else if trailingLineWidth + statusSize.width < textConstrainedSize.width {
|
||||
frame.origin = CGPoint(x: textFrame.minX + trailingLineWidth, y: textFrame.maxY - statusSize.height)
|
||||
} else {
|
||||
frame.origin = CGPoint(x: textFrame.maxX - statusSize.width, y: textFrame.maxY)
|
||||
}
|
||||
statusFrame = frame
|
||||
}
|
||||
|
||||
textFrame = textFrame.offsetBy(dx: layoutConstants.text.bubbleInsets.left, dy: layoutConstants.text.bubbleInsets.top)
|
||||
statusFrame = statusFrame?.offsetBy(dx: layoutConstants.text.bubbleInsets.left, dy: layoutConstants.text.bubbleInsets.top)
|
||||
|
||||
var boundingSize: CGSize
|
||||
if let statusFrame = statusFrame {
|
||||
boundingSize = textFrame.union(statusFrame).size
|
||||
} else {
|
||||
boundingSize = textFrame.size
|
||||
}
|
||||
boundingSize.width += layoutConstants.text.bubbleInsets.left + layoutConstants.text.bubbleInsets.right
|
||||
boundingSize.height += layoutConstants.text.bubbleInsets.top + layoutConstants.text.bubbleInsets.bottom
|
||||
|
||||
return (boundingSize.width, { boundingWidth in
|
||||
var adjustedStatusFrame: CGRect?
|
||||
if let statusFrame = statusFrame {
|
||||
adjustedStatusFrame = CGRect(origin: CGPoint(x: boundingWidth - statusFrame.size.width - layoutConstants.text.bubbleInsets.right, y: statusFrame.origin.y), size: statusFrame.size)
|
||||
}
|
||||
|
||||
return (boundingSize, { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let _ = textApply()
|
||||
|
||||
if let statusApply = statusApply, let adjustedStatusFrame = adjustedStatusFrame {
|
||||
strongSelf.statusNode.frame = adjustedStatusFrame
|
||||
statusApply()
|
||||
if strongSelf.statusNode.supernode == nil {
|
||||
strongSelf.addSubnode(strongSelf.statusNode)
|
||||
}
|
||||
} else if strongSelf.statusNode.supernode != nil {
|
||||
strongSelf.statusNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
strongSelf.textNode.frame = textFrame
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override func animateInsertion(_ currentTimestamp: Double, duration: Double) {
|
||||
self.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)
|
||||
}
|
||||
|
||||
override func animateAdded(_ currentTimestamp: Double, duration: Double) {
|
||||
self.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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user