Merge commit 'b5bede3d8bef92acee1d7152bdf5736eabae1638'

This commit is contained in:
Ali
2023-10-22 01:22:29 +04:00
11 changed files with 193 additions and 31 deletions
@@ -36,8 +36,37 @@ private class AdMessagesHistoryContextImpl {
}
struct Invite: Equatable, Codable {
enum CodingKeys: String, CodingKey {
case title
case joinHash
case nameColor
}
var title: String
var joinHash: String
var nameColor: PeerNameColor?
init(title: String, joinHash: String, nameColor: PeerNameColor? = nil) {
self.title = title
self.joinHash = joinHash
self.nameColor = nameColor
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.title = try container.decode(String.self, forKey: .title)
self.joinHash = try container.decode(String.self, forKey: .joinHash)
self.nameColor = try container.decodeIfPresent(Int32.self, forKey: .nameColor).flatMap { PeerNameColor(rawValue: $0) }
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.title, forKey: .title)
try container.encode(self.joinHash, forKey: .joinHash)
try container.encodeIfPresent(self.nameColor?.rawValue, forKey: .nameColor)
}
}
struct WebPage: Equatable, Codable {
@@ -265,7 +294,7 @@ private class AdMessagesHistoryContextImpl {
defaultBannedRights: nil,
usernames: [],
storiesHidden: nil,
nameColor: nil,
nameColor: invite.nameColor,
backgroundEmojiId: nil
)
case let .webPage(webPage):
@@ -286,7 +315,7 @@ private class AdMessagesHistoryContextImpl {
defaultBannedRights: nil,
usernames: [],
storiesHidden: nil,
nameColor: nil,
nameColor: .blue,
backgroundEmojiId: nil
)
}
@@ -522,24 +551,26 @@ private class AdMessagesHistoryContextImpl {
let _ = flags
let _ = participantsCount
let _ = participants
let _ = nameColor
target = .invite(CachedMessage.Target.Invite(
title: title,
joinHash: chatInviteHash
joinHash: chatInviteHash,
nameColor: PeerNameColor(rawValue: nameColor)
))
case let .chatInvitePeek(chat, _):
if let peer = parseTelegramGroupOrChannel(chat: chat) {
target = .invite(CachedMessage.Target.Invite(
title: peer.debugDisplayTitle,
joinHash: chatInviteHash
joinHash: chatInviteHash,
nameColor: peer.nameColor
))
}
case let .chatInviteAlready(chat):
if let peer = parseTelegramGroupOrChannel(chat: chat) {
target = .invite(CachedMessage.Target.Invite(
title: peer.debugDisplayTitle,
joinHash: chatInviteHash
joinHash: chatInviteHash,
nameColor: peer.nameColor
))
}
}
@@ -304,6 +304,8 @@ public enum PresentationResourceKey: Int32 {
case chatReplyLineDashTemplateIncomingImage
case chatReplyLineDashTemplateOutgoingImage
case chatBubbleCloseIcon
}
public enum ChatExpiredStoryIndicatorType: Hashable {
@@ -1350,4 +1350,30 @@ public struct PresentationResourcesChat {
})?.resizableImage(withCapInsets: .zero, resizingMode: .tile).withRenderingMode(.alwaysTemplate)
})
}
public static func chatBubbleCloseIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatBubbleCloseIcon.rawValue, { theme in
return generateImage(CGSize(width: 12.0, height: 12.0), rotatedContext: { size, context in
context.clear(CGRect(origin: .zero, size: size))
let color = theme.chat.message.incoming.secondaryTextColor
context.setAlpha(color.alpha)
context.setBlendMode(.copy)
context.setStrokeColor(theme.chat.message.incoming.secondaryTextColor.withAlphaComponent(1.0).cgColor)
context.setLineWidth(1.0 + UIScreenPixel)
context.setLineCap(.round)
let bounds = CGRect(origin: .zero, size: size).insetBy(dx: 1.0 + UIScreenPixel, dy: 1.0 + UIScreenPixel)
context.move(to: CGPoint(x: bounds.minX, y: bounds.minY))
context.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
context.strokePath()
context.move(to: CGPoint(x: bounds.maxX, y: bounds.minY))
context.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY))
context.strokePath()
})
})
}
}
@@ -515,7 +515,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
}
}
}
let (buttonWidth, continueLayout) = makeActionButtonLayout(
maxContentsWidth,
buttonIconImage,
@@ -523,7 +523,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
actionTitle,
mainColor,
false,
false
message.adAttribute != nil
)
actionButtonMinWidthAndFinalizeLayout = (buttonWidth, continueLayout)
actualWidth = max(actualWidth, buttonWidth)
@@ -588,9 +588,17 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
let maxStatusContentWidth: CGFloat = constrainedSize.width - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right
var actionButtonTrailingContentWidth: CGFloat?
if !displayLine, let (actionButtonMinWidth, _) = actionButtonMinWidthAndFinalizeLayout {
actionButtonTrailingContentWidth = actionButtonMinWidth
var trailingContentWidth: CGFloat?
if let _ = message.adAttribute, let (textLayout, _) = textLayoutAndApply {
if textLayout.hasRTL {
trailingContentWidth = 10000.0
} else {
trailingContentWidth = textLayout.trailingLineWidth
}
} else {
if !displayLine, let (actionButtonMinWidth, _) = actionButtonMinWidthAndFinalizeLayout {
trailingContentWidth = actionButtonMinWidth
}
}
let statusLayoutAndContinue = makeStatusLayout(ChatMessageDateAndStatusNode.Arguments(
@@ -601,7 +609,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
dateText: dateText,
type: statusType,
layoutInput: .trailingContent(
contentWidth: actionButtonTrailingContentWidth,
contentWidth: trailingContentWidth,
reactionSettings: ChatMessageDateAndStatusNode.TrailingReactionSettings(displayInline: shouldDisplayInlineDateReactions(message: message, isPremium: associatedData.isPremium, forceInline: associatedData.forceInlineReactions), preferAdditionalInset: false)
),
constrainedSize: CGSize(width: maxStatusContentWidth, height: CGFloat.greatestFiniteMagnitude),
@@ -1076,7 +1084,10 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
}
if let item = contentDisplayOrder.first(where: { $0.item == .actionButton }), let (actionButtonSize, actionButtonApply) = actionButtonSizeAndApply {
let actionButtonFrame = CGRect(origin: CGPoint(x: insets.left, y: item.offsetY), size: actionButtonSize)
var actionButtonFrame = CGRect(origin: CGPoint(x: insets.left, y: item.offsetY), size: actionButtonSize)
if let _ = message.adAttribute {
actionButtonFrame.origin.y += statusSizeAndApply.0.height
}
let actionButton = actionButtonApply(animation)
@@ -1096,35 +1107,43 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
animation.animator.updateFrame(layer: actionButton.layer, frame: actionButtonFrame, completion: nil)
}
let separatorFrame = CGRect(origin: CGPoint(x: actionButtonFrame.minX, y: actionButtonFrame.minY - 1.0), size: CGSize(width: actionButtonFrame.width, height: UIScreenPixel))
let actionButtonSeparator: SimpleLayer
if let current = self.actionButtonSeparator {
actionButtonSeparator = current
animation.animator.updateFrame(layer: actionButtonSeparator, frame: separatorFrame, completion: nil)
if let _ = message.adAttribute {
} else {
actionButtonSeparator = SimpleLayer()
self.actionButtonSeparator = actionButtonSeparator
self.layer.addSublayer(actionButtonSeparator)
actionButtonSeparator.frame = separatorFrame
let separatorFrame = CGRect(origin: CGPoint(x: actionButtonFrame.minX, y: actionButtonFrame.minY - 1.0), size: CGSize(width: actionButtonFrame.width, height: UIScreenPixel))
let actionButtonSeparator: SimpleLayer
if let current = self.actionButtonSeparator {
actionButtonSeparator = current
animation.animator.updateFrame(layer: actionButtonSeparator, frame: separatorFrame, completion: nil)
} else {
actionButtonSeparator = SimpleLayer()
self.actionButtonSeparator = actionButtonSeparator
self.layer.addSublayer(actionButtonSeparator)
actionButtonSeparator.frame = separatorFrame
}
actionButtonSeparator.backgroundColor = mainColor.withMultipliedAlpha(0.2).cgColor
}
actionButtonSeparator.backgroundColor = mainColor.withMultipliedAlpha(0.2).cgColor
} else {
if let actionButton = self.actionButton {
self.actionButton = nil
actionButton.removeFromSupernode()
}
if let actionButtonSeparator = self.actionButtonSeparator {
self.actionButtonSeparator = nil
actionButtonSeparator.removeFromSuperlayer()
}
}
if self.actionButton == nil, let actionButtonSeparator = self.actionButtonSeparator {
self.actionButtonSeparator = nil
actionButtonSeparator.removeFromSuperlayer()
}
do {
statusSizeAndApply.1(animation)
let statusFrame = CGRect(origin: CGPoint(x: actualSize.width - insets.right - statusSizeAndApply.0.width, y: actualSize.height - layoutConstants.text.bubbleInsets.bottom - statusSizeAndApply.0.height), size: statusSizeAndApply.0)
var statusFrame = CGRect(origin: CGPoint(x: actualSize.width - insets.right - statusSizeAndApply.0.width, y: actualSize.height - layoutConstants.text.bubbleInsets.bottom - statusSizeAndApply.0.height), size: statusSizeAndApply.0)
if let _ = message.adAttribute, let (actionButtonSize, _) = actionButtonSizeAndApply {
statusFrame.origin.y -= actionButtonSize.height + statusBackgroundSpacing
}
animation.animator.updateFrame(layer: self.statusNode.layer, frame: statusFrame, completion: nil)
self.statusNode.reactionSelected = { [weak self] value in
@@ -538,6 +538,9 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
private var adminBadgeNode: TextNode?
private var credibilityIconView: ComponentHostView<Empty>?
private var credibilityIconComponent: EmojiStatusComponent?
private var closeButtonNode: HighlightTrackingButtonNode?
private var closeIconNode: ASImageNode?
private var forwardInfoNode: ChatMessageForwardInfoNode?
public var forwardInfoReferenceNode: ASDisplayNode? {
return self.forwardInfoNode
@@ -1029,6 +1032,12 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
return .fail
}
if let closeButtonNode = strongSelf.closeButtonNode {
if let _ = closeButtonNode.hitTest(strongSelf.view.convert(point, to: closeButtonNode.view), with: nil) {
return .fail
}
}
if let shareButtonNode = strongSelf.shareButtonNode, shareButtonNode.frame.contains(point) {
return .fail
@@ -2965,6 +2974,55 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
strongSelf.adminBadgeNode?.removeFromSupernode()
strongSelf.adminBadgeNode = nil
}
if let _ = item.message.adAttribute {
let buttonNode: HighlightTrackingButtonNode
let iconNode: ASImageNode
if let currentButton = strongSelf.closeButtonNode, let currentIcon = strongSelf.closeIconNode {
buttonNode = currentButton
iconNode = currentIcon
} else {
buttonNode = HighlightTrackingButtonNode()
iconNode = ASImageNode()
iconNode.displaysAsynchronously = false
iconNode.isUserInteractionEnabled = false
buttonNode.addTarget(strongSelf, action: #selector(strongSelf.closeButtonPressed), forControlEvents: .touchUpInside)
buttonNode.highligthedChanged = { [weak iconNode] highlighted in
guard let iconNode else {
return
}
if highlighted {
iconNode.layer.removeAnimation(forKey: "opacity")
iconNode.alpha = 0.4
} else {
iconNode.alpha = 1.0
iconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
}
}
strongSelf.clippingNode.addSubnode(buttonNode)
strongSelf.clippingNode.addSubnode(iconNode)
strongSelf.closeButtonNode = buttonNode
strongSelf.closeIconNode = iconNode
}
iconNode.image = PresentationResourcesChat.chatBubbleCloseIcon(item.presentationData.theme.theme)
let closeButtonSize = CGSize(width: 32.0, height: 32.0)
let closeIconSize = CGSize(width: 12.0, height: 12.0)
let closeButtonFrame = CGRect(origin: CGPoint(x: contentUpperRightCorner.x - closeButtonSize.width, y: layoutConstants.bubble.contentInsets.top), size: closeButtonSize)
let closeButtonIconFrame = CGRect(origin: CGPoint(x: contentUpperRightCorner.x - layoutConstants.text.bubbleInsets.left - closeIconSize.width + 1.0, y: layoutConstants.bubble.contentInsets.top + nameNodeOriginY + 2.0), size: closeIconSize)
animation.animator.updateFrame(layer: buttonNode.layer, frame: closeButtonFrame, completion: nil)
animation.animator.updateFrame(layer: iconNode.layer, frame: closeButtonIconFrame, completion: nil)
} else {
strongSelf.closeButtonNode?.removeFromSupernode()
strongSelf.closeButtonNode = nil
strongSelf.closeIconNode?.removeFromSupernode()
strongSelf.closeIconNode = nil
}
} else {
if animation.isAnimated {
if let nameNode = strongSelf.nameNode {
@@ -4231,7 +4289,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
}
return nil
}
if let threadInfoNode = self.threadInfoNode, let result = threadInfoNode.hitTest(self.view.convert(point, to: threadInfoNode.view), with: event) {
return result
}
@@ -4637,6 +4695,12 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
}
}
@objc private func closeButtonPressed() {
if let item = self.item {
item.controllerInteraction.openNoAdsDemo()
}
}
private var playedSwipeToReplyHaptic = false
@objc private func swipeToReplyGesture(_ recognizer: ChatSwipeToReplyRecognizer) {
var offset: CGFloat = 0.0
@@ -565,6 +565,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}, activateAdAction: { _ in
}, openRequestedPeerSelection: { _, _, _ in
}, saveMediaToFiles: { _ in
}, openNoAdsDemo: {
}, requestMessageUpdate: { _, _ in
}, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: {
@@ -196,6 +196,7 @@ public final class ChatControllerInteraction {
public let activateAdAction: (EngineMessage.Id) -> Void
public let openRequestedPeerSelection: (EngineMessage.Id, ReplyMarkupButtonRequestPeerType, Int32) -> Void
public let saveMediaToFiles: (EngineMessage.Id) -> Void
public let openNoAdsDemo: () -> Void
public let requestMessageUpdate: (MessageId, Bool) -> Void
public let cancelInteractiveKeyboardGestures: () -> Void
@@ -310,6 +311,7 @@ public final class ChatControllerInteraction {
activateAdAction: @escaping (EngineMessage.Id) -> Void,
openRequestedPeerSelection: @escaping (EngineMessage.Id, ReplyMarkupButtonRequestPeerType, Int32) -> Void,
saveMediaToFiles: @escaping (EngineMessage.Id) -> Void,
openNoAdsDemo: @escaping () -> Void,
requestMessageUpdate: @escaping (MessageId, Bool) -> Void,
cancelInteractiveKeyboardGestures: @escaping () -> Void,
dismissTextInput: @escaping () -> Void,
@@ -406,6 +408,7 @@ public final class ChatControllerInteraction {
self.activateAdAction = activateAdAction
self.openRequestedPeerSelection = openRequestedPeerSelection
self.saveMediaToFiles = saveMediaToFiles
self.openNoAdsDemo = openNoAdsDemo
self.requestMessageUpdate = requestMessageUpdate
self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures
self.dismissTextInput = dismissTextInput
@@ -4881,6 +4881,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}))
})
}, openNoAdsDemo: { [weak self] in
guard let self else {
return
}
var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumDemoScreen(context: self.context, subject: .noAds, action: {
let controller = PremiumIntroScreen(context: self.context, source: .ads)
replaceImpl?(controller)
})
replaceImpl = { [weak controller] c in
controller?.replace(with: c)
}
self.push(controller)
}, requestMessageUpdate: { [weak self] id, scroll in
if let self {
self.chatDisplayNode.historyNode.requestMessageUpdate(id, andScrollToItem: scroll)
@@ -166,6 +166,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}, activateAdAction: { _ in
}, openRequestedPeerSelection: { _, _, _ in
}, saveMediaToFiles: { _ in
}, openNoAdsDemo: {
}, requestMessageUpdate: { _, _ in
}, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: {
@@ -2917,6 +2917,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}, activateAdAction: { _ in
}, openRequestedPeerSelection: { _, _, _ in
}, saveMediaToFiles: { _ in
}, openNoAdsDemo: {
}, requestMessageUpdate: { _, _ in
}, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: {
@@ -1555,6 +1555,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}, activateAdAction: { _ in
}, openRequestedPeerSelection: { _, _, _ in
}, saveMediaToFiles: { _ in
}, openNoAdsDemo: {
}, requestMessageUpdate: { _, _ in
}, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: {