mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
ChatHistoryListNode: rewrite transition queue
ChatMessageBubbleItemNode: another possible crash point
This commit is contained in:
parent
bb94bd7e88
commit
819583121e
@ -6,7 +6,7 @@ import MtProtoKitMac
|
||||
#else
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
#if BUCK
|
||||
#if BUCK
|
||||
import MtProtoKit
|
||||
#else
|
||||
import MtProtoKitDynamic
|
||||
|
@ -262,7 +262,8 @@ public final class ChatHistoryGridNode: GridNode, ChatHistoryNode {
|
||||
|
||||
let previousView = Atomic<ChatHistoryView?>(value: nil)
|
||||
|
||||
let historyViewTransition = combineLatest(historyViewUpdate, self.chatPresentationDataPromise.get()) |> mapToQueue { [weak self] update, chatPresentationData -> Signal<ChatHistoryGridViewTransition, NoError> in
|
||||
let historyViewTransition = combineLatest(queue: messageViewQueue, historyViewUpdate, self.chatPresentationDataPromise.get())
|
||||
|> mapToQueue { [weak self] update, chatPresentationData -> Signal<ChatHistoryGridViewTransition, NoError> in
|
||||
switch update {
|
||||
case .Loading:
|
||||
Queue.mainQueue().async { [weak self] in
|
||||
@ -283,11 +284,9 @@ public final class ChatHistoryGridNode: GridNode, ChatHistoryNode {
|
||||
return .complete()
|
||||
case let .HistoryView(view, type, scrollPosition, flashIndicators, _, _, id):
|
||||
let reason: ChatHistoryViewTransitionReason
|
||||
var prepareOnMainQueue = false
|
||||
switch type {
|
||||
case let .Initial(fadeIn):
|
||||
reason = ChatHistoryViewTransitionReason.Initial(fadeIn: fadeIn)
|
||||
prepareOnMainQueue = !fadeIn
|
||||
case let .Generic(genericType):
|
||||
switch genericType {
|
||||
case .InitialUnread, .Initial:
|
||||
@ -304,7 +303,9 @@ public final class ChatHistoryGridNode: GridNode, ChatHistoryNode {
|
||||
let processedView = ChatHistoryView(originalView: view, filteredEntries: chatHistoryEntriesForView(location: .peer(peerId), view: view, includeUnreadEntry: false, includeEmptyEntry: false, includeChatInfoEntry: false, includeSearchEntry: false, reverse: false, groupMessages: false, selectedMessages: nil, presentationData: chatPresentationData, historyAppearsCleared: false), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: false), id: id)
|
||||
let previous = previousView.swap(processedView)
|
||||
|
||||
return preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: false, chatLocation: .peer(peerId), controllerInteraction: controllerInteraction, scrollPosition: scrollPosition, initialData: nil, keyboardButtonsMessage: nil, cachedData: nil, cachedDataMessages: nil, readStateData: nil, flashIndicators: flashIndicators) |> map({ mappedChatHistoryViewListTransition(context: context, peerId: peerId, controllerInteraction: controllerInteraction, transition: $0, from: previous, presentationData: chatPresentationData) }) |> runOn(prepareOnMainQueue ? Queue.mainQueue() : messageViewQueue)
|
||||
let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: false, chatLocation: .peer(peerId), controllerInteraction: controllerInteraction, scrollPosition: scrollPosition, initialData: nil, keyboardButtonsMessage: nil, cachedData: nil, cachedDataMessages: nil, readStateData: nil, flashIndicators: flashIndicators)
|
||||
let mappedTransition = mappedChatHistoryViewListTransition(context: context, peerId: peerId, controllerInteraction: controllerInteraction, transition: rawTransition, from: previous, presentationData: chatPresentationData)
|
||||
return .single(mappedTransition)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,7 +323,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
private let messageViewQueue = Queue(name: "ChatHistoryListNode processing")
|
||||
|
||||
private var dequeuedInitialTransitionOnLayout = false
|
||||
private var enqueuedHistoryViewTransition: (ChatHistoryListViewTransition, () -> Void)?
|
||||
private var enqueuedHistoryViewTransitions: [ChatHistoryListViewTransition] = []
|
||||
private var hasActiveTransition = false
|
||||
var layoutActionOnViewTransition: ((ChatHistoryListViewTransition) -> (ChatHistoryListViewTransition, ListViewUpdateSizeAndInsets?))?
|
||||
|
||||
public let historyState = ValuePromise<ChatHistoryNodeHistoryState>()
|
||||
@ -518,9 +519,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
|
||||
let nextTransitionVersion = Atomic<Int>(value: 0)
|
||||
|
||||
let historyViewTransition = combineLatest(queue: messageViewQueue, historyViewUpdate, self.chatPresentationDataPromise.get(), selectedMessages, automaticDownloadNetworkType, self.historyAppearsClearedPromise.get())
|
||||
|> introduceError(Void.self)
|
||||
|> mapToQueue { [weak self] update, chatPresentationData, selectedMessages, networkType, historyAppearsCleared -> Signal<(ChatHistoryListViewTransition, Int), Void> in
|
||||
let historyViewTransitionDisposable = combineLatest(queue: messageViewQueue, historyViewUpdate, self.chatPresentationDataPromise.get(), selectedMessages, automaticDownloadNetworkType, self.historyAppearsClearedPromise.get()).start(next: { [weak self] update, chatPresentationData, selectedMessages, networkType, historyAppearsCleared in
|
||||
func applyHole() {
|
||||
Queue.mainQueue().async {
|
||||
if let strongSelf = self {
|
||||
@ -543,140 +542,115 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
|
||||
let initialData: ChatHistoryCombinedInitialData?
|
||||
switch update.0 {
|
||||
case let .Loading(combinedInitialData, type):
|
||||
if case .Generic(.FillHole) = type {
|
||||
applyHole()
|
||||
return .fail(Void())
|
||||
}
|
||||
|
||||
initialData = combinedInitialData
|
||||
Queue.mainQueue().async {
|
||||
if let strongSelf = self {
|
||||
if !strongSelf.didSetInitialData {
|
||||
strongSelf.didSetInitialData = true
|
||||
strongSelf._initialData.set(.single(combinedInitialData))
|
||||
}
|
||||
|
||||
strongSelf._cachedPeerDataAndMessages.set(.single((nil, nil)))
|
||||
|
||||
let loadState: ChatHistoryNodeLoadState = .loading
|
||||
if strongSelf.loadState != loadState {
|
||||
strongSelf.loadState = loadState
|
||||
strongSelf.loadStateUpdated?(loadState, false)
|
||||
}
|
||||
|
||||
let historyState: ChatHistoryNodeHistoryState = .loading
|
||||
if strongSelf.currentHistoryState != historyState {
|
||||
strongSelf.currentHistoryState = historyState
|
||||
strongSelf.historyState.set(historyState)
|
||||
}
|
||||
}
|
||||
}
|
||||
return .complete()
|
||||
case let .HistoryView(view, type, scrollPosition, flashIndicators, originalScrollPosition, data, id):
|
||||
if case .Generic(.FillHole) = type {
|
||||
applyHole()
|
||||
return .fail(Void())
|
||||
}
|
||||
|
||||
initialData = data
|
||||
var updatedScrollPosition = scrollPosition
|
||||
|
||||
var reverse = false
|
||||
var includeSearchEntry = false
|
||||
if case let .list(search, reverseValue) = mode {
|
||||
includeSearchEntry = search
|
||||
reverse = reverseValue
|
||||
}
|
||||
|
||||
let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType)
|
||||
|
||||
let processedView = ChatHistoryView(originalView: view, filteredEntries: chatHistoryEntriesForView(location: chatLocation, view: view, includeUnreadEntry: mode == .bubbles, includeEmptyEntry: mode == .bubbles && tagMask == nil, includeChatInfoEntry: mode == .bubbles, includeSearchEntry: includeSearchEntry && tagMask != nil, reverse: reverse, groupMessages: mode == .bubbles, selectedMessages: selectedMessages, presentationData: chatPresentationData, historyAppearsCleared: historyAppearsCleared), associatedData: associatedData, id: id)
|
||||
let previousValueAndVersion = previousView.swap((processedView, update.1))
|
||||
let previous = previousValueAndVersion?.0
|
||||
|
||||
if let previousVersion = previousValueAndVersion?.1 {
|
||||
if !GlobalExperimentalSettings.isAppStoreBuild {
|
||||
precondition(update.1 >= previousVersion)
|
||||
}
|
||||
assert(update.1 >= previousVersion)
|
||||
}
|
||||
|
||||
if scrollPosition == nil, let originalScrollPosition = originalScrollPosition {
|
||||
switch originalScrollPosition {
|
||||
case let .index(index, position, _, _):
|
||||
if case .upperBound = index {
|
||||
if let previous = previous, previous.filteredEntries.isEmpty {
|
||||
updatedScrollPosition = .index(index: index, position: position, directionHint: .Down, animated: false)
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let reason: ChatHistoryViewTransitionReason
|
||||
var prepareOnMainQueue = false
|
||||
|
||||
let previousHistoryAppearsClearedValue = previousHistoryAppearsCleared.swap(historyAppearsCleared)
|
||||
if previousHistoryAppearsClearedValue != nil && previousHistoryAppearsClearedValue != historyAppearsCleared && !historyAppearsCleared {
|
||||
reason = ChatHistoryViewTransitionReason.Initial(fadeIn: !processedView.filteredEntries.isEmpty)
|
||||
} else if let previous = previous, previous.id == processedView.id, previous.originalView.entries == processedView.originalView.entries {
|
||||
reason = ChatHistoryViewTransitionReason.InteractiveChanges
|
||||
updatedScrollPosition = nil
|
||||
} else {
|
||||
switch type {
|
||||
case let .Initial(fadeIn):
|
||||
reason = ChatHistoryViewTransitionReason.Initial(fadeIn: fadeIn)
|
||||
prepareOnMainQueue = !fadeIn
|
||||
case let .Generic(genericType):
|
||||
switch genericType {
|
||||
case .InitialUnread, .Initial:
|
||||
reason = ChatHistoryViewTransitionReason.Initial(fadeIn: false)
|
||||
case .Generic:
|
||||
reason = ChatHistoryViewTransitionReason.InteractiveChanges
|
||||
case .UpdateVisible:
|
||||
reason = ChatHistoryViewTransitionReason.Reload
|
||||
case .FillHole:
|
||||
reason = ChatHistoryViewTransitionReason.HoleReload
|
||||
}
|
||||
}
|
||||
}
|
||||
let transitionVersion = nextTransitionVersion.modify { $0 + 1 }
|
||||
return preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, initialData: initialData?.initialData, keyboardButtonsMessage: view.topTaggedMessages.first, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators)
|
||||
|> map({
|
||||
(mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, transition: $0), transitionVersion)
|
||||
})
|
||||
|> runOn(prepareOnMainQueue ? Queue.mainQueue() : messageViewQueue)
|
||||
|> introduceError(Void.self)
|
||||
}
|
||||
}
|
||||
|
||||
let appliedTransitionVersion = Atomic<Int?>(value: nil)
|
||||
|
||||
let appliedTransition = historyViewTransition
|
||||
|> deliverOnMainQueue
|
||||
|> mapToQueue { [weak self] (transition, version) -> Signal<Void, Void> in
|
||||
if let strongSelf = self {
|
||||
let previousAppliedVersion = appliedTransitionVersion.swap(version) ?? 0
|
||||
if !GlobalExperimentalSettings.isAppStoreBuild {
|
||||
precondition(version == previousAppliedVersion + 1)
|
||||
case let .Loading(combinedInitialData, type):
|
||||
if case .Generic(.FillHole) = type {
|
||||
applyHole()
|
||||
return
|
||||
}
|
||||
|
||||
initialData = combinedInitialData
|
||||
Queue.mainQueue().async {
|
||||
if let strongSelf = self {
|
||||
if !strongSelf.didSetInitialData {
|
||||
strongSelf.didSetInitialData = true
|
||||
strongSelf._initialData.set(.single(combinedInitialData))
|
||||
}
|
||||
|
||||
strongSelf._cachedPeerDataAndMessages.set(.single((nil, nil)))
|
||||
|
||||
let loadState: ChatHistoryNodeLoadState = .loading
|
||||
if strongSelf.loadState != loadState {
|
||||
strongSelf.loadState = loadState
|
||||
strongSelf.loadStateUpdated?(loadState, false)
|
||||
}
|
||||
|
||||
let historyState: ChatHistoryNodeHistoryState = .loading
|
||||
if strongSelf.currentHistoryState != historyState {
|
||||
strongSelf.currentHistoryState = historyState
|
||||
strongSelf.historyState.set(historyState)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
case let .HistoryView(view, type, scrollPosition, flashIndicators, originalScrollPosition, data, id):
|
||||
if case .Generic(.FillHole) = type {
|
||||
applyHole()
|
||||
return
|
||||
}
|
||||
|
||||
initialData = data
|
||||
var updatedScrollPosition = scrollPosition
|
||||
|
||||
var reverse = false
|
||||
var includeSearchEntry = false
|
||||
if case let .list(search, reverseValue) = mode {
|
||||
includeSearchEntry = search
|
||||
reverse = reverseValue
|
||||
}
|
||||
|
||||
let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType)
|
||||
|
||||
let processedView = ChatHistoryView(originalView: view, filteredEntries: chatHistoryEntriesForView(location: chatLocation, view: view, includeUnreadEntry: mode == .bubbles, includeEmptyEntry: mode == .bubbles && tagMask == nil, includeChatInfoEntry: mode == .bubbles, includeSearchEntry: includeSearchEntry && tagMask != nil, reverse: reverse, groupMessages: mode == .bubbles, selectedMessages: selectedMessages, presentationData: chatPresentationData, historyAppearsCleared: historyAppearsCleared), associatedData: associatedData, id: id)
|
||||
let previousValueAndVersion = previousView.swap((processedView, update.1))
|
||||
let previous = previousValueAndVersion?.0
|
||||
|
||||
if let previousVersion = previousValueAndVersion?.1 {
|
||||
if !GlobalExperimentalSettings.isAppStoreBuild {
|
||||
precondition(update.1 >= previousVersion)
|
||||
}
|
||||
assert(update.1 >= previousVersion)
|
||||
}
|
||||
|
||||
if scrollPosition == nil, let originalScrollPosition = originalScrollPosition {
|
||||
switch originalScrollPosition {
|
||||
case let .index(index, position, _, _):
|
||||
if case .upperBound = index {
|
||||
if let previous = previous, previous.filteredEntries.isEmpty {
|
||||
updatedScrollPosition = .index(index: index, position: position, directionHint: .Down, animated: false)
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let reason: ChatHistoryViewTransitionReason
|
||||
|
||||
let previousHistoryAppearsClearedValue = previousHistoryAppearsCleared.swap(historyAppearsCleared)
|
||||
if previousHistoryAppearsClearedValue != nil && previousHistoryAppearsClearedValue != historyAppearsCleared && !historyAppearsCleared {
|
||||
reason = ChatHistoryViewTransitionReason.Initial(fadeIn: !processedView.filteredEntries.isEmpty)
|
||||
} else if let previous = previous, previous.id == processedView.id, previous.originalView.entries == processedView.originalView.entries {
|
||||
reason = ChatHistoryViewTransitionReason.InteractiveChanges
|
||||
updatedScrollPosition = nil
|
||||
} else {
|
||||
switch type {
|
||||
case let .Initial(fadeIn):
|
||||
reason = ChatHistoryViewTransitionReason.Initial(fadeIn: fadeIn)
|
||||
case let .Generic(genericType):
|
||||
switch genericType {
|
||||
case .InitialUnread, .Initial:
|
||||
reason = ChatHistoryViewTransitionReason.Initial(fadeIn: false)
|
||||
case .Generic:
|
||||
reason = ChatHistoryViewTransitionReason.InteractiveChanges
|
||||
case .UpdateVisible:
|
||||
reason = ChatHistoryViewTransitionReason.Reload
|
||||
case .FillHole:
|
||||
reason = ChatHistoryViewTransitionReason.HoleReload
|
||||
}
|
||||
}
|
||||
}
|
||||
let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, initialData: initialData?.initialData, keyboardButtonsMessage: view.topTaggedMessages.first, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators)
|
||||
let mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, transition: rawTransition)
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.enqueueHistoryViewTransition(mappedTransition)
|
||||
}
|
||||
assert(version == previousAppliedVersion + 1)
|
||||
return strongSelf.enqueueHistoryViewTransition(transition)
|
||||
|> introduceError(Void.self)
|
||||
}
|
||||
return .complete()
|
||||
}
|
||||
})
|
||||
|
||||
let restartedTransition = (
|
||||
appliedTransition
|
||||
|> `catch` { _ -> Signal<Void, Void> in
|
||||
return .complete()
|
||||
}
|
||||
)
|
||||
|> restart
|
||||
self.historyDisposable.set(restartedTransition.start())
|
||||
self.historyDisposable.set(historyViewTransitionDisposable)
|
||||
|
||||
let previousMaxIncomingMessageIndexByNamespace = Atomic<[MessageId.Namespace: MessageIndex]>(value: [:])
|
||||
let readHistory = combineLatest(self.maxVisibleIncomingMessageIndex.get(), self.canReadHistory.get())
|
||||
@ -743,7 +717,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
|
||||
self.displayedItemRangeChanged = { [weak self] displayedRange, opaqueTransactionState in
|
||||
if let strongSelf = self, let transactionState = opaqueTransactionState as? ChatHistoryTransactionOpaqueState {
|
||||
self?.processDisplayedItemRangeChanged(displayedRange: displayedRange, transactionState: transactionState)
|
||||
strongSelf.processDisplayedItemRangeChanged(displayedRange: displayedRange, transactionState: transactionState)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1204,143 +1178,132 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
self.maxVisibleIncomingMessageIndex.set(index)
|
||||
}
|
||||
|
||||
private func enqueueHistoryViewTransition(_ transition: ChatHistoryListViewTransition) -> Signal<Void, NoError> {
|
||||
return Signal { [weak self] subscriber in
|
||||
private func enqueueHistoryViewTransition(_ transition: ChatHistoryListViewTransition) {
|
||||
self.enqueuedHistoryViewTransitions.append(transition)
|
||||
self.prefetchManager.updateOptions(InChatPrefetchOptions(networkType: transition.networkType, peerType: transition.peerType))
|
||||
|
||||
if !self.didSetInitialData {
|
||||
self.didSetInitialData = true
|
||||
self._initialData.set(.single(ChatHistoryCombinedInitialData(initialData: transition.initialData, buttonKeyboardMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData)))
|
||||
}
|
||||
|
||||
if self.isNodeLoaded {
|
||||
self.dequeueHistoryViewTransitions()
|
||||
} else {
|
||||
self._cachedPeerDataAndMessages.set(.single((transition.cachedData, transition.cachedDataMessages)))
|
||||
|
||||
let loadState: ChatHistoryNodeLoadState
|
||||
if transition.historyView.filteredEntries.isEmpty {
|
||||
loadState = .empty
|
||||
} else {
|
||||
loadState = .messages
|
||||
}
|
||||
if self.loadState != loadState {
|
||||
self.loadState = loadState
|
||||
self.loadStateUpdated?(loadState, transition.options.contains(.AnimateInsertion))
|
||||
}
|
||||
|
||||
let historyState: ChatHistoryNodeHistoryState = .loaded(isEmpty: transition.historyView.originalView.entries.isEmpty)
|
||||
if self.currentHistoryState != historyState {
|
||||
self.currentHistoryState = historyState
|
||||
self.historyState.set(historyState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func dequeueHistoryViewTransitions() {
|
||||
if self.enqueuedHistoryViewTransitions.isEmpty || self.hasActiveTransition {
|
||||
return
|
||||
}
|
||||
self.hasActiveTransition = true
|
||||
let transition = self.enqueuedHistoryViewTransitions.removeFirst()
|
||||
|
||||
let animated = transition.options.contains(.AnimateInsertion)
|
||||
|
||||
let completion: (ListViewDisplayedItemRange) -> Void = { [weak self] visibleRange in
|
||||
if let strongSelf = self {
|
||||
if let _ = strongSelf.enqueuedHistoryViewTransition {
|
||||
preconditionFailure()
|
||||
}
|
||||
strongSelf.historyView = transition.historyView
|
||||
|
||||
strongSelf.prefetchManager.updateOptions(InChatPrefetchOptions(networkType: transition.networkType, peerType: transition.peerType))
|
||||
|
||||
if !strongSelf.didSetInitialData {
|
||||
strongSelf.didSetInitialData = true
|
||||
strongSelf._initialData.set(.single(ChatHistoryCombinedInitialData(initialData: transition.initialData, buttonKeyboardMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData)))
|
||||
}
|
||||
|
||||
strongSelf.enqueuedHistoryViewTransition = (transition, {
|
||||
if let scrolledToIndex = transition.scrolledToIndex {
|
||||
if let strongSelf = self {
|
||||
strongSelf.scrolledToIndex?(scrolledToIndex)
|
||||
}
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
|
||||
if strongSelf.isNodeLoaded {
|
||||
strongSelf.dequeueHistoryViewTransition()
|
||||
} else {
|
||||
strongSelf._cachedPeerDataAndMessages.set(.single((transition.cachedData, transition.cachedDataMessages)))
|
||||
|
||||
let loadState: ChatHistoryNodeLoadState
|
||||
if transition.historyView.filteredEntries.isEmpty {
|
||||
let loadState: ChatHistoryNodeLoadState
|
||||
if let historyView = strongSelf.historyView {
|
||||
if historyView.filteredEntries.isEmpty {
|
||||
loadState = .empty
|
||||
} else {
|
||||
loadState = .messages
|
||||
}
|
||||
if strongSelf.loadState != loadState {
|
||||
strongSelf.loadState = loadState
|
||||
strongSelf.loadStateUpdated?(loadState, transition.options.contains(.AnimateInsertion))
|
||||
}
|
||||
|
||||
let historyState: ChatHistoryNodeHistoryState = .loaded(isEmpty: transition.historyView.originalView.entries.isEmpty)
|
||||
if strongSelf.currentHistoryState != historyState {
|
||||
strongSelf.currentHistoryState = historyState
|
||||
strongSelf.historyState.set(historyState)
|
||||
}
|
||||
} else {
|
||||
loadState = .loading
|
||||
}
|
||||
} else {
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
|
||||
return EmptyDisposable
|
||||
} |> runOn(Queue.mainQueue())
|
||||
}
|
||||
|
||||
private func dequeueHistoryViewTransition() {
|
||||
if let (transition, completion) = self.enqueuedHistoryViewTransition {
|
||||
self.enqueuedHistoryViewTransition = nil
|
||||
|
||||
let animated = transition.options.contains(.AnimateInsertion)
|
||||
|
||||
let completion: (ListViewDisplayedItemRange) -> Void = { [weak self] visibleRange in
|
||||
if let strongSelf = self {
|
||||
strongSelf.historyView = transition.historyView
|
||||
|
||||
let loadState: ChatHistoryNodeLoadState
|
||||
if let historyView = strongSelf.historyView {
|
||||
if historyView.filteredEntries.isEmpty {
|
||||
loadState = .empty
|
||||
} else {
|
||||
loadState = .messages
|
||||
}
|
||||
} else {
|
||||
loadState = .loading
|
||||
}
|
||||
|
||||
if strongSelf.loadState != loadState {
|
||||
strongSelf.loadState = loadState
|
||||
strongSelf.loadStateUpdated?(loadState, animated)
|
||||
}
|
||||
|
||||
if let range = visibleRange.loadedRange {
|
||||
if let visible = visibleRange.visibleRange {
|
||||
var visibleFirstIndex = visible.firstIndex
|
||||
/*if !visible.firstIndexFullyVisible {
|
||||
visibleFirstIndex += 1
|
||||
}*/
|
||||
if visibleFirstIndex <= visible.lastIndex {
|
||||
let (messageIndex, _) = maxMessageIndexForEntries(transition.historyView, indexRange: (transition.historyView.filteredEntries.count - 1 - visible.lastIndex, transition.historyView.filteredEntries.count - 1 - visibleFirstIndex))
|
||||
if let messageIndex = messageIndex {
|
||||
strongSelf.updateMaxVisibleReadIncomingMessageIndex(messageIndex)
|
||||
}
|
||||
|
||||
if strongSelf.loadState != loadState {
|
||||
strongSelf.loadState = loadState
|
||||
strongSelf.loadStateUpdated?(loadState, animated)
|
||||
}
|
||||
|
||||
if let range = visibleRange.loadedRange {
|
||||
if let visible = visibleRange.visibleRange {
|
||||
var visibleFirstIndex = visible.firstIndex
|
||||
/*if !visible.firstIndexFullyVisible {
|
||||
visibleFirstIndex += 1
|
||||
}*/
|
||||
if visibleFirstIndex <= visible.lastIndex {
|
||||
let (messageIndex, _) = maxMessageIndexForEntries(transition.historyView, indexRange: (transition.historyView.filteredEntries.count - 1 - visible.lastIndex, transition.historyView.filteredEntries.count - 1 - visibleFirstIndex))
|
||||
if let messageIndex = messageIndex {
|
||||
strongSelf.updateMaxVisibleReadIncomingMessageIndex(messageIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !strongSelf.didSetInitialData {
|
||||
strongSelf.didSetInitialData = true
|
||||
strongSelf._initialData.set(.single(ChatHistoryCombinedInitialData(initialData: transition.initialData, buttonKeyboardMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData)))
|
||||
}
|
||||
strongSelf._cachedPeerDataAndMessages.set(.single((transition.cachedData, transition.cachedDataMessages)))
|
||||
let historyState: ChatHistoryNodeHistoryState = .loaded(isEmpty: transition.historyView.originalView.entries.isEmpty)
|
||||
if strongSelf.currentHistoryState != historyState {
|
||||
strongSelf.currentHistoryState = historyState
|
||||
strongSelf.historyState.set(historyState)
|
||||
}
|
||||
|
||||
var buttonKeyboardMessageUpdated = false
|
||||
if let currentButtonKeyboardMessage = strongSelf.currentButtonKeyboardMessage, let buttonKeyboardMessage = transition.keyboardButtonsMessage {
|
||||
if currentButtonKeyboardMessage.id != buttonKeyboardMessage.id || currentButtonKeyboardMessage.stableVersion != buttonKeyboardMessage.stableVersion {
|
||||
buttonKeyboardMessageUpdated = true
|
||||
}
|
||||
} else if (strongSelf.currentButtonKeyboardMessage != nil) != (transition.keyboardButtonsMessage != nil) {
|
||||
}
|
||||
if !strongSelf.didSetInitialData {
|
||||
strongSelf.didSetInitialData = true
|
||||
strongSelf._initialData.set(.single(ChatHistoryCombinedInitialData(initialData: transition.initialData, buttonKeyboardMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData)))
|
||||
}
|
||||
strongSelf._cachedPeerDataAndMessages.set(.single((transition.cachedData, transition.cachedDataMessages)))
|
||||
let historyState: ChatHistoryNodeHistoryState = .loaded(isEmpty: transition.historyView.originalView.entries.isEmpty)
|
||||
if strongSelf.currentHistoryState != historyState {
|
||||
strongSelf.currentHistoryState = historyState
|
||||
strongSelf.historyState.set(historyState)
|
||||
}
|
||||
|
||||
var buttonKeyboardMessageUpdated = false
|
||||
if let currentButtonKeyboardMessage = strongSelf.currentButtonKeyboardMessage, let buttonKeyboardMessage = transition.keyboardButtonsMessage {
|
||||
if currentButtonKeyboardMessage.id != buttonKeyboardMessage.id || currentButtonKeyboardMessage.stableVersion != buttonKeyboardMessage.stableVersion {
|
||||
buttonKeyboardMessageUpdated = true
|
||||
}
|
||||
if buttonKeyboardMessageUpdated {
|
||||
strongSelf.currentButtonKeyboardMessage = transition.keyboardButtonsMessage
|
||||
strongSelf._buttonKeyboardMessage.set(.single(transition.keyboardButtonsMessage))
|
||||
}
|
||||
|
||||
if transition.animateIn {
|
||||
strongSelf.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
}
|
||||
|
||||
completion()
|
||||
} else if (strongSelf.currentButtonKeyboardMessage != nil) != (transition.keyboardButtonsMessage != nil) {
|
||||
buttonKeyboardMessageUpdated = true
|
||||
}
|
||||
if buttonKeyboardMessageUpdated {
|
||||
strongSelf.currentButtonKeyboardMessage = transition.keyboardButtonsMessage
|
||||
strongSelf._buttonKeyboardMessage.set(.single(transition.keyboardButtonsMessage))
|
||||
}
|
||||
}
|
||||
|
||||
if let layoutActionOnViewTransition = self.layoutActionOnViewTransition {
|
||||
self.layoutActionOnViewTransition = nil
|
||||
let (mappedTransition, updateSizeAndInsets) = layoutActionOnViewTransition(transition)
|
||||
|
||||
self.transaction(deleteIndices: mappedTransition.deleteItems, insertIndicesAndItems: transition.insertItems, updateIndicesAndItems: transition.updateItems, options: mappedTransition.options, scrollToItem: mappedTransition.scrollToItem, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: mappedTransition.stationaryItemRange, updateOpaqueState: ChatHistoryTransactionOpaqueState(historyView: transition.historyView), completion: completion)
|
||||
} else {
|
||||
self.transaction(deleteIndices: transition.deleteItems, insertIndicesAndItems: transition.insertItems, updateIndicesAndItems: transition.updateItems, options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, updateOpaqueState: ChatHistoryTransactionOpaqueState(historyView: transition.historyView), completion: completion)
|
||||
if transition.animateIn {
|
||||
strongSelf.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
}
|
||||
|
||||
if let scrolledToIndex = transition.scrolledToIndex {
|
||||
if let strongSelf = self {
|
||||
strongSelf.scrolledToIndex?(scrolledToIndex)
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.hasActiveTransition = false
|
||||
strongSelf.dequeueHistoryViewTransitions()
|
||||
}
|
||||
}
|
||||
|
||||
if let layoutActionOnViewTransition = self.layoutActionOnViewTransition {
|
||||
self.layoutActionOnViewTransition = nil
|
||||
let (mappedTransition, updateSizeAndInsets) = layoutActionOnViewTransition(transition)
|
||||
|
||||
if transition.flashIndicators {
|
||||
//self.flashHeaderItems()
|
||||
}
|
||||
self.transaction(deleteIndices: mappedTransition.deleteItems, insertIndicesAndItems: transition.insertItems, updateIndicesAndItems: transition.updateItems, options: mappedTransition.options, scrollToItem: mappedTransition.scrollToItem, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: mappedTransition.stationaryItemRange, updateOpaqueState: ChatHistoryTransactionOpaqueState(historyView: transition.historyView), completion: completion)
|
||||
} else {
|
||||
self.transaction(deleteIndices: transition.deleteItems, insertIndicesAndItems: transition.insertItems, updateIndicesAndItems: transition.updateItems, options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, updateOpaqueState: ChatHistoryTransactionOpaqueState(historyView: transition.historyView), completion: completion)
|
||||
}
|
||||
|
||||
if transition.flashIndicators {
|
||||
//self.flashHeaderItems()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1357,7 +1320,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
|
||||
if !self.dequeuedInitialTransitionOnLayout {
|
||||
self.dequeuedInitialTransitionOnLayout = true
|
||||
self.dequeueHistoryViewTransition()
|
||||
self.dequeueHistoryViewTransitions()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1537,6 +1537,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
|
||||
for (relativeFrame, _, apply) in contentNodeFramesPropertiesAndApply {
|
||||
apply(animation, synchronousLoads)
|
||||
|
||||
if contentNodeIndex >= strongSelf.contentNodes.count {
|
||||
break
|
||||
}
|
||||
|
||||
let contentNode = strongSelf.contentNodes[contentNodeIndex]
|
||||
let contentNodeFrame = relativeFrame.offsetBy(dx: contentOrigin.x, dy: contentOrigin.y)
|
||||
let previousContentNodeFrame = contentNode.frame
|
||||
|
@ -4,197 +4,192 @@ import Postbox
|
||||
import TelegramCore
|
||||
import Display
|
||||
|
||||
func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toView: ChatHistoryView, reason: ChatHistoryViewTransitionReason, reverse: Bool, chatLocation: ChatLocation, controllerInteraction: ChatControllerInteraction, scrollPosition: ChatHistoryViewScrollPosition?, initialData: InitialMessageHistoryData?, keyboardButtonsMessage: Message?, cachedData: CachedPeerData?, cachedDataMessages: [MessageId: Message]?, readStateData: [PeerId: ChatHistoryCombinedInitialReadStateData]?, flashIndicators: Bool) -> Signal<ChatHistoryViewTransition, NoError> {
|
||||
return Signal { subscriber in
|
||||
let mergeResult: (deleteIndices: [Int], indicesAndItems: [(Int, ChatHistoryEntry, Int?)], updateIndices: [(Int, ChatHistoryEntry, Int)])
|
||||
let 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)
|
||||
}
|
||||
|
||||
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)
|
||||
/*if let (_, removeDirection) = removeHoleDirections.first {
|
||||
switch removeDirection {
|
||||
case .LowerToUpper:
|
||||
var holeIndex: MessageIndex?
|
||||
for (index, _) in filledHoleDirections {
|
||||
if holeIndex == nil || index < holeIndex! {
|
||||
holeIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
if let holeIndex = holeIndex {
|
||||
for i in 0 ..< toView.filteredEntries.count {
|
||||
if toView.filteredEntries[i].index >= holeIndex {
|
||||
let index = toView.filteredEntries.count - 1 - (i - 1)
|
||||
stationaryItemRange = (index, Int.max)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
case .UpperToLower:
|
||||
break
|
||||
case .AroundId, .AroundIndex:
|
||||
break
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
for (index, entry, previousIndex) in mergeResult.indicesAndItems {
|
||||
let adjustedIndex = updatedCount - 1 - index
|
||||
|
||||
let adjustedPrevousIndex: Int?
|
||||
if let previousIndex = previousIndex {
|
||||
adjustedPrevousIndex = previousCount - 1 - previousIndex
|
||||
func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toView: ChatHistoryView, reason: ChatHistoryViewTransitionReason, reverse: Bool, chatLocation: ChatLocation, controllerInteraction: ChatControllerInteraction, scrollPosition: ChatHistoryViewScrollPosition?, initialData: InitialMessageHistoryData?, keyboardButtonsMessage: Message?, cachedData: CachedPeerData?, cachedDataMessages: [MessageId: Message]?, readStateData: [PeerId: ChatHistoryCombinedInitialReadStateData]?, flashIndicators: Bool) -> ChatHistoryViewTransition {
|
||||
let mergeResult: (deleteIndices: [Int], indicesAndItems: [(Int, ChatHistoryEntry, Int?)], updateIndices: [(Int, ChatHistoryEntry, Int)])
|
||||
let 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)
|
||||
}
|
||||
|
||||
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 {
|
||||
adjustedPrevousIndex = nil
|
||||
let _ = options.insert(.LowLatency)
|
||||
let _ = options.insert(.Synchronous)
|
||||
let _ = options.insert(.PreferSynchronousResourceLoading)
|
||||
}
|
||||
case .InteractiveChanges:
|
||||
let _ = options.insert(.AnimateAlpha)
|
||||
let _ = options.insert(.AnimateInsertion)
|
||||
|
||||
var directionHint: ListViewItemOperationDirectionHint?
|
||||
if maxAnimatedInsertionIndex >= 0 && adjustedIndex <= maxAnimatedInsertionIndex {
|
||||
directionHint = .Down
|
||||
for (index, _, _) in mergeResult.indicesAndItems.sorted(by: { $0.0 > $1.0 }) {
|
||||
let adjustedIndex = updatedCount - 1 - index
|
||||
if adjustedIndex == maxAnimatedInsertionIndex + 1 {
|
||||
maxAnimatedInsertionIndex += 1
|
||||
}
|
||||
}
|
||||
|
||||
adjustedIndicesAndItems.append(ChatHistoryViewTransitionInsertEntry(index: adjustedIndex, previousIndex: adjustedPrevousIndex, entry: entry, directionHint: directionHint))
|
||||
case .Reload:
|
||||
stationaryItemRange = (0, Int.max)
|
||||
case .HoleReload:
|
||||
stationaryItemRange = (0, Int.max)
|
||||
/*if let (_, removeDirection) = removeHoleDirections.first {
|
||||
switch removeDirection {
|
||||
case .LowerToUpper:
|
||||
var holeIndex: MessageIndex?
|
||||
for (index, _) in filledHoleDirections {
|
||||
if holeIndex == nil || index < holeIndex! {
|
||||
holeIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
if let holeIndex = holeIndex {
|
||||
for i in 0 ..< toView.filteredEntries.count {
|
||||
if toView.filteredEntries[i].index >= holeIndex {
|
||||
let index = toView.filteredEntries.count - 1 - (i - 1)
|
||||
stationaryItemRange = (index, Int.max)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
case .UpperToLower:
|
||||
break
|
||||
case .AroundId, .AroundIndex:
|
||||
break
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 directionHint: ListViewItemOperationDirectionHint?
|
||||
if maxAnimatedInsertionIndex >= 0 && adjustedIndex <= maxAnimatedInsertionIndex {
|
||||
directionHint = .Down
|
||||
}
|
||||
|
||||
var scrolledToIndex: MessageHistoryAnchorIndex?
|
||||
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
|
||||
|
||||
if let scrollPosition = scrollPosition {
|
||||
switch scrollPosition {
|
||||
case let .unread(unreadIndex):
|
||||
let directionHint: ListViewItemOperationDirectionHint? = nil
|
||||
adjustedUpdateItems.append(ChatHistoryViewTransitionUpdateEntry(index: adjustedIndex, previousIndex: adjustedPreviousIndex, entry: entry, directionHint: directionHint))
|
||||
}
|
||||
|
||||
var scrolledToIndex: MessageHistoryAnchorIndex?
|
||||
|
||||
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: .Default(duration: nil), directionHint: .Down)
|
||||
break
|
||||
}
|
||||
index -= 1
|
||||
}
|
||||
|
||||
if scrollToItem == nil {
|
||||
var index = toView.filteredEntries.count - 1
|
||||
for entry in toView.filteredEntries {
|
||||
if case .UnreadEntry = entry {
|
||||
if entry.index >= unreadIndex {
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: .Default(duration: nil), 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: .Default(duration: nil), directionHint: .Down)
|
||||
break
|
||||
}
|
||||
index -= 1
|
||||
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: .Default(duration: nil), 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: .Default(duration: nil), directionHint: .Down)
|
||||
break
|
||||
}
|
||||
|
||||
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: .Default(duration: nil), 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: .Default(duration: nil), directionHint: .Down)
|
||||
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: .Default(duration: nil), directionHint: .Down)
|
||||
break
|
||||
}
|
||||
index -= 1
|
||||
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: .Default(duration: nil), directionHint: .Down)
|
||||
break
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
case let .index(scrollIndex, position, directionHint, animated):
|
||||
if case .center = position {
|
||||
scrolledToIndex = scrollIndex
|
||||
}
|
||||
var index = toView.filteredEntries.count - 1
|
||||
for entry in toView.filteredEntries {
|
||||
if scrollIndex.isLessOrEqual(to: entry.index) {
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default(duration: nil), directionHint: directionHint)
|
||||
break
|
||||
}
|
||||
case let .index(scrollIndex, position, directionHint, animated):
|
||||
if case .center = position {
|
||||
scrolledToIndex = scrollIndex
|
||||
}
|
||||
var index = toView.filteredEntries.count - 1
|
||||
for entry in toView.filteredEntries {
|
||||
if scrollIndex.isLessOrEqual(to: entry.index) {
|
||||
index -= 1
|
||||
}
|
||||
|
||||
if scrollToItem == nil {
|
||||
var index = 0
|
||||
for entry in toView.filteredEntries.reversed() {
|
||||
if !scrollIndex.isLess(than: entry.index) {
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default(duration: nil), directionHint: directionHint)
|
||||
break
|
||||
}
|
||||
index -= 1
|
||||
index += 1
|
||||
}
|
||||
|
||||
if scrollToItem == nil {
|
||||
var index = 0
|
||||
for entry in toView.filteredEntries.reversed() {
|
||||
if !scrollIndex.isLess(than: entry.index) {
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: position, animated: animated, curve: .Default(duration: nil), directionHint: directionHint)
|
||||
break
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subscriber.putNext(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, animateIn: animateIn, reason: reason, flashIndicators: flashIndicators))
|
||||
subscriber.putCompletion()
|
||||
|
||||
return EmptyDisposable
|
||||
}
|
||||
|
||||
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, animateIn: animateIn, reason: reason, flashIndicators: flashIndicators)
|
||||
}
|
||||
|
@ -7,12 +7,12 @@
|
||||
<key>Lottie.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>3</integer>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
<key>Lottie_iOS.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>32</integer>
|
||||
<integer>31</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
|
Loading…
x
Reference in New Issue
Block a user