mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-08 19:10:53 +00:00
Comments [WIP]
This commit is contained in:
parent
933e1f788f
commit
c197d9cd4c
@ -3654,7 +3654,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
}
|
||||
}
|
||||
|
||||
private func updateVisibleItemRange(force: Bool = false) {
|
||||
public func updateVisibleItemRange(force: Bool = false) {
|
||||
let currentRange = self.immediateDisplayedItemRange()
|
||||
|
||||
if currentRange != self.displayedItemRange || force {
|
||||
|
@ -3,7 +3,7 @@ import SwiftSignalKit
|
||||
|
||||
public enum ChatLocationInput {
|
||||
case peer(PeerId)
|
||||
case external(PeerId, Signal<MessageHistoryViewExternalInput, NoError>)
|
||||
case external(PeerId, Int64, Signal<MessageHistoryViewExternalInput, NoError>)
|
||||
}
|
||||
|
||||
public enum ResolvedChatLocationInput {
|
||||
|
@ -2416,7 +2416,7 @@ public final class Postbox {
|
||||
switch chatLocation {
|
||||
case let .peer(peerId):
|
||||
return .single((.peer(peerId), false))
|
||||
case let .external(_, input):
|
||||
case let .external(_, _, input):
|
||||
return Signal { subscriber in
|
||||
var isHoleFill = false
|
||||
return (input
|
||||
|
@ -190,7 +190,7 @@ private func wrappedHistoryViewAdditionalData(chatLocation: ChatLocationInput, a
|
||||
result.append(.peerChatState(peerId))
|
||||
}
|
||||
}
|
||||
case let .external(peerId, _):
|
||||
case let .external(peerId, _, _):
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
if result.firstIndex(where: { if case .peerChatState = $0 { return true } else { return false } }) == nil {
|
||||
result.append(.peerChatState(peerId))
|
||||
@ -1241,6 +1241,8 @@ public final class AccountViewTracker {
|
||||
strongSelf.updatePolls(viewId: viewId, messageIds: pollMessageIds, messages: pollMessageDict)
|
||||
if case let .peer(peerId) = chatLocation, peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: next.0)
|
||||
} else if case let .external(peerId, _, _) = chatLocation, peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: next.0, location: chatLocation)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1254,9 +1256,9 @@ public final class AccountViewTracker {
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: nil)
|
||||
}
|
||||
case let .external(peerId, _):
|
||||
case let .external(peerId, _, _):
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: nil)
|
||||
strongSelf.historyViewStateValidationContexts.updateView(id: viewId, view: nil, location: chatLocation)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1267,7 +1269,7 @@ public final class AccountViewTracker {
|
||||
switch chatLocation {
|
||||
case let .peer(peerIdValue):
|
||||
peerId = peerIdValue
|
||||
case let .external(peerIdValue, _):
|
||||
case let .external(peerIdValue, _, _):
|
||||
peerId = peerIdValue
|
||||
}
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
|
@ -146,6 +146,7 @@ final class HistoryViewStateValidationContexts {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var historyState: HistoryState?
|
||||
for entry in view.additionalData {
|
||||
if case let .peerChatState(peerId, chatState) = entry {
|
||||
@ -156,7 +157,101 @@ final class HistoryViewStateValidationContexts {
|
||||
}
|
||||
}
|
||||
|
||||
if let historyState = historyState, historyState.hasInvalidationIndex {
|
||||
if let location = location, case let .external(peerId, threadId, _) = location {
|
||||
var rangesToInvalidate: [[MessageId]] = []
|
||||
let addToRange: (MessageId, inout [[MessageId]]) -> Void = { id, ranges in
|
||||
if ranges.isEmpty {
|
||||
ranges = [[id]]
|
||||
} else {
|
||||
ranges[ranges.count - 1].append(id)
|
||||
}
|
||||
}
|
||||
|
||||
let addRangeBreak: (inout [[MessageId]]) -> Void = { ranges in
|
||||
if ranges.last?.count != 0 {
|
||||
ranges.append([])
|
||||
}
|
||||
}
|
||||
|
||||
for entry in view.entries {
|
||||
if entry.message.id.peerId == peerId && entry.message.id.namespace == Namespaces.Message.Cloud {
|
||||
addToRange(entry.message.id, &rangesToInvalidate)
|
||||
}
|
||||
}
|
||||
|
||||
if !rangesToInvalidate.isEmpty && rangesToInvalidate[rangesToInvalidate.count - 1].isEmpty {
|
||||
rangesToInvalidate.removeLast()
|
||||
}
|
||||
|
||||
var invalidatedMessageIds = Set<MessageId>()
|
||||
|
||||
if !rangesToInvalidate.isEmpty {
|
||||
let context: HistoryStateValidationContext
|
||||
if let current = self.contexts[id] {
|
||||
context = current
|
||||
} else {
|
||||
context = HistoryStateValidationContext()
|
||||
self.contexts[id] = context
|
||||
}
|
||||
|
||||
var addedRanges: [[MessageId]] = []
|
||||
for messages in rangesToInvalidate {
|
||||
for id in messages {
|
||||
invalidatedMessageIds.insert(id)
|
||||
|
||||
if context.batchReferences[id] != nil {
|
||||
addRangeBreak(&addedRanges)
|
||||
} else {
|
||||
addToRange(id, &addedRanges)
|
||||
}
|
||||
}
|
||||
addRangeBreak(&addedRanges)
|
||||
}
|
||||
|
||||
if !addedRanges.isEmpty && addedRanges[addedRanges.count - 1].isEmpty {
|
||||
addedRanges.removeLast()
|
||||
}
|
||||
|
||||
for rangeMessages in addedRanges {
|
||||
for messages in slicedForValidationMessages(rangeMessages) {
|
||||
let disposable = MetaDisposable()
|
||||
let batch = HistoryStateValidationBatch(disposable: disposable, invalidatedState: historyState)
|
||||
for messageId in messages {
|
||||
context.batchReferences[messageId] = batch
|
||||
}
|
||||
|
||||
disposable.set((validateReplyThreadMessagesBatch(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, peerId: peerId, threadMessageId: makeThreadIdMessageId(peerId: peerId, threadId: threadId).id, messageIds: messages)
|
||||
|> deliverOn(self.queue)).start(completed: { [weak self, weak batch] in
|
||||
if let strongSelf = self, let context = strongSelf.contexts[id], let batch = batch {
|
||||
var completedMessageIds: [MessageId] = []
|
||||
for (messageId, messageBatch) in context.batchReferences {
|
||||
if messageBatch === batch {
|
||||
completedMessageIds.append(messageId)
|
||||
}
|
||||
}
|
||||
for messageId in completedMessageIds {
|
||||
//context.batchReferences.removeValue(forKey: messageId)
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let context = self.contexts[id] {
|
||||
var removeIds: [MessageId] = []
|
||||
|
||||
for batchMessageId in context.batchReferences.keys {
|
||||
if !invalidatedMessageIds.contains(batchMessageId) {
|
||||
removeIds.append(batchMessageId)
|
||||
}
|
||||
}
|
||||
|
||||
for messageId in removeIds {
|
||||
context.batchReferences.removeValue(forKey: messageId)
|
||||
}
|
||||
}
|
||||
} else if let historyState = historyState, historyState.hasInvalidationIndex {
|
||||
var rangesToInvalidate: [[MessageId]] = []
|
||||
let addToRange: (MessageId, inout [[MessageId]]) -> Void = { id, ranges in
|
||||
if ranges.isEmpty {
|
||||
@ -421,6 +516,59 @@ private func validateChannelMessagesBatch(postbox: Postbox, network: Network, ac
|
||||
} |> switchToLatest
|
||||
}
|
||||
|
||||
private func validateReplyThreadMessagesBatch(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, threadMessageId: Int32, messageIds: [MessageId]) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
var previousMessages: [Message] = []
|
||||
var previous: [MessageId: Message] = [:]
|
||||
for messageId in messageIds {
|
||||
if let message = transaction.getMessage(messageId) {
|
||||
previousMessages.append(message)
|
||||
previous[message.id] = message
|
||||
}
|
||||
}
|
||||
|
||||
var signal: Signal<ValidatedMessages, MTRpcError>
|
||||
let hash = hashForMessages(previousMessages, withChannelIds: false)
|
||||
Logger.shared.log("HistoryValidation", "validate reply thread batch for \(peerId): \(previousMessages.map({ $0.id }))")
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
let requestSignal: Signal<Api.messages.Messages, MTRpcError>
|
||||
requestSignal = network.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: threadMessageId, offsetId: messageIds[messageIds.count - 1].id, offsetDate: 0, addOffset: -1, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1, hash: hash))
|
||||
|
||||
signal = requestSignal
|
||||
|> map { result -> ValidatedMessages in
|
||||
let messages: [Api.Message]
|
||||
let chats: [Api.Chat]
|
||||
let users: [Api.User]
|
||||
var channelPts: Int32?
|
||||
|
||||
switch result {
|
||||
case let .messages(messages: apiMessages, chats: apiChats, users: apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let .messagesSlice(_, _, _, messages: apiMessages, chats: apiChats, users: apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let .channelMessages(_, pts, _, apiMessages, apiChats, apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
channelPts = pts
|
||||
case .messagesNotModified:
|
||||
return .notModified
|
||||
}
|
||||
return .messages(messages, chats, users, channelPts)
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
|
||||
return validateReplyThreadBatch(postbox: postbox, network: network, transaction: transaction, accountPeerId: accountPeerId, peerId: peerId, threadId: makeMessageThreadId(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: threadMessageId)), signal: signal, previous: previous, messageNamespace: Namespaces.Message.Cloud)
|
||||
}
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
private func validateScheduledMessagesBatch(postbox: Postbox, network: Network, accountPeerId: PeerId, tag: MessageTags?, messages: [Message], historyState: HistoryState) -> Signal<Void, NoError> {
|
||||
return postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
var signal: Signal<ValidatedMessages, MTRpcError>
|
||||
@ -715,3 +863,154 @@ private func validateBatch(postbox: Postbox, network: Network, transaction: Tran
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func validateReplyThreadBatch(postbox: Postbox, network: Network, transaction: Transaction, accountPeerId: PeerId, peerId: PeerId, threadId: Int64, signal: Signal<ValidatedMessages, MTRpcError>, previous: [MessageId: Message], messageNamespace: MessageId.Namespace) -> Signal<Void, NoError> {
|
||||
return signal
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<ValidatedMessages?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||
guard let result = result else {
|
||||
return .complete()
|
||||
}
|
||||
switch result {
|
||||
case let .messages(messages, _, _, channelPts):
|
||||
var storeMessages: [StoreMessage] = []
|
||||
|
||||
for message in messages {
|
||||
if let storeMessage = StoreMessage(apiMessage: message, namespace: messageNamespace) {
|
||||
var attributes = storeMessage.attributes
|
||||
if let channelPts = channelPts {
|
||||
attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts))
|
||||
}
|
||||
storeMessages.append(storeMessage.withUpdatedAttributes(attributes))
|
||||
}
|
||||
}
|
||||
|
||||
var validMessageIds = Set<MessageId>()
|
||||
for message in storeMessages {
|
||||
if case let .Id(id) = message.id {
|
||||
validMessageIds.insert(id)
|
||||
}
|
||||
}
|
||||
|
||||
var maybeRemovedMessageIds: [MessageId] = []
|
||||
for id in previous.keys {
|
||||
if !validMessageIds.contains(id) {
|
||||
maybeRemovedMessageIds.append(id)
|
||||
}
|
||||
}
|
||||
|
||||
let actuallyRemovedMessagesSignal: Signal<Set<MessageId>, NoError>
|
||||
if maybeRemovedMessageIds.isEmpty {
|
||||
actuallyRemovedMessagesSignal = .single(Set())
|
||||
} else {
|
||||
actuallyRemovedMessagesSignal = postbox.transaction { transaction -> Signal<Set<MessageId>, NoError> in
|
||||
if let inputChannel = transaction.getPeer(peerId).flatMap(apiInputChannel) {
|
||||
return network.request(Api.functions.channels.getMessages(channel: inputChannel, id: maybeRemovedMessageIds.map({ Api.InputMessage.inputMessageID(id: $0.id) })))
|
||||
|> map { result -> Set<MessageId> in
|
||||
let apiMessages: [Api.Message]
|
||||
switch result {
|
||||
case let .channelMessages(_, _, _, messages, _, _):
|
||||
apiMessages = messages
|
||||
case let .messages(messages, _, _):
|
||||
apiMessages = messages
|
||||
case let .messagesSlice(_, _, _, messages, _, _):
|
||||
apiMessages = messages
|
||||
case .messagesNotModified:
|
||||
return Set()
|
||||
}
|
||||
var ids = Set<MessageId>()
|
||||
for message in apiMessages {
|
||||
if let parsedMessage = StoreMessage(apiMessage: message, namespace: messageNamespace), case let .Id(id) = parsedMessage.id {
|
||||
ids.insert(id)
|
||||
}
|
||||
}
|
||||
return Set(maybeRemovedMessageIds).subtracting(ids)
|
||||
}
|
||||
|> `catch` { _ -> Signal<Set<MessageId>, NoError> in
|
||||
return .single(Set(maybeRemovedMessageIds))
|
||||
}
|
||||
}
|
||||
return .single(Set(maybeRemovedMessageIds))
|
||||
}
|
||||
|> switchToLatest
|
||||
}
|
||||
|
||||
return actuallyRemovedMessagesSignal
|
||||
|> mapToSignal { removedMessageIds -> Signal<Void, NoError> in
|
||||
return postbox.transaction { transaction -> Void in
|
||||
var validMessageIds = Set<MessageId>()
|
||||
for message in storeMessages {
|
||||
if case let .Id(id) = message.id {
|
||||
validMessageIds.insert(id)
|
||||
let previousMessage = previous[id] ?? transaction.getMessage(id)
|
||||
|
||||
if let previousMessage = previousMessage {
|
||||
var updatedTimestamp = message.timestamp
|
||||
inner: for attribute in message.attributes {
|
||||
if let attribute = attribute as? EditedMessageAttribute {
|
||||
updatedTimestamp = attribute.date
|
||||
break inner
|
||||
}
|
||||
}
|
||||
|
||||
var timestamp = previousMessage.timestamp
|
||||
inner: for attribute in previousMessage.attributes {
|
||||
if let attribute = attribute as? EditedMessageAttribute {
|
||||
timestamp = attribute.date
|
||||
break inner
|
||||
}
|
||||
}
|
||||
|
||||
transaction.updateMessage(id, update: { currentMessage in
|
||||
if updatedTimestamp != timestamp {
|
||||
var updatedLocalTags = message.localTags
|
||||
if currentMessage.localTags.contains(.OutgoingLiveLocation) {
|
||||
updatedLocalTags.insert(.OutgoingLiveLocation)
|
||||
}
|
||||
return .update(message.withUpdatedLocalTags(updatedLocalTags))
|
||||
} else {
|
||||
var storeForwardInfo: StoreMessageForwardInfo?
|
||||
if let forwardInfo = currentMessage.forwardInfo {
|
||||
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType)
|
||||
}
|
||||
var attributes = currentMessage.attributes
|
||||
if let channelPts = channelPts {
|
||||
for i in (0 ..< attributes.count).reversed() {
|
||||
if let _ = attributes[i] as? ChannelMessageStateVersionAttribute {
|
||||
attributes.remove(at: i)
|
||||
}
|
||||
}
|
||||
attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts))
|
||||
}
|
||||
|
||||
let updatedFlags = StoreMessageFlags(currentMessage.flags)
|
||||
|
||||
return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: updatedFlags, tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
}
|
||||
})
|
||||
|
||||
if previous[id] == nil {
|
||||
print("\(id) missing")
|
||||
}
|
||||
} else {
|
||||
let _ = transaction.addMessages([message], location: .Random)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for id in removedMessageIds {
|
||||
if !validMessageIds.contains(id) {
|
||||
deleteMessages(transaction: transaction, mediaBox: postbox.mediaBox, ids: [id])
|
||||
Logger.shared.log("HistoryValidation", "deleting thread message \(id) in \(id.peerId)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case .notModified:
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -305,7 +305,7 @@ public final class AccountContextImpl: AccountContext {
|
||||
return .peer(peerId)
|
||||
case let .replyThread(data):
|
||||
let context = chatLocationContext(holder: contextHolder, account: self.account, data: data)
|
||||
return .external(data.messageId.peerId, context.state)
|
||||
return .external(data.messageId.peerId, makeMessageThreadId(data.messageId), context.state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5398,7 +5398,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
override public func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
self.willAppear = true
|
||||
if self.willAppear {
|
||||
self.chatDisplayNode.historyNode.refreshPollActionsForVisibleMessages()
|
||||
} else {
|
||||
self.willAppear = true
|
||||
}
|
||||
|
||||
if self.scheduledActivateInput {
|
||||
self.scheduledActivateInput = false
|
||||
|
@ -547,6 +547,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
|
||||
let isTopReplyThreadMessageShown = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
|
||||
private let clientId: Atomic<Int32>
|
||||
|
||||
public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, tagMask: MessageTags?, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, selectedMessages: Signal<Set<MessageId>?, NoError>, mode: ChatHistoryListMode = .bubbles) {
|
||||
self.context = context
|
||||
self.chatLocation = chatLocation
|
||||
@ -563,6 +565,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
|
||||
self.prefetchManager = InChatPrefetchManager(context: context)
|
||||
|
||||
let clientId = Atomic<Int32>(value: nextClientId)
|
||||
self.clientId = clientId
|
||||
nextClientId += 1
|
||||
|
||||
super.init()
|
||||
|
||||
self.dynamicBounceEnabled = !self.currentPresentationData.disableAnimations
|
||||
@ -570,11 +576,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
|
||||
//self.debugInfo = true
|
||||
|
||||
let clientId = nextClientId
|
||||
nextClientId += 1
|
||||
|
||||
self.messageProcessingManager.process = { [weak context] messageIds in
|
||||
context?.account.viewTracker.updateViewCountForMessageIds(messageIds: messageIds, clientId: clientId)
|
||||
context?.account.viewTracker.updateViewCountForMessageIds(messageIds: messageIds, clientId: clientId.with { $0 })
|
||||
}
|
||||
self.messageReactionsProcessingManager.process = { [weak context] messageIds in
|
||||
context?.account.viewTracker.updateReactionsForMessageIds(messageIds: messageIds)
|
||||
@ -1098,6 +1101,13 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
self.loadStateUpdated = f
|
||||
}
|
||||
|
||||
func refreshPollActionsForVisibleMessages() {
|
||||
let _ = self.clientId.swap(nextClientId)
|
||||
nextClientId += 1
|
||||
|
||||
self.updateVisibleItemRange(force: true)
|
||||
}
|
||||
|
||||
private func processDisplayedItemRangeChanged(displayedRange: ListViewDisplayedItemRange, transactionState: ChatHistoryTransactionOpaqueState) {
|
||||
let historyView = transactionState.historyView
|
||||
var isTopReplyThreadMessageShownValue = false
|
||||
|
Loading…
x
Reference in New Issue
Block a user