mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
Initial reply stats implementation
This commit is contained in:
@@ -160,12 +160,15 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
private var reactionNodes: [StatusReactionNode] = []
|
||||
private var reactionCountNode: TextNode?
|
||||
private var reactionButtonNode: HighlightTrackingButtonNode?
|
||||
private var repliesIcon: ASImageNode?
|
||||
private var replyCountNode: TextNode?
|
||||
|
||||
private var type: ChatMessageDateAndStatusType?
|
||||
private var theme: ChatPresentationThemeData?
|
||||
private var layoutSize: CGSize?
|
||||
|
||||
var openReactions: (() -> Void)?
|
||||
var openReplies: (() -> Void)?
|
||||
|
||||
override init() {
|
||||
self.dateNode = TextNode()
|
||||
@@ -177,7 +180,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
self.addSubnode(self.dateNode)
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ edited: Bool, _ impressionCount: Int?, _ dateText: String, _ type: ChatMessageDateAndStatusType, _ constrainedSize: CGSize, _ reactions: [MessageReaction]) -> (CGSize, (Bool) -> Void) {
|
||||
func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ edited: Bool, _ impressionCount: Int?, _ dateText: String, _ type: ChatMessageDateAndStatusType, _ constrainedSize: CGSize, _ reactions: [MessageReaction], _ replies: Int) -> (CGSize, (Bool) -> Void) {
|
||||
let dateLayout = TextNode.asyncLayout(self.dateNode)
|
||||
|
||||
var checkReadNode = self.checkReadNode
|
||||
@@ -187,15 +190,17 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
|
||||
var currentBackgroundNode = self.backgroundNode
|
||||
var currentImpressionIcon = self.impressionIcon
|
||||
var currentRepliesIcon = self.repliesIcon
|
||||
|
||||
let currentType = self.type
|
||||
let currentTheme = self.theme
|
||||
|
||||
let makeReactionCountLayout = TextNode.asyncLayout(self.reactionCountNode)
|
||||
let makeReplyCountLayout = TextNode.asyncLayout(self.replyCountNode)
|
||||
|
||||
let previousLayoutSize = self.layoutSize
|
||||
|
||||
return { context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions in
|
||||
return { context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replyCount in
|
||||
let dateColor: UIColor
|
||||
var backgroundImage: UIImage?
|
||||
var outgoingStatus: ChatMessageDateAndStatusOutgoingType?
|
||||
@@ -206,6 +211,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
let clockFrameImage: UIImage?
|
||||
let clockMinImage: UIImage?
|
||||
var impressionImage: UIImage?
|
||||
var repliesImage: UIImage?
|
||||
|
||||
let themeUpdated = presentationData.theme != currentTheme || type != currentType
|
||||
|
||||
@@ -226,6 +232,9 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
if impressionCount != nil {
|
||||
impressionImage = graphics.incomingDateAndStatusImpressionIcon
|
||||
}
|
||||
if replyCount != 0 {
|
||||
repliesImage = graphics.incomingDateAndStatusRepliesIcon
|
||||
}
|
||||
case let .BubbleOutgoing(status):
|
||||
dateColor = presentationData.theme.theme.chat.message.outgoing.secondaryTextColor
|
||||
outgoingStatus = status
|
||||
@@ -237,6 +246,9 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
if impressionCount != nil {
|
||||
impressionImage = graphics.outgoingDateAndStatusImpressionIcon
|
||||
}
|
||||
if replyCount != 0 {
|
||||
repliesImage = graphics.outgoingDateAndStatusRepliesIcon
|
||||
}
|
||||
case .ImageIncoming:
|
||||
dateColor = presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor
|
||||
backgroundImage = graphics.dateAndStatusMediaBackground
|
||||
@@ -248,6 +260,9 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
if impressionCount != nil {
|
||||
impressionImage = graphics.mediaImpressionIcon
|
||||
}
|
||||
if replyCount != 0 {
|
||||
repliesImage = graphics.mediaRepliesIcon
|
||||
}
|
||||
case let .ImageOutgoing(status):
|
||||
dateColor = presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor
|
||||
outgoingStatus = status
|
||||
@@ -260,6 +275,9 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
if impressionCount != nil {
|
||||
impressionImage = graphics.mediaImpressionIcon
|
||||
}
|
||||
if replyCount != 0 {
|
||||
repliesImage = graphics.mediaRepliesIcon
|
||||
}
|
||||
case .FreeIncoming:
|
||||
let serviceColor = serviceMessageColorComponents(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
|
||||
dateColor = serviceColor.primaryText
|
||||
@@ -272,6 +290,9 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
if impressionCount != nil {
|
||||
impressionImage = graphics.freeImpressionIcon
|
||||
}
|
||||
if replyCount != 0 {
|
||||
repliesImage = graphics.freeRepliesIcon
|
||||
}
|
||||
case let .FreeOutgoing(status):
|
||||
let serviceColor = serviceMessageColorComponents(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
|
||||
dateColor = serviceColor.primaryText
|
||||
@@ -285,6 +306,9 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
if impressionCount != nil {
|
||||
impressionImage = graphics.freeImpressionIcon
|
||||
}
|
||||
if replyCount != 0 {
|
||||
repliesImage = graphics.freeRepliesIcon
|
||||
}
|
||||
}
|
||||
|
||||
var updatedDateText = dateText
|
||||
@@ -323,6 +347,20 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
currentImpressionIcon = nil
|
||||
}
|
||||
|
||||
var repliesIconSize = CGSize()
|
||||
if let repliesImage = repliesImage {
|
||||
if currentRepliesIcon == nil {
|
||||
let iconNode = ASImageNode()
|
||||
iconNode.isLayerBacked = true
|
||||
iconNode.displayWithoutProcessing = true
|
||||
iconNode.displaysAsynchronously = false
|
||||
currentRepliesIcon = iconNode
|
||||
}
|
||||
repliesIconSize = repliesImage.size
|
||||
} else {
|
||||
currentRepliesIcon = nil
|
||||
}
|
||||
|
||||
if let outgoingStatus = outgoingStatus {
|
||||
switch outgoingStatus {
|
||||
case .Sending:
|
||||
@@ -433,6 +471,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
|
||||
let reactionSize: CGFloat = 14.0
|
||||
var reactionCountLayoutAndApply: (TextNodeLayout, () -> TextNode)?
|
||||
var replyCountLayoutAndApply: (TextNodeLayout, () -> TextNode)?
|
||||
|
||||
let reactionSpacing: CGFloat = -4.0
|
||||
let reactionTrailingSpacing: CGFloat = 4.0
|
||||
@@ -458,6 +497,22 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
reactionInset += max(10.0, layoutAndApply.0.size.width) + 2.0
|
||||
reactionCountLayoutAndApply = layoutAndApply
|
||||
}
|
||||
|
||||
if replyCount > 0 {
|
||||
let countString: String
|
||||
if replyCount > 1000000 {
|
||||
countString = "\(replyCount / 1000000)M"
|
||||
} else if replyCount > 1000 {
|
||||
countString = "\(replyCount / 1000)K"
|
||||
} else {
|
||||
countString = "\(replyCount)"
|
||||
}
|
||||
|
||||
let layoutAndApply = makeReplyCountLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: countString, font: dateFont, textColor: dateColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0)))
|
||||
reactionInset += 14.0 + layoutAndApply.0.size.width + 4.0
|
||||
replyCountLayoutAndApply = layoutAndApply
|
||||
}
|
||||
|
||||
leftInset += reactionInset
|
||||
|
||||
let layoutSize = CGSize(width: leftInset + impressionWidth + date.size.width + statusWidth + backgroundInsets.left + backgroundInsets.right, height: date.size.height + backgroundInsets.top + backgroundInsets.bottom)
|
||||
@@ -510,7 +565,6 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
strongSelf.impressionIcon = nil
|
||||
}
|
||||
|
||||
|
||||
strongSelf.dateNode.frame = CGRect(origin: CGPoint(x: leftInset + backgroundInsets.left + impressionWidth, y: backgroundInsets.top + 1.0 + offset), size: date.size)
|
||||
|
||||
if let clockFrameNode = clockFrameNode {
|
||||
@@ -677,6 +731,48 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
if let currentRepliesIcon = currentRepliesIcon {
|
||||
currentRepliesIcon.displaysAsynchronously = !presentationData.isPreview
|
||||
if currentRepliesIcon.image !== repliesImage {
|
||||
currentRepliesIcon.image = repliesImage
|
||||
}
|
||||
if currentRepliesIcon.supernode == nil {
|
||||
strongSelf.repliesIcon = currentRepliesIcon
|
||||
strongSelf.addSubnode(currentRepliesIcon)
|
||||
}
|
||||
currentRepliesIcon.frame = CGRect(origin: CGPoint(x: reactionOffset - 2.0, y: backgroundInsets.top + offset + floor((date.size.height - repliesIconSize.height) / 2.0)), size: repliesIconSize)
|
||||
reactionOffset += 9.0
|
||||
} else if let repliesIcon = strongSelf.repliesIcon {
|
||||
repliesIcon.removeFromSupernode()
|
||||
strongSelf.repliesIcon = nil
|
||||
}
|
||||
|
||||
if let (layout, apply) = replyCountLayoutAndApply {
|
||||
let node = apply()
|
||||
if strongSelf.replyCountNode !== node {
|
||||
strongSelf.replyCountNode?.removeFromSupernode()
|
||||
strongSelf.addSubnode(node)
|
||||
strongSelf.replyCountNode = node
|
||||
if animated {
|
||||
node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
}
|
||||
}
|
||||
node.frame = CGRect(origin: CGPoint(x: reactionOffset + 4.0, y: backgroundInsets.top + 1.0 + offset), size: layout.size)
|
||||
reactionOffset += 4.0 + layout.size.width
|
||||
} else if let replyCountNode = strongSelf.replyCountNode {
|
||||
strongSelf.replyCountNode = nil
|
||||
if animated {
|
||||
if let previousLayoutSize = previousLayoutSize {
|
||||
replyCountNode.frame = replyCountNode.frame.offsetBy(dx: layoutSize.width - previousLayoutSize.width, dy: 0.0)
|
||||
}
|
||||
replyCountNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak replyCountNode] _ in
|
||||
replyCountNode?.removeFromSupernode()
|
||||
})
|
||||
} else {
|
||||
replyCountNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
|
||||
/*if !strongSelf.reactionNodes.isEmpty {
|
||||
if strongSelf.reactionButtonNode == nil {
|
||||
let reactionButtonNode = HighlightTrackingButtonNode()
|
||||
@@ -709,17 +805,17 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
static func asyncLayout(_ node: ChatMessageDateAndStatusNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ edited: Bool, _ impressionCount: Int?, _ dateText: String, _ type: ChatMessageDateAndStatusType, _ constrainedSize: CGSize, _ reactions: [MessageReaction]) -> (CGSize, (Bool) -> ChatMessageDateAndStatusNode) {
|
||||
static func asyncLayout(_ node: ChatMessageDateAndStatusNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ edited: Bool, _ impressionCount: Int?, _ dateText: String, _ type: ChatMessageDateAndStatusType, _ constrainedSize: CGSize, _ reactions: [MessageReaction], _ replies: Int) -> (CGSize, (Bool) -> ChatMessageDateAndStatusNode) {
|
||||
let currentLayout = node?.asyncLayout()
|
||||
return { context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions in
|
||||
return { context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replies in
|
||||
let resultNode: ChatMessageDateAndStatusNode
|
||||
let resultSizeAndApply: (CGSize, (Bool) -> Void)
|
||||
if let node = node, let currentLayout = currentLayout {
|
||||
resultNode = node
|
||||
resultSizeAndApply = currentLayout(context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions)
|
||||
resultSizeAndApply = currentLayout(context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replies)
|
||||
} else {
|
||||
resultNode = ChatMessageDateAndStatusNode()
|
||||
resultSizeAndApply = resultNode.asyncLayout()(context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions)
|
||||
resultSizeAndApply = resultNode.asyncLayout()(context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replies)
|
||||
}
|
||||
|
||||
return (resultSizeAndApply.0, { animated in
|
||||
|
||||
Reference in New Issue
Block a user