Swiftgram/submodules/TelegramUI/Sources/ChatEmptyNode.swift
Ilya Laktyushin 3dcc32c3c3 Cleanup
2020-07-07 02:49:11 +03:00

710 lines
34 KiB
Swift

import Foundation
import UIKit
import AsyncDisplayKit
import Display
import SwiftSignalKit
import Postbox
import TelegramCore
import SyncCore
import TelegramPresentationData
import AppBundle
import LocalizedPeerData
import TelegramStringFormatting
private protocol ChatEmptyNodeContent {
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize
}
private let titleFont = Font.medium(15.0)
private let messageFont = Font.regular(14.0)
private final class ChatEmptyNodeRegularChatContent: ASDisplayNode, ChatEmptyNodeContent {
private let textNode: ImmediateTextNode
private var currentTheme: PresentationTheme?
private var currentStrings: PresentationStrings?
override init() {
self.textNode = ImmediateTextNode()
super.init()
self.addSubnode(self.textNode)
}
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
self.currentTheme = interfaceState.theme
self.currentStrings = interfaceState.strings
let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
self.textNode.attributedText = NSAttributedString(string: interfaceState.isScheduledMessages ? interfaceState.strings.ScheduledMessages_EmptyPlaceholder : interfaceState.strings.Conversation_EmptyPlaceholder, font: messageFont, textColor: serviceColor.primaryText)
}
let insets = UIEdgeInsets(top: 6.0, left: 10.0, bottom: 6.0, right: 10.0)
let textSize = self.textNode.updateLayout(CGSize(width: size.width - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude))
let contentWidth = textSize.width
let contentHeight = textSize.height
let contentRect = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: CGSize(width: contentWidth, height: contentHeight))
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: contentRect.minX + floor((contentRect.width - textSize.width) / 2.0), y: insets.top), size: textSize))
return contentRect.insetBy(dx: -insets.left, dy: -insets.top).size
}
}
private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNodeContent, UIGestureRecognizerDelegate {
private let account: Account
private let interaction: ChatPanelInterfaceInteraction?
private let titleNode: ImmediateTextNode
private let textNode: ImmediateTextNode
private var stickerItem: ChatMediaInputStickerGridItem?
private let stickerNode: ChatMediaInputStickerGridItemNode
private var currentTheme: PresentationTheme?
private var currentStrings: PresentationStrings?
private var didSetupSticker = false
private let disposable = MetaDisposable()
var greetingStickerNode: ASDisplayNode? {
if let animationNode = self.stickerNode.animationNode, animationNode.supernode === stickerNode {
return animationNode
} else if self.stickerNode.imageNode.supernode === stickerNode {
return self.stickerNode.imageNode
} else {
return nil
}
}
init(account: Account, interaction: ChatPanelInterfaceInteraction?) {
self.account = account
self.interaction = interaction
self.titleNode = ImmediateTextNode()
self.titleNode.maximumNumberOfLines = 0
self.titleNode.lineSpacing = 0.15
self.titleNode.textAlignment = .center
self.titleNode.isUserInteractionEnabled = false
self.titleNode.displaysAsynchronously = false
self.textNode = ImmediateTextNode()
self.textNode.maximumNumberOfLines = 0
self.textNode.lineSpacing = 0.15
self.textNode.textAlignment = .center
self.textNode.isUserInteractionEnabled = false
self.textNode.displaysAsynchronously = false
self.stickerNode = ChatMediaInputStickerGridItemNode()
super.init()
self.addSubnode(self.titleNode)
self.addSubnode(self.textNode)
self.addSubnode(self.stickerNode)
}
override func didLoad() {
super.didLoad()
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.stickerTapGesture(_:)))
tapRecognizer.delegate = self
self.stickerNode.view.addGestureRecognizer(tapRecognizer)
}
deinit {
self.disposable.dispose()
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
@objc private func stickerTapGesture(_ gestureRecognizer: UITapGestureRecognizer) {
guard let stickerItem = self.stickerItem else {
return
}
let _ = self.interaction?.sendSticker(.standalone(media: stickerItem.stickerItem.file), self.stickerNode, self.stickerNode.bounds)
}
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
self.currentTheme = interfaceState.theme
self.currentStrings = interfaceState.strings
var displayName = ""
let distance = interfaceState.peerNearbyData?.distance ?? 0
if let renderedPeer = interfaceState.renderedPeer {
if let chatPeer = renderedPeer.peers[renderedPeer.peerId] {
displayName = chatPeer.compactDisplayTitle
}
}
let titleString = interfaceState.strings.Conversation_PeerNearbyTitle(displayName, shortStringForDistance(strings: interfaceState.strings, distance: distance)).0
let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
self.titleNode.attributedText = NSAttributedString(string: titleString, font: titleFont, textColor: serviceColor.primaryText)
self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Conversation_PeerNearbyText, font: messageFont, textColor: serviceColor.primaryText)
}
let stickerSize = CGSize(width: 160.0, height: 160.0)
if let item = self.stickerItem {
self.stickerNode.updateLayout(item: item, size: stickerSize, isVisible: true, synchronousLoads: true)
} else if !self.didSetupSticker {
let sticker: Signal<TelegramMediaFile?, NoError>
if let preloadedSticker = interfaceState.peerNearbyData?.sticker {
sticker = .single(preloadedSticker)
} else {
sticker = randomGreetingSticker(account: self.account)
|> map { item -> TelegramMediaFile? in
return item?.file
}
}
self.didSetupSticker = true
self.disposable.set((sticker
|> deliverOnMainQueue).start(next: { [weak self] sticker in
if let strongSelf = self, let sticker = sticker {
let inputNodeInteraction = ChatMediaInputNodeInteraction(
navigateToCollectionId: { _ in
},
navigateBackToStickers: {
},
setGifMode: { _ in
},
openSettings: {
},
toggleSearch: { _, _, _ in
},
openPeerSpecificSettings: {
},
dismissPeerSpecificSettings: {
},
clearRecentlyUsedStickers: {
}
)
inputNodeInteraction.displayStickerPlaceholder = false
let index = ItemCollectionItemIndex(index: 0, id: 0)
let collectionId = ItemCollectionId(namespace: 0, id: 0)
let stickerPackItem = StickerPackItem(index: index, file: sticker, indexKeys: [])
let item = ChatMediaInputStickerGridItem(account: strongSelf.account, collectionId: collectionId, stickerPackInfo: nil, index: ItemCollectionViewEntryIndex(collectionIndex: 0, collectionId: collectionId, itemIndex: index), stickerItem: stickerPackItem, canManagePeerSpecificPack: nil, interfaceInteraction: nil, inputNodeInteraction: inputNodeInteraction, hasAccessory: false, theme: interfaceState.theme, large: true, selected: {})
strongSelf.stickerItem = item
strongSelf.stickerNode.updateLayout(item: item, size: stickerSize, isVisible: true, synchronousLoads: true)
strongSelf.stickerNode.isVisibleInGrid = true
strongSelf.stickerNode.updateIsPanelVisible(true)
}
}))
}
let insets = UIEdgeInsets(top: 15.0, left: 15.0, bottom: 15.0, right: 15.0)
let titleSpacing: CGFloat = 5.0
let stickerSpacing: CGFloat = 5.0
var contentWidth: CGFloat = 210.0
var contentHeight: CGFloat = 0.0
let titleSize = self.titleNode.updateLayout(CGSize(width: contentWidth, height: CGFloat.greatestFiniteMagnitude))
let textSize = self.textNode.updateLayout(CGSize(width: contentWidth, height: CGFloat.greatestFiniteMagnitude))
contentWidth = max(contentWidth, max(titleSize.width, textSize.width))
contentHeight += titleSize.height + titleSpacing + textSize.height + stickerSpacing + stickerSize.height
let contentRect = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: CGSize(width: contentWidth, height: contentHeight))
let titleFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((contentRect.width - titleSize.width) / 2.0), y: contentRect.minY), size: titleSize)
transition.updateFrame(node: self.titleNode, frame: titleFrame)
let textFrame = CGRect(origin: CGPoint(x: contentRect.minX, y: titleFrame.maxY + titleSpacing), size: textSize)
transition.updateFrame(node: self.textNode, frame: textFrame)
let stickerFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((contentRect.width - stickerSize.width) / 2.0), y: textFrame.maxY + stickerSpacing), size: stickerSize)
transition.updateFrame(node: self.stickerNode, frame: stickerFrame)
return contentRect.insetBy(dx: -insets.left, dy: -insets.top).size
}
}
private final class ChatEmptyNodeSecretChatContent: ASDisplayNode, ChatEmptyNodeContent {
private let titleNode: ImmediateTextNode
private let subtitleNode: ImmediateTextNode
private var lineNodes: [(ASImageNode, ImmediateTextNode)] = []
private var currentTheme: PresentationTheme?
private var currentStrings: PresentationStrings?
override init() {
self.titleNode = ImmediateTextNode()
self.titleNode.maximumNumberOfLines = 0
self.titleNode.lineSpacing = 0.25
self.titleNode.textAlignment = .center
self.titleNode.isUserInteractionEnabled = false
self.titleNode.displaysAsynchronously = false
self.subtitleNode = ImmediateTextNode()
self.subtitleNode.maximumNumberOfLines = 0
self.subtitleNode.lineSpacing = 0.25
self.subtitleNode.isUserInteractionEnabled = false
self.subtitleNode.displaysAsynchronously = false
super.init()
self.addSubnode(self.titleNode)
self.addSubnode(self.subtitleNode)
}
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
self.currentTheme = interfaceState.theme
self.currentStrings = interfaceState.strings
var title = " "
var incoming = false
if let renderedPeer = interfaceState.renderedPeer {
if let chatPeer = renderedPeer.peers[renderedPeer.peerId] as? TelegramSecretChat {
if case .participant = chatPeer.role {
incoming = true
}
if let user = renderedPeer.peers[chatPeer.regularPeerId] {
title = user.compactDisplayTitle
}
}
}
let titleString: String
if incoming {
titleString = interfaceState.strings.Conversation_EncryptedPlaceholderTitleIncoming(title).0
} else {
titleString = interfaceState.strings.Conversation_EncryptedPlaceholderTitleOutgoing(title).0
}
let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
self.titleNode.attributedText = NSAttributedString(string: titleString, font: titleFont, textColor: serviceColor.primaryText)
self.subtitleNode.attributedText = NSAttributedString(string: interfaceState.strings.Conversation_EncryptedDescriptionTitle, font: messageFont, textColor: serviceColor.primaryText)
let strings: [String] = [
interfaceState.strings.Conversation_EncryptedDescription1,
interfaceState.strings.Conversation_EncryptedDescription2,
interfaceState.strings.Conversation_EncryptedDescription3,
interfaceState.strings.Conversation_EncryptedDescription4
]
let lines: [NSAttributedString] = strings.map { NSAttributedString(string: $0, font: messageFont, textColor: serviceColor.primaryText) }
let graphics = PresentationResourcesChat.additionalGraphics(interfaceState.theme, wallpaper: interfaceState.chatWallpaper, bubbleCorners: interfaceState.bubbleCorners)
let lockIcon = graphics.chatEmptyItemLockIcon
for i in 0 ..< lines.count {
if i >= self.lineNodes.count {
let iconNode = ASImageNode()
iconNode.isLayerBacked = true
iconNode.displaysAsynchronously = false
iconNode.displayWithoutProcessing = true
let textNode = ImmediateTextNode()
textNode.maximumNumberOfLines = 0
textNode.isUserInteractionEnabled = false
textNode.displaysAsynchronously = false
self.addSubnode(iconNode)
self.addSubnode(textNode)
self.lineNodes.append((iconNode, textNode))
}
self.lineNodes[i].0.image = lockIcon
self.lineNodes[i].1.attributedText = lines[i]
}
}
let insets = UIEdgeInsets(top: 15.0, left: 15.0, bottom: 15.0, right: 15.0)
let titleSpacing: CGFloat = 5.0
let subtitleSpacing: CGFloat = 11.0
let iconInset: CGFloat = 14.0
var contentWidth: CGFloat = 100.0
var contentHeight: CGFloat = 0.0
var lineNodes: [(CGSize, ASImageNode, ImmediateTextNode)] = []
for (iconNode, textNode) in self.lineNodes {
let textSize = textNode.updateLayout(CGSize(width: size.width - insets.left - insets.right - 10.0, height: CGFloat.greatestFiniteMagnitude))
contentWidth = max(contentWidth, iconInset + textSize.width)
contentHeight += textSize.height + subtitleSpacing
lineNodes.append((textSize, iconNode, textNode))
}
let titleSize = self.titleNode.updateLayout(CGSize(width: contentWidth, height: CGFloat.greatestFiniteMagnitude))
let subtitleSize = self.subtitleNode.updateLayout(CGSize(width: contentWidth, height: CGFloat.greatestFiniteMagnitude))
contentWidth = max(contentWidth, max(titleSize.width, subtitleSize.width))
contentHeight += titleSize.height + titleSpacing + subtitleSize.height
let contentRect = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: CGSize(width: contentWidth, height: contentHeight))
let titleFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((contentRect.width - titleSize.width) / 2.0), y: contentRect.minY), size: titleSize)
transition.updateFrame(node: self.titleNode, frame: titleFrame)
let subtitleFrame = CGRect(origin: CGPoint(x: contentRect.minX, y: titleFrame.maxY + titleSpacing), size: subtitleSize)
transition.updateFrame(node: self.subtitleNode, frame: subtitleFrame)
var lineOffset = subtitleFrame.maxY + subtitleSpacing / 2.0
for (textSize, iconNode, textNode) in lineNodes {
if let image = iconNode.image {
transition.updateFrame(node: iconNode, frame: CGRect(origin: CGPoint(x: contentRect.minX, y: lineOffset + 1.0), size: image.size))
}
transition.updateFrame(node: textNode, frame: CGRect(origin: CGPoint(x: contentRect.minX + iconInset, y: lineOffset), size: textSize))
lineOffset += textSize.height + subtitleSpacing
}
return contentRect.insetBy(dx: -insets.left, dy: -insets.top).size
}
}
private final class ChatEmptyNodeGroupChatContent: ASDisplayNode, ChatEmptyNodeContent {
private let titleNode: ImmediateTextNode
private let subtitleNode: ImmediateTextNode
private var lineNodes: [(ASImageNode, ImmediateTextNode)] = []
private var currentTheme: PresentationTheme?
private var currentStrings: PresentationStrings?
override init() {
self.titleNode = ImmediateTextNode()
self.titleNode.maximumNumberOfLines = 0
self.titleNode.lineSpacing = 0.25
self.titleNode.textAlignment = .center
self.titleNode.isUserInteractionEnabled = false
self.titleNode.displaysAsynchronously = false
self.subtitleNode = ImmediateTextNode()
self.subtitleNode.maximumNumberOfLines = 0
self.subtitleNode.lineSpacing = 0.25
self.subtitleNode.isUserInteractionEnabled = false
self.subtitleNode.displaysAsynchronously = false
super.init()
self.addSubnode(self.titleNode)
self.addSubnode(self.subtitleNode)
}
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
self.currentTheme = interfaceState.theme
self.currentStrings = interfaceState.strings
let titleString: String = interfaceState.strings.EmptyGroupInfo_Title
let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
self.titleNode.attributedText = NSAttributedString(string: titleString, font: titleFont, textColor: serviceColor.primaryText)
self.subtitleNode.attributedText = NSAttributedString(string: interfaceState.strings.EmptyGroupInfo_Subtitle, font: messageFont, textColor: serviceColor.primaryText)
let strings: [String] = [
interfaceState.strings.EmptyGroupInfo_Line1("\(interfaceState.limitsConfiguration.maxSupergroupMemberCount)").0,
interfaceState.strings.EmptyGroupInfo_Line2,
interfaceState.strings.EmptyGroupInfo_Line3,
interfaceState.strings.EmptyGroupInfo_Line4
]
let lines: [NSAttributedString] = strings.map { NSAttributedString(string: $0, font: messageFont, textColor: serviceColor.primaryText) }
let graphics = PresentationResourcesChat.additionalGraphics(interfaceState.theme, wallpaper: interfaceState.chatWallpaper, bubbleCorners: interfaceState.bubbleCorners)
let lockIcon = graphics.emptyChatListCheckIcon
for i in 0 ..< lines.count {
if i >= self.lineNodes.count {
let iconNode = ASImageNode()
iconNode.isLayerBacked = true
iconNode.displaysAsynchronously = false
iconNode.displayWithoutProcessing = true
let textNode = ImmediateTextNode()
textNode.maximumNumberOfLines = 0
textNode.isUserInteractionEnabled = false
textNode.displaysAsynchronously = false
self.addSubnode(iconNode)
self.addSubnode(textNode)
self.lineNodes.append((iconNode, textNode))
}
self.lineNodes[i].0.image = lockIcon
self.lineNodes[i].1.attributedText = lines[i]
}
}
let insets = UIEdgeInsets(top: 15.0, left: 15.0, bottom: 15.0, right: 15.0)
let titleSpacing: CGFloat = 5.0
let subtitleSpacing: CGFloat = 11.0
let iconInset: CGFloat = 19.0
var contentWidth: CGFloat = 100.0
var contentHeight: CGFloat = 0.0
var lineNodes: [(CGSize, ASImageNode, ImmediateTextNode)] = []
for (iconNode, textNode) in self.lineNodes {
let textSize = textNode.updateLayout(CGSize(width: size.width - insets.left - insets.right - 10.0, height: CGFloat.greatestFiniteMagnitude))
contentWidth = max(contentWidth, iconInset + textSize.width)
contentHeight += textSize.height + subtitleSpacing
lineNodes.append((textSize, iconNode, textNode))
}
let titleSize = self.titleNode.updateLayout(CGSize(width: contentWidth, height: CGFloat.greatestFiniteMagnitude))
let subtitleSize = self.subtitleNode.updateLayout(CGSize(width: contentWidth, height: CGFloat.greatestFiniteMagnitude))
contentWidth = max(contentWidth, max(titleSize.width, subtitleSize.width))
contentHeight += titleSize.height + titleSpacing + subtitleSize.height
let contentRect = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: CGSize(width: contentWidth, height: contentHeight))
let titleFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((contentRect.width - titleSize.width) / 2.0), y: contentRect.minY), size: titleSize)
transition.updateFrame(node: self.titleNode, frame: titleFrame)
let subtitleFrame = CGRect(origin: CGPoint(x: contentRect.minX, y: titleFrame.maxY + titleSpacing), size: subtitleSize)
transition.updateFrame(node: self.subtitleNode, frame: subtitleFrame)
var lineOffset = subtitleFrame.maxY + subtitleSpacing / 2.0
for (textSize, iconNode, textNode) in lineNodes {
if let image = iconNode.image {
transition.updateFrame(node: iconNode, frame: CGRect(origin: CGPoint(x: contentRect.minX, y: lineOffset + 2.0), size: image.size))
}
transition.updateFrame(node: textNode, frame: CGRect(origin: CGPoint(x: contentRect.minX + iconInset, y: lineOffset), size: textSize))
lineOffset += textSize.height + subtitleSpacing
}
return contentRect.insetBy(dx: -insets.left, dy: -insets.top).size
}
}
private final class ChatEmptyNodeCloudChatContent: ASDisplayNode, ChatEmptyNodeContent {
private let iconNode: ASImageNode
private let titleNode: ImmediateTextNode
private var lineNodes: [ImmediateTextNode] = []
private var currentTheme: PresentationTheme?
private var currentStrings: PresentationStrings?
override init() {
self.iconNode = ASImageNode()
self.iconNode.isLayerBacked = true
self.iconNode.displaysAsynchronously = false
self.iconNode.displayWithoutProcessing = true
self.titleNode = ImmediateTextNode()
self.titleNode.maximumNumberOfLines = 0
self.titleNode.lineSpacing = 0.15
self.titleNode.textAlignment = .center
self.titleNode.isUserInteractionEnabled = false
self.titleNode.displaysAsynchronously = false
super.init()
self.addSubnode(self.iconNode)
self.addSubnode(self.titleNode)
}
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
self.currentTheme = interfaceState.theme
self.currentStrings = interfaceState.strings
let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Empty Chat/Cloud"), color: serviceColor.primaryText)
let titleString = interfaceState.strings.Conversation_CloudStorageInfo_Title
self.titleNode.attributedText = NSAttributedString(string: titleString, font: titleFont, textColor: serviceColor.primaryText)
let strings: [String] = [
interfaceState.strings.Conversation_ClousStorageInfo_Description1,
interfaceState.strings.Conversation_ClousStorageInfo_Description2,
interfaceState.strings.Conversation_ClousStorageInfo_Description3,
interfaceState.strings.Conversation_ClousStorageInfo_Description4
]
let lines: [NSAttributedString] = strings.map { NSAttributedString(string: $0, font: messageFont, textColor: serviceColor.primaryText) }
for i in 0 ..< lines.count {
if i >= self.lineNodes.count {
let textNode = ImmediateTextNode()
textNode.maximumNumberOfLines = 0
textNode.isUserInteractionEnabled = false
textNode.displaysAsynchronously = false
self.addSubnode(textNode)
self.lineNodes.append(textNode)
}
self.lineNodes[i].attributedText = lines[i]
}
}
let insets = UIEdgeInsets(top: 15.0, left: 15.0, bottom: 15.0, right: 15.0)
let imageSpacing: CGFloat = 12.0
let titleSpacing: CGFloat = 4.0
var contentWidth: CGFloat = 100.0
var contentHeight: CGFloat = 0.0
if let image = self.iconNode.image {
contentHeight += image.size.height
contentHeight += imageSpacing
contentWidth = max(contentWidth, image.size.width)
}
var lineNodes: [(CGSize, ImmediateTextNode)] = []
for textNode in self.lineNodes {
let textSize = textNode.updateLayout(CGSize(width: size.width - insets.left - insets.right - 10.0, height: CGFloat.greatestFiniteMagnitude))
contentWidth = max(contentWidth, textSize.width)
contentHeight += textSize.height + titleSpacing
lineNodes.append((textSize, textNode))
}
let titleSize = self.titleNode.updateLayout(CGSize(width: contentWidth, height: CGFloat.greatestFiniteMagnitude))
contentWidth = max(contentWidth, titleSize.width)
contentHeight += titleSize.height + titleSpacing
var imageAreaHeight: CGFloat = 0.0
if let image = self.iconNode.image {
imageAreaHeight += image.size.height
imageAreaHeight += imageSpacing
transition.updateFrame(node: self.iconNode, frame: CGRect(origin: CGPoint(x: insets.left + floor((contentWidth - image.size.width) / 2.0), y: insets.top), size: image.size))
}
let contentRect = CGRect(origin: CGPoint(x: insets.left, y: insets.top + imageAreaHeight), size: CGSize(width: contentWidth, height: contentHeight))
let titleFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((contentRect.width - titleSize.width) / 2.0), y: contentRect.minY), size: titleSize)
transition.updateFrame(node: self.titleNode, frame: titleFrame)
var lineOffset = titleFrame.maxY + titleSpacing
for (textSize, textNode) in lineNodes {
let isRTL = textNode.cachedLayout?.hasRTL ?? false
transition.updateFrame(node: textNode, frame: CGRect(origin: CGPoint(x: isRTL ? contentRect.maxX - textSize.width : contentRect.minX, y: lineOffset), size: textSize))
lineOffset += textSize.height + 4.0
}
return contentRect.insetBy(dx: -insets.left, dy: -insets.top).size
}
}
private enum ChatEmptyNodeContentType {
case regular
case secret
case group
case cloud
case peerNearby
}
final class ChatEmptyNode: ASDisplayNode {
private let account: Account
private let interaction: ChatPanelInterfaceInteraction?
private let backgroundNode: ASImageNode
private var currentTheme: PresentationTheme?
private var currentStrings: PresentationStrings?
private var content: (ChatEmptyNodeContentType, ASDisplayNode & ChatEmptyNodeContent)?
init(account: Account, interaction: ChatPanelInterfaceInteraction?) {
self.account = account
self.interaction = interaction
self.backgroundNode = ASImageNode()
self.backgroundNode.isLayerBacked = true
self.backgroundNode.displayWithoutProcessing = true
self.backgroundNode.displaysAsynchronously = false
super.init()
self.isUserInteractionEnabled = false
self.addSubnode(self.backgroundNode)
}
func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) {
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
self.currentTheme = interfaceState.theme
self.currentStrings = interfaceState.strings
let graphics = PresentationResourcesChat.additionalGraphics(interfaceState.theme, wallpaper: interfaceState.chatWallpaper, bubbleCorners: interfaceState.bubbleCorners)
self.backgroundNode.image = graphics.chatEmptyItemBackgroundImage
}
let contentType: ChatEmptyNodeContentType
if let peer = interfaceState.renderedPeer?.peer, !interfaceState.isScheduledMessages {
if peer.id == self.account.peerId {
contentType = .cloud
} else if let _ = peer as? TelegramSecretChat {
contentType = .secret
} else if let group = peer as? TelegramGroup, case .creator = group.role {
contentType = .group
} else if let channel = peer as? TelegramChannel, case .group = channel.info, channel.flags.contains(.isCreator) {
contentType = .group
} else if let _ = interfaceState.peerNearbyData {
contentType = .peerNearby
} else {
contentType = .regular
}
} else {
contentType = .regular
}
var contentTransition = transition
if self.content?.0 != contentType {
if let node = self.content?.1 {
node.removeFromSupernode()
}
let node: ASDisplayNode & ChatEmptyNodeContent
switch contentType {
case .regular:
node = ChatEmptyNodeRegularChatContent()
case .secret:
node = ChatEmptyNodeSecretChatContent()
case .group:
node = ChatEmptyNodeGroupChatContent()
case .cloud:
node = ChatEmptyNodeCloudChatContent()
case .peerNearby:
node = ChatEmptyNodeNearbyChatContent(account: self.account, interaction: self.interaction)
}
self.content = (contentType, node)
self.addSubnode(node)
contentTransition = .immediate
}
self.isUserInteractionEnabled = contentType == .peerNearby
let displayRect = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top - insets.bottom))
var contentSize = CGSize()
if let contentNode = self.content?.1 {
contentSize = contentNode.updateLayout(interfaceState: interfaceState, size: displayRect.size, transition: contentTransition)
}
let contentFrame = CGRect(origin: CGPoint(x: displayRect.minX + floor((displayRect.width - contentSize.width) / 2.0), y: displayRect.minY + floor((displayRect.height - contentSize.height) / 2.0)), size: contentSize)
if let contentNode = self.content?.1 {
contentTransition.updateFrame(node: contentNode, frame: contentFrame)
}
transition.updateFrame(node: self.backgroundNode, frame: contentFrame)
}
var greetingStickerNode: ASDisplayNode? {
if let (_, node) = self.content {
if let node = node as? ChatEmptyNodeNearbyChatContent {
return node.greetingStickerNode
}
}
return nil
}
}