Refactoring

This commit is contained in:
Ali
2023-10-14 23:50:34 +04:00
parent abacd0654b
commit 2f700ac59c
35 changed files with 845 additions and 433 deletions

View File

@@ -372,6 +372,15 @@ swift_library(
"//submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageItem",
"//submodules/TelegramUI/Components/Chat/ChatMessageItemView",
"//submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageSwipeToReplyNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageSelectionNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageDeliveryFailedNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageShareButton",
"//submodules/TelegramUI/Components/Chat/ChatMessageThreadInfoNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageActionButtonsNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode",
"//submodules/TelegramUI/Components/Chat/ChatSwipeToReplyRecognizer",
] + select({
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
"//build-system:ios_sim_arm64": [],

View File

@@ -0,0 +1,24 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ChatMessageActionButtonsNode",
module_name = "ChatMessageActionButtonsNode",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/AsyncDisplayKit",
"//submodules/TelegramCore",
"//submodules/Postbox",
"//submodules/Display",
"//submodules/TelegramPresentationData",
"//submodules/AccountContext",
"//submodules/WallpaperBackgroundNode",
],
visibility = [
"//visibility:public",
],
)

View File

@@ -343,7 +343,7 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
let titleFrame = CGRect(origin: CGPoint(x: floor((width - titleSize.size.width) / 2.0), y: floor((42.0 - titleSize.size.height) / 2.0) + 1.0), size: titleSize.size)
titleNode.layer.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
animation.animator.updatePosition(layer: titleNode.layer, position: titleFrame.center, completion: nil)
animation.animator.updatePosition(layer: titleNode.layer, position: CGPoint(x: titleFrame.midX, y: titleFrame.midY), completion: nil)
if let buttonView = node.buttonView {
buttonView.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: 42.0))
@@ -366,17 +366,17 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
}
}
final class ChatMessageActionButtonsNode: ASDisplayNode {
public final class ChatMessageActionButtonsNode: ASDisplayNode {
private var buttonNodes: [ChatMessageActionButtonNode] = []
private var buttonPressedWrapper: ((ReplyMarkupButton) -> Void)?
private var buttonLongTappedWrapper: ((ReplyMarkupButton) -> Void)?
var buttonPressed: ((ReplyMarkupButton) -> Void)?
var buttonLongTapped: ((ReplyMarkupButton) -> Void)?
public var buttonPressed: ((ReplyMarkupButton) -> Void)?
public var buttonLongTapped: ((ReplyMarkupButton) -> Void)?
private var absolutePosition: (CGRect, CGSize)?
override init() {
override public init() {
super.init()
self.buttonPressedWrapper = { [weak self] button in
@@ -392,7 +392,7 @@ final class ChatMessageActionButtonsNode: ASDisplayNode {
}
}
func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
self.absolutePosition = (rect, containerSize)
for button in buttonNodes {
@@ -403,7 +403,7 @@ final class ChatMessageActionButtonsNode: ASDisplayNode {
}
}
class func asyncLayout(_ maybeNode: ChatMessageActionButtonsNode?) -> (_ context: AccountContext, _ theme: ChatPresentationThemeData, _ chatBubbleCorners: PresentationChatBubbleCorners, _ strings: PresentationStrings, _ backgroundNode: WallpaperBackgroundNode?, _ replyMarkup: ReplyMarkupMessageAttribute, _ message: Message, _ constrainedWidth: CGFloat) -> (minWidth: CGFloat, layout: (CGFloat) -> (CGSize, (_ animation: ListViewItemUpdateAnimation) -> ChatMessageActionButtonsNode)) {
public class func asyncLayout(_ maybeNode: ChatMessageActionButtonsNode?) -> (_ context: AccountContext, _ theme: ChatPresentationThemeData, _ chatBubbleCorners: PresentationChatBubbleCorners, _ strings: PresentationStrings, _ backgroundNode: WallpaperBackgroundNode?, _ replyMarkup: ReplyMarkupMessageAttribute, _ message: Message, _ constrainedWidth: CGFloat) -> (minWidth: CGFloat, layout: (CGFloat) -> (CGSize, (_ animation: ListViewItemUpdateAnimation) -> ChatMessageActionButtonsNode)) {
let currentButtonLayouts = maybeNode?.buttonNodes.map { ChatMessageActionButtonNode.asyncLayout($0) } ?? []
return { context, theme, chatBubbleCorners, strings, backgroundNode, replyMarkup, message, constrainedWidth in

View File

@@ -0,0 +1,20 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ChatMessageDeliveryFailedNode",
module_name = "ChatMessageDeliveryFailedNode",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/AsyncDisplayKit",
"//submodules/Display",
"//submodules/TelegramPresentationData",
],
visibility = [
"//visibility:public",
],
)

View File

@@ -4,11 +4,11 @@ import AsyncDisplayKit
import Display
import TelegramPresentationData
final class ChatMessageDeliveryFailedNode: ASImageNode {
public final class ChatMessageDeliveryFailedNode: ASImageNode {
private let tapped: () -> Void
private var theme: PresentationTheme?
init(tapped: @escaping () -> Void) {
public init(tapped: @escaping () -> Void) {
self.tapped = tapped
super.init()
@@ -18,7 +18,7 @@ final class ChatMessageDeliveryFailedNode: ASImageNode {
self.isUserInteractionEnabled = true
}
override func didLoad() {
override public func didLoad() {
super.didLoad()
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
}
@@ -29,7 +29,7 @@ final class ChatMessageDeliveryFailedNode: ASImageNode {
}
}
func updateLayout(theme: PresentationTheme) -> CGSize {
public func updateLayout(theme: PresentationTheme) -> CGSize {
if self.theme !== theme {
self.theme = theme
self.image = PresentationResourcesChat.chatBubbleDeliveryFailedIcon(theme)

View File

@@ -17,6 +17,7 @@ swift_library(
"//submodules/TelegramCore",
"//submodules/AccountContext",
"//submodules/TelegramUI/Components/Chat/ChatHistoryEntry",
"//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon",
"//submodules/TelegramUI/Components/ChatControllerInteraction",
"//submodules/TelegramPresentationData",
],

View File

@@ -9,6 +9,7 @@ import AccountContext
import ChatHistoryEntry
import ChatControllerInteraction
import TelegramPresentationData
import ChatMessageItemCommon
public enum ChatMessageItemContent: Sequence {
case message(message: Message, read: Bool, selection: ChatHistoryMessageSelection, attributes: ChatMessageEntryAttributes, location: MessageHistoryEntryLocation?)
@@ -124,3 +125,47 @@ public protocol ChatMessageItem: ListViewItem {
func mergedWithItems(top: ListViewItem?, bottom: ListViewItem?) -> (top: ChatMessageMerge, bottom: ChatMessageMerge, dateAtBottom: Bool)
}
public func hasCommentButton(item: ChatMessageItem) -> Bool {
let firstMessage = item.content.firstMessage
var hasDiscussion = false
if let channel = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
hasDiscussion = true
}
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.effectiveTopId == firstMessage.id {
hasDiscussion = false
}
if firstMessage.adAttribute != nil {
hasDiscussion = false
}
if hasDiscussion {
var canComment = false
if case .pinnedMessages = item.associatedData.subject {
canComment = false
} else if firstMessage.id.namespace == Namespaces.Message.Local {
canComment = true
} else {
for attribute in firstMessage.attributes {
if let attribute = attribute as? ReplyThreadMessageAttribute, let commentsPeerId = attribute.commentsPeerId {
switch item.associatedData.channelDiscussionGroup {
case .unknown:
canComment = true
case let .known(groupId):
canComment = groupId == commentsPeerId
}
break
}
}
}
if canComment {
return true
}
} else if firstMessage.id.peerId.isReplies {
return true
}
return false
}

View File

@@ -13,6 +13,7 @@ swift_library(
"//submodules/Display",
"//submodules/Postbox",
"//submodules/TelegramCore",
"//submodules/Emoji",
],
visibility = [
"//visibility:public",

View File

@@ -3,6 +3,7 @@ import UIKit
import Display
import Postbox
import TelegramCore
import Emoji
public struct ChatMessageItemWidthFill {
public var compactInset: CGFloat
@@ -230,3 +231,68 @@ public func isPollEffectivelyClosed(message: Message, poll: TelegramMediaPoll) -
return false
}
}
public extension ChatReplyThreadMessage {
var effectiveTopId: MessageId {
return self.channelMessageId ?? self.messageId
}
}
public func messageIsElligibleForLargeEmoji(_ message: Message) -> Bool {
if !message.text.isEmpty && message.text.containsOnlyEmoji {
if !(message.textEntitiesAttribute?.entities.isEmpty ?? true) {
return false
}
return true
} else {
return false
}
}
public func messageIsElligibleForLargeCustomEmoji(_ message: Message) -> Bool {
let text = message.text.replacingOccurrences(of: "\n", with: "").replacingOccurrences(of: " ", with: "")
guard !text.isEmpty && text.containsOnlyEmoji else {
return false
}
let entities = message.textEntitiesAttribute?.entities ?? []
guard entities.count > 0 else {
return false
}
for entity in entities {
if case let .CustomEmoji(_, fileId) = entity.type {
if let _ = message.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] as? TelegramMediaFile {
} else {
return false
}
} else {
return false
}
}
return true
}
public func canAddMessageReactions(message: Message) -> Bool {
if message.id.namespace != Namespaces.Message.Cloud {
return false
}
if let peer = message.peers[message.id.peerId] {
if let _ = peer as? TelegramSecretChat {
return false
}
} else {
return false
}
for media in message.media {
if let _ = media as? TelegramMediaAction {
return false
} else if let story = media as? TelegramMediaStory {
if story.isMention {
return false
}
} else if let _ = media as? TelegramMediaExpiredContent {
return false
}
}
return true
}

View File

@@ -614,6 +614,12 @@ public final class ChatMessageAccessibilityData {
}
}
public enum InternalBubbleTapAction {
case action(() -> Void)
case optionalAction(() -> Void)
case openContextMenu(tapMessage: Message, selectAll: Bool, subFrame: CGRect)
}
open class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol {
public let layoutConstants = (ChatMessageItemLayoutConstants.compact, ChatMessageItemLayoutConstants.regular)

View File

@@ -0,0 +1,32 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ChatMessageReactionsFooterContentNode",
module_name = "ChatMessageReactionsFooterContentNode",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/Postbox",
"//submodules/Display",
"//submodules/AsyncDisplayKit",
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/TelegramCore",
"//submodules/TelegramPresentationData",
"//submodules/RadialStatusNode",
"//submodules/AnimatedCountLabelNode",
"//submodules/AnimatedAvatarSetNode",
"//submodules/Components/ReactionButtonListComponent",
"//submodules/AccountContext",
"//submodules/WallpaperBackgroundNode",
"//submodules/TelegramUI/Components/ChatControllerInteraction",
"//submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon",
],
visibility = [
"//visibility:public",
],
)

View File

@@ -16,14 +16,14 @@ import ChatControllerInteraction
import ChatMessageBubbleContentNode
import ChatMessageItemCommon
final class MessageReactionButtonsNode: ASDisplayNode {
enum DisplayType {
public final class MessageReactionButtonsNode: ASDisplayNode {
public enum DisplayType {
case incoming
case outgoing
case freeform
}
enum DisplayAlignment {
public enum DisplayAlignment {
case left
case right
}
@@ -33,10 +33,10 @@ final class MessageReactionButtonsNode: ASDisplayNode {
private var backgroundMaskView: UIView?
private var backgroundMaskButtons: [MessageReaction.Reaction: UIView] = [:]
var reactionSelected: ((MessageReaction.Reaction) -> Void)?
var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingView, MessageReaction.Reaction) -> Void)?
public var reactionSelected: ((MessageReaction.Reaction) -> Void)?
public var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingView, MessageReaction.Reaction) -> Void)?
override init() {
override public init() {
self.container = ReactionButtonsAsyncLayoutContainer()
super.init()
@@ -46,10 +46,10 @@ final class MessageReactionButtonsNode: ASDisplayNode {
}
func update() {
public func update() {
}
func prepareUpdate(
public func prepareUpdate(
context: AccountContext,
presentationData: ChatPresentationData,
presentationContext: ChatPresentationContext,
@@ -387,7 +387,7 @@ final class MessageReactionButtonsNode: ASDisplayNode {
private var absoluteRect: (CGRect, CGSize)?
func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
public func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
self.absoluteRect = (rect, containerSize)
if let bubbleBackgroundNode = self.bubbleBackgroundNode {
@@ -395,7 +395,7 @@ final class MessageReactionButtonsNode: ASDisplayNode {
}
}
func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) {
public func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) {
self.absoluteRect = (rect, containerSize)
if let bubbleBackgroundNode = self.bubbleBackgroundNode {
@@ -403,19 +403,19 @@ final class MessageReactionButtonsNode: ASDisplayNode {
}
}
func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
public func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
if let bubbleBackgroundNode = self.bubbleBackgroundNode {
bubbleBackgroundNode.offset(value: value, animationCurve: animationCurve, duration: duration)
}
}
func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
public func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
if let bubbleBackgroundNode = self.bubbleBackgroundNode {
bubbleBackgroundNode.offsetSpring(value: value, duration: duration, damping: damping)
}
}
func reactionTargetView(value: MessageReaction.Reaction) -> UIView? {
public func reactionTargetView(value: MessageReaction.Reaction) -> UIView? {
for (key, button) in self.container.buttons {
if key == value {
return button.view.iconView
@@ -424,19 +424,19 @@ final class MessageReactionButtonsNode: ASDisplayNode {
return nil
}
func animateIn(animation: ListViewItemUpdateAnimation) {
public func animateIn(animation: ListViewItemUpdateAnimation) {
for (_, button) in self.container.buttons {
animation.animator.animateScale(layer: button.view.layer, from: 0.01, to: 1.0, completion: nil)
}
}
func animateOut(animation: ListViewItemUpdateAnimation) {
public func animateOut(animation: ListViewItemUpdateAnimation) {
for (_, button) in self.container.buttons {
animation.animator.updateScale(layer: button.view.layer, scale: 0.01, completion: nil)
}
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
for (_, button) in self.container.buttons {
if button.view.frame.contains(point) {
if let result = button.view.hitTest(self.view.convert(point, to: button.view), with: event) {
@@ -449,10 +449,10 @@ final class MessageReactionButtonsNode: ASDisplayNode {
}
}
final class ChatMessageReactionsFooterContentNode: ChatMessageBubbleContentNode {
public final class ChatMessageReactionsFooterContentNode: ChatMessageBubbleContentNode {
private let buttonsNode: MessageReactionButtonsNode
required init() {
required public init() {
self.buttonsNode = MessageReactionButtonsNode()
super.init()
@@ -476,11 +476,11 @@ final class ChatMessageReactionsFooterContentNode: ChatMessageBubbleContentNode
}
}
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 buttonsNode = self.buttonsNode
return { item, layoutConstants, preparePosition, _, constrainedSize, _ in
@@ -529,25 +529,25 @@ final class ChatMessageReactionsFooterContentNode: 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.25)
}
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.25)
}
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.25, removeOnCompletion: false)
self.buttonsNode.animateOut(animation: ListViewItemUpdateAnimation.System(duration: 0.25, transition: ControlledTransition(duration: 0.25, curve: .spring, interactive: false)))
}
override func animateInsertionIntoBubble(_ duration: Double) {
override public func animateInsertionIntoBubble(_ duration: Double) {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
self.layer.animatePosition(from: CGPoint(x: 0.0, y: -self.bounds.height / 2.0), to: CGPoint(), duration: duration, removeOnCompletion: true, additive: true)
}
override func animateRemovalFromBubble(_ duration: Double, completion: @escaping () -> Void) {
override public func animateRemovalFromBubble(_ duration: Double, completion: @escaping () -> Void) {
self.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -self.bounds.height / 2.0), duration: duration, removeOnCompletion: false, additive: true)
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
completion()
@@ -555,38 +555,38 @@ final class ChatMessageReactionsFooterContentNode: ChatMessageBubbleContentNode
self.buttonsNode.animateOut(animation: ListViewItemUpdateAnimation.System(duration: 0.25, transition: ControlledTransition(duration: 0.25, curve: .spring, interactive: false)))
}
override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction {
override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction {
if let result = self.buttonsNode.hitTest(self.view.convert(point, to: self.buttonsNode.view), with: nil), result !== self.buttonsNode.view {
return .ignore
}
return .none
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let result = self.buttonsNode.hitTest(self.view.convert(point, to: self.buttonsNode.view), with: event), result !== self.buttonsNode.view {
return result
}
return nil
}
override func reactionTargetView(value: MessageReaction.Reaction) -> UIView? {
override public func reactionTargetView(value: MessageReaction.Reaction) -> UIView? {
return self.buttonsNode.reactionTargetView(value: value)
}
}
final class ChatMessageReactionButtonsNode: ASDisplayNode {
final class Arguments {
let context: AccountContext
let presentationData: ChatPresentationData
let presentationContext: ChatPresentationContext
let availableReactions: AvailableReactions?
let reactions: ReactionsMessageAttribute
let message: Message
let accountPeer: EnginePeer?
let isIncoming: Bool
let constrainedWidth: CGFloat
public final class ChatMessageReactionButtonsNode: ASDisplayNode {
public final class Arguments {
public let context: AccountContext
public let presentationData: ChatPresentationData
public let presentationContext: ChatPresentationContext
public let availableReactions: AvailableReactions?
public let reactions: ReactionsMessageAttribute
public let message: Message
public let accountPeer: EnginePeer?
public let isIncoming: Bool
public let constrainedWidth: CGFloat
init(
public init(
context: AccountContext,
presentationData: ChatPresentationData,
presentationContext: ChatPresentationContext,
@@ -611,10 +611,10 @@ final class ChatMessageReactionButtonsNode: ASDisplayNode {
private let buttonsNode: MessageReactionButtonsNode
var reactionSelected: ((MessageReaction.Reaction) -> Void)?
var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingView, MessageReaction.Reaction) -> Void)?
public var reactionSelected: ((MessageReaction.Reaction) -> Void)?
public var openReactionPreview: ((ContextGesture?, ContextExtractedContentContainingView, MessageReaction.Reaction) -> Void)?
override init() {
override public init() {
self.buttonsNode = MessageReactionButtonsNode()
super.init()
@@ -630,7 +630,7 @@ final class ChatMessageReactionButtonsNode: ASDisplayNode {
}
}
class func asyncLayout(_ maybeNode: ChatMessageReactionButtonsNode?) -> (_ arguments: ChatMessageReactionButtonsNode.Arguments) -> (minWidth: CGFloat, layout: (CGFloat) -> (size: CGSize, apply: (_ animation: ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode)) {
public class func asyncLayout(_ maybeNode: ChatMessageReactionButtonsNode?) -> (_ arguments: ChatMessageReactionButtonsNode.Arguments) -> (minWidth: CGFloat, layout: (CGFloat) -> (size: CGSize, apply: (_ animation: ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode)) {
return { arguments in
let node = maybeNode ?? ChatMessageReactionButtonsNode()
@@ -660,12 +660,12 @@ final class ChatMessageReactionButtonsNode: ASDisplayNode {
}
}
func animateIn(animation: ListViewItemUpdateAnimation) {
public func animateIn(animation: ListViewItemUpdateAnimation) {
self.buttonsNode.animateIn(animation: animation)
self.buttonsNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
func animateOut(animation: ListViewItemUpdateAnimation, completion: @escaping () -> Void) {
public func animateOut(animation: ListViewItemUpdateAnimation, completion: @escaping () -> Void) {
self.buttonsNode.animateOut(animation: animation)
animation.animator.updateAlpha(layer: self.buttonsNode.layer, alpha: 0.0, completion: { _ in
completion()
@@ -673,30 +673,30 @@ final class ChatMessageReactionButtonsNode: ASDisplayNode {
animation.animator.updateFrame(layer: self.buttonsNode.layer, frame: self.buttonsNode.layer.frame.offsetBy(dx: 0.0, dy: -self.buttonsNode.layer.bounds.height / 2.0), completion: nil)
}
func reactionTargetView(value: MessageReaction.Reaction) -> UIView? {
public func reactionTargetView(value: MessageReaction.Reaction) -> UIView? {
return self.buttonsNode.reactionTargetView(value: value)
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let result = self.buttonsNode.hitTest(self.view.convert(point, to: self.buttonsNode.view), with: event), result !== self.buttonsNode.view {
return result
}
return nil
}
func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
public func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
self.buttonsNode.update(rect: rect, within: containerSize, transition: transition)
}
func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) {
public func update(rect: CGRect, within containerSize: CGSize, transition: CombinedTransition) {
self.buttonsNode.update(rect: rect, within: containerSize, transition: transition)
}
func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
public func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
self.buttonsNode.offset(value: value, animationCurve: animationCurve, duration: duration)
}
func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
public func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) {
self.buttonsNode.offsetSpring(value: value, duration: duration, damping: damping)
}
}

View File

@@ -0,0 +1,21 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ChatMessageSelectionNode",
module_name = "ChatMessageSelectionNode",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/AsyncDisplayKit",
"//submodules/TelegramPresentationData",
"//submodules/CheckNode",
"//submodules/TelegramCore",
],
visibility = [
"//visibility:public",
],
)

View File

@@ -5,13 +5,13 @@ import TelegramPresentationData
import CheckNode
import TelegramCore
final class ChatMessageSelectionNode: ASDisplayNode {
public final class ChatMessageSelectionNode: ASDisplayNode {
private let toggle: (Bool) -> Void
private(set) var selected = false
public private(set) var selected = false
private let checkNode: CheckNode
init(wallpaper: TelegramWallpaper, theme: PresentationTheme, toggle: @escaping (Bool) -> Void) {
public init(wallpaper: TelegramWallpaper, theme: PresentationTheme, toggle: @escaping (Bool) -> Void) {
self.toggle = toggle
let style: CheckNodeTheme.Style
@@ -29,26 +29,26 @@ final class ChatMessageSelectionNode: ASDisplayNode {
self.addSubnode(self.checkNode)
}
override func didLoad() {
override public func didLoad() {
super.didLoad()
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
}
func updateSelected(_ selected: Bool, animated: Bool) {
public func updateSelected(_ selected: Bool, animated: Bool) {
if self.selected != selected {
self.selected = selected
self.checkNode.setSelected(selected, animated: animated)
}
}
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.toggle(!self.selected)
}
}
func updateLayout(size: CGSize, leftInset: CGFloat) {
public func updateLayout(size: CGSize, leftInset: CGFloat) {
let checkSize = CGSize(width: 28.0, height: 28.0)
self.checkNode.frame = CGRect(origin: CGPoint(x: 6.0 + leftInset, y: floor((size.height - checkSize.height) / 2.0)), size: checkSize)
}

View File

@@ -0,0 +1,26 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ChatMessageShareButton",
module_name = "ChatMessageShareButton",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/AsyncDisplayKit",
"//submodules/Display",
"//submodules/TelegramPresentationData",
"//submodules/TelegramUI/Components/ChatControllerInteraction",
"//submodules/AccountContext",
"//submodules/TelegramCore",
"//submodules/Postbox",
"//submodules/WallpaperBackgroundNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon",
],
visibility = [
"//visibility:public",
],
)

View File

@@ -0,0 +1,176 @@
import Foundation
import UIKit
import AsyncDisplayKit
import Display
import TelegramPresentationData
import ChatControllerInteraction
import AccountContext
import TelegramCore
import Postbox
import WallpaperBackgroundNode
import ChatMessageItemCommon
public class ChatMessageShareButton: HighlightableButtonNode {
private var backgroundContent: WallpaperBubbleBackgroundNode?
private var backgroundBlurView: PortalView?
private let iconNode: ASImageNode
private var iconOffset = CGPoint()
private var theme: PresentationTheme?
private var isReplies: Bool = false
private var textNode: ImmediateTextNode?
private var absolutePosition: (CGRect, CGSize)?
public init() {
self.iconNode = ASImageNode()
super.init(pointerStyle: nil)
self.allowsGroupOpacity = true
self.addSubnode(self.iconNode)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func update(presentationData: ChatPresentationData, controllerInteraction: ChatControllerInteraction, chatLocation: ChatLocation, subject: ChatControllerSubject?, message: Message, account: Account, disableComments: Bool = false) -> CGSize {
var isReplies = false
var replyCount = 0
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
for attribute in message.attributes {
if let attribute = attribute as? ReplyThreadMessageAttribute {
replyCount = Int(attribute.count)
isReplies = true
break
}
}
}
if case let .replyThread(replyThreadMessage) = chatLocation, replyThreadMessage.effectiveTopId == message.id {
replyCount = 0
isReplies = false
}
if disableComments {
replyCount = 0
isReplies = false
}
if self.theme !== presentationData.theme.theme || self.isReplies != isReplies {
self.theme = presentationData.theme.theme
self.isReplies = isReplies
var updatedIconImage: UIImage?
var updatedIconOffset = CGPoint()
if case .pinnedMessages = subject {
updatedIconImage = PresentationResourcesChat.chatFreeNavigateButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
updatedIconOffset = CGPoint(x: UIScreenPixel, y: 1.0)
} else if isReplies {
updatedIconImage = PresentationResourcesChat.chatFreeCommentButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
} else if message.id.peerId.isRepliesOrSavedMessages(accountPeerId: account.peerId) {
updatedIconImage = PresentationResourcesChat.chatFreeNavigateButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
updatedIconOffset = CGPoint(x: UIScreenPixel, y: 1.0)
} else {
updatedIconImage = PresentationResourcesChat.chatFreeShareButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
}
//self.backgroundNode.updateColor(color: selectDateFillStaticColor(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper), enableBlur: controllerInteraction.enableFullTranslucency && dateFillNeedsBlur(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper), transition: .immediate)
self.iconNode.image = updatedIconImage
self.iconOffset = updatedIconOffset
}
var size = CGSize(width: 30.0, height: 30.0)
var offsetIcon = false
if isReplies, replyCount > 0 {
offsetIcon = true
let textNode: ImmediateTextNode
if let current = self.textNode {
textNode = current
} else {
textNode = ImmediateTextNode()
self.textNode = textNode
self.addSubnode(textNode)
}
let textColor = bubbleVariableColor(variableColor: presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: presentationData.theme.wallpaper)
let countString: String
if replyCount >= 1000 * 1000 {
countString = "\(replyCount / 1000_000)M"
} else if replyCount >= 1000 {
countString = "\(replyCount / 1000)K"
} else {
countString = "\(replyCount)"
}
textNode.attributedText = NSAttributedString(string: countString, font: Font.regular(11.0), textColor: textColor)
let textSize = textNode.updateLayout(CGSize(width: 100.0, height: 100.0))
size.height += textSize.height - 1.0
textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: size.height - textSize.height - 4.0), size: textSize)
} else if let textNode = self.textNode {
self.textNode = nil
textNode.removeFromSupernode()
}
if self.backgroundBlurView == nil {
if let backgroundBlurView = controllerInteraction.presentationContext.backgroundNode?.makeFreeBackground() {
self.backgroundBlurView = backgroundBlurView
self.view.insertSubview(backgroundBlurView.view, at: 0)
backgroundBlurView.view.clipsToBounds = true
}
}
if let backgroundBlurView = self.backgroundBlurView {
backgroundBlurView.view.frame = CGRect(origin: CGPoint(), size: size)
backgroundBlurView.view.layer.cornerRadius = min(size.width, size.height) / 2.0
}
//self.backgroundNode.frame = CGRect(origin: CGPoint(), size: size)
//self.backgroundNode.update(size: self.backgroundNode.bounds.size, cornerRadius: min(self.backgroundNode.bounds.width, self.backgroundNode.bounds.height) / 2.0, transition: .immediate)
if let image = self.iconNode.image {
self.iconNode.frame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0) + self.iconOffset.x, y: floor((size.width - image.size.width) / 2.0) - (offsetIcon ? 1.0 : 0.0) + self.iconOffset.y), size: image.size)
}
if controllerInteraction.presentationContext.backgroundNode?.hasExtraBubbleBackground() == true {
if self.backgroundContent == nil, let backgroundContent = controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
backgroundContent.clipsToBounds = true
self.backgroundContent = backgroundContent
self.insertSubnode(backgroundContent, at: 0)
}
} else {
self.backgroundContent?.removeFromSupernode()
self.backgroundContent = nil
}
if let backgroundContent = self.backgroundContent {
//self.backgroundNode.isHidden = true
self.backgroundBlurView?.view.isHidden = true
backgroundContent.cornerRadius = min(size.width, size.height) / 2.0
backgroundContent.frame = CGRect(origin: CGPoint(), size: size)
if let (rect, containerSize) = self.absolutePosition {
var backgroundFrame = backgroundContent.frame
backgroundFrame.origin.x += rect.minX
backgroundFrame.origin.y += rect.minY
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
}
} else {
//self.backgroundNode.isHidden = false
self.backgroundBlurView?.view.isHidden = false
}
return size
}
public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
self.absolutePosition = (rect, containerSize)
if let backgroundContent = self.backgroundContent {
var backgroundFrame = backgroundContent.frame
backgroundFrame.origin.x += rect.minX
backgroundFrame.origin.y += rect.minY
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
}
}
}

View File

@@ -0,0 +1,45 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ChatMessageStickerItemNode",
module_name = "ChatMessageStickerItemNode",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/AsyncDisplayKit",
"//submodules/Display",
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/Postbox",
"//submodules/TelegramCore",
"//submodules/TelegramPresentationData",
"//submodules/TextFormat",
"//submodules/AccountContext",
"//submodules/StickerResources",
"//submodules/ContextUI",
"//submodules/Markdown",
"//submodules/ShimmerEffect",
"//submodules/WallpaperBackgroundNode",
"//submodules/TelegramUI/Components/ChatControllerInteraction",
"//submodules/TelegramUI/Components/Chat/ChatMessageForwardInfoNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon",
"//submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageItem",
"//submodules/TelegramUI/Components/Chat/ChatMessageItemView",
"//submodules/TelegramUI/Components/Chat/ChatMessageSwipeToReplyNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageSelectionNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageDeliveryFailedNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageShareButton",
"//submodules/TelegramUI/Components/Chat/ChatMessageThreadInfoNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageActionButtonsNode",
"//submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode",
"//submodules/TelegramUI/Components/Chat/ChatSwipeToReplyRecognizer",
],
visibility = [
"//visibility:public",
],
)

View File

@@ -20,18 +20,26 @@ import ChatMessageItemCommon
import ChatMessageReplyInfoNode
import ChatMessageItem
import ChatMessageItemView
import ChatMessageSwipeToReplyNode
import ChatMessageSelectionNode
import ChatMessageDeliveryFailedNode
import ChatMessageShareButton
import ChatMessageThreadInfoNode
import ChatMessageActionButtonsNode
import ChatMessageReactionsFooterContentNode
import ChatSwipeToReplyRecognizer
private let nameFont = Font.medium(14.0)
private let inlineBotPrefixFont = Font.regular(14.0)
private let inlineBotNameFont = nameFont
class ChatMessageStickerItemNode: ChatMessageItemView {
let contextSourceNode: ContextExtractedContentContainingNode
public class ChatMessageStickerItemNode: ChatMessageItemView {
public let contextSourceNode: ContextExtractedContentContainingNode
private let containerNode: ContextControllerSourceNode
let imageNode: TransformImageNode
public let imageNode: TransformImageNode
private var backgroundNode: WallpaperBubbleBackgroundNode?
private var placeholderNode: StickerShimmerEffectNode
var textNode: TextNode?
public var textNode: TextNode?
private var swipeToReplyNode: ChatMessageSwipeToReplyNode?
private var swipeToReplyFeedback: HapticFeedback?
@@ -40,7 +48,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
private var deliveryFailedNode: ChatMessageDeliveryFailedNode?
private var shareButtonNode: ChatMessageShareButton?
var telegramFile: TelegramMediaFile?
public var telegramFile: TelegramMediaFile?
private let fetchDisposable = MetaDisposable()
private var viaBotNode: TextNode?
@@ -67,7 +75,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
private var enableSynchronousImageApply: Bool = false
override var visibility: ListViewItemNodeVisibility {
override public var visibility: ListViewItemNodeVisibility {
didSet {
let wasVisible = oldValue != .none
let isVisible = self.visibility != .none
@@ -87,7 +95,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
required init() {
required public init() {
self.contextSourceNode = ContextExtractedContentContainingNode()
self.containerNode = ContextControllerSourceNode()
self.imageNode = TransformImageNode()
@@ -171,7 +179,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
required init?(coder aDecoder: NSCoder) {
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@@ -179,7 +187,6 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
self.fetchDisposable.dispose()
}
private func removePlaceholder(animated: Bool) {
if !animated {
self.placeholderNode.removeFromSupernode()
@@ -191,7 +198,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
override func didLoad() {
override public func didLoad() {
super.didLoad()
let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:)))
@@ -268,7 +275,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
self.view.addGestureRecognizer(replyRecognizer)
}
override func setupItem(_ item: ChatMessageItem, synchronousLoad: Bool) {
override public func setupItem(_ item: ChatMessageItem, synchronousLoad: Bool) {
super.setupItem(item, synchronousLoad: synchronousLoad)
self.replyRecognizer?.allowBothDirections = !item.context.sharedContext.immediateExperimentalUISettings.unidirectionalSwipeToReply
@@ -291,7 +298,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
private var absoluteRect: (CGRect, CGSize)?
override func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
override public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
self.absoluteRect = (rect, containerSize)
if !self.contextSourceNode.isExtractedToContextPreview {
var rect = rect
@@ -345,7 +352,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
override func applyAbsoluteOffset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
override public func applyAbsoluteOffset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
if let backgroundNode = self.backgroundNode {
backgroundNode.offset(value: value, animationCurve: animationCurve, duration: duration)
}
@@ -355,7 +362,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
override func updateAccessibilityData(_ accessibilityData: ChatMessageAccessibilityData) {
override public func updateAccessibilityData(_ accessibilityData: ChatMessageAccessibilityData) {
super.updateAccessibilityData(accessibilityData)
self.messageAccessibilityArea.accessibilityLabel = accessibilityData.label
@@ -386,7 +393,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
override func asyncLayout() -> (_ item: ChatMessageItem, _ params: ListViewItemLayoutParams, _ mergedTop: ChatMessageMerge, _ mergedBottom: ChatMessageMerge, _ dateHeaderAtBottom: Bool) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation, ListViewItemApply, Bool) -> Void) {
override public func asyncLayout() -> (_ item: ChatMessageItem, _ params: ListViewItemLayoutParams, _ mergedTop: ChatMessageMerge, _ mergedBottom: ChatMessageMerge, _ dateHeaderAtBottom: Bool) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation, ListViewItemApply, Bool) -> Void) {
let displaySize = CGSize(width: 184.0, height: 184.0)
let telegramFile = self.telegramFile
let layoutConstants = self.layoutConstants
@@ -1261,7 +1268,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
@objc func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
@objc private func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
switch recognizer.state {
case .ended:
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
@@ -1383,7 +1390,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
return nil
}
@objc func shareButtonPressed() {
@objc private func shareButtonPressed() {
if let item = self.item {
if case .pinnedMessages = item.associatedData.subject {
item.controllerInteraction.navigateToMessageStandalone(item.content.firstMessage.id)
@@ -1415,7 +1422,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
private var playedSwipeToReplyHaptic = false
@objc func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) {
@objc private func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) {
var offset: CGFloat = 0.0
var leftOffset: CGFloat = 0.0
var swipeOffset: CGFloat = 45.0
@@ -1537,7 +1544,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let shareButtonNode = self.shareButtonNode, shareButtonNode.frame.contains(point) {
return shareButtonNode.view
}
@@ -1552,7 +1559,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
return super.hitTest(point, with: event)
}
override func updateSelectionState(animated: Bool) {
override public func updateSelectionState(animated: Bool) {
guard let item = self.item else {
return
}
@@ -1660,7 +1667,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
override func updateHighlightedState(animated: Bool) {
override public func updateHighlightedState(animated: Bool) {
super.updateHighlightedState(animated: animated)
if let item = self.item {
@@ -1683,37 +1690,51 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
override func cancelInsertionAnimations() {
override public func cancelInsertionAnimations() {
self.layer.removeAllAnimations()
}
override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
super.animateInsertion(currentTimestamp, duration: duration, short: short)
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) {
super.animateRemoved(currentTimestamp, duration: duration)
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
}
override func animateAdded(_ currentTimestamp: Double, duration: Double) {
override public func animateAdded(_ currentTimestamp: Double, duration: Double) {
super.animateAdded(currentTimestamp, duration: duration)
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
override func getMessageContextSourceNode(stableId: UInt32?) -> ContextExtractedContentContainingNode? {
override public func getMessageContextSourceNode(stableId: UInt32?) -> ContextExtractedContentContainingNode? {
return self.contextSourceNode
}
override func addAccessoryItemNode(_ accessoryItemNode: ListViewAccessoryItemNode) {
override public func addAccessoryItemNode(_ accessoryItemNode: ListViewAccessoryItemNode) {
self.contextSourceNode.contentNode.addSubnode(accessoryItemNode)
}
func animateContentFromTextInputField(textInput: ChatMessageTransitionNode.Source.TextInput, transition: CombinedTransition) {
public final class AnimationTransitionTextInput {
public let backgroundView: UIView
public let contentView: UIView
public let sourceRect: CGRect
public let scrollOffset: CGFloat
public init(backgroundView: UIView, contentView: UIView, sourceRect: CGRect, scrollOffset: CGFloat) {
self.backgroundView = backgroundView
self.contentView = contentView
self.sourceRect = sourceRect
self.scrollOffset = scrollOffset
}
}
public func animateContentFromTextInputField(textInput: AnimationTransitionTextInput, transition: CombinedTransition) {
guard let _ = self.item else {
return
}
@@ -1766,7 +1787,55 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
self.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: self.dateAndStatusNode.alpha, duration: 0.15, delay: 0.16)
}
func animateContentFromStickerGridItem(stickerSource: ChatMessageTransitionNode.Sticker, transition: CombinedTransition) {
public final class AnimationTransitionSticker {
public let imageNode: TransformImageNode?
public let animationNode: ASDisplayNode?
public let placeholderNode: ASDisplayNode?
public let imageLayer: CALayer?
public let relativeSourceRect: CGRect
var sourceFrame: CGRect {
if let imageNode = self.imageNode {
return imageNode.frame
} else if let imageLayer = self.imageLayer {
return imageLayer.bounds
} else {
return CGRect(origin: CGPoint(), size: relativeSourceRect.size)
}
}
var sourceLayer: CALayer? {
if let imageNode = self.imageNode {
return imageNode.layer
} else if let imageLayer = self.imageLayer {
return imageLayer
} else {
return nil
}
}
func snapshotContentTree() -> UIView? {
if let animationNode = self.animationNode {
return animationNode.view.snapshotContentTree()
} else if let imageNode = self.imageNode {
return imageNode.view.snapshotContentTree()
} else if let sourceLayer = self.imageLayer {
return sourceLayer.snapshotContentTreeAsView()
} else {
return nil
}
}
public init(imageNode: TransformImageNode?, animationNode: ASDisplayNode?, placeholderNode: ASDisplayNode?, imageLayer: CALayer?, relativeSourceRect: CGRect) {
self.imageNode = imageNode
self.animationNode = animationNode
self.placeholderNode = placeholderNode
self.imageLayer = imageLayer
self.relativeSourceRect = relativeSourceRect
}
}
public func animateContentFromStickerGridItem(stickerSource: AnimationTransitionSticker, transition: CombinedTransition) {
guard let _ = self.item else {
return
}
@@ -1851,7 +1920,25 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
func animateReplyPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, transition: CombinedTransition) {
public final class AnimationTransitionReplyPanel {
public let titleNode: ASDisplayNode
public let textNode: ASDisplayNode
public let lineNode: ASDisplayNode
public let imageNode: ASDisplayNode
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
self.relativeSourceRect = relativeSourceRect
self.relativeTargetRect = relativeTargetRect
}
}
public func animateReplyPanel(sourceReplyPanel: AnimationTransitionReplyPanel, transition: CombinedTransition) {
if let replyInfoNode = self.replyInfoNode {
let localRect = self.contextSourceNode.contentNode.view.convert(sourceReplyPanel.relativeSourceRect, to: replyInfoNode.view)
let mappedPanel = ChatMessageReplyInfoNode.TransitionReplyPanel(
@@ -1871,7 +1958,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
func animateFromLoadingPlaceholder(messageContainer: ChatLoadingPlaceholderMessageContainer, delay: Double, transition: ContainedViewLayoutTransition) {
public func animateFromLoadingPlaceholder(delay: Double, transition: ContainedViewLayoutTransition) {
guard let item = self.item else {
return
}
@@ -1881,14 +1968,14 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
transition.animateTransformScale(node: self, from: CGPoint(x: 0.85, y: 0.85), delay: delay)
}
override func openMessageContextMenu() {
override public func openMessageContextMenu() {
guard let item = self.item else {
return
}
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, nil, nil)
}
override func targetForStoryTransition(id: StoryId) -> UIView? {
override public func targetForStoryTransition(id: StoryId) -> UIView? {
guard let item = self.item else {
return nil
}
@@ -1904,7 +1991,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
return nil
}
override func targetReactionView(value: MessageReaction.Reaction) -> UIView? {
override public func targetReactionView(value: MessageReaction.Reaction) -> UIView? {
if let result = self.reactionButtonsNode?.reactionTargetView(value: value) {
return result
}
@@ -1914,7 +2001,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
return nil
}
override func contentFrame() -> CGRect {
override public func contentFrame() -> CGRect {
return self.imageNode.frame
}
}

View File

@@ -0,0 +1,22 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ChatMessageSwipeToReplyNode",
module_name = "ChatMessageSwipeToReplyNode",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/Display",
"//submodules/AsyncDisplayKit",
"//submodules/AppBundle",
"//submodules/WallpaperBackgroundNode",
"//submodules/TelegramUI/Components/ChatControllerInteraction",
],
visibility = [
"//visibility:public",
],
)

View File

@@ -8,8 +8,8 @@ import ChatControllerInteraction
private let size = CGSize(width: 33.0, height: 33.0)
final class ChatMessageSwipeToReplyNode: ASDisplayNode {
enum Action {
public final class ChatMessageSwipeToReplyNode: ASDisplayNode {
public enum Action {
case reply
case like
case unlike
@@ -27,7 +27,7 @@ final class ChatMessageSwipeToReplyNode: ASDisplayNode {
private var absolutePosition: (CGRect, CGSize)?
init(fillColor: UIColor, enableBlur: Bool, foregroundColor: UIColor, backgroundNode: WallpaperBackgroundNode?, action: ChatMessageSwipeToReplyNode.Action) {
public init(fillColor: UIColor, enableBlur: Bool, foregroundColor: UIColor, backgroundNode: WallpaperBackgroundNode?, action: ChatMessageSwipeToReplyNode.Action) {
self.backgroundNode = NavigationBackgroundNode(color: fillColor, enableBlur: enableBlur)
self.backgroundNode.isUserInteractionEnabled = false
@@ -138,7 +138,7 @@ final class ChatMessageSwipeToReplyNode: ASDisplayNode {
}
}
override func didLoad() {
override public func didLoad() {
super.didLoad()
if let backgroundContent = self.backgroundContent {
@@ -149,7 +149,7 @@ final class ChatMessageSwipeToReplyNode: ASDisplayNode {
}
private var animatedWave = false
func updateProgress(_ progress: CGFloat) {
public func updateProgress(_ progress: CGFloat) {
let progress = max(0.0, min(1.0, progress))
var foregroundProgress = min(1.0, progress * 1.2)
var scaleProgress = 0.65 + foregroundProgress * 0.35
@@ -175,7 +175,7 @@ final class ChatMessageSwipeToReplyNode: ASDisplayNode {
}
}
func playSuccessAnimation() {
public func playSuccessAnimation() {
guard !self.animatedWave else {
return
}
@@ -214,7 +214,7 @@ final class ChatMessageSwipeToReplyNode: ASDisplayNode {
self.fillLayer.animate(from: path, to: targetPath, keyPath: "path", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.3)
}
func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
self.absolutePosition = (rect, containerSize)
if let backgroundContent = self.backgroundContent {
var backgroundFrame = backgroundContent.frame
@@ -225,7 +225,7 @@ final class ChatMessageSwipeToReplyNode: ASDisplayNode {
}
}
extension ChatMessageSwipeToReplyNode.Action {
public extension ChatMessageSwipeToReplyNode.Action {
init(_ action: ChatControllerInteractionSwipeAction?) {
if let action = action {
switch action {

View File

@@ -0,0 +1,36 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ChatMessageThreadInfoNode",
module_name = "ChatMessageThreadInfoNode",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/AsyncDisplayKit",
"//submodules/Postbox",
"//submodules/Display",
"//submodules/TelegramCore",
"//submodules/SSignalKit/SwiftSignalKit",
"//submodules/TelegramPresentationData",
"//submodules/AccountContext",
"//submodules/LocalizedPeerData",
"//submodules/PhotoResources",
"//submodules/TelegramStringFormatting",
"//submodules/TextFormat",
"//submodules/InvisibleInkDustNode",
"//submodules/TelegramUI/Components/TextNodeWithEntities",
"//submodules/TelegramUI/Components/AnimationCache",
"//submodules/TelegramUI/Components/MultiAnimationRenderer",
"//submodules/ComponentFlow",
"//submodules/TelegramUI/Components/EmojiStatusComponent",
"//submodules/WallpaperBackgroundNode",
"//submodules/TelegramUI/Components/ChatControllerInteraction",
],
visibility = [
"//visibility:public",
],
)

View File

@@ -172,25 +172,25 @@ private func generateRectsImage(color: UIColor, rects: [CGRect], inset: CGFloat,
}))
}
enum ChatMessageThreadInfoType {
public enum ChatMessageThreadInfoType {
case bubble(incoming: Bool)
case standalone
}
class ChatMessageThreadInfoNode: ASDisplayNode {
class Arguments {
let presentationData: ChatPresentationData
let strings: PresentationStrings
let context: AccountContext
let controllerInteraction: ChatControllerInteraction
let type: ChatMessageThreadInfoType
let threadId: Int64
let parentMessage: Message
let constrainedSize: CGSize
let animationCache: AnimationCache?
let animationRenderer: MultiAnimationRenderer?
public class ChatMessageThreadInfoNode: ASDisplayNode {
public class Arguments {
public let presentationData: ChatPresentationData
public let strings: PresentationStrings
public let context: AccountContext
public let controllerInteraction: ChatControllerInteraction
public let type: ChatMessageThreadInfoType
public let threadId: Int64
public let parentMessage: Message
public let constrainedSize: CGSize
public let animationCache: AnimationCache?
public let animationRenderer: MultiAnimationRenderer?
init(
public init(
presentationData: ChatPresentationData,
strings: PresentationStrings,
context: AccountContext,
@@ -215,7 +215,7 @@ class ChatMessageThreadInfoNode: ASDisplayNode {
}
}
var visibility: Bool = false {
public var visibility: Bool = false {
didSet {
if self.visibility != oldValue {
self.textNode?.visibilityRect = self.visibility ? CGRect.infinite : nil
@@ -249,7 +249,7 @@ class ChatMessageThreadInfoNode: ASDisplayNode {
private var absolutePosition: (CGRect, CGSize)?
override init() {
override public init() {
self.contentNode = HighlightTrackingButtonNode()
self.contentBackgroundNode = ASImageNode()
@@ -298,7 +298,7 @@ class ChatMessageThreadInfoNode: ASDisplayNode {
self.pressed()
}
func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
self.absolutePosition = (rect, containerSize)
if let backgroundContent = self.backgroundContent {
var backgroundFrame = backgroundContent.frame
@@ -308,7 +308,7 @@ class ChatMessageThreadInfoNode: ASDisplayNode {
}
}
class func asyncLayout(_ maybeNode: ChatMessageThreadInfoNode?) -> (_ arguments: Arguments) -> (CGSize, (Bool) -> ChatMessageThreadInfoNode) {
public class func asyncLayout(_ maybeNode: ChatMessageThreadInfoNode?) -> (_ arguments: Arguments) -> (CGSize, (Bool) -> ChatMessageThreadInfoNode) {
let textNodeLayout = TextNodeWithEntities.asyncLayout(maybeNode?.textNode)
return { arguments in

View File

@@ -0,0 +1,17 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "ChatSwipeToReplyRecognizer",
module_name = "ChatSwipeToReplyRecognizer",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
],
visibility = [
"//visibility:public",
],
)

View File

@@ -1,26 +1,26 @@
import Foundation
import UIKit
class ChatSwipeToReplyRecognizer: UIPanGestureRecognizer {
var validatedGesture = false
var firstLocation: CGPoint = CGPoint()
var allowBothDirections: Bool = true
public class ChatSwipeToReplyRecognizer: UIPanGestureRecognizer {
public var validatedGesture = false
public var firstLocation: CGPoint = CGPoint()
public var allowBothDirections: Bool = true
var shouldBegin: (() -> Bool)?
public var shouldBegin: (() -> Bool)?
override init(target: Any?, action: Selector?) {
override public init(target: Any?, action: Selector?) {
super.init(target: target, action: action)
self.maximumNumberOfTouches = 1
}
override func reset() {
override public func reset() {
super.reset()
self.validatedGesture = false
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
if let shouldBegin = self.shouldBegin, !shouldBegin() {
@@ -31,7 +31,7 @@ class ChatSwipeToReplyRecognizer: UIPanGestureRecognizer {
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
override public func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
let location = touches.first!.location(in: self.view)
let translation = CGPoint(x: location.x - firstLocation.x, y: location.y - firstLocation.y)

View File

@@ -109,6 +109,7 @@ import TextSelectionNode
import ChatMessagePollBubbleContentNode
import ChatMessageItem
import ChatMessageItemView
import ChatMessageItemCommon
public enum ChatControllerPeekActions {
case standard
@@ -19523,31 +19524,6 @@ extension Peer {
}
}
func canAddMessageReactions(message: Message) -> Bool {
if message.id.namespace != Namespaces.Message.Cloud {
return false
}
if let peer = message.peers[message.id.peerId] {
if let _ = peer as? TelegramSecretChat {
return false
}
} else {
return false
}
for media in message.media {
if let _ = media as? TelegramMediaAction {
return false
} else if let story = media as? TelegramMediaStory {
if story.isMention {
return false
}
} else if let _ = media as? TelegramMediaExpiredContent {
return false
}
}
return true
}
enum AllowedReactions {
case set(Set<MessageReaction.Reaction>)
case all

View File

@@ -33,6 +33,7 @@ import ChatInputContextPanelNode
import TextSelectionNode
import ReplyAccessoryPanelNode
import ChatMessageItemView
import ChatMessageSelectionNode
final class VideoNavigationControllerDropContentItem: NavigationControllerDropContentItem {
let itemNode: OverlayMediaItemNode

View File

@@ -6,6 +6,7 @@ import Emoji
import AccountContext
import TelegramPresentationData
import ChatHistoryEntry
import ChatMessageItemCommon
func chatHistoryEntriesForView(
location: ChatLocation,

View File

@@ -29,12 +29,6 @@ import ChatBotInfoItem
import ChatMessageItem
import ChatMessageItemView
extension ChatReplyThreadMessage {
var effectiveTopId: MessageId {
return self.channelMessageId ?? self.messageId
}
}
struct ChatTopVisibleMessageRange: Equatable {
var lowerBound: MessageIndex
var upperBound: MessageIndex

View File

@@ -12,6 +12,7 @@ import ChatPresentationInterfaceState
import AccountContext
import ChatMessageItem
import ChatMessageItemView
import ChatMessageStickerItemNode
final class ChatLoadingNode: ASDisplayNode {
private let backgroundNode: NavigationBackgroundNode
@@ -95,7 +96,7 @@ final class ChatLoadingPlaceholderMessageContainer {
if let bubbleItemNode = listItemNode as? ChatMessageBubbleItemNode {
bubbleItemNode.animateFromLoadingPlaceholder(messageContainer: self, delay: delay, transition: transition)
} else if let stickerItemNode = listItemNode as? ChatMessageStickerItemNode {
stickerItemNode.animateFromLoadingPlaceholder(messageContainer: self, delay: delay, transition: transition)
stickerItemNode.animateFromLoadingPlaceholder(delay: delay, transition: transition)
} else if let stickerItemNode = listItemNode as? ChatMessageAnimatedStickerItemNode {
stickerItemNode.animateFromLoadingPlaceholder(messageContainer: self, delay: delay, transition: transition)
} else if let videoItemNode = listItemNode as? ChatMessageInstantVideoItemNode {

View File

@@ -35,6 +35,14 @@ import ChatMessageBubbleContentNode
import ChatMessageReplyInfoNode
import ChatMessageItem
import ChatMessageItemView
import ChatMessageSwipeToReplyNode
import ChatMessageSelectionNode
import ChatMessageDeliveryFailedNode
import ChatMessageShareButton
import ChatMessageThreadInfoNode
import ChatMessageActionButtonsNode
import ChatSwipeToReplyRecognizer
import ChatMessageReactionsFooterContentNode
private let nameFont = Font.medium(14.0)
private let inlineBotPrefixFont = Font.regular(14.0)
@@ -63,174 +71,6 @@ extension SlotMachineAnimationNode: GenericAnimatedStickerNode {
}
}
class ChatMessageShareButton: HighlightableButtonNode {
private var backgroundContent: WallpaperBubbleBackgroundNode?
//private let backgroundNode: NavigationBackgroundNode
private var backgroundBlurView: PortalView?
private let iconNode: ASImageNode
private var iconOffset = CGPoint()
private var theme: PresentationTheme?
private var isReplies: Bool = false
private var textNode: ImmediateTextNode?
private var absolutePosition: (CGRect, CGSize)?
init() {
//self.backgroundNode = NavigationBackgroundNode(color: .clear)
self.iconNode = ASImageNode()
super.init(pointerStyle: nil)
self.allowsGroupOpacity = true
//self.addSubnode(self.backgroundNode)
self.addSubnode(self.iconNode)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(presentationData: ChatPresentationData, controllerInteraction: ChatControllerInteraction, chatLocation: ChatLocation, subject: ChatControllerSubject?, message: Message, account: Account, disableComments: Bool = false) -> CGSize {
var isReplies = false
var replyCount = 0
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
for attribute in message.attributes {
if let attribute = attribute as? ReplyThreadMessageAttribute {
replyCount = Int(attribute.count)
isReplies = true
break
}
}
}
if case let .replyThread(replyThreadMessage) = chatLocation, replyThreadMessage.effectiveTopId == message.id {
replyCount = 0
isReplies = false
}
if disableComments {
replyCount = 0
isReplies = false
}
if self.theme !== presentationData.theme.theme || self.isReplies != isReplies {
self.theme = presentationData.theme.theme
self.isReplies = isReplies
var updatedIconImage: UIImage?
var updatedIconOffset = CGPoint()
if case .pinnedMessages = subject {
updatedIconImage = PresentationResourcesChat.chatFreeNavigateButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
updatedIconOffset = CGPoint(x: UIScreenPixel, y: 1.0)
} else if isReplies {
updatedIconImage = PresentationResourcesChat.chatFreeCommentButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
} else if message.id.peerId.isRepliesOrSavedMessages(accountPeerId: account.peerId) {
updatedIconImage = PresentationResourcesChat.chatFreeNavigateButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
updatedIconOffset = CGPoint(x: UIScreenPixel, y: 1.0)
} else {
updatedIconImage = PresentationResourcesChat.chatFreeShareButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper)
}
//self.backgroundNode.updateColor(color: selectDateFillStaticColor(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper), enableBlur: controllerInteraction.enableFullTranslucency && dateFillNeedsBlur(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper), transition: .immediate)
self.iconNode.image = updatedIconImage
self.iconOffset = updatedIconOffset
}
var size = CGSize(width: 30.0, height: 30.0)
var offsetIcon = false
if isReplies, replyCount > 0 {
offsetIcon = true
let textNode: ImmediateTextNode
if let current = self.textNode {
textNode = current
} else {
textNode = ImmediateTextNode()
self.textNode = textNode
self.addSubnode(textNode)
}
let textColor = bubbleVariableColor(variableColor: presentationData.theme.theme.chat.message.shareButtonForegroundColor, wallpaper: presentationData.theme.wallpaper)
let countString: String
if replyCount >= 1000 * 1000 {
countString = "\(replyCount / 1000_000)M"
} else if replyCount >= 1000 {
countString = "\(replyCount / 1000)K"
} else {
countString = "\(replyCount)"
}
textNode.attributedText = NSAttributedString(string: countString, font: Font.regular(11.0), textColor: textColor)
let textSize = textNode.updateLayout(CGSize(width: 100.0, height: 100.0))
size.height += textSize.height - 1.0
textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: size.height - textSize.height - 4.0), size: textSize)
} else if let textNode = self.textNode {
self.textNode = nil
textNode.removeFromSupernode()
}
if self.backgroundBlurView == nil {
if let backgroundBlurView = controllerInteraction.presentationContext.backgroundNode?.makeFreeBackground() {
self.backgroundBlurView = backgroundBlurView
self.view.insertSubview(backgroundBlurView.view, at: 0)
backgroundBlurView.view.clipsToBounds = true
}
}
if let backgroundBlurView = self.backgroundBlurView {
backgroundBlurView.view.frame = CGRect(origin: CGPoint(), size: size)
backgroundBlurView.view.layer.cornerRadius = min(size.width, size.height) / 2.0
}
//self.backgroundNode.frame = CGRect(origin: CGPoint(), size: size)
//self.backgroundNode.update(size: self.backgroundNode.bounds.size, cornerRadius: min(self.backgroundNode.bounds.width, self.backgroundNode.bounds.height) / 2.0, transition: .immediate)
if let image = self.iconNode.image {
self.iconNode.frame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0) + self.iconOffset.x, y: floor((size.width - image.size.width) / 2.0) - (offsetIcon ? 1.0 : 0.0) + self.iconOffset.y), size: image.size)
}
if controllerInteraction.presentationContext.backgroundNode?.hasExtraBubbleBackground() == true {
if self.backgroundContent == nil, let backgroundContent = controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
backgroundContent.clipsToBounds = true
self.backgroundContent = backgroundContent
self.insertSubnode(backgroundContent, at: 0)
}
} else {
self.backgroundContent?.removeFromSupernode()
self.backgroundContent = nil
}
if let backgroundContent = self.backgroundContent {
//self.backgroundNode.isHidden = true
self.backgroundBlurView?.view.isHidden = true
backgroundContent.cornerRadius = min(size.width, size.height) / 2.0
backgroundContent.frame = CGRect(origin: CGPoint(), size: size)
if let (rect, containerSize) = self.absolutePosition {
var backgroundFrame = backgroundContent.frame
backgroundFrame.origin.x += rect.minX
backgroundFrame.origin.y += rect.minY
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
}
} else {
//self.backgroundNode.isHidden = false
self.backgroundBlurView?.view.isHidden = false
}
return size
}
func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
self.absolutePosition = (rect, containerSize)
if let backgroundContent = self.backgroundContent {
var backgroundFrame = backgroundContent.frame
backgroundFrame.origin.x += rect.minX
backgroundFrame.origin.y += rect.minY
backgroundContent.update(rect: backgroundFrame, within: containerSize, transition: .immediate)
}
}
}
class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
let contextSourceNode: ContextExtractedContentContainingNode
private let containerNode: ContextControllerSourceNode

View File

@@ -43,12 +43,14 @@ import ChatMessageWebpageBubbleContentNode
import ChatMessagePollBubbleContentNode
import ChatMessageItem
import ChatMessageItemView
enum InternalBubbleTapAction {
case action(() -> Void)
case optionalAction(() -> Void)
case openContextMenu(tapMessage: Message, selectAll: Bool, subFrame: CGRect)
}
import ChatMessageSwipeToReplyNode
import ChatMessageSelectionNode
import ChatMessageDeliveryFailedNode
import ChatMessageShareButton
import ChatMessageThreadInfoNode
import ChatMessageActionButtonsNode
import ChatSwipeToReplyRecognizer
import ChatMessageReactionsFooterContentNode
private struct BubbleItemAttributes {
var isAttachment: Bool
@@ -67,50 +69,6 @@ private final class ChatMessageBubbleClippingNode: ASDisplayNode {
}
}
func hasCommentButton(item: ChatMessageItem) -> Bool {
let firstMessage = item.content.firstMessage
var hasDiscussion = false
if let channel = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
hasDiscussion = true
}
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.effectiveTopId == firstMessage.id {
hasDiscussion = false
}
if firstMessage.adAttribute != nil {
hasDiscussion = false
}
if hasDiscussion {
var canComment = false
if case .pinnedMessages = item.associatedData.subject {
canComment = false
} else if firstMessage.id.namespace == Namespaces.Message.Local {
canComment = true
} else {
for attribute in firstMessage.attributes {
if let attribute = attribute as? ReplyThreadMessageAttribute, let commentsPeerId = attribute.commentsPeerId {
switch item.associatedData.channelDiscussionGroup {
case .unknown:
canComment = true
case let .known(groupId):
canComment = groupId == commentsPeerId
}
break
}
}
}
if canComment {
return true
}
} else if firstMessage.id.peerId.isReplies {
return true
}
return false
}
private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([(Message, AnyClass, ChatMessageEntryAttributes, BubbleItemAttributes)], Bool, Bool) {
var result: [(Message, AnyClass, ChatMessageEntryAttributes, BubbleItemAttributes)] = []
var skipText = false

View File

@@ -21,6 +21,13 @@ import ChatMessageReplyInfoNode
import ChatMessageInteractiveInstantVideoNode
import ChatMessageItem
import ChatMessageItemView
import ChatMessageSwipeToReplyNode
import ChatMessageSelectionNode
import ChatMessageDeliveryFailedNode
import ChatMessageShareButton
import ChatMessageActionButtonsNode
import ChatSwipeToReplyRecognizer
import ChatMessageReactionsFooterContentNode
private let nameFont = Font.medium(14.0)

View File

@@ -14,6 +14,7 @@ import ChatControllerInteraction
import ChatHistoryEntry
import ChatMessageItem
import ChatMessageItemView
import ChatMessageStickerItemNode
private func mediaMergeableStyle(_ media: Media) -> ChatMessageMerge {
if let story = media as? TelegramMediaStory, story.isMention {

View File

@@ -14,6 +14,7 @@ import FeaturedStickersScreen
import ChatTextInputMediaRecordingButton
import ReplyAccessoryPanelNode
import ChatMessageItemView
import ChatMessageStickerItemNode
private func convertAnimatingSourceRect(_ rect: CGRect, fromView: UIView, toView: UIView?) -> CGRect {
if let presentationLayer = fromView.layer.presentation() {
@@ -418,9 +419,27 @@ public final class ChatMessageTransitionNode: ASDisplayNode, ChatMessageTransiti
itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: combinedTransition)
}
} else if let itemNode = self.itemNode as? ChatMessageStickerItemNode {
itemNode.animateContentFromTextInputField(textInput: textInput, transition: combinedTransition)
itemNode.animateContentFromTextInputField(
textInput: ChatMessageStickerItemNode.AnimationTransitionTextInput(
backgroundView: textInput.backgroundView,
contentView: textInput.contentView,
sourceRect: textInput.sourceRect,
scrollOffset: textInput.scrollOffset
),
transition: combinedTransition
)
if let sourceReplyPanel = sourceReplyPanel {
itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: combinedTransition)
itemNode.animateReplyPanel(
sourceReplyPanel: ChatMessageStickerItemNode.AnimationTransitionReplyPanel(
titleNode: sourceReplyPanel.titleNode,
textNode: sourceReplyPanel.textNode,
lineNode: sourceReplyPanel.lineNode,
imageNode: sourceReplyPanel.imageNode,
relativeSourceRect: sourceReplyPanel.relativeSourceRect,
relativeTargetRect: sourceReplyPanel.relativeTargetRect
),
transition: combinedTransition
)
}
}
case let .stickerMediaInput(stickerMediaInput, replyPanel):
@@ -470,9 +489,28 @@ public final class ChatMessageTransitionNode: ASDisplayNode, ChatMessageTransiti
itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: combinedTransition)
}
} else if let itemNode = self.itemNode as? ChatMessageStickerItemNode {
itemNode.animateContentFromStickerGridItem(stickerSource: stickerSource, transition: combinedTransition)
itemNode.animateContentFromStickerGridItem(
stickerSource: ChatMessageStickerItemNode.AnimationTransitionSticker(
imageNode: stickerSource.imageNode,
animationNode: stickerSource.animationNode,
placeholderNode: stickerSource.placeholderNode,
imageLayer: stickerSource.imageLayer,
relativeSourceRect: stickerSource.relativeSourceRect
),
transition: combinedTransition
)
if let sourceReplyPanel = sourceReplyPanel {
itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: combinedTransition)
itemNode.animateReplyPanel(
sourceReplyPanel: ChatMessageStickerItemNode.AnimationTransitionReplyPanel(
titleNode: sourceReplyPanel.titleNode,
textNode: sourceReplyPanel.textNode,
lineNode: sourceReplyPanel.lineNode,
imageNode: sourceReplyPanel.imageNode,
relativeSourceRect: sourceReplyPanel.relativeSourceRect,
relativeTargetRect: sourceReplyPanel.relativeTargetRect
),
transition: combinedTransition
)
}
}

View File

@@ -1,39 +0,0 @@
import Foundation
import UIKit
import Postbox
import TelegramCore
import Emoji
func messageIsElligibleForLargeEmoji(_ message: Message) -> Bool {
if !message.text.isEmpty && message.text.containsOnlyEmoji {
if !(message.textEntitiesAttribute?.entities.isEmpty ?? true) {
return false
}
return true
} else {
return false
}
}
func messageIsElligibleForLargeCustomEmoji(_ message: Message) -> Bool {
let text = message.text.replacingOccurrences(of: "\n", with: "").replacingOccurrences(of: " ", with: "")
guard !text.isEmpty && text.containsOnlyEmoji else {
return false
}
let entities = message.textEntitiesAttribute?.entities ?? []
guard entities.count > 0 else {
return false
}
for entity in entities {
if case let .CustomEmoji(_, fileId) = entity.type {
if let _ = message.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] as? TelegramMediaFile {
} else {
return false
}
} else {
return false
}
}
return true
}