Various improvements

This commit is contained in:
Isaac
2025-09-19 15:36:36 +04:00
parent 1d5de1be39
commit d4a4305f5b
21 changed files with 417 additions and 238 deletions

View File

@@ -22,6 +22,9 @@ swift_library(
"//submodules/TelegramUI/Components/EntityKeyboard",
"//submodules/TelegramUI/Components/Chat/TopMessageReactions",
"//submodules/ReactionSelectionNode",
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
"//submodules/ComponentFlow",
"//submodules/Components/ComponentDisplayAdapters",
],
visibility = [
"//visibility:public",

View File

@@ -13,6 +13,9 @@ import ChatInputPanelNode
import ReactionSelectionNode
import EntityKeyboard
import TopMessageReactions
import GlassBackgroundComponent
import ComponentFlow
import ComponentDisplayAdapters
private final class ChatMessageSelectionInputPanelNodeViewForOverlayContent: UIView, ChatInputPanelViewForOverlayContent {
var reactionContextNode: ReactionContextNode?
@@ -61,14 +64,125 @@ private final class ChatMessageSelectionInputPanelNodeViewForOverlayContent: UIV
}
}
private final class GlassButtonView: HighlightTrackingButton {
private struct Params: Equatable {
let theme: PresentationTheme
let size: CGSize
init(theme: PresentationTheme, size: CGSize) {
self.theme = theme
self.size = size
}
static func ==(lhs: Params, rhs: Params) -> Bool {
if lhs.theme !== rhs.theme {
return false
}
if lhs.size != rhs.size {
return false
}
return true
}
}
private let backgroundView: GlassBackgroundView
private let iconView: GlassBackgroundView.ContentImageView
private var params: Params?
var isImplicitlyDisabled: Bool = false {
didSet {
self.updateIsEnabled()
}
}
override var isEnabled: Bool {
didSet {
self.updateIsEnabled()
}
}
var icon: String? {
didSet {
if self.icon == oldValue {
return
}
if let icon = self.icon {
self.iconView.image = UIImage(bundleImageName: icon)?.withRenderingMode(.alwaysTemplate)
} else {
self.iconView.image = nil
}
if let params = self.params {
self.updateImpl(params: params, transition: .immediate)
}
}
}
override init(frame: CGRect) {
self.backgroundView = GlassBackgroundView()
self.backgroundView.isUserInteractionEnabled = false
self.iconView = GlassBackgroundView.ContentImageView()
self.backgroundView.contentView.addSubview(self.iconView)
super.init(frame: frame)
self.addSubview(self.backgroundView)
self.highligthedChanged = { [weak self] highlighted in
guard let self else {
return
}
if highlighted && self.isEnabled && !self.isImplicitlyDisabled {
self.backgroundView.contentView.alpha = 0.6
} else {
self.backgroundView.contentView.alpha = 1.0
self.backgroundView.contentView.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2)
}
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(theme: PresentationTheme, size: CGSize, transition: ComponentTransition) {
let params = Params(theme: theme, size: size)
if self.params != params {
self.iconView.tintColor = params.theme.chat.inputPanel.inputControlColor
self.params = params
self.updateImpl(params: params, transition: transition)
}
}
private func updateImpl(params: Params, transition: ComponentTransition) {
if let image = self.iconView.image {
let iconFrame = image.size.centered(in: CGRect(origin: CGPoint(), size: params.size))
transition.setFrame(view: self.iconView, frame: iconFrame)
}
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: params.size))
self.backgroundView.update(size: params.size, cornerRadius: min(params.size.width, params.size.height) * 0.5, isDark: params.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: params.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: transition)
let isEnabled = self.isEnabled && !self.isImplicitlyDisabled
self.iconView.alpha = isEnabled ? 1.0 : 0.5
self.iconView.tintMask.alpha = self.iconView.alpha
}
private func updateIsEnabled() {
if let params = self.params {
self.updateImpl(params: params, transition: .immediate)
}
}
}
public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
private let deleteButton: HighlightableButtonNode
private let reportButton: HighlightableButtonNode
private let forwardButton: HighlightableButtonNode
private let shareButton: HighlightableButtonNode
private let tagButton: HighlightableButtonNode
private let tagEditButton: HighlightableButtonNode
private let separatorNode: ASDisplayNode
private let deleteButton: GlassButtonView
private let reportButton: GlassButtonView
private let forwardButton: GlassButtonView
private let shareButton: GlassButtonView
private let tagButton: GlassButtonView
private let tagEditButton: GlassButtonView
private let reactionOverlayContainer: ChatMessageSelectionInputPanelNodeViewForOverlayContent
@@ -93,69 +207,60 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
self.theme = theme
self.peerMedia = peerMedia
self.deleteButton = HighlightableButtonNode(pointerStyle: .rectangle(CGSize(width: 56.0, height: 40.0)))
self.deleteButton = GlassButtonView()
self.deleteButton.icon = "Chat/Input/Accessory Panels/MessageSelectionTrash"
self.deleteButton.isEnabled = false
self.deleteButton.isAccessibilityElement = true
self.deleteButton.accessibilityLabel = strings.VoiceOver_MessageContextDelete
self.reportButton = HighlightableButtonNode(pointerStyle: .rectangle(CGSize(width: 56.0, height: 40.0)))
self.reportButton = GlassButtonView()
self.reportButton.icon = "Chat/Input/Accessory Panels/MessageSelectionReport"
self.reportButton.isEnabled = false
self.reportButton.isAccessibilityElement = true
self.reportButton.accessibilityLabel = strings.VoiceOver_MessageContextReport
self.forwardButton = HighlightableButtonNode(pointerStyle: .rectangle(CGSize(width: 56.0, height: 40.0)))
self.forwardButton = GlassButtonView()
self.forwardButton.icon = "Chat/Input/Accessory Panels/MessageSelectionForward"
self.forwardButton.isAccessibilityElement = true
self.forwardButton.accessibilityLabel = strings.VoiceOver_MessageContextForward
self.shareButton = HighlightableButtonNode(pointerStyle: .rectangle(CGSize(width: 56.0, height: 40.0)))
self.shareButton = GlassButtonView()
self.shareButton.icon = "Chat/Input/Accessory Panels/MessageSelectionAction"
self.shareButton.isAccessibilityElement = true
self.shareButton.accessibilityLabel = strings.VoiceOver_MessageContextShare
self.tagButton = HighlightableButtonNode(pointerStyle: .rectangle(CGSize(width: 56.0, height: 40.0)))
self.tagButton = GlassButtonView()
self.tagButton.icon = "Chat/Input/Accessory Panels/TagIcon"
self.tagButton.isAccessibilityElement = true
self.tagButton.accessibilityLabel = strings.VoiceOver_MessageSelectionButtonTag
self.tagEditButton = HighlightableButtonNode(pointerStyle: .rectangle(CGSize(width: 56.0, height: 40.0)))
self.tagEditButton = GlassButtonView()
self.tagEditButton.icon = "Chat/Input/Accessory Panels/TagEditIcon"
self.tagEditButton.isAccessibilityElement = true
self.tagEditButton.accessibilityLabel = strings.VoiceOver_MessageSelectionButtonTag
self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
self.reportButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionReport"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.reportButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionReport"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
self.forwardButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.forwardButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
self.tagButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/TagIcon"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.tagEditButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/TagEditIcon"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.separatorNode = ASDisplayNode()
self.separatorNode.backgroundColor = theme.chat.inputPanel.panelSeparatorColor
self.reactionOverlayContainer = ChatMessageSelectionInputPanelNodeViewForOverlayContent()
super.init()
self.addSubnode(self.deleteButton)
self.addSubnode(self.reportButton)
self.addSubnode(self.forwardButton)
self.addSubnode(self.shareButton)
self.addSubnode(self.tagButton)
self.addSubnode(self.tagEditButton)
self.addSubnode(self.separatorNode)
self.view.addSubview(self.deleteButton)
self.view.addSubview(self.reportButton)
self.view.addSubview(self.forwardButton)
self.view.addSubview(self.shareButton)
self.view.addSubview(self.tagButton)
self.view.addSubview(self.tagEditButton)
self.viewForOverlayContent = self.reactionOverlayContainer
self.forwardButton.isImplicitlyDisabled = true
self.shareButton.isImplicitlyDisabled = true
self.deleteButton.addTarget(self, action: #selector(self.deleteButtonPressed), forControlEvents: .touchUpInside)
self.reportButton.addTarget(self, action: #selector(self.reportButtonPressed), forControlEvents: .touchUpInside)
self.forwardButton.addTarget(self, action: #selector(self.forwardButtonPressed), forControlEvents: .touchUpInside)
self.shareButton.addTarget(self, action: #selector(self.shareButtonPressed), forControlEvents: .touchUpInside)
self.tagButton.addTarget(self, action: #selector(self.tagButtonPressed), forControlEvents: .touchUpInside)
self.tagEditButton.addTarget(self, action: #selector(self.tagButtonPressed), forControlEvents: .touchUpInside)
self.deleteButton.addTarget(self, action: #selector(self.deleteButtonPressed), for: .touchUpInside)
self.reportButton.addTarget(self, action: #selector(self.reportButtonPressed), for: .touchUpInside)
self.forwardButton.addTarget(self, action: #selector(self.forwardButtonPressed), for: .touchUpInside)
self.shareButton.addTarget(self, action: #selector(self.shareButtonPressed), for: .touchUpInside)
self.tagButton.addTarget(self, action: #selector(self.tagButtonPressed), for: .touchUpInside)
self.tagEditButton.addTarget(self, action: #selector(self.tagButtonPressed), for: .touchUpInside)
}
deinit {
@@ -187,19 +292,6 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
public func updateTheme(theme: PresentationTheme) {
if self.theme !== theme {
self.theme = theme
self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.deleteButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
self.reportButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionReport"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.reportButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionReport"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
self.forwardButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.forwardButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.shareButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: theme.chat.inputPanel.panelControlDisabledColor), for: [.disabled])
self.tagButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/WebpageIcon"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.tagEditButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/LinkSettingsIcon"), color: theme.chat.inputPanel.panelControlAccentColor), for: [.normal])
self.separatorNode.backgroundColor = theme.chat.inputPanel.panelSeparatorColor
}
}
@@ -356,6 +448,12 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
override public 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 {
self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, maxOverlayHeight, metrics, isSecondary, isMediaInputExpanded)
var leftInset = leftInset
leftInset += 8.0
var rightInset = rightInset
rightInset += 8.0
let panelHeight = defaultHeight(metrics: metrics)
if self.presentationInterfaceState != interfaceState {
@@ -419,14 +517,14 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
width -= additionalSideInsets.right
}
var tagButton: HighlightableButtonNode?
var tagButton: GlassButtonView?
if !self.tagButton.isHidden {
tagButton = self.tagButton
} else if !self.tagEditButton.isHidden {
tagButton = self.tagEditButton
}
let buttons: [HighlightableButtonNode]
let buttons: [GlassButtonView]
if self.reportButton.isHidden {
if let tagButton {
buttons = [
@@ -478,24 +576,25 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
}
}
let buttonSize = CGSize(width: 57.0, height: panelHeight)
let buttonSize = CGSize(width: 40.0, height: 40.0)
let availableWidth = width - leftInset - rightInset
let spacing: CGFloat = floor((availableWidth - buttonSize.width * CGFloat(buttons.count)) / CGFloat(buttons.count - 1))
var offset: CGFloat = leftInset
for i in 0 ..< buttons.count {
let button = buttons[i]
let buttonFrame: CGRect
if i == buttons.count - 1 {
button.frame = CGRect(origin: CGPoint(x: width - rightInset - buttonSize.width, y: 0.0), size: buttonSize)
buttonFrame = CGRect(origin: CGPoint(x: width - rightInset - buttonSize.width, y: 0.0), size: buttonSize)
} else {
button.frame = CGRect(origin: CGPoint(x: offset, y: 0.0), size: buttonSize)
buttonFrame = CGRect(origin: CGPoint(x: offset, y: 0.0), size: buttonSize)
}
transition.updateFrame(view: button, frame: buttonFrame)
button.update(theme: interfaceState.theme, size: buttonFrame.size, transition: ComponentTransition(transition))
offset += buttonSize.width + spacing
}
transition.updateAlpha(node: self.separatorNode, alpha: isSecondary ? 1.0 : 0.0)
self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: panelHeight), size: CGSize(width: width, height: UIScreenPixel))
if let reactionContextNode = self.reactionOverlayContainer.reactionContextNode, let tagButton {
let isFirstTime = reactionContextNode.bounds.isEmpty