Better history-ad integration

This commit is contained in:
Ali 2022-11-01 15:17:08 +04:00
parent 554cd0e6fe
commit efe0566b79
15 changed files with 186 additions and 131 deletions

View File

@ -689,6 +689,10 @@ public final class Message {
self.associatedThreadInfo = associatedThreadInfo self.associatedThreadInfo = associatedThreadInfo
} }
public func withUpdatedStableVersion(stableVersion: UInt32) -> Message {
return Message(stableId: self.stableId, stableVersion: stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: self.text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo)
}
public func withUpdatedText(_ text: String) -> Message { public func withUpdatedText(_ text: String) -> Message {
return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo) return Message(stableId: self.stableId, stableVersion: self.stableVersion, id: self.id, globallyUniqueId: self.globallyUniqueId, groupingKey: self.groupingKey, groupInfo: self.groupInfo, threadId: self.threadId, timestamp: self.timestamp, flags: self.flags, tags: self.tags, globalTags: self.globalTags, localTags: self.localTags, forwardInfo: self.forwardInfo, author: self.author, text: text, attributes: self.attributes, media: self.media, peers: self.peers, associatedMessages: self.associatedMessages, associatedMessageIds: self.associatedMessageIds, associatedMedia: self.associatedMedia, associatedThreadInfo: self.associatedThreadInfo)
} }

View File

@ -4038,6 +4038,32 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})], parseMarkdown: true), in: .window(.root), with: nil) })], parseMarkdown: true), in: .window(.root), with: nil)
} }
}) })
}, activateAdAction: { [weak self] messageId in
guard let self, let message = self.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId), let adAttribute = message.adAttribute else {
return
}
switch adAttribute.target {
case let .peer(id, messageId, startParam):
let navigationData: ChatControllerInteractionNavigateToPeer
if let bot = message.author as? TelegramUser, bot.botInfo != nil, let startParam = startParam {
navigationData = .withBotStartPayload(ChatControllerInitialBotStart(payload: startParam, behavior: .interactive))
} else {
var subject: ChatControllerSubject?
if let messageId = messageId {
subject = .message(id: .id(messageId), highlight: true, timecode: nil)
}
navigationData = .chat(textInputState: nil, subject: subject, peekData: nil)
}
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: id))
|> deliverOnMainQueue).start(next: { [weak self] peer in
if let self, let peer = peer {
self.openPeer(peer: peer, navigation: navigationData, fromMessage: nil)
}
})
case let .join(_, joinHash):
self.controllerInteraction?.openJoinLink(joinHash)
}
}, requestMessageUpdate: { [weak self] id, scroll in }, requestMessageUpdate: { [weak self] id, scroll in
if let strongSelf = self { if let strongSelf = self {
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id, andScrollToItem: scroll) strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(id, andScrollToItem: scroll)
@ -6554,13 +6580,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var minOffsetForNavigation: CGFloat = 40.0 var minOffsetForNavigation: CGFloat = 40.0
strongSelf.chatDisplayNode.historyNode.enumerateItemNodes { itemNode in strongSelf.chatDisplayNode.historyNode.enumerateItemNodes { itemNode in
if let itemNode = itemNode as? ChatMessageBubbleItemNode { if let itemNode = itemNode as? ChatMessageBubbleItemNode {
if let message = itemNode.item?.content.firstMessage, message.adAttribute != nil { if let message = itemNode.item?.content.firstMessage, let adAttribute = message.adAttribute {
minOffsetForNavigation += itemNode.bounds.height minOffsetForNavigation += itemNode.bounds.height
switch offset { switch offset {
case let .known(offset): case let .known(offset):
if offset <= 50.0 { if offset <= 50.0 {
strongSelf.chatDisplayNode.historyNode.adSeenProcessingManager.add([message.id]) strongSelf.chatDisplayNode.historyNode.markAdAsSeen(opaqueId: adAttribute.opaqueId)
} }
default: default:
break break

View File

@ -141,6 +141,7 @@ public final class ChatControllerInteraction {
let openLargeEmojiInfo: (String, String?, TelegramMediaFile) -> Void let openLargeEmojiInfo: (String, String?, TelegramMediaFile) -> Void
let openJoinLink: (String) -> Void let openJoinLink: (String) -> Void
let openWebView: (String, String, Bool, Bool) -> Void let openWebView: (String, String, Bool, Bool) -> Void
let activateAdAction: (EngineMessage.Id) -> Void
let requestMessageUpdate: (MessageId, Bool) -> Void let requestMessageUpdate: (MessageId, Bool) -> Void
let cancelInteractiveKeyboardGestures: () -> Void let cancelInteractiveKeyboardGestures: () -> Void
@ -248,6 +249,7 @@ public final class ChatControllerInteraction {
openLargeEmojiInfo: @escaping (String, String?, TelegramMediaFile) -> Void, openLargeEmojiInfo: @escaping (String, String?, TelegramMediaFile) -> Void,
openJoinLink: @escaping (String) -> Void, openJoinLink: @escaping (String) -> Void,
openWebView: @escaping (String, String, Bool, Bool) -> Void, openWebView: @escaping (String, String, Bool, Bool) -> Void,
activateAdAction: @escaping (EngineMessage.Id) -> Void,
requestMessageUpdate: @escaping (MessageId, Bool) -> Void, requestMessageUpdate: @escaping (MessageId, Bool) -> Void,
cancelInteractiveKeyboardGestures: @escaping () -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void,
dismissTextInput: @escaping () -> Void, dismissTextInput: @escaping () -> Void,
@ -338,6 +340,7 @@ public final class ChatControllerInteraction {
self.openLargeEmojiInfo = openLargeEmojiInfo self.openLargeEmojiInfo = openLargeEmojiInfo
self.openJoinLink = openJoinLink self.openJoinLink = openJoinLink
self.openWebView = openWebView self.openWebView = openWebView
self.activateAdAction = activateAdAction
self.requestMessageUpdate = requestMessageUpdate self.requestMessageUpdate = requestMessageUpdate
self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures
self.dismissTextInput = dismissTextInput self.dismissTextInput = dismissTextInput

View File

@ -25,7 +25,7 @@ func chatHistoryEntriesForView(
customChannelDiscussionReadState: MessageId?, customChannelDiscussionReadState: MessageId?,
customThreadOutgoingReadState: MessageId?, customThreadOutgoingReadState: MessageId?,
cachedData: CachedPeerData?, cachedData: CachedPeerData?,
adMessages: (interPostInterval: Int32?, messages: [Message]), adMessage: Message?,
dynamicAdMessages: [Message] dynamicAdMessages: [Message]
) -> [ChatHistoryEntry] { ) -> [ChatHistoryEntry] {
if historyAppearsCleared { if historyAppearsCleared {
@ -339,36 +339,34 @@ func chatHistoryEntriesForView(
} }
if view.laterId == nil && !view.isLoading { if view.laterId == nil && !view.isLoading {
if !entries.isEmpty, case let .MessageEntry(lastMessage, _, _, _, _, _) = entries[entries.count - 1], !adMessages.messages.isEmpty, adMessages.interPostInterval == nil { if !entries.isEmpty, case let .MessageEntry(lastMessage, _, _, _, _, _) = entries[entries.count - 1], let message = adMessage {
var nextAdMessageId: Int32 = 1 var nextAdMessageId: Int32 = 10000
if let message = adMessages.messages.first { let updatedMessage = Message(
let updatedMessage = Message( stableId: ChatHistoryListNode.fixedAdMessageStableId,
stableId: UInt32.max - 1 - UInt32(nextAdMessageId), stableVersion: message.stableVersion,
stableVersion: message.stableVersion, id: MessageId(peerId: message.id.peerId, namespace: message.id.namespace, id: nextAdMessageId),
id: MessageId(peerId: message.id.peerId, namespace: message.id.namespace, id: nextAdMessageId), globallyUniqueId: nil,
globallyUniqueId: nil, groupingKey: nil,
groupingKey: nil, groupInfo: nil,
groupInfo: nil, threadId: nil,
threadId: nil, timestamp: lastMessage.timestamp,
timestamp: lastMessage.timestamp, flags: message.flags,
flags: message.flags, tags: message.tags,
tags: message.tags, globalTags: message.globalTags,
globalTags: message.globalTags, localTags: message.localTags,
localTags: message.localTags, forwardInfo: message.forwardInfo,
forwardInfo: message.forwardInfo, author: message.author,
author: message.author, text: /*"\(message.adAttribute!.opaqueId.hashValue)" + */message.text,
text: message.text, attributes: message.attributes,
attributes: message.attributes, media: message.media,
media: message.media, peers: message.peers,
peers: message.peers, associatedMessages: message.associatedMessages,
associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds,
associatedMessageIds: message.associatedMessageIds, associatedMedia: message.associatedMedia,
associatedMedia: message.associatedMedia, associatedThreadInfo: message.associatedThreadInfo
associatedThreadInfo: message.associatedThreadInfo )
) nextAdMessageId += 1
nextAdMessageId += 1 entries.append(.MessageEntry(updatedMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false)))
entries.append(.MessageEntry(updatedMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false)))
}
} }
} }
} else if includeSearchEntry { } else if includeSearchEntry {

View File

@ -408,6 +408,8 @@ public enum ChatHistoryListSource {
} }
public final class ChatHistoryListNode: ListView, ChatHistoryNode { public final class ChatHistoryListNode: ListView, ChatHistoryNode {
static let fixedAdMessageStableId: UInt32 = UInt32.max - 5000
private let context: AccountContext private let context: AccountContext
private let chatLocation: ChatLocation private let chatLocation: ChatLocation
private let chatLocationContextHolder: Atomic<ChatLocationContextHolder?> private let chatLocationContextHolder: Atomic<ChatLocationContextHolder?>
@ -495,7 +497,6 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
private let messageProcessingManager = ChatMessageThrottledProcessingManager() private let messageProcessingManager = ChatMessageThrottledProcessingManager()
private let messageWithReactionsProcessingManager = ChatMessageThrottledProcessingManager(submitInterval: 4.0) private let messageWithReactionsProcessingManager = ChatMessageThrottledProcessingManager(submitInterval: 4.0)
let adSeenProcessingManager = ChatMessageThrottledProcessingManager()
private let seenLiveLocationProcessingManager = ChatMessageThrottledProcessingManager() private let seenLiveLocationProcessingManager = ChatMessageThrottledProcessingManager()
private let unsupportedMessageProcessingManager = ChatMessageThrottledProcessingManager() private let unsupportedMessageProcessingManager = ChatMessageThrottledProcessingManager()
private let refreshMediaProcessingManager = ChatMessageThrottledProcessingManager() private let refreshMediaProcessingManager = ChatMessageThrottledProcessingManager()
@ -606,18 +607,20 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
private var contentInsetAnimator: DisplayLinkAnimator? private var contentInsetAnimator: DisplayLinkAnimator?
private let adMessagesContext: AdMessagesHistoryContext? private let adMessagesContext: AdMessagesHistoryContext?
private var adMessagesDisposable: Disposable?
private var preloadAdPeerId: PeerId? private var preloadAdPeerId: PeerId?
private let preloadAdPeerDisposable = MetaDisposable() private let preloadAdPeerDisposable = MetaDisposable()
private var pendingDynamicAdMessages: [Message] = [] private var pendingDynamicAdMessages: [Message] = []
private var pendingDynamicAdMessageInterval: Int? private var pendingDynamicAdMessageInterval: Int?
private var remainingDynamicAdMessageInterval: Int? private var remainingDynamicAdMessageInterval: Int?
private var remainingDynamicAdMessageDistance: CGFloat?
private var nextPendingDynamicMessageId: Int32 = 1 private var nextPendingDynamicMessageId: Int32 = 1
private var dynamicAdMessages: ([Message], Int) = ([], 0) { private var allAdMessages: (fixed: Message?, opportunistic: [Message], version: Int) = (nil, [], 0) {
didSet { didSet {
self.dynamicAdMessagesPromise.set(.single(self.dynamicAdMessages)) self.allAdMessagesPromise.set(.single(self.allAdMessages))
} }
} }
private let dynamicAdMessagesPromise = Promise<([Message], Int)>(([], 0)) private let allAdMessagesPromise = Promise<(fixed: Message?, opportunistic: [Message], version: Int)>((nil, [], 0))
private var seenMessageIds = Set<MessageId>() private var seenMessageIds = Set<MessageId>()
private var refreshDisplayedItemRangeTimer: SwiftSignalKit.Timer? private var refreshDisplayedItemRangeTimer: SwiftSignalKit.Timer?
@ -686,35 +689,43 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
super.init() super.init()
adMessages = adMessages self.adMessagesDisposable = (adMessages
|> afterNext { [weak self] interPostInterval, messages in |> deliverOnMainQueue).start(next: { [weak self] interPostInterval, messages in
Queue.mainQueue().async { guard let self else {
guard let strongSelf = self else { return
return }
if let interPostInterval = interPostInterval {
self.pendingDynamicAdMessages = messages
self.pendingDynamicAdMessageInterval = Int(interPostInterval)
if self.remainingDynamicAdMessageInterval == nil {
self.remainingDynamicAdMessageInterval = Int(interPostInterval)
}
if self.remainingDynamicAdMessageDistance == nil {
self.remainingDynamicAdMessageDistance = self.bounds.height
} }
if let interPostInterval = interPostInterval { self.allAdMessages = (messages.first, [], 0)
strongSelf.pendingDynamicAdMessages = messages } else {
strongSelf.pendingDynamicAdMessageInterval = Int(interPostInterval) var adPeerId: PeerId?
strongSelf.remainingDynamicAdMessageInterval = Int(interPostInterval) adPeerId = messages.first?.author?.id
} else {
var adPeerId: PeerId? if self.preloadAdPeerId != adPeerId {
adPeerId = messages.first?.author?.id self.preloadAdPeerId = adPeerId
if let adPeerId = adPeerId {
if strongSelf.preloadAdPeerId != adPeerId { let combinedDisposable = DisposableSet()
strongSelf.preloadAdPeerId = adPeerId self.preloadAdPeerDisposable.set(combinedDisposable)
if let adPeerId = adPeerId { combinedDisposable.add(self.context.account.viewTracker.polledChannel(peerId: adPeerId).start())
let combinedDisposable = DisposableSet() combinedDisposable.add(self.context.account.addAdditionalPreloadHistoryPeerId(peerId: adPeerId))
strongSelf.preloadAdPeerDisposable.set(combinedDisposable) } else {
combinedDisposable.add(strongSelf.context.account.viewTracker.polledChannel(peerId: adPeerId).start()) self.preloadAdPeerDisposable.set(nil)
combinedDisposable.add(strongSelf.context.account.addAdditionalPreloadHistoryPeerId(peerId: adPeerId))
} else {
strongSelf.preloadAdPeerDisposable.set(nil)
}
} }
} }
self.allAdMessages = (messages.first, [], 0)
} }
} })
self.clipsToBounds = false self.clipsToBounds = false
@ -737,16 +748,6 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
self.messageWithReactionsProcessingManager.process = { [weak context] messageIds in self.messageWithReactionsProcessingManager.process = { [weak context] messageIds in
context?.account.viewTracker.updateReactionsForMessageIds(messageIds: messageIds) context?.account.viewTracker.updateReactionsForMessageIds(messageIds: messageIds)
} }
self.adSeenProcessingManager.process = { [weak self] messageIds in
guard let strongSelf = self, let adMessagesContext = strongSelf.adMessagesContext else {
return
}
for id in messageIds {
if let message = strongSelf.messageInCurrentHistoryView(id), let adAttribute = message.adAttribute {
adMessagesContext.markAsSeen(opaqueId: adAttribute.opaqueId)
}
}
}
self.seenLiveLocationProcessingManager.process = { [weak context] messageIds in self.seenLiveLocationProcessingManager.process = { [weak context] messageIds in
context?.account.viewTracker.updateSeenLiveLocationForMessageIds(messageIds: messageIds) context?.account.viewTracker.updateSeenLiveLocationForMessageIds(messageIds: messageIds)
} }
@ -1096,15 +1097,14 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
additionalAnimatedEmojiStickers, additionalAnimatedEmojiStickers,
customChannelDiscussionReadState, customChannelDiscussionReadState,
customThreadOutgoingReadState, customThreadOutgoingReadState,
adMessages,
availableReactions, availableReactions,
defaultReaction, defaultReaction,
accountPeer, accountPeer,
audioTranscriptionSuggestion, audioTranscriptionSuggestion,
promises, promises,
topicAuthorId, topicAuthorId,
self.dynamicAdMessagesPromise.get() self.allAdMessagesPromise.get()
).start(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, animatedEmojiStickers, additionalAnimatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, adMessages, availableReactions, defaultReaction, accountPeer, suggestAudioTranscription, promises, topicAuthorId, dynamicAdMessages in ).start(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, animatedEmojiStickers, additionalAnimatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, availableReactions, defaultReaction, accountPeer, suggestAudioTranscription, promises, topicAuthorId, allAdMessages in
let (historyAppearsCleared, pendingUnpinnedAllMessages, pendingRemovedMessages, currentlyPlayingMessageIdAndType, scrollToMessageId) = promises let (historyAppearsCleared, pendingUnpinnedAllMessages, pendingRemovedMessages, currentlyPlayingMessageIdAndType, scrollToMessageId) = promises
let currentlyPlayingMessageId = currentlyPlayingMessageIdAndType?.0 let currentlyPlayingMessageId = currentlyPlayingMessageIdAndType?.0
@ -1265,12 +1265,12 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
customChannelDiscussionReadState: customChannelDiscussionReadState, customChannelDiscussionReadState: customChannelDiscussionReadState,
customThreadOutgoingReadState: customThreadOutgoingReadState, customThreadOutgoingReadState: customThreadOutgoingReadState,
cachedData: data.cachedData, cachedData: data.cachedData,
adMessages: adMessages, adMessage: allAdMessages.fixed,
dynamicAdMessages: dynamicAdMessages.0 dynamicAdMessages: allAdMessages.opportunistic
) )
let lastHeaderId = filteredEntries.last.flatMap { listMessageDateHeaderId(timestamp: $0.index.timestamp) } ?? 0 let lastHeaderId = filteredEntries.last.flatMap { listMessageDateHeaderId(timestamp: $0.index.timestamp) } ?? 0
let processedView = ChatHistoryView(originalView: view, filteredEntries: filteredEntries, associatedData: associatedData, lastHeaderId: lastHeaderId, id: id, locationInput: update.2, ignoreMessagesInTimestampRange: update.3) let processedView = ChatHistoryView(originalView: view, filteredEntries: filteredEntries, associatedData: associatedData, lastHeaderId: lastHeaderId, id: id, locationInput: update.2, ignoreMessagesInTimestampRange: update.3)
let previousValueAndVersion = previousView.swap((processedView, update.1, selectedMessages, dynamicAdMessages.1)) let previousValueAndVersion = previousView.swap((processedView, update.1, selectedMessages, allAdMessages.version))
let previous = previousValueAndVersion?.0 let previous = previousValueAndVersion?.0
let previousSelectedMessages = previousValueAndVersion?.2 let previousSelectedMessages = previousValueAndVersion?.2
@ -1323,7 +1323,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
var disableAnimations = false var disableAnimations = false
var forceSynchronous = false var forceSynchronous = false
if let previousValueAndVersion = previousValueAndVersion, dynamicAdMessages.1 != previousValueAndVersion.3 { if let previousValueAndVersion = previousValueAndVersion, allAdMessages.version != previousValueAndVersion.3 {
reason = ChatHistoryViewTransitionReason.Reload reason = ChatHistoryViewTransitionReason.Reload
disableAnimations = true disableAnimations = true
forceSynchronous = true forceSynchronous = true
@ -1688,6 +1688,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
self.preloadAdPeerDisposable.dispose() self.preloadAdPeerDisposable.dispose()
self.refreshDisplayedItemRangeTimer?.invalidate() self.refreshDisplayedItemRangeTimer?.invalidate()
self.genericReactionEffectDisposable?.dispose() self.genericReactionEffectDisposable?.dispose()
self.adMessagesDisposable?.dispose()
} }
private func attemptReadingReactions() { private func attemptReadingReactions() {
@ -1895,7 +1896,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
let initialMessage = self.pendingDynamicAdMessages.removeFirst() let initialMessage = self.pendingDynamicAdMessages.removeFirst()
let message = Message( let message = Message(
stableId: UInt32.max - 1 - UInt32(self.nextPendingDynamicMessageId), stableId: UInt32.max - 1 - UInt32(self.nextPendingDynamicMessageId),
stableVersion: 1, stableVersion: initialMessage.stableVersion,
id: MessageId(peerId: initialMessage.id.peerId, namespace: initialMessage.id.namespace, id: self.nextPendingDynamicMessageId), id: MessageId(peerId: initialMessage.id.peerId, namespace: initialMessage.id.namespace, id: self.nextPendingDynamicMessageId),
globallyUniqueId: nil, globallyUniqueId: nil,
groupingKey: nil, groupingKey: nil,
@ -1908,7 +1909,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
localTags: initialMessage.localTags, localTags: initialMessage.localTags,
forwardInfo: initialMessage.forwardInfo, forwardInfo: initialMessage.forwardInfo,
author: initialMessage.author, author: initialMessage.author,
text: initialMessage.text, text: /*"\(initialMessage.adAttribute!.opaqueId.hashValue)" + */initialMessage.text,
attributes: initialMessage.attributes, attributes: initialMessage.attributes,
media: initialMessage.media, media: initialMessage.media,
peers: initialMessage.peers, peers: initialMessage.peers,
@ -1919,12 +1920,26 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
) )
self.nextPendingDynamicMessageId += 1 self.nextPendingDynamicMessageId += 1
var dynamicAdMessages = self.dynamicAdMessages var allAdMessages = self.allAdMessages
dynamicAdMessages.0.append(message) if allAdMessages.fixed?.adAttribute?.opaqueId == message.adAttribute?.opaqueId {
dynamicAdMessages.1 += 1 allAdMessages.fixed = self.pendingDynamicAdMessages.first?.withUpdatedStableVersion(stableVersion: UInt32(self.nextPendingDynamicMessageId))
self.dynamicAdMessages = dynamicAdMessages }
allAdMessages.opportunistic.append(message)
allAdMessages.version += 1
self.allAdMessages = allAdMessages
} }
} }
//TODO:loc mark all ads as seen
}
func markAdAsSeen(opaqueId: Data) {
for i in 0 ..< self.pendingDynamicAdMessages.count {
if let pendingAttribute = self.pendingDynamicAdMessages[i].adAttribute, pendingAttribute.opaqueId == opaqueId {
self.pendingDynamicAdMessages.remove(at: i)
break
}
}
self.adMessagesContext?.markAsSeen(opaqueId: opaqueId)
} }
private func processDisplayedItemRangeChanged(displayedRange: ListViewDisplayedItemRange, transactionState: ChatHistoryTransactionOpaqueState) { private func processDisplayedItemRangeChanged(displayedRange: ListViewDisplayedItemRange, transactionState: ChatHistoryTransactionOpaqueState) {
@ -1952,10 +1967,13 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
var messageIdsWithUnseenReactions: [MessageId] = [] var messageIdsWithUnseenReactions: [MessageId] = []
var messageIdsWithInactiveExtendedMedia = Set<MessageId>() var messageIdsWithInactiveExtendedMedia = Set<MessageId>()
var downloadableResourceIds: [(messageId: MessageId, resourceId: String)] = [] var downloadableResourceIds: [(messageId: MessageId, resourceId: String)] = []
var allVisibleAnchorMessageIds: [MessageId] = [] var allVisibleAnchorMessageIds: [(MessageId, Int)] = []
var visibleAdOpaqueIds: [Data] = []
if indexRange.0 <= indexRange.1 { if indexRange.0 <= indexRange.1 {
for i in (indexRange.0 ... indexRange.1) { for i in (indexRange.0 ... indexRange.1) {
let nodeIndex = historyView.filteredEntries.count - 1 - i
switch historyView.filteredEntries[i] { switch historyView.filteredEntries[i] {
case let .MessageEntry(message, _, _, _, _, _): case let .MessageEntry(message, _, _, _, _, _):
var hasUnconsumedMention = false var hasUnconsumedMention = false
@ -1985,6 +2003,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
contentRequiredValidation = true contentRequiredValidation = true
} else if let attribute = attribute as? ReactionsMessageAttribute, attribute.hasUnseen { } else if let attribute = attribute as? ReactionsMessageAttribute, attribute.hasUnseen {
hasUnseenReactions = true hasUnseenReactions = true
} else if let attribute = attribute as? AdMessageAttribute {
if message.stableId != ChatHistoryListNode.fixedAdMessageStableId {
visibleAdOpaqueIds.append(attribute.opaqueId)
}
} }
} }
for media in message.media { for media in message.media {
@ -2044,7 +2066,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
topVisibleMessageRange = ChatTopVisibleMessageRange(lowerBound: message.index, upperBound: message.index, isLast: i == historyView.filteredEntries.count - 1) topVisibleMessageRange = ChatTopVisibleMessageRange(lowerBound: message.index, upperBound: message.index, isLast: i == historyView.filteredEntries.count - 1)
} }
if message.id.namespace == Namespaces.Message.Cloud, self.remainingDynamicAdMessageInterval != nil { if message.id.namespace == Namespaces.Message.Cloud, self.remainingDynamicAdMessageInterval != nil {
allVisibleAnchorMessageIds.append(message.id) allVisibleAnchorMessageIds.append((message.id, nodeIndex))
} }
case let .MessageGroupEntry(_, messages, _): case let .MessageGroupEntry(_, messages, _):
for (message, _, _, _, _) in messages { for (message, _, _, _, _) in messages {
@ -2099,7 +2121,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
} }
if let message = messages.first { if let message = messages.first {
if message.0.id.namespace == Namespaces.Message.Cloud, self.remainingDynamicAdMessageInterval != nil { if message.0.id.namespace == Namespaces.Message.Cloud, self.remainingDynamicAdMessageInterval != nil {
allVisibleAnchorMessageIds.append(message.0.id) allVisibleAnchorMessageIds.append((message.0.id, nodeIndex))
} }
} }
default: default:
@ -2242,6 +2264,11 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
if !messageIdsWithInactiveExtendedMedia.isEmpty { if !messageIdsWithInactiveExtendedMedia.isEmpty {
self.extendedMediaProcessingManager.update(messageIdsWithInactiveExtendedMedia) self.extendedMediaProcessingManager.update(messageIdsWithInactiveExtendedMedia)
} }
if !visibleAdOpaqueIds.isEmpty {
for opaqueId in visibleAdOpaqueIds {
self.markAdAsSeen(opaqueId: opaqueId)
}
}
self.currentEarlierPrefetchMessages = toEarlierMediaMessages self.currentEarlierPrefetchMessages = toEarlierMediaMessages
self.currentLaterPrefetchMessages = toLaterMediaMessages self.currentLaterPrefetchMessages = toLaterMediaMessages
@ -2275,15 +2302,23 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
if let visible = displayedRange.visibleRange { if let visible = displayedRange.visibleRange {
let indexRange = (historyView.filteredEntries.count - 1 - visible.lastIndex, historyView.filteredEntries.count - 1 - visible.firstIndex) let indexRange = (historyView.filteredEntries.count - 1 - visible.lastIndex, historyView.filteredEntries.count - 1 - visible.firstIndex)
if indexRange.0 <= indexRange.1 { if indexRange.0 <= indexRange.1 {
for messageId in allVisibleAnchorMessageIds { for (messageId, nodeIndex) in allVisibleAnchorMessageIds {
guard let itemNode = self.itemNodeAtIndex(nodeIndex) else {
continue
}
//TODO:loc optimize eviction //TODO:loc optimize eviction
if self.seenMessageIds.insert(messageId).inserted, let remainingDynamicAdMessageIntervalValue = self.remainingDynamicAdMessageInterval { if self.seenMessageIds.insert(messageId).inserted, let remainingDynamicAdMessageIntervalValue = self.remainingDynamicAdMessageInterval, let remainingDynamicAdMessageDistanceValue = self.remainingDynamicAdMessageDistance {
let pendingInterval = remainingDynamicAdMessageIntervalValue - 1 let itemHeight = itemNode.bounds.height
if pendingInterval <= 0 {
let remainingDynamicAdMessageInterval = remainingDynamicAdMessageIntervalValue - 1
let remainingDynamicAdMessageDistance = remainingDynamicAdMessageDistanceValue - itemHeight
if remainingDynamicAdMessageInterval <= 0 && remainingDynamicAdMessageDistance <= 0.0 {
self.remainingDynamicAdMessageInterval = self.pendingDynamicAdMessageInterval self.remainingDynamicAdMessageInterval = self.pendingDynamicAdMessageInterval
self.remainingDynamicAdMessageDistance = self.bounds.height
self.maybeInsertPendingAdMessage(historyView: historyView, toLaterRange: toLaterRange, toEarlierRange: toEarlierRange) self.maybeInsertPendingAdMessage(historyView: historyView, toLaterRange: toLaterRange, toEarlierRange: toEarlierRange)
} else { } else {
self.remainingDynamicAdMessageInterval = pendingInterval self.remainingDynamicAdMessageInterval = remainingDynamicAdMessageInterval
self.remainingDynamicAdMessageDistance = remainingDynamicAdMessageDistance
} }
} }
} }

View File

@ -93,11 +93,7 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, ignoreMess
let combinedInitialData = ChatHistoryCombinedInitialData(initialData: initialData, buttonKeyboardMessage: view.topTaggedMessages.first, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData) let combinedInitialData = ChatHistoryCombinedInitialData(initialData: initialData, buttonKeyboardMessage: view.topTaggedMessages.first, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData)
if preloaded { if preloaded {
if tagMask == nil && view.entries.isEmpty {
print("")
}
return .HistoryView(view: view, type: .Generic(type: updateType), scrollPosition: nil, flashIndicators: false, originalScrollPosition: nil, initialData: combinedInitialData, id: location.id) return .HistoryView(view: view, type: .Generic(type: updateType), scrollPosition: nil, flashIndicators: false, originalScrollPosition: nil, initialData: combinedInitialData, id: location.id)
} else { } else {
if view.isLoading { if view.isLoading {
@ -168,10 +164,6 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, ignoreMess
} }
} }
if tagMask == nil && view.entries.isEmpty {
print("")
}
preloaded = true preloaded = true
return .HistoryView(view: view, type: .Initial(fadeIn: fadeIn), scrollPosition: scrollPosition, flashIndicators: false, originalScrollPosition: nil, initialData: ChatHistoryCombinedInitialData(initialData: initialData, buttonKeyboardMessage: view.topTaggedMessages.first, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData), id: location.id) return .HistoryView(view: view, type: .Initial(fadeIn: fadeIn), scrollPosition: scrollPosition, flashIndicators: false, originalScrollPosition: nil, initialData: ChatHistoryCombinedInitialData(initialData: initialData, buttonKeyboardMessage: view.topTaggedMessages.first, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData), id: location.id)
} }

View File

@ -373,6 +373,7 @@ final class ChatMessageAvatarHeader: ListViewItemHeader {
let peerId: PeerId let peerId: PeerId
let peer: Peer? let peer: Peer?
let messageReference: MessageReference? let messageReference: MessageReference?
let adMessageId: EngineMessage.Id?
let effectiveTimestamp: Int32 let effectiveTimestamp: Int32
let presentationData: ChatPresentationData let presentationData: ChatPresentationData
let context: AccountContext let context: AccountContext
@ -382,6 +383,11 @@ final class ChatMessageAvatarHeader: ListViewItemHeader {
self.peerId = peerId self.peerId = peerId
self.peer = peer self.peer = peer
self.messageReference = messageReference self.messageReference = messageReference
if message.adAttribute != nil {
self.adMessageId = message.id
} else {
self.adMessageId = nil
}
var effectiveTimestamp = message.timestamp var effectiveTimestamp = message.timestamp
if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported) { if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported) {
@ -412,7 +418,7 @@ final class ChatMessageAvatarHeader: ListViewItemHeader {
} }
func node(synchronousLoad: Bool) -> ListViewItemHeaderNode { func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
return ChatMessageAvatarHeaderNode(peerId: self.peerId, peer: self.peer, messageReference: self.messageReference, presentationData: self.presentationData, context: self.context, controllerInteraction: self.controllerInteraction, synchronousLoad: synchronousLoad) return ChatMessageAvatarHeaderNode(peerId: self.peerId, peer: self.peer, messageReference: self.messageReference, adMessageId: self.adMessageId, presentationData: self.presentationData, context: self.context, controllerInteraction: self.controllerInteraction, synchronousLoad: synchronousLoad)
} }
func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?) { func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?) {
@ -435,6 +441,7 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
private let peerId: PeerId private let peerId: PeerId
private let messageReference: MessageReference? private let messageReference: MessageReference?
private let peer: Peer? private let peer: Peer?
private let adMessageId: EngineMessage.Id?
private let containerNode: ContextControllerSourceNode private let containerNode: ContextControllerSourceNode
private let avatarNode: AvatarNode private let avatarNode: AvatarNode
@ -459,10 +466,11 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
} }
} }
init(peerId: PeerId, peer: Peer?, messageReference: MessageReference?, presentationData: ChatPresentationData, context: AccountContext, controllerInteraction: ChatControllerInteraction, synchronousLoad: Bool) { init(peerId: PeerId, peer: Peer?, messageReference: MessageReference?, adMessageId: EngineMessage.Id?, presentationData: ChatPresentationData, context: AccountContext, controllerInteraction: ChatControllerInteraction, synchronousLoad: Bool) {
self.peerId = peerId self.peerId = peerId
self.peer = peer self.peer = peer
self.messageReference = messageReference self.messageReference = messageReference
self.adMessageId = adMessageId
self.presentationData = presentationData self.presentationData = presentationData
self.context = context self.context = context
self.controllerInteraction = controllerInteraction self.controllerInteraction = controllerInteraction
@ -636,10 +644,14 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
if self.peerId.namespace == Namespaces.Peer.Empty, case let .message(_, id, _, _, _) = self.messageReference?.content { if self.peerId.namespace == Namespaces.Peer.Empty, case let .message(_, id, _, _, _) = self.messageReference?.content {
self.controllerInteraction.displayMessageTooltip(id, self.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, self.avatarNode.frame) self.controllerInteraction.displayMessageTooltip(id, self.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, self.avatarNode.frame)
} else if let peer = self.peer { } else if let peer = self.peer {
if let channel = peer as? TelegramChannel, case .broadcast = channel.info { if let adMessageId = self.adMessageId {
self.controllerInteraction.openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: nil, peekData: nil), self.messageReference, false) self.controllerInteraction.activateAdAction(adMessageId)
} else { } else {
self.controllerInteraction.openPeer(EnginePeer(peer), .info, self.messageReference, false) if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
self.controllerInteraction.openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: nil, peekData: nil), self.messageReference, false)
} else {
self.controllerInteraction.openPeer(EnginePeer(peer), .info, self.messageReference, false)
}
} }
} }
} }

View File

@ -63,28 +63,8 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
} }
self.contentNode.activateAction = { [weak self] in self.contentNode.activateAction = { [weak self] in
if let strongSelf = self, let item = strongSelf.item { if let strongSelf = self, let item = strongSelf.item {
if let adAttribute = item.message.adAttribute { if let _ = item.message.adAttribute {
switch adAttribute.target { item.controllerInteraction.activateAdAction(item.message.id)
case let .peer(id, messageId, startParam):
let navigationData: ChatControllerInteractionNavigateToPeer
if let bot = item.message.author as? TelegramUser, bot.botInfo != nil, let startParam = startParam {
navigationData = .withBotStartPayload(ChatControllerInitialBotStart(payload: startParam, behavior: .interactive))
} else {
var subject: ChatControllerSubject?
if let messageId = messageId {
subject = .message(id: .id(messageId), highlight: true, timecode: nil)
}
navigationData = .chat(textInputState: nil, subject: subject, peekData: nil)
}
let _ = (item.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: id))
|> deliverOnMainQueue).start(next: { peer in
if let peer = peer {
item.controllerInteraction.openPeer(peer, navigationData, nil, false)
}
})
case let .join(_, joinHash):
item.controllerInteraction.openJoinLink(joinHash)
}
} else { } else {
var webPageContent: TelegramMediaWebpageLoadedContent? var webPageContent: TelegramMediaWebpageLoadedContent?
for media in item.message.media { for media in item.message.media {

View File

@ -540,6 +540,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}, openLargeEmojiInfo: { _, _, _ in }, openLargeEmojiInfo: { _, _, _ in
}, openJoinLink: { _ in }, openJoinLink: { _ in
}, openWebView: { _, _, _, _ in }, openWebView: { _, _, _, _ in
}, activateAdAction: { _ in
}, requestMessageUpdate: { _, _ in }, requestMessageUpdate: { _, _ in
}, cancelInteractiveKeyboardGestures: { }, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: { }, dismissTextInput: {

View File

@ -161,6 +161,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
}, openLargeEmojiInfo: { _, _, _ in }, openLargeEmojiInfo: { _, _, _ in
}, openJoinLink: { _ in }, openJoinLink: { _ in
}, openWebView: { _, _, _, _ in }, openWebView: { _, _, _, _ in
}, activateAdAction: { _ in
}, requestMessageUpdate: { _, _ in }, requestMessageUpdate: { _, _ in
}, cancelInteractiveKeyboardGestures: { }, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: { }, dismissTextInput: {

View File

@ -154,6 +154,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}, openLargeEmojiInfo: { _, _, _ in }, openLargeEmojiInfo: { _, _, _ in
}, openJoinLink: { _ in }, openJoinLink: { _ in
}, openWebView: { _, _, _, _ in }, openWebView: { _, _, _, _ in
}, activateAdAction: { _ in
}, requestMessageUpdate: { _, _ in }, requestMessageUpdate: { _, _ in
}, cancelInteractiveKeyboardGestures: { }, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: { }, dismissTextInput: {

View File

@ -2599,6 +2599,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
}, openLargeEmojiInfo: { _, _, _ in }, openLargeEmojiInfo: { _, _, _ in
}, openJoinLink: { _ in }, openJoinLink: { _ in
}, openWebView: { _, _, _, _ in }, openWebView: { _, _, _, _ in
}, activateAdAction: { _ in
}, requestMessageUpdate: { _, _ in }, requestMessageUpdate: { _, _ in
}, cancelInteractiveKeyboardGestures: { }, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: { }, dismissTextInput: {

View File

@ -1348,6 +1348,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}, openLargeEmojiInfo: { _, _, _ in }, openLargeEmojiInfo: { _, _, _ in
}, openJoinLink: { _ in }, openJoinLink: { _ in
}, openWebView: { _, _, _, _ in }, openWebView: { _, _, _, _ in
}, activateAdAction: { _ in
}, requestMessageUpdate: { _, _ in }, requestMessageUpdate: { _, _ in
}, cancelInteractiveKeyboardGestures: { }, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: { }, dismissTextInput: {

View File

@ -626,7 +626,7 @@ tgcalls::VideoCaptureInterfaceObject *GetVideoCaptureAssumingSameThread(tgcalls:
} }
std::shared_ptr<tgcalls::VideoCaptureInterface> interface = strongSelf->_interface; std::shared_ptr<tgcalls::VideoCaptureInterface> interface = strongSelf->_interface;
if (false && requestClone) { /*if (false && requestClone) {
VideoSampleBufferView *remoteRenderer = [[VideoSampleBufferView alloc] initWithFrame:CGRectZero]; VideoSampleBufferView *remoteRenderer = [[VideoSampleBufferView alloc] initWithFrame:CGRectZero];
remoteRenderer.videoContentMode = UIViewContentModeScaleAspectFill; remoteRenderer.videoContentMode = UIViewContentModeScaleAspectFill;
@ -643,7 +643,7 @@ tgcalls::VideoCaptureInterfaceObject *GetVideoCaptureAssumingSameThread(tgcalls:
} }
completion(remoteRenderer, cloneRenderer); completion(remoteRenderer, cloneRenderer);
} else if ([VideoMetalView isSupported]) { } else */if ([VideoMetalView isSupported]) {
VideoMetalView *remoteRenderer = [[VideoMetalView alloc] initWithFrame:CGRectZero]; VideoMetalView *remoteRenderer = [[VideoMetalView alloc] initWithFrame:CGRectZero];
remoteRenderer.videoContentMode = UIViewContentModeScaleAspectFill; remoteRenderer.videoContentMode = UIViewContentModeScaleAspectFill;

@ -1 +1 @@
Subproject commit c8f6a173253e88fc77ef4ceece919a786023108e Subproject commit 07b225568fb596ba44d472c3fa413da2f8bd3f2e