mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-09-04 20:00:53 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
72ab414a74
@ -249,7 +249,7 @@ public enum ChatTextInputStateTextAttributeType: Codable, Equatable {
|
||||
case monospace
|
||||
case textMention(EnginePeer.Id)
|
||||
case textUrl(String)
|
||||
case customEmoji(stickerPack: StickerPackReference, fileId: Int64)
|
||||
case customEmoji(stickerPack: StickerPackReference?, fileId: Int64)
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: StringCodingKey.self)
|
||||
@ -268,7 +268,7 @@ public enum ChatTextInputStateTextAttributeType: Codable, Equatable {
|
||||
let url = (try? container.decode(String.self, forKey: "url")) ?? ""
|
||||
self = .textUrl(url)
|
||||
case 5:
|
||||
let stickerPack = try container.decode(StickerPackReference.self, forKey: "s")
|
||||
let stickerPack = try container.decodeIfPresent(StickerPackReference.self, forKey: "s")
|
||||
let fileId = try container.decode(Int64.self, forKey: "f")
|
||||
self = .customEmoji(stickerPack: stickerPack, fileId: fileId)
|
||||
default:
|
||||
@ -294,7 +294,7 @@ public enum ChatTextInputStateTextAttributeType: Codable, Equatable {
|
||||
try container.encode(url, forKey: "url")
|
||||
case let .customEmoji(stickerPack, fileId):
|
||||
try container.encode(5 as Int32, forKey: "t")
|
||||
try container.encode(stickerPack, forKey: "s")
|
||||
try container.encodeIfPresent(stickerPack, forKey: "s")
|
||||
try container.encode(fileId, forKey: "f")
|
||||
}
|
||||
}
|
||||
@ -400,7 +400,7 @@ public struct ChatTextInputStateText: Codable, Equatable {
|
||||
case let .textUrl(url):
|
||||
result.addAttribute(ChatTextInputAttributes.textUrl, value: ChatTextInputTextUrlAttribute(url: url), range: NSRange(location: attribute.range.lowerBound, length: attribute.range.count))
|
||||
case let .customEmoji(stickerPack, fileId):
|
||||
result.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(stickerPack: stickerPack, fileId: fileId), range: NSRange(location: attribute.range.lowerBound, length: attribute.range.count))
|
||||
result.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(stickerPack: stickerPack, fileId: fileId, file: nil), range: NSRange(location: attribute.range.lowerBound, length: attribute.range.count))
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
@ -71,6 +71,9 @@ swift_library(
|
||||
"//submodules/PremiumUI:PremiumUI",
|
||||
"//submodules/TelegramUniversalVideoContent:TelegramUniversalVideoContent",
|
||||
"//submodules/Components/HierarchyTrackingLayer:HierarchyTrackingLayer",
|
||||
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
|
||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer",
|
||||
"//submodules/TelegramUI/Components/TextNodeWithEntities",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -180,7 +180,7 @@ private final class ChatListShimmerNode: ASDisplayNode {
|
||||
let peer1: EnginePeer = .user(TelegramUser(id: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []))
|
||||
let timestamp1: Int32 = 100000
|
||||
let peers: [EnginePeer.Id: EnginePeer] = [:]
|
||||
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
let interaction = ChatListNodeInteraction(context: context, activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in })
|
||||
@ -206,7 +206,8 @@ private final class ChatListShimmerNode: ASDisplayNode {
|
||||
media: [],
|
||||
peers: peers,
|
||||
associatedMessages: [:],
|
||||
associatedMessageIds: []
|
||||
associatedMessageIds: [],
|
||||
associatedMedia: [:]
|
||||
)
|
||||
let readState = EnginePeerReadCounters()
|
||||
|
||||
|
@ -1210,7 +1210,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
if !entities.isEmpty {
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
result.append(.message(text: text.string, attributes: attributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
result.append(.message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1691,7 +1691,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
}
|
||||
}
|
||||
|
||||
let chatListInteraction = ChatListNodeInteraction(activateSearch: {
|
||||
let chatListInteraction = ChatListNodeInteraction(context: context, activateSearch: {
|
||||
}, peerSelected: { [weak self] peer, chatPeer, _ in
|
||||
interaction.dismissInput()
|
||||
interaction.openPeer(peer, chatPeer, false)
|
||||
@ -2924,7 +2924,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
|
||||
let timestamp1: Int32 = 100000
|
||||
var peers: [EnginePeer.Id: EnginePeer] = [:]
|
||||
peers[peer1.id] = peer1
|
||||
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
let interaction = ChatListNodeInteraction(context: context, activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in })
|
||||
@ -2952,7 +2952,8 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
|
||||
media: [],
|
||||
peers: peers,
|
||||
associatedMessages: [:],
|
||||
associatedMessageIds: []
|
||||
associatedMessageIds: [],
|
||||
associatedMedia: [:]
|
||||
)
|
||||
let readState = EnginePeerReadCounters()
|
||||
return ChatListItem(presentationData: chatListPresentationData, context: context, peerGroupId: .root, filterData: nil, index: EngineChatList.Item.Index(pinningIndex: 0, messageIndex: EngineMessage.Index(id: EngineMessage.Id(peerId: peer1.id, namespace: 0, id: 0), timestamp: timestamp1)), content: .peer(messages: [message], peer: EngineRenderedPeer(peer: peer1), combinedReadState: readState, isRemovedFromTotalUnreadCount: false, presence: nil, hasUnseenMentions: false, hasUnseenReactions: false, draftState: nil, inputActivities: nil, promoInfo: nil, ignoreUnreadBadge: false, displayAsMessage: false, hasFailedMessages: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
|
||||
@ -2981,7 +2982,8 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
|
||||
media: media,
|
||||
peers: peers,
|
||||
associatedMessages: [:],
|
||||
associatedMessageIds: []
|
||||
associatedMessageIds: [],
|
||||
associatedMedia: [:]
|
||||
)
|
||||
|
||||
return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(id: peer1.id), interaction: ListMessageItemInteraction.default, message: message._asMessage(), selection: hasSelection ? .selectable(selected: false) : .none, displayHeader: false, customHeader: nil, hintIsLink: true, isGlobalSearchResult: true)
|
||||
@ -3008,7 +3010,8 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
|
||||
media: media,
|
||||
peers: peers,
|
||||
associatedMessages: [:],
|
||||
associatedMessageIds: []
|
||||
associatedMessageIds: [],
|
||||
associatedMedia: [:]
|
||||
)
|
||||
|
||||
return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(id: peer1.id), interaction: ListMessageItemInteraction.default, message: message._asMessage(), selection: hasSelection ? .selectable(selected: false) : .none, displayHeader: false, customHeader: nil, hintIsLink: false, isGlobalSearchResult: true)
|
||||
@ -3035,7 +3038,8 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
|
||||
media: media,
|
||||
peers: peers,
|
||||
associatedMessages: [:],
|
||||
associatedMessageIds: []
|
||||
associatedMessageIds: [],
|
||||
associatedMedia: [:]
|
||||
)
|
||||
|
||||
return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(id: peer1.id), interaction: ListMessageItemInteraction.default, message: message._asMessage(), selection: hasSelection ? .selectable(selected: false) : .none, displayHeader: false, customHeader: nil, hintIsLink: false, isGlobalSearchResult: true)
|
||||
@ -3062,7 +3066,8 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
|
||||
media: media,
|
||||
peers: peers,
|
||||
associatedMessages: [:],
|
||||
associatedMessageIds: []
|
||||
associatedMessageIds: [],
|
||||
associatedMedia: [:]
|
||||
)
|
||||
|
||||
return ListMessageItem(presentationData: ChatPresentationData(presentationData: presentationData), context: context, chatLocation: .peer(id: peer1.id), interaction: ListMessageItemInteraction.default, message: message._asMessage(), selection: hasSelection ? .selectable(selected: false) : .none, displayHeader: false, customHeader: nil, hintIsLink: false, isGlobalSearchResult: true)
|
||||
|
@ -24,19 +24,25 @@ import TelegramUniversalVideoContent
|
||||
import UniversalMediaPlayer
|
||||
import GalleryUI
|
||||
import HierarchyTrackingLayer
|
||||
import TextNodeWithEntities
|
||||
|
||||
public enum ChatListItemContent {
|
||||
public final class DraftState: Equatable {
|
||||
let text: String
|
||||
let entities: [MessageTextEntity]
|
||||
|
||||
public init(text: String) {
|
||||
self.text = text
|
||||
public init(draft: EngineChatList.Draft) {
|
||||
self.text = draft.text
|
||||
self.entities = draft.entities
|
||||
}
|
||||
|
||||
public static func ==(lhs: DraftState, rhs: DraftState) -> Bool {
|
||||
if lhs.text != rhs.text {
|
||||
return false
|
||||
}
|
||||
if lhs.entities != rhs.entities {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -440,7 +446,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let authorNode: TextNode
|
||||
let measureNode: TextNode
|
||||
private var currentItemHeight: CGFloat?
|
||||
let textNode: TextNode
|
||||
let textNode: TextNodeWithEntities
|
||||
var dustNode: InvisibleInkDustNode?
|
||||
let inputActivitiesNode: ChatListInputActivitiesNode
|
||||
let dateNode: TextNode
|
||||
@ -624,6 +630,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
self.videoLoopCount = 0
|
||||
}
|
||||
self.updateVideoVisibility()
|
||||
|
||||
self.textNode.visibilityRect = self.visibilityStatus ? CGRect.infinite : nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -663,9 +671,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
self.authorNode.isUserInteractionEnabled = false
|
||||
self.authorNode.displaysAsynchronously = true
|
||||
|
||||
self.textNode = TextNode()
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
self.textNode.displaysAsynchronously = true
|
||||
self.textNode = TextNodeWithEntities()
|
||||
self.textNode.textNode.isUserInteractionEnabled = false
|
||||
self.textNode.textNode.displaysAsynchronously = true
|
||||
|
||||
self.inputActivitiesNode = ChatListInputActivitiesNode()
|
||||
self.inputActivitiesNode.isUserInteractionEnabled = false
|
||||
@ -707,7 +715,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
self.contextContainer.addSubnode(self.titleNode)
|
||||
self.contextContainer.addSubnode(self.authorNode)
|
||||
self.contextContainer.addSubnode(self.textNode)
|
||||
self.contextContainer.addSubnode(self.textNode.textNode)
|
||||
self.contextContainer.addSubnode(self.dateNode)
|
||||
self.contextContainer.addSubnode(self.statusNode)
|
||||
self.contextContainer.addSubnode(self.pinnedIconNode)
|
||||
@ -918,7 +926,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
func asyncLayout() -> (_ item: ChatListItem, _ params: ListViewItemLayoutParams, _ first: Bool, _ last: Bool, _ firstWithHeader: Bool, _ nextIsPinned: Bool) -> (ListViewItemNodeLayout, (Bool, Bool) -> Void) {
|
||||
let dateLayout = TextNode.asyncLayout(self.dateNode)
|
||||
let textLayout = TextNode.asyncLayout(self.textNode)
|
||||
let textLayout = TextNodeWithEntities.asyncLayout(self.textNode)
|
||||
let titleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let authorLayout = TextNode.asyncLayout(self.authorNode)
|
||||
let makeMeasureLayout = TextNode.asyncLayout(self.measureNode)
|
||||
@ -1186,10 +1194,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
if inlineAuthorPrefix == nil, let draftState = draftState {
|
||||
hasDraft = true
|
||||
authorAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Draft, font: textFont, textColor: theme.messageDraftTextColor)
|
||||
|
||||
let draftText: String = draftState.text
|
||||
|
||||
attributedText = NSAttributedString(string: foldLineBreaks(draftText.replacingOccurrences(of: "\n\n", with: " ")), font: textFont, textColor: theme.messageTextColor)
|
||||
let draftText = stringWithAppliedEntities(draftState.text, entities: draftState.entities, baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, message: nil)
|
||||
|
||||
attributedText = foldLineBreaks(draftText)
|
||||
} else if let message = messages.last {
|
||||
var composedString: NSMutableAttributedString
|
||||
|
||||
@ -1198,15 +1206,16 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
let entities = (message._asMessage().textEntitiesAttribute?.entities ?? []).filter { entity in
|
||||
if case .Spoiler = entity.type {
|
||||
switch entity.type {
|
||||
case .Spoiler, .CustomEmoji:
|
||||
return true
|
||||
} else {
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
let messageString: NSAttributedString
|
||||
if !message.text.isEmpty && entities.count > 0 {
|
||||
messageString = stringWithAppliedEntities(trimToLineCount(message.text, lineCount: authorAttributedString == nil ? 2 : 1), entities: entities, baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false)
|
||||
messageString = stringWithAppliedEntities(trimToLineCount(message.text, lineCount: authorAttributedString == nil ? 2 : 1), entities: entities, baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message._asMessage())
|
||||
} else if let spoilers = spoilers {
|
||||
let mutableString = NSMutableAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor)
|
||||
for range in spoilers {
|
||||
@ -1787,7 +1796,14 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
let _ = measureApply()
|
||||
let _ = dateApply()
|
||||
let _ = textApply()
|
||||
|
||||
let _ = textApply(TextNodeWithEntities.Arguments(
|
||||
context: item.context,
|
||||
cache: item.interaction.animationCache,
|
||||
renderer: item.interaction.animationRenderer,
|
||||
placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor
|
||||
))
|
||||
|
||||
let _ = authorApply()
|
||||
let _ = titleApply()
|
||||
let _ = badgeApply(animateBadges, !isMuted)
|
||||
@ -1862,7 +1878,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let authorNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x - 1.0, y: contentRect.minY + titleLayout.size.height), size: authorLayout.size)
|
||||
strongSelf.authorNode.frame = authorNodeFrame
|
||||
let textNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x - 1.0, y: contentRect.minY + titleLayout.size.height - 1.0 + UIScreenPixel + (authorLayout.size.height.isZero ? 0.0 : (authorLayout.size.height - 3.0))), size: textLayout.size)
|
||||
strongSelf.textNode.frame = textNodeFrame
|
||||
strongSelf.textNode.textNode.frame = textNodeFrame
|
||||
|
||||
if !textLayout.spoilers.isEmpty {
|
||||
let dustNode: InvisibleInkDustNode
|
||||
@ -1872,7 +1888,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
dustNode = InvisibleInkDustNode(textNode: nil)
|
||||
dustNode.isUserInteractionEnabled = false
|
||||
strongSelf.dustNode = dustNode
|
||||
strongSelf.contextContainer.insertSubnode(dustNode, aboveSubnode: strongSelf.textNode)
|
||||
strongSelf.contextContainer.insertSubnode(dustNode, aboveSubnode: strongSelf.textNode.textNode)
|
||||
}
|
||||
dustNode.update(size: textNodeFrame.size, color: theme.messageTextColor, textColor: theme.messageTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) })
|
||||
dustNode.frame = textNodeFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
||||
@ -1901,13 +1917,13 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
if strongSelf.inputActivitiesNode.alpha.isZero {
|
||||
strongSelf.inputActivitiesNode.alpha = 1.0
|
||||
strongSelf.textNode.alpha = 0.0
|
||||
strongSelf.textNode.textNode.alpha = 0.0
|
||||
strongSelf.authorNode.alpha = 0.0
|
||||
strongSelf.dustNode?.alpha = 0.0
|
||||
|
||||
if animated || animateContent {
|
||||
strongSelf.inputActivitiesNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
strongSelf.textNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15)
|
||||
strongSelf.textNode.textNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15)
|
||||
strongSelf.authorNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15)
|
||||
strongSelf.dustNode?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15)
|
||||
}
|
||||
@ -1915,7 +1931,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
} else {
|
||||
if !strongSelf.inputActivitiesNode.alpha.isZero {
|
||||
strongSelf.inputActivitiesNode.alpha = 0.0
|
||||
strongSelf.textNode.alpha = 1.0
|
||||
strongSelf.textNode.textNode.alpha = 1.0
|
||||
strongSelf.authorNode.alpha = 1.0
|
||||
strongSelf.dustNode?.alpha = 1.0
|
||||
if animated || animateContent {
|
||||
@ -1924,7 +1940,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.inputActivitiesNode.removeFromSupernode()
|
||||
}
|
||||
})
|
||||
strongSelf.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
strongSelf.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
strongSelf.authorNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
strongSelf.dustNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
} else {
|
||||
@ -1983,7 +1999,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let titlePosition = strongSelf.titleNode.position
|
||||
transition.animatePosition(node: strongSelf.titleNode, from: CGPoint(x: titlePosition.x - contentDelta.x, y: titlePosition.y - contentDelta.y))
|
||||
|
||||
transition.animatePositionAdditive(node: strongSelf.textNode, offset: CGPoint(x: -contentDelta.x, y: -contentDelta.y))
|
||||
transition.animatePositionAdditive(node: strongSelf.textNode.textNode, offset: CGPoint(x: -contentDelta.x, y: -contentDelta.y))
|
||||
if let dustNode = strongSelf.dustNode {
|
||||
transition.animatePositionAdditive(node: dustNode, offset: CGPoint(x: -contentDelta.x, y: -contentDelta.y))
|
||||
}
|
||||
@ -1995,7 +2011,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
if crossfadeContent {
|
||||
strongSelf.authorNode.recursivelyEnsureDisplaySynchronously(true)
|
||||
strongSelf.titleNode.recursivelyEnsureDisplaySynchronously(true)
|
||||
strongSelf.textNode.recursivelyEnsureDisplaySynchronously(true)
|
||||
strongSelf.textNode.textNode.recursivelyEnsureDisplaySynchronously(true)
|
||||
}
|
||||
|
||||
var nextTitleIconOrigin: CGFloat = contentRect.origin.x + titleLayout.size.width + 3.0 + titleOffset
|
||||
@ -2245,9 +2261,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
transition.updateFrame(node: self.inputActivitiesNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x, y: self.inputActivitiesNode.frame.minY), size: self.inputActivitiesNode.bounds.size))
|
||||
|
||||
var textFrame = self.textNode.frame
|
||||
var textFrame = self.textNode.textNode.frame
|
||||
textFrame.origin.x = contentRect.origin.x
|
||||
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
|
||||
transition.updateFrameAdditive(node: self.textNode.textNode, frame: textFrame)
|
||||
|
||||
if let dustNode = self.dustNode {
|
||||
transition.updateFrameAdditive(node: dustNode, frame: textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0))
|
||||
|
@ -14,6 +14,9 @@ import ItemListUI
|
||||
import SearchUI
|
||||
import ChatListSearchItemHeader
|
||||
import PremiumUI
|
||||
import AnimationCache
|
||||
import MultiAnimationRenderer
|
||||
import Postbox
|
||||
|
||||
public enum ChatListNodeMode {
|
||||
case chatList
|
||||
@ -75,7 +78,10 @@ public final class ChatListNodeInteraction {
|
||||
public var searchTextHighightState: String?
|
||||
var highlightedChatLocation: ChatListHighlightedLocation?
|
||||
|
||||
public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (EnginePeer, EnginePeer?, ChatListNodeEntryPromoInfo?) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, togglePeerSelected: @escaping (EnginePeer) -> Void, togglePeersSelection: @escaping ([PeerEntry], Bool) -> Void, additionalCategorySelected: @escaping (Int) -> Void, messageSelected: @escaping (EnginePeer, EngineMessage, ChatListNodeEntryPromoInfo?) -> Void, groupSelected: @escaping (EngineChatList.Group) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, setItemPinned: @escaping (EngineChatList.PinnedItem.Id, Bool) -> Void, setPeerMuted: @escaping (EnginePeer.Id, Bool) -> Void, deletePeer: @escaping (EnginePeer.Id, Bool) -> Void, updatePeerGrouping: @escaping (EnginePeer.Id, Bool) -> Void, togglePeerMarkedUnread: @escaping (EnginePeer.Id, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, hidePsa: @escaping (EnginePeer.Id) -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void, present: @escaping (ViewController) -> Void) {
|
||||
let animationCache: AnimationCache
|
||||
let animationRenderer: MultiAnimationRenderer
|
||||
|
||||
public init(context: AccountContext, activateSearch: @escaping () -> Void, peerSelected: @escaping (EnginePeer, EnginePeer?, ChatListNodeEntryPromoInfo?) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, togglePeerSelected: @escaping (EnginePeer) -> Void, togglePeersSelection: @escaping ([PeerEntry], Bool) -> Void, additionalCategorySelected: @escaping (Int) -> Void, messageSelected: @escaping (EnginePeer, EngineMessage, ChatListNodeEntryPromoInfo?) -> Void, groupSelected: @escaping (EngineChatList.Group) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, setItemPinned: @escaping (EngineChatList.PinnedItem.Id, Bool) -> Void, setPeerMuted: @escaping (EnginePeer.Id, Bool) -> Void, deletePeer: @escaping (EnginePeer.Id, Bool) -> Void, updatePeerGrouping: @escaping (EnginePeer.Id, Bool) -> Void, togglePeerMarkedUnread: @escaping (EnginePeer.Id, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, hidePsa: @escaping (EnginePeer.Id) -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void, present: @escaping (ViewController) -> Void) {
|
||||
self.activateSearch = activateSearch
|
||||
self.peerSelected = peerSelected
|
||||
self.disabledPeerSelected = disabledPeerSelected
|
||||
@ -95,6 +101,11 @@ public final class ChatListNodeInteraction {
|
||||
self.hidePsa = hidePsa
|
||||
self.activateChatPreview = activateChatPreview
|
||||
self.present = present
|
||||
|
||||
self.animationCache = AnimationCacheImpl(basePath: context.account.postbox.mediaBox.basePath + "/animation-cache", allocateTempFile: {
|
||||
return TempBox.shared.tempFile(fileName: "file").path
|
||||
})
|
||||
self.animationRenderer = MultiAnimationRendererImpl()
|
||||
}
|
||||
}
|
||||
|
||||
@ -733,7 +744,7 @@ public final class ChatListNode: ListView {
|
||||
|
||||
self.keepMinimalScrollHeightWithTopInset = navigationBarSearchContentHeight
|
||||
|
||||
let nodeInteraction = ChatListNodeInteraction(activateSearch: { [weak self] in
|
||||
let nodeInteraction = ChatListNodeInteraction(context: context, activateSearch: { [weak self] in
|
||||
if let strongSelf = self, let activateSearch = strongSelf.activateSearch {
|
||||
activateSearch()
|
||||
}
|
||||
|
@ -325,8 +325,8 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState
|
||||
}
|
||||
|
||||
var draftState: ChatListItemContent.DraftState?
|
||||
if let draftText = entry.draftText {
|
||||
draftState = ChatListItemContent.DraftState(text: draftText)
|
||||
if let draft = entry.draft {
|
||||
draftState = ChatListItemContent.DraftState(draft: draft)
|
||||
}
|
||||
|
||||
result.append(.PeerEntry(index: offsetPinnedIndex(entry.index, offset: pinnedIndexOffset), presentationData: state.presentationData, messages: updatedMessages, readState: updatedCombinedReadState, isRemovedFromTotalUnreadCount: entry.isMuted, draftState: draftState, peer: entry.renderedPeer, presence: entry.presence, hasUnseenMentions: entry.hasUnseenMentions, hasUnseenReactions: entry.hasUnseenReactions, editing: state.editing, hasActiveRevealControls: entry.index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(entry.index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[entry.index.messageIndex.id.peerId], promoInfo: nil, hasFailedMessages: entry.hasFailed, isContact: entry.isContact))
|
||||
@ -380,7 +380,7 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState
|
||||
case let .psa(type, message):
|
||||
promoInfo = .psa(type: type, message: message)
|
||||
}
|
||||
let draftState = item.item.draftText.flatMap(ChatListItemContent.DraftState.init(text:))
|
||||
let draftState = item.item.draft.flatMap(ChatListItemContent.DraftState.init)
|
||||
result.append(.PeerEntry(
|
||||
index: EngineChatList.Item.Index(pinningIndex: pinningIndex, messageIndex: item.item.index.messageIndex),
|
||||
presentationData: state.presentationData,
|
||||
|
@ -202,20 +202,26 @@ public struct Transition {
|
||||
}
|
||||
switch self.animation {
|
||||
case .none:
|
||||
view.bounds = CGRect(origin: view.bounds.origin, size: frame.size)
|
||||
view.layer.position = CGPoint(x: frame.midX, y: frame.midY)
|
||||
view.frame = frame
|
||||
//view.bounds = CGRect(origin: view.bounds.origin, size: frame.size)
|
||||
//view.layer.position = CGPoint(x: frame.midX, y: frame.midY)
|
||||
view.layer.removeAnimation(forKey: "position")
|
||||
view.layer.removeAnimation(forKey: "bounds")
|
||||
completion?(true)
|
||||
case .curve:
|
||||
let previousPosition = view.layer.presentation()?.position ?? view.center
|
||||
let previousBounds = view.layer.presentation()?.bounds ?? view.bounds
|
||||
let previousFrame: CGRect
|
||||
if (view.layer.animation(forKey: "position") != nil || view.layer.animation(forKey: "bounds") != nil), let presentation = view.layer.presentation() {
|
||||
previousFrame = presentation.frame
|
||||
} else {
|
||||
previousFrame = view.frame
|
||||
}
|
||||
|
||||
view.bounds = CGRect(origin: previousBounds.origin, size: frame.size)
|
||||
view.center = CGPoint(x: frame.midX, y: frame.midY)
|
||||
view.frame = frame
|
||||
//view.bounds = CGRect(origin: previousBounds.origin, size: frame.size)
|
||||
//view.center = CGPoint(x: frame.midX, y: frame.midY)
|
||||
|
||||
self.animatePosition(view: view, from: previousPosition, to: view.center, completion: completion)
|
||||
self.animateBounds(view: view, from: previousBounds, to: view.bounds)
|
||||
self.animatePosition(view: view, from: CGPoint(x: previousFrame.midX, y: previousFrame.midY), to: CGPoint(x: frame.midX, y: frame.midY), completion: completion)
|
||||
self.animateBounds(view: view, from: CGRect(origin: view.bounds.origin, size: previousFrame.size), to: CGRect(origin: view.bounds.origin, size: frame.size))
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,7 +236,12 @@ public struct Transition {
|
||||
view.layer.removeAnimation(forKey: "bounds")
|
||||
completion?(true)
|
||||
case .curve:
|
||||
let previousBounds = view.layer.presentation()?.bounds ?? view.bounds
|
||||
let previousBounds: CGRect
|
||||
if view.layer.animation(forKey: "bounds") != nil, let presentation = view.layer.presentation() {
|
||||
previousBounds = presentation.bounds
|
||||
} else {
|
||||
previousBounds = view.layer.bounds
|
||||
}
|
||||
view.bounds = bounds
|
||||
|
||||
self.animateBounds(view: view, from: previousBounds, to: view.bounds, completion: completion)
|
||||
@ -248,7 +259,12 @@ public struct Transition {
|
||||
view.layer.removeAnimation(forKey: "position")
|
||||
completion?(true)
|
||||
case .curve:
|
||||
let previousPosition = view.layer.presentation()?.position ?? view.center
|
||||
let previousPosition: CGPoint
|
||||
if view.layer.animation(forKey: "position") != nil, let presentation = view.layer.presentation() {
|
||||
previousPosition = presentation.position
|
||||
} else {
|
||||
previousPosition = view.layer.position
|
||||
}
|
||||
view.center = position
|
||||
|
||||
self.animatePosition(view: view, from: previousPosition, to: view.center, completion: completion)
|
||||
@ -266,7 +282,12 @@ public struct Transition {
|
||||
layer.removeAnimation(forKey: "bounds")
|
||||
completion?(true)
|
||||
case .curve:
|
||||
let previousBounds = layer.presentation()?.bounds ?? layer.bounds
|
||||
let previousBounds: CGRect
|
||||
if layer.animation(forKey: "bounds") != nil, let presentation = layer.presentation() {
|
||||
previousBounds = presentation.bounds
|
||||
} else {
|
||||
previousBounds = layer.bounds
|
||||
}
|
||||
layer.bounds = bounds
|
||||
|
||||
self.animateBounds(layer: layer, from: previousBounds, to: layer.bounds, completion: completion)
|
||||
@ -284,7 +305,12 @@ public struct Transition {
|
||||
layer.removeAnimation(forKey: "position")
|
||||
completion?(true)
|
||||
case .curve:
|
||||
let previousPosition = layer.presentation()?.position ?? layer.position
|
||||
let previousPosition: CGPoint
|
||||
if layer.animation(forKey: "position") != nil, let presentation = layer.presentation() {
|
||||
previousPosition = presentation.position
|
||||
} else {
|
||||
previousPosition = layer.position
|
||||
}
|
||||
layer.position = position
|
||||
|
||||
self.animatePosition(layer: layer, from: previousPosition, to: layer.position, completion: completion)
|
||||
|
@ -291,7 +291,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: gzippedData)
|
||||
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: Int64(gzippedData.count), attributes: [.FileName(fileName: "Log-iOS-Full.txt.zip")])
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
|
||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
|
||||
}
|
||||
@ -371,7 +371,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: logData)
|
||||
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: Int64(logData.count), attributes: [.FileName(fileName: "Log-iOS-Short.txt")])
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
|
||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
|
||||
}
|
||||
@ -457,7 +457,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: gzippedData)
|
||||
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: Int64(gzippedData.count), attributes: [.FileName(fileName: "Log-iOS-Full.txt.zip")])
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
|
||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
|
||||
}
|
||||
@ -541,7 +541,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: gzippedData)
|
||||
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: Int64(gzippedData.count), attributes: [.FileName(fileName: "Log-iOS-Full.txt.zip")])
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
|
||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
|
||||
}
|
||||
@ -625,7 +625,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: gzippedData)
|
||||
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: Int64(gzippedData.count), attributes: [.FileName(fileName: "Log-iOS-Full.txt.zip")])
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
|
||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
|
||||
}
|
||||
@ -678,7 +678,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
let messages = logs.map { (name, path) -> EnqueueMessage in
|
||||
let id = Int64.random(in: Int64.min ... Int64.max)
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
|
||||
return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
return .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
}
|
||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start()
|
||||
}
|
||||
@ -786,7 +786,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: gzippedData)
|
||||
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/zip", size: Int64(gzippedData.count), attributes: [.FileName(fileName: "Log-iOS-All.txt.zip")])
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
|
||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
|
||||
}
|
||||
@ -1388,7 +1388,7 @@ public func triggerDebugSendLogsUI(context: AccountContext, additionalInfo: Stri
|
||||
context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: gzippedData)
|
||||
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: Int64(gzippedData.count), attributes: [.FileName(fileName: "Log-iOS-Full.txt.zip")])
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
|
||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
|
||||
}
|
||||
|
@ -5,6 +5,12 @@ public struct ImmediateTextNodeLayoutInfo {
|
||||
public let size: CGSize
|
||||
public let truncated: Bool
|
||||
public let numberOfLines: Int
|
||||
|
||||
public init(size: CGSize, truncated: Bool, numberOfLines: Int) {
|
||||
self.size = size
|
||||
self.truncated = truncated
|
||||
self.numberOfLines = numberOfLines
|
||||
}
|
||||
}
|
||||
|
||||
public class ImmediateTextNode: TextNode {
|
||||
@ -125,7 +131,7 @@ public class ImmediateTextNode: TextNode {
|
||||
}
|
||||
}
|
||||
|
||||
override public func didLoad() {
|
||||
override open func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.updateInteractiveActions()
|
||||
|
@ -148,7 +148,23 @@ public final class TextNodeLayoutArguments {
|
||||
public let textStroke: (UIColor, CGFloat)?
|
||||
public let displaySpoilers: Bool
|
||||
|
||||
public init(attributedString: NSAttributedString?, backgroundColor: UIColor? = nil, minimumNumberOfLines: Int = 0, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment = .natural, verticalAlignment: TextVerticalAlignment = .top, lineSpacing: CGFloat = 0.12, cutout: TextNodeCutout? = nil, insets: UIEdgeInsets = UIEdgeInsets(), lineColor: UIColor? = nil, textShadowColor: UIColor? = nil, textStroke: (UIColor, CGFloat)? = nil, displaySpoilers: Bool = false) {
|
||||
public init(
|
||||
attributedString: NSAttributedString?,
|
||||
backgroundColor: UIColor? = nil,
|
||||
minimumNumberOfLines: Int = 0,
|
||||
maximumNumberOfLines: Int,
|
||||
truncationType: CTLineTruncationType,
|
||||
constrainedSize: CGSize,
|
||||
alignment: NSTextAlignment = .natural,
|
||||
verticalAlignment: TextVerticalAlignment = .top,
|
||||
lineSpacing: CGFloat = 0.12,
|
||||
cutout: TextNodeCutout? = nil,
|
||||
insets: UIEdgeInsets = UIEdgeInsets(),
|
||||
lineColor: UIColor? = nil,
|
||||
textShadowColor: UIColor? = nil,
|
||||
textStroke: (UIColor, CGFloat)? = nil,
|
||||
displaySpoilers: Bool = false
|
||||
) {
|
||||
self.attributedString = attributedString
|
||||
self.backgroundColor = backgroundColor
|
||||
self.minimumNumberOfLines = minimumNumberOfLines
|
||||
@ -165,6 +181,26 @@ public final class TextNodeLayoutArguments {
|
||||
self.textStroke = textStroke
|
||||
self.displaySpoilers = displaySpoilers
|
||||
}
|
||||
|
||||
public func withAttributedString(_ attributedString: NSAttributedString?) -> TextNodeLayoutArguments {
|
||||
return TextNodeLayoutArguments(
|
||||
attributedString: attributedString,
|
||||
backgroundColor: self.backgroundColor,
|
||||
minimumNumberOfLines: self.minimumNumberOfLines,
|
||||
maximumNumberOfLines: self.maximumNumberOfLines,
|
||||
truncationType: self.truncationType,
|
||||
constrainedSize: self.constrainedSize,
|
||||
alignment: self.alignment,
|
||||
verticalAlignment: self.verticalAlignment,
|
||||
lineSpacing: self.lineSpacing,
|
||||
cutout: self.cutout,
|
||||
insets: self.insets,
|
||||
lineColor: self.lineColor,
|
||||
textShadowColor: self.textShadowColor,
|
||||
textStroke: self.textStroke,
|
||||
displaySpoilers: self.displaySpoilers
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public final class TextNodeLayout: NSObject {
|
||||
@ -881,7 +917,7 @@ public final class TextAccessibilityOverlayNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
public class TextNode: ASDisplayNode {
|
||||
open class TextNode: ASDisplayNode {
|
||||
public internal(set) var cachedLayout: TextNodeLayout?
|
||||
|
||||
override public init() {
|
||||
@ -892,7 +928,7 @@ public class TextNode: ASDisplayNode {
|
||||
self.clipsToBounds = false
|
||||
}
|
||||
|
||||
override public func didLoad() {
|
||||
override open func didLoad() {
|
||||
super.didLoad()
|
||||
}
|
||||
|
||||
|
@ -684,7 +684,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
||||
break
|
||||
}
|
||||
}
|
||||
messageText = galleryCaptionStringWithAppliedEntities(message.text, entities: entities)
|
||||
messageText = galleryCaptionStringWithAppliedEntities(message.text, entities: entities, message: message)
|
||||
}
|
||||
|
||||
if self.currentMessageText != messageText || canDelete != !self.deleteButton.isHidden || canFullscreen != !self.fullscreenButton.isHidden || canShare != !self.actionButton.isHidden || canEdit != !self.editButton.isHidden || self.currentAuthorNameText != authorNameText || self.currentDateText != dateText {
|
||||
|
@ -131,8 +131,8 @@ private let italicFont = Font.italic(16.0)
|
||||
private let boldItalicFont = Font.semiboldItalic(16.0)
|
||||
private let fixedFont = UIFont(name: "Menlo-Regular", size: 15.0) ?? textFont
|
||||
|
||||
public func galleryCaptionStringWithAppliedEntities(_ text: String, entities: [MessageTextEntity]) -> NSAttributedString {
|
||||
return stringWithAppliedEntities(text, entities: entities, baseColor: .white, linkColor: UIColor(rgb: 0x5ac8fa), baseFont: textFont, linkFont: textFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: fixedFont, blockQuoteFont: textFont, underlineLinks: false)
|
||||
public func galleryCaptionStringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], message: Message?) -> NSAttributedString {
|
||||
return stringWithAppliedEntities(text, entities: entities, baseColor: .white, linkColor: UIColor(rgb: 0x5ac8fa), baseFont: textFont, linkFont: textFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: fixedFont, blockQuoteFont: textFont, underlineLinks: false, message: message)
|
||||
}
|
||||
|
||||
private func galleryMessageCaptionText(_ message: Message) -> String {
|
||||
@ -176,7 +176,7 @@ public func galleryItemForEntry(context: AccountContext, presentationData: Prese
|
||||
entities = result
|
||||
}
|
||||
|
||||
let caption = galleryCaptionStringWithAppliedEntities(text, entities: entities)
|
||||
let caption = galleryCaptionStringWithAppliedEntities(text, entities: entities, message: message)
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.effectiveAuthor.flatMap(EnginePeer.init)?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: caption, displayInfoOnTop: displayInfoOnTop, hideControls: hideControls, fromPlayingVideo: fromPlayingVideo, isSecret: isSecret, landscape: landscape, timecode: timecode, playbackRate: playbackRate, configuration: configuration, playbackCompleted: playbackCompleted, performAction: performAction, openActionOptions: openActionOptions, storeMediaPlaybackState: storeMediaPlaybackState, present: present)
|
||||
} else {
|
||||
if let fileName = file.fileName, (fileName as NSString).pathExtension.lowercased() == "json" {
|
||||
@ -224,7 +224,7 @@ public func galleryItemForEntry(context: AccountContext, presentationData: Prese
|
||||
if let result = addLocallyGeneratedEntities(descriptionText, enabledTypes: [.timecode], entities: entities, mediaDuration: 86400) {
|
||||
entities = result
|
||||
}
|
||||
description = galleryCaptionStringWithAppliedEntities(descriptionText, entities: entities)
|
||||
description = galleryCaptionStringWithAppliedEntities(descriptionText, entities: entities, message: message)
|
||||
}
|
||||
return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.effectiveAuthor.flatMap(EnginePeer.init)?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: NSAttributedString(string: ""), description: description, displayInfoOnTop: displayInfoOnTop, fromPlayingVideo: fromPlayingVideo, isSecret: isSecret, landscape: landscape, timecode: timecode, playbackRate: playbackRate, configuration: configuration, performAction: performAction, openActionOptions: openActionOptions, storeMediaPlaybackState: storeMediaPlaybackState, present: present)
|
||||
} else {
|
||||
|
@ -340,7 +340,7 @@ public final class SecretMediaPreviewController: ViewController {
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
if let strongSelf = self, strongSelf.traceVisibility() {
|
||||
if strongSelf.messageId.peerId.namespace == Namespaces.Peer.CloudUser {
|
||||
let _ = enqueueMessages(account: strongSelf.context.account, peerId: strongSelf.messageId.peerId, messages: [.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaAction(action: TelegramMediaActionType.historyScreenshot)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]).start()
|
||||
let _ = enqueueMessages(account: strongSelf.context.account, peerId: strongSelf.messageId.peerId, messages: [.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaAction(action: TelegramMediaActionType.historyScreenshot)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]).start()
|
||||
} else if strongSelf.messageId.peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
let _ = strongSelf.context.engine.messages.addSecretChatMessageScreenshot(peerId: strongSelf.messageId.peerId).start()
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ public final class HashtagSearchController: TelegramBaseController {
|
||||
let chatListPresentationData = ChatListPresentationData(theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true)
|
||||
return result.messages.map({ .message(EngineMessage($0), EngineRenderedPeer(message: EngineMessage($0)), result.readStates[$0.id.peerId].flatMap(EnginePeerReadCounters.init), chatListPresentationData, result.totalCount, nil, false, .index($0.index), nil, .generic, false) })
|
||||
}
|
||||
let interaction = ChatListNodeInteraction(activateSearch: {
|
||||
let interaction = ChatListNodeInteraction(context: context, activateSearch: {
|
||||
}, peerSelected: { _, _, _ in
|
||||
}, disabledPeerSelected: { _ in
|
||||
}, togglePeerSelected: { _ in
|
||||
|
@ -405,7 +405,7 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll
|
||||
} else {
|
||||
title = self.presentationData.strings.ImportStickerPack_StickerCount(Int32(self.currentItems.count))
|
||||
}
|
||||
self.contentTitleNode.attributedText = stringWithAppliedEntities(title, entities: [], baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleFont, italicFont: titleFont, boldItalicFont: titleFont, fixedFont: titleFont, blockQuoteFont: titleFont)
|
||||
self.contentTitleNode.attributedText = stringWithAppliedEntities(title, entities: [], baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleFont, italicFont: titleFont, boldItalicFont: titleFont, fixedFont: titleFont, blockQuoteFont: titleFont, message: nil)
|
||||
|
||||
if !forceTitleUpdate {
|
||||
transaction = StickerPackPreviewGridTransaction(previousList: previousItems, list: self.currentItems, account: self.context.account, interaction: self.interaction, theme: self.presentationData.theme)
|
||||
|
@ -1365,7 +1365,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}, showAll: false)
|
||||
|
||||
let peer = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
|
||||
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer, text: "", attributes: [], media: [map], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [])
|
||||
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer, text: "", attributes: [], media: [map], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:])
|
||||
|
||||
let controller = LocationViewController(context: self.context, subject: message, params: controllerParams)
|
||||
self.pushController(controller)
|
||||
|
@ -197,7 +197,7 @@ public class ItemListAddressItemNode: ListViewItemNode {
|
||||
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.label, font: labelFont, textColor: labelColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftOffset - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let baseColor = item.theme.list.itemPrimaryTextColor
|
||||
let string = stringWithAppliedEntities(item.text, entities: [], baseColor: baseColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textFont)
|
||||
let string = stringWithAppliedEntities(item.text, entities: [], baseColor: baseColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textFont, message: nil)
|
||||
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftOffset - leftInset - rightInset - 98.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let padding: CGFloat = !item.label.isEmpty ? 39.0 : 20.0
|
||||
|
@ -197,7 +197,7 @@ public class ItemListMultilineTextItemNode: ListViewItemNode {
|
||||
}
|
||||
|
||||
let entities = generateTextEntities(item.text, enabledTypes: item.enabledEntityTypes)
|
||||
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: textColor, linkColor: item.presentationData.theme.list.itemAccentColor, baseFont: baseFont, linkFont: linkFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: titleFixedFont, blockQuoteFont: baseFont)
|
||||
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: textColor, linkColor: item.presentationData.theme.list.itemAccentColor, baseFont: baseFont, linkFont: linkFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: titleFixedFont, blockQuoteFont: baseFont, message: nil)
|
||||
|
||||
let (titleLayout, titleApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
|
@ -215,7 +215,7 @@ public class ItemListTextWithLabelItemNode: ListViewItemNode {
|
||||
case .highlighted:
|
||||
baseColor = item.presentationData.theme.list.itemHighlightedColor
|
||||
}
|
||||
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: baseColor, linkColor: item.presentationData.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textFont)
|
||||
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: baseColor, linkColor: item.presentationData.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textFont, message: nil)
|
||||
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: item.multiline ? 0 : 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftOffset - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let contentSize = CGSize(width: params.width, height: textLayout.size.height + labelLayout.size.height + 22.0)
|
||||
|
@ -347,7 +347,7 @@ public func legacyEnqueueGifMessage(account: Account, data: Data, correlationId:
|
||||
fileAttributes.append(.Animated)
|
||||
|
||||
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes)
|
||||
subscriber.putNext(.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId))
|
||||
subscriber.putNext(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId))
|
||||
subscriber.putCompletion()
|
||||
} else {
|
||||
subscriber.putError(Void())
|
||||
@ -389,7 +389,7 @@ public func legacyEnqueueVideoMessage(account: Account, data: Data, correlationI
|
||||
fileAttributes.append(.Animated)
|
||||
|
||||
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes)
|
||||
subscriber.putNext(.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId))
|
||||
subscriber.putNext(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId))
|
||||
subscriber.putCompletion()
|
||||
} else {
|
||||
subscriber.putError(Void())
|
||||
@ -463,7 +463,7 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
|
||||
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId, correlationId: nil), uniqueId: item.uniqueId, isFile: false))
|
||||
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId, correlationId: nil), uniqueId: item.uniqueId, isFile: false))
|
||||
}
|
||||
}
|
||||
case let .asset(asset):
|
||||
@ -486,7 +486,7 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
|
||||
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId, correlationId: nil), uniqueId: item.uniqueId, isFile: false))
|
||||
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId, correlationId: nil), uniqueId: item.uniqueId, isFile: false))
|
||||
case .tempFile:
|
||||
break
|
||||
}
|
||||
@ -516,7 +516,7 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
|
||||
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId, correlationId: nil), uniqueId: item.uniqueId, isFile: true))
|
||||
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId, correlationId: nil), uniqueId: item.uniqueId, isFile: true))
|
||||
case let .asset(asset):
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
@ -530,7 +530,7 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
|
||||
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId, correlationId: nil), uniqueId: item.uniqueId, isFile: true))
|
||||
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId, correlationId: nil), uniqueId: item.uniqueId, isFile: true))
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -665,7 +665,7 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
|
||||
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId, correlationId: nil), uniqueId: item.uniqueId, isFile: asFile))
|
||||
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId, correlationId: nil), uniqueId: item.uniqueId, isFile: asFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -732,10 +732,10 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
|
||||
|
||||
let previewText = groupLayouts.count > 1 ? presentationData.strings.Attachment_MessagesPreview : presentationData.strings.Attachment_MessagePreview
|
||||
|
||||
let previewMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: previewText, entities: []))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
|
||||
let previewMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: previewText, entities: []))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:])
|
||||
let previewItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [previewMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, isCentered: true)
|
||||
|
||||
let dragMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: presentationData.strings.Attachment_DragToReorder, entities: []))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
|
||||
let dragMessage = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [TelegramMediaAction(action: .customText(text: presentationData.strings.Attachment_DragToReorder, entities: []))], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:])
|
||||
let dragItem = self.context.sharedContext.makeChatMessagePreviewItem(context: context, messages: [dragMessage], theme: theme, strings: presentationData.strings, wallpaper: wallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: bubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.wallpaperBackgroundNode, availableReactions: nil, isCentered: true)
|
||||
|
||||
let headerItems: [ListViewItem] = [previewItem, dragItem]
|
||||
|
@ -6,7 +6,16 @@ import MobileCoreServices
|
||||
import TextFormat
|
||||
|
||||
private func rtfStringWithAppliedEntities(_ text: String, entities: [MessageTextEntity]) -> String {
|
||||
let test = stringWithAppliedEntities(text, entities: entities, baseColor: .black, linkColor: .black, baseFont: Font.regular(14.0), linkFont: Font.regular(14.0), boldFont: Font.semibold(14.0), italicFont: Font.italic(14.0), boldItalicFont: Font.semiboldItalic(14.0), fixedFont: Font.monospace(14.0), blockQuoteFont: Font.regular(14.0), underlineLinks: false, external: true)
|
||||
let sourceString = stringWithAppliedEntities(text, entities: entities, baseColor: .black, linkColor: .black, baseFont: Font.regular(14.0), linkFont: Font.regular(14.0), boldFont: Font.semibold(14.0), italicFont: Font.italic(14.0), boldItalicFont: Font.semiboldItalic(14.0), fixedFont: Font.monospace(14.0), blockQuoteFont: Font.regular(14.0), underlineLinks: false, external: true, message: nil)
|
||||
let test = NSMutableAttributedString(attributedString: sourceString)
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
test.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: NSRange(location: 0, length: sourceString.length), using: { value, range, _ in
|
||||
if let value = value as? ChatTextInputTextCustomEmojiAttribute {
|
||||
test.addAttribute(NSAttributedString.Key.link, value: URL(string: "tg://emoji?\(value.fileId)")!, range: range)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if let data = try? test.data(from: NSRange(location: 0, length: test.length), documentAttributes: [NSAttributedString.DocumentAttributeKey.documentType: NSAttributedString.DocumentType.rtf]) {
|
||||
if var rtf = String(data: data, encoding: .windowsCP1252) {
|
||||
@ -60,7 +69,16 @@ private func chatInputStateString(attributedString: NSAttributedString) -> NSAtt
|
||||
|
||||
public func chatInputStateStringFromRTF(_ data: Data, type: NSAttributedString.DocumentType) -> NSAttributedString? {
|
||||
if let attributedString = try? NSAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType: type], documentAttributes: nil) {
|
||||
return chatInputStateString(attributedString: attributedString)
|
||||
let updatedString = NSMutableAttributedString(attributedString: attributedString)
|
||||
updatedString.enumerateAttribute(NSAttributedString.Key.link, in: NSRange(location: 0, length: attributedString.length), using: { value, range, _ in
|
||||
if let url = value as? URL, url.scheme == "tg", url.host == "emoji", let query = url.query {
|
||||
if let fileId = Int64(query) {
|
||||
updatedString.removeAttribute(NSAttributedString.Key.link, range: range)
|
||||
updatedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(stickerPack: nil, fileId: fileId, file: nil), range: range)
|
||||
}
|
||||
}
|
||||
})
|
||||
return chatInputStateString(attributedString: updatedString)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ private func updateMessagePeers(_ message: Message, updatedPeers: [PeerId: Peer]
|
||||
peers[peerId] = currentPeer
|
||||
}
|
||||
}
|
||||
return Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds)
|
||||
return Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds, associatedMedia: message.associatedMedia)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ final class MutableGlobalMessageTagsView: MutablePostboxView {
|
||||
hasChanges = true
|
||||
}
|
||||
case let .message(message):
|
||||
if self.add(.message(Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: updatedTimestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds))) {
|
||||
if self.add(.message(Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: updatedTimestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds, associatedMedia: message.associatedMedia))) {
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
|
@ -68,10 +68,6 @@ public struct MediaId: Hashable, PostboxCoding, CustomStringConvertible, Codable
|
||||
}
|
||||
}
|
||||
|
||||
public protocol AssociatedMediaData: AnyObject, PostboxCoding {
|
||||
func isEqual(to: AssociatedMediaData) -> Bool
|
||||
}
|
||||
|
||||
public protocol Media: AnyObject, PostboxCoding {
|
||||
var id: MediaId? { get }
|
||||
var peerIds: [PeerId] { get }
|
||||
|
@ -576,6 +576,7 @@ public struct MessageForwardInfo: Equatable {
|
||||
public protocol MessageAttribute: AnyObject, PostboxCoding {
|
||||
var associatedPeerIds: [PeerId] { get }
|
||||
var associatedMessageIds: [MessageId] { get }
|
||||
var associatedMediaIds: [MediaId] { get }
|
||||
var automaticTimestampBasedAttribute: (UInt16, Int32)? { get }
|
||||
}
|
||||
|
||||
@ -588,6 +589,10 @@ public extension MessageAttribute {
|
||||
return []
|
||||
}
|
||||
|
||||
var associatedMediaIds: [MediaId] {
|
||||
return []
|
||||
}
|
||||
|
||||
var automaticTimestampBasedAttribute: (UInt16, Int32)? {
|
||||
return nil
|
||||
}
|
||||
@ -619,12 +624,13 @@ public final class Message {
|
||||
public let peers: SimpleDictionary<PeerId, Peer>
|
||||
public let associatedMessages: SimpleDictionary<MessageId, Message>
|
||||
public let associatedMessageIds: [MessageId]
|
||||
public let associatedMedia: [MediaId: Media]
|
||||
|
||||
public var index: MessageIndex {
|
||||
return MessageIndex(id: self.id, timestamp: self.timestamp)
|
||||
}
|
||||
|
||||
public init(stableId: UInt32, stableVersion: UInt32, id: MessageId, globallyUniqueId: Int64?, groupingKey: Int64?, groupInfo: MessageGroupInfo?, threadId: Int64?, timestamp: Int32, flags: MessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: MessageForwardInfo?, author: Peer?, text: String, attributes: [MessageAttribute], media: [Media], peers: SimpleDictionary<PeerId, Peer>, associatedMessages: SimpleDictionary<MessageId, Message>, associatedMessageIds: [MessageId]) {
|
||||
public init(stableId: UInt32, stableVersion: UInt32, id: MessageId, globallyUniqueId: Int64?, groupingKey: Int64?, groupInfo: MessageGroupInfo?, threadId: Int64?, timestamp: Int32, flags: MessageFlags, tags: MessageTags, globalTags: GlobalMessageTags, localTags: LocalMessageTags, forwardInfo: MessageForwardInfo?, author: Peer?, text: String, attributes: [MessageAttribute], media: [Media], peers: SimpleDictionary<PeerId, Peer>, associatedMessages: SimpleDictionary<MessageId, Message>, associatedMessageIds: [MessageId], associatedMedia: [MediaId: Media]) {
|
||||
self.stableId = stableId
|
||||
self.stableVersion = stableVersion
|
||||
self.id = id
|
||||
@ -645,46 +651,47 @@ public final class Message {
|
||||
self.peers = peers
|
||||
self.associatedMessages = associatedMessages
|
||||
self.associatedMessageIds = associatedMessageIds
|
||||
self.associatedMedia = associatedMedia
|
||||
}
|
||||
|
||||
public func withUpdatedText(_ text: String) -> Message {
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia)
|
||||
}
|
||||
|
||||
public func withUpdatedTimestamp(_ timestamp: Int32) -> Message {
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia)
|
||||
}
|
||||
|
||||
public func withUpdatedMedia(_ media: [Media]) -> Message {
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia)
|
||||
}
|
||||
|
||||
public func withUpdatedPeers(_ peers: SimpleDictionary<PeerId, Peer>) -> Message {
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia)
|
||||
}
|
||||
|
||||
public func withUpdatedFlags(_ flags: MessageFlags) -> Message {
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia)
|
||||
}
|
||||
|
||||
func withUpdatedGroupInfo(_ groupInfo: MessageGroupInfo?) -> Message {
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia)
|
||||
}
|
||||
|
||||
public func withUpdatedAttributes(_ attributes: [MessageAttribute]) -> Message {
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia)
|
||||
}
|
||||
|
||||
func withUpdatedAssociatedMessages(_ associatedMessages: SimpleDictionary<MessageId, Message>) -> Message {
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: associatedMessages, associatedMessageIds: self.associatedMessageIds)
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia)
|
||||
}
|
||||
|
||||
public func withUpdatedForwardInfo(_ forwardInfo: MessageForwardInfo?) -> Message {
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia)
|
||||
}
|
||||
|
||||
public func withUpdatedAuthor(_ author: Peer?) -> Message {
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds)
|
||||
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -548,6 +548,18 @@ final class MessageHistoryTable: Table {
|
||||
}
|
||||
}
|
||||
|
||||
func storeMediaIfNotPresent(media: Media) {
|
||||
guard let id = media.id else {
|
||||
return
|
||||
}
|
||||
if let _ = self.messageMediaTable.get(id, embedded: { index, id in
|
||||
return self.embeddedMediaForIndex(index, id: id)
|
||||
}) {
|
||||
} else {
|
||||
let _ = self.messageMediaTable.set(media, index: nil, messageHistoryTable: self)
|
||||
}
|
||||
}
|
||||
|
||||
func getMedia(_ id: MediaId) -> Media? {
|
||||
return self.messageMediaTable.get(id, embedded: { index, id in
|
||||
return self.embeddedMediaForIndex(index, id: id)
|
||||
@ -2516,12 +2528,20 @@ final class MessageHistoryTable: Table {
|
||||
|
||||
var associatedMessageIds: [MessageId] = []
|
||||
var associatedMessages = SimpleDictionary<MessageId, Message>()
|
||||
var associatedMedia: [MediaId: Media] = [:]
|
||||
for attribute in parsedAttributes {
|
||||
for peerId in attribute.associatedPeerIds {
|
||||
if let peer = peerTable.get(peerId) {
|
||||
peers[peer.id] = peer
|
||||
}
|
||||
}
|
||||
for mediaId in attribute.associatedMediaIds {
|
||||
if associatedMedia[mediaId] == nil {
|
||||
if let media = self.getMedia(mediaId) {
|
||||
associatedMedia[mediaId] = media
|
||||
}
|
||||
}
|
||||
}
|
||||
associatedMessageIds.append(contentsOf: attribute.associatedMessageIds)
|
||||
if addAssociatedMessages {
|
||||
for messageId in attribute.associatedMessageIds {
|
||||
@ -2534,7 +2554,7 @@ final class MessageHistoryTable: Table {
|
||||
}
|
||||
}
|
||||
|
||||
return Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: parsedAttributes, media: parsedMedia, peers: peers, associatedMessages: associatedMessages, associatedMessageIds: associatedMessageIds)
|
||||
return Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: parsedAttributes, media: parsedMedia, peers: peers, associatedMessages: associatedMessages, associatedMessageIds: associatedMessageIds, associatedMedia: associatedMedia)
|
||||
}
|
||||
|
||||
func renderMessagePeers(_ message: Message, peerTable: PeerTable) -> Message {
|
||||
|
@ -137,7 +137,7 @@ enum MutableMessageHistoryEntry {
|
||||
return .IntermediateMessageEntry(updatedMessage, location, monthLocation)
|
||||
case let .MessageEntry(value, reloadAssociatedMessages, reloadPeers):
|
||||
let message = value.message
|
||||
let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds)
|
||||
let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds, associatedMedia: message.associatedMedia)
|
||||
return .MessageEntry(MessageHistoryMessageEntry(message: updatedMessage, location: value.location, monthLocation: value.monthLocation, attributes: value.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers)
|
||||
}
|
||||
}
|
||||
|
@ -1239,7 +1239,7 @@ final class HistoryViewLoadedState {
|
||||
messageMedia.append(media)
|
||||
}
|
||||
}
|
||||
let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: messageMedia, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds)
|
||||
let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: messageMedia, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds, associatedMedia: message.associatedMedia)
|
||||
return .MessageEntry(MessageHistoryMessageEntry(message: updatedMessage, location: value.location, monthLocation: value.monthLocation, attributes: value.attributes), reloadAssociatedMessages: reloadAssociatedMessages, reloadPeers: reloadPeers)
|
||||
}
|
||||
case .IntermediateMessageEntry:
|
||||
|
@ -63,7 +63,7 @@ final class MessageMediaTable: Table {
|
||||
return nil
|
||||
}
|
||||
|
||||
func set(_ media: Media, index: MessageIndex, messageHistoryTable: MessageHistoryTable, sharedWriteBuffer: WriteBuffer = WriteBuffer(), sharedEncoder: PostboxEncoder = PostboxEncoder()) -> InsertMediaResult {
|
||||
func set(_ media: Media, index: MessageIndex?, messageHistoryTable: MessageHistoryTable, sharedWriteBuffer: WriteBuffer = WriteBuffer(), sharedEncoder: PostboxEncoder = PostboxEncoder()) -> InsertMediaResult {
|
||||
if let id = media.id {
|
||||
if let value = self.valueBox.get(self.table, key: self.key(id)) {
|
||||
var type: Int8 = 0
|
||||
@ -126,23 +126,45 @@ final class MessageMediaTable: Table {
|
||||
return .Embed(media)
|
||||
}
|
||||
} else {
|
||||
sharedWriteBuffer.reset()
|
||||
var type: Int8 = MediaEntryType.MessageReference.rawValue
|
||||
sharedWriteBuffer.write(&type, offset: 0, length: 1)
|
||||
var idPeerId: Int64 = index.id.peerId.toInt64()
|
||||
var idNamespace: Int32 = index.id.namespace
|
||||
var idId: Int32 = index.id.id
|
||||
var idTimestamp: Int32 = index.timestamp
|
||||
sharedWriteBuffer.write(&idPeerId, offset: 0, length: 8)
|
||||
sharedWriteBuffer.write(&idNamespace, offset: 0, length: 4)
|
||||
sharedWriteBuffer.write(&idId, offset: 0, length: 4)
|
||||
sharedWriteBuffer.write(&idTimestamp, offset: 0, length: 4)
|
||||
|
||||
withExtendedLifetime(sharedWriteBuffer, {
|
||||
self.valueBox.set(self.table, key: self.key(id), value: sharedWriteBuffer.readBufferNoCopy())
|
||||
})
|
||||
|
||||
return .Embed(media)
|
||||
if let index = index {
|
||||
sharedWriteBuffer.reset()
|
||||
var type: Int8 = MediaEntryType.MessageReference.rawValue
|
||||
sharedWriteBuffer.write(&type, offset: 0, length: 1)
|
||||
var idPeerId: Int64 = index.id.peerId.toInt64()
|
||||
var idNamespace: Int32 = index.id.namespace
|
||||
var idId: Int32 = index.id.id
|
||||
var idTimestamp: Int32 = index.timestamp
|
||||
sharedWriteBuffer.write(&idPeerId, offset: 0, length: 8)
|
||||
sharedWriteBuffer.write(&idNamespace, offset: 0, length: 4)
|
||||
sharedWriteBuffer.write(&idId, offset: 0, length: 4)
|
||||
sharedWriteBuffer.write(&idTimestamp, offset: 0, length: 4)
|
||||
|
||||
withExtendedLifetime(sharedWriteBuffer, {
|
||||
self.valueBox.set(self.table, key: self.key(id), value: sharedWriteBuffer.readBufferNoCopy())
|
||||
})
|
||||
|
||||
return .Embed(media)
|
||||
} else {
|
||||
sharedWriteBuffer.reset()
|
||||
var directType: Int8 = MediaEntryType.Direct.rawValue
|
||||
sharedWriteBuffer.write(&directType, offset: 0, length: 1)
|
||||
|
||||
sharedEncoder.reset()
|
||||
sharedEncoder.encodeRootObject(media)
|
||||
let mediaBuffer = sharedEncoder.memoryBuffer()
|
||||
var mediaBufferLength = Int32(mediaBuffer.length)
|
||||
sharedWriteBuffer.write(&mediaBufferLength, offset: 0, length: 4)
|
||||
sharedWriteBuffer.write(mediaBuffer.memory, offset: 0, length: mediaBuffer.length)
|
||||
|
||||
var messageReferenceCount: Int32 = 2
|
||||
sharedWriteBuffer.write(&messageReferenceCount, offset: 0, length: 4)
|
||||
|
||||
withExtendedLifetime(sharedWriteBuffer, {
|
||||
self.valueBox.set(self.table, key: self.key(id), value: sharedWriteBuffer.readBufferNoCopy())
|
||||
})
|
||||
|
||||
return .Reference
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .Embed(media)
|
||||
|
@ -529,6 +529,11 @@ public final class Transaction {
|
||||
return self.postbox?.updateMedia(id, update: update) ?? Set()
|
||||
}
|
||||
|
||||
public func storeMediaIfNotPresent(media: Media) {
|
||||
assert(!self.disposed)
|
||||
self.postbox?.storeMediaIfNotPresent(media: media)
|
||||
}
|
||||
|
||||
public func replaceItemCollections(namespace: ItemCollectionId.Namespace, itemCollections: [(ItemCollectionId, ItemCollectionInfo, [ItemCollectionItem])]) {
|
||||
assert(!self.disposed)
|
||||
self.postbox?.replaceItemCollections(namespace: namespace, itemCollections: itemCollections)
|
||||
@ -2295,6 +2300,10 @@ final class PostboxImpl {
|
||||
return updatedMessageIndices
|
||||
}
|
||||
|
||||
fileprivate func storeMediaIfNotPresent(media: Media) {
|
||||
self.messageHistoryTable.storeMediaIfNotPresent(media: media)
|
||||
}
|
||||
|
||||
fileprivate func replaceItemCollections(namespace: ItemCollectionId.Namespace, itemCollections: [(ItemCollectionId, ItemCollectionInfo, [ItemCollectionItem])]) {
|
||||
var infos: [(ItemCollectionId, ItemCollectionInfo)] = []
|
||||
for (id, info, items) in itemCollections {
|
||||
|
@ -1063,7 +1063,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
if isGiftView {
|
||||
termsString = .plain(NSAttributedString())
|
||||
} else if let promoConfiguration = context.state.promoConfiguration {
|
||||
let attributedString = stringWithAppliedEntities(promoConfiguration.status, entities: promoConfiguration.statusEntities, baseColor: termsTextColor, linkColor: environment.theme.list.itemAccentColor, baseFont: termsFont, linkFont: termsFont, boldFont: boldTermsFont, italicFont: italicTermsFont, boldItalicFont: boldItalicTermsFont, fixedFont: monospaceTermsFont, blockQuoteFont: termsFont)
|
||||
let attributedString = stringWithAppliedEntities(promoConfiguration.status, entities: promoConfiguration.statusEntities, baseColor: termsTextColor, linkColor: environment.theme.list.itemAccentColor, baseFont: termsFont, linkFont: termsFont, boldFont: boldTermsFont, italicFont: italicTermsFont, boldItalicFont: boldItalicTermsFont, fixedFont: monospaceTermsFont, blockQuoteFont: termsFont, message: nil)
|
||||
termsString = .plain(attributedString)
|
||||
} else {
|
||||
termsString = .markdown(
|
||||
|
@ -167,22 +167,22 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
|
||||
peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
|
||||
|
||||
let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3)
|
||||
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
|
||||
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:])
|
||||
|
||||
let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
|
||||
|
||||
let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
|
||||
|
||||
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
|
||||
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)]
|
||||
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
|
||||
|
||||
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
|
||||
|
||||
let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
|
||||
|
||||
let width: CGFloat
|
||||
|
@ -149,7 +149,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
||||
|
||||
let forwardInfo = MessageForwardInfo(author: item.linkEnabled ? peers[peerId] : nil, source: nil, sourceMessageId: nil, date: 0, authorSignature: item.linkEnabled ? nil : item.peerName, psaType: nil, flags: [])
|
||||
|
||||
let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, isCentered: false)
|
||||
let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: forwardInfo, author: nil, text: item.strings.Privacy_Forwards_PreviewMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, isCentered: false)
|
||||
|
||||
var node: ListViewItemNode?
|
||||
if let current = currentNode {
|
||||
|
@ -227,7 +227,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
|
||||
attributes.append(ReactionsMessageAttribute(canViewList: false, reactions: [MessageReaction(value: reaction, count: 1, isSelected: true)], recentPeers: []))
|
||||
}
|
||||
|
||||
let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: chatPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[userPeerId], text: messageText, attributes: attributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: item.availableReactions, isCentered: true)
|
||||
let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: chatPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[userPeerId], text: messageText, attributes: attributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: item.availableReactions, isCentered: true)
|
||||
|
||||
var node: ListViewItemNode?
|
||||
if let current = currentNode {
|
||||
|
@ -60,7 +60,7 @@ final class TermsOfServiceControllerNode: ViewControllerTracingNode {
|
||||
|
||||
let fontSize = floor(presentationData.listsFontSize.baseDisplaySize * 15.0 / 17.0)
|
||||
|
||||
self.contentTextNode.attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: presentationData.theme.list.itemPrimaryTextColor, linkColor: presentationData.theme.list.itemAccentColor, baseFont: Font.regular(fontSize), linkFont: Font.regular(fontSize), boldFont: Font.semibold(fontSize), italicFont: Font.italic(fontSize), boldItalicFont: Font.semiboldItalic(fontSize), fixedFont: Font.monospace(fontSize), blockQuoteFont: Font.regular(fontSize))
|
||||
self.contentTextNode.attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: presentationData.theme.list.itemPrimaryTextColor, linkColor: presentationData.theme.list.itemAccentColor, baseFont: Font.regular(fontSize), linkFont: Font.regular(fontSize), boldFont: Font.semibold(fontSize), italicFont: Font.italic(fontSize), boldItalicFont: Font.semiboldItalic(fontSize), fixedFont: Font.monospace(fontSize), blockQuoteFont: Font.regular(fontSize), message: nil)
|
||||
|
||||
self.toolbarNode = ASDisplayNode()
|
||||
self.toolbarSeparatorNode = ASDisplayNode()
|
||||
|
@ -210,7 +210,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
||||
private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
var items: [ChatListItem] = []
|
||||
|
||||
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
let interaction = ChatListNodeInteraction(context: self.context, activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
|
||||
}, activateChatPreview: { _, _, gesture in
|
||||
gesture?.cancel()
|
||||
@ -256,7 +256,8 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
||||
media: [],
|
||||
peers: [:],
|
||||
associatedMessages: [:],
|
||||
associatedMessageIds: []
|
||||
associatedMessageIds: [],
|
||||
associatedMedia: [:]
|
||||
)
|
||||
],
|
||||
peer: EngineRenderedPeer(peer: peer),
|
||||
@ -411,22 +412,22 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
||||
peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_ThemePreview_Chat_2_ReplyName, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
|
||||
|
||||
let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3)
|
||||
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
|
||||
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:])
|
||||
|
||||
let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
|
||||
|
||||
let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
|
||||
|
||||
let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA="
|
||||
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)]
|
||||
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
|
||||
|
||||
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message3 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message3], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.paused), fetchStatus: .Local), tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
|
||||
|
||||
let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message4], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, isCentered: false))
|
||||
|
||||
let width: CGFloat
|
||||
|
@ -537,7 +537,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
||||
|> take(1)).start(next: { previewTheme, settings in
|
||||
let saveThemeTemplateFile: (String, LocalFileMediaResource, @escaping () -> Void) -> Void = { title, resource, completion in
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: resource.fileId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/x-tgtheme-ios", size: nil, attributes: [.FileName(fileName: "\(title).tgios-theme")])
|
||||
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
|
||||
let _ = enqueueMessages(account: context.account, peerId: context.account.peerId, messages: [message]).start()
|
||||
|
||||
|
@ -830,7 +830,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
var items: [ChatListItem] = []
|
||||
|
||||
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
let interaction = ChatListNodeInteraction(context: self.context, activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
|
||||
}, activateChatPreview: { _, _, gesture in
|
||||
gesture?.cancel()
|
||||
@ -876,7 +876,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
media: [],
|
||||
peers: [:],
|
||||
associatedMessages: [:],
|
||||
associatedMessageIds: []
|
||||
associatedMessageIds: [],
|
||||
associatedMedia: [:]
|
||||
)
|
||||
],
|
||||
peer: EngineRenderedPeer(peer: peer),
|
||||
@ -1002,20 +1003,20 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
|
||||
var sampleMessages: [Message] = []
|
||||
|
||||
let message1 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message1 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
sampleMessages.append(message1)
|
||||
|
||||
let message2 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message2 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
sampleMessages.append(message2)
|
||||
|
||||
let message3 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message3 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
sampleMessages.append(message3)
|
||||
|
||||
let message4 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_7_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message4 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_7_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
messages[message4.id] = message4
|
||||
sampleMessages.append(message4)
|
||||
|
||||
let message5 = Message(stableId: 5, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 5), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66004, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: message4.id, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message5 = Message(stableId: 5, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 5), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66004, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: message4.id, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
messages[message5.id] = message5
|
||||
sampleMessages.append(message5)
|
||||
|
||||
@ -1023,13 +1024,13 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)]
|
||||
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
|
||||
|
||||
let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
sampleMessages.append(message6)
|
||||
|
||||
let message7 = Message(stableId: 7, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 7), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66006, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: message5.id, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message7 = Message(stableId: 7, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 7), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66006, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: message5.id, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
sampleMessages.append(message7)
|
||||
|
||||
let message8 = Message(stableId: 8, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 8), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66007, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message8 = Message(stableId: 8, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 8), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66007, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
sampleMessages.append(message8)
|
||||
|
||||
items = sampleMessages.reversed().map { message in
|
||||
|
@ -354,7 +354,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
var items: [ChatListItem] = []
|
||||
|
||||
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
let interaction = ChatListNodeInteraction(context: self.context, activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
|
||||
}, activateChatPreview: { _, _, gesture in
|
||||
gesture?.cancel()
|
||||
@ -399,7 +399,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
media: [],
|
||||
peers: [:],
|
||||
associatedMessages: [:],
|
||||
associatedMessageIds: []
|
||||
associatedMessageIds: [],
|
||||
associatedMedia: [:]
|
||||
)
|
||||
],
|
||||
peer: EngineRenderedPeer(peer: peer),
|
||||
@ -563,20 +564,20 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
var sampleMessages: [Message] = []
|
||||
|
||||
let message1 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message1 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
sampleMessages.append(message1)
|
||||
|
||||
let message2 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message2 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_5_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
sampleMessages.append(message2)
|
||||
|
||||
let message3 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message3 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_6_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
sampleMessages.append(message3)
|
||||
|
||||
let message4 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_7_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message4 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_7_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
messages[message4.id] = message4
|
||||
sampleMessages.append(message4)
|
||||
|
||||
let message5 = Message(stableId: 5, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 5), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66004, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: message4.id, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message5 = Message(stableId: 5, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 5), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66004, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: message4.id, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
messages[message5.id] = message5
|
||||
sampleMessages.append(message5)
|
||||
|
||||
@ -584,13 +585,13 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 23, title: nil, performer: nil, waveform: Data(base64Encoded: waveformBase64)!)]
|
||||
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: 0, attributes: voiceAttributes)
|
||||
|
||||
let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
sampleMessages.append(message6)
|
||||
|
||||
let message7 = Message(stableId: 7, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 7), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66006, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: message5.id, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message7 = Message(stableId: 7, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 7), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66006, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: message5.id, threadMessageId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
sampleMessages.append(message7)
|
||||
|
||||
let message8 = Message(stableId: 8, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 8), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66007, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message8 = Message(stableId: 8, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 8), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66007, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
sampleMessages.append(message8)
|
||||
|
||||
items = sampleMessages.reversed().map { message in
|
||||
|
@ -156,10 +156,10 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
|
||||
let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3)
|
||||
if let (author, text) = messageItem.reply {
|
||||
peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: author, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
|
||||
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
|
||||
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:])
|
||||
}
|
||||
|
||||
let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, isCentered: false))
|
||||
}
|
||||
|
||||
|
@ -1102,10 +1102,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
|
||||
let theme = self.presentationData.theme.withUpdated(preview: true)
|
||||
|
||||
let message1 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: bottomMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message1 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: bottomMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, isCentered: false))
|
||||
|
||||
let message2 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: topMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
let message2 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: topMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.nativeNode, availableReactions: nil, isCentered: false))
|
||||
|
||||
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
|
||||
|
@ -583,9 +583,9 @@ public final class ShareController: ViewController {
|
||||
for peerId in peerIds {
|
||||
var messages: [EnqueueMessage] = []
|
||||
if !text.isEmpty {
|
||||
messages.append(.message(text: url + "\n\n" + text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages.append(.message(text: url + "\n\n" + text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
} else {
|
||||
messages.append(.message(text: url, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages.append(.message(text: url, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
}
|
||||
messages = transformMessages(messages, showNames: showNames, silently: silently)
|
||||
shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages))
|
||||
@ -594,9 +594,9 @@ public final class ShareController: ViewController {
|
||||
for peerId in peerIds {
|
||||
var messages: [EnqueueMessage] = []
|
||||
if !text.isEmpty {
|
||||
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
}
|
||||
messages.append(.message(text: string, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages.append(.message(text: string, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages = transformMessages(messages, showNames: showNames, silently: silently)
|
||||
shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages))
|
||||
}
|
||||
@ -604,19 +604,19 @@ public final class ShareController: ViewController {
|
||||
for peerId in peerIds {
|
||||
var messages: [EnqueueMessage] = []
|
||||
if !text.isEmpty {
|
||||
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
}
|
||||
let attributedText = NSMutableAttributedString(string: string, attributes: [ChatTextInputAttributes.italic: true as NSNumber])
|
||||
attributedText.append(NSAttributedString(string: "\n\n\(url)"))
|
||||
let entities = generateChatInputTextEntities(attributedText)
|
||||
messages.append(.message(text: attributedText.string, attributes: [TextEntitiesMessageAttribute(entities: entities)], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages.append(.message(text: attributedText.string, attributes: [TextEntitiesMessageAttribute(entities: entities)], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages = transformMessages(messages, showNames: showNames, silently: silently)
|
||||
shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages))
|
||||
}
|
||||
case let .image(representations):
|
||||
for peerId in peerIds {
|
||||
var messages: [EnqueueMessage] = []
|
||||
messages.append(.message(text: text, attributes: [], mediaReference: .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: Int64.random(in: Int64.min ... Int64.max)), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: Int64.random(in: Int64.min ... Int64.max)), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages = transformMessages(messages, showNames: showNames, silently: silently)
|
||||
shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages))
|
||||
}
|
||||
@ -629,9 +629,9 @@ public final class ShareController: ViewController {
|
||||
for peerId in peerIds {
|
||||
var messages: [EnqueueMessage] = []
|
||||
if !text.isEmpty && !sendTextAsCaption {
|
||||
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
}
|
||||
messages.append(.message(text: sendTextAsCaption ? text : "", attributes: [], mediaReference: mediaReference, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages.append(.message(text: sendTextAsCaption ? text : "", attributes: [], inlineStickers: [:], mediaReference: mediaReference, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages = transformMessages(messages, showNames: showNames, silently: silently)
|
||||
shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages))
|
||||
}
|
||||
@ -639,9 +639,9 @@ public final class ShareController: ViewController {
|
||||
for peerId in peerIds {
|
||||
var messages: [EnqueueMessage] = []
|
||||
if !text.isEmpty {
|
||||
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
}
|
||||
messages.append(.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages = transformMessages(messages, showNames: showNames, silently: silently)
|
||||
shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages))
|
||||
}
|
||||
@ -649,7 +649,7 @@ public final class ShareController: ViewController {
|
||||
for peerId in peerIds {
|
||||
var messagesToEnqueue: [EnqueueMessage] = []
|
||||
if !text.isEmpty {
|
||||
messagesToEnqueue.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messagesToEnqueue.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
}
|
||||
for message in messages {
|
||||
messagesToEnqueue.append(.forward(source: message.id, grouping: .auto, attributes: [], correlationId: nil))
|
||||
|
@ -459,11 +459,11 @@ public func sentShareItems(account: Account, to peerIds: [PeerId], items: [Prepa
|
||||
for item in items {
|
||||
switch item {
|
||||
case let .text(text):
|
||||
messages.append(.message(text: text, attributes: attributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages.append(.message(text: text, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
case let .media(media):
|
||||
switch media {
|
||||
case let .media(reference):
|
||||
let message: EnqueueMessage = .message(text: "", attributes: attributes, mediaReference: reference, replyToMessageId: nil, localGroupingKey: groupingKey, correlationId: nil)
|
||||
let message: EnqueueMessage = .message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: reference, replyToMessageId: nil, localGroupingKey: groupingKey, correlationId: nil)
|
||||
messages.append(message)
|
||||
mediaMessages.append(message)
|
||||
|
||||
|
@ -242,7 +242,7 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode {
|
||||
|
||||
let presentationData = item.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let contentKind = messageContentKind(contentSettings: item.context.currentContentSettings.with { $0 }, message: EngineMessage(item.message), strings: item.presentationData.strings, nameDisplayOrder: .firstLast, dateTimeFormat: presentationData.dateTimeFormat, accountPeerId: item.context.account.peerId)
|
||||
var text = !item.message.text.isEmpty ? item.message.text : stringForMediaKind(contentKind, strings: item.presentationData.strings).0
|
||||
var text = !item.message.text.isEmpty ? item.message.text : stringForMediaKind(contentKind, strings: item.presentationData.strings).0.string
|
||||
text = foldLineBreaks(text)
|
||||
|
||||
var contentImageMedia: Media?
|
||||
|
@ -399,7 +399,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
|
||||
if self.currentItems.isEmpty && !updatedItems.isEmpty {
|
||||
let entities = generateTextEntities(info.title, enabledTypes: [.mention])
|
||||
let font = Font.medium(20.0)
|
||||
self.contentTitleNode.attributedText = stringWithAppliedEntities(info.title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: font, linkFont: font, boldFont: font, italicFont: font, boldItalicFont: font, fixedFont: font, blockQuoteFont: font)
|
||||
self.contentTitleNode.attributedText = stringWithAppliedEntities(info.title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: font, linkFont: font, boldFont: font, italicFont: font, boldItalicFont: font, fixedFont: font, blockQuoteFont: font, message: nil)
|
||||
animateIn = true
|
||||
}
|
||||
transaction = StickerPackPreviewGridTransaction(previousList: self.currentItems, list: updatedItems, account: self.context.account, interaction: self.interaction, theme: self.presentationData.theme)
|
||||
|
@ -467,7 +467,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
let titleFont = Font.semibold(17.0)
|
||||
let title = self.titleNode.attributedText?.string ?? ""
|
||||
let entities = generateTextEntities(title, enabledTypes: [.mention])
|
||||
self.titleNode.attributedText = stringWithAppliedEntities(title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleFont, italicFont: titleFont, boldItalicFont: titleFont, fixedFont: titleFont, blockQuoteFont: titleFont)
|
||||
self.titleNode.attributedText = stringWithAppliedEntities(title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleFont, italicFont: titleFont, boldItalicFont: titleFont, fixedFont: titleFont, blockQuoteFont: titleFont, message: nil)
|
||||
|
||||
if let (layout, _, _, _) = self.validLayout {
|
||||
let _ = self.titleNode.updateLayout(CGSize(width: layout.size.width - max(12.0, self.cancelButtonNode.frame.width) * 2.0 - 40.0, height: .greatestFiniteMagnitude))
|
||||
@ -649,7 +649,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
|
||||
let titleFont = Font.semibold(17.0)
|
||||
let entities = generateTextEntities(info.title, enabledTypes: [.mention])
|
||||
self.titleNode.attributedText = stringWithAppliedEntities(info.title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleFont, italicFont: titleFont, boldItalicFont: titleFont, fixedFont: titleFont, blockQuoteFont: titleFont)
|
||||
self.titleNode.attributedText = stringWithAppliedEntities(info.title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleFont, italicFont: titleFont, boldItalicFont: titleFont, fixedFont: titleFont, blockQuoteFont: titleFont, message: nil)
|
||||
|
||||
updateLayout = true
|
||||
|
||||
|
@ -182,6 +182,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[922273905] = { return Api.Document.parse_documentEmpty($0) }
|
||||
dict[297109817] = { return Api.DocumentAttribute.parse_documentAttributeAnimated($0) }
|
||||
dict[-1739392570] = { return Api.DocumentAttribute.parse_documentAttributeAudio($0) }
|
||||
dict[-48981863] = { return Api.DocumentAttribute.parse_documentAttributeCustomEmoji($0) }
|
||||
dict[358154344] = { return Api.DocumentAttribute.parse_documentAttributeFilename($0) }
|
||||
dict[-1744710921] = { return Api.DocumentAttribute.parse_documentAttributeHasStickers($0) }
|
||||
dict[1815593308] = { return Api.DocumentAttribute.parse_documentAttributeImageSize($0) }
|
||||
@ -436,7 +437,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1827637959] = { return Api.MessageEntity.parse_messageEntityBotCommand($0) }
|
||||
dict[1280209983] = { return Api.MessageEntity.parse_messageEntityCashtag($0) }
|
||||
dict[681706865] = { return Api.MessageEntity.parse_messageEntityCode($0) }
|
||||
dict[-727707947] = { return Api.MessageEntity.parse_messageEntityCustomEmoji($0) }
|
||||
dict[-925956616] = { return Api.MessageEntity.parse_messageEntityCustomEmoji($0) }
|
||||
dict[1692693954] = { return Api.MessageEntity.parse_messageEntityEmail($0) }
|
||||
dict[1868782349] = { return Api.MessageEntity.parse_messageEntityHashtag($0) }
|
||||
dict[-2106619040] = { return Api.MessageEntity.parse_messageEntityItalic($0) }
|
||||
|
@ -7,7 +7,7 @@ public extension Api {
|
||||
case messageEntityBotCommand(offset: Int32, length: Int32)
|
||||
case messageEntityCashtag(offset: Int32, length: Int32)
|
||||
case messageEntityCode(offset: Int32, length: Int32)
|
||||
case messageEntityCustomEmoji(offset: Int32, length: Int32, stickerset: Api.InputStickerSet, documentId: Int64)
|
||||
case messageEntityCustomEmoji(offset: Int32, length: Int32, documentId: Int64)
|
||||
case messageEntityEmail(offset: Int32, length: Int32)
|
||||
case messageEntityHashtag(offset: Int32, length: Int32)
|
||||
case messageEntityItalic(offset: Int32, length: Int32)
|
||||
@ -74,13 +74,12 @@ public extension Api {
|
||||
serializeInt32(offset, buffer: buffer, boxed: false)
|
||||
serializeInt32(length, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .messageEntityCustomEmoji(let offset, let length, let stickerset, let documentId):
|
||||
case .messageEntityCustomEmoji(let offset, let length, let documentId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-727707947)
|
||||
buffer.appendInt32(-925956616)
|
||||
}
|
||||
serializeInt32(offset, buffer: buffer, boxed: false)
|
||||
serializeInt32(length, buffer: buffer, boxed: false)
|
||||
stickerset.serialize(buffer, true)
|
||||
serializeInt64(documentId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .messageEntityEmail(let offset, let length):
|
||||
@ -196,8 +195,8 @@ public extension Api {
|
||||
return ("messageEntityCashtag", [("offset", String(describing: offset)), ("length", String(describing: length))])
|
||||
case .messageEntityCode(let offset, let length):
|
||||
return ("messageEntityCode", [("offset", String(describing: offset)), ("length", String(describing: length))])
|
||||
case .messageEntityCustomEmoji(let offset, let length, let stickerset, let documentId):
|
||||
return ("messageEntityCustomEmoji", [("offset", String(describing: offset)), ("length", String(describing: length)), ("stickerset", String(describing: stickerset)), ("documentId", String(describing: documentId))])
|
||||
case .messageEntityCustomEmoji(let offset, let length, let documentId):
|
||||
return ("messageEntityCustomEmoji", [("offset", String(describing: offset)), ("length", String(describing: length)), ("documentId", String(describing: documentId))])
|
||||
case .messageEntityEmail(let offset, let length):
|
||||
return ("messageEntityEmail", [("offset", String(describing: offset)), ("length", String(describing: length))])
|
||||
case .messageEntityHashtag(let offset, let length):
|
||||
@ -335,18 +334,13 @@ public extension Api {
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Api.InputStickerSet?
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.InputStickerSet
|
||||
}
|
||||
var _4: Int64?
|
||||
_4 = reader.readInt64()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.MessageEntity.messageEntityCustomEmoji(offset: _1!, length: _2!, stickerset: _3!, documentId: _4!)
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.MessageEntity.messageEntityCustomEmoji(offset: _1!, length: _2!, documentId: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -3965,6 +3965,25 @@ public extension Api.functions.messages {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func getCustomEmojiDocuments(documentId: [Int64]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.Document]>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-643100844)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(documentId.count))
|
||||
for item in documentId {
|
||||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
return (FunctionDescription(name: "messages.getCustomEmojiDocuments", parameters: [("documentId", String(describing: documentId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.Document]? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: [Api.Document]?
|
||||
if let _ = reader.readInt32() {
|
||||
result = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func getDhConfig(version: Int32, randomLength: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.DhConfig>) {
|
||||
let buffer = Buffer()
|
||||
@ -4115,6 +4134,21 @@ public extension Api.functions.messages {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func getEmojiStickers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.AllStickers>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-67329649)
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "messages.getEmojiStickers", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.AllStickers? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.AllStickers?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.AllStickers
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func getEmojiURL(langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.EmojiURL>) {
|
||||
let buffer = Buffer()
|
||||
@ -6245,12 +6279,12 @@ public extension Api.functions.payments {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.payments {
|
||||
static func assignPlayMarketTransaction(purchaseToken: String, purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
static func assignPlayMarketTransaction(receipt: Api.DataJSON, purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-179907586)
|
||||
serializeString(purchaseToken, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(-537046829)
|
||||
receipt.serialize(buffer, true)
|
||||
purpose.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "payments.assignPlayMarketTransaction", parameters: [("purchaseToken", String(describing: purchaseToken)), ("purpose", String(describing: purpose))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
return (FunctionDescription(name: "payments.assignPlayMarketTransaction", parameters: [("receipt", String(describing: receipt)), ("purpose", String(describing: purpose))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
|
@ -1202,6 +1202,7 @@ public extension Api {
|
||||
enum DocumentAttribute: TypeConstructorDescription {
|
||||
case documentAttributeAnimated
|
||||
case documentAttributeAudio(flags: Int32, duration: Int32, title: String?, performer: String?, waveform: Buffer?)
|
||||
case documentAttributeCustomEmoji(flags: Int32, alt: String, stickerset: Api.InputStickerSet)
|
||||
case documentAttributeFilename(fileName: String)
|
||||
case documentAttributeHasStickers
|
||||
case documentAttributeImageSize(w: Int32, h: Int32)
|
||||
@ -1226,6 +1227,14 @@ public extension Api {
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(performer!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeBytes(waveform!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .documentAttributeCustomEmoji(let flags, let alt, let stickerset):
|
||||
if boxed {
|
||||
buffer.appendInt32(-48981863)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(alt, buffer: buffer, boxed: false)
|
||||
stickerset.serialize(buffer, true)
|
||||
break
|
||||
case .documentAttributeFilename(let fileName):
|
||||
if boxed {
|
||||
buffer.appendInt32(358154344)
|
||||
@ -1272,6 +1281,8 @@ public extension Api {
|
||||
return ("documentAttributeAnimated", [])
|
||||
case .documentAttributeAudio(let flags, let duration, let title, let performer, let waveform):
|
||||
return ("documentAttributeAudio", [("flags", String(describing: flags)), ("duration", String(describing: duration)), ("title", String(describing: title)), ("performer", String(describing: performer)), ("waveform", String(describing: waveform))])
|
||||
case .documentAttributeCustomEmoji(let flags, let alt, let stickerset):
|
||||
return ("documentAttributeCustomEmoji", [("flags", String(describing: flags)), ("alt", String(describing: alt)), ("stickerset", String(describing: stickerset))])
|
||||
case .documentAttributeFilename(let fileName):
|
||||
return ("documentAttributeFilename", [("fileName", String(describing: fileName))])
|
||||
case .documentAttributeHasStickers:
|
||||
@ -1311,6 +1322,25 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_documentAttributeCustomEmoji(_ reader: BufferReader) -> DocumentAttribute? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: Api.InputStickerSet?
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.InputStickerSet
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.DocumentAttribute.documentAttributeCustomEmoji(flags: _1!, alt: _2!, stickerset: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_documentAttributeFilename(_ reader: BufferReader) -> DocumentAttribute? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
@ -1386,85 +1416,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum DraftMessage: TypeConstructorDescription {
|
||||
case draftMessage(flags: Int32, replyToMsgId: Int32?, message: String, entities: [Api.MessageEntity]?, date: Int32)
|
||||
case draftMessageEmpty(flags: Int32, date: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .draftMessage(let flags, let replyToMsgId, let message, let entities, let date):
|
||||
if boxed {
|
||||
buffer.appendInt32(-40996577)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(replyToMsgId!, buffer: buffer, boxed: false)}
|
||||
serializeString(message, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(entities!.count))
|
||||
for item in entities! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .draftMessageEmpty(let flags, let date):
|
||||
if boxed {
|
||||
buffer.appendInt32(453805082)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(date!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .draftMessage(let flags, let replyToMsgId, let message, let entities, let date):
|
||||
return ("draftMessage", [("flags", String(describing: flags)), ("replyToMsgId", String(describing: replyToMsgId)), ("message", String(describing: message)), ("entities", String(describing: entities)), ("date", String(describing: date))])
|
||||
case .draftMessageEmpty(let flags, let date):
|
||||
return ("draftMessageEmpty", [("flags", String(describing: flags)), ("date", String(describing: date))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_draftMessage(_ reader: BufferReader) -> DraftMessage? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() }
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: [Api.MessageEntity]?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
|
||||
} }
|
||||
var _5: Int32?
|
||||
_5 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.DraftMessage.draftMessage(flags: _1!, replyToMsgId: _2, message: _3!, entities: _4, date: _5!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_draftMessageEmpty(_ reader: BufferReader) -> DraftMessage? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.DraftMessage.draftMessageEmpty(flags: _1!, date: _2)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,85 @@
|
||||
public extension Api {
|
||||
enum DraftMessage: TypeConstructorDescription {
|
||||
case draftMessage(flags: Int32, replyToMsgId: Int32?, message: String, entities: [Api.MessageEntity]?, date: Int32)
|
||||
case draftMessageEmpty(flags: Int32, date: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .draftMessage(let flags, let replyToMsgId, let message, let entities, let date):
|
||||
if boxed {
|
||||
buffer.appendInt32(-40996577)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(replyToMsgId!, buffer: buffer, boxed: false)}
|
||||
serializeString(message, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(entities!.count))
|
||||
for item in entities! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .draftMessageEmpty(let flags, let date):
|
||||
if boxed {
|
||||
buffer.appendInt32(453805082)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(date!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .draftMessage(let flags, let replyToMsgId, let message, let entities, let date):
|
||||
return ("draftMessage", [("flags", String(describing: flags)), ("replyToMsgId", String(describing: replyToMsgId)), ("message", String(describing: message)), ("entities", String(describing: entities)), ("date", String(describing: date))])
|
||||
case .draftMessageEmpty(let flags, let date):
|
||||
return ("draftMessageEmpty", [("flags", String(describing: flags)), ("date", String(describing: date))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_draftMessage(_ reader: BufferReader) -> DraftMessage? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() }
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: [Api.MessageEntity]?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
|
||||
} }
|
||||
var _5: Int32?
|
||||
_5 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.DraftMessage.draftMessage(flags: _1!, replyToMsgId: _2, message: _3!, entities: _4, date: _5!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_draftMessageEmpty(_ reader: BufferReader) -> DraftMessage? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.DraftMessage.draftMessageEmpty(flags: _1!, date: _2)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum EmojiKeyword: TypeConstructorDescription {
|
||||
case emojiKeyword(keyword: String, emoticons: [String])
|
||||
@ -1298,87 +1380,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum HighScore: TypeConstructorDescription {
|
||||
case highScore(pos: Int32, userId: Int64, score: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .highScore(let pos, let userId, let score):
|
||||
if boxed {
|
||||
buffer.appendInt32(1940093419)
|
||||
}
|
||||
serializeInt32(pos, buffer: buffer, boxed: false)
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
serializeInt32(score, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .highScore(let pos, let userId, let score):
|
||||
return ("highScore", [("pos", String(describing: pos)), ("userId", String(describing: userId)), ("score", String(describing: score))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_highScore(_ reader: BufferReader) -> HighScore? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.HighScore.highScore(pos: _1!, userId: _2!, score: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum ImportedContact: TypeConstructorDescription {
|
||||
case importedContact(userId: Int64, clientId: Int64)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .importedContact(let userId, let clientId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1052885936)
|
||||
}
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
serializeInt64(clientId, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .importedContact(let userId, let clientId):
|
||||
return ("importedContact", [("userId", String(describing: userId)), ("clientId", String(describing: clientId))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_importedContact(_ reader: BufferReader) -> ImportedContact? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.ImportedContact.importedContact(userId: _1!, clientId: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,87 @@
|
||||
public extension Api {
|
||||
enum HighScore: TypeConstructorDescription {
|
||||
case highScore(pos: Int32, userId: Int64, score: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .highScore(let pos, let userId, let score):
|
||||
if boxed {
|
||||
buffer.appendInt32(1940093419)
|
||||
}
|
||||
serializeInt32(pos, buffer: buffer, boxed: false)
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
serializeInt32(score, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .highScore(let pos, let userId, let score):
|
||||
return ("highScore", [("pos", String(describing: pos)), ("userId", String(describing: userId)), ("score", String(describing: score))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_highScore(_ reader: BufferReader) -> HighScore? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.HighScore.highScore(pos: _1!, userId: _2!, score: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum ImportedContact: TypeConstructorDescription {
|
||||
case importedContact(userId: Int64, clientId: Int64)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .importedContact(let userId, let clientId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1052885936)
|
||||
}
|
||||
serializeInt64(userId, buffer: buffer, boxed: false)
|
||||
serializeInt64(clientId, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .importedContact(let userId, let clientId):
|
||||
return ("importedContact", [("userId", String(describing: userId)), ("clientId", String(describing: clientId))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_importedContact(_ reader: BufferReader) -> ImportedContact? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.ImportedContact.importedContact(userId: _1!, clientId: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InlineBotSwitchPM: TypeConstructorDescription {
|
||||
case inlineBotSwitchPM(text: String, startParam: String)
|
||||
@ -1060,61 +1144,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputDialogPeer: TypeConstructorDescription {
|
||||
case inputDialogPeer(peer: Api.InputPeer)
|
||||
case inputDialogPeerFolder(folderId: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputDialogPeer(let peer):
|
||||
if boxed {
|
||||
buffer.appendInt32(-55902537)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
break
|
||||
case .inputDialogPeerFolder(let folderId):
|
||||
if boxed {
|
||||
buffer.appendInt32(1684014375)
|
||||
}
|
||||
serializeInt32(folderId, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputDialogPeer(let peer):
|
||||
return ("inputDialogPeer", [("peer", String(describing: peer))])
|
||||
case .inputDialogPeerFolder(let folderId):
|
||||
return ("inputDialogPeerFolder", [("folderId", String(describing: folderId))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputDialogPeer(_ reader: BufferReader) -> InputDialogPeer? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputDialogPeer.inputDialogPeer(peer: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputDialogPeerFolder(_ reader: BufferReader) -> InputDialogPeer? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputDialogPeer.inputDialogPeerFolder(folderId: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,61 @@
|
||||
public extension Api {
|
||||
enum InputDialogPeer: TypeConstructorDescription {
|
||||
case inputDialogPeer(peer: Api.InputPeer)
|
||||
case inputDialogPeerFolder(folderId: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputDialogPeer(let peer):
|
||||
if boxed {
|
||||
buffer.appendInt32(-55902537)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
break
|
||||
case .inputDialogPeerFolder(let folderId):
|
||||
if boxed {
|
||||
buffer.appendInt32(1684014375)
|
||||
}
|
||||
serializeInt32(folderId, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputDialogPeer(let peer):
|
||||
return ("inputDialogPeer", [("peer", String(describing: peer))])
|
||||
case .inputDialogPeerFolder(let folderId):
|
||||
return ("inputDialogPeerFolder", [("folderId", String(describing: folderId))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputDialogPeer(_ reader: BufferReader) -> InputDialogPeer? {
|
||||
var _1: Api.InputPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.InputPeer
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputDialogPeer.inputDialogPeer(peer: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputDialogPeerFolder(_ reader: BufferReader) -> InputDialogPeer? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputDialogPeer.inputDialogPeerFolder(folderId: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputDocument: TypeConstructorDescription {
|
||||
case inputDocument(id: Int64, accessHash: Int64, fileReference: Buffer)
|
||||
|
@ -292,7 +292,7 @@ func rateCallAndSendLogs(engine: TelegramEngine, callId: CallId, starsCount: Int
|
||||
let name = "\(callId.id)_\(callId.accessHash).log.json"
|
||||
let path = callLogsPath(account: engine.account) + "/" + name
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
|
||||
let message = EnqueueMessage.message(text: comment, attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
let message = EnqueueMessage.message(text: comment, attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
return rate
|
||||
|> then(enqueueMessages(account: engine.account, peerId: peerId, messages: [message])
|
||||
|> mapToSignal({ _ -> Signal<Void, NoError> in
|
||||
@ -300,7 +300,7 @@ func rateCallAndSendLogs(engine: TelegramEngine, callId: CallId, starsCount: Int
|
||||
}))
|
||||
} else if !comment.isEmpty {
|
||||
return rate
|
||||
|> then(enqueueMessages(account: engine.account, peerId: peerId, messages: [.message(text: comment, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)])
|
||||
|> then(enqueueMessages(account: engine.account, peerId: peerId, messages: [.message(text: comment, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)])
|
||||
|> mapToSignal({ _ -> Signal<Void, NoError> in
|
||||
return .single(Void())
|
||||
}))
|
||||
|
@ -1264,7 +1264,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
||||
dismissController?()
|
||||
|
||||
if let strongSelf = self {
|
||||
let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: [.message(text: listenerLink, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)])
|
||||
let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: [.message(text: listenerLink, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)])
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.presentUndoOverlay(content: .forward(savedMessages: false, text: strongSelf.presentationData.strings.UserInfo_LinkForwardTooltip_Chat_One(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string), action: { _ in return true })
|
||||
|
@ -374,10 +374,8 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes
|
||||
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BankCard))
|
||||
case let .messageEntitySpoiler(offset, length):
|
||||
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Spoiler))
|
||||
case let .messageEntityCustomEmoji(offset, length, stickerset, documentId):
|
||||
if let stickerPack = StickerPackReference(apiInputSet: stickerset) {
|
||||
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .CustomEmoji(stickerPack: stickerPack, fileId: documentId)))
|
||||
}
|
||||
case let .messageEntityCustomEmoji(offset, length, documentId):
|
||||
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .CustomEmoji(stickerPack: nil, fileId: documentId)))
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
@ -102,6 +102,9 @@ func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.DocumentAtt
|
||||
let isVoice = (flags & (1 << 10)) != 0
|
||||
let waveformBuffer: Data? = waveform?.makeData()
|
||||
result.append(.Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: performer, waveform: waveformBuffer))
|
||||
case let .documentAttributeCustomEmoji(flags, alt, stickerSet):
|
||||
let isFree = (flags & (1 << 0)) != 0
|
||||
result.append(.CustomEmoji(isPremium: !isFree, alt: alt, packReference: StickerPackReference(apiInputSet: stickerSet)))
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
@ -48,8 +48,8 @@ func apiEntitiesFromMessageTextEntities(_ entities: [MessageTextEntity], associa
|
||||
apiEntities.append(.messageEntityBankCard(offset: offset, length: length))
|
||||
case .Spoiler:
|
||||
apiEntities.append(.messageEntitySpoiler(offset: offset, length: length))
|
||||
case let .CustomEmoji(stickerPack, fileId):
|
||||
apiEntities.append(.messageEntityCustomEmoji(offset: offset, length: length, stickerset: stickerPack.apiInputStickerSet, documentId: fileId))
|
||||
case let .CustomEmoji(_, fileId):
|
||||
apiEntities.append(.messageEntityCustomEmoji(offset: offset, length: length, documentId: fileId))
|
||||
case .Custom:
|
||||
break
|
||||
}
|
||||
|
@ -9,47 +9,47 @@ public enum EnqueueMessageGrouping {
|
||||
}
|
||||
|
||||
public enum EnqueueMessage {
|
||||
case message(text: String, attributes: [MessageAttribute], mediaReference: AnyMediaReference?, replyToMessageId: MessageId?, localGroupingKey: Int64?, correlationId: Int64?)
|
||||
case message(text: String, attributes: [MessageAttribute], inlineStickers: [MediaId: Media], mediaReference: AnyMediaReference?, replyToMessageId: MessageId?, localGroupingKey: Int64?, correlationId: Int64?)
|
||||
case forward(source: MessageId, grouping: EnqueueMessageGrouping, attributes: [MessageAttribute], correlationId: Int64?)
|
||||
|
||||
public func withUpdatedReplyToMessageId(_ replyToMessageId: MessageId?) -> EnqueueMessage {
|
||||
switch self {
|
||||
case let .message(text, attributes, mediaReference, _, localGroupingKey, correlationId):
|
||||
return .message(text: text, attributes: attributes, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey, correlationId: correlationId)
|
||||
case .forward:
|
||||
return self
|
||||
case let .message(text, attributes, inlineStickers, mediaReference, _, localGroupingKey, correlationId):
|
||||
return .message(text: text, attributes: attributes, inlineStickers: inlineStickers, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey, correlationId: correlationId)
|
||||
case .forward:
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
public func withUpdatedAttributes(_ f: ([MessageAttribute]) -> [MessageAttribute]) -> EnqueueMessage {
|
||||
switch self {
|
||||
case let .message(text, attributes, mediaReference, replyToMessageId, localGroupingKey, correlationId):
|
||||
return .message(text: text, attributes: f(attributes), mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey, correlationId: correlationId)
|
||||
case let .forward(source, grouping, attributes, correlationId):
|
||||
return .forward(source: source, grouping: grouping, attributes: f(attributes), correlationId: correlationId)
|
||||
case let .message(text, attributes, inlineStickers, mediaReference, replyToMessageId, localGroupingKey, correlationId):
|
||||
return .message(text: text, attributes: f(attributes), inlineStickers: inlineStickers, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey, correlationId: correlationId)
|
||||
case let .forward(source, grouping, attributes, correlationId):
|
||||
return .forward(source: source, grouping: grouping, attributes: f(attributes), correlationId: correlationId)
|
||||
}
|
||||
}
|
||||
|
||||
public func withUpdatedGroupingKey(_ f: (Int64?) -> Int64?) -> EnqueueMessage {
|
||||
switch self {
|
||||
case let .message(text, attributes, mediaReference, replyToMessageId, localGroupingKey, correlationId):
|
||||
return .message(text: text, attributes: attributes, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: f(localGroupingKey), correlationId: correlationId)
|
||||
case .forward:
|
||||
return self
|
||||
case let .message(text, attributes, inlineStickers, mediaReference, replyToMessageId, localGroupingKey, correlationId):
|
||||
return .message(text: text, attributes: attributes, inlineStickers: inlineStickers, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: f(localGroupingKey), correlationId: correlationId)
|
||||
case .forward:
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
public func withUpdatedCorrelationId(_ value: Int64?) -> EnqueueMessage {
|
||||
switch self {
|
||||
case let .message(text, attributes, mediaReference, replyToMessageId, localGroupingKey, _):
|
||||
return .message(text: text, attributes: attributes, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey, correlationId: value)
|
||||
case let .forward(source, grouping, attributes, _):
|
||||
return .forward(source: source, grouping: grouping, attributes: attributes, correlationId: value)
|
||||
case let .message(text, attributes, inlineStickers, mediaReference, replyToMessageId, localGroupingKey, _):
|
||||
return .message(text: text, attributes: attributes, inlineStickers: inlineStickers, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey, correlationId: value)
|
||||
case let .forward(source, grouping, attributes, _):
|
||||
return .forward(source: source, grouping: grouping, attributes: attributes, correlationId: value)
|
||||
}
|
||||
}
|
||||
|
||||
public var groupingKey: Int64? {
|
||||
if case let .message(_, _, _, _, localGroupingKey, _) = self {
|
||||
if case let .message(_, _, _, _, _, localGroupingKey, _) = self {
|
||||
return localGroupingKey
|
||||
} else {
|
||||
return nil
|
||||
@ -60,7 +60,7 @@ public enum EnqueueMessage {
|
||||
private extension EnqueueMessage {
|
||||
var correlationId: Int64? {
|
||||
switch self {
|
||||
case let .message(_, _, _, _, _, correlationId):
|
||||
case let .message(_, _, _, _, _, _, correlationId):
|
||||
return correlationId
|
||||
case let .forward(_, _, _, correlationId):
|
||||
return correlationId
|
||||
@ -179,7 +179,7 @@ private func opportunisticallyTransformOutgoingMedia(network: Network, postbox:
|
||||
var hasMedia = false
|
||||
loop: for message in messages {
|
||||
switch message {
|
||||
case let .message(_, _, mediaReference, _, _, _):
|
||||
case let .message(_, _, _, mediaReference, _, _, _):
|
||||
if mediaReference != nil {
|
||||
hasMedia = true
|
||||
break loop
|
||||
@ -196,14 +196,14 @@ private func opportunisticallyTransformOutgoingMedia(network: Network, postbox:
|
||||
var signals: [Signal<(Bool, EnqueueMessage), NoError>] = []
|
||||
for message in messages {
|
||||
switch message {
|
||||
case let .message(text, attributes, mediaReference, replyToMessageId, localGroupingKey, correlationId):
|
||||
case let .message(text, attributes, inlineStickers, mediaReference, replyToMessageId, localGroupingKey, correlationId):
|
||||
if let mediaReference = mediaReference {
|
||||
signals.append(opportunisticallyTransformMessageWithMedia(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, mediaReference: mediaReference, userInteractive: userInteractive)
|
||||
|> map { result -> (Bool, EnqueueMessage) in
|
||||
if let result = result {
|
||||
return (true, .message(text: text, attributes: attributes, mediaReference: .standalone(media: result.media), replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey, correlationId: correlationId))
|
||||
return (true, .message(text: text, attributes: attributes, inlineStickers: inlineStickers, mediaReference: .standalone(media: result.media), replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey, correlationId: correlationId))
|
||||
} else {
|
||||
return (false, .message(text: text, attributes: attributes, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey, correlationId: correlationId))
|
||||
return (false, .message(text: text, attributes: attributes, inlineStickers: inlineStickers, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey, correlationId: correlationId))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@ -275,7 +275,7 @@ public func resendMessages(account: Account, messageIds: [MessageId]) -> Signal<
|
||||
}
|
||||
}
|
||||
|
||||
messages.append(.message(text: message.text, attributes: filteredAttributes, mediaReference: message.media.first.flatMap(AnyMediaReference.standalone), replyToMessageId: replyToMessageId, localGroupingKey: message.groupingKey, correlationId: nil))
|
||||
messages.append(.message(text: message.text, attributes: filteredAttributes, inlineStickers: [:], mediaReference: message.media.first.flatMap(AnyMediaReference.standalone), replyToMessageId: replyToMessageId, localGroupingKey: message.groupingKey, correlationId: nil))
|
||||
}
|
||||
}
|
||||
let _ = enqueueMessages(transaction: transaction, account: account, peerId: peerId, messages: messages.map { (false, $0) })
|
||||
@ -305,7 +305,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
|
||||
}
|
||||
}
|
||||
switch message {
|
||||
case let .message(_, attributes, _, replyToMessageId, _, _):
|
||||
case let .message(_, attributes, _, _, replyToMessageId, _, _):
|
||||
if let replyToMessageId = replyToMessageId, replyToMessageId.peerId != peerId, let replyMessage = transaction.getMessage(replyToMessageId) {
|
||||
var canBeForwarded = true
|
||||
if replyMessage.id.namespace != Namespaces.Message.Cloud {
|
||||
@ -329,7 +329,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
|
||||
mediaReference = .standalone(media: media)
|
||||
}
|
||||
}
|
||||
updatedMessages.append((transformedMedia, .message(text: sourceMessage.text, attributes: sourceMessage.attributes, mediaReference: mediaReference, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)))
|
||||
updatedMessages.append((transformedMedia, .message(text: sourceMessage.text, attributes: sourceMessage.attributes, inlineStickers: [:], mediaReference: mediaReference, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)))
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
@ -370,7 +370,11 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
|
||||
globallyUniqueIds.append(randomId)
|
||||
|
||||
switch message {
|
||||
case let .message(text, requestedAttributes, mediaReference, replyToMessageId, localGroupingKey, _):
|
||||
case let .message(text, requestedAttributes, inlineStickers, mediaReference, replyToMessageId, localGroupingKey, _):
|
||||
for (_, file) in inlineStickers {
|
||||
transaction.storeMediaIfNotPresent(media: file)
|
||||
}
|
||||
|
||||
var peerAutoremoveTimeout: Int32?
|
||||
if let peer = peer as? TelegramSecretChat {
|
||||
var isAction = false
|
||||
|
@ -544,6 +544,8 @@ func inputDocumentAttributesFromFileAttributes(_ fileAttributes: [TelegramMediaF
|
||||
break
|
||||
case .NoPremium:
|
||||
break
|
||||
case .CustomEmoji:
|
||||
break
|
||||
}
|
||||
}
|
||||
return attributes
|
||||
|
@ -16,7 +16,7 @@ func _internal_setSecretChatMessageAutoremoveTimeoutInteractively(account: Accou
|
||||
transaction.setPeerChatState(peerId, state: updatedState)
|
||||
}
|
||||
|
||||
let _ = enqueueMessages(transaction: transaction, account: account, peerId: peerId, messages: [(true, .message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaAction(action: TelegramMediaActionType.messageAutoremoveTimeoutUpdated(timeout == nil ? 0 : timeout!))), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))])
|
||||
let _ = enqueueMessages(transaction: transaction, account: account, peerId: peerId, messages: [(true, .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaAction(action: TelegramMediaActionType.messageAutoremoveTimeoutUpdated(timeout == nil ? 0 : timeout!))), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -31,7 +31,7 @@ func _internal_addSecretChatMessageScreenshot(account: Account, peerId: PeerId)
|
||||
default:
|
||||
break
|
||||
}
|
||||
let _ = enqueueMessages(transaction: transaction, account: account, peerId: peerId, messages: [(true, .message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaAction(action: TelegramMediaActionType.historyScreenshot)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))])
|
||||
let _ = enqueueMessages(transaction: transaction, account: account, peerId: peerId, messages: [(true, .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaAction(action: TelegramMediaActionType.historyScreenshot)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -544,6 +544,8 @@ private func decryptedAttributes46(_ attributes: [TelegramMediaFileAttribute], t
|
||||
break
|
||||
case .NoPremium:
|
||||
break
|
||||
case .CustomEmoji:
|
||||
break
|
||||
}
|
||||
}
|
||||
return result
|
||||
@ -605,6 +607,8 @@ private func decryptedAttributes73(_ attributes: [TelegramMediaFileAttribute], t
|
||||
break
|
||||
case .NoPremium:
|
||||
break
|
||||
case .CustomEmoji:
|
||||
break
|
||||
}
|
||||
}
|
||||
return result
|
||||
@ -666,6 +670,8 @@ private func decryptedAttributes101(_ attributes: [TelegramMediaFileAttribute],
|
||||
break
|
||||
case .NoPremium:
|
||||
break
|
||||
case .CustomEmoji:
|
||||
break
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
@ -11,6 +11,7 @@ private let typeHasLinkedStickers: Int32 = 6
|
||||
private let typeHintFileIsLarge: Int32 = 7
|
||||
private let typeHintIsValidated: Int32 = 8
|
||||
private let typeNoPremium: Int32 = 9
|
||||
private let typeCustomEmoji: Int32 = 10
|
||||
|
||||
public enum StickerPackReference: PostboxCoding, Hashable, Equatable, Codable {
|
||||
case id(id: Int64, accessHash: Int64)
|
||||
@ -188,6 +189,7 @@ public enum TelegramMediaFileAttribute: PostboxCoding {
|
||||
case hintFileIsLarge
|
||||
case hintIsValidated
|
||||
case NoPremium
|
||||
case CustomEmoji(isPremium: Bool, alt: String, packReference: StickerPackReference?)
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
let type: Int32 = decoder.decodeInt32ForKey("t", orElse: 0)
|
||||
@ -217,6 +219,8 @@ public enum TelegramMediaFileAttribute: PostboxCoding {
|
||||
self = .hintIsValidated
|
||||
case typeNoPremium:
|
||||
self = .NoPremium
|
||||
case typeCustomEmoji:
|
||||
self = .CustomEmoji(isPremium: decoder.decodeBoolForKey("ip", orElse: true), alt: decoder.decodeStringForKey("dt", orElse: ""), packReference: decoder.decodeObjectForKey("pr", decoder: { StickerPackReference(decoder: $0) }) as? StickerPackReference)
|
||||
default:
|
||||
preconditionFailure()
|
||||
}
|
||||
@ -273,6 +277,15 @@ public enum TelegramMediaFileAttribute: PostboxCoding {
|
||||
encoder.encodeInt32(typeHintIsValidated, forKey: "t")
|
||||
case .NoPremium:
|
||||
encoder.encodeInt32(typeNoPremium, forKey: "t")
|
||||
case let .CustomEmoji(isPremium, alt, packReference):
|
||||
encoder.encodeInt32(typeCustomEmoji, forKey: "t")
|
||||
encoder.encodeBool(isPremium, forKey: "ip")
|
||||
encoder.encodeString(alt, forKey: "dt")
|
||||
if let packReference = packReference {
|
||||
encoder.encodeObject(packReference, forKey: "pr")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "pr")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ public enum MessageTextEntityType: Equatable {
|
||||
case Underline
|
||||
case BankCard
|
||||
case Spoiler
|
||||
case CustomEmoji(stickerPack: StickerPackReference, fileId: Int64)
|
||||
case CustomEmoji(stickerPack: StickerPackReference?, fileId: Int64)
|
||||
case Custom(type: CustomEntityType)
|
||||
}
|
||||
|
||||
@ -73,11 +73,8 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable {
|
||||
case 17:
|
||||
self.type = .Spoiler
|
||||
case 18:
|
||||
if let stickerPack = decoder.decodeObjectForKey("s", decoder: { StickerPackReference(decoder: $0) }) as? StickerPackReference {
|
||||
self.type = .CustomEmoji(stickerPack: stickerPack, fileId: decoder.decodeInt64ForKey("f", orElse: 0))
|
||||
} else {
|
||||
self.type = .Unknown
|
||||
}
|
||||
let stickerPack = decoder.decodeObjectForKey("s", decoder: { StickerPackReference(decoder: $0) }) as? StickerPackReference
|
||||
self.type = .CustomEmoji(stickerPack: stickerPack, fileId: decoder.decodeInt64ForKey("f", orElse: 0))
|
||||
case Int32.max:
|
||||
self.type = .Custom(type: decoder.decodeInt32ForKey("type", orElse: 0))
|
||||
default:
|
||||
@ -134,7 +131,7 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable {
|
||||
case 17:
|
||||
self.type = .Spoiler
|
||||
case 18:
|
||||
self.type = .CustomEmoji(stickerPack: try container.decode(StickerPackReference.self, forKey: "s"), fileId: try container.decode(Int64.self, forKey: "f"))
|
||||
self.type = .CustomEmoji(stickerPack: try container.decodeIfPresent(StickerPackReference.self, forKey: "s"), fileId: try container.decode(Int64.self, forKey: "f"))
|
||||
case Int32.max:
|
||||
let customType: Int32 = (try? container.decode(Int32.self, forKey: "type")) ?? 0
|
||||
self.type = .Custom(type: customType)
|
||||
@ -187,7 +184,11 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable {
|
||||
encoder.encodeInt32(17, forKey: "_rawValue")
|
||||
case let .CustomEmoji(stickerPack, fileId):
|
||||
encoder.encodeInt32(18, forKey: "_rawValue")
|
||||
encoder.encodeObject(stickerPack, forKey: "s")
|
||||
if let stickerPack = stickerPack {
|
||||
encoder.encodeObject(stickerPack, forKey: "s")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "s")
|
||||
}
|
||||
encoder.encodeInt64(fileId, forKey: "f")
|
||||
case let .Custom(type):
|
||||
encoder.encodeInt32(Int32.max, forKey: "_rawValue")
|
||||
@ -241,7 +242,7 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable {
|
||||
try container.encode(17 as Int32, forKey: "_rawValue")
|
||||
case let .CustomEmoji(stickerPack, fileId):
|
||||
try container.encode(18 as Int32, forKey: "_rawValue")
|
||||
try container.encode(stickerPack, forKey: "s")
|
||||
try container.encodeIfPresent(stickerPack, forKey: "s")
|
||||
try container.encode(fileId, forKey: "f")
|
||||
case let .Custom(type):
|
||||
try container.encode(Int32.max as Int32, forKey: "_rawValue")
|
||||
@ -270,6 +271,23 @@ public class TextEntitiesMessageAttribute: MessageAttribute, Equatable {
|
||||
return result
|
||||
}
|
||||
|
||||
public var associatedMediaIds: [MediaId] {
|
||||
var result: [MediaId] = []
|
||||
for entity in self.entities {
|
||||
switch entity.type {
|
||||
case let .CustomEmoji(_, fileId):
|
||||
result.append(MediaId(namespace: Namespaces.Media.CloudFile, id: fileId))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
if result.isEmpty {
|
||||
return result
|
||||
} else {
|
||||
return Array(Set(result))
|
||||
}
|
||||
}
|
||||
|
||||
public init(entities: [MessageTextEntity]) {
|
||||
self.entities = entities
|
||||
}
|
||||
|
@ -247,7 +247,8 @@ private class AdMessagesHistoryContextImpl {
|
||||
media: self.media,
|
||||
peers: messagePeers,
|
||||
associatedMessages: SimpleDictionary<MessageId, Message>(),
|
||||
associatedMessageIds: []
|
||||
associatedMessageIds: [],
|
||||
associatedMedia: [:]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,16 @@ public final class EngineChatList {
|
||||
case later(than: EngineChatList.Item.Index?)
|
||||
case earlier(than: EngineChatList.Item.Index?)
|
||||
}
|
||||
|
||||
public struct Draft {
|
||||
public var text: String
|
||||
public var entities: [MessageTextEntity]
|
||||
|
||||
public init(text: String, entities: [MessageTextEntity]) {
|
||||
self.text = text
|
||||
self.entities = entities
|
||||
}
|
||||
}
|
||||
|
||||
public final class Item {
|
||||
public typealias Index = ChatListIndex
|
||||
@ -24,7 +34,7 @@ public final class EngineChatList {
|
||||
public let messages: [EngineMessage]
|
||||
public let readCounters: EnginePeerReadCounters?
|
||||
public let isMuted: Bool
|
||||
public let draftText: String?
|
||||
public let draft: Draft?
|
||||
public let renderedPeer: EngineRenderedPeer
|
||||
public let presence: EnginePeer.Presence?
|
||||
public let hasUnseenMentions: Bool
|
||||
@ -37,7 +47,7 @@ public final class EngineChatList {
|
||||
messages: [EngineMessage],
|
||||
readCounters: EnginePeerReadCounters?,
|
||||
isMuted: Bool,
|
||||
draftText: String?,
|
||||
draft: Draft?,
|
||||
renderedPeer: EngineRenderedPeer,
|
||||
presence: EnginePeer.Presence?,
|
||||
hasUnseenMentions: Bool,
|
||||
@ -49,7 +59,7 @@ public final class EngineChatList {
|
||||
self.messages = messages
|
||||
self.readCounters = readCounters
|
||||
self.isMuted = isMuted
|
||||
self.draftText = draftText
|
||||
self.draft = draft
|
||||
self.renderedPeer = renderedPeer
|
||||
self.presence = presence
|
||||
self.hasUnseenMentions = hasUnseenMentions
|
||||
@ -209,11 +219,11 @@ extension EngineChatList.Item {
|
||||
convenience init?(_ entry: ChatListEntry) {
|
||||
switch entry {
|
||||
case let .MessageEntry(index, messages, readState, isRemovedFromTotalUnreadCount, embeddedState, renderedPeer, presence, tagSummaryInfo, hasFailed, isContact):
|
||||
var draftText: String?
|
||||
var draft: EngineChatList.Draft?
|
||||
if let embeddedState = embeddedState, let _ = embeddedState.overrideChatTimestamp {
|
||||
if let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) {
|
||||
if let text = opaqueState.synchronizeableInputState?.text {
|
||||
draftText = text
|
||||
draft = EngineChatList.Draft(text: text, entities: opaqueState.synchronizeableInputState?.entities ?? [])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -239,7 +249,7 @@ extension EngineChatList.Item {
|
||||
messages: messages.map(EngineMessage.init),
|
||||
readCounters: readState.flatMap(EnginePeerReadCounters.init),
|
||||
isMuted: isRemovedFromTotalUnreadCount,
|
||||
draftText: draftText,
|
||||
draft: draft,
|
||||
renderedPeer: EngineRenderedPeer(renderedPeer),
|
||||
presence: presence.flatMap(EnginePeer.Presence.init),
|
||||
hasUnseenMentions: hasUnseenMentions,
|
||||
|
@ -75,6 +75,9 @@ public final class EngineMessage {
|
||||
public var associatedMessageIds: [EngineMessage.Id] {
|
||||
return self.impl.associatedMessageIds
|
||||
}
|
||||
public var associatedMedia: [MediaId: Media] {
|
||||
return self.impl.associatedMedia
|
||||
}
|
||||
|
||||
public var index: MessageIndex {
|
||||
return self.impl.index
|
||||
@ -100,7 +103,8 @@ public final class EngineMessage {
|
||||
media: [EngineMedia],
|
||||
peers: [EnginePeer.Id: EnginePeer],
|
||||
associatedMessages: [EngineMessage.Id: EngineMessage],
|
||||
associatedMessageIds: [EngineMessage.Id]
|
||||
associatedMessageIds: [EngineMessage.Id],
|
||||
associatedMedia: [MediaId: Media]
|
||||
) {
|
||||
var mappedPeers: [PeerId: Peer] = [:]
|
||||
for (id, peer) in peers {
|
||||
@ -132,7 +136,8 @@ public final class EngineMessage {
|
||||
media: media.map { $0._asMedia() },
|
||||
peers: SimpleDictionary(mappedPeers),
|
||||
associatedMessages: SimpleDictionary(mappedAssociatedMessages),
|
||||
associatedMessageIds: associatedMessageIds
|
||||
associatedMessageIds: associatedMessageIds,
|
||||
associatedMedia: associatedMedia
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -41,19 +41,19 @@ func _internal_outgoingMessageWithChatContextResult(to peerId: PeerId, botId: Pe
|
||||
return true
|
||||
}
|
||||
if let media: Media = internalReference.file ?? internalReference.image {
|
||||
return .message(text: caption, attributes: filteredAttributes, mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
return .message(text: caption, attributes: filteredAttributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
} else {
|
||||
return .message(text: caption, attributes: filteredAttributes, mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
return .message(text: caption, attributes: filteredAttributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
}
|
||||
} else {
|
||||
return .message(text: "", attributes: attributes, mediaReference: .standalone(media: TelegramMediaGame(gameId: 0, accessHash: 0, name: "", title: internalReference.title ?? "", description: internalReference.description ?? "", image: internalReference.image, file: internalReference.file)), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
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, localGroupingKey: nil, correlationId: correlationId)
|
||||
}
|
||||
} else if let file = internalReference.file, internalReference.type == "gif" {
|
||||
return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: file), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
} else if let image = internalReference.image {
|
||||
return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: image), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: image), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
} else if let file = internalReference.file {
|
||||
return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: file), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
@ -65,9 +65,9 @@ func _internal_outgoingMessageWithChatContextResult(to peerId: PeerId, botId: Pe
|
||||
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)], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
|
||||
return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: tmpImage), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: tmpImage), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
} else {
|
||||
return .message(text: caption, attributes: attributes, mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
}
|
||||
} else if externalReference.type == "document" || externalReference.type == "gif" || externalReference.type == "audio" || externalReference.type == "voice" {
|
||||
var videoThumbnails: [TelegramMediaFile.VideoThumbnail] = []
|
||||
@ -127,9 +127,9 @@ func _internal_outgoingMessageWithChatContextResult(to peerId: PeerId, botId: Pe
|
||||
}
|
||||
|
||||
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, mediaReference: .standalone(media: file), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
} else {
|
||||
return .message(text: caption, attributes: attributes, mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
return .message(text: caption, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
}
|
||||
}
|
||||
case let .text(text, entities, _, replyMarkup):
|
||||
@ -139,21 +139,21 @@ func _internal_outgoingMessageWithChatContextResult(to peerId: PeerId, botId: Pe
|
||||
if let replyMarkup = replyMarkup {
|
||||
attributes.append(replyMarkup)
|
||||
}
|
||||
return .message(text: text, attributes: attributes, mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
return .message(text: text, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
case let .mapLocation(media, replyMarkup):
|
||||
if let replyMarkup = replyMarkup {
|
||||
attributes.append(replyMarkup)
|
||||
}
|
||||
return .message(text: "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
return .message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
case let .contact(media, replyMarkup):
|
||||
if let replyMarkup = replyMarkup {
|
||||
attributes.append(replyMarkup)
|
||||
}
|
||||
return .message(text: "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
return .message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
case let .invoice(media, replyMarkup):
|
||||
if let replyMarkup = replyMarkup {
|
||||
attributes.append(replyMarkup)
|
||||
}
|
||||
return .message(text: "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
return .message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyToMessageId, localGroupingKey: nil, correlationId: correlationId)
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ func _internal_requestStartBot(account: Account, botPeerId: PeerId, payload: Str
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return enqueueMessages(account: account, peerId: botPeerId, messages: [.message(text: "/start", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]) |> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return enqueueMessages(account: account, peerId: botPeerId, messages: [.message(text: "/start", attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]) |> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
import TelegramApi
|
||||
import SwiftSignalKit
|
||||
|
@ -1,5 +1,6 @@
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramApi
|
||||
|
||||
public extension TelegramEngine {
|
||||
final class Stickers {
|
||||
@ -155,5 +156,36 @@ public extension TelegramEngine {
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
|
||||
public func resolveInlineSticker(fileId: Int64) -> Signal<TelegramMediaFile?, NoError> {
|
||||
return self.account.postbox.transaction { transaction -> TelegramMediaFile? in
|
||||
return transaction.getMedia(MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)) as? TelegramMediaFile
|
||||
}
|
||||
|> mapToSignal { cachedFile -> Signal<TelegramMediaFile?, NoError> in
|
||||
if let cachedFile = cachedFile {
|
||||
return .single(cachedFile)
|
||||
}
|
||||
|
||||
return self.account.network.request(Api.functions.messages.getCustomEmojiDocuments(documentId: [fileId]))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<[Api.Document]?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<TelegramMediaFile?, NoError> in
|
||||
guard let result = result else {
|
||||
return .single(nil)
|
||||
}
|
||||
return self.account.postbox.transaction { transaction -> TelegramMediaFile? in
|
||||
for document in result {
|
||||
if let file = telegramMediaFileFromApiDocument(document) {
|
||||
transaction.storeMediaIfNotPresent(media: file)
|
||||
}
|
||||
}
|
||||
|
||||
return transaction.getMedia(MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)) as? TelegramMediaFile
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ func locallyRenderedMessage(message: StoreMessage, peers: [PeerId: Peer]) -> Mes
|
||||
let second = UInt32(hashValue & 0xffffffff)
|
||||
let stableId = first &+ second
|
||||
|
||||
return Message(stableId: stableId, stableVersion: 0, id: id, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: message.threadId, timestamp: message.timestamp, flags: MessageFlags(message.flags), tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: message.attributes, media: message.media, peers: messagePeers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
|
||||
return Message(stableId: stableId, stableVersion: 0, id: id, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: message.threadId, timestamp: message.timestamp, flags: MessageFlags(message.flags), tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: message.attributes, media: message.media, peers: messagePeers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:])
|
||||
}
|
||||
|
||||
public extension Message {
|
||||
|
@ -1,8 +1,10 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import PlatformRestrictionMatching
|
||||
import TextFormat
|
||||
|
||||
public enum MessageContentKindKey {
|
||||
case text
|
||||
@ -26,7 +28,7 @@ public enum MessageContentKindKey {
|
||||
}
|
||||
|
||||
public enum MessageContentKind: Equatable {
|
||||
case text(String)
|
||||
case text(NSAttributedString)
|
||||
case image
|
||||
case video
|
||||
case videoMessage
|
||||
@ -87,6 +89,40 @@ public enum MessageContentKind: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
public func messageTextWithAttributes(message: EngineMessage) -> NSAttributedString {
|
||||
var attributedText = NSAttributedString(string: message.text)
|
||||
|
||||
var entities: TextEntitiesMessageAttribute?
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? TextEntitiesMessageAttribute {
|
||||
entities = attribute
|
||||
break
|
||||
}
|
||||
}
|
||||
if let entities = entities?.entities {
|
||||
let updatedString = NSMutableAttributedString(attributedString: attributedText)
|
||||
|
||||
for entity in entities.sorted(by: { $0.range.lowerBound > $1.range.lowerBound }) {
|
||||
guard case let .CustomEmoji(stickerPack, fileId) = entity.type else {
|
||||
continue
|
||||
}
|
||||
|
||||
let range = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound)
|
||||
|
||||
let currentDict = updatedString.attributes(at: range.lowerBound, effectiveRange: nil)
|
||||
var updatedAttributes: [NSAttributedString.Key: Any] = currentDict
|
||||
//updatedAttributes[NSAttributedString.Key.foregroundColor] = UIColor.clear.cgColor
|
||||
updatedAttributes[ChatTextInputAttributes.customEmoji] = ChatTextInputTextCustomEmojiAttribute(stickerPack: stickerPack, fileId: fileId, file: message.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] as? TelegramMediaFile)
|
||||
|
||||
let insertString = NSAttributedString(string: updatedString.attributedSubstring(from: range).string, attributes: updatedAttributes)
|
||||
updatedString.replaceCharacters(in: range, with: insertString)
|
||||
}
|
||||
attributedText = updatedString
|
||||
}
|
||||
|
||||
return attributedText
|
||||
}
|
||||
|
||||
public func messageContentKind(contentSettings: ContentSettings, message: EngineMessage, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, accountPeerId: EnginePeer.Id) -> MessageContentKind {
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? RestrictedContentMessageAttribute {
|
||||
@ -101,7 +137,7 @@ public func messageContentKind(contentSettings: ContentSettings, message: Engine
|
||||
return kind
|
||||
}
|
||||
}
|
||||
return .text(message.text)
|
||||
return .text(messageTextWithAttributes(message: message))
|
||||
}
|
||||
|
||||
public func mediaContentKind(_ media: EngineMedia, message: EngineMessage? = nil, strings: PresentationStrings? = nil, nameDisplayOrder: PresentationPersonNameOrder? = nil, dateTimeFormat: PresentationDateTimeFormat? = nil, accountPeerId: EnginePeer.Id? = nil) -> MessageContentKind? {
|
||||
@ -170,7 +206,7 @@ public func mediaContentKind(_ media: EngineMedia, message: EngineMessage? = nil
|
||||
}
|
||||
case .action:
|
||||
if let message = message, let strings = strings, let nameDisplayOrder = nameDisplayOrder, let accountPeerId = accountPeerId {
|
||||
return .text(plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat ?? PresentationDateTimeFormat(timeFormat: .military, dateFormat: .dayFirst, dateSeparator: ".", dateSuffix: "", requiresFullYear: false, decimalSeparator: ".", groupingSeparator: ""), message: message, accountPeerId: accountPeerId, forChatList: false)?.0 ?? "")
|
||||
return .text(NSAttributedString(string: plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat ?? PresentationDateTimeFormat(timeFormat: .military, dateFormat: .dayFirst, dateSeparator: ".", dateSuffix: "", requiresFullYear: false, decimalSeparator: ".", groupingSeparator: ""), message: message, accountPeerId: accountPeerId, forChatList: false)?.0 ?? ""))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
@ -189,59 +225,59 @@ public func mediaContentKind(_ media: EngineMedia, message: EngineMessage? = nil
|
||||
}
|
||||
}
|
||||
|
||||
public func stringForMediaKind(_ kind: MessageContentKind, strings: PresentationStrings) -> (String, Bool) {
|
||||
public func stringForMediaKind(_ kind: MessageContentKind, strings: PresentationStrings) -> (NSAttributedString, Bool) {
|
||||
switch kind {
|
||||
case let .text(text):
|
||||
return (foldLineBreaks(text), false)
|
||||
case .image:
|
||||
return (strings.Message_Photo, true)
|
||||
return (NSAttributedString(string: strings.Message_Photo), true)
|
||||
case .video:
|
||||
return (strings.Message_Video, true)
|
||||
return (NSAttributedString(string: strings.Message_Video), true)
|
||||
case .videoMessage:
|
||||
return (strings.Message_VideoMessage, true)
|
||||
return (NSAttributedString(string: strings.Message_VideoMessage), true)
|
||||
case .audioMessage:
|
||||
return (strings.Message_Audio, true)
|
||||
return (NSAttributedString(string: strings.Message_Audio), true)
|
||||
case let .sticker(text):
|
||||
if text.isEmpty {
|
||||
return (strings.Message_Sticker, true)
|
||||
return (NSAttributedString(string: strings.Message_Sticker), true)
|
||||
} else {
|
||||
return (strings.Message_StickerText(text).string, true)
|
||||
return (NSAttributedString(string: strings.Message_StickerText(text).string), true)
|
||||
}
|
||||
case .animation:
|
||||
return (strings.Message_Animation, true)
|
||||
return (NSAttributedString(string: strings.Message_Animation), true)
|
||||
case let .file(text):
|
||||
if text.isEmpty {
|
||||
return (strings.Message_File, true)
|
||||
return (NSAttributedString(string: strings.Message_File), true)
|
||||
} else {
|
||||
return (text, true)
|
||||
return (NSAttributedString(string: text), true)
|
||||
}
|
||||
case .contact:
|
||||
return (strings.Message_Contact, true)
|
||||
return (NSAttributedString(string: strings.Message_Contact), true)
|
||||
case let .game(text):
|
||||
return (text, true)
|
||||
return (NSAttributedString(string: text), true)
|
||||
case .location:
|
||||
return (strings.Message_Location, true)
|
||||
return (NSAttributedString(string: strings.Message_Location), true)
|
||||
case .liveLocation:
|
||||
return (strings.Message_LiveLocation, true)
|
||||
return (NSAttributedString(string: strings.Message_LiveLocation), true)
|
||||
case .expiredImage:
|
||||
return (strings.Message_ImageExpired, true)
|
||||
return (NSAttributedString(string: strings.Message_ImageExpired), true)
|
||||
case .expiredVideo:
|
||||
return (strings.Message_VideoExpired, true)
|
||||
return (NSAttributedString(string: strings.Message_VideoExpired), true)
|
||||
case let .poll(text):
|
||||
return ("📊 \(text)", false)
|
||||
return (NSAttributedString(string: "📊 \(text)"), false)
|
||||
case let .restricted(text):
|
||||
return (text, false)
|
||||
return (NSAttributedString(string: text), false)
|
||||
case let .dice(emoji):
|
||||
return (emoji, true)
|
||||
return (NSAttributedString(string: emoji), true)
|
||||
case let .invoice(text):
|
||||
return (text, true)
|
||||
return (NSAttributedString(string: text), true)
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionStringForMessage(contentSettings: ContentSettings, message: EngineMessage, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, accountPeerId: EnginePeer.Id) -> (String, Bool, Bool) {
|
||||
public func descriptionStringForMessage(contentSettings: ContentSettings, message: EngineMessage, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, accountPeerId: EnginePeer.Id) -> (NSAttributedString, Bool, Bool) {
|
||||
let contentKind = messageContentKind(contentSettings: contentSettings, message: message, strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, accountPeerId: accountPeerId)
|
||||
if !message.text.isEmpty && ![.expiredImage, .expiredVideo].contains(contentKind.key) {
|
||||
return (foldLineBreaks(message.text), false, true)
|
||||
return (foldLineBreaks(messageTextWithAttributes(message: message)), false, true)
|
||||
}
|
||||
let result = stringForMediaKind(contentKind, strings: strings)
|
||||
return (result.0, result.1, false)
|
||||
@ -263,6 +299,10 @@ public func foldLineBreaks(_ text: String) -> String {
|
||||
return result
|
||||
}
|
||||
|
||||
public func foldLineBreaks(_ text: NSAttributedString) -> NSAttributedString {
|
||||
//TODO:localize
|
||||
return text
|
||||
}
|
||||
|
||||
public func trimToLineCount(_ text: String, lineCount: Int) -> String {
|
||||
if lineCount < 1 {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
@ -15,6 +16,10 @@ private func spoilerAttributes(primaryTextColor: UIColor) -> MarkdownAttributeSe
|
||||
return MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [TelegramTextAttributes.Spoiler: true])
|
||||
}
|
||||
|
||||
private func customEmojiAttributes(primaryTextColor: UIColor, emoji: ChatTextInputTextCustomEmojiAttribute) -> MarkdownAttributeSet {
|
||||
return MarkdownAttributeSet(font: titleFont, textColor: primaryTextColor, additionalAttributes: [ChatTextInputAttributes.customEmoji.rawValue: emoji])
|
||||
}
|
||||
|
||||
private func peerMentionAttributes(primaryTextColor: UIColor, peerId: EnginePeer.Id) -> MarkdownAttributeSet {
|
||||
return MarkdownAttributeSet(font: titleBoldFont, textColor: primaryTextColor, additionalAttributes: [TelegramTextAttributes.PeerMention: TelegramPeerMention(peerId: peerId, mention: "")])
|
||||
}
|
||||
@ -176,9 +181,10 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
var type: PinnnedMediaType
|
||||
if let pinnedMessage = pinnedMessage?._asMessage() {
|
||||
let entities = (pinnedMessage.textEntitiesAttribute?.entities ?? []).filter { entity in
|
||||
if case .Spoiler = entity.type {
|
||||
switch entity.type {
|
||||
case .Spoiler, .CustomEmoji:
|
||||
return true
|
||||
} else {
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -264,9 +270,18 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
let location = entityOffset + entity.range.startIndex
|
||||
let length = max(0, min(entity.range.count, stringLength - location - 1))
|
||||
if length > 0 {
|
||||
let index = ranges.count
|
||||
ranges.append((ranges.count, NSRange(location: location, length: length)))
|
||||
attributes[index] = spoilerAttributes(primaryTextColor: primaryTextColor)
|
||||
switch entity.type {
|
||||
case .Spoiler:
|
||||
let index = ranges.count
|
||||
ranges.append((ranges.count, NSRange(location: location, length: length)))
|
||||
attributes[index] = spoilerAttributes(primaryTextColor: primaryTextColor)
|
||||
case let .CustomEmoji(stickerPack, fileId):
|
||||
let index = ranges.count
|
||||
ranges.append((ranges.count, NSRange(location: location, length: length)))
|
||||
attributes[index] = customEmojiAttributes(primaryTextColor: primaryTextColor, emoji: ChatTextInputTextCustomEmojiAttribute(stickerPack: stickerPack, fileId: fileId, file: message.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)] as? TelegramMediaFile))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
attributedString = addAttributesToStringWithRanges((string, ranges), body: bodyAttributes, argumentAttributes: attributes)
|
||||
@ -567,7 +582,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
}
|
||||
}
|
||||
case let .customText(text, entities):
|
||||
attributedString = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false)
|
||||
attributedString = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false, message: message._asMessage())
|
||||
case let .botDomainAccessGranted(domain):
|
||||
attributedString = NSAttributedString(string: strings.AuthSessions_Message(domain).string, font: titleFont, textColor: primaryTextColor)
|
||||
case let .botSentSecureValues(types):
|
||||
|
@ -288,6 +288,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/VideoAnimationCache:VideoAnimationCache",
|
||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
||||
"//submodules/TelegramUI/Components/ChatInputPanelContainer:ChatInputPanelContainer",
|
||||
"//submodules/TelegramUI/Components/TextNodeWithEntities:TextNodeWithEntities",
|
||||
"//submodules/Components/ComponentDisplayAdapters:ComponentDisplayAdapters",
|
||||
"//submodules/Media/ConvertOpusToAAC:ConvertOpusToAAC",
|
||||
"//submodules/Media/LocalAudioTranscription:LocalAudioTranscription",
|
||||
|
@ -54,7 +54,7 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
|
||||
}
|
||||
}
|
||||
|
||||
public init(context: AccountContext, groupId: String, attemptSynchronousLoad: Bool, emoji: ChatTextInputTextCustomEmojiAttribute, cache: AnimationCache, renderer: MultiAnimationRenderer, placeholderColor: UIColor) {
|
||||
public init(context: AccountContext, groupId: String, attemptSynchronousLoad: Bool, emoji: ChatTextInputTextCustomEmojiAttribute, file: TelegramMediaFile?, cache: AnimationCache, renderer: MultiAnimationRenderer, placeholderColor: UIColor, pointSize: CGSize) {
|
||||
self.context = context
|
||||
self.groupId = groupId
|
||||
self.emoji = emoji
|
||||
@ -63,28 +63,24 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
|
||||
self.placeholderColor = placeholderColor
|
||||
|
||||
let scale = min(2.0, UIScreenScale)
|
||||
self.pointSize = CGSize(width: 24, height: 24)
|
||||
self.pointSize = pointSize
|
||||
self.pixelSize = CGSize(width: self.pointSize.width * scale, height: self.pointSize.height * scale)
|
||||
|
||||
super.init()
|
||||
|
||||
self.infoDisposable = (context.engine.stickers.loadedStickerPack(reference: emoji.stickerPack, forceActualized: false)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
switch result {
|
||||
case let .result(_, items, _):
|
||||
for item in items {
|
||||
if item.file.fileId.id == emoji.fileId {
|
||||
strongSelf.updateFile(file: item.file, attemptSynchronousLoad: false)
|
||||
break
|
||||
}
|
||||
if let file = file {
|
||||
self.updateFile(file: file, attemptSynchronousLoad: true)
|
||||
} else {
|
||||
self.infoDisposable = (context.engine.stickers.resolveInlineSticker(fileId: emoji.fileId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] file in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
if let file = file {
|
||||
strongSelf.updateFile(file: file, attemptSynchronousLoad: false)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override public init(layer: Any) {
|
||||
@ -179,8 +175,8 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget {
|
||||
public final class EmojiTextAttachmentView: UIView {
|
||||
private let contentLayer: InlineStickerItemLayer
|
||||
|
||||
public init(context: AccountContext, emoji: ChatTextInputTextCustomEmojiAttribute, cache: AnimationCache, renderer: MultiAnimationRenderer, placeholderColor: UIColor) {
|
||||
self.contentLayer = InlineStickerItemLayer(context: context, groupId: "textInputView", attemptSynchronousLoad: true, emoji: emoji, cache: cache, renderer: renderer, placeholderColor: placeholderColor)
|
||||
public init(context: AccountContext, emoji: ChatTextInputTextCustomEmojiAttribute, file: TelegramMediaFile?, cache: AnimationCache, renderer: MultiAnimationRenderer, placeholderColor: UIColor) {
|
||||
self.contentLayer = InlineStickerItemLayer(context: context, groupId: "textInputView", attemptSynchronousLoad: true, emoji: emoji, file: file, cache: cache, renderer: renderer, placeholderColor: placeholderColor, pointSize: CGSize(width: 24.0, height: 24.0))
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
|
@ -41,16 +41,43 @@ public final class EntityKeyboardComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
public struct GifSearchEmoji: Equatable {
|
||||
public var emoji: String
|
||||
public var file: TelegramMediaFile
|
||||
public var title: String
|
||||
|
||||
public init(emoji: String, file: TelegramMediaFile, title: String) {
|
||||
self.emoji = emoji
|
||||
self.file = file
|
||||
self.title = title
|
||||
}
|
||||
|
||||
public static func ==(lhs: GifSearchEmoji, rhs: GifSearchEmoji) -> Bool {
|
||||
if lhs.emoji != rhs.emoji {
|
||||
return false
|
||||
}
|
||||
if lhs.file.fileId != rhs.file.fileId {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public let theme: PresentationTheme
|
||||
public let bottomInset: CGFloat
|
||||
public let emojiContent: EmojiPagerContentComponent
|
||||
public let stickerContent: EmojiPagerContentComponent
|
||||
public let gifContent: GifPagerContentComponent
|
||||
public let availableGifSearchEmojies: [GifSearchEmoji]
|
||||
public let defaultToEmojiTab: Bool
|
||||
public let externalTopPanelContainer: PagerExternalTopPanelContainer?
|
||||
public let topPanelExtensionUpdated: (CGFloat, Transition) -> Void
|
||||
public let hideInputUpdated: (Bool, Bool, Transition) -> Void
|
||||
public let switchToTextInput: () -> Void
|
||||
public let switchToGifSubject: (GifPagerContentComponent.Subject) -> Void
|
||||
public let makeSearchContainerNode: (EntitySearchContentType) -> EntitySearchContainerNode
|
||||
public let deviceMetrics: DeviceMetrics
|
||||
public let hiddenInputHeight: CGFloat
|
||||
@ -62,11 +89,13 @@ public final class EntityKeyboardComponent: Component {
|
||||
emojiContent: EmojiPagerContentComponent,
|
||||
stickerContent: EmojiPagerContentComponent,
|
||||
gifContent: GifPagerContentComponent,
|
||||
availableGifSearchEmojies: [GifSearchEmoji],
|
||||
defaultToEmojiTab: Bool,
|
||||
externalTopPanelContainer: PagerExternalTopPanelContainer?,
|
||||
topPanelExtensionUpdated: @escaping (CGFloat, Transition) -> Void,
|
||||
hideInputUpdated: @escaping (Bool, Bool, Transition) -> Void,
|
||||
switchToTextInput: @escaping () -> Void,
|
||||
switchToGifSubject: @escaping (GifPagerContentComponent.Subject) -> Void,
|
||||
makeSearchContainerNode: @escaping (EntitySearchContentType) -> EntitySearchContainerNode,
|
||||
deviceMetrics: DeviceMetrics,
|
||||
hiddenInputHeight: CGFloat,
|
||||
@ -77,11 +106,13 @@ public final class EntityKeyboardComponent: Component {
|
||||
self.emojiContent = emojiContent
|
||||
self.stickerContent = stickerContent
|
||||
self.gifContent = gifContent
|
||||
self.availableGifSearchEmojies = availableGifSearchEmojies
|
||||
self.defaultToEmojiTab = defaultToEmojiTab
|
||||
self.externalTopPanelContainer = externalTopPanelContainer
|
||||
self.topPanelExtensionUpdated = topPanelExtensionUpdated
|
||||
self.hideInputUpdated = hideInputUpdated
|
||||
self.switchToTextInput = switchToTextInput
|
||||
self.switchToGifSubject = switchToGifSubject
|
||||
self.makeSearchContainerNode = makeSearchContainerNode
|
||||
self.deviceMetrics = deviceMetrics
|
||||
self.hiddenInputHeight = hiddenInputHeight
|
||||
@ -104,6 +135,9 @@ public final class EntityKeyboardComponent: Component {
|
||||
if lhs.gifContent != rhs.gifContent {
|
||||
return false
|
||||
}
|
||||
if lhs.availableGifSearchEmojies != rhs.availableGifSearchEmojies {
|
||||
return false
|
||||
}
|
||||
if lhs.defaultToEmojiTab != rhs.defaultToEmojiTab {
|
||||
return false
|
||||
}
|
||||
@ -162,26 +196,58 @@ public final class EntityKeyboardComponent: Component {
|
||||
let gifsContentItemIdUpdated = ActionSlot<(AnyHashable, Transition)>()
|
||||
contents.append(AnyComponentWithIdentity(id: "gifs", component: AnyComponent(component.gifContent)))
|
||||
var topGifItems: [EntityKeyboardTopPanelComponent.Item] = []
|
||||
topGifItems.removeAll()
|
||||
/*topGifItems.append(EntityKeyboardTopPanelComponent.Item(
|
||||
//TODO:localize
|
||||
topGifItems.append(EntityKeyboardTopPanelComponent.Item(
|
||||
id: "recent",
|
||||
content: AnyComponent(BundleIconComponent(
|
||||
name: "Chat/Input/Media/RecentTabIcon",
|
||||
tintColor: component.theme.chat.inputMediaPanel.panelIconColor,
|
||||
maxSize: CGSize(width: 30.0, height: 30.0))
|
||||
)
|
||||
content: AnyComponent(EntityKeyboardIconTopPanelComponent(
|
||||
imageName: "Chat/Input/Media/RecentTabIcon",
|
||||
theme: component.theme,
|
||||
title: "Recent",
|
||||
pressed: { [weak self] in
|
||||
self?.component?.switchToGifSubject(.recent)
|
||||
}
|
||||
))
|
||||
))
|
||||
topGifItems.append(EntityKeyboardTopPanelComponent.Item(
|
||||
id: "trending",
|
||||
content: AnyComponent(BundleIconComponent(
|
||||
name: "Chat/Input/Media/TrendingGifs",
|
||||
tintColor: component.theme.chat.inputMediaPanel.panelIconColor,
|
||||
maxSize: CGSize(width: 30.0, height: 30.0))
|
||||
)
|
||||
))*/
|
||||
content: AnyComponent(EntityKeyboardIconTopPanelComponent(
|
||||
imageName: "Chat/Input/Media/TrendingGifs",
|
||||
theme: component.theme,
|
||||
title: "Trending",
|
||||
pressed: { [weak self] in
|
||||
self?.component?.switchToGifSubject(.trending)
|
||||
}
|
||||
))
|
||||
))
|
||||
for emoji in component.availableGifSearchEmojies {
|
||||
topGifItems.append(EntityKeyboardTopPanelComponent.Item(
|
||||
id: emoji.emoji,
|
||||
content: AnyComponent(EntityKeyboardAnimationTopPanelComponent(
|
||||
context: component.stickerContent.context,
|
||||
file: emoji.file,
|
||||
animationCache: component.stickerContent.animationCache,
|
||||
animationRenderer: component.stickerContent.animationRenderer,
|
||||
theme: component.theme,
|
||||
title: emoji.title,
|
||||
pressed: { [weak self] in
|
||||
self?.component?.switchToGifSubject(.emojiSearch(emoji.emoji))
|
||||
}
|
||||
))
|
||||
))
|
||||
}
|
||||
let defaultActiveGifItemId: AnyHashable
|
||||
switch component.gifContent.subject {
|
||||
case .recent:
|
||||
defaultActiveGifItemId = "recent"
|
||||
case .trending:
|
||||
defaultActiveGifItemId = "trending"
|
||||
case let .emojiSearch(value):
|
||||
defaultActiveGifItemId = AnyHashable(value)
|
||||
}
|
||||
contentTopPanels.append(AnyComponentWithIdentity(id: "gifs", component: AnyComponent(EntityKeyboardTopPanelComponent(
|
||||
theme: component.theme,
|
||||
items: topGifItems,
|
||||
defaultActiveItemId: defaultActiveGifItemId,
|
||||
activeContentItemIdUpdated: gifsContentItemIdUpdated
|
||||
))))
|
||||
contentIcons.append(AnyComponentWithIdentity(id: "gifs", component: AnyComponent(BundleIconComponent(
|
||||
|
@ -364,15 +364,18 @@ final class EntityKeyboardTopPanelComponent: Component {
|
||||
|
||||
let theme: PresentationTheme
|
||||
let items: [Item]
|
||||
let defaultActiveItemId: AnyHashable?
|
||||
let activeContentItemIdUpdated: ActionSlot<(AnyHashable, Transition)>
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
items: [Item],
|
||||
defaultActiveItemId: AnyHashable? = nil,
|
||||
activeContentItemIdUpdated: ActionSlot<(AnyHashable, Transition)>
|
||||
) {
|
||||
self.theme = theme
|
||||
self.items = items
|
||||
self.defaultActiveItemId = defaultActiveItemId
|
||||
self.activeContentItemIdUpdated = activeContentItemIdUpdated
|
||||
}
|
||||
|
||||
@ -383,6 +386,9 @@ final class EntityKeyboardTopPanelComponent: Component {
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
if lhs.defaultActiveItemId != rhs.defaultActiveItemId {
|
||||
return false
|
||||
}
|
||||
if lhs.activeContentItemIdUpdated !== rhs.activeContentItemIdUpdated {
|
||||
return false
|
||||
}
|
||||
@ -613,6 +619,10 @@ final class EntityKeyboardTopPanelComponent: Component {
|
||||
}
|
||||
self.component = component
|
||||
|
||||
if let defaultActiveItemId = component.defaultActiveItemId {
|
||||
self.activeContentItemId = defaultActiveItemId
|
||||
}
|
||||
|
||||
let panelEnvironment = environment[EntityKeyboardTopContainerPanelEnvironment.self].value
|
||||
self.environment = panelEnvironment
|
||||
|
||||
@ -639,8 +649,8 @@ final class EntityKeyboardTopPanelComponent: Component {
|
||||
let previousItemFrame = previousItemLayout.containerFrame(at: previousVisibleRange.minIndex)
|
||||
let updatedItemFrame = itemLayout.containerFrame(at: previousVisibleRange.minIndex)
|
||||
|
||||
let previousDistanceToItemFraction = (previousItemFrame.minX - self.scrollView.bounds.minX) / previousItemFrame.width
|
||||
let newBounds = CGRect(origin: CGPoint(x: updatedItemFrame.minX - floor(previousDistanceToItemFraction * updatedItemFrame.width), y: 0.0), size: availableSize)
|
||||
let previousDistanceToItem = (previousItemFrame.minX - self.scrollView.bounds.minX)// / previousItemFrame.width
|
||||
let newBounds = CGRect(origin: CGPoint(x: updatedItemFrame.minX - previousDistanceToItem/* * updatedItemFrame.width)*/, y: 0.0), size: availableSize)
|
||||
updatedBounds = newBounds
|
||||
|
||||
var updatedVisibleBounds = newBounds
|
||||
|
@ -117,6 +117,12 @@ private class GifVideoLayer: AVSampleBufferDisplayLayer {
|
||||
public final class GifPagerContentComponent: Component {
|
||||
public typealias EnvironmentType = (EntityKeyboardChildEnvironment, PagerComponentChildEnvironment)
|
||||
|
||||
public enum Subject: Equatable {
|
||||
case recent
|
||||
case trending
|
||||
case emojiSearch(String)
|
||||
}
|
||||
|
||||
public final class InputInteraction {
|
||||
public let performItemAction: (Item, UIView, CGRect) -> Void
|
||||
|
||||
@ -148,15 +154,18 @@ public final class GifPagerContentComponent: Component {
|
||||
|
||||
public let context: AccountContext
|
||||
public let inputInteraction: InputInteraction
|
||||
public let subject: Subject
|
||||
public let items: [Item]
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
inputInteraction: InputInteraction,
|
||||
subject: Subject,
|
||||
items: [Item]
|
||||
) {
|
||||
self.context = context
|
||||
self.inputInteraction = inputInteraction
|
||||
self.subject = subject
|
||||
self.items = items
|
||||
}
|
||||
|
||||
@ -167,6 +176,9 @@ public final class GifPagerContentComponent: Component {
|
||||
if lhs.inputInteraction !== rhs.inputInteraction {
|
||||
return false
|
||||
}
|
||||
if lhs.subject != rhs.subject {
|
||||
return false
|
||||
}
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
@ -363,7 +375,10 @@ public final class GifPagerContentComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
private let scrollView: UIScrollView
|
||||
private final class ContentScrollView: UIScrollView, PagerExpandableScrollView {
|
||||
}
|
||||
|
||||
private let scrollView: ContentScrollView
|
||||
|
||||
private var visibleItemLayers: [MediaId: ItemLayer] = [:]
|
||||
private var ignoreScrolling: Bool = false
|
||||
@ -374,7 +389,7 @@ public final class GifPagerContentComponent: Component {
|
||||
private var itemLayout: ItemLayout?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.scrollView = UIScrollView()
|
||||
self.scrollView = ContentScrollView()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
|
24
submodules/TelegramUI/Components/TextNodeWithEntities/BUILD
Normal file
24
submodules/TelegramUI/Components/TextNodeWithEntities/BUILD
Normal file
@ -0,0 +1,24 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "TextNodeWithEntities",
|
||||
module_name = "TextNodeWithEntities",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/TextFormat:TextFormat",
|
||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView:EmojiTextAttachmentView",
|
||||
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
|
||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -0,0 +1,472 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import EmojiTextAttachmentView
|
||||
import TextFormat
|
||||
import AccountContext
|
||||
import AnimationCache
|
||||
import MultiAnimationRenderer
|
||||
import TelegramCore
|
||||
|
||||
private final class InlineStickerItem: Hashable {
|
||||
let emoji: ChatTextInputTextCustomEmojiAttribute
|
||||
let file: TelegramMediaFile?
|
||||
let fontSize: CGFloat
|
||||
|
||||
init(emoji: ChatTextInputTextCustomEmojiAttribute, file: TelegramMediaFile?, fontSize: CGFloat) {
|
||||
self.emoji = emoji
|
||||
self.file = file
|
||||
self.fontSize = fontSize
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(emoji.fileId)
|
||||
hasher.combine(self.fontSize)
|
||||
}
|
||||
|
||||
static func ==(lhs: InlineStickerItem, rhs: InlineStickerItem) -> Bool {
|
||||
if lhs.emoji.fileId != rhs.emoji.fileId {
|
||||
return false
|
||||
}
|
||||
if lhs.file?.fileId != rhs.file?.fileId {
|
||||
return false
|
||||
}
|
||||
if lhs.fontSize != rhs.fontSize {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public final class TextNodeWithEntities {
|
||||
public final class Arguments {
|
||||
public let context: AccountContext
|
||||
public let cache: AnimationCache
|
||||
public let renderer: MultiAnimationRenderer
|
||||
public let placeholderColor: UIColor
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
cache: AnimationCache,
|
||||
renderer: MultiAnimationRenderer,
|
||||
placeholderColor: UIColor
|
||||
) {
|
||||
self.context = context
|
||||
self.cache = cache
|
||||
self.renderer = renderer
|
||||
self.placeholderColor = placeholderColor
|
||||
}
|
||||
}
|
||||
|
||||
public let textNode: TextNode
|
||||
private var inlineStickerItemLayers: [InlineStickerItemLayer.Key: InlineStickerItemLayer] = [:]
|
||||
|
||||
public var visibilityRect: CGRect? {
|
||||
didSet {
|
||||
if !self.inlineStickerItemLayers.isEmpty && oldValue != self.visibilityRect {
|
||||
for (_, itemLayer) in self.inlineStickerItemLayers {
|
||||
let isItemVisible: Bool
|
||||
if let visibilityRect = self.visibilityRect {
|
||||
if itemLayer.frame.intersects(visibilityRect) {
|
||||
isItemVisible = true
|
||||
} else {
|
||||
isItemVisible = false
|
||||
}
|
||||
} else {
|
||||
isItemVisible = false
|
||||
}
|
||||
itemLayer.isVisibleForAnimations = isItemVisible
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public init() {
|
||||
self.textNode = TextNode()
|
||||
}
|
||||
|
||||
private init(textNode: TextNode) {
|
||||
self.textNode = textNode
|
||||
}
|
||||
|
||||
public static func asyncLayout(_ maybeNode: TextNodeWithEntities?) -> (TextNodeLayoutArguments) -> (TextNodeLayout, (TextNodeWithEntities.Arguments?) -> TextNodeWithEntities) {
|
||||
let makeLayout = TextNode.asyncLayout(maybeNode?.textNode)
|
||||
return { [weak maybeNode] arguments in
|
||||
var updatedString: NSAttributedString?
|
||||
if let sourceString = arguments.attributedString {
|
||||
let string = NSMutableAttributedString(attributedString: sourceString)
|
||||
|
||||
let fullRange = NSRange(location: 0, length: string.length)
|
||||
string.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: fullRange, options: [], using: { value, range, _ in
|
||||
if let value = value as? ChatTextInputTextCustomEmojiAttribute {
|
||||
if let font = string.attribute(.font, at: range.location, effectiveRange: nil) as? UIFont {
|
||||
string.addAttribute(NSAttributedString.Key("Attribute__EmbeddedItem"), value: InlineStickerItem(emoji: value, file: value.file, fontSize: font.pointSize), range: range)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
updatedString = string
|
||||
}
|
||||
|
||||
let (layout, apply) = makeLayout(arguments.withAttributedString(updatedString))
|
||||
return (layout, { applyArguments in
|
||||
let result = apply()
|
||||
|
||||
if let maybeNode = maybeNode {
|
||||
if let applyArguments = applyArguments {
|
||||
maybeNode.updateInlineStickers(context: applyArguments.context, cache: applyArguments.cache, renderer: applyArguments.renderer, textLayout: layout, placeholderColor: applyArguments.placeholderColor)
|
||||
}
|
||||
|
||||
return maybeNode
|
||||
} else {
|
||||
let resultNode = TextNodeWithEntities(textNode: result)
|
||||
|
||||
if let applyArguments = applyArguments {
|
||||
resultNode.updateInlineStickers(context: applyArguments.context, cache: applyArguments.cache, renderer: applyArguments.renderer, textLayout: layout, placeholderColor: applyArguments.placeholderColor)
|
||||
}
|
||||
|
||||
return resultNode
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private func isItemVisible(itemRect: CGRect) -> Bool {
|
||||
if let visibilityRect = self.visibilityRect {
|
||||
return itemRect.intersects(visibilityRect)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private func updateInlineStickers(context: AccountContext, cache: AnimationCache, renderer: MultiAnimationRenderer, textLayout: TextNodeLayout?, placeholderColor: UIColor) {
|
||||
var nextIndexById: [Int64: Int] = [:]
|
||||
var validIds: [InlineStickerItemLayer.Key] = []
|
||||
|
||||
if let textLayout = textLayout {
|
||||
for item in textLayout.embeddedItems {
|
||||
if let stickerItem = item.value as? InlineStickerItem {
|
||||
let index: Int
|
||||
if let currentNext = nextIndexById[stickerItem.emoji.fileId] {
|
||||
index = currentNext
|
||||
} else {
|
||||
index = 0
|
||||
}
|
||||
nextIndexById[stickerItem.emoji.fileId] = index + 1
|
||||
let id = InlineStickerItemLayer.Key(id: stickerItem.emoji.fileId, index: index)
|
||||
validIds.append(id)
|
||||
|
||||
let itemSize = floor(stickerItem.fontSize * 24.0 / 17.0)
|
||||
|
||||
let itemFrame = CGRect(origin: item.rect.offsetBy(dx: textLayout.insets.left, dy: textLayout.insets.top + 0.0).center, size: CGSize()).insetBy(dx: -itemSize / 2.0, dy: -itemSize / 2.0)
|
||||
|
||||
let itemLayer: InlineStickerItemLayer
|
||||
if let current = self.inlineStickerItemLayers[id] {
|
||||
itemLayer = current
|
||||
} else {
|
||||
itemLayer = InlineStickerItemLayer(context: context, groupId: "inlineEmoji", attemptSynchronousLoad: false, emoji: stickerItem.emoji, file: stickerItem.file, cache: cache, renderer: renderer, placeholderColor: placeholderColor, pointSize: CGSize(width: itemSize, height: itemSize))
|
||||
self.inlineStickerItemLayers[id] = itemLayer
|
||||
self.textNode.layer.addSublayer(itemLayer)
|
||||
|
||||
itemLayer.isVisibleForAnimations = self.isItemVisible(itemRect: itemFrame)
|
||||
}
|
||||
|
||||
itemLayer.frame = itemFrame
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var removeKeys: [InlineStickerItemLayer.Key] = []
|
||||
for (key, itemLayer) in self.inlineStickerItemLayers {
|
||||
if !validIds.contains(key) {
|
||||
removeKeys.append(key)
|
||||
itemLayer.removeFromSuperlayer()
|
||||
}
|
||||
}
|
||||
for key in removeKeys {
|
||||
self.inlineStickerItemLayers.removeValue(forKey: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ImmediateTextNodeWithEntities: TextNode {
|
||||
public var attributedText: NSAttributedString?
|
||||
public var textAlignment: NSTextAlignment = .natural
|
||||
public var verticalAlignment: TextVerticalAlignment = .top
|
||||
public var truncationType: CTLineTruncationType = .end
|
||||
public var maximumNumberOfLines: Int = 1
|
||||
public var lineSpacing: CGFloat = 0.0
|
||||
public var insets: UIEdgeInsets = UIEdgeInsets()
|
||||
public var textShadowColor: UIColor?
|
||||
public var textStroke: (UIColor, CGFloat)?
|
||||
public var cutout: TextNodeCutout?
|
||||
public var displaySpoilers = false
|
||||
|
||||
public var arguments: TextNodeWithEntities.Arguments?
|
||||
|
||||
private var inlineStickerItemLayers: [InlineStickerItemLayer.Key: InlineStickerItemLayer] = [:]
|
||||
|
||||
public var visibility: Bool = false {
|
||||
didSet {
|
||||
if !self.inlineStickerItemLayers.isEmpty && oldValue != self.visibility {
|
||||
for (_, itemLayer) in self.inlineStickerItemLayers {
|
||||
let isItemVisible: Bool = self.visibility
|
||||
itemLayer.isVisibleForAnimations = isItemVisible
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var truncationMode: NSLineBreakMode {
|
||||
get {
|
||||
switch self.truncationType {
|
||||
case .start:
|
||||
return .byTruncatingHead
|
||||
case .middle:
|
||||
return .byTruncatingMiddle
|
||||
case .end:
|
||||
return .byTruncatingTail
|
||||
@unknown default:
|
||||
return .byTruncatingTail
|
||||
}
|
||||
} set(value) {
|
||||
switch value {
|
||||
case .byTruncatingHead:
|
||||
self.truncationType = .start
|
||||
case .byTruncatingMiddle:
|
||||
self.truncationType = .middle
|
||||
case .byTruncatingTail:
|
||||
self.truncationType = .end
|
||||
default:
|
||||
self.truncationType = .end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var tapRecognizer: TapLongTapOrDoubleTapGestureRecognizer?
|
||||
private var linkHighlightingNode: LinkHighlightingNode?
|
||||
|
||||
public var linkHighlightColor: UIColor?
|
||||
|
||||
public var trailingLineWidth: CGFloat?
|
||||
|
||||
var constrainedSize: CGSize?
|
||||
|
||||
public var highlightAttributeAction: (([NSAttributedString.Key: Any]) -> NSAttributedString.Key?)? {
|
||||
didSet {
|
||||
if self.isNodeLoaded {
|
||||
self.updateInteractiveActions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var tapAttributeAction: (([NSAttributedString.Key: Any], Int) -> Void)?
|
||||
public var longTapAttributeAction: (([NSAttributedString.Key: Any], Int) -> Void)?
|
||||
|
||||
private func processedAttributedText() -> NSAttributedString? {
|
||||
var updatedString: NSAttributedString?
|
||||
if let sourceString = self.attributedText {
|
||||
let string = NSMutableAttributedString(attributedString: sourceString)
|
||||
|
||||
let fullRange = NSRange(location: 0, length: string.length)
|
||||
string.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: fullRange, options: [], using: { value, range, _ in
|
||||
if let value = value as? ChatTextInputTextCustomEmojiAttribute {
|
||||
if let font = string.attribute(.font, at: range.location, effectiveRange: nil) as? UIFont {
|
||||
string.addAttribute(NSAttributedString.Key("Attribute__EmbeddedItem"), value: InlineStickerItem(emoji: value, file: value.file, fontSize: font.pointSize), range: range)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
updatedString = string
|
||||
}
|
||||
return updatedString
|
||||
}
|
||||
|
||||
public func updateLayout(_ constrainedSize: CGSize) -> CGSize {
|
||||
self.constrainedSize = constrainedSize
|
||||
|
||||
let makeLayout = TextNode.asyncLayout(self)
|
||||
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.processedAttributedText(), backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, textShadowColor: self.textShadowColor, textStroke: self.textStroke, displaySpoilers: self.displaySpoilers))
|
||||
|
||||
let _ = apply()
|
||||
|
||||
if let arguments = self.arguments {
|
||||
self.updateInlineStickers(context: arguments.context, cache: arguments.cache, renderer: arguments.renderer, textLayout: layout, placeholderColor: arguments.placeholderColor)
|
||||
}
|
||||
|
||||
if layout.numberOfLines > 1 {
|
||||
self.trailingLineWidth = layout.trailingLineWidth
|
||||
} else {
|
||||
self.trailingLineWidth = nil
|
||||
}
|
||||
return layout.size
|
||||
}
|
||||
|
||||
private func updateInlineStickers(context: AccountContext, cache: AnimationCache, renderer: MultiAnimationRenderer, textLayout: TextNodeLayout?, placeholderColor: UIColor) {
|
||||
var nextIndexById: [Int64: Int] = [:]
|
||||
var validIds: [InlineStickerItemLayer.Key] = []
|
||||
|
||||
if let textLayout = textLayout {
|
||||
for item in textLayout.embeddedItems {
|
||||
if let stickerItem = item.value as? InlineStickerItem {
|
||||
let index: Int
|
||||
if let currentNext = nextIndexById[stickerItem.emoji.fileId] {
|
||||
index = currentNext
|
||||
} else {
|
||||
index = 0
|
||||
}
|
||||
nextIndexById[stickerItem.emoji.fileId] = index + 1
|
||||
let id = InlineStickerItemLayer.Key(id: stickerItem.emoji.fileId, index: index)
|
||||
validIds.append(id)
|
||||
|
||||
let itemSize = floor(stickerItem.fontSize * 24.0 / 17.0)
|
||||
|
||||
let itemFrame = CGRect(origin: item.rect.offsetBy(dx: textLayout.insets.left, dy: textLayout.insets.top + 0.0).center, size: CGSize()).insetBy(dx: -itemSize / 2.0, dy: -itemSize / 2.0)
|
||||
|
||||
let itemLayer: InlineStickerItemLayer
|
||||
if let current = self.inlineStickerItemLayers[id] {
|
||||
itemLayer = current
|
||||
} else {
|
||||
itemLayer = InlineStickerItemLayer(context: context, groupId: "inlineEmoji", attemptSynchronousLoad: false, emoji: stickerItem.emoji, file: stickerItem.file, cache: cache, renderer: renderer, placeholderColor: placeholderColor, pointSize: CGSize(width: itemSize, height: itemSize))
|
||||
self.inlineStickerItemLayers[id] = itemLayer
|
||||
self.layer.addSublayer(itemLayer)
|
||||
|
||||
itemLayer.isVisibleForAnimations = self.visibility
|
||||
}
|
||||
|
||||
itemLayer.frame = itemFrame
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var removeKeys: [InlineStickerItemLayer.Key] = []
|
||||
for (key, itemLayer) in self.inlineStickerItemLayers {
|
||||
if !validIds.contains(key) {
|
||||
removeKeys.append(key)
|
||||
itemLayer.removeFromSuperlayer()
|
||||
}
|
||||
}
|
||||
for key in removeKeys {
|
||||
self.inlineStickerItemLayers.removeValue(forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
public func updateLayoutInfo(_ constrainedSize: CGSize) -> ImmediateTextNodeLayoutInfo {
|
||||
self.constrainedSize = constrainedSize
|
||||
|
||||
let makeLayout = TextNode.asyncLayout(self)
|
||||
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.processedAttributedText(), backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers))
|
||||
|
||||
let _ = apply()
|
||||
|
||||
if let arguments = self.arguments {
|
||||
self.updateInlineStickers(context: arguments.context, cache: arguments.cache, renderer: arguments.renderer, textLayout: layout, placeholderColor: arguments.placeholderColor)
|
||||
}
|
||||
|
||||
return ImmediateTextNodeLayoutInfo(size: layout.size, truncated: layout.truncated, numberOfLines: layout.numberOfLines)
|
||||
}
|
||||
|
||||
public func updateLayoutFullInfo(_ constrainedSize: CGSize) -> TextNodeLayout {
|
||||
self.constrainedSize = constrainedSize
|
||||
|
||||
let makeLayout = TextNode.asyncLayout(self)
|
||||
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.processedAttributedText(), backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers))
|
||||
|
||||
let _ = apply()
|
||||
|
||||
if let arguments = self.arguments {
|
||||
self.updateInlineStickers(context: arguments.context, cache: arguments.cache, renderer: arguments.renderer, textLayout: layout, placeholderColor: arguments.placeholderColor)
|
||||
}
|
||||
|
||||
return layout
|
||||
}
|
||||
|
||||
public func redrawIfPossible() {
|
||||
if let constrainedSize = self.constrainedSize {
|
||||
let _ = self.updateLayout(constrainedSize)
|
||||
}
|
||||
}
|
||||
|
||||
override public func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.updateInteractiveActions()
|
||||
}
|
||||
|
||||
private func updateInteractiveActions() {
|
||||
if self.highlightAttributeAction != nil {
|
||||
if self.tapRecognizer == nil {
|
||||
let tapRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapAction(_:)))
|
||||
tapRecognizer.highlight = { [weak self] point in
|
||||
if let strongSelf = self {
|
||||
var rects: [CGRect]?
|
||||
if let point = point {
|
||||
if let (index, attributes) = strongSelf.attributesAtPoint(CGPoint(x: point.x, y: point.y)) {
|
||||
if let selectedAttribute = strongSelf.highlightAttributeAction?(attributes) {
|
||||
let initialRects = strongSelf.lineAndAttributeRects(name: selectedAttribute.rawValue, at: index)
|
||||
if let initialRects = initialRects, case .center = strongSelf.textAlignment {
|
||||
var mappedRects: [CGRect] = []
|
||||
for i in 0 ..< initialRects.count {
|
||||
let lineRect = initialRects[i].0
|
||||
var itemRect = initialRects[i].1
|
||||
itemRect.origin.x = floor((strongSelf.bounds.size.width - lineRect.width) / 2.0) + itemRect.origin.x
|
||||
mappedRects.append(itemRect)
|
||||
}
|
||||
rects = mappedRects
|
||||
} else {
|
||||
rects = strongSelf.attributeRects(name: selectedAttribute.rawValue, at: index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let rects = rects {
|
||||
let linkHighlightingNode: LinkHighlightingNode
|
||||
if let current = strongSelf.linkHighlightingNode {
|
||||
linkHighlightingNode = current
|
||||
} else {
|
||||
linkHighlightingNode = LinkHighlightingNode(color: strongSelf.linkHighlightColor ?? .clear)
|
||||
strongSelf.linkHighlightingNode = linkHighlightingNode
|
||||
strongSelf.addSubnode(linkHighlightingNode)
|
||||
}
|
||||
linkHighlightingNode.frame = strongSelf.bounds
|
||||
linkHighlightingNode.updateRects(rects.map { $0.offsetBy(dx: 0.0, dy: 0.0) })
|
||||
} else if let linkHighlightingNode = strongSelf.linkHighlightingNode {
|
||||
strongSelf.linkHighlightingNode = nil
|
||||
linkHighlightingNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak linkHighlightingNode] _ in
|
||||
linkHighlightingNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
self.view.addGestureRecognizer(tapRecognizer)
|
||||
}
|
||||
} else if let tapRecognizer = self.tapRecognizer {
|
||||
self.tapRecognizer = nil
|
||||
self.view.removeGestureRecognizer(tapRecognizer)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func tapAction(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .ended:
|
||||
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
||||
switch gesture {
|
||||
case .tap:
|
||||
if let (index, attributes) = self.attributesAtPoint(CGPoint(x: location.x, y: location.y)) {
|
||||
self.tapAttributeAction?(attributes, index)
|
||||
}
|
||||
case .longTap:
|
||||
if let (index, attributes) = self.attributesAtPoint(CGPoint(x: location.x, y: location.y)) {
|
||||
self.longTapAttributeAction?(attributes, index)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
@ -2000,7 +2000,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
if let messageId = messageIdFromNotification(peerId: peerId, notification: response.notification) {
|
||||
let _ = TelegramEngine(account: account).messages.applyMaxReadIndexInteractively(index: MessageIndex(id: messageId, timestamp: 0)).start()
|
||||
}
|
||||
return enqueueMessages(account: account, peerId: peerId, messages: [EnqueueMessage.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)])
|
||||
return enqueueMessages(account: account, peerId: peerId, messages: [EnqueueMessage.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)])
|
||||
|> map { messageIds -> MessageId? in
|
||||
if messageIds.isEmpty {
|
||||
return nil
|
||||
|
@ -305,7 +305,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
}
|
||||
var dismissImpl: (() -> Void)?
|
||||
let alertTheme = AlertControllerTheme(presentationData: strongSelf.presentationData)
|
||||
let attributedText = stringWithAppliedEntities(termsOfService.text, entities: termsOfService.entities, baseColor: alertTheme.primaryColor, linkColor: alertTheme.accentColor, baseFont: Font.regular(13.0), linkFont: Font.regular(13.0), boldFont: Font.semibold(13.0), italicFont: Font.italic(13.0), boldItalicFont: Font.semiboldItalic(13.0), fixedFont: Font.regular(13.0), blockQuoteFont: Font.regular(13.0))
|
||||
let attributedText = stringWithAppliedEntities(termsOfService.text, entities: termsOfService.entities, baseColor: alertTheme.primaryColor, linkColor: alertTheme.accentColor, baseFont: Font.regular(13.0), linkFont: Font.regular(13.0), boldFont: Font.semibold(13.0), italicFont: Font.italic(13.0), boldItalicFont: Font.semiboldItalic(13.0), fixedFont: Font.regular(13.0), blockQuoteFont: Font.regular(13.0), message: nil)
|
||||
let contentNode = TextAlertContentNode(theme: alertTheme, title: NSAttributedString(string: strongSelf.presentationData.strings.Login_TermsOfServiceHeader, font: Font.medium(17.0), textColor: alertTheme.primaryColor, paragraphAlignment: .center), text: attributedText, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Login_TermsOfServiceAgree, action: {
|
||||
dismissImpl?()
|
||||
|
@ -207,7 +207,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
|
||||
updatedTextAndEntities = (item.text, generateTextEntities(item.text, enabledTypes: .all))
|
||||
}
|
||||
|
||||
let attributedText = stringWithAppliedEntities(updatedTextAndEntities.0, entities: updatedTextAndEntities.1, baseColor: item.presentationData.theme.theme.chat.message.infoPrimaryTextColor, linkColor: item.presentationData.theme.theme.chat.message.infoLinkTextColor, baseFont: messageFont, linkFont: messageFont, boldFont: messageBoldFont, italicFont: messageItalicFont, boldItalicFont: messageBoldItalicFont, fixedFont: messageFixedFont, blockQuoteFont: messageFont)
|
||||
let attributedText = stringWithAppliedEntities(updatedTextAndEntities.0, entities: updatedTextAndEntities.1, baseColor: item.presentationData.theme.theme.chat.message.infoPrimaryTextColor, linkColor: item.presentationData.theme.theme.chat.message.infoLinkTextColor, baseFont: messageFont, linkFont: messageFont, boldFont: messageBoldFont, italicFont: messageItalicFont, boldItalicFont: messageBoldItalicFont, fixedFont: messageFixedFont, blockQuoteFont: messageFont, message: nil)
|
||||
|
||||
let horizontalEdgeInset: CGFloat = 10.0 + params.leftInset
|
||||
let horizontalContentInset: CGFloat = 12.0
|
||||
|
@ -1499,7 +1499,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if !entities.isEmpty {
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
strongSelf.sendMessages([.message(text: text, attributes: attributes, mediaReference: nil, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil)])
|
||||
strongSelf.sendMessages([.message(text: text, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil)])
|
||||
}, sendSticker: { [weak self] fileReference, silentPosting, schedule, query, clearInput, sourceView, sourceRect, sourceLayer in
|
||||
guard let strongSelf = self else {
|
||||
return false
|
||||
@ -1596,7 +1596,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
let messages: [EnqueueMessage] = [.message(text: "", attributes: attributes, mediaReference: fileReference.abstract, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: correlationId)]
|
||||
let messages: [EnqueueMessage] = [.message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: fileReference.abstract, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: correlationId)]
|
||||
if silentPosting {
|
||||
let transformedMessages = strongSelf.transformEnqueueMessages(messages, silentPosting: silentPosting)
|
||||
strongSelf.sendMessages(transformedMessages)
|
||||
@ -1634,7 +1634,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}, nil)
|
||||
|
||||
var messages = [EnqueueMessage.message(text: "", attributes: [], mediaReference: fileReference.abstract, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil)]
|
||||
var messages = [EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: fileReference.abstract, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil)]
|
||||
if silentPosting {
|
||||
messages = strongSelf.transformEnqueueMessages(messages, silentPosting: true)
|
||||
strongSelf.sendMessages(messages)
|
||||
@ -1968,7 +1968,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|> deliverOnMainQueue).start(next: { coordinate in
|
||||
if let strongSelf = self {
|
||||
if let coordinate = coordinate {
|
||||
strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaMap(latitude: coordinate.latitude, longitude: coordinate.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)])
|
||||
strongSelf.sendMessages([.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaMap(latitude: coordinate.latitude, longitude: coordinate.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)])
|
||||
} else {
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {})]), in: .window(.root))
|
||||
}
|
||||
@ -1992,7 +1992,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let _ = (strongSelf.context.account.postbox.loadedPeerWithId(strongSelf.context.account.peerId)
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let peer = peer as? TelegramUser, let phone = peer.phone, !phone.isEmpty {
|
||||
strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)])
|
||||
strongSelf.sendMessages([.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)])
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -2034,7 +2034,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if !entities.isEmpty {
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
strongSelf.sendMessages([.message(text: command, attributes: attributes, mediaReference: nil, replyToMessageId: (postAsReply && messageId != nil) ? messageId! : nil, localGroupingKey: nil, correlationId: nil)])
|
||||
strongSelf.sendMessages([.message(text: command, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: (postAsReply && messageId != nil) ? messageId! : nil, localGroupingKey: nil, correlationId: nil)])
|
||||
}
|
||||
}, openInstantPage: { [weak self] message, associatedData in
|
||||
if let strongSelf = self, strongSelf.isNodeLoaded, let navigationController = strongSelf.effectiveNavigationController, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) {
|
||||
@ -2337,7 +2337,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ShareMenu_Send, color: .accent, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
if let strongSelf = self {
|
||||
strongSelf.sendMessages([.message(text: command, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)])
|
||||
strongSelf.sendMessages([.message(text: command, attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)])
|
||||
}
|
||||
}))
|
||||
}
|
||||
@ -6044,7 +6044,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
var correlationIds: [Int64] = []
|
||||
for message in messages {
|
||||
switch message {
|
||||
case let .message(_, _, _, _, _, correlationId):
|
||||
case let .message(_, _, _, _, _, _, correlationId):
|
||||
if let correlationId = correlationId {
|
||||
correlationIds.append(correlationId)
|
||||
}
|
||||
@ -7256,7 +7256,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if !entities.isEmpty {
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
strongSelf.sendMessages([.message(text: messageText, attributes: attributes, mediaReference: nil, replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)])
|
||||
strongSelf.sendMessages([.message(text: messageText, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)])
|
||||
strongSelf.interfaceInteraction?.updateShowCommands { _ in
|
||||
return false
|
||||
}
|
||||
@ -10635,7 +10635,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
private func editMessageMediaWithMessages(_ messages: [EnqueueMessage]) {
|
||||
if let message = messages.first, case let .message(text, attributes, maybeMediaReference, _, _, _) = message, let mediaReference = maybeMediaReference {
|
||||
if let message = messages.first, case let .message(text, attributes, _, maybeMediaReference, _, _, _) = message, let mediaReference = maybeMediaReference {
|
||||
self.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
|
||||
var entities: [MessageTextEntity] = []
|
||||
for attribute in attributes {
|
||||
@ -11011,7 +11011,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId else {
|
||||
return
|
||||
}
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: mediaReference, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: mediaReference, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: strongSelf.transformEnqueueMessages([message]))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
if let strongSelf = self, strongSelf.presentationInterfaceState.subject != .scheduledMessages {
|
||||
@ -11048,7 +11048,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: location), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: location), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)
|
||||
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
@ -11081,7 +11081,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if !entities.isEmpty {
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
textEnqueueMessage = .message(text: text.string, attributes: attributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
textEnqueueMessage = .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
}
|
||||
if peers.count > 1 {
|
||||
var enqueueMessages: [EnqueueMessage] = []
|
||||
@ -11118,7 +11118,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
}
|
||||
}, nil)
|
||||
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)
|
||||
let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)
|
||||
enqueueMessages.append(message)
|
||||
}
|
||||
}
|
||||
@ -11182,7 +11182,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let textEnqueueMessage = textEnqueueMessage {
|
||||
enqueueMessages.append(textEnqueueMessage)
|
||||
}
|
||||
enqueueMessages.append(.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil))
|
||||
enqueueMessages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil))
|
||||
strongSelf.sendMessages(strongSelf.transformEnqueueMessages(enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime))
|
||||
} else {
|
||||
let contactController = strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .filter(peer: peerAndContactData.0, contactId: nil, contactData: contactData, completion: { peer, contactData in
|
||||
@ -11205,7 +11205,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let textEnqueueMessage = textEnqueueMessage {
|
||||
enqueueMessages.append(textEnqueueMessage)
|
||||
}
|
||||
enqueueMessages.append(.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil))
|
||||
enqueueMessages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil))
|
||||
strongSelf.sendMessages(strongSelf.transformEnqueueMessages(enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime))
|
||||
}
|
||||
}), completed: nil, cancelled: nil)
|
||||
@ -11637,7 +11637,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: fileId), partialReference: nil, resource: ICloudFileResource(urlData: item.urlData, thumbnail: false), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int64(item.fileSize), attributes: attributes)
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: replyMessageId, localGroupingKey: groupingKey, correlationId: nil)
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: replyMessageId, localGroupingKey: groupingKey, correlationId: nil)
|
||||
messages.append(message)
|
||||
}
|
||||
if let _ = groupingKey, messages.count % 10 == 0 {
|
||||
@ -11963,7 +11963,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: location), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: location), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)
|
||||
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, {
|
||||
@ -12018,7 +12018,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
}
|
||||
}, nil)
|
||||
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)
|
||||
let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)
|
||||
enqueueMessages.append(message)
|
||||
}
|
||||
}
|
||||
@ -12077,7 +12077,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
}
|
||||
}, nil)
|
||||
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)
|
||||
let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)
|
||||
strongSelf.sendMessages([message])
|
||||
} else {
|
||||
let contactController = strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .filter(peer: peerAndContactData.0, contactId: nil, contactData: contactData, completion: { peer, contactData in
|
||||
@ -12095,7 +12095,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
}
|
||||
}, nil)
|
||||
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)
|
||||
let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)
|
||||
strongSelf.sendMessages([message])
|
||||
}
|
||||
}), completed: nil, cancelled: nil)
|
||||
@ -12420,6 +12420,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let message: EnqueueMessage = .message(
|
||||
text: "",
|
||||
attributes: [],
|
||||
inlineStickers: [:],
|
||||
mediaReference: .standalone(media: TelegramMediaPoll(
|
||||
pollId: MediaId(namespace: Namespaces.Media.LocalPoll, id: Int64.random(in: Int64.min ... Int64.max)),
|
||||
publicity: poll.publicity,
|
||||
@ -12539,7 +12540,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let value = value {
|
||||
self.present(UndoOverlayController(presentationData: self.presentationData, content: .dice(dice: dice, context: self.context, text: value, action: canSendMessagesToChat(self.presentationInterfaceState) ? self.presentationData.strings.Conversation_SendDice : nil), elevatedLayout: false, action: { [weak self] action in
|
||||
if let strongSelf = self, canSendMessagesToChat(strongSelf.presentationInterfaceState), action == .undo {
|
||||
strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: dice.emoji)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)])
|
||||
strongSelf.sendMessages([.message(text: "", attributes: [], inlineStickers: [:], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: dice.emoji)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)])
|
||||
}
|
||||
return false
|
||||
}), in: .current)
|
||||
@ -12562,9 +12563,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
if let defaultReplyMessageId = defaultReplyMessageId {
|
||||
switch message {
|
||||
case let .message(text, attributes, mediaReference, replyToMessageId, localGroupingKey, correlationId):
|
||||
case let .message(text, attributes, inlineStickers, mediaReference, replyToMessageId, localGroupingKey, correlationId):
|
||||
if replyToMessageId == nil {
|
||||
message = .message(text: text, attributes: attributes, mediaReference: mediaReference, replyToMessageId: defaultReplyMessageId, localGroupingKey: localGroupingKey, correlationId: correlationId)
|
||||
message = .message(text: text, attributes: attributes, inlineStickers: inlineStickers, mediaReference: mediaReference, replyToMessageId: defaultReplyMessageId, localGroupingKey: localGroupingKey, correlationId: correlationId)
|
||||
}
|
||||
case .forward:
|
||||
break
|
||||
@ -12837,7 +12838,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
fileAttributes.append(.ImageSize(size: PixelDimensions(size)))
|
||||
|
||||
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "image/webp", size: Int64(data.count), attributes: fileAttributes)
|
||||
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
let message = EnqueueMessage.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
|
||||
let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId
|
||||
strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({
|
||||
@ -13100,7 +13101,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}, usedCorrelationId ? correlationId : nil)
|
||||
|
||||
strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int64(data.compressedData.count), attributes: [.Audio(isVoice: true, duration: Int(data.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: correlationId)])
|
||||
strongSelf.sendMessages([.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int64(data.compressedData.count), attributes: [.Audio(isVoice: true, duration: Int(data.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: correlationId)])
|
||||
|
||||
strongSelf.recorderFeedback?.tap()
|
||||
strongSelf.recorderFeedback = nil
|
||||
@ -13195,7 +13196,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}, nil)
|
||||
|
||||
let messages: [EnqueueMessage] = [.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: recordedMediaPreview.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int64(recordedMediaPreview.fileSize), attributes: [.Audio(isVoice: true, duration: Int(recordedMediaPreview.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: self.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil)]
|
||||
let messages: [EnqueueMessage] = [.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: recordedMediaPreview.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int64(recordedMediaPreview.fileSize), attributes: [.Audio(isVoice: true, duration: Int(recordedMediaPreview.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: self.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil)]
|
||||
|
||||
let transformedMessages: [EnqueueMessage]
|
||||
if let silentPosting = silentPosting {
|
||||
@ -14098,7 +14099,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if !entities.isEmpty {
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
result.append(.message(text: text.string, attributes: attributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
result.append(.message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14589,7 +14590,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
Queue.mainQueue().async {
|
||||
unblockingPeer.set(false)
|
||||
if let strongSelf = self, restartBot {
|
||||
let _ = enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: [.message(text: "/start", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]).start()
|
||||
let _ = enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: [.message(text: "/start", attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]).start()
|
||||
}
|
||||
}
|
||||
})).start())
|
||||
|
@ -918,7 +918,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
wasDraggingKeyboard = true
|
||||
}
|
||||
var wasDraggingInputNode = false
|
||||
if let derivedLayoutState = self.derivedLayoutState, let inputNodeHeight = derivedLayoutState.inputNodeHeight, !inputNodeHeight.isZero, let upperInputPositionBound = derivedLayoutState.upperInputPositionBound {
|
||||
if let derivedLayoutState = self.derivedLayoutState, let inputNodeHeight = derivedLayoutState.inputNodeHeight, !inputNodeHeight.isZero, let upperInputPositionBound = derivedLayoutState.upperInputPositionBound {
|
||||
let normalizedHeight = max(0.0, layout.size.height - upperInputPositionBound)
|
||||
if normalizedHeight < inputNodeHeight {
|
||||
wasDraggingInputNode = true
|
||||
@ -964,7 +964,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
var titleAccessoryPanelHeight: CGFloat?
|
||||
var titleAccessoryPanelBackgroundHeight: CGFloat?
|
||||
var extraTransition = transition
|
||||
if let titleAccessoryPanelNode = titlePanelForChatPresentationInterfaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.titleAccessoryPanelNode, interfaceInteraction: self.interfaceInteraction) {
|
||||
if let titleAccessoryPanelNode = titlePanelForChatPresentationInterfaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.titleAccessoryPanelNode, controllerInteraction: self.controllerInteraction, interfaceInteraction: self.interfaceInteraction) {
|
||||
if self.titleAccessoryPanelNode != titleAccessoryPanelNode {
|
||||
dismissedTitleAccessoryPanelNode = self.titleAccessoryPanelNode
|
||||
self.titleAccessoryPanelNode = titleAccessoryPanelNode
|
||||
@ -1113,7 +1113,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
var accessoryPanelSize: CGSize?
|
||||
var immediatelyLayoutAccessoryPanelAndAnimateAppearance = false
|
||||
if let accessoryPanelNode = accessoryPanelForChatPresentationIntefaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.accessoryPanelNode, interfaceInteraction: self.interfaceInteraction) {
|
||||
if let accessoryPanelNode = accessoryPanelForChatPresentationIntefaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.accessoryPanelNode, chatControllerInteraction: self.controllerInteraction, interfaceInteraction: self.interfaceInteraction) {
|
||||
accessoryPanelSize = accessoryPanelNode.measure(CGSize(width: layout.size.width, height: layout.size.height))
|
||||
|
||||
accessoryPanelNode.updateState(size: layout.size, inset: layout.safeInsets.left, interfaceState: self.chatPresentationInterfaceState)
|
||||
@ -2679,7 +2679,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
let keyboardGestureBeginLocation = location
|
||||
let accessoryHeight = self.getWindowInputAccessoryHeight()
|
||||
if let inputHeight = derivedLayoutState.inputNodeHeight, !inputHeight.isZero, keyboardGestureBeginLocation.y < validLayout.size.height - inputHeight - accessoryHeight {
|
||||
if let inputHeight = derivedLayoutState.inputNodeHeight, !inputHeight.isZero, keyboardGestureBeginLocation.y < validLayout.size.height - inputHeight - accessoryHeight, !self.inputPanelContainerNode.stableIsExpanded {
|
||||
var enableGesture = true
|
||||
if let view = self.view.hitTest(location, with: nil) {
|
||||
if doesViewTreeDisableInteractiveTransitionGestureRecognizer(view) {
|
||||
@ -2820,7 +2820,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
let trimmedInputText = effectiveInputText.string.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let peerId = effectivePresentationInterfaceState.chatLocation.peerId
|
||||
if peerId?.namespace != Namespaces.Peer.SecretChat, let interactiveEmojis = self.interactiveEmojis, interactiveEmojis.emojis.contains(trimmedInputText) {
|
||||
messages.append(.message(text: "", attributes: [], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: trimmedInputText)), replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil))
|
||||
messages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: trimmedInputText)), replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil))
|
||||
} else {
|
||||
let inputText = convertMarkdownToAttributes(effectiveInputText)
|
||||
|
||||
@ -2838,14 +2838,14 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
webpage = self.chatPresentationInterfaceState.urlPreview?.1
|
||||
}
|
||||
|
||||
messages.append(.message(text: text.string, attributes: attributes, mediaReference: webpage.flatMap(AnyMediaReference.standalone), replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil))
|
||||
messages.append(.message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: webpage.flatMap(AnyMediaReference.standalone), replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil))
|
||||
|
||||
#if DEBUG
|
||||
if text.string == "sleep" {
|
||||
messages.removeAll()
|
||||
|
||||
for i in 0 ..< 5 {
|
||||
messages.append(.message(text: "sleep\(i)", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
messages.append(.message(text: "sleep\(i)", attributes: [], inlineStickers: [:], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -18,18 +18,21 @@ import PagerComponent
|
||||
|
||||
final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
struct InputData: Equatable {
|
||||
let emoji: EmojiPagerContentComponent
|
||||
let stickers: EmojiPagerContentComponent
|
||||
let gifs: GifPagerContentComponent
|
||||
var emoji: EmojiPagerContentComponent
|
||||
var stickers: EmojiPagerContentComponent
|
||||
var gifs: GifPagerContentComponent
|
||||
var availableGifSearchEmojies: [EntityKeyboardComponent.GifSearchEmoji]
|
||||
|
||||
init(
|
||||
emoji: EmojiPagerContentComponent,
|
||||
stickers: EmojiPagerContentComponent,
|
||||
gifs: GifPagerContentComponent
|
||||
gifs: GifPagerContentComponent,
|
||||
availableGifSearchEmojies: [EntityKeyboardComponent.GifSearchEmoji]
|
||||
) {
|
||||
self.emoji = emoji
|
||||
self.stickers = stickers
|
||||
self.gifs = gifs
|
||||
self.availableGifSearchEmojies = availableGifSearchEmojies
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,7 +52,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
case let .Sticker(displayText, packReference, _):
|
||||
text = displayText
|
||||
if let packReference = packReference {
|
||||
emojiAttribute = ChatTextInputTextCustomEmojiAttribute(stickerPack: packReference, fileId: item.file.fileId.id)
|
||||
emojiAttribute = ChatTextInputTextCustomEmojiAttribute(stickerPack: packReference, fileId: item.file.fileId.id, file: item.file)
|
||||
break loop
|
||||
}
|
||||
default:
|
||||
@ -419,7 +422,39 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
)
|
||||
}
|
||||
|
||||
let gifItems: Signal<GifPagerContentComponent, NoError> = context.engine.data.subscribe(TelegramEngine.EngineData.Item.OrderedLists.ListItems(collectionId: Namespaces.OrderedItemList.CloudRecentGifs))
|
||||
let reactions: Signal<[String], NoError> = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.App())
|
||||
|> map { appConfiguration -> [String] in
|
||||
let defaultReactions: [String] = ["👍", "👎", "😍", "😂", "😯", "😕", "😢", "😡", "💪", "👏", "🙈", "😒"]
|
||||
|
||||
guard let data = appConfiguration.data, let emojis = data["gif_search_emojies"] as? [String] else {
|
||||
return defaultReactions
|
||||
}
|
||||
return emojis
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|
||||
let animatedEmojiStickers = context.engine.stickers.loadedStickerPack(reference: .animatedEmoji, forceActualized: false)
|
||||
|> map { animatedEmoji -> [String: [StickerPackItem]] in
|
||||
var animatedEmojiStickers: [String: [StickerPackItem]] = [:]
|
||||
switch animatedEmoji {
|
||||
case let .result(_, items, _):
|
||||
for item in items {
|
||||
if let emoji = item.getStringRepresentationsOfIndexKeys().first {
|
||||
animatedEmojiStickers[emoji.basicEmoji.0] = [item]
|
||||
let strippedEmoji = emoji.basicEmoji.0.strippedEmoji
|
||||
if animatedEmojiStickers[strippedEmoji] == nil {
|
||||
animatedEmojiStickers[strippedEmoji] = [item]
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
return animatedEmojiStickers
|
||||
}
|
||||
|
||||
// We are intentionally not subscribing to the recent gif updates here
|
||||
let gifItems: Signal<GifPagerContentComponent, NoError> = context.engine.data.get(TelegramEngine.EngineData.Item.OrderedLists.ListItems(collectionId: Namespaces.OrderedItemList.CloudRecentGifs))
|
||||
|> map { savedGifs -> GifPagerContentComponent in
|
||||
var items: [GifPagerContentComponent.Item] = []
|
||||
for gifItem in savedGifs {
|
||||
@ -430,6 +465,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
return GifPagerContentComponent(
|
||||
context: context,
|
||||
inputInteraction: gifInputInteraction,
|
||||
subject: .recent,
|
||||
items: items
|
||||
)
|
||||
}
|
||||
@ -437,13 +473,23 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
return combineLatest(queue: .mainQueue(),
|
||||
emojiItems,
|
||||
stickerItems,
|
||||
gifItems
|
||||
gifItems,
|
||||
reactions,
|
||||
animatedEmojiStickers
|
||||
)
|
||||
|> map { emoji, stickers, gifs -> InputData in
|
||||
|> map { emoji, stickers, gifs, reactions, animatedEmojiStickers -> InputData in
|
||||
var availableGifSearchEmojies: [EntityKeyboardComponent.GifSearchEmoji] = []
|
||||
for reaction in reactions {
|
||||
if let file = animatedEmojiStickers[reaction]?.first?.file {
|
||||
availableGifSearchEmojies.append(EntityKeyboardComponent.GifSearchEmoji(emoji: reaction, file: file, title: reaction))
|
||||
}
|
||||
}
|
||||
|
||||
return InputData(
|
||||
emoji: emoji,
|
||||
stickers: stickers,
|
||||
gifs: gifs
|
||||
gifs: gifs,
|
||||
availableGifSearchEmojies: availableGifSearchEmojies
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -470,6 +516,13 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
|
||||
private var currentState: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, interfaceState: ChatPresentationInterfaceState, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool)?
|
||||
|
||||
private var gifMode: GifPagerContentComponent.Subject = .recent {
|
||||
didSet {
|
||||
self.gifModeSubject.set(self.gifMode)
|
||||
}
|
||||
}
|
||||
private let gifModeSubject: ValuePromise<GifPagerContentComponent.Subject>
|
||||
|
||||
init(context: AccountContext, currentInputData: InputData, updatedInputData: Signal<InputData, NoError>, defaultToEmojiTab: Bool, controllerInteraction: ChatControllerInteraction) {
|
||||
self.context = context
|
||||
self.currentInputData = currentInputData
|
||||
@ -479,17 +532,25 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
|
||||
self.entityKeyboardView = ComponentHostView<Empty>()
|
||||
|
||||
self.gifModeSubject = ValuePromise<GifPagerContentComponent.Subject>(self.gifMode, ignoreRepeated: true)
|
||||
|
||||
super.init()
|
||||
|
||||
self.view.addSubview(self.entityKeyboardView)
|
||||
|
||||
self.externalTopPanelContainerImpl = PagerExternalTopPanelContainer()
|
||||
|
||||
self.inputDataDisposable = (updatedInputData
|
||||
|> deliverOnMainQueue).start(next: { [weak self] inputData in
|
||||
self.inputDataDisposable = (combineLatest(queue: .mainQueue(),
|
||||
updatedInputData,
|
||||
self.updatedGifs()
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] inputData, gifs in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
var inputData = inputData
|
||||
inputData.gifs = gifs
|
||||
|
||||
strongSelf.currentInputData = inputData
|
||||
strongSelf.performLayout()
|
||||
})
|
||||
@ -531,6 +592,92 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
self.inputDataDisposable?.dispose()
|
||||
}
|
||||
|
||||
private func updatedGifs() -> Signal<GifPagerContentComponent, NoError> {
|
||||
let gifInputInteraction = GifPagerContentComponent.InputInteraction(
|
||||
performItemAction: { [weak controllerInteraction] item, view, rect in
|
||||
guard let controllerInteraction = controllerInteraction else {
|
||||
return
|
||||
}
|
||||
let _ = controllerInteraction.sendGif(.savedGif(media: item.file), view, rect, false, false)
|
||||
}
|
||||
)
|
||||
|
||||
let context = self.context
|
||||
let trendingGifs = self.trendingGifsPromise.get()
|
||||
let updatedGifs = self.gifModeSubject.get()
|
||||
|> mapToSignal { subject -> Signal<GifPagerContentComponent, NoError> in
|
||||
switch subject {
|
||||
case .recent:
|
||||
let gifItems: Signal<GifPagerContentComponent, NoError> = context.engine.data.subscribe(TelegramEngine.EngineData.Item.OrderedLists.ListItems(collectionId: Namespaces.OrderedItemList.CloudRecentGifs))
|
||||
|> map { savedGifs -> GifPagerContentComponent in
|
||||
var items: [GifPagerContentComponent.Item] = []
|
||||
for gifItem in savedGifs {
|
||||
items.append(GifPagerContentComponent.Item(
|
||||
file: gifItem.contents.get(RecentMediaItem.self)!.media
|
||||
))
|
||||
}
|
||||
return GifPagerContentComponent(
|
||||
context: context,
|
||||
inputInteraction: gifInputInteraction,
|
||||
subject: subject,
|
||||
items: items
|
||||
)
|
||||
}
|
||||
return gifItems
|
||||
case .trending:
|
||||
return trendingGifs
|
||||
|> map { trendingGifs -> GifPagerContentComponent in
|
||||
var items: [GifPagerContentComponent.Item] = []
|
||||
|
||||
if let trendingGifs = trendingGifs {
|
||||
for file in trendingGifs.files {
|
||||
items.append(GifPagerContentComponent.Item(
|
||||
file: file.file.media
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return GifPagerContentComponent(
|
||||
context: context,
|
||||
inputInteraction: gifInputInteraction,
|
||||
subject: subject,
|
||||
items: items
|
||||
)
|
||||
}
|
||||
case let .emojiSearch(query):
|
||||
return paneGifSearchForQuery(context: context, query: query, offset: nil, incompleteResults: true, staleCachedResults: true, delayRequest: false, updateActivity: nil)
|
||||
|> map { result -> GifPagerContentComponent in
|
||||
var items: [GifPagerContentComponent.Item] = []
|
||||
|
||||
/*let canLoadMore: Bool
|
||||
if let result = result {
|
||||
canLoadMore = !result.isComplete
|
||||
} else {
|
||||
canLoadMore = true
|
||||
}*/
|
||||
|
||||
if let result = result {
|
||||
for file in result.files {
|
||||
items.append(GifPagerContentComponent.Item(
|
||||
file: file.file.media
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return GifPagerContentComponent(
|
||||
context: context,
|
||||
inputInteraction: gifInputInteraction,
|
||||
subject: subject,
|
||||
items: items
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return .single(self.currentInputData.gifs)
|
||||
|> then(updatedGifs)
|
||||
}
|
||||
|
||||
func markInputCollapsed() {
|
||||
self.isMarkInputCollapsed = true
|
||||
}
|
||||
@ -574,6 +721,7 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
emojiContent: self.currentInputData.emoji,
|
||||
stickerContent: self.currentInputData.stickers,
|
||||
gifContent: self.currentInputData.gifs,
|
||||
availableGifSearchEmojies: self.currentInputData.availableGifSearchEmojies,
|
||||
defaultToEmojiTab: self.defaultToEmojiTab,
|
||||
externalTopPanelContainer: self.externalTopPanelContainerImpl,
|
||||
topPanelExtensionUpdated: { [weak self] topPanelExtension, transition in
|
||||
@ -603,6 +751,12 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
return .text
|
||||
}
|
||||
},
|
||||
switchToGifSubject: { [weak self] subject in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.gifModeSubject.set(subject)
|
||||
},
|
||||
makeSearchContainerNode: { content in
|
||||
let mappedMode: ChatMediaInputSearchMode
|
||||
switch content {
|
||||
|
@ -71,7 +71,8 @@ func chatHistoryEntriesForView(
|
||||
media: [TelegramMediaAction(action: .joinedByRequest)],
|
||||
peers: SimpleDictionary<PeerId, Peer>(),
|
||||
associatedMessages: SimpleDictionary<MessageId, Message>(),
|
||||
associatedMessageIds: []
|
||||
associatedMessageIds: [],
|
||||
associatedMedia: [:]
|
||||
)
|
||||
}
|
||||
|
||||
@ -323,7 +324,8 @@ func chatHistoryEntriesForView(
|
||||
media: message.media,
|
||||
peers: message.peers,
|
||||
associatedMessages: message.associatedMessages,
|
||||
associatedMessageIds: message.associatedMessageIds
|
||||
associatedMessageIds: message.associatedMessageIds,
|
||||
associatedMedia: message.associatedMedia
|
||||
)
|
||||
nextAdMessageId += 1
|
||||
entries.append(.MessageEntry(updatedMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false)))
|
||||
|
@ -272,8 +272,17 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte
|
||||
case .inputButtons:
|
||||
return ChatTextInputPanelState(accessoryItems: [.keyboard], contextPlaceholder: contextPlaceholder, mediaRecordingState: chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState)
|
||||
case .none, .text:
|
||||
if let _ = chatPresentationInterfaceState.interfaceState.editMessage {
|
||||
return ChatTextInputPanelState(accessoryItems: [], contextPlaceholder: contextPlaceholder, mediaRecordingState: chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState)
|
||||
if let editMessage = chatPresentationInterfaceState.interfaceState.editMessage {
|
||||
let isTextEmpty = editMessage.inputState.inputText.length == 0
|
||||
|
||||
let stickersAreEmoji = !isTextEmpty
|
||||
|
||||
var stickersEnabled = true
|
||||
stickersEnabled = true
|
||||
|
||||
accessoryItems.append(.stickers(isEnabled: stickersEnabled, isEmoji: stickersAreEmoji))
|
||||
|
||||
return ChatTextInputPanelState(accessoryItems: accessoryItems, contextPlaceholder: contextPlaceholder, mediaRecordingState: chatPresentationInterfaceState.inputTextPanelState.mediaRecordingState)
|
||||
} else {
|
||||
var accessoryItems: [ChatTextInputAccessoryItem] = []
|
||||
var extendedSearchLayout = false
|
||||
|
@ -5,7 +5,7 @@ import TelegramCore
|
||||
import AccountContext
|
||||
import ChatPresentationInterfaceState
|
||||
|
||||
func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: AccessoryPanelNode?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> AccessoryPanelNode? {
|
||||
func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: AccessoryPanelNode?, chatControllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> AccessoryPanelNode? {
|
||||
if let _ = chatPresentationInterfaceState.interfaceState.selectionState {
|
||||
return nil
|
||||
}
|
||||
@ -70,7 +70,7 @@ func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceS
|
||||
replyPanelNode.updateThemeAndStrings(theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings)
|
||||
return replyPanelNode
|
||||
} else {
|
||||
let panelNode = ReplyAccessoryPanelNode(context: context, messageId: replyMessageId, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, nameDisplayOrder: chatPresentationInterfaceState.nameDisplayOrder, dateTimeFormat: chatPresentationInterfaceState.dateTimeFormat)
|
||||
let panelNode = ReplyAccessoryPanelNode(context: context, messageId: replyMessageId, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, nameDisplayOrder: chatPresentationInterfaceState.nameDisplayOrder, dateTimeFormat: chatPresentationInterfaceState.dateTimeFormat, animationCache: chatControllerInteraction?.presentationContext.animationCache, animationRenderer: chatControllerInteraction?.presentationContext.animationRenderer)
|
||||
panelNode.interfaceInteraction = interfaceInteraction
|
||||
return panelNode
|
||||
}
|
||||
|
@ -691,7 +691,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
|
||||
let id = Int64.random(in: Int64.min ... Int64.max)
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: logPath, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: "CallStats.log")])
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)
|
||||
|
||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import TelegramCore
|
||||
import AccountContext
|
||||
import ChatPresentationInterfaceState
|
||||
|
||||
func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatTitleAccessoryPanelNode?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> ChatTitleAccessoryPanelNode? {
|
||||
func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatTitleAccessoryPanelNode?, controllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?) -> ChatTitleAccessoryPanelNode? {
|
||||
if case .overlay = chatPresentationInterfaceState.mode {
|
||||
return nil
|
||||
}
|
||||
@ -90,7 +90,7 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat
|
||||
if let currentPanel = currentPanel as? ChatPinnedMessageTitlePanelNode {
|
||||
return currentPanel
|
||||
} else {
|
||||
let panel = ChatPinnedMessageTitlePanelNode(context: context)
|
||||
let panel = ChatPinnedMessageTitlePanelNode(context: context, animationCache: controllerInteraction?.presentationContext.animationCache, animationRenderer: controllerInteraction?.presentationContext.animationRenderer)
|
||||
panel.interfaceInteraction = interfaceInteraction
|
||||
return panel
|
||||
}
|
||||
|
@ -1455,7 +1455,7 @@ final class ChatMediaInputNode: ChatInputNode {
|
||||
}
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: PeerId(0), namespace: Namespaces.Message.Local, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [file.file.media], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [])
|
||||
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: PeerId(0), namespace: Namespaces.Message.Local, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: [file.file.media], peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:])
|
||||
|
||||
let gallery = GalleryController(context: strongSelf.context, source: .standaloneMessage(message), streamSingleVideo: true, replaceRootController: { _, _ in
|
||||
}, baseNavigationController: nil)
|
||||
|
@ -18,13 +18,14 @@ import TelegramUniversalVideoContent
|
||||
import GalleryUI
|
||||
import WallpaperBackgroundNode
|
||||
import InvisibleInkDustNode
|
||||
import TextNodeWithEntities
|
||||
|
||||
private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: Message, accountPeerId: PeerId) -> NSAttributedString? {
|
||||
return universalServiceMessageString(presentationData: (theme.theme, theme.wallpaper), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: EngineMessage(message), accountPeerId: accountPeerId, forChatList: false)
|
||||
}
|
||||
|
||||
class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
let labelNode: TextNode
|
||||
let labelNode: TextNodeWithEntities
|
||||
private var dustNode: InvisibleInkDustNode?
|
||||
var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||
var backgroundColorNode: ASDisplayNode
|
||||
@ -42,9 +43,9 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
private var absoluteRect: (CGRect, CGSize)?
|
||||
|
||||
required init() {
|
||||
self.labelNode = TextNode()
|
||||
self.labelNode.isUserInteractionEnabled = false
|
||||
self.labelNode.displaysAsynchronously = false
|
||||
self.labelNode = TextNodeWithEntities()
|
||||
self.labelNode.textNode.isUserInteractionEnabled = false
|
||||
self.labelNode.textNode.displaysAsynchronously = false
|
||||
|
||||
self.backgroundColorNode = ASDisplayNode()
|
||||
self.backgroundMaskNode = ASImageNode()
|
||||
@ -55,7 +56,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.labelNode)
|
||||
self.addSubnode(self.labelNode.textNode)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
@ -130,7 +131,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
|
||||
override func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (ChatMessageBubbleContentProperties, unboundSize: CGSize?, maxWidth: CGFloat, layout: (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) {
|
||||
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
||||
let makeLabelLayout = TextNodeWithEntities.asyncLayout(self.labelNode)
|
||||
|
||||
let cachedMaskBackgroundImage = self.cachedMaskBackgroundImage
|
||||
|
||||
@ -273,10 +274,15 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
videoNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
let _ = apply()
|
||||
let _ = apply(TextNodeWithEntities.Arguments(
|
||||
context: item.context,
|
||||
cache: item.controllerInteraction.presentationContext.animationCache,
|
||||
renderer: item.controllerInteraction.presentationContext.animationRenderer,
|
||||
placeholderColor: item.presentationData.theme.theme.chat.message.freeform.withWallpaper.reactionInactiveBackground
|
||||
))
|
||||
|
||||
let labelFrame = CGRect(origin: CGPoint(x: 8.0, y: image != nil ? 2 : floorToScreenPixels((backgroundSize.height - labelLayout.size.height) / 2.0) - 1.0), size: labelLayout.size)
|
||||
strongSelf.labelNode.frame = labelFrame
|
||||
strongSelf.labelNode.textNode.frame = labelFrame
|
||||
strongSelf.backgroundColorNode.backgroundColor = selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)
|
||||
|
||||
if !labelLayout.spoilers.isEmpty {
|
||||
@ -289,7 +295,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
dustNode = InvisibleInkDustNode(textNode: nil)
|
||||
dustNode.isUserInteractionEnabled = false
|
||||
strongSelf.dustNode = dustNode
|
||||
strongSelf.insertSubnode(dustNode, aboveSubnode: strongSelf.labelNode)
|
||||
strongSelf.insertSubnode(dustNode, aboveSubnode: strongSelf.labelNode.textNode)
|
||||
}
|
||||
dustNode.frame = labelFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 1.0)
|
||||
dustNode.update(size: dustNode.frame.size, color: dustColor, textColor: dustColor, rects: labelLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: labelLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
|
||||
@ -367,9 +373,9 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
override func updateTouchesAtPoint(_ point: CGPoint?) {
|
||||
if let item = self.item {
|
||||
var rects: [(CGRect, CGRect)]?
|
||||
let textNodeFrame = self.labelNode.frame
|
||||
let textNodeFrame = self.labelNode.textNode.frame
|
||||
if let point = point {
|
||||
if let (index, attributes) = self.labelNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY - 10.0)) {
|
||||
if let (index, attributes) = self.labelNode.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY - 10.0)) {
|
||||
let possibleNames: [String] = [
|
||||
TelegramTextAttributes.URL,
|
||||
TelegramTextAttributes.PeerMention,
|
||||
@ -379,7 +385,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
]
|
||||
for name in possibleNames {
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: name)] {
|
||||
rects = self.labelNode.lineAndAttributeRects(name: name, at: index)
|
||||
rects = self.labelNode.textNode.lineAndAttributeRects(name: name, at: index)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -403,9 +409,9 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
linkHighlightingNode = LinkHighlightingNode(color: serviceColor.linkHighlight)
|
||||
linkHighlightingNode.inset = 2.5
|
||||
self.linkHighlightingNode = linkHighlightingNode
|
||||
self.insertSubnode(linkHighlightingNode, belowSubnode: self.labelNode)
|
||||
self.insertSubnode(linkHighlightingNode, belowSubnode: self.labelNode.textNode)
|
||||
}
|
||||
linkHighlightingNode.frame = self.labelNode.frame.offsetBy(dx: 0.0, dy: 1.5)
|
||||
linkHighlightingNode.frame = self.labelNode.textNode.frame.offsetBy(dx: 0.0, dy: 1.5)
|
||||
linkHighlightingNode.updateRects(mappedRects)
|
||||
} else if let linkHighlightingNode = self.linkHighlightingNode {
|
||||
self.linkHighlightingNode = nil
|
||||
@ -417,11 +423,11 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
|
||||
override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction {
|
||||
let textNodeFrame = self.labelNode.frame
|
||||
if let (index, attributes) = self.labelNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY - 10.0)), gesture == .tap {
|
||||
let textNodeFrame = self.labelNode.textNode.frame
|
||||
if let (index, attributes) = self.labelNode.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY - 10.0)), gesture == .tap {
|
||||
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||
var concealed = true
|
||||
if let (attributeText, fullText) = self.labelNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) {
|
||||
if let (attributeText, fullText) = self.labelNode.textNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) {
|
||||
concealed = !doesUrlMatchText(url: url, text: attributeText, fullText: fullText)
|
||||
}
|
||||
return .url(url: url, concealed: concealed)
|
||||
|
@ -1017,7 +1017,17 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId {
|
||||
} else {
|
||||
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, item.message, CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
replyInfoApply = makeReplyInfoLayout(ChatMessageReplyInfoNode.Arguments(
|
||||
presentationData: item.presentationData,
|
||||
strings: item.presentationData.strings,
|
||||
context: item.context,
|
||||
type: .standalone,
|
||||
message: replyMessage,
|
||||
parentMessage: item.message,
|
||||
constrainedSize: CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude),
|
||||
animationCache: item.controllerInteraction.presentationContext.animationCache,
|
||||
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
|
||||
))
|
||||
}
|
||||
} else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty {
|
||||
replyMarkup = attribute
|
||||
|
@ -393,7 +393,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
string.append(NSAttributedString(string: "\n", font: textFont, textColor: messageTheme.primaryTextColor))
|
||||
}
|
||||
if let entities = entities {
|
||||
string.append(stringWithAppliedEntities(text, entities: entities, baseColor: messageTheme.primaryTextColor, linkColor: messageTheme.linkTextColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textBlockQuoteFont))
|
||||
string.append(stringWithAppliedEntities(text, entities: entities, baseColor: messageTheme.primaryTextColor, linkColor: messageTheme.linkTextColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textBlockQuoteFont, message: nil))
|
||||
} else {
|
||||
string.append(NSAttributedString(string: text + "\n", font: textFont, textColor: messageTheme.primaryTextColor))
|
||||
}
|
||||
|
@ -530,13 +530,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
contentNode.visibility = mapVisibility(self.visibility, boundsSize: self.bounds.size, insets: self.insets, to: contentNode)
|
||||
}
|
||||
|
||||
/*switch self.visibility {
|
||||
case let .visible(_, subRect):
|
||||
let topEdge = self.bounds.height - self.insets.top - (subRect.origin.y + subRect.height)
|
||||
self.debugNode.frame = CGRect(origin: CGPoint(x: 0.0, y: topEdge), size: CGSize(width: 100.0, height: 2.0))
|
||||
case .none:
|
||||
break
|
||||
}*/
|
||||
if let replyInfoNode = self.replyInfoNode {
|
||||
replyInfoNode.visibility = self.visibility != .none
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1039,7 +1035,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
authorNameLayout: (TextNodeLayoutArguments) -> (TextNodeLayout, () -> TextNode),
|
||||
adminBadgeLayout: (TextNodeLayoutArguments) -> (TextNodeLayout, () -> TextNode),
|
||||
forwardInfoLayout: (ChatPresentationData, PresentationStrings, ChatMessageForwardInfoType, Peer?, String?, String?, CGSize) -> (CGSize, (CGFloat) -> ChatMessageForwardInfoNode),
|
||||
replyInfoLayout: (ChatPresentationData, PresentationStrings, AccountContext, ChatMessageReplyInfoType, Message, Message, CGSize) -> (CGSize, () -> ChatMessageReplyInfoNode),
|
||||
replyInfoLayout: (ChatMessageReplyInfoNode.Arguments) -> (CGSize, () -> ChatMessageReplyInfoNode),
|
||||
actionButtonsLayout: (AccountContext, ChatPresentationThemeData, PresentationChatBubbleCorners, PresentationStrings, ReplyMarkupMessageAttribute, Message, CGFloat) -> (minWidth: CGFloat, layout: (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation) -> ChatMessageActionButtonsNode)),
|
||||
reactionButtonsLayout: (ChatMessageReactionButtonsNode.Arguments) -> (minWidth: CGFloat, layout: (CGFloat) -> (size: CGSize, apply: (ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode)),
|
||||
mosaicStatusLayout: (ChatMessageDateAndStatusNode.Arguments) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation) -> ChatMessageDateAndStatusNode)),
|
||||
@ -1802,7 +1798,17 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
} else {
|
||||
headerSize.height += 2.0
|
||||
}
|
||||
let sizeAndApply = replyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .bubble(incoming: incoming), replyMessage, item.message, CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude))
|
||||
let sizeAndApply = replyInfoLayout(ChatMessageReplyInfoNode.Arguments(
|
||||
presentationData: item.presentationData,
|
||||
strings: item.presentationData.strings,
|
||||
context: item.context,
|
||||
type: .bubble(incoming: incoming),
|
||||
message: replyMessage,
|
||||
parentMessage: item.message,
|
||||
constrainedSize: CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude),
|
||||
animationCache: item.controllerInteraction.presentationContext.animationCache,
|
||||
animationRenderer: item.controllerInteraction.presentationContext.animationRenderer
|
||||
))
|
||||
replyInfoSizeApply = (sizeAndApply.0, { sizeAndApply.1() })
|
||||
|
||||
replyInfoOriginY = headerSize.height
|
||||
@ -2489,6 +2495,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
if replyInfoNode.supernode == nil {
|
||||
strongSelf.clippingNode.addSubnode(replyInfoNode)
|
||||
animateFrame = false
|
||||
|
||||
replyInfoNode.visibility = strongSelf.visibility != .none
|
||||
}
|
||||
let previousReplyInfoNodeFrame = replyInfoNode.frame
|
||||
replyInfoNode.frame = CGRect(origin: CGPoint(x: contentOrigin.x + layoutConstants.text.bubbleInsets.left, y: layoutConstants.bubble.contentInsets.top + replyInfoOriginY), size: replyInfoSizeApply.0)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user