import Foundation import Display import AsyncDisplayKit import Postbox import TelegramCore private let avatarFont: UIFont = UIFont(name: ".SFCompactRounded-Semibold", size: 13.0)! final class MultipleAvatarsNode: ASDisplayNode { private var nodes: [(Peer, AvatarNode)] = [] static func asyncLayout(_ current: MultipleAvatarsNode?) -> (Account, [Peer], CGSize) -> (Bool) -> MultipleAvatarsNode { let currentNodes: [(Peer, AvatarNode)] = current?.nodes ?? [] return { account, peers, size in var node: MultipleAvatarsNode if let current = current { node = current } else { node = MultipleAvatarsNode() } var resultNodes: [(Peer, AvatarNode)] = [] for peer in peers { var found = false inner: for (currentPeer, currentNode) in currentNodes { if currentPeer.id == peer.id { resultNodes.append((peer, currentNode)) found = true break inner } } if !found { resultNodes.append((peer, AvatarNode(font: avatarFont))) } if resultNodes.count == 4 { break } } return { animated in let partitionSize = floor(size.width / 2.0) let singleSize = partitionSize - 1.0 var index = 0 for (peer, avatarNode) in resultNodes { let xPosition: CGFloat = index % 2 == 0 ? 0.0 : size.width - singleSize let yPosition = index / 2 == 0 ? 0.0 : size.height - singleSize let avatarFrame = CGRect(origin: CGPoint(x: xPosition, y: yPosition), size: CGSize(width: singleSize, height: singleSize)) if avatarNode.supernode == nil { node.addSubnode(avatarNode) avatarNode.frame = avatarFrame if animated { avatarNode.layer.animateScale(from: 0.2, to: 1.0, duration: 0.2) avatarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } } else { let distance = CGPoint(x: avatarNode.frame.midX - avatarFrame.midX, y: avatarNode.frame.midY - avatarFrame.midY) avatarNode.frame = avatarFrame if animated { avatarNode.layer.animatePosition(from: distance, to: CGPoint(), duration: 0.2, timingFunction: kCAMediaTimingFunctionEaseInEaseOut, additive: true) } } avatarNode.setPeer(account: account, peer: peer) index += 1 } index += 1 for (_, currentNode) in node.nodes { var found = false inner: for (_, resultNode) in resultNodes { if currentNode === resultNode { found = true break inner } } if !found { if animated { currentNode.layer.animateScale(from: 1.0, to: 0.4, duration: 0.2, removeOnCompletion: false) currentNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak currentNode] _ in currentNode?.removeFromSupernode() }) } else { currentNode.removeFromSupernode() } } } node.nodes = resultNodes return node } } } }