diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/BUILD index 4d00881529..b5507029f4 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/BUILD @@ -53,6 +53,7 @@ swift_library( "//submodules/TelegramUI/Components/Chat/ManagedDiceAnimationNode", "//submodules/TelegramUI/Components/Chat/MessageHaptics", "//submodules/TelegramUI/Components/Chat/ChatMessageTransitionNode", + "//submodules/TelegramUI/Components/Chat/ChatMessageSuggestedPostInfoNode", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift index 67834c78ce..448adb8f78 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -46,6 +46,7 @@ import ChatMessageReactionsFooterContentNode import ManagedDiceAnimationNode import MessageHaptics import ChatMessageTransitionNode +import ChatMessageSuggestedPostInfoNode private let nameFont = Font.medium(14.0) private let inlineBotPrefixFont = Font.regular(14.0) @@ -143,6 +144,8 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { private var fetchEffectDisposable: Disposable? + private var suggestedPostInfoNode: ChatMessageSuggestedPostInfoNode? + required public init(rotated: Bool) { self.contextSourceNode = ContextExtractedContentContainingNode() self.containerNode = ContextControllerSourceNode() @@ -823,6 +826,8 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let textLayout = TextNodeWithEntities.asyncLayout(self.textNode) + let makeSuggestedPostInfoNodeLayout: ChatMessageSuggestedPostInfoNode.AsyncLayout = ChatMessageSuggestedPostInfoNode.asyncLayout(self.suggestedPostInfoNode) + func continueAsyncLayout(_ weakSelf: Weak, _ item: ChatMessageItem, _ params: ListViewItemLayoutParams, _ mergedTop: ChatMessageMerge, _ mergedBottom: ChatMessageMerge, _ dateHeaderAtBottom: ChatMessageHeaderSpec) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation, ListViewItemApply, Bool) -> Void) { let accessibilityData = ChatMessageAccessibilityData(item: item, isSelected: nil) let layoutConstants = chatMessageItemLayoutConstants(layoutConstants, params: params, presentationData: item.presentationData) @@ -1030,7 +1035,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { var innerImageSize = imageSize imageSize = CGSize(width: imageSize.width + imageInset * 2.0, height: imageSize.height + imageInset * 2.0) - let imageFrame = CGRect(origin: CGPoint(x: 0.0 + (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - imageSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - deliveryFailedInset - imageHorizontalOffset)), y: imageVerticalInset + imageTopPadding), size: CGSize(width: imageSize.width, height: imageSize.height)) + var imageFrame = CGRect(origin: CGPoint(x: 0.0 + (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - imageSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - deliveryFailedInset - imageHorizontalOffset)), y: imageVerticalInset + imageTopPadding), size: CGSize(width: imageSize.width, height: imageSize.height)) if isEmoji { innerImageSize = imageSize } @@ -1287,6 +1292,42 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let (minWidth, buttonsLayout) = actionButtonsLayout(item.context, item.presentationData.theme, item.presentationData.chatBubbleCorners, item.presentationData.strings, item.controllerInteraction.presentationContext.backgroundNode, replyMarkup, [:], item.message, maxContentWidth) maxContentWidth = max(maxContentWidth, minWidth) actionButtonsFinalize = buttonsLayout + } else if incoming, let attribute = item.message.attributes.first(where: { $0 is SuggestedPostMessageAttribute }) as? SuggestedPostMessageAttribute, attribute.state == nil { + //TODO:localize + var buttonDeclineValue: UInt8 = 0 + let buttonDecline = MemoryBuffer(data: Data(bytes: &buttonDeclineValue, count: 1)) + var buttonApproveValue: UInt8 = 1 + let buttonApprove = MemoryBuffer(data: Data(bytes: &buttonApproveValue, count: 1)) + var buttonSuggestChangesValue: UInt8 = 2 + let buttonSuggestChanges = MemoryBuffer(data: Data(bytes: &buttonSuggestChangesValue, count: 1)) + + let customIcons: [MemoryBuffer: ChatMessageActionButtonsNode.CustomIcon] = [ + buttonDecline: .suggestedPostReject, + buttonApprove: .suggestedPostApprove, + buttonSuggestChanges: .suggestedPostEdit + ] + + let (minWidth, buttonsLayout) = actionButtonsLayout( + item.context, + item.presentationData.theme, + item.presentationData.chatBubbleCorners, + item.presentationData.strings, + item.controllerInteraction.presentationContext.backgroundNode, + ReplyMarkupMessageAttribute( + rows: [ + ReplyMarkupRow(buttons: [ + ReplyMarkupButton(title: "Decline", titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonDecline)), + ReplyMarkupButton(title: "Approve", titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonApprove)) + ]), + ReplyMarkupRow(buttons: [ + ReplyMarkupButton(title: "Suggest Changes", titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonSuggestChanges)) + ]) + ], + flags: [], + placeholder: nil + ), customIcons, item.message, maxContentWidth) + maxContentWidth = max(maxContentWidth, minWidth) + actionButtonsFinalize = buttonsLayout } var actionButtonsSizeAndApply: (CGSize, (ListViewItemUpdateAnimation) -> ChatMessageActionButtonsNode)? @@ -1336,6 +1377,22 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { layoutSize.height += 4.0 + reactionButtonsSizeAndApply.0.height } + let baseWidth = params.width - params.leftInset - params.rightInset + var suggestedPostInfoNodeLayout: (CGSize, () -> ChatMessageSuggestedPostInfoNode)? + for attribute in item.message.attributes { + if let _ = attribute as? SuggestedPostMessageAttribute { + let suggestedPostInfoNodeLayoutValue = makeSuggestedPostInfoNodeLayout(item, baseWidth) + suggestedPostInfoNodeLayout = suggestedPostInfoNodeLayoutValue + } + } + + var additionalTopHeight: CGFloat = 0.0 + if let suggestedPostInfoNodeLayout { + additionalTopHeight += 4.0 + suggestedPostInfoNodeLayout.0.height + 8.0 + } + layoutSize.height += additionalTopHeight + imageFrame.origin.y += additionalTopHeight + var headersOffset: CGFloat = 0.0 if let (threadInfoSize, _) = threadInfoApply { headersOffset += threadInfoSize.height + 10.0 @@ -1390,6 +1447,20 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { strongSelf.updateAttachedDateHeader(hasDate: dateHeaderAtBottom.hasDate, hasPeer: dateHeaderAtBottom.hasTopic) + if let (suggestedPostInfoSize, suggestedPostInfoApply) = suggestedPostInfoNodeLayout { + let suggestedPostInfoNode = suggestedPostInfoApply() + if suggestedPostInfoNode !== strongSelf.suggestedPostInfoNode { + strongSelf.suggestedPostInfoNode?.removeFromSupernode() + strongSelf.suggestedPostInfoNode = suggestedPostInfoNode + strongSelf.addSubnode(suggestedPostInfoNode) + } + let suggestedPostInfoFrame = CGRect(origin: CGPoint(x: floor((params.width - suggestedPostInfoSize.width) * 0.5), y: 4.0), size: suggestedPostInfoSize) + suggestedPostInfoNode.frame = suggestedPostInfoFrame + } else if let suggestedPostInfoNode = strongSelf.suggestedPostInfoNode { + strongSelf.suggestedPostInfoNode = nil + suggestedPostInfoNode.removeFromSupernode() + } + strongSelf.messageAccessibilityArea.frame = CGRect(origin: CGPoint(), size: layoutSize) strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layoutSize) strongSelf.contextSourceNode.frame = CGRect(origin: CGPoint(), size: layoutSize) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index a403045005..ade51b1c64 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -2805,7 +2805,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI actionButtonsFinalize = buttonsLayout lastNodeTopPosition = .None(.Both) - } else if incoming, /*let channel = item.message.peers[item.message.id.peerId] as? TelegramChannel, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = item.message.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.manageDirect),*/ let attribute = item.message.attributes.first(where: { $0 is SuggestedPostMessageAttribute }) as? SuggestedPostMessageAttribute, attribute.state == nil { + } else if incoming, let attribute = item.message.attributes.first(where: { $0 is SuggestedPostMessageAttribute }) as? SuggestedPostMessageAttribute, attribute.state == nil { //TODO:localize var buttonDeclineValue: UInt8 = 0 let buttonDecline = MemoryBuffer(data: Data(bytes: &buttonDeclineValue, count: 1)) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/BUILD index 54233cea21..93400be766 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/BUILD @@ -38,6 +38,7 @@ swift_library( "//submodules/TelegramUI/Components/Chat/ChatMessageActionButtonsNode", "//submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode", "//submodules/TelegramUI/Components/Chat/ChatSwipeToReplyRecognizer", + "//submodules/TelegramUI/Components/Chat/ChatMessageSuggestedPostInfoNode", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift index 1388eb443a..96199c54a7 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift @@ -28,6 +28,7 @@ import ChatMessageThreadInfoNode import ChatMessageActionButtonsNode import ChatMessageReactionsFooterContentNode import ChatSwipeToReplyRecognizer +import ChatMessageSuggestedPostInfoNode private let nameFont = Font.medium(14.0) private let inlineBotPrefixFont = Font.regular(14.0) @@ -51,6 +52,8 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { public var telegramFile: TelegramMediaFile? private let fetchDisposable = MetaDisposable() + private var suggestedPostInfoNode: ChatMessageSuggestedPostInfoNode? + private var viaBotNode: TextNode? private let dateAndStatusNode: ChatMessageDateAndStatusNode private var threadInfoNode: ChatMessageThreadInfoNode? @@ -435,6 +438,8 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { let currentShareButtonNode = self.shareButtonNode let currentForwardInfo = self.appliedForwardInfo + let makeSuggestedPostInfoNodeLayout: ChatMessageSuggestedPostInfoNode.AsyncLayout = ChatMessageSuggestedPostInfoNode.asyncLayout(self.suggestedPostInfoNode) + func continueAsyncLayout(_ weakSelf: Weak, _ item: ChatMessageItem, _ params: ListViewItemLayoutParams, _ mergedTop: ChatMessageMerge, _ mergedBottom: ChatMessageMerge, _ dateHeaderAtBottom: ChatMessageHeaderSpec) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation, ListViewItemApply, Bool) -> Void) { let accessibilityData = ChatMessageAccessibilityData(item: item, isSelected: nil) @@ -591,7 +596,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { let innerImageInset: CGFloat = 10.0 let innerImageSize = CGSize(width: imageSize.width + innerImageInset * 2.0, height: imageSize.height + innerImageInset * 2.0) - let imageFrame = CGRect(origin: CGPoint(x: 0.0 + (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - innerImageSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - deliveryFailedInset)), y: -innerImageInset), size: innerImageSize) + var imageFrame = CGRect(origin: CGPoint(x: 0.0 + (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - innerImageSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - deliveryFailedInset)), y: -innerImageInset), size: innerImageSize) let arguments = TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(top: innerImageInset, left: innerImageInset, bottom: innerImageInset, right: innerImageInset)) @@ -848,6 +853,42 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { let (minWidth, buttonsLayout) = actionButtonsLayout(item.context, item.presentationData.theme, item.presentationData.chatBubbleCorners, item.presentationData.strings, item.controllerInteraction.presentationContext.backgroundNode, replyMarkup, [:], item.message, maxContentWidth) maxContentWidth = max(maxContentWidth, minWidth) actionButtonsFinalize = buttonsLayout + } else if incoming, let attribute = item.message.attributes.first(where: { $0 is SuggestedPostMessageAttribute }) as? SuggestedPostMessageAttribute, attribute.state == nil { + //TODO:localize + var buttonDeclineValue: UInt8 = 0 + let buttonDecline = MemoryBuffer(data: Data(bytes: &buttonDeclineValue, count: 1)) + var buttonApproveValue: UInt8 = 1 + let buttonApprove = MemoryBuffer(data: Data(bytes: &buttonApproveValue, count: 1)) + var buttonSuggestChangesValue: UInt8 = 2 + let buttonSuggestChanges = MemoryBuffer(data: Data(bytes: &buttonSuggestChangesValue, count: 1)) + + let customIcons: [MemoryBuffer: ChatMessageActionButtonsNode.CustomIcon] = [ + buttonDecline: .suggestedPostReject, + buttonApprove: .suggestedPostApprove, + buttonSuggestChanges: .suggestedPostEdit + ] + + let (minWidth, buttonsLayout) = actionButtonsLayout( + item.context, + item.presentationData.theme, + item.presentationData.chatBubbleCorners, + item.presentationData.strings, + item.controllerInteraction.presentationContext.backgroundNode, + ReplyMarkupMessageAttribute( + rows: [ + ReplyMarkupRow(buttons: [ + ReplyMarkupButton(title: "Decline", titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonDecline)), + ReplyMarkupButton(title: "Approve", titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonApprove)) + ]), + ReplyMarkupRow(buttons: [ + ReplyMarkupButton(title: "Suggest Changes", titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonSuggestChanges)) + ]) + ], + flags: [], + placeholder: nil + ), customIcons, item.message, maxContentWidth) + maxContentWidth = max(maxContentWidth, minWidth) + actionButtonsFinalize = buttonsLayout } var actionButtonsSizeAndApply: (CGSize, (ListViewItemUpdateAnimation) -> ChatMessageActionButtonsNode)? @@ -901,6 +942,22 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { layoutSize.height += actionButtonsSizeAndApply.0.height } + let baseWidth = params.width - params.leftInset - params.rightInset + var suggestedPostInfoNodeLayout: (CGSize, () -> ChatMessageSuggestedPostInfoNode)? + for attribute in item.message.attributes { + if let _ = attribute as? SuggestedPostMessageAttribute { + let suggestedPostInfoNodeLayoutValue = makeSuggestedPostInfoNodeLayout(item, baseWidth) + suggestedPostInfoNodeLayout = suggestedPostInfoNodeLayoutValue + } + } + + var additionalTopHeight: CGFloat = 0.0 + if let suggestedPostInfoNodeLayout { + additionalTopHeight += 4.0 + suggestedPostInfoNodeLayout.0.height + 8.0 + } + layoutSize.height += additionalTopHeight + imageFrame.origin.y += additionalTopHeight + var updatedImageFrame = imageFrame.offsetBy(dx: 0.0, dy: floor((contentHeight - imageSize.height) / 2.0)) var dateOffset = CGPoint(x: dateAndStatusSize.width + 4.0, y: dateAndStatusSize.height + 16.0) @@ -1002,6 +1059,20 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { animation.animator.updateFrame(layer: strongSelf.placeholderNode.layer, frame: placeholderFrame, completion: nil) } + if let (suggestedPostInfoSize, suggestedPostInfoApply) = suggestedPostInfoNodeLayout { + let suggestedPostInfoNode = suggestedPostInfoApply() + if suggestedPostInfoNode !== strongSelf.suggestedPostInfoNode { + strongSelf.suggestedPostInfoNode?.removeFromSupernode() + strongSelf.suggestedPostInfoNode = suggestedPostInfoNode + strongSelf.addSubnode(suggestedPostInfoNode) + } + let suggestedPostInfoFrame = CGRect(origin: CGPoint(x: floor((params.width - suggestedPostInfoSize.width) * 0.5), y: 4.0), size: suggestedPostInfoSize) + suggestedPostInfoNode.frame = suggestedPostInfoFrame + } else if let suggestedPostInfoNode = strongSelf.suggestedPostInfoNode { + strongSelf.suggestedPostInfoNode = nil + suggestedPostInfoNode.removeFromSupernode() + } + strongSelf.messageAccessibilityArea.frame = CGRect(origin: CGPoint(), size: layoutSize) strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layoutSize) strongSelf.contextSourceNode.frame = CGRect(origin: CGPoint(), size: layoutSize)