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() } } } }