From 62b50d9e4962cb779d9358b2acb14f6df00b53e2 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 24 Oct 2023 01:10:30 +0400 Subject: [PATCH] Various improvements --- submodules/Display/Source/TextNode.swift | 22 +- .../Sources/ApiUtils/ChatContextResult.swift | 343 +++++++++++------- ...OutgoingMessageWithChatContextResult.swift | 269 +++++++------- .../ChatMessageAnimatedStickerItemNode.swift | 3 +- .../Sources/ChatMessageReplyInfoNode.swift | 19 +- .../Sources/ChatMessageStickerItemNode.swift | 3 +- 6 files changed, 389 insertions(+), 270 deletions(-) diff --git a/submodules/Display/Source/TextNode.swift b/submodules/Display/Source/TextNode.swift index 0cb4747996..e74ec20299 100644 --- a/submodules/Display/Source/TextNode.swift +++ b/submodules/Display/Source/TextNode.swift @@ -1713,7 +1713,7 @@ open class TextNode: ASDisplayNode { embeddedItems.append(TextNodeEmbeddedItem(range: NSMakeRange(startIndex, endIndex - startIndex + 1), frame: CGRect(x: min(leftOffset, rightOffset), y: descent - (ascent + descent), width: abs(rightOffset - leftOffset) + rightInset, height: ascent + descent), item: item)) } - func addAttachment(attachment: UIImage, line: CTLine, ascent: CGFloat, descent: CGFloat, startIndex: Int, endIndex: Int, rightInset: CGFloat = 0.0) { + func addAttachment(attachment: UIImage, line: CTLine, ascent: CGFloat, descent: CGFloat, startIndex: Int, endIndex: Int, isAtEndOfTheLine: Bool, rightInset: CGFloat = 0.0) { var secondaryLeftOffset: CGFloat = 0.0 let rawLeftOffset = CTLineGetOffsetForStringIndex(line, startIndex, &secondaryLeftOffset) var leftOffset = floor(rawLeftOffset) @@ -1721,11 +1721,17 @@ open class TextNode: ASDisplayNode { leftOffset = floor(secondaryLeftOffset) } - var secondaryRightOffset: CGFloat = 0.0 - let rawRightOffset = CTLineGetOffsetForStringIndex(line, endIndex, &secondaryRightOffset) - var rightOffset = ceil(rawRightOffset) - if !rawRightOffset.isEqual(to: secondaryRightOffset) { - rightOffset = ceil(secondaryRightOffset) + var rightOffset: CGFloat = leftOffset + if isAtEndOfTheLine { + let rawRightOffset = CTLineGetTypographicBounds(line, nil, nil, nil) + rightOffset = floor(rawRightOffset) + } else { + var secondaryRightOffset: CGFloat = 0.0 + let rawRightOffset = CTLineGetOffsetForStringIndex(line, endIndex, &secondaryRightOffset) + rightOffset = ceil(rawRightOffset) + if !rawRightOffset.isEqual(to: secondaryRightOffset) { + rightOffset = ceil(secondaryRightOffset) + } } attachments.append(TextNodeAttachment(range: NSMakeRange(startIndex, endIndex - startIndex), frame: CGRect(x: min(leftOffset, rightOffset), y: descent - (ascent + descent), width: abs(rightOffset - leftOffset) + rightInset, height: ascent + descent), attachment: attachment)) @@ -1918,7 +1924,7 @@ open class TextNode: ASDisplayNode { var descent: CGFloat = 0.0 CTLineGetTypographicBounds(coreTextLine, &ascent, &descent, nil) - addAttachment(attachment: attachment, line: coreTextLine, ascent: ascent, descent: descent, startIndex: range.location, endIndex: range.location + range.length) + addAttachment(attachment: attachment, line: coreTextLine, ascent: ascent, descent: descent, startIndex: range.location, endIndex: max(range.location, min(lineRange.location + lineRange.length - 1, range.location + range.length)), isAtEndOfTheLine: range.location + range.length >= lineRange.location + lineRange.length - 1) } } } @@ -2037,7 +2043,7 @@ open class TextNode: ASDisplayNode { var descent: CGFloat = 0.0 CTLineGetTypographicBounds(coreTextLine, &ascent, &descent, nil) - addAttachment(attachment: attachment, line: coreTextLine, ascent: ascent, descent: descent, startIndex: range.location, endIndex: range.location + range.length) + addAttachment(attachment: attachment, line: coreTextLine, ascent: ascent, descent: descent, startIndex: range.location, endIndex: max(range.location, min(lineRange.location + lineRange.length - 1, range.location + range.length)), isAtEndOfTheLine: range.location + range.length >= lineRange.location + lineRange.length - 1) } } diff --git a/submodules/TelegramCore/Sources/ApiUtils/ChatContextResult.swift b/submodules/TelegramCore/Sources/ApiUtils/ChatContextResult.swift index 7324205a7c..65fa28f513 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ChatContextResult.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ChatContextResult.swift @@ -15,81 +15,108 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable, Codable { } case auto(caption: String, entities: TextEntitiesMessageAttribute?, replyMarkup: ReplyMarkupMessageAttribute?) - case text(text: String, entities: TextEntitiesMessageAttribute?, disableUrlPreview: Bool, replyMarkup: ReplyMarkupMessageAttribute?) + case text(text: String, entities: TextEntitiesMessageAttribute?, disableUrlPreview: Bool, previewParameters: WebpagePreviewMessageAttribute?, replyMarkup: ReplyMarkupMessageAttribute?) case mapLocation(media: TelegramMediaMap, replyMarkup: ReplyMarkupMessageAttribute?) case contact(media: TelegramMediaContact, replyMarkup: ReplyMarkupMessageAttribute?) case invoice(media: TelegramMediaInvoice, replyMarkup: ReplyMarkupMessageAttribute?) + case webpage(text: String, entities: TextEntitiesMessageAttribute?, url: String, previewParameters: WebpagePreviewMessageAttribute?, replyMarkup: ReplyMarkupMessageAttribute?) public init(decoder: PostboxDecoder) { switch decoder.decodeInt32ForKey("_v", orElse: 0) { - case 0: - self = .auto(caption: decoder.decodeStringForKey("c", orElse: ""), entities: decoder.decodeObjectForKey("e") as? TextEntitiesMessageAttribute, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) - case 1: - self = .text(text: decoder.decodeStringForKey("t", orElse: ""), entities: decoder.decodeObjectForKey("e") as? TextEntitiesMessageAttribute, disableUrlPreview: decoder.decodeInt32ForKey("du", orElse: 0) != 0, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) - case 2: - self = .mapLocation(media: decoder.decodeObjectForKey("l") as! TelegramMediaMap, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) - case 3: - self = .contact(media: decoder.decodeObjectForKey("c") as! TelegramMediaContact, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) - case 4: - self = .invoice(media: decoder.decodeObjectForKey("i") as! TelegramMediaInvoice, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) - default: - self = .auto(caption: "", entities: nil, replyMarkup: nil) + case 0: + self = .auto(caption: decoder.decodeStringForKey("c", orElse: ""), entities: decoder.decodeObjectForKey("e") as? TextEntitiesMessageAttribute, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) + case 1: + self = .text(text: decoder.decodeStringForKey("t", orElse: ""), entities: decoder.decodeObjectForKey("e") as? TextEntitiesMessageAttribute, disableUrlPreview: decoder.decodeInt32ForKey("du", orElse: 0) != 0, previewParameters: decoder.decodeObjectForKey("prp") as? WebpagePreviewMessageAttribute, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) + case 2: + self = .mapLocation(media: decoder.decodeObjectForKey("l") as! TelegramMediaMap, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) + case 3: + self = .contact(media: decoder.decodeObjectForKey("c") as! TelegramMediaContact, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) + case 4: + self = .invoice(media: decoder.decodeObjectForKey("i") as! TelegramMediaInvoice, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) + case 5: + self = .webpage(text: decoder.decodeStringForKey("t", orElse: ""), entities: decoder.decodeObjectForKey("e") as? TextEntitiesMessageAttribute, url: decoder.decodeStringForKey("url", orElse: ""), previewParameters: decoder.decodeObjectForKey("prp") as? WebpagePreviewMessageAttribute, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) + default: + self = .auto(caption: "", entities: nil, replyMarkup: nil) } } public func encode(_ encoder: PostboxEncoder) { switch self { - case let .auto(caption, entities, replyMarkup): - encoder.encodeInt32(0, forKey: "_v") - encoder.encodeString(caption, forKey: "c") - if let entities = entities { - encoder.encodeObject(entities, forKey: "e") - } else { - encoder.encodeNil(forKey: "e") - } - if let replyMarkup = replyMarkup { - encoder.encodeObject(replyMarkup, forKey: "m") - } else { - encoder.encodeNil(forKey: "m") - } - case let .text(text, entities, disableUrlPreview, replyMarkup): - encoder.encodeInt32(1, forKey: "_v") - encoder.encodeString(text, forKey: "t") - if let entities = entities { - encoder.encodeObject(entities, forKey: "e") - } else { - encoder.encodeNil(forKey: "e") - } - encoder.encodeInt32(disableUrlPreview ? 1 : 0, forKey: "du") - if let replyMarkup = replyMarkup { - encoder.encodeObject(replyMarkup, forKey: "m") - } else { - encoder.encodeNil(forKey: "m") - } - case let .mapLocation(media, replyMarkup): - encoder.encodeInt32(2, forKey: "_v") - encoder.encodeObject(media, forKey: "l") - if let replyMarkup = replyMarkup { - encoder.encodeObject(replyMarkup, forKey: "m") - } else { - encoder.encodeNil(forKey: "m") - } - case let .contact(media, replyMarkup): - encoder.encodeInt32(3, forKey: "_v") - encoder.encodeObject(media, forKey: "c") - if let replyMarkup = replyMarkup { - encoder.encodeObject(replyMarkup, forKey: "m") - } else { - encoder.encodeNil(forKey: "m") - } - case let .invoice(media: media, replyMarkup): - encoder.encodeInt32(4, forKey: "_v") - encoder.encodeObject(media, forKey: "i") - if let replyMarkup = replyMarkup { - encoder.encodeObject(replyMarkup, forKey: "m") - } else { - encoder.encodeNil(forKey: "m") - } + case let .auto(caption, entities, replyMarkup): + encoder.encodeInt32(0, forKey: "_v") + encoder.encodeString(caption, forKey: "c") + if let entities = entities { + encoder.encodeObject(entities, forKey: "e") + } else { + encoder.encodeNil(forKey: "e") + } + if let replyMarkup = replyMarkup { + encoder.encodeObject(replyMarkup, forKey: "m") + } else { + encoder.encodeNil(forKey: "m") + } + case let .text(text, entities, disableUrlPreview, previewParameters, replyMarkup): + encoder.encodeInt32(1, forKey: "_v") + encoder.encodeString(text, forKey: "t") + if let entities = entities { + encoder.encodeObject(entities, forKey: "e") + } else { + encoder.encodeNil(forKey: "e") + } + encoder.encodeInt32(disableUrlPreview ? 1 : 0, forKey: "du") + if let previewParameters = previewParameters { + encoder.encodeObject(previewParameters, forKey: "prp") + } else { + encoder.encodeNil(forKey: "prp") + } + if let replyMarkup = replyMarkup { + encoder.encodeObject(replyMarkup, forKey: "m") + } else { + encoder.encodeNil(forKey: "m") + } + case let .mapLocation(media, replyMarkup): + encoder.encodeInt32(2, forKey: "_v") + encoder.encodeObject(media, forKey: "l") + if let replyMarkup = replyMarkup { + encoder.encodeObject(replyMarkup, forKey: "m") + } else { + encoder.encodeNil(forKey: "m") + } + case let .contact(media, replyMarkup): + encoder.encodeInt32(3, forKey: "_v") + encoder.encodeObject(media, forKey: "c") + if let replyMarkup = replyMarkup { + encoder.encodeObject(replyMarkup, forKey: "m") + } else { + encoder.encodeNil(forKey: "m") + } + case let .invoice(media, replyMarkup): + encoder.encodeInt32(4, forKey: "_v") + encoder.encodeObject(media, forKey: "i") + if let replyMarkup = replyMarkup { + encoder.encodeObject(replyMarkup, forKey: "m") + } else { + encoder.encodeNil(forKey: "m") + } + case let .webpage(text, entities, url, previewParameters, replyMarkup): + encoder.encodeInt32(5, forKey: "_v") + encoder.encodeString(text, forKey: "t") + if let entities = entities { + encoder.encodeObject(entities, forKey: "e") + } else { + encoder.encodeNil(forKey: "e") + } + encoder.encodeString(url, forKey: "url") + if let previewParameters = previewParameters { + encoder.encodeObject(previewParameters, forKey: "prp") + } else { + encoder.encodeNil(forKey: "prp") + } + if let replyMarkup = replyMarkup { + encoder.encodeObject(replyMarkup, forKey: "m") + } else { + encoder.encodeNil(forKey: "m") + } } } @@ -110,75 +137,99 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable, Codable { public static func ==(lhs: ChatContextResultMessage, rhs: ChatContextResultMessage) -> Bool { switch lhs { - case let .auto(lhsCaption, lhsEntities, lhsReplyMarkup): - if case let .auto(rhsCaption, rhsEntities, rhsReplyMarkup) = rhs { - if lhsCaption != rhsCaption { - return false - } - if lhsEntities != rhsEntities { - return false - } - if lhsReplyMarkup != rhsReplyMarkup { - return false - } - return true - } else { + case let .auto(lhsCaption, lhsEntities, lhsReplyMarkup): + if case let .auto(rhsCaption, rhsEntities, rhsReplyMarkup) = rhs { + if lhsCaption != rhsCaption { return false } - case let .text(lhsText, lhsEntities, lhsDisableUrlPreview, lhsReplyMarkup): - if case let .text(rhsText, rhsEntities, rhsDisableUrlPreview, rhsReplyMarkup) = rhs { - if lhsText != rhsText { - return false - } - if lhsEntities != rhsEntities { - return false - } - if lhsDisableUrlPreview != rhsDisableUrlPreview { - return false - } - if lhsReplyMarkup != rhsReplyMarkup { - return false - } - return true - } else { + if lhsEntities != rhsEntities { return false } - case let .mapLocation(lhsMedia, lhsReplyMarkup): - if case let .mapLocation(rhsMedia, rhsReplyMarkup) = rhs { - if !lhsMedia.isEqual(to: rhsMedia) { - return false - } - if lhsReplyMarkup != rhsReplyMarkup { - return false - } - return true - } else { + if lhsReplyMarkup != rhsReplyMarkup { return false } - case let .contact(lhsMedia, lhsReplyMarkup): - if case let .contact(rhsMedia, rhsReplyMarkup) = rhs { - if !lhsMedia.isEqual(to: rhsMedia) { - return false - } - if lhsReplyMarkup != rhsReplyMarkup { - return false - } - return true - } else { + return true + } else { + return false + } + case let .text(lhsText, lhsEntities, lhsDisableUrlPreview, lhsPreviewParameters, lhsReplyMarkup): + if case let .text(rhsText, rhsEntities, rhsDisableUrlPreview, rhsPreviewParameters, rhsReplyMarkup) = rhs { + if lhsText != rhsText { return false } - case let .invoice(lhsMedia, lhsReplyMarkup): - if case let .invoice(rhsMedia, rhsReplyMarkup) = rhs { - if !lhsMedia.isEqual(to: rhsMedia) { - return false - } - if lhsReplyMarkup != rhsReplyMarkup { - return false - } - return true - } else { + if lhsEntities != rhsEntities { return false } + if lhsDisableUrlPreview != rhsDisableUrlPreview { + return false + } + if lhsPreviewParameters != rhsPreviewParameters { + return false + } + if lhsReplyMarkup != rhsReplyMarkup { + return false + } + return true + } else { + return false + } + case let .mapLocation(lhsMedia, lhsReplyMarkup): + if case let .mapLocation(rhsMedia, rhsReplyMarkup) = rhs { + if !lhsMedia.isEqual(to: rhsMedia) { + return false + } + if lhsReplyMarkup != rhsReplyMarkup { + return false + } + return true + } else { + return false + } + case let .contact(lhsMedia, lhsReplyMarkup): + if case let .contact(rhsMedia, rhsReplyMarkup) = rhs { + if !lhsMedia.isEqual(to: rhsMedia) { + return false + } + if lhsReplyMarkup != rhsReplyMarkup { + return false + } + return true + } else { + return false + } + case let .invoice(lhsMedia, lhsReplyMarkup): + if case let .invoice(rhsMedia, rhsReplyMarkup) = rhs { + if !lhsMedia.isEqual(to: rhsMedia) { + return false + } + if lhsReplyMarkup != rhsReplyMarkup { + return false + } + return true + } else { + return false + } + case let .webpage(lhsText, lhsEntities, lhsUrl, lhsPreviewParameters, lhsReplyMarkup): + if case let .webpage(rhsText, rhsEntities, rhsUrl, rhsPreviewParameters, rhsReplyMarkup) = rhs { + if lhsText != rhsText { + return false + } + if lhsEntities != rhsEntities { + return false + } + if lhsUrl != rhsUrl { + return false + } + if lhsPreviewParameters != rhsPreviewParameters { + return false + } + if lhsReplyMarkup != rhsReplyMarkup { + return false + } + return true + } else { + return false + } } } } @@ -458,7 +509,13 @@ extension ChatContextResultMessage { if let replyMarkup = replyMarkup { parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup) } - self = .text(text: message, entities: parsedEntities, disableUrlPreview: (flags & (1 << 0)) != 0, replyMarkup: parsedReplyMarkup) + let leadingPreview = (flags & (1 << 3)) != 0 + self = .text(text: message, entities: parsedEntities, disableUrlPreview: (flags & (1 << 0)) != 0, previewParameters: WebpagePreviewMessageAttribute( + leadingPreview: leadingPreview, + forceLargeMedia: nil, + isManuallyAdded: false, + isSafe: false + ), replyMarkup: parsedReplyMarkup) case let .botInlineMessageMediaGeo(_, geo, heading, period, proximityNotificationRadius, replyMarkup): let media = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period, liveProximityNotificationRadius: proximityNotificationRadius, heading: heading) var parsedReplyMarkup: ReplyMarkupMessageAttribute? @@ -494,12 +551,38 @@ extension ChatContextResultMessage { } self = .invoice(media: TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: nil, currency: currency, totalAmount: totalAmount, startParam: "", extendedMedia: nil, flags: parsedFlags, version: TelegramMediaInvoice.lastVersion), replyMarkup: parsedReplyMarkup) case let .botInlineMessageMediaWebPage(flags, message, entities, url, replyMarkup): - let _ = flags - let _ = message - let _ = entities - let _ = url - let _ = replyMarkup - fatalError() + var parsedReplyMarkup: ReplyMarkupMessageAttribute? + if let replyMarkup = replyMarkup { + parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup) + } + + let leadingPreview = (flags & (1 << 3)) != 0 + var forceLargeMedia: Bool? + if (flags & (1 << 4)) != 0 { + forceLargeMedia = true + } else if (flags & (1 << 5)) != 0 { + forceLargeMedia = false + } + let isManuallyAdded = (flags & (1 << 7)) != 0 + let isSafe = (flags & (1 << 8)) != 0 + + var parsedEntities: TextEntitiesMessageAttribute? + if let entities = entities, !entities.isEmpty { + parsedEntities = TextEntitiesMessageAttribute(entities: messageTextEntitiesFromApiEntities(entities)) + } + + self = .webpage( + text: message, + entities: parsedEntities, + url: url, + previewParameters: WebpagePreviewMessageAttribute( + leadingPreview: leadingPreview, + forceLargeMedia: forceLargeMedia, + isManuallyAdded: isManuallyAdded, + isSafe: isSafe + ), + replyMarkup: parsedReplyMarkup + ) } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/OutgoingMessageWithChatContextResult.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/OutgoingMessageWithChatContextResult.swift index 9db50ceaf1..5abaa52dbb 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/OutgoingMessageWithChatContextResult.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/OutgoingMessageWithChatContextResult.swift @@ -28,137 +28,154 @@ func _internal_outgoingMessageWithChatContextResult(to peerId: PeerId, threadId: attributes.append(NotificationInfoMessageAttribute(flags: .muted)) } switch result.message { - case let .auto(caption, entities, replyMarkup): - if let entities = entities { - attributes.append(entities) - } - if let replyMarkup = replyMarkup { - attributes.append(replyMarkup) - } - switch result { - case let .internalReference(internalReference): - if internalReference.type == "game" { - if peerId.namespace == Namespaces.Peer.SecretChat { - let filteredAttributes = attributes.filter { attribute in - if let _ = attribute as? ReplyMarkupMessageAttribute { - return false - } - return true - } - if let media: Media = internalReference.file ?? internalReference.image { - return .message(text: caption, attributes: filteredAttributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) - } else { - return .message(text: caption, attributes: filteredAttributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) - } - } else { - return .message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaGame(gameId: 0, accessHash: 0, name: "", title: internalReference.title ?? "", description: internalReference.description ?? "", image: internalReference.image, file: internalReference.file)), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) + case let .auto(caption, entities, replyMarkup): + if let entities = entities { + attributes.append(entities) + } + if let replyMarkup = replyMarkup { + attributes.append(replyMarkup) + } + switch result { + case let .internalReference(internalReference): + if internalReference.type == "game" { + if peerId.namespace == Namespaces.Peer.SecretChat { + let filteredAttributes = attributes.filter { attribute in + if let _ = attribute as? ReplyMarkupMessageAttribute { + return false } - } else if let file = internalReference.file, internalReference.type == "gif" { - return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) - } else if let image = internalReference.image { - return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: image), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) - } else if let file = internalReference.file { - return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) - } else { - return nil + return true } - case let .externalReference(externalReference): - if externalReference.type == "photo" { - if let thumbnail = externalReference.thumbnail { - var randomId: Int64 = 0 - arc4random_buf(&randomId, 8) - let thumbnailResource = thumbnail.resource - let imageDimensions = thumbnail.dimensions ?? PixelDimensions(width: 128, height: 128) - let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: [TelegramMediaImageRepresentation(dimensions: imageDimensions, resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: []) - return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: tmpImage), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) - } else { - return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) - } - } else if externalReference.type == "document" || externalReference.type == "gif" || externalReference.type == "audio" || externalReference.type == "voice" { - var videoThumbnails: [TelegramMediaFile.VideoThumbnail] = [] - var previewRepresentations: [TelegramMediaImageRepresentation] = [] - if let thumbnail = externalReference.thumbnail { - var randomId: Int64 = 0 - arc4random_buf(&randomId, 8) - let thumbnailResource = thumbnail.resource - - if thumbnail.mimeType.hasPrefix("video/") { - videoThumbnails.append(TelegramMediaFile.VideoThumbnail(dimensions: thumbnail.dimensions ?? PixelDimensions(width: 128, height: 128), resource: thumbnailResource)) - } else { - previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: thumbnail.dimensions ?? PixelDimensions(width: 128, height: 128), resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)) - } - } - var fileName = "file" - if let content = externalReference.content { - var contentUrl: String? - if let resource = content.resource as? HttpReferenceMediaResource { - contentUrl = resource.url - } else if let resource = content.resource as? WebFileReferenceMediaResource { - contentUrl = resource.url - } - if let contentUrl = contentUrl, let url = URL(string: contentUrl) { - if !url.lastPathComponent.isEmpty { - fileName = url.lastPathComponent - } - } - } - - var fileAttributes: [TelegramMediaFileAttribute] = [] - fileAttributes.append(.FileName(fileName: fileName)) - - if externalReference.type == "gif" { - fileAttributes.append(.Animated) - } - - if let dimensions = externalReference.content?.dimensions { - fileAttributes.append(.ImageSize(size: dimensions)) - if externalReference.type == "gif" { - fileAttributes.append(.Video(duration: externalReference.content?.duration ?? 0.0, size: dimensions, flags: [], preloadSize: nil)) - } - } - - if externalReference.type == "audio" || externalReference.type == "voice" { - fileAttributes.append(.Audio(isVoice: externalReference.type == "voice", duration: Int(Int32(externalReference.content?.duration ?? 0)), title: externalReference.title, performer: externalReference.description, waveform: nil)) - } - - var randomId: Int64 = 0 - arc4random_buf(&randomId, 8) - - let resource: TelegramMediaResource - if peerId.namespace == Namespaces.Peer.SecretChat, let webResource = externalReference.content?.resource as? WebFileReferenceMediaResource { - resource = webResource - } else { - resource = EmptyMediaResource() - } - - let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: videoThumbnails, immediateThumbnailData: nil, mimeType: externalReference.content?.mimeType ?? "application/binary", size: nil, attributes: fileAttributes) - return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) + if let media: Media = internalReference.file ?? internalReference.image { + return .message(text: caption, attributes: filteredAttributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) } else { - return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) + return .message(text: caption, attributes: filteredAttributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) } + } else { + return .message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaGame(gameId: 0, accessHash: 0, name: "", title: internalReference.title ?? "", description: internalReference.description ?? "", image: internalReference.image, file: internalReference.file)), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) + } + } else if let file = internalReference.file, internalReference.type == "gif" { + return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) + } else if let image = internalReference.image { + return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: image), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) + } else if let file = internalReference.file { + return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) + } else { + return nil } - case let .text(text, entities, _, replyMarkup): - if let entities = entities { - attributes.append(entities) + case let .externalReference(externalReference): + if externalReference.type == "photo" { + if let thumbnail = externalReference.thumbnail { + var randomId: Int64 = 0 + arc4random_buf(&randomId, 8) + let thumbnailResource = thumbnail.resource + let imageDimensions = thumbnail.dimensions ?? PixelDimensions(width: 128, height: 128) + let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: [TelegramMediaImageRepresentation(dimensions: imageDimensions, resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: []) + return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: tmpImage), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) + } else { + return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) + } + } else if externalReference.type == "document" || externalReference.type == "gif" || externalReference.type == "audio" || externalReference.type == "voice" { + var videoThumbnails: [TelegramMediaFile.VideoThumbnail] = [] + var previewRepresentations: [TelegramMediaImageRepresentation] = [] + if let thumbnail = externalReference.thumbnail { + var randomId: Int64 = 0 + arc4random_buf(&randomId, 8) + let thumbnailResource = thumbnail.resource + + if thumbnail.mimeType.hasPrefix("video/") { + videoThumbnails.append(TelegramMediaFile.VideoThumbnail(dimensions: thumbnail.dimensions ?? PixelDimensions(width: 128, height: 128), resource: thumbnailResource)) + } else { + previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: thumbnail.dimensions ?? PixelDimensions(width: 128, height: 128), resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)) + } + } + var fileName = "file" + if let content = externalReference.content { + var contentUrl: String? + if let resource = content.resource as? HttpReferenceMediaResource { + contentUrl = resource.url + } else if let resource = content.resource as? WebFileReferenceMediaResource { + contentUrl = resource.url + } + if let contentUrl = contentUrl, let url = URL(string: contentUrl) { + if !url.lastPathComponent.isEmpty { + fileName = url.lastPathComponent + } + } + } + + var fileAttributes: [TelegramMediaFileAttribute] = [] + fileAttributes.append(.FileName(fileName: fileName)) + + if externalReference.type == "gif" { + fileAttributes.append(.Animated) + } + + if let dimensions = externalReference.content?.dimensions { + fileAttributes.append(.ImageSize(size: dimensions)) + if externalReference.type == "gif" { + fileAttributes.append(.Video(duration: externalReference.content?.duration ?? 0.0, size: dimensions, flags: [], preloadSize: nil)) + } + } + + if externalReference.type == "audio" || externalReference.type == "voice" { + fileAttributes.append(.Audio(isVoice: externalReference.type == "voice", duration: Int(Int32(externalReference.content?.duration ?? 0)), title: externalReference.title, performer: externalReference.description, waveform: nil)) + } + + var randomId: Int64 = 0 + arc4random_buf(&randomId, 8) + + let resource: TelegramMediaResource + if peerId.namespace == Namespaces.Peer.SecretChat, let webResource = externalReference.content?.resource as? WebFileReferenceMediaResource { + resource = webResource + } else { + resource = EmptyMediaResource() + } + + let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: videoThumbnails, immediateThumbnailData: nil, mimeType: externalReference.content?.mimeType ?? "application/binary", size: nil, attributes: fileAttributes) + return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) + } else { + return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) } - if let replyMarkup = replyMarkup { - attributes.append(replyMarkup) - } - return .message(text: text, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) - case let .mapLocation(media, replyMarkup): - if let replyMarkup = replyMarkup { - attributes.append(replyMarkup) - } - return .message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) - case let .contact(media, replyMarkup): - if let replyMarkup = replyMarkup { - attributes.append(replyMarkup) - } - return .message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) - case let .invoice(media, replyMarkup): - if let replyMarkup = replyMarkup { - attributes.append(replyMarkup) - } - return .message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) + } + case let .text(text, entities, disableUrlPreview, previewParameters, replyMarkup): + if let entities = entities { + attributes.append(entities) + } + if let replyMarkup = replyMarkup { + attributes.append(replyMarkup) + } + if let previewParameters = previewParameters { + attributes.append(previewParameters) + } + if disableUrlPreview { + attributes.append(OutgoingContentInfoMessageAttribute(flags: [.disableLinkPreviews])) + } + return .message(text: text, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) + case let .mapLocation(media, replyMarkup): + if let replyMarkup = replyMarkup { + attributes.append(replyMarkup) + } + return .message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) + case let .contact(media, replyMarkup): + if let replyMarkup = replyMarkup { + attributes.append(replyMarkup) + } + return .message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) + case let .invoice(media, replyMarkup): + if let replyMarkup = replyMarkup { + attributes.append(replyMarkup) + } + return .message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) + case let .webpage(text, entities, _, previewParameters, replyMarkup): + if let entities = entities { + attributes.append(entities) + } + if let replyMarkup = replyMarkup { + attributes.append(replyMarkup) + } + if let previewParameters = previewParameters { + attributes.append(previewParameters) + } + return .message(text: text, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, replyToStoryId: replyToStoryId, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []) } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift index 62ba4b7d20..aaa4332abc 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1061,7 +1061,8 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { var needsReplyBackground = false var replyMarkup: ReplyMarkupMessageAttribute? - let availableContentWidth = min(120.0, max(60.0, params.width - params.leftInset - params.rightInset - max(imageSize.width, 160.0) - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left)) + var availableContentWidth = min(200.0, max(60.0, params.width - params.leftInset - params.rightInset - max(imageSize.width, 160.0) - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left)) + availableContentWidth -= 20.0 var ignoreForward = false if let forwardInfo = item.message.forwardInfo { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift index 4b1eac6b92..da1b9037a8 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift @@ -37,14 +37,21 @@ private let channelIcon: UIImage = { })!.precomposed().withRenderingMode(.alwaysTemplate) }() -private let groupIcon: UIImage = { +private func generateGroupIcon() -> UIImage { let sourceImage = UIImage(bundleImageName: "Chat/Input/Accessory Panels/PanelTextGroupIcon")! - return generateImage(CGSize(width: sourceImage.size.width + 3.0, height: sourceImage.size.height + 4.0), rotatedContext: { size, context in + return generateImage(CGSize(width: sourceImage.size.width, height: sourceImage.size.height + 4.0), rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) UIGraphicsPushContext(context) - sourceImage.draw(at: CGPoint(x: 3.0, y: 1.0 - UIScreenPixel)) + sourceImage.draw(at: CGPoint(x: 0.0, y: 1.0 - UIScreenPixel)) UIGraphicsPopContext() + + //context.setFillColor(UIColor.white.cgColor) + //context.fill(CGRect(origin: CGPoint(), size: size)) })!.precomposed().withRenderingMode(.alwaysTemplate) +} + +private let groupIcon: UIImage = { + return generateGroupIcon() }() public class ChatMessageReplyInfoNode: ASDisplayNode { @@ -286,6 +293,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { titleString = rawTitleString } else { let rawTitleString = NSMutableAttributedString(attributedString: titleString) + rawTitleString.append(NSAttributedString(string: "\u{200B}", font: titleFont, textColor: titleColor)) rawTitleString.append(NSAttributedString(string: ">", attributes: [ .attachment: groupIcon, .foregroundColor: titleColor, @@ -603,7 +611,10 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { } } - var size = CGSize(width: max(titleLayout.size.width + additionalTitleWidth - textInsets.left - textInsets.right, textLayout.size.width - textInsets.left - textInsets.right - textCutoutWidth) + leftInset + 6.0, height: titleLayout.size.height + textLayout.size.height - 2 * (textInsets.top + textInsets.bottom) + 2 * spacing) + var size = CGSize() + size.width = max(titleLayout.size.width + additionalTitleWidth - textInsets.left - textInsets.right, textLayout.size.width - textInsets.left - textInsets.right - textCutoutWidth) + leftInset + 6.0 + size.height = titleLayout.size.height + textLayout.size.height - 2 * (textInsets.top + textInsets.bottom) + 2 * spacing + size.height += 2.0 if isExpiredStory || isStory { size.width += 16.0 } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift index 27b8b28c82..ce84f89d42 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift @@ -629,7 +629,8 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { var replyInfoApply: (CGSize, (CGSize, Bool, ListViewItemUpdateAnimation) -> ChatMessageReplyInfoNode)? var replyMarkup: ReplyMarkupMessageAttribute? - var availableWidth = max(60.0, params.width - params.leftInset - params.rightInset - max(imageSize.width, 160.0) - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left) + var availableWidth = min(200.0, max(60.0, params.width - params.leftInset - params.rightInset - max(imageSize.width, 160.0) - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left)) + availableWidth -= 20.0 if isEmoji { availableWidth -= 24.0 }