Swiftgram/submodules/TelegramUI/Sources/ChatTextInputActionButtonsNode.swift
2024-05-26 18:59:44 +04:00

220 lines
11 KiB
Swift

import Foundation
import UIKit
import AsyncDisplayKit
import Display
import SwiftSignalKit
import TelegramCore
import TelegramPresentationData
import ContextUI
import ChatPresentationInterfaceState
import ChatMessageBackground
import ChatControllerInteraction
import AccountContext
import ChatTextInputMediaRecordingButton
import ChatSendButtonRadialStatusNode
final class ChatTextInputActionButtonsNode: ASDisplayNode {
private let presentationContext: ChatPresentationContext?
private let strings: PresentationStrings
let micButton: ChatTextInputMediaRecordingButton
let sendContainerNode: ASDisplayNode
let backdropNode: ChatMessageBubbleBackdrop
let backgroundNode: ASDisplayNode
let sendButton: HighlightTrackingButtonNode
var sendButtonRadialStatusNode: ChatSendButtonRadialStatusNode?
var sendButtonHasApplyIcon = false
var animatingSendButton = false
let expandMediaInputButton: HighlightableButtonNode
var sendButtonLongPressed: ((ASDisplayNode, ContextGesture) -> Void)?
private var gestureRecognizer: ContextGesture?
var sendButtonLongPressEnabled = false {
didSet {
self.gestureRecognizer?.isEnabled = self.sendButtonLongPressEnabled
}
}
private var micButtonPointerInteraction: PointerInteraction?
private var sendButtonPointerInteraction: PointerInteraction?
private var validLayout: CGSize?
init(context: AccountContext, presentationInterfaceState: ChatPresentationInterfaceState, presentationContext: ChatPresentationContext?, presentController: @escaping (ViewController) -> Void) {
self.presentationContext = presentationContext
let theme = presentationInterfaceState.theme
let strings = presentationInterfaceState.strings
self.strings = strings
self.micButton = ChatTextInputMediaRecordingButton(context: context, theme: theme, pause: true, strings: strings, presentController: presentController)
self.sendContainerNode = ASDisplayNode()
self.sendContainerNode.layer.allowsGroupOpacity = true
self.backgroundNode = ASDisplayNode()
self.backgroundNode.backgroundColor = theme.chat.inputPanel.actionControlFillColor
self.backgroundNode.clipsToBounds = true
self.backdropNode = ChatMessageBubbleBackdrop()
self.sendButton = HighlightTrackingButtonNode(pointerStyle: nil)
self.expandMediaInputButton = HighlightableButtonNode(pointerStyle: .circle(36.0))
super.init()
self.isAccessibilityElement = true
self.accessibilityTraits = [.button, .notEnabled]
self.sendButton.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if !strongSelf.sendButtonLongPressEnabled {
if highlighted {
strongSelf.sendContainerNode.layer.removeAnimation(forKey: "opacity")
strongSelf.sendContainerNode.alpha = 0.4
} else {
strongSelf.sendContainerNode.alpha = 1.0
strongSelf.sendContainerNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
}
} else {
if highlighted {
strongSelf.sendContainerNode.layer.animateScale(from: 1.0, to: 0.75, duration: 0.4, removeOnCompletion: false)
} else if let presentationLayer = strongSelf.sendContainerNode.layer.presentation() {
strongSelf.sendContainerNode.layer.animateScale(from: CGFloat((presentationLayer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0), to: 1.0, duration: 0.25, removeOnCompletion: false)
}
}
}
}
self.micButton.layer.allowsGroupOpacity = true
self.view.addSubview(self.micButton)
self.addSubnode(self.sendContainerNode)
self.sendContainerNode.addSubnode(self.backgroundNode)
if let presentationContext = presentationContext {
let graphics = PresentationResourcesChat.principalGraphics(theme: theme, wallpaper: presentationInterfaceState.chatWallpaper, bubbleCorners: presentationInterfaceState.bubbleCorners)
self.backdropNode.setType(type: .outgoing(.None), theme: ChatPresentationThemeData(theme: theme, wallpaper: presentationInterfaceState.chatWallpaper), essentialGraphics: graphics, maskMode: true, backgroundNode: presentationContext.backgroundNode)
self.backgroundNode.addSubnode(self.backdropNode)
}
self.sendContainerNode.addSubnode(self.sendButton)
self.addSubnode(self.expandMediaInputButton)
}
override func didLoad() {
super.didLoad()
let gestureRecognizer = ContextGesture(target: nil, action: nil)
self.gestureRecognizer = gestureRecognizer
self.sendButton.view.addGestureRecognizer(gestureRecognizer)
gestureRecognizer.activated = { [weak self] recognizer, _ in
guard let strongSelf = self else {
return
}
strongSelf.sendButtonLongPressed?(strongSelf, recognizer)
}
self.micButtonPointerInteraction = PointerInteraction(view: self.micButton, style: .circle(36.0))
self.sendButtonPointerInteraction = PointerInteraction(view: self.sendButton.view, customInteractionView: self.backgroundNode.view, style: .lift)
}
func updateTheme(theme: PresentationTheme, wallpaper: TelegramWallpaper) {
self.micButton.updateTheme(theme: theme)
self.expandMediaInputButton.setImage(PresentationResourcesChat.chatInputPanelExpandButtonImage(theme), for: [])
self.backgroundNode.backgroundColor = theme.chat.inputPanel.actionControlFillColor
if [.day, .night].contains(theme.referenceTheme.baseTheme) && !theme.chat.message.outgoing.bubble.withWallpaper.hasSingleFillColor {
self.backdropNode.isHidden = false
} else {
self.backdropNode.isHidden = true
}
let graphics = PresentationResourcesChat.principalGraphics(theme: theme, wallpaper: wallpaper, bubbleCorners: .init(mainRadius: 1, auxiliaryRadius: 1, mergeBubbleCorners: false))
self.backdropNode.setType(type: .outgoing(.None), theme: ChatPresentationThemeData(theme: theme, wallpaper: wallpaper), essentialGraphics: graphics, maskMode: false, backgroundNode: self.presentationContext?.backgroundNode)
}
private var absoluteRect: (CGRect, CGSize)?
func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
let previousContaierSize = self.absoluteRect?.1
self.absoluteRect = (rect, containerSize)
self.backdropNode.update(rect: rect, within: containerSize, transition: transition)
if let previousContaierSize, previousContaierSize != containerSize {
Queue.mainQueue().after(0.2) {
self.micButton.reset()
}
}
}
func updateLayout(size: CGSize, isMediaInputExpanded: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) {
self.validLayout = size
transition.updateFrame(layer: self.micButton.layer, frame: CGRect(origin: CGPoint(), size: size))
self.micButton.layoutItems()
transition.updateFrame(layer: self.sendButton.layer, frame: CGRect(origin: CGPoint(), size: size))
transition.updateFrame(node: self.sendContainerNode, frame: CGRect(origin: CGPoint(), size: size))
let backgroundSize = CGSize(width: 33.0, height: 33.0)
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - backgroundSize.width) / 2.0), y: floorToScreenPixels((size.height - backgroundSize.height) / 2.0)), size: backgroundSize))
self.backgroundNode.cornerRadius = backgroundSize.width / 2.0
transition.updateFrame(node: self.backdropNode, frame: CGRect(origin: CGPoint(x: -2.0, y: -2.0), size: CGSize(width: size.width + 12.0, height: size.height + 2.0)))
if let (rect, containerSize) = self.absoluteRect {
self.backdropNode.update(rect: rect, within: containerSize)
}
// var isScheduledMessages = false
// if case .scheduledMessages = interfaceState.subject {
// isScheduledMessages = true
// }
//
// if let slowmodeState = interfaceState.slowmodeState, !isScheduledMessages && interfaceState.editMessageState == nil {
// let sendButtonRadialStatusNode: ChatSendButtonRadialStatusNode
// if let current = self.sendButtonRadialStatusNode {
// sendButtonRadialStatusNode = current
// } else {
// sendButtonRadialStatusNode = ChatSendButtonRadialStatusNode(color: interfaceState.theme.chat.inputPanel.panelControlAccentColor)
// sendButtonRadialStatusNode.alpha = self.sendContainerNode.alpha
// self.sendButtonRadialStatusNode = sendButtonRadialStatusNode
// self.addSubnode(sendButtonRadialStatusNode)
// }
//
// transition.updateSublayerTransformScale(layer: self.sendContainerNode.layer, scale: CGPoint(x: 0.7575, y: 0.7575))
//
// let defaultSendButtonSize: CGFloat = 25.0
// let defaultOriginX = floorToScreenPixels((self.sendButton.bounds.width - defaultSendButtonSize) / 2.0)
// let defaultOriginY = floorToScreenPixels((self.sendButton.bounds.height - defaultSendButtonSize) / 2.0)
//
// let radialStatusFrame = CGRect(origin: CGPoint(x: defaultOriginX - 4.0, y: defaultOriginY - 4.0), size: CGSize(width: 33.0, height: 33.0))
// sendButtonRadialStatusNode.frame = radialStatusFrame
// sendButtonRadialStatusNode.slowmodeState = slowmodeState
// } else {
// if let sendButtonRadialStatusNode = self.sendButtonRadialStatusNode {
// self.sendButtonRadialStatusNode = nil
// sendButtonRadialStatusNode.removeFromSupernode()
// }
// transition.updateSublayerTransformScale(layer: self.sendContainerNode.layer, scale: CGPoint(x: 1.0, y: 1.0))
// }
transition.updateFrame(node: self.expandMediaInputButton, frame: CGRect(origin: CGPoint(), size: size))
let expanded = isMediaInputExpanded
transition.updateSublayerTransformScale(node: self.expandMediaInputButton, scale: CGPoint(x: 1.0, y: expanded ? 1.0 : -1.0))
}
func updateAccessibility() {
self.accessibilityTraits = .button
if !self.micButton.alpha.isZero {
switch self.micButton.mode {
case .audio:
self.accessibilityLabel = self.strings.VoiceOver_Chat_RecordModeVoiceMessage
self.accessibilityHint = self.strings.VoiceOver_Chat_RecordModeVoiceMessageInfo
case .video:
self.accessibilityLabel = self.strings.VoiceOver_Chat_RecordModeVideoMessage
self.accessibilityHint = self.strings.VoiceOver_Chat_RecordModeVideoMessageInfo
}
} else {
self.accessibilityLabel = self.strings.MediaPicker_Send
self.accessibilityHint = nil
}
}
}