From b17138d501e8d11893a9d5d2b06f8840dd614737 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Mon, 9 Oct 2023 20:46:17 +0400 Subject: [PATCH] [WIP] Quotes --- submodules/TelegramUI/BUILD | 2 + .../ChatMessageCallBubbleContentNode/BUILD | 26 +++++++ .../ChatMessageCallBubbleContentNode.swift | 22 +++--- .../Chat/ReplyAccessoryPanelNode/BUILD | 36 ++++++++++ .../Sources/ReplyAccessoryPanelNode.swift | 70 ++++++++++++++----- .../TelegramUI/Sources/ChatController.swift | 1 + .../Sources/ChatControllerNode.swift | 1 + .../ChatInterfaceStateAccessoryPanels.swift | 1 + .../Sources/ChatMessageBubbleItemNode.swift | 1 + .../Sources/ChatMessageTransitionNode.swift | 1 + 10 files changed, 131 insertions(+), 30 deletions(-) create mode 100644 submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode/BUILD rename submodules/TelegramUI/{ => Components/Chat/ChatMessageCallBubbleContentNode}/Sources/ChatMessageCallBubbleContentNode.swift (91%) create mode 100644 submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/BUILD rename submodules/TelegramUI/{ => Components/Chat/ReplyAccessoryPanelNode}/Sources/ReplyAccessoryPanelNode.swift (84%) diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 641edc1441..d26f9f182d 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -359,6 +359,8 @@ swift_library( "//submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode", "//submodules/TelegramUI/Components/Chat/ChatContextResultPeekContent", "//submodules/TelegramUI/Components/Chat/ChatInputContextPanelNode", + "//submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode", + "//submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode", ] + select({ "@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets, "//build-system:ios_sim_arm64": [], diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode/BUILD new file mode 100644 index 0000000000..17460293d1 --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode/BUILD @@ -0,0 +1,26 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ChatMessageCallBubbleContentNode", + module_name = "ChatMessageCallBubbleContentNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/TelegramCore", + "//submodules/Postbox", + "//submodules/TelegramPresentationData", + "//submodules/AppBundle", + "//submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode", + "//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon", + "//submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Sources/ChatMessageCallBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode/Sources/ChatMessageCallBubbleContentNode.swift similarity index 91% rename from submodules/TelegramUI/Sources/ChatMessageCallBubbleContentNode.swift rename to submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode/Sources/ChatMessageCallBubbleContentNode.swift index abf91721bf..0c8a27b15f 100644 --- a/submodules/TelegramUI/Sources/ChatMessageCallBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode/Sources/ChatMessageCallBubbleContentNode.swift @@ -19,13 +19,13 @@ private let incomingRedIcon = generateTintedImage(image: UIImage(bundleImageName private let outgoingGreenIcon = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/CallOutgoingArrow"), color: UIColor(rgb: 0x36c033)) private let outgoingRedIcon = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/CallOutgoingArrow"), color: UIColor(rgb: 0xff4747)) -class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode { +public class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode { private let titleNode: TextNode private let labelNode: TextNode private let iconNode: ASImageNode private let buttonNode: HighlightableButtonNode - required init() { + required public init() { self.titleNode = TextNode() self.labelNode = TextNode() @@ -57,22 +57,22 @@ class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode { self.buttonNode.addTarget(self, action: #selector(self.callButtonPressed), forControlEvents: .touchUpInside) } - override func accessibilityActivate() -> Bool { + override public func accessibilityActivate() -> Bool { self.callButtonPressed() return true } - override func didLoad() { + override public func didLoad() { super.didLoad() self.view.accessibilityElementsHidden = true } - required init?(coder aDecoder: NSCoder) { + required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - override func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, CGSize?, CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) { + override public func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, CGSize?, CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) { let makeTitleLayout = TextNode.asyncLayout(self.titleNode) let makeLabelLayout = TextNode.asyncLayout(self.labelNode) @@ -236,19 +236,19 @@ class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode { } } - override func animateInsertion(_ currentTimestamp: Double, duration: Double) { + override public func animateInsertion(_ currentTimestamp: Double, duration: Double) { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } - override func animateAdded(_ currentTimestamp: Double, duration: Double) { + override public func animateAdded(_ currentTimestamp: Double, duration: Double) { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } - override func animateRemoved(_ currentTimestamp: Double, duration: Double) { + override public func animateRemoved(_ currentTimestamp: Double, duration: Double) { self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) } - @objc func callButtonPressed() { + @objc private func callButtonPressed() { if let item = self.item { var isVideo = false for media in item.message.media { @@ -260,7 +260,7 @@ class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode { } } - override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { + override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.buttonNode.frame.contains(point) { return .ignore } else if self.bounds.contains(point), let item = self.item { diff --git a/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/BUILD b/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/BUILD new file mode 100644 index 0000000000..c31c9ccbc1 --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/BUILD @@ -0,0 +1,36 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ReplyAccessoryPanelNode", + module_name = "ReplyAccessoryPanelNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/TelegramCore", + "//submodules/Postbox", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/Display", + "//submodules/TelegramPresentationData", + "//submodules/TelegramUIPreferences", + "//submodules/AccountContext", + "//submodules/LocalizedPeerData", + "//submodules/PhotoResources", + "//submodules/TelegramStringFormatting", + "//submodules/TextFormat", + "//submodules/ChatPresentationInterfaceState", + "//submodules/TelegramUI/Components/TextNodeWithEntities", + "//submodules/TelegramUI/Components/AnimationCache", + "//submodules/TelegramUI/Components/MultiAnimationRenderer", + "//submodules/TelegramUI/Components/Chat/AccessoryPanelNode", + "//submodules/TelegramNotices", + ], + visibility = [ + "//visibility:public", + ], +) + diff --git a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift b/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/Sources/ReplyAccessoryPanelNode.swift similarity index 84% rename from submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift rename to submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/Sources/ReplyAccessoryPanelNode.swift index 985da36930..90a83c52be 100644 --- a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/Sources/ReplyAccessoryPanelNode.swift @@ -19,29 +19,29 @@ import MultiAnimationRenderer import AccessoryPanelNode import TelegramNotices -final class ReplyAccessoryPanelNode: AccessoryPanelNode { +public final class ReplyAccessoryPanelNode: AccessoryPanelNode { private let messageDisposable = MetaDisposable() - let messageId: MessageId - let quote: EngineMessageReplyQuote? + public let messageId: MessageId + public let quote: EngineMessageReplyQuote? private var previousMediaReference: AnyMediaReference? - let closeButton: HighlightableButtonNode - let lineNode: ASImageNode - let iconNode: ASImageNode - let titleNode: ImmediateTextNode - let textNode: ImmediateTextNodeWithEntities - let imageNode: TransformImageNode + public let closeButton: HighlightableButtonNode + public let lineNode: ASImageNode + public let iconNode: ASImageNode + public let titleNode: ImmediateTextNode + public let textNode: ImmediateTextNodeWithEntities + public let imageNode: TransformImageNode private let actionArea: AccessibilityAreaNode private let context: AccountContext - var theme: PresentationTheme - var strings: PresentationStrings + public var theme: PresentationTheme + public var strings: PresentationStrings private var validLayout: (size: CGSize, inset: CGFloat, interfaceState: ChatPresentationInterfaceState)? - init(context: AccountContext, messageId: MessageId, quote: EngineMessageReplyQuote?, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, animationCache: AnimationCache?, animationRenderer: MultiAnimationRenderer?) { + public init(context: AccountContext, messageId: MessageId, quote: EngineMessageReplyQuote?, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, animationCache: AnimationCache?, animationRenderer: MultiAnimationRenderer?) { self.messageId = messageId self.quote = quote @@ -264,6 +264,38 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { if let (size, inset, interfaceState) = strongSelf.validLayout { strongSelf.updateState(size: size, inset: inset, interfaceState: interfaceState) } + + let _ = (ApplicationSpecificNotice.getChatReplyOptionsTip(accountManager: strongSelf.context.sharedContext.accountManager) + |> deliverOnMainQueue).start(next: { [weak self] count in + if let strongSelf = self, count < 3 { + Queue.mainQueue().after(3.0) { + if let snapshotView = strongSelf.textNode.view.snapshotContentTree() { + let text: String + //TODO:localize + if let (size, _, _) = strongSelf.validLayout, size.width > 320.0 { + text = "Tap here for options" + } else { + text = "Tap here for forwarding options" + } + + strongSelf.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(15.0), textColor: strongSelf.theme.chat.inputPanel.secondaryTextColor) + + strongSelf.view.addSubview(snapshotView) + + if let (size, inset, interfaceState) = strongSelf.validLayout { + strongSelf.updateState(size: size, inset: inset, interfaceState: interfaceState) + } + + strongSelf.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + + let _ = ApplicationSpecificNotice.incrementChatReplyOptionsTip(accountManager: strongSelf.context.sharedContext.accountManager).start() + } + } + }) } })) } @@ -272,21 +304,21 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { self.messageDisposable.dispose() } - override func didLoad() { + override public func didLoad() { super.didLoad() self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) } - override func animateIn() { + override public func animateIn() { self.iconNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) } - override func animateOut() { + override public func animateOut() { self.iconNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false) } - override func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { + override public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { self.updateThemeAndStrings(theme: theme, strings: strings, force: false) } @@ -314,11 +346,11 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { } } - override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { + override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { return CGSize(width: constrainedSize.width, height: 45.0) } - override func updateState(size: CGSize, inset: CGFloat, interfaceState: ChatPresentationInterfaceState) { + override public func updateState(size: CGSize, inset: CGFloat, interfaceState: ChatPresentationInterfaceState) { self.validLayout = (size, inset, interfaceState) let bounds = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: 45.0)) @@ -361,7 +393,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { } } - @objc func closePressed() { + @objc private func closePressed() { if let dismiss = self.dismiss { dismiss() } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index edd939af7d..b718e31223 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -104,6 +104,7 @@ import PeerReportScreen import PeerSelectionController import SaveToCameraRoll import ChatMessageDateAndStatusNode +import ReplyAccessoryPanelNode public enum ChatControllerPeekActions { case standard diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 20768e9dc3..d8276f166e 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -31,6 +31,7 @@ import ChatOverscrollControl import ChatInputPanelNode import ChatInputContextPanelNode import TextSelectionNode +import ReplyAccessoryPanelNode final class VideoNavigationControllerDropContentItem: NavigationControllerDropContentItem { let itemNode: OverlayMediaItemNode diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift index dab11d8ea4..002f3a64ba 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift @@ -7,6 +7,7 @@ import ChatPresentationInterfaceState import ChatControllerInteraction import AccessoryPanelNode import ForwardAccessoryPanelNode +import ReplyAccessoryPanelNode func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: AccessoryPanelNode?, chatControllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> AccessoryPanelNode? { if let _ = chatPresentationInterfaceState.interfaceState.selectionState { diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index b43bbd1c26..766c97424a 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -36,6 +36,7 @@ import ChatHistoryEntry import ChatMessageTextBubbleContentNode import ChatMessageItemCommon import ChatMessageReplyInfoNode +import ChatMessageCallBubbleContentNode enum InternalBubbleTapAction { case action(() -> Void) diff --git a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift index c4430e5403..02fc77f16f 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift @@ -12,6 +12,7 @@ import ReactionSelectionNode import ChatControllerInteraction import FeaturedStickersScreen import ChatTextInputMediaRecordingButton +import ReplyAccessoryPanelNode private func convertAnimatingSourceRect(_ rect: CGRect, fromView: UIView, toView: UIView?) -> CGRect { if let presentationLayer = fromView.layer.presentation() {