From 3ce113465d5b0e65e7db8c7eab14ce03bf7a8c8a Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 25 Aug 2020 17:35:21 +0100 Subject: [PATCH] Initial reply stats implementation --- Telegram/NotificationService/Serialization.m | 2 +- .../Sources/PhotoResources.swift | 8 +- .../Postbox/Sources/MessageHistoryView.swift | 12 +- .../Sources/MessageHistoryViewState.swift | 8 +- .../Sources/MessageOfInterestHolesView.swift | 8 +- submodules/Postbox/Sources/Postbox.swift | 14 +- .../BubbleSettingsController.swift | 2 +- .../SettingsUI/Sources/DebugController.swift | 1 + .../TextSizeSelectionController.swift | 2 +- .../ThemeAccentColorControllerNode.swift | 2 +- .../Themes/ThemePreviewControllerNode.swift | 2 +- .../Themes/ThemeSettingsChatPreviewItem.swift | 2 +- .../Sources/ReplyMessageAttribute.swift | 32 +++- .../Sources/ReplyThreadMessageAttribute.swift | 18 ++ submodules/TelegramApi/Sources/Api0.swift | 4 +- submodules/TelegramApi/Sources/Api1.swift | 102 ++++++----- submodules/TelegramApi/Sources/Api3.swift | 159 ++++++++++-------- .../TelegramCore/Sources/AccountManager.swift | 1 + .../Sources/AccountStateManagementUtils.swift | 33 +++- .../Sources/AccountViewTracker.swift | 7 +- .../Sources/ApplyUpdateMessage.swift | 17 +- .../TelegramCore/Sources/DeleteMessages.swift | 20 ++- .../TelegramCore/Sources/EnqueueMessage.swift | 6 +- ...ecretChatIncomingDecryptedOperations.swift | 6 +- .../Sources/ReplyThreadHistory.swift | 31 ++++ .../TelegramCore/Sources/Serialization.swift | 2 +- .../Sources/StoreMessage_Telegram.swift | 18 +- .../Sources/UpdateMessageMedia.swift | 20 +++ .../Sources/UpdateMessageService.swift | 4 +- .../PresentationThemeEssentialGraphics.swift | 16 ++ .../Message/ReplyCount.imageset/Contents.json | 12 ++ .../Message/ReplyCount.imageset/replies.pdf | Bin 0 -> 4117 bytes .../ChatMessageAnimatedStickerItemNode.swift | 5 +- .../ChatMessageAttachedContentNode.swift | 5 +- .../Sources/ChatMessageBubbleItemNode.swift | 7 +- .../ChatMessageContactBubbleContentNode.swift | 5 +- .../ChatMessageDateAndStatusNode.swift | 110 +++++++++++- .../ChatMessageInteractiveFileNode.swift | 5 +- ...atMessageInteractiveInstantVideoNode.swift | 5 +- .../ChatMessageMapBubbleContentNode.swift | 5 +- .../ChatMessageMediaBubbleContentNode.swift | 5 +- .../ChatMessagePollBubbleContentNode.swift | 5 +- ...atMessageRestrictedBubbleContentNode.swift | 5 +- .../Sources/ChatMessageStickerItemNode.swift | 5 +- .../ChatMessageTextBubbleContentNode.swift | 5 +- 45 files changed, 568 insertions(+), 175 deletions(-) create mode 100644 submodules/SyncCore/Sources/ReplyThreadMessageAttribute.swift create mode 100644 submodules/TelegramCore/Sources/ReplyThreadHistory.swift create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/ReplyCount.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/ReplyCount.imageset/replies.pdf diff --git a/Telegram/NotificationService/Serialization.m b/Telegram/NotificationService/Serialization.m index ddfa890a93..f137bbe5df 100644 --- a/Telegram/NotificationService/Serialization.m +++ b/Telegram/NotificationService/Serialization.m @@ -3,7 +3,7 @@ @implementation Serialization - (NSUInteger)currentLayer { - return 118; + return 119; } - (id _Nullable)parseMessage:(NSData * _Nullable)data { diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index b8e3aa46fd..4980951cd6 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -32,9 +32,9 @@ public func largestRepresentationForPhoto(_ photo: TelegramMediaImage) -> Telegr private let progressiveRangeMap: [(Int, [Int])] = [ (100, [0]), - (400, [2]), - (600, [4, 5]), - (Int(Int32.max), [4, 5, 8, 9]) + (400, [1]), + (600, [2, 3]), + (Int(Int32.max), [2, 3, 4]) ] public func representationFetchRangeForDisplayAtSize(representation: TelegramMediaImageRepresentation, dimension: Int) -> Range? { @@ -52,7 +52,7 @@ public func representationFetchRangeForDisplayAtSize(representation: TelegramMed } public func chatMessagePhotoDatas(postbox: Postbox, photoReference: ImageMediaReference, fullRepresentationSize: CGSize = CGSize(width: 1280.0, height: 1280.0), autoFetchFullSize: Bool = false, tryAdditionalRepresentations: Bool = false, synchronousLoad: Bool = false, useMiniThumbnailIfAvailable: Bool = false) -> Signal, NoError> { - if let progressiveRepresentation = progressiveImageRepresentation(photoReference.media.representations), progressiveRepresentation.progressiveSizes.count == 10 { + if let progressiveRepresentation = progressiveImageRepresentation(photoReference.media.representations), progressiveRepresentation.progressiveSizes.count == 5 { enum SizeSource { case miniThumbnail(data: Data) case image(size: Int) diff --git a/submodules/Postbox/Sources/MessageHistoryView.swift b/submodules/Postbox/Sources/MessageHistoryView.swift index 87c2e0db6a..895cdbfe4f 100644 --- a/submodules/Postbox/Sources/MessageHistoryView.swift +++ b/submodules/Postbox/Sources/MessageHistoryView.swift @@ -236,7 +236,13 @@ public struct MessageHistoryViewOrderStatistics: OptionSet { public static let locationWithinMonth = MessageHistoryViewOrderStatistics(rawValue: 1 << 1) } -public enum MessageHistoryViewPeerIds: Equatable { +public protocol MessageHistoryViewExternalInput: class { + func getNamespaces() -> [MessageId.Namespace] + func getMessageIds(namespace: MessageId.Namespace) -> [MessageId] + func getHoleIndices(namespace: MessageId.Namespace) -> IndexSet +} + +public enum MessageHistoryViewInput: Equatable { case single(PeerId) case associated(PeerId, MessageId?) } @@ -254,7 +260,7 @@ public enum HistoryViewInputAnchor: Equatable { } final class MutableMessageHistoryView { - private(set) var peerIds: MessageHistoryViewPeerIds + private(set) var peerIds: MessageHistoryViewInput let tag: MessageTags? let namespaces: MessageIdNamespaces private let orderStatistics: MessageHistoryViewOrderStatistics @@ -274,7 +280,7 @@ final class MutableMessageHistoryView { fileprivate var isAddedToChatList: Bool - init(postbox: Postbox, orderStatistics: MessageHistoryViewOrderStatistics, clipHoles: Bool, peerIds: MessageHistoryViewPeerIds, anchor inputAnchor: HistoryViewInputAnchor, combinedReadStates: MessageHistoryViewReadState?, transientReadStates: MessageHistoryViewReadState?, tag: MessageTags?, namespaces: MessageIdNamespaces, count: Int, topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?], additionalDatas: [AdditionalMessageHistoryViewDataEntry], getMessageCountInRange: (MessageIndex, MessageIndex) -> Int32) { + init(postbox: Postbox, orderStatistics: MessageHistoryViewOrderStatistics, clipHoles: Bool, peerIds: MessageHistoryViewInput, anchor inputAnchor: HistoryViewInputAnchor, combinedReadStates: MessageHistoryViewReadState?, transientReadStates: MessageHistoryViewReadState?, tag: MessageTags?, namespaces: MessageIdNamespaces, count: Int, topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?], additionalDatas: [AdditionalMessageHistoryViewDataEntry], getMessageCountInRange: (MessageIndex, MessageIndex) -> Int32) { self.anchor = inputAnchor self.orderStatistics = orderStatistics diff --git a/submodules/Postbox/Sources/MessageHistoryViewState.swift b/submodules/Postbox/Sources/MessageHistoryViewState.swift index 7f46f97daa..f53136c21e 100644 --- a/submodules/Postbox/Sources/MessageHistoryViewState.swift +++ b/submodules/Postbox/Sources/MessageHistoryViewState.swift @@ -781,7 +781,7 @@ final class HistoryViewLoadedState { var holes: HistoryViewHoles var spacesWithRemovals = Set() - init(anchor: HistoryViewAnchor, tag: MessageTags?, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, halfLimit: Int, locations: MessageHistoryViewPeerIds, postbox: Postbox, holes: HistoryViewHoles) { + init(anchor: HistoryViewAnchor, tag: MessageTags?, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, halfLimit: Int, locations: MessageHistoryViewInput, postbox: Postbox, holes: HistoryViewHoles) { precondition(halfLimit >= 3) self.anchor = anchor self.tag = tag @@ -1229,7 +1229,7 @@ final class HistoryViewLoadedState { } } -private func fetchHoles(postbox: Postbox, locations: MessageHistoryViewPeerIds, tag: MessageTags?, namespaces: MessageIdNamespaces) -> [PeerIdAndNamespace: IndexSet] { +private func fetchHoles(postbox: Postbox, locations: MessageHistoryViewInput, tag: MessageTags?, namespaces: MessageIdNamespaces) -> [PeerIdAndNamespace: IndexSet] { var holesBySpace: [PeerIdAndNamespace: IndexSet] = [:] var peerIds: [PeerId] = [] switch locations { @@ -1268,7 +1268,7 @@ final class HistoryViewLoadingState { let halfLimit: Int var holes: HistoryViewHoles - init(postbox: Postbox, locations: MessageHistoryViewPeerIds, tag: MessageTags?, namespaces: MessageIdNamespaces, messageId: MessageId, halfLimit: Int) { + init(postbox: Postbox, locations: MessageHistoryViewInput, tag: MessageTags?, namespaces: MessageIdNamespaces, messageId: MessageId, halfLimit: Int) { self.messageId = messageId self.tag = tag self.halfLimit = halfLimit @@ -1312,7 +1312,7 @@ enum HistoryViewState { case loaded(HistoryViewLoadedState) case loading(HistoryViewLoadingState) - init(postbox: Postbox, inputAnchor: HistoryViewInputAnchor, tag: MessageTags?, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, halfLimit: Int, locations: MessageHistoryViewPeerIds) { + init(postbox: Postbox, inputAnchor: HistoryViewInputAnchor, tag: MessageTags?, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, halfLimit: Int, locations: MessageHistoryViewInput) { switch inputAnchor { case let .index(index): self = .loaded(HistoryViewLoadedState(anchor: .index(index), tag: tag, namespaces: namespaces, statistics: statistics, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) diff --git a/submodules/Postbox/Sources/MessageOfInterestHolesView.swift b/submodules/Postbox/Sources/MessageOfInterestHolesView.swift index caa7f03362..9efbe3090e 100644 --- a/submodules/Postbox/Sources/MessageOfInterestHolesView.swift +++ b/submodules/Postbox/Sources/MessageOfInterestHolesView.swift @@ -33,7 +33,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView { private let count: Int private var anchor: HistoryViewInputAnchor private var wrappedView: MutableMessageHistoryView - private var peerIds: MessageHistoryViewPeerIds + private var peerIds: MessageHistoryViewInput fileprivate var closestHole: MessageOfInterestHole? fileprivate var closestLaterMedia: [HolesViewMedia] = [] @@ -43,7 +43,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView { self.count = count let mainPeerId: PeerId - let peerIds: MessageHistoryViewPeerIds + let peerIds: MessageHistoryViewInput switch self.location { case let .peer(id): mainPeerId = id @@ -127,7 +127,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView { if self.anchor != anchor { self.anchor = anchor - let peerIds: MessageHistoryViewPeerIds + let peerIds: MessageHistoryViewInput switch self.location { case let .peer(id): peerIds = postbox.peerIdsForLocation(.peer(id), tagMask: nil) @@ -155,7 +155,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView { } } if reloadView { - let peerIds: MessageHistoryViewPeerIds + let peerIds: MessageHistoryViewInput switch self.location { case let .peer(id): peerIds = postbox.peerIdsForLocation(.peer(id), tagMask: nil) diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 4dcf6d4dcc..a9d559ebbd 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -38,6 +38,14 @@ public final class Transaction { } } + public func messageExists(id: MessageId) -> Bool { + if let postbox = self.postbox { + return postbox.messageHistoryIndexTable.exists(id) + } else { + return false + } + } + public func countIncomingMessage(id: MessageId) { assert(!self.disposed) if let postbox = self.postbox { @@ -2333,8 +2341,8 @@ public final class Postbox { } } - func peerIdsForLocation(_ chatLocation: ChatLocation, tagMask: MessageTags?) -> MessageHistoryViewPeerIds { - var peerIds: MessageHistoryViewPeerIds + func peerIdsForLocation(_ chatLocation: ChatLocation, tagMask: MessageTags?) -> MessageHistoryViewInput { + var peerIds: MessageHistoryViewInput switch chatLocation { case let .peer(peerId): peerIds = .single(peerId) @@ -2410,7 +2418,7 @@ public final class Postbox { } } - private func syncAroundMessageHistoryViewForPeerId(subscriber: Subscriber<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>, peerIds: MessageHistoryViewPeerIds, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Disposable { + private func syncAroundMessageHistoryViewForPeerId(subscriber: Subscriber<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>, peerIds: MessageHistoryViewInput, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Disposable { var topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?] = [:] var mainPeerId: PeerId? switch peerIds { diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift index 600f8741f7..0925787231 100644 --- a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift +++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift @@ -175,7 +175,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: 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: []) 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)) - let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: 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)], 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, 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: []) 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)) let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA=" diff --git a/submodules/SettingsUI/Sources/DebugController.swift b/submodules/SettingsUI/Sources/DebugController.swift index 4b505dae7d..80e166ea34 100644 --- a/submodules/SettingsUI/Sources/DebugController.swift +++ b/submodules/SettingsUI/Sources/DebugController.swift @@ -468,6 +468,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { actionSheet?.dismissAnimated() let databasePath = context.account.basePath + "/postbox/db" let _ = try? FileManager.default.removeItem(atPath: databasePath) + exit(0) preconditionFailure() }), ]), ActionSheetItemGroup(items: [ diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index 862a27987f..e3089a6248 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -321,7 +321,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: 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: []) 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)) - let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: 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)], 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, 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: []) 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)) let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA=" diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 2830ef49ba..32b8f2268b 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -875,7 +875,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: []) sampleMessages.append(message6) - let message7 = Message(stableId: 7, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 7), globallyUniqueId: nil, groupingKey: nil, groupInfo: 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)], 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, 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: []) sampleMessages.append(message7) let message8 = Message(stableId: 8, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 8), globallyUniqueId: nil, groupingKey: nil, groupInfo: 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: []) diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index e6a3736b1f..9bca619021 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -480,7 +480,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: []) sampleMessages.append(message6) - let message7 = Message(stableId: 7, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 7), globallyUniqueId: nil, groupingKey: nil, groupInfo: 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)], 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, 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: []) sampleMessages.append(message7) let message8 = Message(stableId: 8, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 8), globallyUniqueId: nil, groupingKey: nil, groupInfo: 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: []) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift index df4f629682..975a874880 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift @@ -164,7 +164,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), 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, 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)] : [], 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, 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: []) 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)) } diff --git a/submodules/SyncCore/Sources/ReplyMessageAttribute.swift b/submodules/SyncCore/Sources/ReplyMessageAttribute.swift index cbcc36b5fa..5ab64d24fe 100644 --- a/submodules/SyncCore/Sources/ReplyMessageAttribute.swift +++ b/submodules/SyncCore/Sources/ReplyMessageAttribute.swift @@ -3,23 +3,53 @@ import Postbox public class ReplyMessageAttribute: MessageAttribute { public let messageId: MessageId + public let threadMessageId: MessageId? public var associatedMessageIds: [MessageId] { return [self.messageId] } - public init(messageId: MessageId) { + public init(messageId: MessageId, threadMessageId: MessageId?) { self.messageId = messageId + self.threadMessageId = threadMessageId } required public init(decoder: PostboxDecoder) { let namespaceAndId: Int64 = decoder.decodeInt64ForKey("i", orElse: 0) self.messageId = MessageId(peerId: PeerId(decoder.decodeInt64ForKey("p", orElse: 0)), namespace: Int32(namespaceAndId & 0xffffffff), id: Int32((namespaceAndId >> 32) & 0xffffffff)) + + if let threadNamespaceAndId = decoder.decodeOptionalInt64ForKey("ti"), let threadPeerId = decoder.decodeOptionalInt64ForKey("tp") { + self.threadMessageId = MessageId(peerId: PeerId(threadPeerId), namespace: Int32(threadNamespaceAndId & 0xffffffff), id: Int32((threadNamespaceAndId >> 32) & 0xffffffff)) + } else { + self.threadMessageId = nil + } } public func encode(_ encoder: PostboxEncoder) { let namespaceAndId = Int64(self.messageId.namespace) | (Int64(self.messageId.id) << 32) encoder.encodeInt64(namespaceAndId, forKey: "i") encoder.encodeInt64(self.messageId.peerId.toInt64(), forKey: "p") + if let threadMessageId = self.threadMessageId { + let threadNamespaceAndId = Int64(threadMessageId.namespace) | (Int64(threadMessageId.id) << 32) + encoder.encodeInt64(threadNamespaceAndId, forKey: "ti") + encoder.encodeInt64(threadMessageId.peerId.toInt64(), forKey: "tp") + } + } +} + +public extension ReplyMessageAttribute { + var effectiveReplyThreadMessageId: MessageId { + return self.threadMessageId ?? self.messageId + } +} + +public extension Message { + var effectiveReplyThreadMessageId: MessageId? { + for attribute in self.attributes { + if let attribute = attribute as? ReplyMessageAttribute { + return attribute.effectiveReplyThreadMessageId + } + } + return nil } } diff --git a/submodules/SyncCore/Sources/ReplyThreadMessageAttribute.swift b/submodules/SyncCore/Sources/ReplyThreadMessageAttribute.swift new file mode 100644 index 0000000000..c7cc4f4da0 --- /dev/null +++ b/submodules/SyncCore/Sources/ReplyThreadMessageAttribute.swift @@ -0,0 +1,18 @@ +import Foundation +import Postbox + +public class ReplyThreadMessageAttribute: MessageAttribute { + public let count: Int32 + + public init(count: Int32) { + self.count = count + } + + required public init(decoder: PostboxDecoder) { + self.count = decoder.decodeInt32ForKey("c", orElse: 0) + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt32(self.count, forKey: "c") + } +} diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 9d6c66583c..ae34427ed6 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -388,7 +388,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1694474197] = { return Api.messages.Chats.parse_chats($0) } dict[-1663561404] = { return Api.messages.Chats.parse_chatsSlice($0) } dict[482797855] = { return Api.InputSingleMedia.parse_inputSingleMedia($0) } - dict[1831138451] = { return Api.MessageViews.parse_messageViews($0) } + dict[23453020] = { return Api.MessageViews.parse_messageViews($0) } dict[218751099] = { return Api.InputPrivacyRule.parse_inputPrivacyValueAllowContacts($0) } dict[407582158] = { return Api.InputPrivacyRule.parse_inputPrivacyValueAllowAll($0) } dict[320652927] = { return Api.InputPrivacyRule.parse_inputPrivacyValueAllowUsers($0) } @@ -602,7 +602,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1820043071] = { return Api.User.parse_user($0) } dict[-2082087340] = { return Api.Message.parse_messageEmpty($0) } dict[-1642487306] = { return Api.Message.parse_messageService($0) } - dict[-181507201] = { return Api.Message.parse_message($0) } + dict[2119777911] = { return Api.Message.parse_message($0) } dict[831924812] = { return Api.StatsGroupTopInviter.parse_statsGroupTopInviter($0) } dict[186120336] = { return Api.messages.RecentStickers.parse_recentStickersNotModified($0) } dict[586395571] = { return Api.messages.RecentStickers.parse_recentStickers($0) } diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index 5a30fc3b91..a67fd40605 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -11752,24 +11752,26 @@ public extension Api { } public enum MessageViews: TypeConstructorDescription { - case messageViews(views: Int32, forwards: Int32) + case messageViews(views: Int32, forwards: Int32, replies: Int32, repliesPts: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .messageViews(let views, let forwards): + case .messageViews(let views, let forwards, let replies, let repliesPts): if boxed { - buffer.appendInt32(1831138451) + buffer.appendInt32(23453020) } serializeInt32(views, buffer: buffer, boxed: false) serializeInt32(forwards, buffer: buffer, boxed: false) + serializeInt32(replies, buffer: buffer, boxed: false) + serializeInt32(repliesPts, buffer: buffer, boxed: false) break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .messageViews(let views, let forwards): - return ("messageViews", [("views", views), ("forwards", forwards)]) + case .messageViews(let views, let forwards, let replies, let repliesPts): + return ("messageViews", [("views", views), ("forwards", forwards), ("replies", replies), ("repliesPts", repliesPts)]) } } @@ -11778,10 +11780,16 @@ public extension Api { _1 = reader.readInt32() var _2: Int32? _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageViews.messageViews(views: _1!, forwards: _2!) + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.MessageViews.messageViews(views: _1!, forwards: _2!, replies: _3!, repliesPts: _4!) } else { return nil @@ -17162,7 +17170,7 @@ public extension Api { public enum Message: TypeConstructorDescription { case messageEmpty(id: Int32) case messageService(flags: Int32, id: Int32, fromId: Int32?, toId: Api.Peer, replyToMsgId: Int32?, date: Int32, action: Api.MessageAction) - case message(flags: Int32, id: Int32, fromId: Int32?, toId: Api.Peer, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int32?, replyToMsgId: Int32?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, restrictionReason: [Api.RestrictionReason]?) + case message(flags: Int32, id: Int32, fromId: Int32?, toId: Api.Peer, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int32?, replyToMsgId: Int32?, replyToTopId: Int32?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Int32?, repliesPts: Int32?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, restrictionReason: [Api.RestrictionReason]?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -17184,9 +17192,9 @@ public extension Api { serializeInt32(date, buffer: buffer, boxed: false) action.serialize(buffer, true) break - case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let editDate, let postAuthor, let groupedId, let restrictionReason): + case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let replyToTopId, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let repliesPts, let editDate, let postAuthor, let groupedId, let restrictionReason): if boxed { - buffer.appendInt32(-181507201) + buffer.appendInt32(2119777911) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false) @@ -17195,6 +17203,7 @@ public extension Api { if Int(flags) & Int(1 << 2) != 0 {fwdFrom!.serialize(buffer, true)} if Int(flags) & Int(1 << 11) != 0 {serializeInt32(viaBotId!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 3) != 0 {serializeInt32(replyToMsgId!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 24) != 0 {serializeInt32(replyToTopId!, buffer: buffer, boxed: false)} serializeInt32(date, buffer: buffer, boxed: false) serializeString(message, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 9) != 0 {media!.serialize(buffer, true)} @@ -17206,6 +17215,8 @@ public extension Api { }} if Int(flags) & Int(1 << 10) != 0 {serializeInt32(views!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 10) != 0 {serializeInt32(forwards!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 23) != 0 {serializeInt32(replies!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 23) != 0 {serializeInt32(repliesPts!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 15) != 0 {serializeInt32(editDate!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 16) != 0 {serializeString(postAuthor!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 17) != 0 {serializeInt64(groupedId!, buffer: buffer, boxed: false)} @@ -17224,8 +17235,8 @@ public extension Api { return ("messageEmpty", [("id", id)]) case .messageService(let flags, let id, let fromId, let toId, let replyToMsgId, let date, let action): return ("messageService", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("replyToMsgId", replyToMsgId), ("date", date), ("action", action)]) - case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let editDate, let postAuthor, let groupedId, let restrictionReason): - return ("message", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("fwdFrom", fwdFrom), ("viaBotId", viaBotId), ("replyToMsgId", replyToMsgId), ("date", date), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("views", views), ("forwards", forwards), ("editDate", editDate), ("postAuthor", postAuthor), ("groupedId", groupedId), ("restrictionReason", restrictionReason)]) + case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let replyToTopId, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let repliesPts, let editDate, let postAuthor, let groupedId, let restrictionReason): + return ("message", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("fwdFrom", fwdFrom), ("viaBotId", viaBotId), ("replyToMsgId", replyToMsgId), ("replyToTopId", replyToTopId), ("date", date), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("views", views), ("forwards", forwards), ("replies", replies), ("repliesPts", repliesPts), ("editDate", editDate), ("postAuthor", postAuthor), ("groupedId", groupedId), ("restrictionReason", restrictionReason)]) } } @@ -17293,34 +17304,40 @@ public extension Api { var _7: Int32? if Int(_1!) & Int(1 << 3) != 0 {_7 = reader.readInt32() } var _8: Int32? - _8 = reader.readInt32() - var _9: String? - _9 = parseString(reader) - var _10: Api.MessageMedia? + if Int(_1!) & Int(1 << 24) != 0 {_8 = reader.readInt32() } + var _9: Int32? + _9 = reader.readInt32() + var _10: String? + _10 = parseString(reader) + var _11: Api.MessageMedia? if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.MessageMedia + _11 = Api.parse(reader, signature: signature) as? Api.MessageMedia } } - var _11: Api.ReplyMarkup? + var _12: Api.ReplyMarkup? if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() { - _11 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup + _12 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup } } - var _12: [Api.MessageEntity]? + var _13: [Api.MessageEntity]? if Int(_1!) & Int(1 << 7) != 0 {if let _ = reader.readInt32() { - _12 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) + _13 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self) } } - var _13: Int32? - if Int(_1!) & Int(1 << 10) != 0 {_13 = reader.readInt32() } var _14: Int32? if Int(_1!) & Int(1 << 10) != 0 {_14 = reader.readInt32() } var _15: Int32? - if Int(_1!) & Int(1 << 15) != 0 {_15 = reader.readInt32() } - var _16: String? - if Int(_1!) & Int(1 << 16) != 0 {_16 = parseString(reader) } - var _17: Int64? - if Int(_1!) & Int(1 << 17) != 0 {_17 = reader.readInt64() } - var _18: [Api.RestrictionReason]? + if Int(_1!) & Int(1 << 10) != 0 {_15 = reader.readInt32() } + var _16: Int32? + if Int(_1!) & Int(1 << 23) != 0 {_16 = reader.readInt32() } + var _17: Int32? + if Int(_1!) & Int(1 << 23) != 0 {_17 = reader.readInt32() } + var _18: Int32? + if Int(_1!) & Int(1 << 15) != 0 {_18 = reader.readInt32() } + var _19: String? + if Int(_1!) & Int(1 << 16) != 0 {_19 = parseString(reader) } + var _20: Int64? + if Int(_1!) & Int(1 << 17) != 0 {_20 = reader.readInt64() } + var _21: [Api.RestrictionReason]? if Int(_1!) & Int(1 << 22) != 0 {if let _ = reader.readInt32() { - _18 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self) + _21 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self) } } let _c1 = _1 != nil let _c2 = _2 != nil @@ -17329,19 +17346,22 @@ public extension Api { let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 11) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 3) == 0) || _7 != nil - let _c8 = _8 != nil + let _c8 = (Int(_1!) & Int(1 << 24) == 0) || _8 != nil let _c9 = _9 != nil - let _c10 = (Int(_1!) & Int(1 << 9) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 6) == 0) || _11 != nil - let _c12 = (Int(_1!) & Int(1 << 7) == 0) || _12 != nil - let _c13 = (Int(_1!) & Int(1 << 10) == 0) || _13 != nil + let _c10 = _10 != nil + let _c11 = (Int(_1!) & Int(1 << 9) == 0) || _11 != nil + let _c12 = (Int(_1!) & Int(1 << 6) == 0) || _12 != nil + let _c13 = (Int(_1!) & Int(1 << 7) == 0) || _13 != nil let _c14 = (Int(_1!) & Int(1 << 10) == 0) || _14 != nil - let _c15 = (Int(_1!) & Int(1 << 15) == 0) || _15 != nil - let _c16 = (Int(_1!) & Int(1 << 16) == 0) || _16 != nil - let _c17 = (Int(_1!) & Int(1 << 17) == 0) || _17 != nil - let _c18 = (Int(_1!) & Int(1 << 22) == 0) || _18 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 { - return Api.Message.message(flags: _1!, id: _2!, fromId: _3, toId: _4!, fwdFrom: _5, viaBotId: _6, replyToMsgId: _7, date: _8!, message: _9!, media: _10, replyMarkup: _11, entities: _12, views: _13, forwards: _14, editDate: _15, postAuthor: _16, groupedId: _17, restrictionReason: _18) + let _c15 = (Int(_1!) & Int(1 << 10) == 0) || _15 != nil + let _c16 = (Int(_1!) & Int(1 << 23) == 0) || _16 != nil + let _c17 = (Int(_1!) & Int(1 << 23) == 0) || _17 != nil + let _c18 = (Int(_1!) & Int(1 << 15) == 0) || _18 != nil + let _c19 = (Int(_1!) & Int(1 << 16) == 0) || _19 != nil + let _c20 = (Int(_1!) & Int(1 << 17) == 0) || _20 != nil + let _c21 = (Int(_1!) & Int(1 << 22) == 0) || _21 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 { + return Api.Message.message(flags: _1!, id: _2!, fromId: _3, toId: _4!, fwdFrom: _5, viaBotId: _6, replyToMsgId: _7, replyToTopId: _8, date: _9!, message: _10!, media: _11, replyMarkup: _12, entities: _13, views: _14, forwards: _15, replies: _16, repliesPts: _17, editDate: _18, postAuthor: _19, groupedId: _20, restrictionReason: _21) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index 67999cfce8..27251ed127 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -3629,24 +3629,6 @@ public extension Api { }) } - public static func getBotCallbackAnswer(flags: Int32, peer: Api.InputPeer, msgId: Int32, data: Buffer?, password: Api.InputCheckPasswordSRP?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1824339449) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeBytes(data!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 2) != 0 {password!.serialize(buffer, true)} - return (FunctionDescription(name: "messages.getBotCallbackAnswer", parameters: [("flags", flags), ("peer", peer), ("msgId", msgId), ("data", data), ("password", password)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.BotCallbackAnswer? in - let reader = BufferReader(buffer) - var result: Api.messages.BotCallbackAnswer? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.BotCallbackAnswer - } - return result - }) - } - public static func getMessagesViews(peer: Api.InputPeer, id: [Int32], increment: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.MessageViews]>) { let buffer = Buffer() buffer.appendInt32(-39035462) @@ -3667,22 +3649,19 @@ public extension Api { }) } - public static func searchGlobal(flags: Int32, folderId: Int32?, q: String, filter: Api.MessagesFilter, offsetRate: Int32, offsetPeer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + public static func getBotCallbackAnswer(flags: Int32, peer: Api.InputPeer, msgId: Int32, data: Buffer?, password: Api.InputCheckPasswordSRP?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(1934479725) + buffer.appendInt32(-1824339449) serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} - serializeString(q, buffer: buffer, boxed: false) - filter.serialize(buffer, true) - serializeInt32(offsetRate, buffer: buffer, boxed: false) - offsetPeer.serialize(buffer, true) - serializeInt32(offsetId, buffer: buffer, boxed: false) - serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.searchGlobal", parameters: [("flags", flags), ("folderId", folderId), ("q", q), ("filter", filter), ("offsetRate", offsetRate), ("offsetPeer", offsetPeer), ("offsetId", offsetId), ("limit", limit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeBytes(data!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {password!.serialize(buffer, true)} + return (FunctionDescription(name: "messages.getBotCallbackAnswer", parameters: [("flags", flags), ("peer", peer), ("msgId", msgId), ("data", data), ("password", password)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.BotCallbackAnswer? in let reader = BufferReader(buffer) - var result: Api.messages.Messages? + var result: Api.messages.BotCallbackAnswer? if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.messages.Messages + result = Api.parse(reader, signature: signature) as? Api.messages.BotCallbackAnswer } return result }) @@ -3722,6 +3701,48 @@ public extension Api { return result }) } + + public static func searchGlobal(flags: Int32, folderId: Int32?, q: String, filter: Api.MessagesFilter, offsetRate: Int32, offsetPeer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1934479725) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} + serializeString(q, buffer: buffer, boxed: false) + filter.serialize(buffer, true) + serializeInt32(offsetRate, buffer: buffer, boxed: false) + offsetPeer.serialize(buffer, true) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.searchGlobal", parameters: [("flags", flags), ("folderId", folderId), ("q", q), ("filter", filter), ("offsetRate", offsetRate), ("offsetPeer", offsetPeer), ("offsetId", offsetId), ("limit", limit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } + + public static func getReplies(peer: Api.InputPeer, msgId: Int32, offsetId: Int32, addOffset: Int32, limit: Int32, maxId: Int32, minId: Int32, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-39505956) + peer.serialize(buffer, true) + serializeInt32(msgId, buffer: buffer, boxed: false) + serializeInt32(offsetId, buffer: buffer, boxed: false) + serializeInt32(addOffset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + serializeInt32(maxId, buffer: buffer, boxed: false) + serializeInt32(minId, buffer: buffer, boxed: false) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getReplies", parameters: [("peer", peer), ("msgId", msgId), ("offsetId", offsetId), ("addOffset", addOffset), ("limit", limit), ("maxId", maxId), ("minId", minId), ("hash", hash)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in + let reader = BufferReader(buffer) + var result: Api.messages.Messages? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.Messages + } + return result + }) + } } public struct channels { public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { @@ -5408,6 +5429,34 @@ public extension Api { }) } + public static func getPromoData() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1063816159) + + return (FunctionDescription(name: "help.getPromoData", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PromoData? in + let reader = BufferReader(buffer) + var result: Api.help.PromoData? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.help.PromoData + } + return result + }) + } + + public static func hidePromoData(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(505748629) + peer.serialize(buffer, true) + return (FunctionDescription(name: "help.hidePromoData", parameters: [("peer", peer)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } + public static func editUserInfo(userId: Api.InputUser, message: String, entities: [Api.MessageEntity]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(1723407216) @@ -5442,34 +5491,6 @@ public extension Api { }) } - public static func getPromoData() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-1063816159) - - return (FunctionDescription(name: "help.getPromoData", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PromoData? in - let reader = BufferReader(buffer) - var result: Api.help.PromoData? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.help.PromoData - } - return result - }) - } - - public static func hidePromoData(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(505748629) - peer.serialize(buffer, true) - return (FunctionDescription(name: "help.hidePromoData", parameters: [("peer", peer)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in - let reader = BufferReader(buffer) - var result: Api.Bool? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Bool - } - return result - }) - } - public static func dismissSuggestion(suggestion: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(125807007) @@ -6856,14 +6877,11 @@ public extension Api { }) } - public static func uploadProfilePhoto(flags: Int32, file: Api.InputFile?, video: Api.InputFile?, videoStartTs: Double?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + public static func updateProfilePhoto(id: Api.InputPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-1980559511) - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {file!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {video!.serialize(buffer, true)} - if Int(flags) & Int(1 << 2) != 0 {serializeDouble(videoStartTs!, buffer: buffer, boxed: false)} - return (FunctionDescription(name: "photos.uploadProfilePhoto", parameters: [("flags", flags), ("file", file), ("video", video), ("videoStartTs", videoStartTs)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in + buffer.appendInt32(1926525996) + id.serialize(buffer, true) + return (FunctionDescription(name: "photos.updateProfilePhoto", parameters: [("id", id)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in let reader = BufferReader(buffer) var result: Api.photos.Photo? if let signature = reader.readInt32() { @@ -6873,11 +6891,14 @@ public extension Api { }) } - public static func updateProfilePhoto(id: Api.InputPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + public static func uploadProfilePhoto(flags: Int32, file: Api.InputFile?, video: Api.InputFile?, videoStartTs: Double?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(1926525996) - id.serialize(buffer, true) - return (FunctionDescription(name: "photos.updateProfilePhoto", parameters: [("id", id)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in + buffer.appendInt32(-1980559511) + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {file!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {video!.serialize(buffer, true)} + if Int(flags) & Int(1 << 2) != 0 {serializeDouble(videoStartTs!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "photos.uploadProfilePhoto", parameters: [("flags", flags), ("file", file), ("video", video), ("videoStartTs", videoStartTs)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in let reader = BufferReader(buffer) var result: Api.photos.Photo? if let signature = reader.readInt32() { diff --git a/submodules/TelegramCore/Sources/AccountManager.swift b/submodules/TelegramCore/Sources/AccountManager.swift index 9877a921ae..b19adab9a4 100644 --- a/submodules/TelegramCore/Sources/AccountManager.swift +++ b/submodules/TelegramCore/Sources/AccountManager.swift @@ -30,6 +30,7 @@ private var declaredEncodables: Void = { declareEncodable(InlineBotMessageAttribute.self, f: { InlineBotMessageAttribute(decoder: $0) }) declareEncodable(TextEntitiesMessageAttribute.self, f: { TextEntitiesMessageAttribute(decoder: $0) }) declareEncodable(ReplyMessageAttribute.self, f: { ReplyMessageAttribute(decoder: $0) }) + declareEncodable(ReplyThreadMessageAttribute.self, f: { ReplyThreadMessageAttribute(decoder: $0) }) declareEncodable(ReactionsMessageAttribute.self, f: { ReactionsMessageAttribute(decoder: $0) }) declareEncodable(PendingReactionsMessageAttribute.self, f: { PendingReactionsMessageAttribute(decoder: $0) }) declareEncodable(CloudDocumentMediaResource.self, f: { CloudDocumentMediaResource(decoder: $0) }) diff --git a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift index 57c725bf36..e193a30c6b 100644 --- a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift @@ -2274,9 +2274,34 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP var topUpperHistoryBlockMessages: [PeerIdAndMessageNamespace: MessageId.Id] = [:] + var messageThreadStatsDifferences: [MessageId: Int] = [:] + func addMessageThreadStatsDifference(threadMessageId: MessageId, add: Int, remove: Int) { + if let value = messageThreadStatsDifferences[threadMessageId] { + messageThreadStatsDifferences[threadMessageId] = value + add - remove + } else { + messageThreadStatsDifferences[threadMessageId] = add - remove + } + } + for operation in optimizedOperations(finalState.state.operations) { switch operation { case let .AddMessages(messages, location): + if case .UpperHistoryBlock = location { + for message in messages { + if case let .Id(id) = message.id { + findAttribute: for attribute in message.attributes { + if let attribute = attribute as? ReplyMessageAttribute { + if id.peerId.namespace == Namespaces.Peer.CloudChannel { + if !transaction.messageExists(id: id) { + addMessageThreadStatsDifference(threadMessageId: attribute.effectiveReplyThreadMessageId, add: 1, remove: 0) + } + } + break findAttribute + } + } + } + } + } let _ = transaction.addMessages(messages, location: location) if case .UpperHistoryBlock = location { for message in messages { @@ -2384,7 +2409,9 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP let _ = mediaBox.removeCachedResources(Set(resourceIds)).start() } case let .DeleteMessages(ids): - deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: ids) + deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: ids, manualAddMessageThreadStatsDifference: { id, add, remove in + addMessageThreadStatsDifference(threadMessageId: id, add: add, remove: remove) + }) case let .UpdateMinAvailableMessage(id): if let message = transaction.getMessage(id) { updatePeerChatInclusionWithMinTimestamp(transaction: transaction, id: id.peerId, minTimestamp: message.timestamp, forceRootGroupIfNotExists: false) @@ -2908,6 +2935,10 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP } } + for (threadMessageId, difference) in messageThreadStatsDifferences { + updateMessageThreadStats(transaction: transaction, threadMessageId: threadMessageId, difference: difference) + } + if !peerActivityTimestamps.isEmpty { updatePeerPresenceLastActivities(transaction: transaction, accountPeerId: accountPeerId, activities: peerActivityTimestamps) } diff --git a/submodules/TelegramCore/Sources/AccountViewTracker.swift b/submodules/TelegramCore/Sources/AccountViewTracker.swift index 9f166ff156..db01fd8b92 100644 --- a/submodules/TelegramCore/Sources/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/AccountViewTracker.swift @@ -599,16 +599,17 @@ public final class AccountViewTracker { return account.postbox.transaction { transaction -> Void in for i in 0 ..< messageIds.count { if i < viewCounts.count { - if case let .messageViews(views, forwards) = viewCounts[i] { + if case let .messageViews(views, forwards, replies, _) = viewCounts[i] { transaction.updateMessage(messageIds[i], update: { currentMessage in let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init) var attributes = currentMessage.attributes loop: for j in 0 ..< attributes.count { if let attribute = attributes[j] as? ViewCountMessageAttribute { attributes[j] = ViewCountMessageAttribute(count: max(attribute.count, Int(views))) - } - if let _ = attributes[j] as? ForwardCountMessageAttribute { + } else if let _ = attributes[j] as? ForwardCountMessageAttribute { attributes[j] = ForwardCountMessageAttribute(count: Int(forwards)) + } else if let _ = attributes[j] as? ReplyThreadMessageAttribute { + attributes[j] = ReplyThreadMessageAttribute(count: replies) } } return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) diff --git a/submodules/TelegramCore/Sources/ApplyUpdateMessage.swift b/submodules/TelegramCore/Sources/ApplyUpdateMessage.swift index 9d09f0de6e..1ec9818c25 100644 --- a/submodules/TelegramCore/Sources/ApplyUpdateMessage.swift +++ b/submodules/TelegramCore/Sources/ApplyUpdateMessage.swift @@ -90,6 +90,8 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes transaction.offsetPendingMessagesTimestamps(lowerBound: message.id, excludeIds: Set([message.id]), timestamp: updatedTimestamp) } + var updatedMessage: StoreMessage? + transaction.updateMessage(message.id, update: { currentMessage in let updatedId: MessageId if let messageId = messageId { @@ -222,8 +224,21 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes } } - return .update(StoreMessage(id: updatedId, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: updatedTimestamp ?? currentMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: forwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media)) + let updatedMessageValue = StoreMessage(id: updatedId, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: updatedTimestamp ?? currentMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: forwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media) + updatedMessage = updatedMessageValue + + return .update(updatedMessageValue) }) + if let updatedMessage = updatedMessage, case let .Id(updatedId) = updatedMessage.id { + if message.id.namespace == Namespaces.Message.Local && updatedId.namespace == Namespaces.Message.Cloud && updatedId.peerId.namespace == Namespaces.Peer.CloudChannel { + for attribute in updatedMessage.attributes { + if let attribute = attribute as? ReplyMessageAttribute { + updateMessageThreadStats(transaction: transaction, threadMessageId: attribute.effectiveReplyThreadMessageId, difference: 1) + break + } + } + } + } for file in sentStickers { transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentStickers, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: RecentMediaItem(file)), removeTailIfCountExceeds: 20) } diff --git a/submodules/TelegramCore/Sources/DeleteMessages.swift b/submodules/TelegramCore/Sources/DeleteMessages.swift index 14dfab3fd3..f42329c25a 100644 --- a/submodules/TelegramCore/Sources/DeleteMessages.swift +++ b/submodules/TelegramCore/Sources/DeleteMessages.swift @@ -23,7 +23,7 @@ func addMessageMediaResourceIdsToRemove(message: Message, resourceIds: inout [Wr } } -public func deleteMessages(transaction: Transaction, mediaBox: MediaBox, ids: [MessageId], deleteMedia: Bool = true) { +public func deleteMessages(transaction: Transaction, mediaBox: MediaBox, ids: [MessageId], deleteMedia: Bool = true, manualAddMessageThreadStatsDifference: ((MessageId, Int, Int) -> Void)? = nil) { var resourceIds: [WrappedMediaResourceId] = [] if deleteMedia { for id in ids { @@ -37,6 +37,24 @@ public func deleteMessages(transaction: Transaction, mediaBox: MediaBox, ids: [M if !resourceIds.isEmpty { let _ = mediaBox.removeCachedResources(Set(resourceIds)).start() } + for id in ids { + if id.peerId.namespace == Namespaces.Peer.CloudChannel && id.namespace == Namespaces.Message.Cloud { + if let message = transaction.getMessage(id) { + findAttribute: for attribute in message.attributes { + if let attribute = attribute as? ReplyMessageAttribute { + if id.peerId.namespace == Namespaces.Peer.CloudChannel { + if let manualAddMessageThreadStatsDifference = manualAddMessageThreadStatsDifference { + manualAddMessageThreadStatsDifference(attribute.messageId, 0, 1) + } else { + updateMessageThreadStats(transaction: transaction, threadMessageId: attribute.effectiveReplyThreadMessageId, difference: -1) + } + } + break findAttribute + } + } + } + } + } transaction.deleteMessages(ids, forEachMedia: { _ in }) } diff --git a/submodules/TelegramCore/Sources/EnqueueMessage.swift b/submodules/TelegramCore/Sources/EnqueueMessage.swift index b1fc81bf18..b7dab93053 100644 --- a/submodules/TelegramCore/Sources/EnqueueMessage.swift +++ b/submodules/TelegramCore/Sources/EnqueueMessage.swift @@ -316,7 +316,11 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, attributes.append(contentsOf: filterMessageAttributesForOutgoingMessage(requestedAttributes)) if let replyToMessageId = replyToMessageId, replyToMessageId.peerId == peerId { - attributes.append(ReplyMessageAttribute(messageId: replyToMessageId)) + var threadMessageId: MessageId? + if let replyMessage = transaction.getMessage(replyToMessageId) { + threadMessageId = replyMessage.effectiveReplyThreadMessageId + } + attributes.append(ReplyMessageAttribute(messageId: replyToMessageId, threadMessageId: threadMessageId)) } var mediaList: [Media] = [] if let mediaReference = mediaReference { diff --git a/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift b/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift index b86d428398..9b4d821ae6 100644 --- a/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift +++ b/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift @@ -809,7 +809,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 } if let replyToRandomId = replyToRandomId, let replyMessageId = messageIdForGloballyUniqueMessageId(replyToRandomId) { - attributes.append(ReplyMessageAttribute(messageId: replyMessageId)) + attributes.append(ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)) } var entitiesAttribute: TextEntitiesMessageAttribute? @@ -1041,7 +1041,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 } if let replyToRandomId = replyToRandomId, let replyMessageId = messageIdForGloballyUniqueMessageId(replyToRandomId) { - attributes.append(ReplyMessageAttribute(messageId: replyMessageId)) + attributes.append(ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)) } var entitiesAttribute: TextEntitiesMessageAttribute? @@ -1279,7 +1279,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 } if let replyToRandomId = replyToRandomId, let replyMessageId = messageIdForGloballyUniqueMessageId(replyToRandomId) { - attributes.append(ReplyMessageAttribute(messageId: replyMessageId)) + attributes.append(ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil)) } var entitiesAttribute: TextEntitiesMessageAttribute? diff --git a/submodules/TelegramCore/Sources/ReplyThreadHistory.swift b/submodules/TelegramCore/Sources/ReplyThreadHistory.swift new file mode 100644 index 0000000000..89c331d2bc --- /dev/null +++ b/submodules/TelegramCore/Sources/ReplyThreadHistory.swift @@ -0,0 +1,31 @@ +import Foundation +import SyncCore +import Postbox +import SwiftSignalKit + +private class ReplyThreadHistoryContextImpl { + private let queue: Queue + private let account: Account + + struct NamespaceState: Equatable { + var sortedMessageIds: [MessageId] + var holeIndices: IndexSet + } + + init(queue: Queue, account: Account) { + self.queue = queue + self.account = account + } +} + +class ReplyThreadHistoryContext { + private let queue = Queue() + private let impl: QueueLocalObject + + public init(account: Account, peerId: PeerId, threadMessageId: MessageId) { + let queue = self.queue + self.impl = QueueLocalObject(queue: queue, generate: { + return ReplyThreadHistoryContextImpl(queue: queue, account: account) + }) + } +} diff --git a/submodules/TelegramCore/Sources/Serialization.swift b/submodules/TelegramCore/Sources/Serialization.swift index 9d7dec93c3..94cd1882c6 100644 --- a/submodules/TelegramCore/Sources/Serialization.swift +++ b/submodules/TelegramCore/Sources/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 118 + return 119 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift index 02fd29efd6..2bf27e1f18 100644 --- a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift @@ -137,7 +137,7 @@ func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? { func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { switch message { - case let .message(flags, _, fromId, toId, fwdHeader, viaBotId, _, _, _, media, _, entities, _, _, _, _, _, _): + case let .message(flags, _, fromId, toId, fwdHeader, viaBotId, _, _, _, _, media, _, entities, _, _, _, _, _, _, _, _): let peerId: PeerId switch toId { case let .peerUser(userId): @@ -241,7 +241,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { func apiMessageAssociatedMessageIds(_ message: Api.Message) -> [MessageId]? { switch message { - case let .message(flags, _, fromId, toId, _, _, replyToMsgId, _, _, _, _, _, _, _, _, _, _, _): + case let .message(flags, _, fromId, toId, _, _, replyToMsgId, _, _, _, _, _, _, _, _, _, _, _, _, _, _): if let replyToMsgId = replyToMsgId { let peerId: PeerId switch toId { @@ -399,7 +399,7 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes extension StoreMessage { convenience init?(apiMessage: Api.Message, namespace: MessageId.Namespace = Namespaces.Message.Cloud) { switch apiMessage { - case let .message(flags, id, fromId, toId, fwdFrom, viaBotId, replyToMsgId, date, message, media, replyMarkup, entities, views, forwards, editDate, postAuthor, groupingId, restrictionReason): + case let .message(flags, id, fromId, toId, fwdFrom, viaBotId, replyToMsgId, replyToTopId, date, message, media, replyMarkup, entities, views, forwards, replies, _, editDate, postAuthor, groupingId, restrictionReason): let peerId: PeerId var authorId: PeerId? switch toId { @@ -515,7 +515,11 @@ extension StoreMessage { } if let replyToMsgId = replyToMsgId { - attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId))) + var threadMessageId: MessageId? + if let replyToTopId = replyToTopId { + threadMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToTopId) + } + attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId), threadMessageId: threadMessageId)) } if namespace != Namespaces.Message.ScheduledCloud { @@ -564,6 +568,10 @@ extension StoreMessage { attributes.append(ReactionsMessageAttribute(apiReactions: reactions)) }*/ + if let replies = replies { + attributes.append(ReplyThreadMessageAttribute(count: replies)) + } + if let restrictionReason = restrictionReason { attributes.append(RestrictedContentMessageAttribute(rules: restrictionReason.map(RestrictionRule.init(apiReason:)))) } @@ -636,7 +644,7 @@ extension StoreMessage { var attributes: [MessageAttribute] = [] if let replyToMsgId = replyToMsgId { - attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId))) + attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId), threadMessageId: nil)) } if (flags & (1 << 17)) != 0 { diff --git a/submodules/TelegramCore/Sources/UpdateMessageMedia.swift b/submodules/TelegramCore/Sources/UpdateMessageMedia.swift index 62e433f3f2..121f228cf0 100644 --- a/submodules/TelegramCore/Sources/UpdateMessageMedia.swift +++ b/submodules/TelegramCore/Sources/UpdateMessageMedia.swift @@ -28,3 +28,23 @@ func updateMessageMedia(transaction: Transaction, id: MediaId, media: Media?) { }) } } + +func updateMessageThreadStats(transaction: Transaction, threadMessageId: MessageId, difference: Int) { + transaction.updateMessage(threadMessageId, update: { currentMessage in + let countDifference = Int32(difference) + + var attributes = currentMessage.attributes + var updated = false + loop: for j in 0 ..< attributes.count { + if let attribute = attributes[j] as? ReplyThreadMessageAttribute { + attributes[j] = ReplyThreadMessageAttribute(count: max(0, attribute.count + countDifference)) + updated = true + break loop + } + } + if !updated { + attributes.append(ReplyThreadMessageAttribute(count: max(0, countDifference))) + } + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + }) +} diff --git a/submodules/TelegramCore/Sources/UpdateMessageService.swift b/submodules/TelegramCore/Sources/UpdateMessageService.swift index 7ca077332f..b79e3b5e56 100644 --- a/submodules/TelegramCore/Sources/UpdateMessageService.swift +++ b/submodules/TelegramCore/Sources/UpdateMessageService.swift @@ -58,7 +58,7 @@ class UpdateMessageService: NSObject, MTMessageService { self.putNext(groups) } case let .updateShortChatMessage(flags, id, fromId, chatId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyToMsgId, entities): - let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: fromId, toId: Api.Peer.peerChat(chatId: chatId), fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil) + let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: fromId, toId: Api.Peer.peerChat(chatId: chatId), fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, replyToTopId: nil, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, repliesPts: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil) let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount) let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil) if groups.count != 0 { @@ -75,7 +75,7 @@ class UpdateMessageService: NSObject, MTMessageService { generatedToId = Api.Peer.peerUser(userId: self.peerId.id) } - let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: generatedFromId, toId: generatedToId, fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil) + let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: generatedFromId, toId: generatedToId, fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, replyToTopId: nil, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, repliesPts: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil) let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount) let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil) if groups.count != 0 { diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift index 065fbfd7b5..12d70c6efc 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift @@ -162,6 +162,10 @@ public final class PrincipalThemeEssentialGraphics { public let outgoingDateAndStatusImpressionIcon: UIImage public let mediaImpressionIcon: UIImage public let freeImpressionIcon: UIImage + public let incomingDateAndStatusRepliesIcon: UIImage + public let outgoingDateAndStatusRepliesIcon: UIImage + public let mediaRepliesIcon: UIImage + public let freeRepliesIcon: UIImage public let dateStaticBackground: UIImage public let dateFloatingBackground: UIImage @@ -315,6 +319,12 @@ public final class PrincipalThemeEssentialGraphics { self.mediaImpressionIcon = generateTintedImage(image: impressionCountImage, color: .white)! self.freeImpressionIcon = generateTintedImage(image: impressionCountImage, color: serviceColor.primaryText)! + let repliesImage = UIImage(bundleImageName: "Chat/Message/ReplyCount")! + self.incomingDateAndStatusRepliesIcon = generateTintedImage(image: repliesImage, color: theme.message.incoming.secondaryTextColor)! + self.outgoingDateAndStatusRepliesIcon = generateTintedImage(image: repliesImage, color: theme.message.outgoing.secondaryTextColor)! + self.mediaRepliesIcon = generateTintedImage(image: repliesImage, color: .white)! + self.freeRepliesIcon = generateTintedImage(image: repliesImage, color: serviceColor.primaryText)! + self.radialIndicatorFileIconIncoming = emptyImage self.radialIndicatorFileIconOutgoing = emptyImage } else { @@ -410,6 +420,12 @@ public final class PrincipalThemeEssentialGraphics { self.mediaImpressionIcon = generateTintedImage(image: impressionCountImage, color: .white)! self.freeImpressionIcon = generateTintedImage(image: impressionCountImage, color: serviceColor.primaryText)! + let repliesImage = UIImage(bundleImageName: "Chat/Message/ReplyCount")! + self.incomingDateAndStatusRepliesIcon = generateTintedImage(image: repliesImage, color: theme.message.incoming.secondaryTextColor)! + self.outgoingDateAndStatusRepliesIcon = generateTintedImage(image: repliesImage, color: theme.message.outgoing.secondaryTextColor)! + self.mediaRepliesIcon = generateTintedImage(image: repliesImage, color: .white)! + self.freeRepliesIcon = generateTintedImage(image: repliesImage, color: serviceColor.primaryText)! + self.radialIndicatorFileIconIncoming = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocument"), color: .black)! self.radialIndicatorFileIconOutgoing = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocument"), color: .black)! } diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/ReplyCount.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Message/ReplyCount.imageset/Contents.json new file mode 100644 index 0000000000..358678e10d --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Message/ReplyCount.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "replies.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/ReplyCount.imageset/replies.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Message/ReplyCount.imageset/replies.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f55a72204ea6de627d9b6d809a60809d47017a07 GIT binary patch literal 4117 zcmai%cT`hbx5X(@ARvNL1W^x25v3%A08!~BQj{i0=m`nEXb4@Z6lv0ppn&uyMM0$n zq=Rq?ND~zhF(6H(2uQiUM7{5N-~HbB&dA8wW1V?scJ`Wo{FaD;hL$8$3I-Nwr>s#H zi`E|ww0DAK0SMrMaRy(!2uSPU+#Lvx0GwJf0;IK^TnRWl_3nx$;52Y{9#|Zpqy+XP z;Bja-urEC>`LeU6DqHy03YR%k;m{6o6cKt&=|$pu70mUU!I$RaVRp>be(Qp1It4*_ zV*aOXOkatox?GDszaLa({o(ApjYSxYvT)E)lBd8rE6FrUdQ&cdqdwQ!A%;SVarNR| zMB&_X)E{z1@R#w#&gcZx5yRi~iBE7t9urZuYbrfPf2e-wLEJ{Y@iW+(Bq3`rN-A%7 zAH>qt<=dC=t(|IY7%zKfg3nxBIm3(C1iuDP6&;9BF9um6yUg5NhgCXGq|6H2-l23l z;{ry^ND=Jtkax*G8ja7Ed$yJuC$xyJvfw!3^Y5qT`~5ZVYM%@vl%_{dcW<(}KFt~& zLq|UlfW-G23CdM<9DqF09bgPkA6g+{2POog9takNma!s*x_6K65y`hK=OeP32NrK|y3}t=y&|;k<|$9j zr(1WO*w3$=noO@)sUc78rb#_}S7UK|HNxlo;Rn;pGgF~ln9{4m;hZn?W?+Fir#3ct zSElnGf^qKHUwxvkXziu-ly;Ye!e|d^OPSv~f7S0g4v;oLJN)>@}Lv$aGoAs zcsraYAosJN?%__LKKBG@$uXiO;+G%oIn9@rBm=yMoiUC8SWuT5T7VTGt?uFKfj7R6 zw!;B5fx0IYkpE@yn}@=09>3Cv_@R=%Lfw$GDlL)j)Hnjt8aQt!JDd?x?Y{>#^RCZT z<8huSGD)5^a6||Y+2HqQ0bX6W#0@fx)($aXhzu9?);OTW#VKxzz50S486{+=$;Emr z3^T^E#$nLeVyK}>nTT^?St*CFPiE|Qp{B;?o{x^dYi2W?Y{a zTP%+p?QP#;IL1NGZbtuN+TI?7+}2N)y7I7vue zUHBj!SaU?~Vvr~=g(20rx`R*VJLu&`nz&3PRE8&qCHIlgOnas4X3^}qhMs#w(T_R& zsD8di*{rw*gUJk$>AW~lj^#a(_YQeCnQ)}5+8K0|tusSI+Yj{UAYAD-eRHBYuF*1@ zTzsFS*BI!SQ`-@Zc=p(D2*)fs-$I9vF`#H|Od;pkJtxYiZu_0pYU6Jh=(*Ik%hR`g z^~f7FY!s!*mM>8}8EYX|6RCLgSyjC8e5ZvVh0#u~e{APj1? znYuj`>C{g%3cD-&GXySLa6rM`O^uG_NtK=nGX8(g?VMt@jB%gVQx!6v-DFF^;ze>L z`0}R0U0WEABr*{mf@MRcAPf?=hXg`k*BK`Py6})o9E^7vr7kfzseqoSD!rxyt3qGX zd*5=tPB$FtKLENCCU^ayCFt6VL)Tf^LXaxp$QE4){bZ;UbJ!OS?p)Q1v}K z$IY4j)Kv`>xEMRsqZ2ujRlW^~79Tr%t29U5Kyl+Vfxn*F>ekLfrU5|q;68m|IOjcv zA@rfwbk9P@t1bvIBU^jlYxr_MVB&r8c3P*FM~VG;EARC2FLV+SHmxblD&5SCgIZ?H z$5%Df63v9^IR#ykgz_0mG&2&}^N-iXUrM_1k?~+u4*K3Po=(1*C{)}u#uPn@(SeNI zv%6=i$2%)$&jAbl7FK{6d0c2F_DI>AMUL}+ykxtnZGx>u74b3SYxL3u0rsvJd>;g^ z2h|?4lyQewfSw;7i)#p3Y&$e9y6W3_c9Lr)>~M?c7tBq;CUCGo@X4u!Q=QFXi7e(I zm$1WvcL8Bn;j0|{%yZ29;he3qhM~p!1ffU{zjjNE_~SdTRU5ShM6aZ9X-5k48VQT1 z*Mwfxb{4V{RTL!&ru3Z0BJsyiD%ScH!r6e0$l+vVgVZykT>3f!nrh=$5IS`#eky+I z1xAO8-G|Le@6~6T>ytDBbpoZ4DKO$Xai*s{r7X!MZIKutqMtfO^dZ_vWsBJe2J8G; zxL3I>kGqFDQWo~C|S0AJRdc^VxoDb3h znKCJ{%CHJsjJ0<U=1mv6P0W-Q>B>b93>lR@=C}BoHzDN%)`!vE3@#PP;;zTYH8jELY}X8_iJ)Vu&RM|}#Yoa^vUw2o`j<{4pG}VDsthLSo$*B3H{84P zr{32~al-dSZlPGKou5oPAaU6*4f{A}H$G;3bniXUv);2C1h;*n8{(ohlW*6wvN>+vz=;nYKGG! z$$krV*A7>1*X0TO>T>6|L?P?JubAEsF(33r-X$+ip#raco9NpNUnUi77VP@G_h$Ba zH!)woZkCQgS&t7?ytgW+^sVKtJ#V8^H4Fwm}Mlh}92W z+ox8t*e;Iy#RQhli|uD`J{vHPG0z!&HyZscU(P8&FJNjuWGM4Q{|QOu``>SF#BHW$ zRKIl4B`5WBKe&&`_6b^3>x@{9jpmIye~qA2U-R)*W<^!SWN#?3(t5QHU-Fi;yJtcy zbErUAT?ot!BrQko$B&+D$hvE@6y!Ajbj6J{f4zGD?o9cerUQXr56mA$CSL-R7{LJ`VXftrQl;OqMk%Z-(yY?AmQ~CCxAC zZ|kRJ$1D5XWZ7Hu6?LI{y)()c*@e8+mw8TTW{$b>`M~xA&m-|L0nRnt+QqG*$s) zhrmGP6%h7VEE+B+kC2m*Rgi TextNode)? var replyInfoApply: (CGSize, () -> ChatMessageReplyInfoNode)? diff --git a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift index 2b7e30a494..3311a17d9a 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift @@ -314,11 +314,14 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { edited = true } var viewCount: Int? + var dateReplies = 0 for attribute in message.attributes { if let attribute = attribute as? EditedMessageAttribute { edited = !attribute.isHidden } else if let attribute = attribute as? ViewCountMessageAttribute { viewCount = attribute.count + } else if let attribute = attribute as? ReplyThreadMessageAttribute { + dateReplies = Int(attribute.count) } } @@ -564,7 +567,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { } } - statusSizeAndApply = statusLayout(context, presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions) + statusSizeAndApply = statusLayout(context, presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies) } default: break diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index 0a03a0f7de..37d61ca176 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -752,7 +752,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode forwardInfoLayout: (ChatPresentationData, PresentationStrings, ChatMessageForwardInfoType, Peer?, String?, String?, CGSize) -> (CGSize, (CGFloat) -> ChatMessageForwardInfoNode), replyInfoLayout: (ChatPresentationData, PresentationStrings, AccountContext, ChatMessageReplyInfoType, Message, CGSize) -> (CGSize, () -> ChatMessageReplyInfoNode), actionButtonsLayout: (AccountContext, ChatPresentationThemeData, PresentationChatBubbleCorners, PresentationStrings, ReplyMarkupMessageAttribute, Message, CGFloat) -> (minWidth: CGFloat, layout: (CGFloat) -> (CGSize, (Bool) -> ChatMessageActionButtonsNode)), - mosaicStatusLayout: (AccountContext, ChatPresentationData, Bool, Int?, String, ChatMessageDateAndStatusType, CGSize, [MessageReaction]) -> (CGSize, (Bool) -> ChatMessageDateAndStatusNode), + mosaicStatusLayout: (AccountContext, ChatPresentationData, Bool, Int?, String, ChatMessageDateAndStatusType, CGSize, [MessageReaction], Int) -> (CGSize, (Bool) -> ChatMessageDateAndStatusNode), currentShareButtonNode: HighlightableButtonNode?, layoutConstants: ChatMessageItemLayoutConstants, currentItem: ChatMessageItem?, @@ -1206,11 +1206,14 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode edited = true } var viewCount: Int? + var dateReplies = 0 for attribute in message.attributes { if let attribute = attribute as? EditedMessageAttribute { edited = !attribute.isHidden } else if let attribute = attribute as? ViewCountMessageAttribute { viewCount = attribute.count + } else if let attribute = attribute as? ReplyThreadMessageAttribute { + dateReplies = Int(attribute.count) } } @@ -1242,7 +1245,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode } } - mosaicStatusSizeAndApply = mosaicStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: 200.0, height: CGFloat.greatestFiniteMagnitude), dateReactions) + mosaicStatusSizeAndApply = mosaicStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: 200.0, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies) } } diff --git a/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift index a740794c22..936c2e034f 100644 --- a/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift @@ -146,11 +146,14 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { edited = true } var viewCount: Int? + var dateReplies = 0 for attribute in item.message.attributes { if let attribute = attribute as? EditedMessageAttribute { edited = !attribute.isHidden } else if let attribute = attribute as? ViewCountMessageAttribute { viewCount = attribute.count + } else if let attribute = attribute as? ReplyThreadMessageAttribute { + dateReplies = Int(attribute.count) } } @@ -191,7 +194,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { var statusApply: ((Bool) -> Void)? if let statusType = statusType { - let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: constrainedSize.width, height: CGFloat.greatestFiniteMagnitude), dateReactions) + let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: constrainedSize.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies) statusSize = size statusApply = apply } diff --git a/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift index 0d6f734525..e14086a7e8 100644 --- a/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift @@ -160,12 +160,15 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { private var reactionNodes: [StatusReactionNode] = [] private var reactionCountNode: TextNode? private var reactionButtonNode: HighlightTrackingButtonNode? + private var repliesIcon: ASImageNode? + private var replyCountNode: TextNode? private var type: ChatMessageDateAndStatusType? private var theme: ChatPresentationThemeData? private var layoutSize: CGSize? var openReactions: (() -> Void)? + var openReplies: (() -> Void)? override init() { self.dateNode = TextNode() @@ -177,7 +180,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { self.addSubnode(self.dateNode) } - func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ edited: Bool, _ impressionCount: Int?, _ dateText: String, _ type: ChatMessageDateAndStatusType, _ constrainedSize: CGSize, _ reactions: [MessageReaction]) -> (CGSize, (Bool) -> Void) { + func asyncLayout() -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ edited: Bool, _ impressionCount: Int?, _ dateText: String, _ type: ChatMessageDateAndStatusType, _ constrainedSize: CGSize, _ reactions: [MessageReaction], _ replies: Int) -> (CGSize, (Bool) -> Void) { let dateLayout = TextNode.asyncLayout(self.dateNode) var checkReadNode = self.checkReadNode @@ -187,15 +190,17 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { var currentBackgroundNode = self.backgroundNode var currentImpressionIcon = self.impressionIcon + var currentRepliesIcon = self.repliesIcon let currentType = self.type let currentTheme = self.theme let makeReactionCountLayout = TextNode.asyncLayout(self.reactionCountNode) + let makeReplyCountLayout = TextNode.asyncLayout(self.replyCountNode) let previousLayoutSize = self.layoutSize - return { context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions in + return { context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replyCount in let dateColor: UIColor var backgroundImage: UIImage? var outgoingStatus: ChatMessageDateAndStatusOutgoingType? @@ -206,6 +211,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { let clockFrameImage: UIImage? let clockMinImage: UIImage? var impressionImage: UIImage? + var repliesImage: UIImage? let themeUpdated = presentationData.theme != currentTheme || type != currentType @@ -226,6 +232,9 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { if impressionCount != nil { impressionImage = graphics.incomingDateAndStatusImpressionIcon } + if replyCount != 0 { + repliesImage = graphics.incomingDateAndStatusRepliesIcon + } case let .BubbleOutgoing(status): dateColor = presentationData.theme.theme.chat.message.outgoing.secondaryTextColor outgoingStatus = status @@ -237,6 +246,9 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { if impressionCount != nil { impressionImage = graphics.outgoingDateAndStatusImpressionIcon } + if replyCount != 0 { + repliesImage = graphics.outgoingDateAndStatusRepliesIcon + } case .ImageIncoming: dateColor = presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor backgroundImage = graphics.dateAndStatusMediaBackground @@ -248,6 +260,9 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { if impressionCount != nil { impressionImage = graphics.mediaImpressionIcon } + if replyCount != 0 { + repliesImage = graphics.mediaRepliesIcon + } case let .ImageOutgoing(status): dateColor = presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor outgoingStatus = status @@ -260,6 +275,9 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { if impressionCount != nil { impressionImage = graphics.mediaImpressionIcon } + if replyCount != 0 { + repliesImage = graphics.mediaRepliesIcon + } case .FreeIncoming: let serviceColor = serviceMessageColorComponents(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) dateColor = serviceColor.primaryText @@ -272,6 +290,9 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { if impressionCount != nil { impressionImage = graphics.freeImpressionIcon } + if replyCount != 0 { + repliesImage = graphics.freeRepliesIcon + } case let .FreeOutgoing(status): let serviceColor = serviceMessageColorComponents(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) dateColor = serviceColor.primaryText @@ -285,6 +306,9 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { if impressionCount != nil { impressionImage = graphics.freeImpressionIcon } + if replyCount != 0 { + repliesImage = graphics.freeRepliesIcon + } } var updatedDateText = dateText @@ -323,6 +347,20 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { currentImpressionIcon = nil } + var repliesIconSize = CGSize() + if let repliesImage = repliesImage { + if currentRepliesIcon == nil { + let iconNode = ASImageNode() + iconNode.isLayerBacked = true + iconNode.displayWithoutProcessing = true + iconNode.displaysAsynchronously = false + currentRepliesIcon = iconNode + } + repliesIconSize = repliesImage.size + } else { + currentRepliesIcon = nil + } + if let outgoingStatus = outgoingStatus { switch outgoingStatus { case .Sending: @@ -433,6 +471,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { let reactionSize: CGFloat = 14.0 var reactionCountLayoutAndApply: (TextNodeLayout, () -> TextNode)? + var replyCountLayoutAndApply: (TextNodeLayout, () -> TextNode)? let reactionSpacing: CGFloat = -4.0 let reactionTrailingSpacing: CGFloat = 4.0 @@ -458,6 +497,22 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { reactionInset += max(10.0, layoutAndApply.0.size.width) + 2.0 reactionCountLayoutAndApply = layoutAndApply } + + if replyCount > 0 { + let countString: String + if replyCount > 1000000 { + countString = "\(replyCount / 1000000)M" + } else if replyCount > 1000 { + countString = "\(replyCount / 1000)K" + } else { + countString = "\(replyCount)" + } + + let layoutAndApply = makeReplyCountLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: countString, font: dateFont, textColor: dateColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0))) + reactionInset += 14.0 + layoutAndApply.0.size.width + 4.0 + replyCountLayoutAndApply = layoutAndApply + } + leftInset += reactionInset let layoutSize = CGSize(width: leftInset + impressionWidth + date.size.width + statusWidth + backgroundInsets.left + backgroundInsets.right, height: date.size.height + backgroundInsets.top + backgroundInsets.bottom) @@ -510,7 +565,6 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { strongSelf.impressionIcon = nil } - strongSelf.dateNode.frame = CGRect(origin: CGPoint(x: leftInset + backgroundInsets.left + impressionWidth, y: backgroundInsets.top + 1.0 + offset), size: date.size) if let clockFrameNode = clockFrameNode { @@ -677,6 +731,48 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { } } + if let currentRepliesIcon = currentRepliesIcon { + currentRepliesIcon.displaysAsynchronously = !presentationData.isPreview + if currentRepliesIcon.image !== repliesImage { + currentRepliesIcon.image = repliesImage + } + if currentRepliesIcon.supernode == nil { + strongSelf.repliesIcon = currentRepliesIcon + strongSelf.addSubnode(currentRepliesIcon) + } + currentRepliesIcon.frame = CGRect(origin: CGPoint(x: reactionOffset - 2.0, y: backgroundInsets.top + offset + floor((date.size.height - repliesIconSize.height) / 2.0)), size: repliesIconSize) + reactionOffset += 9.0 + } else if let repliesIcon = strongSelf.repliesIcon { + repliesIcon.removeFromSupernode() + strongSelf.repliesIcon = nil + } + + if let (layout, apply) = replyCountLayoutAndApply { + let node = apply() + if strongSelf.replyCountNode !== node { + strongSelf.replyCountNode?.removeFromSupernode() + strongSelf.addSubnode(node) + strongSelf.replyCountNode = node + if animated { + node.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + } + } + node.frame = CGRect(origin: CGPoint(x: reactionOffset + 4.0, y: backgroundInsets.top + 1.0 + offset), size: layout.size) + reactionOffset += 4.0 + layout.size.width + } else if let replyCountNode = strongSelf.replyCountNode { + strongSelf.replyCountNode = nil + if animated { + if let previousLayoutSize = previousLayoutSize { + replyCountNode.frame = replyCountNode.frame.offsetBy(dx: layoutSize.width - previousLayoutSize.width, dy: 0.0) + } + replyCountNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak replyCountNode] _ in + replyCountNode?.removeFromSupernode() + }) + } else { + replyCountNode.removeFromSupernode() + } + } + /*if !strongSelf.reactionNodes.isEmpty { if strongSelf.reactionButtonNode == nil { let reactionButtonNode = HighlightTrackingButtonNode() @@ -709,17 +805,17 @@ class ChatMessageDateAndStatusNode: ASDisplayNode { } } - static func asyncLayout(_ node: ChatMessageDateAndStatusNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ edited: Bool, _ impressionCount: Int?, _ dateText: String, _ type: ChatMessageDateAndStatusType, _ constrainedSize: CGSize, _ reactions: [MessageReaction]) -> (CGSize, (Bool) -> ChatMessageDateAndStatusNode) { + static func asyncLayout(_ node: ChatMessageDateAndStatusNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ edited: Bool, _ impressionCount: Int?, _ dateText: String, _ type: ChatMessageDateAndStatusType, _ constrainedSize: CGSize, _ reactions: [MessageReaction], _ replies: Int) -> (CGSize, (Bool) -> ChatMessageDateAndStatusNode) { let currentLayout = node?.asyncLayout() - return { context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions in + return { context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replies in let resultNode: ChatMessageDateAndStatusNode let resultSizeAndApply: (CGSize, (Bool) -> Void) if let node = node, let currentLayout = currentLayout { resultNode = node - resultSizeAndApply = currentLayout(context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions) + resultSizeAndApply = currentLayout(context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replies) } else { resultNode = ChatMessageDateAndStatusNode() - resultSizeAndApply = resultNode.asyncLayout()(context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions) + resultSizeAndApply = resultNode.asyncLayout()(context, presentationData, edited, impressionCount, dateText, type, constrainedSize, reactions, replies) } return (resultSizeAndApply.0, { animated in diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift index c2cf930192..234d060fed 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift @@ -293,11 +293,14 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { edited = true } var viewCount: Int? + var dateReplies = 0 for attribute in message.attributes { if let attribute = attribute as? EditedMessageAttribute { edited = !attribute.isHidden } else if let attribute = attribute as? ViewCountMessageAttribute { viewCount = attribute.count + } else if let attribute = attribute as? ReplyThreadMessageAttribute { + dateReplies = Int(attribute.count) } } @@ -316,7 +319,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { let dateText = stringForMessageTimestampStatus(accountPeerId: context.account.peerId, message: message, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, strings: presentationData.strings, reactionCount: dateReactionCount) - let (size, apply) = statusLayout(context, presentationData, edited, viewCount, dateText, statusType, constrainedSize, dateReactions) + let (size, apply) = statusLayout(context, presentationData, edited, viewCount, dateText, statusType, constrainedSize, dateReactions, dateReplies) statusSize = size statusApply = apply } diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift index 87c3688d60..32a60cdaad 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -250,11 +250,14 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } let sentViaBot = false var viewCount: Int? = nil + var dateReplies = 0 for attribute in item.message.attributes { if let attribute = attribute as? EditedMessageAttribute { edited = !attribute.isHidden } else if let attribute = attribute as? ViewCountMessageAttribute { viewCount = attribute.count + } else if let attribute = attribute as? ReplyThreadMessageAttribute { + dateReplies = Int(attribute.count) } } @@ -279,7 +282,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } else { maxDateAndStatusWidth = width - videoFrame.midX - 85.0 } - let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited && !sentViaBot, viewCount, dateText, statusType, CGSize(width: max(1.0, maxDateAndStatusWidth), height: CGFloat.greatestFiniteMagnitude), dateReactions) + let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited && !sentViaBot, viewCount, dateText, statusType, CGSize(width: max(1.0, maxDateAndStatusWidth), height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies) var contentSize = imageSize var dateAndStatusOverflow = false diff --git a/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift index 192883b28f..f82c61b85a 100644 --- a/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift @@ -176,11 +176,14 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { edited = true } var viewCount: Int? + var dateReplies = 0 for attribute in item.message.attributes { if let attribute = attribute as? EditedMessageAttribute { edited = !attribute.isHidden } else if let attribute = attribute as? ViewCountMessageAttribute { viewCount = attribute.count + } else if let attribute = attribute as? ReplyThreadMessageAttribute { + dateReplies = Int(attribute.count) } } @@ -241,7 +244,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { var statusApply: ((Bool) -> Void)? if let statusType = statusType { - let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: constrainedSize.width, height: CGFloat.greatestFiniteMagnitude), dateReactions) + let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: constrainedSize.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies) statusSize = size statusApply = apply } diff --git a/submodules/TelegramUI/Sources/ChatMessageMediaBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageMediaBubbleContentNode.swift index 577556c523..15ce63a598 100644 --- a/submodules/TelegramUI/Sources/ChatMessageMediaBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageMediaBubbleContentNode.swift @@ -174,6 +174,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { edited = true } var viewCount: Int? + var dateReplies = 0 for attribute in item.message.attributes { if let attribute = attribute as? EditedMessageAttribute { if case .mosaic = preparePosition { @@ -182,6 +183,8 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { } } else if let attribute = attribute as? ViewCountMessageAttribute { viewCount = attribute.count + } else if let attribute = attribute as? ReplyThreadMessageAttribute { + dateReplies = Int(attribute.count) } } @@ -226,7 +229,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { var statusApply: ((Bool) -> Void)? if let statusType = statusType { - let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: imageSize.width - 30.0, height: CGFloat.greatestFiniteMagnitude), dateReactions) + let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: imageSize.width - 30.0, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies) statusSize = size statusApply = apply } diff --git a/submodules/TelegramUI/Sources/ChatMessagePollBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessagePollBubbleContentNode.swift index bb6149c63f..a5f4b74f8e 100644 --- a/submodules/TelegramUI/Sources/ChatMessagePollBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessagePollBubbleContentNode.swift @@ -1024,11 +1024,14 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { edited = true } var viewCount: Int? + var dateReplies = 0 for attribute in item.message.attributes { if let attribute = attribute as? EditedMessageAttribute { edited = !attribute.isHidden } else if let attribute = attribute as? ViewCountMessageAttribute { viewCount = attribute.count + } else if let attribute = attribute as? ReplyThreadMessageAttribute { + dateReplies = Int(attribute.count) } } @@ -1069,7 +1072,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { var statusApply: ((Bool) -> Void)? if let statusType = statusType { - let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions) + let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies) statusSize = size statusApply = apply } diff --git a/submodules/TelegramUI/Sources/ChatMessageRestrictedBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageRestrictedBubbleContentNode.swift index c7ec00047c..626de1c9df 100644 --- a/submodules/TelegramUI/Sources/ChatMessageRestrictedBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageRestrictedBubbleContentNode.swift @@ -53,6 +53,7 @@ class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNode { } var viewCount: Int? var rawText = "" + var dateReplies = 0 for attribute in item.message.attributes { if let attribute = attribute as? EditedMessageAttribute { edited = !attribute.isHidden @@ -60,6 +61,8 @@ class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNode { viewCount = attribute.count } else if let attribute = attribute as? RestrictedContentMessageAttribute { rawText = attribute.platformText(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) ?? "" + } else if let attribute = attribute as? ReplyThreadMessageAttribute { + dateReplies = Int(attribute.count) } } @@ -100,7 +103,7 @@ class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNode { var statusApply: ((Bool) -> Void)? if let statusType = statusType { - let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions) + let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies) statusSize = size statusApply = apply } diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index 3d4d47aeba..c50b3ad5ad 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -337,11 +337,14 @@ class ChatMessageStickerItemNode: ChatMessageItemView { var edited = false var viewCount: Int? = nil + var dateReplies = 0 for attribute in item.message.attributes { if let _ = attribute as? EditedMessageAttribute, isEmoji { edited = true } else if let attribute = attribute as? ViewCountMessageAttribute { viewCount = attribute.count + } else if let attribute = attribute as? ReplyThreadMessageAttribute { + dateReplies = Int(attribute.count) } } @@ -360,7 +363,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular, reactionCount: dateReactionCount) - let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), dateReactions) + let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies) var viaBotApply: (TextNodeLayout, () -> TextNode)? var replyInfoApply: (CGSize, () -> ChatMessageReplyInfoNode)? diff --git a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift index b36dc9ba26..15b83e1a01 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift @@ -109,11 +109,14 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { edited = true } var viewCount: Int? + var dateReplies = 0 for attribute in item.message.attributes { if let attribute = attribute as? EditedMessageAttribute { edited = !attribute.isHidden } else if let attribute = attribute as? ViewCountMessageAttribute { viewCount = attribute.count + } else if let attribute = attribute as? ReplyThreadMessageAttribute { + dateReplies = Int(attribute.count) } } @@ -154,7 +157,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { var statusApply: ((Bool) -> Void)? if let statusType = statusType { - let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions) + let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies) statusSize = size statusApply = apply }