Files
Swiftgram/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenItemSectionContainerNode.swift
2026-01-02 23:11:51 +08:00

149 lines
6.5 KiB
Swift

import Foundation
import UIKit
import Display
import AsyncDisplayKit
import AccountContext
import TelegramPresentationData
final class PeerInfoScreenItemSectionContainerNode: ASDisplayNode {
private let backgroundNode: ASDisplayNode
private let topSeparatorNode: ASDisplayNode
private let bottomSeparatorNode: ASDisplayNode
private let itemContainerNode: ASDisplayNode
private var currentItems: [PeerInfoScreenItem] = []
var itemNodes: [AnyHashable: PeerInfoScreenItemNode] = [:]
override init() {
self.backgroundNode = ASDisplayNode()
self.backgroundNode.isLayerBacked = true
self.topSeparatorNode = ASDisplayNode()
self.topSeparatorNode.isLayerBacked = true
self.bottomSeparatorNode = ASDisplayNode()
self.bottomSeparatorNode.isLayerBacked = true
self.itemContainerNode = ASDisplayNode()
self.itemContainerNode.clipsToBounds = true
super.init()
self.addSubnode(self.backgroundNode)
self.addSubnode(self.itemContainerNode)
self.addSubnode(self.topSeparatorNode)
self.addSubnode(self.bottomSeparatorNode)
}
func update(context: AccountContext, width: CGFloat, safeInsets: UIEdgeInsets, hasCorners: Bool, presentationData: PresentationData, items: [PeerInfoScreenItem], transition: ContainedViewLayoutTransition) -> CGFloat {
self.backgroundNode.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor
self.topSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
self.topSeparatorNode.isHidden = hasCorners
self.bottomSeparatorNode.isHidden = hasCorners
var contentHeight: CGFloat = 0.0
var contentWithBackgroundHeight: CGFloat = 0.0
var contentWithBackgroundOffset: CGFloat = 0.0
for i in 0 ..< items.count {
let item = items[i]
let itemNode: PeerInfoScreenItemNode
var wasAdded = false
if let current = self.itemNodes[item.id] {
itemNode = current
} else {
wasAdded = true
itemNode = item.node()
self.itemNodes[item.id] = itemNode
self.itemContainerNode.addSubnode(itemNode)
itemNode.bringToFrontForHighlight = { [weak self, weak itemNode] in
guard let strongSelf = self, let itemNode = itemNode else {
return
}
strongSelf.view.bringSubviewToFront(itemNode.view)
}
}
let itemTransition: ContainedViewLayoutTransition = wasAdded ? .immediate : transition
let topItem: PeerInfoScreenItem?
if i == 0 {
topItem = nil
} else if items[i - 1] is PeerInfoScreenHeaderItem {
topItem = nil
} else {
topItem = items[i - 1]
}
let bottomItem: PeerInfoScreenItem?
if i == items.count - 1 {
bottomItem = nil
} else if items[i + 1] is PeerInfoScreenCommentItem {
bottomItem = nil
} else {
bottomItem = items[i + 1]
}
let itemHeight = itemNode.update(context: context, width: width, safeInsets: safeInsets, presentationData: presentationData, item: item, topItem: topItem, bottomItem: bottomItem, hasCorners: hasCorners, transition: itemTransition)
let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: width, height: itemHeight))
itemTransition.updateFrame(node: itemNode, frame: itemFrame)
if wasAdded {
itemNode.alpha = 0.0
let alphaTransition: ContainedViewLayoutTransition = transition.isAnimated ? .animated(duration: 0.35, curve: .linear) : .immediate
alphaTransition.updateAlpha(node: itemNode, alpha: 1.0)
}
if item is PeerInfoScreenCommentItem {
} else {
contentWithBackgroundHeight += itemHeight
}
contentHeight += itemHeight
if item is PeerInfoScreenHeaderItem {
contentWithBackgroundOffset = contentHeight
}
}
var removeIds: [AnyHashable] = []
for (id, _) in self.itemNodes {
if !items.contains(where: { $0.id == id }) {
removeIds.append(id)
}
}
for id in removeIds {
if let itemNode = self.itemNodes.removeValue(forKey: id) {
itemNode.view.superview?.sendSubviewToBack(itemNode.view)
transition.updateAlpha(node: itemNode, alpha: 0.0, completion: { [weak itemNode] _ in
itemNode?.removeFromSupernode()
})
}
}
transition.updateFrame(node: self.itemContainerNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: contentHeight)))
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentWithBackgroundOffset), size: CGSize(width: width, height: max(0.0, contentWithBackgroundHeight - contentWithBackgroundOffset))))
transition.updateFrame(node: self.topSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentWithBackgroundOffset - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel)))
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentWithBackgroundHeight), size: CGSize(width: width, height: UIScreenPixel)))
if contentHeight.isZero {
transition.updateAlpha(node: self.topSeparatorNode, alpha: 0.0)
transition.updateAlpha(node: self.bottomSeparatorNode, alpha: 0.0)
} else {
transition.updateAlpha(node: self.topSeparatorNode, alpha: 1.0)
transition.updateAlpha(node: self.bottomSeparatorNode, alpha: 1.0)
}
return contentHeight
}
func animateErrorIfNeeded() {
for (_, itemNode) in self.itemNodes {
if let itemNode = itemNode as? PeerInfoScreenMultilineInputItemNode {
itemNode.animateErrorIfNeeded()
}
}
}
}