mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-07 08:01:10 +00:00
Added recent stickers clearing Added sending logs via email Added forward recipient change on forward acccessory panel tap Tweaked undo panel design Various UI fixes
507 lines
26 KiB
Swift
507 lines
26 KiB
Swift
import Foundation
|
|
import Postbox
|
|
import SwiftSignalKit
|
|
import Display
|
|
import AsyncDisplayKit
|
|
import TelegramCore
|
|
|
|
private class ChatGridLiveSelectorRecognizer: UIPanGestureRecognizer {
|
|
private let selectionGestureActivationThreshold: CGFloat = 2.0
|
|
private let selectionGestureVerticalFailureThreshold: CGFloat = 5.0
|
|
|
|
var validatedGesture: Bool? = nil
|
|
var firstLocation: CGPoint = CGPoint()
|
|
|
|
var shouldBegin: (() -> Bool)?
|
|
|
|
override init(target: Any?, action: Selector?) {
|
|
super.init(target: target, action: action)
|
|
|
|
self.maximumNumberOfTouches = 1
|
|
}
|
|
|
|
override func reset() {
|
|
super.reset()
|
|
|
|
self.validatedGesture = nil
|
|
}
|
|
|
|
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
|
|
super.touchesBegan(touches, with: event)
|
|
|
|
if let shouldBegin = self.shouldBegin, !shouldBegin() {
|
|
self.state = .failed
|
|
} else {
|
|
let touch = touches.first!
|
|
self.firstLocation = touch.location(in: self.view)
|
|
}
|
|
}
|
|
|
|
|
|
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
|
|
let location = touches.first!.location(in: self.view)
|
|
let translation = CGPoint(x: location.x - self.firstLocation.x, y: location.y - self.firstLocation.y)
|
|
|
|
if self.validatedGesture == nil {
|
|
if (fabs(translation.y) >= selectionGestureVerticalFailureThreshold) {
|
|
self.validatedGesture = false
|
|
}
|
|
else if (fabs(translation.x) >= selectionGestureActivationThreshold) {
|
|
self.validatedGesture = true
|
|
}
|
|
}
|
|
|
|
if let validatedGesture = self.validatedGesture {
|
|
if validatedGesture {
|
|
super.touchesMoved(touches, with: event)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ChatHistoryGridViewTransition {
|
|
let historyView: ChatHistoryView
|
|
let topOffsetWithinMonth: Int
|
|
let deleteItems: [Int]
|
|
let insertItems: [GridNodeInsertItem]
|
|
let updateItems: [GridNodeUpdateItem]
|
|
let scrollToItem: GridNodeScrollToItem?
|
|
let stationaryItems: GridNodeStationaryItems
|
|
}
|
|
|
|
private func mappedInsertEntries(context: AccountContext, peerId: PeerId, controllerInteraction: ChatControllerInteraction, entries: [ChatHistoryViewTransitionInsertEntry], theme: PresentationTheme, strings: PresentationStrings) -> [GridNodeInsertItem] {
|
|
return entries.map { entry -> GridNodeInsertItem in
|
|
switch entry.entry {
|
|
case let .MessageEntry(message, _, _, _, _, _):
|
|
return GridNodeInsertItem(index: entry.index, item: GridMessageItem(theme: theme, strings: strings, context: context, message: message, controllerInteraction: controllerInteraction), previousIndex: entry.previousIndex)
|
|
case .MessageGroupEntry:
|
|
return GridNodeInsertItem(index: entry.index, item: GridHoleItem(), previousIndex: entry.previousIndex)
|
|
case .UnreadEntry:
|
|
assertionFailure()
|
|
return GridNodeInsertItem(index: entry.index, item: GridHoleItem(), previousIndex: entry.previousIndex)
|
|
case .ChatInfoEntry, .SearchEntry:
|
|
assertionFailure()
|
|
return GridNodeInsertItem(index: entry.index, item: GridHoleItem(), previousIndex: entry.previousIndex)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func mappedUpdateEntries(context: AccountContext, peerId: PeerId, controllerInteraction: ChatControllerInteraction, entries: [ChatHistoryViewTransitionUpdateEntry], theme: PresentationTheme, strings: PresentationStrings) -> [GridNodeUpdateItem] {
|
|
return entries.map { entry -> GridNodeUpdateItem in
|
|
switch entry.entry {
|
|
case let .MessageEntry(message, _, _, _, _, _):
|
|
return GridNodeUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: GridMessageItem(theme: theme, strings: strings, context: context, message: message, controllerInteraction: controllerInteraction))
|
|
case .MessageGroupEntry:
|
|
return GridNodeUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: GridHoleItem())
|
|
case .UnreadEntry:
|
|
assertionFailure()
|
|
return GridNodeUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: GridHoleItem())
|
|
case .ChatInfoEntry, .SearchEntry:
|
|
assertionFailure()
|
|
return GridNodeUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: GridHoleItem())
|
|
}
|
|
}
|
|
}
|
|
|
|
private func mappedChatHistoryViewListTransition(context: AccountContext, peerId: PeerId, controllerInteraction: ChatControllerInteraction, transition: ChatHistoryViewTransition, from: ChatHistoryView?, presentationData: ChatPresentationData) -> ChatHistoryGridViewTransition {
|
|
var mappedScrollToItem: GridNodeScrollToItem?
|
|
if let scrollToItem = transition.scrollToItem {
|
|
let mappedPosition: GridNodeScrollToItemPosition
|
|
switch scrollToItem.position {
|
|
case .top:
|
|
mappedPosition = .top(0.0)
|
|
case .center:
|
|
mappedPosition = .center(0.0)
|
|
case .bottom:
|
|
mappedPosition = .bottom(0.0)
|
|
case .visible:
|
|
mappedPosition = .bottom(0.0)
|
|
}
|
|
let scrollTransition: ContainedViewLayoutTransition
|
|
if scrollToItem.animated {
|
|
switch scrollToItem.curve {
|
|
case .Default:
|
|
scrollTransition = .animated(duration: 0.3, curve: .easeInOut)
|
|
case let .Spring(duration):
|
|
scrollTransition = .animated(duration: duration, curve: .spring)
|
|
}
|
|
} else {
|
|
scrollTransition = .immediate
|
|
}
|
|
let directionHint: GridNodePreviousItemsTransitionDirectionHint
|
|
switch scrollToItem.directionHint {
|
|
case .Up:
|
|
directionHint = .up
|
|
case .Down:
|
|
directionHint = .down
|
|
}
|
|
mappedScrollToItem = GridNodeScrollToItem(index: scrollToItem.index, position: mappedPosition, transition: scrollTransition, directionHint: directionHint, adjustForSection: true, adjustForTopInset: true)
|
|
}
|
|
|
|
var stationaryItems: GridNodeStationaryItems = .none
|
|
if let previousView = from {
|
|
if let stationaryRange = transition.stationaryItemRange {
|
|
var fromStableIds = Set<UInt64>()
|
|
for i in 0 ..< previousView.filteredEntries.count {
|
|
if i >= stationaryRange.0 && i <= stationaryRange.1 {
|
|
fromStableIds.insert(previousView.filteredEntries[i].stableId)
|
|
}
|
|
}
|
|
var index = 0
|
|
var indices = Set<Int>()
|
|
for entry in transition.historyView.filteredEntries {
|
|
if fromStableIds.contains(entry.stableId) {
|
|
indices.insert(transition.historyView.filteredEntries.count - 1 - index)
|
|
}
|
|
index += 1
|
|
}
|
|
stationaryItems = .indices(indices)
|
|
} else {
|
|
var fromStableIds = Set<UInt64>()
|
|
for i in 0 ..< previousView.filteredEntries.count {
|
|
fromStableIds.insert(previousView.filteredEntries[i].stableId)
|
|
}
|
|
var index = 0
|
|
var indices = Set<Int>()
|
|
for entry in transition.historyView.filteredEntries {
|
|
if fromStableIds.contains(entry.stableId) {
|
|
indices.insert(transition.historyView.filteredEntries.count - 1 - index)
|
|
}
|
|
index += 1
|
|
}
|
|
stationaryItems = .indices(indices)
|
|
}
|
|
}
|
|
|
|
var topOffsetWithinMonth: Int = 0
|
|
if let lastEntry = transition.historyView.filteredEntries.last {
|
|
switch lastEntry {
|
|
case let .MessageEntry(_, _, _, monthLocation, _, _):
|
|
if let monthLocation = monthLocation {
|
|
topOffsetWithinMonth = Int(monthLocation.indexInMonth)
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
return ChatHistoryGridViewTransition(historyView: transition.historyView, topOffsetWithinMonth: topOffsetWithinMonth, deleteItems: transition.deleteItems.map { $0.index }, insertItems: mappedInsertEntries(context: context, peerId: peerId, controllerInteraction: controllerInteraction, entries: transition.insertEntries, theme: presentationData.theme.theme, strings: presentationData.strings), updateItems: mappedUpdateEntries(context: context, peerId: peerId, controllerInteraction: controllerInteraction, entries: transition.updateEntries, theme: presentationData.theme.theme, strings: presentationData.strings), scrollToItem: mappedScrollToItem, stationaryItems: stationaryItems)
|
|
}
|
|
|
|
private func gridNodeLayoutForContainerLayout(size: CGSize) -> GridNodeLayoutType {
|
|
let side = floorToScreenPixels((size.width - 3.0) / 4.0)
|
|
return .fixed(itemSize: CGSize(width: side, height: side), fillWidth: true, lineSpacing: 1.0, itemSpacing: 1.0)
|
|
}
|
|
|
|
public final class ChatHistoryGridNode: GridNode, ChatHistoryNode {
|
|
private let context: AccountContext
|
|
private let peerId: PeerId
|
|
private let messageId: MessageId?
|
|
private let tagMask: MessageTags?
|
|
|
|
private var historyView: ChatHistoryView?
|
|
|
|
private let historyDisposable = MetaDisposable()
|
|
|
|
private let messageViewQueue = Queue()
|
|
|
|
private var dequeuedInitialTransitionOnLayout = false
|
|
private var enqueuedHistoryViewTransition: (ChatHistoryGridViewTransition, () -> Void)?
|
|
var layoutActionOnViewTransition: ((ChatHistoryGridViewTransition) -> (ChatHistoryGridViewTransition, ListViewUpdateSizeAndInsets?))?
|
|
|
|
public let historyState = ValuePromise<ChatHistoryNodeHistoryState>()
|
|
private var currentHistoryState: ChatHistoryNodeHistoryState?
|
|
|
|
public var preloadPages: Bool = true {
|
|
didSet {
|
|
if self.preloadPages != oldValue {
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
private let _chatHistoryLocation = ValuePromise<ChatHistoryLocation>(ignoreRepeated: true)
|
|
private var chatHistoryLocation: Signal<ChatHistoryLocation, NoError> {
|
|
return self._chatHistoryLocation.get()
|
|
}
|
|
|
|
private let galleryHiddenMesageAndMediaDisposable = MetaDisposable()
|
|
|
|
private var presentationData: PresentationData
|
|
private let chatPresentationDataPromise = Promise<ChatPresentationData>()
|
|
|
|
public private(set) var loadState: ChatHistoryNodeLoadState?
|
|
private var loadStateUpdated: ((ChatHistoryNodeLoadState, Bool) -> Void)?
|
|
private let controllerInteraction: ChatControllerInteraction
|
|
|
|
public init(context: AccountContext, peerId: PeerId, messageId: MessageId?, tagMask: MessageTags?, controllerInteraction: ChatControllerInteraction) {
|
|
self.context = context
|
|
self.peerId = peerId
|
|
self.messageId = messageId
|
|
self.tagMask = tagMask
|
|
self.controllerInteraction = controllerInteraction
|
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
|
|
super.init()
|
|
|
|
self.chatPresentationDataPromise.set(context.sharedContext.presentationData
|
|
|> map { presentationData in
|
|
return ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations, largeEmoji: presentationData.largeEmoji)
|
|
})
|
|
|
|
self.floatingSections = true
|
|
|
|
let messageViewQueue = self.messageViewQueue
|
|
|
|
let historyViewUpdate = self.chatHistoryLocation
|
|
|> distinctUntilChanged
|
|
|> mapToSignal { location in
|
|
return chatHistoryViewForLocation(ChatHistoryLocationInput(content: location, id: 0), account: context.account, chatLocation: .peer(peerId), fixedCombinedReadStates: nil, tagMask: tagMask, additionalData: [], orderStatistics: [.locationWithinMonth])
|
|
}
|
|
|
|
let previousView = Atomic<ChatHistoryView?>(value: nil)
|
|
|
|
let historyViewTransition = combineLatest(historyViewUpdate, self.chatPresentationDataPromise.get()) |> mapToQueue { [weak self] update, chatPresentationData -> Signal<ChatHistoryGridViewTransition, NoError> in
|
|
switch update {
|
|
case .Loading:
|
|
Queue.mainQueue().async { [weak self] in
|
|
if let strongSelf = self {
|
|
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, _, _, 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:
|
|
reason = ChatHistoryViewTransitionReason.Initial(fadeIn: false)
|
|
case .Generic:
|
|
reason = ChatHistoryViewTransitionReason.InteractiveChanges
|
|
case .UpdateVisible:
|
|
reason = ChatHistoryViewTransitionReason.Reload
|
|
case .FillHole:
|
|
reason = ChatHistoryViewTransitionReason.Reload
|
|
}
|
|
}
|
|
|
|
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 appliedTransition = historyViewTransition |> deliverOnMainQueue |> mapToQueue { [weak self] transition -> Signal<Void, NoError> in
|
|
if let strongSelf = self {
|
|
return strongSelf.enqueueHistoryViewTransition(transition)
|
|
}
|
|
return .complete()
|
|
}
|
|
|
|
self.historyDisposable.set(appliedTransition.start())
|
|
|
|
if let messageId = messageId {
|
|
self._chatHistoryLocation.set(ChatHistoryLocation.InitialSearch(location: .id(messageId), count: 100))
|
|
} else {
|
|
self._chatHistoryLocation.set(ChatHistoryLocation.Initial(count: 100))
|
|
}
|
|
|
|
self.visibleItemsUpdated = { [weak self] visibleItems in
|
|
if let strongSelf = self, let historyView = strongSelf.historyView, let top = visibleItems.top, let bottom = visibleItems.bottom, let visibleTop = visibleItems.topVisible, let visibleBottom = visibleItems.bottomVisible {
|
|
if top.0 < 5 && historyView.originalView.laterId != nil {
|
|
let lastEntry = historyView.filteredEntries[historyView.filteredEntries.count - 1 - visibleTop.0]
|
|
strongSelf._chatHistoryLocation.set(ChatHistoryLocation.Navigation(index: .message(lastEntry.index), anchorIndex: .message(lastEntry.index), count: 200))
|
|
} else if bottom.0 >= historyView.filteredEntries.count - 5 && historyView.originalView.earlierId != nil {
|
|
let firstEntry = historyView.filteredEntries[historyView.filteredEntries.count - 1 - visibleBottom.0]
|
|
strongSelf._chatHistoryLocation.set(ChatHistoryLocation.Navigation(index: .message(firstEntry.index), anchorIndex: .message(firstEntry.index), count: 200))
|
|
}
|
|
}
|
|
}
|
|
|
|
let selectorRecogizner = ChatGridLiveSelectorRecognizer(target: self, action: #selector(self.panGesture(_:)))
|
|
selectorRecogizner.shouldBegin = { [weak controllerInteraction] in
|
|
return controllerInteraction?.selectionState != nil
|
|
}
|
|
self.view.addGestureRecognizer(selectorRecogizner)
|
|
}
|
|
|
|
public override func didLoad() {
|
|
super.didLoad()
|
|
}
|
|
|
|
private var liveSelectingState: (selecting: Bool, currentMessageId: MessageId)?
|
|
|
|
@objc private func panGesture(_ recognizer: UIGestureRecognizer) -> Void {
|
|
guard let selectionState = controllerInteraction.selectionState else {return}
|
|
|
|
switch recognizer.state {
|
|
case .began:
|
|
if let itemNode = self.itemNodeAtPoint(recognizer.location(in: self.view)) as? GridMessageItemNode, let messageId = itemNode.messageId {
|
|
liveSelectingState = (selecting: !selectionState.selectedIds.contains(messageId), currentMessageId: messageId)
|
|
controllerInteraction.toggleMessagesSelection([messageId], !selectionState.selectedIds.contains(messageId))
|
|
}
|
|
case .changed:
|
|
if let liveSelectingState = liveSelectingState, let itemNode = self.itemNodeAtPoint(recognizer.location(in: self.view)) as? GridMessageItemNode, let messageId = itemNode.messageId, messageId != liveSelectingState.currentMessageId {
|
|
controllerInteraction.toggleMessagesSelection([messageId], liveSelectingState.selecting)
|
|
self.liveSelectingState?.currentMessageId = messageId
|
|
}
|
|
case .ended, .failed, .cancelled:
|
|
liveSelectingState = nil
|
|
case .possible:
|
|
break
|
|
}
|
|
}
|
|
|
|
required public init?(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
deinit {
|
|
self.historyDisposable.dispose()
|
|
}
|
|
|
|
public func setLoadStateUpdated(_ f: @escaping (ChatHistoryNodeLoadState, Bool) -> Void) {
|
|
self.loadStateUpdated = f
|
|
}
|
|
|
|
public func scrollToStartOfHistory() {
|
|
self._chatHistoryLocation.set(ChatHistoryLocation.Scroll(index: .lowerBound, anchorIndex: .lowerBound, sourceIndex: .upperBound, scrollPosition: .bottom(0.0), animated: true))
|
|
}
|
|
|
|
public func scrollToEndOfHistory() {
|
|
self._chatHistoryLocation.set(ChatHistoryLocation.Scroll(index: .upperBound, anchorIndex: .upperBound, sourceIndex: .lowerBound, scrollPosition: .top(0.0), animated: true))
|
|
}
|
|
|
|
public func scrollToMessage(from fromIndex: MessageIndex, to toIndex: MessageIndex, scrollPosition: ListViewScrollPosition = .center(.bottom)) {
|
|
self._chatHistoryLocation.set(ChatHistoryLocation.Scroll(index: .message(toIndex), anchorIndex: .message(toIndex), sourceIndex: .message(fromIndex), scrollPosition: .center(.bottom), animated: true))
|
|
}
|
|
|
|
public func messageInCurrentHistoryView(_ id: MessageId) -> Message? {
|
|
if let historyView = self.historyView {
|
|
for case let .MessageEntry(message, _, _, _, _, _) in historyView.filteredEntries where message.id == id {
|
|
return message
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
private func enqueueHistoryViewTransition(_ transition: ChatHistoryGridViewTransition) -> Signal<Void, NoError> {
|
|
return Signal { [weak self] subscriber in
|
|
if let strongSelf = self {
|
|
if let _ = strongSelf.enqueuedHistoryViewTransition {
|
|
preconditionFailure()
|
|
}
|
|
|
|
strongSelf.enqueuedHistoryViewTransition = (transition, {
|
|
subscriber.putCompletion()
|
|
})
|
|
|
|
if strongSelf.isNodeLoaded {
|
|
strongSelf.dequeueHistoryViewTransition()
|
|
} else {
|
|
let loadState: ChatHistoryNodeLoadState
|
|
if transition.historyView.filteredEntries.isEmpty {
|
|
loadState = .empty
|
|
} else {
|
|
loadState = .messages
|
|
}
|
|
if strongSelf.loadState != loadState {
|
|
strongSelf.loadState = loadState
|
|
strongSelf.loadStateUpdated?(loadState, false)
|
|
}
|
|
|
|
let historyState: ChatHistoryNodeHistoryState = .loaded(isEmpty: transition.historyView.originalView.entries.isEmpty)
|
|
if strongSelf.currentHistoryState != historyState {
|
|
strongSelf.currentHistoryState = historyState
|
|
strongSelf.historyState.set(historyState)
|
|
}
|
|
}
|
|
} else {
|
|
subscriber.putCompletion()
|
|
}
|
|
|
|
return EmptyDisposable
|
|
} |> runOn(Queue.mainQueue())
|
|
}
|
|
|
|
private func dequeueHistoryViewTransition() {
|
|
if let (transition, completion) = self.enqueuedHistoryViewTransition {
|
|
self.enqueuedHistoryViewTransition = nil
|
|
|
|
let completion: (GridNodeDisplayedItemRange) -> 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, false)
|
|
}
|
|
|
|
let historyState: ChatHistoryNodeHistoryState = .loaded(isEmpty: transition.historyView.originalView.entries.isEmpty)
|
|
if strongSelf.currentHistoryState != historyState {
|
|
strongSelf.currentHistoryState = historyState
|
|
strongSelf.historyState.set(historyState)
|
|
}
|
|
|
|
completion()
|
|
}
|
|
}
|
|
|
|
if let layoutActionOnViewTransition = self.layoutActionOnViewTransition {
|
|
self.layoutActionOnViewTransition = nil
|
|
let (mappedTransition, updateSizeAndInsets) = layoutActionOnViewTransition(transition)
|
|
|
|
var updateLayout: GridNodeUpdateLayout?
|
|
if let updateSizeAndInsets = updateSizeAndInsets {
|
|
updateLayout = GridNodeUpdateLayout(layout: GridNodeLayout(size: updateSizeAndInsets.size, insets: updateSizeAndInsets.insets, preloadSize: 400.0, type: .fixed(itemSize: CGSize(width: 200.0, height: 200.0), fillWidth: nil, lineSpacing: 0.0, itemSpacing: nil)), transition: .immediate)
|
|
}
|
|
|
|
self.transaction(GridNodeTransaction(deleteItems: mappedTransition.deleteItems, insertItems: mappedTransition.insertItems, updateItems: mappedTransition.updateItems, scrollToItem: mappedTransition.scrollToItem, updateLayout: updateLayout, itemTransition: .immediate, stationaryItems: transition.stationaryItems, updateFirstIndexInSectionOffset: mappedTransition.topOffsetWithinMonth), completion: completion)
|
|
} else {
|
|
self.transaction(GridNodeTransaction(deleteItems: transition.deleteItems, insertItems: transition.insertItems, updateItems: transition.updateItems, scrollToItem: transition.scrollToItem, updateLayout: nil, itemTransition: .immediate, stationaryItems: transition.stationaryItems, updateFirstIndexInSectionOffset: transition.topOffsetWithinMonth, synchronousLoads: true), completion: completion)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func updateLayout(transition: ContainedViewLayoutTransition, updateSizeAndInsets: ListViewUpdateSizeAndInsets) {
|
|
self.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: updateSizeAndInsets.size, insets: updateSizeAndInsets.insets, preloadSize: 400.0, type: gridNodeLayoutForContainerLayout(size: updateSizeAndInsets.size)), transition: .immediate), itemTransition: .immediate, stationaryItems: .none,updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
|
|
|
if !self.dequeuedInitialTransitionOnLayout {
|
|
self.dequeuedInitialTransitionOnLayout = true
|
|
self.dequeueHistoryViewTransition()
|
|
}
|
|
|
|
}
|
|
|
|
public func disconnect() {
|
|
self.historyDisposable.set(nil)
|
|
}
|
|
}
|