Text input updates

This commit is contained in:
Isaac 2025-10-03 18:47:47 +08:00
parent c962e6f56a
commit 06936fa06c
11 changed files with 235 additions and 152 deletions

View File

@ -39,6 +39,22 @@ public final class ChatInputAccessoryPanelEnvironment: Equatable {
}
}
public final class ChatInputAccessoryPanelTransitionData {
public let titleView: UIView
public let textView: UIView
public let lineView: UIView
public let imageView: UIView?
public init(titleView: UIView, textView: UIView, lineView: UIView, imageView: UIView?) {
self.titleView = titleView
self.textView = textView
self.lineView = lineView
self.imageView = imageView
}
}
public protocol ChatInputAccessoryPanelView: UIView {
var contentTintView: UIView { get }
var storedFrameBeforeDismissed: CGRect? { get set }
var transitionData: ChatInputAccessoryPanelTransitionData? { get }
}

View File

@ -276,6 +276,20 @@ public final class ChatInputMessageAccessoryPanel: Component {
private var inlineTextStarImage: UIImage?
private var inlineTextTonImage: (UIImage, UIColor)?
public var transitionData: ChatInputAccessoryPanelTransitionData? {
guard let textView = self.text.view else {
return nil
}
return ChatInputAccessoryPanelTransitionData(
titleView: self.titleNode.view,
textView: textView,
lineView: self.lineView,
imageView: nil
)
}
public var storedFrameBeforeDismissed: CGRect?
override public init(frame: CGRect) {
self.contentTintView = UIView()

View File

@ -3044,18 +3044,18 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
public final class AnimationTransitionReplyPanel {
public let titleNode: ASDisplayNode
public let textNode: ASDisplayNode
public let lineNode: ASDisplayNode
public let imageNode: ASDisplayNode
public let titleView: UIView
public let textView: UIView
public let lineView: UIView
public let imageView: UIView?
public let relativeSourceRect: CGRect
public let relativeTargetRect: CGRect
public init(titleNode: ASDisplayNode, textNode: ASDisplayNode, lineNode: ASDisplayNode, imageNode: ASDisplayNode, relativeSourceRect: CGRect, relativeTargetRect: CGRect) {
self.titleNode = titleNode
self.textNode = textNode
self.lineNode = lineNode
self.imageNode = imageNode
public init(titleView: UIView, textView: UIView, lineView: UIView, imageView: UIView?, relativeSourceRect: CGRect, relativeTargetRect: CGRect) {
self.titleView = titleView
self.textView = textView
self.lineView = lineView
self.imageView = imageView
self.relativeSourceRect = relativeSourceRect
self.relativeTargetRect = relativeTargetRect
}
@ -3066,10 +3066,10 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
let localRect = self.contextSourceNode.contentNode.view.convert(sourceReplyPanel.relativeSourceRect, to: replyInfoNode.view)
let mappedPanel = ChatMessageReplyInfoNode.TransitionReplyPanel(
titleNode: sourceReplyPanel.titleNode,
textNode: sourceReplyPanel.textNode,
lineNode: sourceReplyPanel.lineNode,
imageNode: sourceReplyPanel.imageNode,
titleView: sourceReplyPanel.titleView,
textView: sourceReplyPanel.textView,
lineView: sourceReplyPanel.lineView,
imageView: sourceReplyPanel.imageView,
relativeSourceRect: sourceReplyPanel.relativeSourceRect,
relativeTargetRect: sourceReplyPanel.relativeTargetRect
)

View File

@ -1094,18 +1094,18 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
}
public final class AnimationTransitionReplyPanel {
public let titleNode: ASDisplayNode
public let textNode: ASDisplayNode
public let lineNode: ASDisplayNode
public let imageNode: ASDisplayNode
public let titleView: UIView
public let textView: UIView
public let lineView: UIView
public let imageView: UIView?
public let relativeSourceRect: CGRect
public let relativeTargetRect: CGRect
public init(titleNode: ASDisplayNode, textNode: ASDisplayNode, lineNode: ASDisplayNode, imageNode: ASDisplayNode, relativeSourceRect: CGRect, relativeTargetRect: CGRect) {
self.titleNode = titleNode
self.textNode = textNode
self.lineNode = lineNode
self.imageNode = imageNode
public init(titleView: UIView, textView: UIView, lineView: UIView, imageView: UIView?, relativeSourceRect: CGRect, relativeTargetRect: CGRect) {
self.titleView = titleView
self.textView = textView
self.lineView = lineView
self.imageView = imageView
self.relativeSourceRect = relativeSourceRect
self.relativeTargetRect = relativeTargetRect
}
@ -1115,10 +1115,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
if let replyInfoNode = self.replyInfoNode {
let localRect = self.mainContextSourceNode.contentNode.view.convert(sourceReplyPanel.relativeSourceRect, to: replyInfoNode.view)
let mappedPanel = ChatMessageReplyInfoNode.TransitionReplyPanel(
titleNode: sourceReplyPanel.titleNode,
textNode: sourceReplyPanel.textNode,
lineNode: sourceReplyPanel.lineNode,
imageNode: sourceReplyPanel.imageNode,
titleView: sourceReplyPanel.titleView,
textView: sourceReplyPanel.textView,
lineView: sourceReplyPanel.lineView,
imageView: sourceReplyPanel.imageView,
relativeSourceRect: sourceReplyPanel.relativeSourceRect,
relativeTargetRect: sourceReplyPanel.relativeTargetRect
)

View File

@ -58,18 +58,18 @@ private let groupIcon: UIImage = {
public class ChatMessageReplyInfoNode: ASDisplayNode {
public final class TransitionReplyPanel {
public let titleNode: ASDisplayNode
public let textNode: ASDisplayNode
public let lineNode: ASDisplayNode
public let imageNode: ASDisplayNode
public let titleView: UIView
public let textView: UIView
public let lineView: UIView
public let imageView: UIView?
public let relativeSourceRect: CGRect
public let relativeTargetRect: CGRect
public init(titleNode: ASDisplayNode, textNode: ASDisplayNode, lineNode: ASDisplayNode, imageNode: ASDisplayNode, relativeSourceRect: CGRect, relativeTargetRect: CGRect) {
self.titleNode = titleNode
self.textNode = textNode
self.lineNode = lineNode
self.imageNode = imageNode
public init(titleView: UIView, textView: UIView, lineView: UIView, imageView: UIView?, relativeSourceRect: CGRect, relativeTargetRect: CGRect) {
self.titleView = titleView
self.textView = textView
self.lineView = lineView
self.imageView = imageView
self.relativeSourceRect = relativeSourceRect
self.relativeTargetRect = relativeTargetRect
}
@ -971,84 +971,88 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
if let titleNode = self.titleNode {
let offset = CGPoint(
x: localRect.minX + sourceReplyPanel.titleNode.frame.minX - titleNode.frame.minX,
y: localRect.minY + sourceReplyPanel.titleNode.frame.midY - titleNode.frame.midY
x: localRect.minX + sourceReplyPanel.titleView.frame.minX - titleNode.frame.minX,
y: localRect.minY + sourceReplyPanel.titleView.frame.midY - titleNode.frame.midY
)
transition.horizontal.animatePositionAdditive(node: titleNode, offset: CGPoint(x: offset.x, y: 0.0))
transition.vertical.animatePositionAdditive(node: titleNode, offset: CGPoint(x: 0.0, y: offset.y))
sourceParentNode.addSubnode(sourceReplyPanel.titleNode)
sourceParentNode.view.addSubview(sourceReplyPanel.titleView)
titleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
sourceReplyPanel.titleNode.frame = sourceReplyPanel.titleNode.frame
sourceReplyPanel.titleView.frame = sourceReplyPanel.titleView.frame
.offsetBy(dx: sourceParentOffset.x, dy: sourceParentOffset.y)
.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y)
transition.horizontal.animatePositionAdditive(node: sourceReplyPanel.titleNode, offset: CGPoint(x: offset.x, y: 0.0), removeOnCompletion: false)
transition.vertical.animatePositionAdditive(node: sourceReplyPanel.titleNode, offset: CGPoint(x: 0.0, y: offset.y), removeOnCompletion: false)
transition.horizontal.animatePositionAdditive(layer: sourceReplyPanel.titleView.layer, offset: CGPoint(x: offset.x, y: 0.0), removeOnCompletion: false)
transition.vertical.animatePositionAdditive(layer: sourceReplyPanel.titleView.layer, offset: CGPoint(x: 0.0, y: offset.y), removeOnCompletion: false)
}
if let textNode = self.textNode {
let offset = CGPoint(
x: localRect.minX + sourceReplyPanel.textNode.frame.minX - textNode.textNode.frame.minX,
y: localRect.minY + sourceReplyPanel.textNode.frame.midY - textNode.textNode.frame.midY
x: localRect.minX + sourceReplyPanel.textView.frame.minX - textNode.textNode.frame.minX,
y: localRect.minY + sourceReplyPanel.textView.frame.midY - textNode.textNode.frame.midY
)
transition.horizontal.animatePositionAdditive(node: textNode.textNode, offset: CGPoint(x: offset.x, y: 0.0))
transition.vertical.animatePositionAdditive(node: textNode.textNode, offset: CGPoint(x: 0.0, y: offset.y))
sourceParentNode.addSubnode(sourceReplyPanel.textNode)
sourceParentNode.view.addSubview(sourceReplyPanel.textView)
textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
sourceReplyPanel.textNode.frame = sourceReplyPanel.textNode.frame
sourceReplyPanel.textView.frame = sourceReplyPanel.textView.frame
.offsetBy(dx: sourceParentOffset.x, dy: sourceParentOffset.y)
.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y)
transition.horizontal.animatePositionAdditive(node: sourceReplyPanel.textNode, offset: CGPoint(x: offset.x, y: 0.0), removeOnCompletion: false)
transition.vertical.animatePositionAdditive(node: sourceReplyPanel.textNode, offset: CGPoint(x: 0.0, y: offset.y), removeOnCompletion: false)
transition.horizontal.animatePositionAdditive(layer: sourceReplyPanel.textView.layer, offset: CGPoint(x: offset.x, y: 0.0), removeOnCompletion: false)
transition.vertical.animatePositionAdditive(layer: sourceReplyPanel.textView.layer, offset: CGPoint(x: 0.0, y: offset.y), removeOnCompletion: false)
}
if let imageNode = self.imageNode {
let offset = CGPoint(
x: localRect.minX + sourceReplyPanel.imageNode.frame.midX - imageNode.frame.midX,
y: localRect.minY + sourceReplyPanel.imageNode.frame.midY - imageNode.frame.midY
)
if let sourceImageView = sourceReplyPanel.imageView {
let offset = CGPoint(
x: localRect.minX + sourceImageView.frame.midX - imageNode.frame.midX,
y: localRect.minY + sourceImageView.frame.midY - imageNode.frame.midY
)
transition.horizontal.animatePositionAdditive(node: imageNode, offset: CGPoint(x: offset.x, y: 0.0))
transition.vertical.animatePositionAdditive(node: imageNode, offset: CGPoint(x: 0.0, y: offset.y))
transition.horizontal.animatePositionAdditive(node: imageNode, offset: CGPoint(x: offset.x, y: 0.0))
transition.vertical.animatePositionAdditive(node: imageNode, offset: CGPoint(x: 0.0, y: offset.y))
sourceParentNode.addSubnode(sourceReplyPanel.imageNode)
sourceParentNode.view.addSubview(sourceImageView)
imageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
imageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
sourceReplyPanel.imageNode.frame = sourceReplyPanel.imageNode.frame
.offsetBy(dx: sourceParentOffset.x, dy: sourceParentOffset.y)
.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y)
transition.horizontal.animatePositionAdditive(node: sourceReplyPanel.imageNode, offset: CGPoint(x: offset.x, y: 0.0), removeOnCompletion: false)
transition.vertical.animatePositionAdditive(node: sourceReplyPanel.imageNode, offset: CGPoint(x: 0.0, y: offset.y), removeOnCompletion: false)
sourceImageView.frame = sourceImageView.frame
.offsetBy(dx: sourceParentOffset.x, dy: sourceParentOffset.y)
.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y)
transition.horizontal.animatePositionAdditive(layer: sourceImageView.layer, offset: CGPoint(x: offset.x, y: 0.0), removeOnCompletion: false)
transition.vertical.animatePositionAdditive(layer: sourceImageView.layer, offset: CGPoint(x: 0.0, y: offset.y), removeOnCompletion: false)
} else {
imageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
}
}
do {
let backgroundView = self.backgroundView
let offset = CGPoint(
x: localRect.minX + sourceReplyPanel.lineNode.frame.minX - backgroundView.frame.minX,
y: localRect.minY + sourceReplyPanel.lineNode.frame.minY - backgroundView.frame.minY
x: localRect.minX + sourceReplyPanel.lineView.frame.minX - backgroundView.frame.minX,
y: localRect.minY + sourceReplyPanel.lineView.frame.minY - backgroundView.frame.minY
)
transition.horizontal.animatePositionAdditive(layer: backgroundView.layer, offset: CGPoint(x: offset.x, y: 0.0))
transition.vertical.animatePositionAdditive(layer: backgroundView.layer, offset: CGPoint(x: 0.0, y: offset.y))
sourceParentNode.addSubnode(sourceReplyPanel.lineNode)
sourceParentNode.view.addSubview(sourceReplyPanel.lineView)
backgroundView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
sourceReplyPanel.lineNode.frame = sourceReplyPanel.lineNode.frame
sourceReplyPanel.lineView.frame = sourceReplyPanel.lineView.frame
.offsetBy(dx: sourceParentOffset.x, dy: sourceParentOffset.y)
.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y)
transition.horizontal.animatePositionAdditive(node: sourceReplyPanel.lineNode, offset: CGPoint(x: offset.x, y: 0.0), removeOnCompletion: false)
transition.vertical.animatePositionAdditive(node: sourceReplyPanel.lineNode, offset: CGPoint(x: 0.0, y: offset.y), removeOnCompletion: false)
transition.horizontal.animatePositionAdditive(layer: sourceReplyPanel.lineView.layer, offset: CGPoint(x: offset.x, y: 0.0), removeOnCompletion: false)
transition.vertical.animatePositionAdditive(layer: sourceReplyPanel.lineView.layer, offset: CGPoint(x: 0.0, y: offset.y), removeOnCompletion: false)
return offset
}

View File

@ -2160,18 +2160,18 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
}
public final class AnimationTransitionReplyPanel {
public let titleNode: ASDisplayNode
public let textNode: ASDisplayNode
public let lineNode: ASDisplayNode
public let imageNode: ASDisplayNode
public let titleView: UIView
public let textView: UIView
public let lineView: UIView
public let imageView: UIView?
public let relativeSourceRect: CGRect
public let relativeTargetRect: CGRect
public init(titleNode: ASDisplayNode, textNode: ASDisplayNode, lineNode: ASDisplayNode, imageNode: ASDisplayNode, relativeSourceRect: CGRect, relativeTargetRect: CGRect) {
self.titleNode = titleNode
self.textNode = textNode
self.lineNode = lineNode
self.imageNode = imageNode
public init(titleView: UIView, textView: UIView, lineView: UIView, imageView: UIView?, relativeSourceRect: CGRect, relativeTargetRect: CGRect) {
self.titleView = titleView
self.textView = textView
self.lineView = lineView
self.imageView = imageView
self.relativeSourceRect = relativeSourceRect
self.relativeTargetRect = relativeTargetRect
}
@ -2181,10 +2181,10 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
if let replyInfoNode = self.replyInfoNode {
let localRect = self.contextSourceNode.contentNode.view.convert(sourceReplyPanel.relativeSourceRect, to: replyInfoNode.view)
let mappedPanel = ChatMessageReplyInfoNode.TransitionReplyPanel(
titleNode: sourceReplyPanel.titleNode,
textNode: sourceReplyPanel.textNode,
lineNode: sourceReplyPanel.lineNode,
imageNode: sourceReplyPanel.imageNode,
titleView: sourceReplyPanel.titleView,
textView: sourceReplyPanel.textView,
lineView: sourceReplyPanel.lineView,
imageView: sourceReplyPanel.imageView,
relativeSourceRect: sourceReplyPanel.relativeSourceRect,
relativeTargetRect: sourceReplyPanel.relativeTargetRect
)

View File

@ -1633,6 +1633,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
}
public func animateFrom(sourceView: UIView, scrollOffset: CGFloat, widthDifference: CGFloat, transition: CombinedTransition) {
self.containerNode.clipsToBounds = false
self.containerNode.view.addSubview(sourceView)
sourceView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak sourceView] _ in
@ -1645,7 +1646,12 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
y: sourceView.frame.minY - (self.textNode.textNode.frame.minY - 3.0) - scrollOffset
)
transition.vertical.animatePositionAdditive(node: self.textNode.textNode, offset: offset)
transition.vertical.animatePositionAdditive(layer: self.textNode.textNode.layer, offset: offset, completion: { [weak self] _ in
guard let self else {
return
}
self.containerNode.clipsToBounds = true
})
transition.updatePosition(layer: sourceView.layer, position: CGPoint(x: sourceView.layer.position.x - offset.x, y: sourceView.layer.position.y - offset.y))
if let statusNode = self.statusNode {

View File

@ -274,6 +274,9 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
public let recordMoreButton: ChatRecordingViewOnceButtonNode
private var accessoryPanel: (component: AnyComponentWithIdentity<ChatInputAccessoryPanelEnvironment>, view: ComponentView<ChatInputAccessoryPanelEnvironment>)?
public var accessoryPanelView: ChatInputAccessoryPanelView? {
return self.accessoryPanel?.view.view as? ChatInputAccessoryPanelView
}
private var contextPanel: (container: UIView, mask: UIImageView, panel: ChatInputContextPanelNode)?
private var mediaPreviewPanelNode: ChatRecordingPreviewInputPanelNodeImpl?
@ -2254,10 +2257,12 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
let alphaTransitionIn: ContainedViewLayoutTransition = transition.isAnimated ? ContainedViewLayoutTransition.animated(duration: 0.15, curve: .easeInOut) : .immediate
let alphaTransitionOut: ContainedViewLayoutTransition = transition.isAnimated ? ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut) : .immediate
let accessoryPanelAnimationBlurRadius: CGFloat = 20.0
var removedAccessoryPanelView: UIView?
if let currentAccessoryPanel = self.accessoryPanel, currentAccessoryPanel.component.id != accessoryPanel?.id {
if let panelView = currentAccessoryPanel.view.view as? ChatInputAccessoryPanelView {
panelView.storedFrameBeforeDismissed = panelView.convert(panelView.bounds, to: nil)
}
self.accessoryPanel = nil
if let accessoryPanelView = currentAccessoryPanel.view.view {
removedAccessoryPanelView = accessoryPanelView
@ -2299,18 +2304,10 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
accessoryPanelComponentView.frame = accessoryPanelFrame.offsetBy(dx: 0.0, dy: self.textInputNodeClippingContainer.frame.minY - accessoryPanelFrame.height)
accessoryPanelComponentView.alpha = 0.0
if transition.isAnimated {
ComponentTransition(alphaTransitionIn).animateBlur(layer: accessoryPanelComponentView.layer, fromRadius: accessoryPanelAnimationBlurRadius, toRadius: 0.0)
}
if let accessoryPanelComponentView = accessoryPanelComponentView as? ChatInputAccessoryPanelView {
self.textInputContainerBackgroundView.maskContentView.addSubview(accessoryPanelComponentView.contentTintView)
accessoryPanelComponentView.contentTintView.frame = accessoryPanelFrame.offsetBy(dx: 0.0, dy: self.textInputNodeClippingContainer.frame.minY - accessoryPanelFrame.height)
accessoryPanelComponentView.contentTintView.alpha = 0.0
if transition.isAnimated {
ComponentTransition(alphaTransitionIn).animateBlur(layer: accessoryPanelComponentView.contentTintView.layer, fromRadius: accessoryPanelAnimationBlurRadius, toRadius: 0.0)
}
}
}
transition.updateFrame(view: accessoryPanelComponentView, frame: accessoryPanelFrame)
@ -2378,16 +2375,17 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
self.textInputContainerBackgroundView.update(size: textInputContainerBackgroundFrame.size, cornerRadius: floor(minimalInputHeight * 0.5), isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: ComponentTransition(transition))
transition.updateFrame(layer: self.textInputBackgroundNode.layer, frame: textInputContainerBackgroundFrame)
transition.updateAlpha(node: self.textInputBackgroundNode, alpha: audioRecordingItemsAlpha)
if let removedAccessoryPanelView {
if let removedAccessoryPanelView = removedAccessoryPanelView as? ChatInputAccessoryPanelView {
let contentTintView = removedAccessoryPanelView.contentTintView
ComponentTransition(alphaTransitionOut).animateBlur(layer: contentTintView.layer, fromRadius: 0.0, toRadius: accessoryPanelAnimationBlurRadius, removeOnCompletion: false)
transition.updateFrame(view: contentTintView, frame: CGRect(origin: CGPoint(x: contentTintView.frame.minX, y: textFieldTopContentOffset - contentTintView.bounds.height), size: contentTintView.bounds.size))
alphaTransitionOut.updateAlpha(layer: contentTintView.layer, alpha: 0.0, completion: { [weak contentTintView] _ in
contentTintView?.removeFromSuperview()
})
}
ComponentTransition(alphaTransitionOut).animateBlur(layer: removedAccessoryPanelView.layer, fromRadius: 0.0, toRadius: accessoryPanelAnimationBlurRadius, removeOnCompletion: false)
transition.updateFrame(view: removedAccessoryPanelView, frame: CGRect(origin: CGPoint(x: removedAccessoryPanelView.frame.minX, y: textFieldTopContentOffset - removedAccessoryPanelView.bounds.height), size: removedAccessoryPanelView.bounds.size))
alphaTransitionOut.updateAlpha(layer: removedAccessoryPanelView.layer, alpha: 0.0, completion: { [weak removedAccessoryPanelView] _ in
removedAccessoryPanelView?.removeFromSuperview()
@ -2518,10 +2516,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
}
}
let textInputBackgroundFrame = CGRect(x: hideOffset.x + leftInset + textFieldInsets.left, y: hideOffset.y + textFieldInsets.top + textFieldTopContentOffset, width: baseWidth - textFieldInsets.left - textFieldInsets.right, height: panelHeight - textFieldInsets.top - textFieldInsets.bottom + self.textInputViewInternalInsets.top + self.textInputViewInternalInsets.bottom)
self.currentTextInputBackgroundWidthOffset = textInputBackgroundWidthOffset
transition.updateFrame(layer: self.textInputBackgroundNode.layer, frame: textInputBackgroundFrame)
transition.updateAlpha(node: self.textInputBackgroundNode, alpha: audioRecordingItemsAlpha)
let textPlaceholderSize: CGSize
let textPlaceholderMaxWidth: CGFloat = max(1.0, nextButtonTopRight.x - 12.0)
@ -2563,7 +2558,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
let textPlaceholderFrame: CGRect
if sendingTextDisabled {
textPlaceholderFrame = CGRect(origin: CGPoint(x: floor((textInputBackgroundFrame.width - textPlaceholderSize.width) / 2.0), y: self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel + textFieldTopContentOffset), size: textPlaceholderSize)
textPlaceholderFrame = CGRect(origin: CGPoint(x: floor((textInputContainerBackgroundFrame.width - textPlaceholderSize.width) / 2.0), y: self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel + textFieldTopContentOffset), size: textPlaceholderSize)
let textLockIconNode: ASImageNode
var textLockIconTransition = transition
@ -4672,7 +4667,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
public func frameForAttachmentButton() -> CGRect? {
if !self.attachmentButton.alpha.isZero {
return self.attachmentButton.frame.insetBy(dx: 0.0, dy: 6.0).offsetBy(dx: 2.0, dy: 0.0)
return self.attachmentButton.frame.insetBy(dx: 0.0, dy: -4.0).offsetBy(dx: 0.0, dy: 0.0)
}
return nil
}
@ -4727,9 +4722,8 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
return nil
}
//TODO
let backgroundView = UIImageView()
backgroundView.frame = self.textInputBackgroundNode.frame
backgroundView.frame = self.textInputBackgroundNode.view.convert(self.textInputBackgroundNode.bounds, to: self.view)
let caretColor = textInputNode.textView.tintColor
textInputNode.textView.tintColor = .clear
@ -4741,7 +4735,10 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
textInputNode.textView.tintColor = caretColor
contentView.frame = textInputNode.frame
if let textInputNodeSuperview = textInputNode.view.superview {
let _ = textInputNodeSuperview
contentView.frame = textInputNode.frame.offsetBy(dx: 0.0, dy: self.textInputNodeClippingContainer.frame.minY)
}
return (
backgroundView: backgroundView,

View File

@ -142,6 +142,8 @@ import AVFoundation
import BalanceNeededScreen
import FaceScanScreen
import ChatThemeScreen
import ChatTextInputPanelNode
import ChatInputAccessoryPanel
public final class ChatControllerOverlayPresentationData {
public let expandData: (ASDisplayNode?, () -> Void)
@ -2178,9 +2180,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let correlationId = Int64.random(in: 0 ..< Int64.max)
var replyPanel: ReplyAccessoryPanelNode?
if let accessoryPanelNode = strongSelf.chatDisplayNode.accessoryPanelNode as? ReplyAccessoryPanelNode {
replyPanel = accessoryPanelNode
var replyPanel: ChatInputAccessoryPanelView?
if let inputPanelNode = strongSelf.chatDisplayNode.inputPanelNode as? ChatTextInputPanelNode {
replyPanel = inputPanelNode.accessoryPanelView
}
var shouldAnimateMessageTransition = strongSelf.chatDisplayNode.shouldAnimateMessageTransition
@ -2188,8 +2190,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
shouldAnimateMessageTransition = true
}
strongSelf.presentPaidMessageAlertIfNeeded(completion: { [weak self] postpone in
guard let strongSelf = self else {
strongSelf.presentPaidMessageAlertIfNeeded(completion: { [weak strongSelf] postpone in
guard let strongSelf else {
return
}
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({

View File

@ -49,6 +49,7 @@ import ChatSideTopicsPanel
import GlassBackgroundComponent
import ChatThemeScreen
import ChatTextInputPanelNode
import ChatInputAccessoryPanel
final class VideoNavigationControllerDropContentItem: NavigationControllerDropContentItem {
let itemNode: OverlayMediaItemNode
@ -4660,11 +4661,10 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
let correlationId = Int64.random(in: 0 ..< Int64.max)
messages[messages.count - 1] = messages[messages.count - 1].withUpdatedCorrelationId(correlationId)
var replyPanel: ReplyAccessoryPanelNode?
if let accessoryPanelNode = self.accessoryPanelNode as? ReplyAccessoryPanelNode {
replyPanel = accessoryPanelNode
}
var replyPanel: ChatInputAccessoryPanelView?
if self.shouldAnimateMessageTransition, let inputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode, let textInput = inputPanelNode.makeSnapshotForTransition() {
replyPanel = inputPanelNode.accessoryPanelView
usedCorrelationId = correlationId
let source: ChatMessageTransitionNodeImpl.Source = .textInput(textInput: ChatMessageTransitionNodeImpl.Source.TextInput(
backgroundView: textInput.backgroundView,

View File

@ -21,6 +21,7 @@ import ChatMessageBubbleItemNode
import ChatEmptyNode
import ChatMediaInputStickerGridItem
import AccountContext
import ChatInputAccessoryPanel
private func convertAnimatingSourceRect(_ rect: CGRect, fromView: UIView, toView: UIView?) -> CGRect {
if let presentationLayer = fromView.layer.presentation() {
@ -105,26 +106,41 @@ private final class OverlayTransitionContainerController: ViewController, Standa
}
}
private func chatMessageTransitionAnimationDuration() -> Double {
#if DEBUG && false
return 3.0
#else
return 0.3
#endif
}
public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTransitionNode, ChatMessageTransitionProtocol {
static let animationDuration: Double = 0.3
static let animationDuration: Double = chatMessageTransitionAnimationDuration()
public static let verticalAnimationControlPoints: (Float, Float, Float, Float) = (0.19919472913616398, 0.010644531250000006, 0.27920937042459737, 0.91025390625)
public static let verticalAnimationCurve: ContainedViewLayoutTransitionCurve = .custom(verticalAnimationControlPoints.0, verticalAnimationControlPoints.1, verticalAnimationControlPoints.2, verticalAnimationControlPoints.3)
public static let horizontalAnimationCurve: ContainedViewLayoutTransitionCurve = .custom(0.23, 1.0, 0.32, 1.0)
final class ReplyPanel {
let titleNode: ASDisplayNode
let textNode: ASDisplayNode
let lineNode: ASDisplayNode
let imageNode: ASDisplayNode
let titleView: UIView
let textView: UIView
let lineView: UIView
let imageView: UIView?
let relativeSourceRect: CGRect
let relativeTargetRect: CGRect
init(titleNode: ASDisplayNode, textNode: ASDisplayNode, lineNode: ASDisplayNode, imageNode: ASDisplayNode, relativeSourceRect: CGRect, relativeTargetRect: CGRect) {
self.titleNode = titleNode
self.textNode = textNode
self.lineNode = lineNode
self.imageNode = imageNode
init(
titleView: UIView,
textView: UIView,
lineView: UIView,
imageView: UIView?,
relativeSourceRect: CGRect,
relativeTargetRect: CGRect
) {
self.titleView = titleView
self.textView = textView
self.lineView = lineView
self.imageView = imageView
self.relativeSourceRect = relativeSourceRect
self.relativeTargetRect = relativeTargetRect
}
@ -232,8 +248,8 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
}
}
case textInput(textInput: TextInput, replyPanel: ReplyAccessoryPanelNode?)
case stickerMediaInput(input: StickerInput, replyPanel: ReplyAccessoryPanelNode?)
case textInput(textInput: TextInput, replyPanel: ChatInputAccessoryPanelView?)
case stickerMediaInput(input: StickerInput, replyPanel: ChatInputAccessoryPanelView?)
case audioMicInput(AudioMicInput)
case videoMessage(VideoMessage)
case mediaInput(MediaInput)
@ -403,9 +419,15 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
textInput.contentView.frame = textInput.contentView.frame.offsetBy(dx: 0.0, dy: sourceAbsoluteRect.height - sourceBackgroundAbsoluteRect.height)
var sourceReplyPanel: ReplyPanel?
if let replyPanel = replyPanel, let replyPanelParentView = replyPanel.view.superview {
let replyPanelFrame = replyPanel.originalFrameBeforeDismissed ?? replyPanel.frame
var replySourceAbsoluteFrame = replyPanelParentView.convert(replyPanelFrame, to: self.view)
if let replyPanel, let replyPanelTransitionData = replyPanel.transitionData, let replyPanelParentView = replyPanel.superview {
let replyPanelFrame = replyPanel.frame
var replySourceAbsoluteFrame: CGRect
if let storedFrameBeforeDismissed = replyPanel.storedFrameBeforeDismissed {
replySourceAbsoluteFrame = self.view.convert(storedFrameBeforeDismissed, from: nil)
} else {
replySourceAbsoluteFrame = replyPanelParentView.convert(replyPanelFrame, to: self.view)
}
replySourceAbsoluteFrame.origin.x -= sourceAbsoluteRect.minX - self.contextSourceNode.contentRect.minX
replySourceAbsoluteFrame.origin.y -= sourceAbsoluteRect.minY - self.contextSourceNode.contentRect.minY
@ -415,7 +437,14 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
globalTargetFrame.origin.x += sourceAbsoluteRect.minX - targetAbsoluteRect.minX
globalTargetFrame.origin.y += sourceAbsoluteRect.minY - targetAbsoluteRect.minY
sourceReplyPanel = ReplyPanel(titleNode: replyPanel.titleNode, textNode: replyPanel.textNode, lineNode: replyPanel.lineNode, imageNode: replyPanel.imageNode, relativeSourceRect: replySourceAbsoluteFrame, relativeTargetRect: globalTargetFrame)
sourceReplyPanel = ReplyPanel(
titleView: replyPanelTransitionData.titleView,
textView: replyPanelTransitionData.textView,
lineView: replyPanelTransitionData.lineView,
imageView: replyPanelTransitionData.imageView,
relativeSourceRect: replySourceAbsoluteFrame,
relativeTargetRect: globalTargetFrame
)
}
self.itemNode.cancelInsertionAnimations()
@ -449,13 +478,13 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
),
transition: combinedTransition
)
if let sourceReplyPanel = sourceReplyPanel {
if let sourceReplyPanel {
itemNode.animateReplyPanel(
sourceReplyPanel: ChatMessageBubbleItemNode.AnimationTransitionReplyPanel(
titleNode: sourceReplyPanel.titleNode,
textNode: sourceReplyPanel.textNode,
lineNode: sourceReplyPanel.lineNode,
imageNode: sourceReplyPanel.imageNode,
titleView: sourceReplyPanel.titleView,
textView: sourceReplyPanel.textView,
lineView: sourceReplyPanel.lineView,
imageView: sourceReplyPanel.imageView,
relativeSourceRect: sourceReplyPanel.relativeSourceRect,
relativeTargetRect: sourceReplyPanel.relativeTargetRect
),
@ -475,10 +504,10 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
if let sourceReplyPanel = sourceReplyPanel {
itemNode.animateReplyPanel(
sourceReplyPanel: ChatMessageAnimatedStickerItemNode.AnimationTransitionReplyPanel(
titleNode: sourceReplyPanel.titleNode,
textNode: sourceReplyPanel.textNode,
lineNode: sourceReplyPanel.lineNode,
imageNode: sourceReplyPanel.imageNode,
titleView: sourceReplyPanel.titleView,
textView: sourceReplyPanel.textView,
lineView: sourceReplyPanel.lineView,
imageView: sourceReplyPanel.imageView,
relativeSourceRect: sourceReplyPanel.relativeSourceRect,
relativeTargetRect: sourceReplyPanel.relativeTargetRect
),
@ -498,10 +527,10 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
if let sourceReplyPanel = sourceReplyPanel {
itemNode.animateReplyPanel(
sourceReplyPanel: ChatMessageStickerItemNode.AnimationTransitionReplyPanel(
titleNode: sourceReplyPanel.titleNode,
textNode: sourceReplyPanel.textNode,
lineNode: sourceReplyPanel.lineNode,
imageNode: sourceReplyPanel.imageNode,
titleView: sourceReplyPanel.titleView,
textView: sourceReplyPanel.textView,
lineView: sourceReplyPanel.lineView,
imageView: sourceReplyPanel.imageView,
relativeSourceRect: sourceReplyPanel.relativeSourceRect,
relativeTargetRect: sourceReplyPanel.relativeTargetRect
),
@ -537,12 +566,27 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
let targetAbsoluteRect = self.contextSourceNode.view.convert(self.contextSourceNode.contentRect, to: self.view)
var sourceReplyPanel: ReplyPanel?
if let replyPanel = replyPanel, let replyPanelParentView = replyPanel.view.superview {
var replySourceAbsoluteFrame = replyPanelParentView.convert(replyPanel.originalFrameBeforeDismissed ?? replyPanel.frame, to: self.view)
if let replyPanel, let replyPanelTransitionData = replyPanel.transitionData, let replyPanelParentView = replyPanel.superview {
let replyPanelFrame = replyPanel.frame
var replySourceAbsoluteFrame: CGRect
if let storedFrameBeforeDismissed = replyPanel.storedFrameBeforeDismissed {
replySourceAbsoluteFrame = self.view.convert(storedFrameBeforeDismissed, from: nil)
} else {
replySourceAbsoluteFrame = replyPanelParentView.convert(replyPanelFrame, to: self.view)
}
replySourceAbsoluteFrame.origin.x -= sourceAbsoluteRect.midX - self.contextSourceNode.contentRect.midX
replySourceAbsoluteFrame.origin.y -= sourceAbsoluteRect.midY - self.contextSourceNode.contentRect.midY
sourceReplyPanel = ReplyPanel(titleNode: replyPanel.titleNode, textNode: replyPanel.textNode, lineNode: replyPanel.lineNode, imageNode: replyPanel.imageNode, relativeSourceRect: replySourceAbsoluteFrame, relativeTargetRect: replySourceAbsoluteFrame.offsetBy(dx: 0.0, dy: replySourceAbsoluteFrame.height))
sourceReplyPanel = ReplyPanel(
titleView: replyPanelTransitionData.titleView,
textView: replyPanelTransitionData.textView,
lineView: replyPanelTransitionData.lineView,
imageView: replyPanelTransitionData.imageView,
relativeSourceRect: replySourceAbsoluteFrame,
relativeTargetRect: replySourceAbsoluteFrame.offsetBy(dx: 0.0, dy: replySourceAbsoluteFrame.height)
)
}
let combinedTransition = CombinedTransition(horizontal: .animated(duration: horizontalDuration, curve: ChatMessageTransitionNodeImpl.horizontalAnimationCurve), vertical: .animated(duration: verticalDuration, curve: ChatMessageTransitionNodeImpl.verticalAnimationCurve))
@ -564,10 +608,10 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
if let sourceReplyPanel = sourceReplyPanel {
itemNode.animateReplyPanel(
sourceReplyPanel: ChatMessageAnimatedStickerItemNode.AnimationTransitionReplyPanel(
titleNode: sourceReplyPanel.titleNode,
textNode: sourceReplyPanel.textNode,
lineNode: sourceReplyPanel.lineNode,
imageNode: sourceReplyPanel.imageNode,
titleView: sourceReplyPanel.titleView,
textView: sourceReplyPanel.textView,
lineView: sourceReplyPanel.lineView,
imageView: sourceReplyPanel.imageView,
relativeSourceRect: sourceReplyPanel.relativeSourceRect,
relativeTargetRect: sourceReplyPanel.relativeTargetRect
),
@ -588,10 +632,10 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
if let sourceReplyPanel = sourceReplyPanel {
itemNode.animateReplyPanel(
sourceReplyPanel: ChatMessageStickerItemNode.AnimationTransitionReplyPanel(
titleNode: sourceReplyPanel.titleNode,
textNode: sourceReplyPanel.textNode,
lineNode: sourceReplyPanel.lineNode,
imageNode: sourceReplyPanel.imageNode,
titleView: sourceReplyPanel.titleView,
textView: sourceReplyPanel.textView,
lineView: sourceReplyPanel.lineView,
imageView: sourceReplyPanel.imageView,
relativeSourceRect: sourceReplyPanel.relativeSourceRect,
relativeTargetRect: sourceReplyPanel.relativeTargetRect
),