Swiftgram/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift
2025-09-23 20:49:12 +08:00

219 lines
13 KiB
Swift

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)
var totalWidth = textSize.width
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
totalWidth += 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)
if let iconView {
combinedFrame = combinedFrame.union(iconView.frame)
}
combinedFrame = combinedFrame.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)
}
}