Swiftgram/submodules/TelegramUI/Sources/PreparedChatHistoryViewTransition.swift
2024-07-03 21:43:18 +04:00

253 lines
12 KiB
Swift

import Foundation
import SwiftSignalKit
import Postbox
import TelegramCore
import Display
import MergeLists
import AccountContext
import ChatControllerInteraction
import ChatHistoryEntry
import ChatMessageBubbleItemNode
func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toView: ChatHistoryView, reason: ChatHistoryViewTransitionReason, reverse: Bool, chatLocation: ChatLocation, controllerInteraction: ChatControllerInteraction, scrollPosition: ChatHistoryViewScrollPosition?, scrollAnimationCurve: ListViewAnimationCurve?, initialData: InitialMessageHistoryData?, keyboardButtonsMessage: Message?, cachedData: CachedPeerData?, cachedDataMessages: [MessageId: Message]?, readStateData: [PeerId: ChatHistoryCombinedInitialReadStateData]?, flashIndicators: Bool, updatedMessageSelection: Bool, messageTransitionNode: ChatMessageTransitionNodeImpl?, allUpdated: Bool) -> ChatHistoryViewTransition {
var mergeResult: (deleteIndices: [Int], indicesAndItems: [(Int, ChatHistoryEntry, Int?)], updateIndices: [(Int, ChatHistoryEntry, Int)])
let allUpdated = allUpdated || (fromView?.associatedData != toView.associatedData)
if reverse {
mergeResult = mergeListsStableWithUpdatesReversed(leftList: fromView?.filteredEntries ?? [], rightList: toView.filteredEntries, allUpdated: allUpdated)
} else {
mergeResult = mergeListsStableWithUpdates(leftList: fromView?.filteredEntries ?? [], rightList: toView.filteredEntries, allUpdated: allUpdated)
}
if let messageTransitionNode = messageTransitionNode, messageTransitionNode.hasOngoingTransitions, let previousEntries = fromView?.filteredEntries {
for i in 0 ..< mergeResult.updateIndices.count {
switch mergeResult.updateIndices[i].1 {
case let .MessageEntry(message, presentationData, flag, monthLocation, messageSelection, entryAttributes):
if messageTransitionNode.isAnimatingMessage(stableId: message.stableId) {
var updatedMessage = message
mediaLoop: for media in message.media {
if let webpage = media as? TelegramMediaWebpage, case .Loaded = webpage.content {
var filterMedia = false
switch previousEntries[mergeResult.updateIndices[i].2] {
case let .MessageEntry(previousMessage, _, _, _, _, _):
if previousMessage.media.contains(where: { value in
if let value = value as? TelegramMediaWebpage, case .Loaded = value.content {
return true
} else {
return false
}
}) {
if messageTransitionNode.hasScheduledUpdateMessageAfterAnimationCompleted(stableId: message.stableId) {
filterMedia = true
}
} else {
filterMedia = true
}
default:
break
}
if filterMedia {
updatedMessage = message.withUpdatedMedia(message.media.filter {
$0 !== media
})
messageTransitionNode.scheduleUpdateMessageAfterAnimationCompleted(stableId: message.stableId)
}
break mediaLoop
}
}
mergeResult.updateIndices[i].1 = .MessageEntry(updatedMessage, presentationData, flag, monthLocation, messageSelection, entryAttributes)
}
default:
break
}
}
}
var adjustedDeleteIndices: [ListViewDeleteItem] = []
let previousCount: Int
if let fromView = fromView {
previousCount = fromView.filteredEntries.count
} else {
previousCount = 0
}
for index in mergeResult.deleteIndices {
adjustedDeleteIndices.append(ListViewDeleteItem(index: previousCount - 1 - index, directionHint: nil))
}
var adjustedIndicesAndItems: [ChatHistoryViewTransitionInsertEntry] = []
var adjustedUpdateItems: [ChatHistoryViewTransitionUpdateEntry] = []
let updatedCount = toView.filteredEntries.count
var options: ListViewDeleteAndInsertOptions = []
var animateIn = false
var maxAnimatedInsertionIndex = -1
var stationaryItemRange: (Int, Int)?
var scrollToItem: ListViewScrollToItem?
switch reason {
case let .Initial(fadeIn):
if fadeIn {
animateIn = true
} else {
let _ = options.insert(.LowLatency)
let _ = options.insert(.Synchronous)
let _ = options.insert(.PreferSynchronousResourceLoading)
}
case .InteractiveChanges:
let _ = options.insert(.AnimateAlpha)
let _ = options.insert(.AnimateInsertion)
for (index, _, _) in mergeResult.indicesAndItems.sorted(by: { $0.0 > $1.0 }) {
let adjustedIndex = updatedCount - 1 - index
if adjustedIndex == maxAnimatedInsertionIndex + 1 {
maxAnimatedInsertionIndex += 1
}
}
case .Reload:
stationaryItemRange = (0, Int.max)
case .HoleReload:
stationaryItemRange = (0, Int.max)
}
for (index, entry, previousIndex) in mergeResult.indicesAndItems {
let adjustedIndex = updatedCount - 1 - index
let adjustedPrevousIndex: Int?
if let previousIndex = previousIndex {
adjustedPrevousIndex = previousCount - 1 - previousIndex
} else {
adjustedPrevousIndex = nil
}
var directionHint: ListViewItemOperationDirectionHint?
if maxAnimatedInsertionIndex >= 0 && adjustedIndex <= maxAnimatedInsertionIndex {
directionHint = .Down
}
adjustedIndicesAndItems.append(ChatHistoryViewTransitionInsertEntry(index: adjustedIndex, previousIndex: adjustedPrevousIndex, entry: entry, directionHint: directionHint))
}
for (index, entry, previousIndex) in mergeResult.updateIndices {
let adjustedIndex = updatedCount - 1 - index
let adjustedPreviousIndex = previousCount - 1 - previousIndex
let directionHint: ListViewItemOperationDirectionHint? = nil
adjustedUpdateItems.append(ChatHistoryViewTransitionUpdateEntry(index: adjustedIndex, previousIndex: adjustedPreviousIndex, entry: entry, directionHint: directionHint))
}
var scrolledToIndex: MessageHistoryScrollToSubject?
var scrolledToSomeIndex = false
let curve: ListViewAnimationCurve = scrollAnimationCurve ?? .Default(duration: nil)
if let scrollPosition = scrollPosition {
switch scrollPosition {
case let .unread(unreadIndex):
var index = toView.filteredEntries.count - 1
for entry in toView.filteredEntries {
if case .UnreadEntry = entry {
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: curve, directionHint: .Down)
break
}
index -= 1
}
if scrollToItem == nil {
var index = toView.filteredEntries.count - 1
for entry in toView.filteredEntries {
if entry.index >= unreadIndex {
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: curve, directionHint: .Down)
break
}
index -= 1
}
}
if scrollToItem == nil {
var index = 0
for entry in toView.filteredEntries.reversed() {
if entry.index < unreadIndex {
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: curve, directionHint: .Down)
break
}
index += 1
}
}
case let .positionRestoration(scrollIndex, relativeOffset):
var index = toView.filteredEntries.count - 1
for entry in toView.filteredEntries {
if entry.index >= scrollIndex {
scrollToItem = ListViewScrollToItem(index: index, position: .top(relativeOffset), animated: false, curve: curve, directionHint: .Down)
break
}
index -= 1
}
if scrollToItem == nil {
var index = 0
for entry in toView.filteredEntries.reversed() {
if entry.index < scrollIndex {
scrollToItem = ListViewScrollToItem(index: index, position: .top(0.0), animated: false, curve: curve, directionHint: .Down)
break
}
index += 1
}
}
case let .index(scrollSubject, position, directionHint, animated, highlight, displayLink, _):
let scrollIndex = scrollSubject
var position = position
if case .center = position, highlight {
scrolledToIndex = scrollSubject
}
if case .center = position, let quote = scrollSubject.quote {
position = .center(.custom({ itemNode in
if let itemNode = itemNode as? ChatMessageBubbleItemNode {
if let quoteRect = itemNode.getQuoteRect(quote: quote.string, offset: quote.offset) {
return quoteRect.midY
}
}
return 0.0
}))
}
var index = toView.filteredEntries.count - 1
for entry in toView.filteredEntries {
if scrollIndex.index.isLessOrEqual(to: entry.index) {
scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: curve, directionHint: directionHint, displayLink: displayLink)
break
}
index -= 1
}
if scrollToItem == nil {
var index = 0
for entry in toView.filteredEntries.reversed() {
if !scrollIndex.index.isLess(than: entry.index) {
scrolledToSomeIndex = true
scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: curve, directionHint: directionHint)
break
}
index += 1
}
}
}
} else if case .Initial = reason, scrollToItem == nil {
var index = toView.filteredEntries.count - 1
for entry in toView.filteredEntries {
if case let .MessageEntry(message, _, _, _, _, _) = entry {
if let _ = message.adAttribute {
scrollToItem = ListViewScrollToItem(index: index + 1, position: .top(0.0), animated: false, curve: curve, directionHint: .Down)
break
}
}
index -= 1
}
}
if updatedMessageSelection {
options.insert(.Synchronous)
}
return ChatHistoryViewTransition(historyView: toView, deleteItems: adjustedDeleteIndices, insertEntries: adjustedIndicesAndItems, updateEntries: adjustedUpdateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange, initialData: initialData, keyboardButtonsMessage: keyboardButtonsMessage, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData, scrolledToIndex: scrolledToIndex, scrolledToSomeIndex: scrolledToSomeIndex || scrolledToIndex != nil, animateIn: animateIn, reason: reason, flashIndicators: flashIndicators)
}