import Foundation import UIKit import AsyncDisplayKit import Display import TelegramCore import Postbox import SwiftSignalKit import TelegramStringFormatting import ChatPresentationInterfaceState import TelegramPresentationData import ChatInputPanelNode import AccountContext import GlassBackgroundComponent import ComponentFlow import ComponentDisplayAdapters final class ChatRestrictedInputPanelNode: ChatInputPanelNode { private let backgroundView: GlassBackgroundView private let buttonNode: HighlightTrackingButtonNode private let textNode: ImmediateTextNode private let tintTextNode: ImmediateTextNode private let subtitleNode: ImmediateTextNode private let tintSubtitleNode: ImmediateTextNode private var iconView: UIImageView? private var presentationInterfaceState: ChatPresentationInterfaceState? override init() { self.backgroundView = GlassBackgroundView() self.textNode = ImmediateTextNode() self.textNode.maximumNumberOfLines = 2 self.textNode.textAlignment = .center self.tintTextNode = ImmediateTextNode() self.tintTextNode.maximumNumberOfLines = 2 self.tintTextNode.textAlignment = .center self.subtitleNode = ImmediateTextNode() self.subtitleNode.maximumNumberOfLines = 1 self.subtitleNode.textAlignment = .center self.tintSubtitleNode = ImmediateTextNode() self.tintSubtitleNode.maximumNumberOfLines = 1 self.tintSubtitleNode.textAlignment = .center self.buttonNode = HighlightTrackingButtonNode() self.buttonNode.isUserInteractionEnabled = false super.init() self.backgroundView.contentView.addSubview(self.textNode.view) self.backgroundView.maskContentView.addSubview(self.tintTextNode.view) self.backgroundView.contentView.addSubview(self.subtitleNode.view) self.backgroundView.maskContentView.addSubview(self.tintSubtitleNode.view) self.view.addSubview(self.backgroundView) self.addSubnode(self.buttonNode) self.buttonNode.highligthedChanged = { [weak self] highlighted in if let self { if highlighted { self.iconView?.layer.removeAnimation(forKey: "opacity") self.iconView?.alpha = 0.4 self.textNode.layer.removeAnimation(forKey: "opacity") self.textNode.alpha = 0.4 self.subtitleNode.layer.removeAnimation(forKey: "opacity") self.subtitleNode.alpha = 0.4 } else { self.iconView?.alpha = 1.0 self.iconView?.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) self.textNode.alpha = 1.0 self.textNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) self.subtitleNode.alpha = 1.0 self.subtitleNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) } } } self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) } @objc private func buttonPressed() { self.interfaceInteraction?.openBoostToUnrestrict() } override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, maxOverlayHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat { if self.presentationInterfaceState != interfaceState { self.presentationInterfaceState = interfaceState } var bannedPermission: (Int32, Bool)? if let channel = interfaceState.renderedPeer?.peer as? TelegramChannel { if let value = channel.hasBannedPermission(.banSendText) { bannedPermission = value } else if !channel.hasPermission(.sendSomething) { bannedPermission = (Int32.max, false) } } else if let group = interfaceState.renderedPeer?.peer as? TelegramGroup { if !group.hasPermission(.sendSomething) { bannedPermission = (Int32.max, false) } } var iconImage: UIImage? var iconSpacing: CGFloat = 4.0 var isUserInteractionEnabled = false var accountFreezeConfiguration: AccountFreezeConfiguration? if let context = self.context { accountFreezeConfiguration = AccountFreezeConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) } if let channel = interfaceState.renderedPeer?.chatMainPeer as? TelegramChannel, channel.isMonoForum { self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelForumModeReplyText, font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) } else if let _ = accountFreezeConfiguration?.freezeUntilDate { self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelFrozenAccount_Title, font: Font.semibold(15.0), textColor: interfaceState.theme.list.itemDestructiveColor) self.subtitleNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelFrozenAccount_Text, font: Font.regular(13.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) isUserInteractionEnabled = true } else if case let .replyThread(message) = interfaceState.chatLocation, message.peerId == self.context?.account.peerId { self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelStatusAuthorHidden, font: Font.regular(13.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) } else if let threadData = interfaceState.threadData, threadData.isClosed { iconImage = PresentationResourcesChat.chatPanelLockIcon(interfaceState.theme) self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelTopicClosedText, font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) } else if let channel = interfaceState.renderedPeer?.peer as? TelegramChannel, channel.isForumOrMonoForum, case .peer = interfaceState.chatLocation { if let replyMessage = interfaceState.replyMessage, let threadInfo = replyMessage.associatedThreadInfo { self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_TopicIsClosedLabel(threadInfo.title).string, font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) } else { self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_PanelForumModeReplyText, font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) } } else if let (untilDate, personal) = bannedPermission { if personal && untilDate != 0 && untilDate != Int32.max { self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Conversation_RestrictedTextTimed(stringForFullDate(timestamp: untilDate, strings: interfaceState.strings, dateTimeFormat: interfaceState.dateTimeFormat)).string, font: Font.regular(13.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) } else if personal { self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Conversation_RestrictedText, font: Font.regular(13.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) } else { if (self.presentationInterfaceState?.boostsToUnrestrict ?? 0) > 0 { iconSpacing = 0.0 iconImage = PresentationResourcesChat.chatPanelBoostIcon(interfaceState.theme) self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Conversation_BoostToUnrestrictText, font: Font.regular(15.0), textColor: interfaceState.theme.chat.inputPanel.panelControlAccentColor) isUserInteractionEnabled = true } else { self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Conversation_DefaultRestrictedText, font: Font.regular(13.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) } } } else if case let .customChatContents(customChatContents) = interfaceState.subject { let displayCount: Int switch customChatContents.kind { case .hashTagSearch: displayCount = 0 case .quickReplyMessageInput: displayCount = customChatContents.messageLimit ?? 20 case .businessLinkSetup: displayCount = 0 } self.textNode.attributedText = NSAttributedString(string: interfaceState.strings.Chat_QuickReplyMessageLimitReachedText(Int32(displayCount)), font: Font.regular(13.0), textColor: interfaceState.theme.chat.inputPanel.secondaryTextColor) } self.buttonNode.isUserInteractionEnabled = isUserInteractionEnabled self.tintTextNode.attributedText = NSAttributedString(string: self.textNode.attributedText?.string ?? "", font: Font.regular(15.0), textColor: .black) self.tintSubtitleNode.attributedText = NSAttributedString(string: self.subtitleNode.attributedText?.string ?? "", font: Font.regular(13.0), textColor: .black) let panelHeight = defaultHeight(metrics: metrics) let textSize = self.textNode.updateLayout(CGSize(width: width - leftInset - rightInset - 8.0 * 2.0, height: panelHeight)) let subtitleSize = self.subtitleNode.updateLayout(CGSize(width: width - leftInset - rightInset - 8.0 * 2.0, height: panelHeight)) var originX: CGFloat = leftInset + floor((width - leftInset - rightInset - textSize.width) / 2.0) if let iconImage { let iconView: UIImageView if let current = self.iconView { iconView = current } else { iconView = UIImageView() self.iconView = iconView self.view.addSubview(iconView) } iconView.image = iconImage let totalWidth = textSize.width + iconImage.size.width + iconSpacing iconView.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - totalWidth) / 2.0), y: floor((panelHeight - textSize.height) / 2.0) + UIScreenPixel + floorToScreenPixels((textSize.height - iconImage.size.height) / 2.0)), size: iconImage.size) originX += iconImage.size.width + iconSpacing } else if let iconView = self.iconView { self.iconView = nil iconView.removeFromSuperview() } var combinedHeight: CGFloat = textSize.height if subtitleSize.height > 0.0 { combinedHeight += subtitleSize.height + 2.0 } let textFrame = CGRect(origin: CGPoint(x: originX, y: floor((panelHeight - combinedHeight) / 2.0)), size: textSize) let subtitleFrame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - subtitleSize.width) / 2.0), y: floor((panelHeight + combinedHeight) / 2.0) - subtitleSize.height), size: subtitleSize) var combinedFrame = textFrame.union(subtitleFrame).insetBy(dx: -12.0, dy: -6.0) combinedFrame.origin.y += 1.0 self.textNode.frame = textFrame.offsetBy(dx: -combinedFrame.minX, dy: -combinedFrame.minY) self.tintTextNode.frame = self.textNode.frame self.subtitleNode.frame = subtitleFrame.offsetBy(dx: -combinedFrame.minX, dy: -combinedFrame.minY) self.tintSubtitleNode.frame = self.subtitleNode.frame self.backgroundView.frame = combinedFrame self.backgroundView.update(size: combinedFrame.size, cornerRadius: combinedFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: ComponentTransition(transition)) self.buttonNode.frame = combinedFrame return panelHeight } override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { return defaultHeight(metrics: metrics) } }