mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Search filters improvements
This commit is contained in:
@@ -0,0 +1,539 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import Postbox
|
||||
import SyncCore
|
||||
import TelegramCore
|
||||
import AccountContext
|
||||
import ContextUI
|
||||
|
||||
protocol ChatListSearchPaneNode: ASDisplayNode {
|
||||
var isReady: Signal<Bool, NoError> { get }
|
||||
|
||||
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition)
|
||||
func scrollToTop() -> Bool
|
||||
func cancelPreviewGestures()
|
||||
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
||||
func addToTransitionSurface(view: UIView)
|
||||
func updateHiddenMedia()
|
||||
func updateSelectedMessages(animated: Bool)
|
||||
func previewViewAndActionAtLocation(_ location: CGPoint) -> (UIView, CGRect, Any)?
|
||||
var searchCurrentMessages: [Message]? { get }
|
||||
}
|
||||
|
||||
final class ChatListSearchPaneWrapper {
|
||||
let key: ChatListSearchPaneKey
|
||||
let node: ChatListSearchPaneNode
|
||||
var isAnimatingOut: Bool = false
|
||||
private var appliedParams: (CGSize, CGFloat, CGFloat, CGFloat, PresentationData)?
|
||||
|
||||
init(key: ChatListSearchPaneKey, node: ChatListSearchPaneNode) {
|
||||
self.key = key
|
||||
self.node = node
|
||||
}
|
||||
|
||||
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
|
||||
if let (currentSize, currentSideInset, currentBottomInset, visibleHeight, currentPresentationData) = self.appliedParams {
|
||||
if currentSize == size && currentSideInset == sideInset && currentBottomInset == bottomInset && currentPresentationData === presentationData {
|
||||
return
|
||||
}
|
||||
}
|
||||
self.appliedParams = (size, sideInset, bottomInset, visibleHeight, presentationData)
|
||||
self.node.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: synchronous, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
enum ChatListSearchPaneKey {
|
||||
case chats
|
||||
case media
|
||||
case links
|
||||
case files
|
||||
case music
|
||||
case voice
|
||||
}
|
||||
|
||||
private let availablePanes: [ChatListSearchPaneKey] = [.chats, .media, .links, .files, .music, .voice]
|
||||
|
||||
struct ChatListSearchPaneSpecifier: Equatable {
|
||||
var key: ChatListSearchPaneKey
|
||||
var title: String
|
||||
}
|
||||
|
||||
private func interpolateFrame(from fromValue: CGRect, to toValue: CGRect, t: CGFloat) -> CGRect {
|
||||
return CGRect(x: floorToScreenPixels(toValue.origin.x * t + fromValue.origin.x * (1.0 - t)), y: floorToScreenPixels(toValue.origin.y * t + fromValue.origin.y * (1.0 - t)), width: floorToScreenPixels(toValue.size.width * t + fromValue.size.width * (1.0 - t)), height: floorToScreenPixels(toValue.size.height * t + fromValue.size.height * (1.0 - t)))
|
||||
}
|
||||
|
||||
private final class ChatListSearchPendingPane {
|
||||
let pane: ChatListSearchPaneWrapper
|
||||
private var disposable: Disposable?
|
||||
var isReady: Bool = false
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
interaction: ChatListSearchInteraction,
|
||||
navigationController: NavigationController?,
|
||||
peersFilter: ChatListNodePeersFilter,
|
||||
searchQuery: Signal<String?, NoError>,
|
||||
searchOptions: Signal<ChatListSearchOptions?, NoError>,
|
||||
key: ChatListSearchPaneKey,
|
||||
hasBecomeReady: @escaping (ChatListSearchPaneKey) -> Void
|
||||
) {
|
||||
let paneNode: ChatListSearchPaneNode
|
||||
switch key {
|
||||
case .chats:
|
||||
paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: nil, peersFilter: peersFilter, searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController)
|
||||
case .media:
|
||||
paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: .photoOrVideo, peersFilter: [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController)
|
||||
case .files:
|
||||
paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: .file, peersFilter: [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController)
|
||||
case .links:
|
||||
paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: .webPage, peersFilter: [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController)
|
||||
case .voice:
|
||||
paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: .voiceOrInstantVideo, peersFilter: [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController)
|
||||
case .music:
|
||||
paneNode = ChatListSearchListPaneNode(context: context, interaction: interaction, tagMask: .music, peersFilter: [], searchQuery: searchQuery, searchOptions: searchOptions, navigationController: navigationController)
|
||||
}
|
||||
|
||||
self.pane = ChatListSearchPaneWrapper(key: key, node: paneNode)
|
||||
self.disposable = (paneNode.isReady
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
self?.isReady = true
|
||||
hasBecomeReady(key)
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.disposable?.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
final class ChatListSearchPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
private let context: AccountContext
|
||||
private let peersFilter: ChatListNodePeersFilter
|
||||
private let searchQuery: Signal<String?, NoError>
|
||||
private let searchOptions: Signal<ChatListSearchOptions?, NoError>
|
||||
private let navigationController: NavigationController?
|
||||
var interaction: ChatListSearchInteraction?
|
||||
|
||||
private let coveringBackgroundNode: ASDisplayNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
|
||||
let isReady = Promise<Bool>()
|
||||
var didSetIsReady = false
|
||||
|
||||
private var currentParams: (size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData)?
|
||||
|
||||
private(set) var currentPaneKey: ChatListSearchPaneKey?
|
||||
var pendingSwitchToPaneKey: ChatListSearchPaneKey?
|
||||
|
||||
var currentPane: ChatListSearchPaneWrapper? {
|
||||
if let currentPaneKey = self.currentPaneKey {
|
||||
return self.currentPanes[currentPaneKey]
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private var currentPanes: [ChatListSearchPaneKey: ChatListSearchPaneWrapper] = [:]
|
||||
private var pendingPanes: [ChatListSearchPaneKey: ChatListSearchPendingPane] = [:]
|
||||
|
||||
private var transitionFraction: CGFloat = 0.0
|
||||
|
||||
var openPeerContextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
|
||||
var currentPaneUpdated: ((ChatListSearchPaneKey?, CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||
var requestExpandTabs: (() -> Bool)?
|
||||
|
||||
private var currentAvailablePanes: [ChatListSearchPaneKey]?
|
||||
|
||||
init(context: AccountContext, peersFilter: ChatListNodePeersFilter, searchQuery: Signal<String?, NoError>, searchOptions: Signal<ChatListSearchOptions?, NoError>, navigationController: NavigationController?) {
|
||||
self.context = context
|
||||
self.peersFilter = peersFilter
|
||||
self.searchQuery = searchQuery
|
||||
self.searchOptions = searchOptions
|
||||
self.navigationController = navigationController
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
self.coveringBackgroundNode = ASDisplayNode()
|
||||
self.coveringBackgroundNode.isLayerBacked = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.separatorNode)
|
||||
self.addSubnode(self.coveringBackgroundNode)
|
||||
}
|
||||
|
||||
func requestSelectPane(_ key: ChatListSearchPaneKey) {
|
||||
if self.currentPaneKey == key {
|
||||
if let requestExpandTabs = self.requestExpandTabs, requestExpandTabs() {
|
||||
} else {
|
||||
let _ = self.currentPane?.node.scrollToTop()
|
||||
}
|
||||
return
|
||||
}
|
||||
if self.currentPanes[key] != nil {
|
||||
self.currentPaneKey = key
|
||||
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData) = self.currentParams {
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
} else if self.pendingSwitchToPaneKey != key {
|
||||
self.pendingSwitchToPaneKey = key
|
||||
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData) = self.currentParams {
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] point in
|
||||
guard let strongSelf = self, let currentPaneKey = strongSelf.currentPaneKey, let index = availablePanes.firstIndex(of: currentPaneKey) else {
|
||||
return []
|
||||
}
|
||||
if index == 0 {
|
||||
return .left
|
||||
}
|
||||
return [.left, .right]
|
||||
})
|
||||
panRecognizer.delegate = self
|
||||
panRecognizer.delaysTouchesBegan = false
|
||||
panRecognizer.cancelsTouchesInView = true
|
||||
self.view.addGestureRecognizer(panRecognizer)
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if let _ = otherGestureRecognizer as? InteractiveTransitionGestureRecognizer {
|
||||
return false
|
||||
}
|
||||
if let _ = otherGestureRecognizer as? UIPanGestureRecognizer {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
func cancelContextGestures(view: UIView) {
|
||||
if let gestureRecognizers = view.gestureRecognizers {
|
||||
for gesture in gestureRecognizers {
|
||||
if let gesture = gesture as? ContextGesture {
|
||||
gesture.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
for subview in view.subviews {
|
||||
cancelContextGestures(view: subview)
|
||||
}
|
||||
}
|
||||
|
||||
cancelContextGestures(view: self.view)
|
||||
case .changed:
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData) = self.currentParams, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) {
|
||||
let translation = recognizer.translation(in: self.view)
|
||||
var transitionFraction = translation.x / size.width
|
||||
if currentIndex <= 0 {
|
||||
transitionFraction = min(0.0, transitionFraction)
|
||||
}
|
||||
if currentIndex >= availablePanes.count - 1 {
|
||||
transitionFraction = max(0.0, transitionFraction)
|
||||
}
|
||||
self.transitionFraction = transitionFraction
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, transition: .immediate)
|
||||
}
|
||||
case .cancelled, .ended:
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData) = self.currentParams, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) {
|
||||
let translation = recognizer.translation(in: self.view)
|
||||
let velocity = recognizer.velocity(in: self.view)
|
||||
var directionIsToRight: Bool?
|
||||
if abs(velocity.x) > 10.0 {
|
||||
directionIsToRight = velocity.x < 0.0
|
||||
} else {
|
||||
if abs(translation.x) > size.width / 2.0 {
|
||||
directionIsToRight = translation.x > size.width / 2.0
|
||||
}
|
||||
}
|
||||
var updated = false
|
||||
if let directionIsToRight = directionIsToRight {
|
||||
var updatedIndex = currentIndex
|
||||
if directionIsToRight {
|
||||
updatedIndex = min(updatedIndex + 1, availablePanes.count - 1)
|
||||
} else {
|
||||
updatedIndex = max(updatedIndex - 1, 0)
|
||||
}
|
||||
let switchToKey = availablePanes[updatedIndex]
|
||||
if switchToKey != self.currentPaneKey && self.currentPanes[switchToKey] != nil{
|
||||
self.currentPaneKey = switchToKey
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
self.transitionFraction = 0.0
|
||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, transition: .animated(duration: 0.35, curve: .spring))
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func scrollToTop() -> Bool {
|
||||
if let currentPane = self.currentPane {
|
||||
return currentPane.node.scrollToTop()
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func updateHiddenMedia() {
|
||||
self.currentPane?.node.updateHiddenMedia()
|
||||
}
|
||||
|
||||
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
||||
return self.currentPane?.node.transitionNodeForGallery(messageId: messageId, media: media)
|
||||
}
|
||||
|
||||
func updateSelectedMessageIds(_ selectedMessageIds: Set<MessageId>?, animated: Bool) {
|
||||
for (_, pane) in self.currentPanes {
|
||||
pane.node.updateSelectedMessages(animated: animated)
|
||||
}
|
||||
for (_, pane) in self.pendingPanes {
|
||||
pane.pane.node.updateSelectedMessages(animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, transition: ContainedViewLayoutTransition) {
|
||||
let previousAvailablePanes = self.currentAvailablePanes ?? []
|
||||
self.currentAvailablePanes = availablePanes
|
||||
|
||||
if let currentPaneKey = self.currentPaneKey, !availablePanes.contains(currentPaneKey) {
|
||||
var nextCandidatePaneKey: ChatListSearchPaneKey?
|
||||
if let index = previousAvailablePanes.firstIndex(of: currentPaneKey), index != 0 {
|
||||
for i in (0 ... index - 1).reversed() {
|
||||
if availablePanes.contains(previousAvailablePanes[i]) {
|
||||
nextCandidatePaneKey = previousAvailablePanes[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
if nextCandidatePaneKey == nil {
|
||||
nextCandidatePaneKey = availablePanes.first
|
||||
}
|
||||
|
||||
if let nextCandidatePaneKey = nextCandidatePaneKey {
|
||||
self.pendingSwitchToPaneKey = nextCandidatePaneKey
|
||||
} else {
|
||||
self.currentPaneKey = nil
|
||||
self.pendingSwitchToPaneKey = nil
|
||||
}
|
||||
} else if self.currentPaneKey == nil {
|
||||
self.pendingSwitchToPaneKey = availablePanes.first
|
||||
}
|
||||
|
||||
let currentIndex: Int?
|
||||
if let currentPaneKey = self.currentPaneKey {
|
||||
currentIndex = availablePanes.firstIndex(of: currentPaneKey)
|
||||
} else {
|
||||
currentIndex = nil
|
||||
}
|
||||
|
||||
self.currentParams = (size, sideInset, bottomInset, visibleHeight, presentationData)
|
||||
|
||||
transition.updateAlpha(node: self.coveringBackgroundNode, alpha: 0.0)
|
||||
|
||||
self.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor
|
||||
self.coveringBackgroundNode.backgroundColor = presentationData.theme.rootController.navigationBar.backgroundColor
|
||||
self.separatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
|
||||
let tabsHeight: CGFloat = 48.0
|
||||
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: size.width, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: self.coveringBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: size.width, height: tabsHeight + UIScreenPixel)))
|
||||
|
||||
let paneFrame = CGRect(origin: CGPoint(x: 0.0, y: tabsHeight), size: CGSize(width: size.width, height: size.height - tabsHeight))
|
||||
|
||||
var visiblePaneIndices: [Int] = []
|
||||
var requiredPendingKeys: [ChatListSearchPaneKey] = []
|
||||
if let currentIndex = currentIndex {
|
||||
if currentIndex != 0 {
|
||||
visiblePaneIndices.append(currentIndex - 1)
|
||||
}
|
||||
visiblePaneIndices.append(currentIndex)
|
||||
if currentIndex != availablePanes.count - 1 {
|
||||
visiblePaneIndices.append(currentIndex + 1)
|
||||
}
|
||||
|
||||
for index in visiblePaneIndices {
|
||||
let key = availablePanes[index]
|
||||
if self.currentPanes[key] == nil && self.pendingPanes[key] == nil {
|
||||
requiredPendingKeys.append(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
if let pendingSwitchToPaneKey = self.pendingSwitchToPaneKey {
|
||||
if self.currentPanes[pendingSwitchToPaneKey] == nil && self.pendingPanes[pendingSwitchToPaneKey] == nil {
|
||||
if !requiredPendingKeys.contains(pendingSwitchToPaneKey) {
|
||||
requiredPendingKeys.append(pendingSwitchToPaneKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for key in requiredPendingKeys {
|
||||
if self.pendingPanes[key] == nil {
|
||||
var leftScope = false
|
||||
let pane = ChatListSearchPendingPane(
|
||||
context: self.context,
|
||||
interaction: self.interaction!,
|
||||
navigationController: self.navigationController,
|
||||
peersFilter: self.peersFilter,
|
||||
searchQuery: self.searchQuery,
|
||||
searchOptions: self.searchOptions,
|
||||
key: key,
|
||||
hasBecomeReady: { [weak self] key in
|
||||
let apply: () -> Void = {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData) = strongSelf.currentParams {
|
||||
var transition: ContainedViewLayoutTransition = .immediate
|
||||
if strongSelf.pendingSwitchToPaneKey == key && strongSelf.currentPaneKey != nil {
|
||||
transition = .animated(duration: 0.4, curve: .spring)
|
||||
}
|
||||
strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, transition: transition)
|
||||
}
|
||||
}
|
||||
if leftScope {
|
||||
apply()
|
||||
}
|
||||
}
|
||||
)
|
||||
self.pendingPanes[key] = pane
|
||||
pane.pane.node.frame = paneFrame
|
||||
pane.pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: .immediate)
|
||||
leftScope = true
|
||||
}
|
||||
}
|
||||
|
||||
for (key, pane) in self.pendingPanes {
|
||||
pane.pane.node.frame = paneFrame
|
||||
pane.pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: self.currentPaneKey == nil, transition: .immediate)
|
||||
|
||||
if pane.isReady {
|
||||
self.pendingPanes.removeValue(forKey: key)
|
||||
self.currentPanes[key] = pane.pane
|
||||
}
|
||||
}
|
||||
|
||||
var paneDefaultTransition = transition
|
||||
var previousPaneKey: ChatListSearchPaneKey?
|
||||
var paneSwitchAnimationOffset: CGFloat = 0.0
|
||||
|
||||
var updatedCurrentIndex = currentIndex
|
||||
var animatePaneTransitionOffset: CGFloat?
|
||||
if let pendingSwitchToPaneKey = self.pendingSwitchToPaneKey, let pane = self.currentPanes[pendingSwitchToPaneKey] {
|
||||
self.pendingSwitchToPaneKey = nil
|
||||
previousPaneKey = self.currentPaneKey
|
||||
self.currentPaneKey = pendingSwitchToPaneKey
|
||||
updatedCurrentIndex = availablePanes.firstIndex(of: pendingSwitchToPaneKey)
|
||||
if let previousPaneKey = previousPaneKey, let previousIndex = availablePanes.firstIndex(of: previousPaneKey), let updatedCurrentIndex = updatedCurrentIndex {
|
||||
if updatedCurrentIndex < previousIndex {
|
||||
paneSwitchAnimationOffset = -size.width
|
||||
} else {
|
||||
paneSwitchAnimationOffset = size.width
|
||||
}
|
||||
}
|
||||
|
||||
paneDefaultTransition = .immediate
|
||||
}
|
||||
|
||||
for (key, pane) in self.currentPanes {
|
||||
if let index = availablePanes.firstIndex(of: key), let updatedCurrentIndex = updatedCurrentIndex {
|
||||
var paneWasAdded = false
|
||||
if pane.node.supernode == nil {
|
||||
self.addSubnode(pane.node)
|
||||
paneWasAdded = true
|
||||
}
|
||||
let indexOffset = CGFloat(index - updatedCurrentIndex)
|
||||
|
||||
let paneTransition: ContainedViewLayoutTransition = paneWasAdded ? .immediate : paneDefaultTransition
|
||||
let adjustedFrame = paneFrame.offsetBy(dx: size.width * self.transitionFraction + indexOffset * size.width, dy: 0.0)
|
||||
|
||||
let paneCompletion: () -> Void = { [weak self, weak pane] in
|
||||
guard let strongSelf = self, let pane = pane else {
|
||||
return
|
||||
}
|
||||
pane.isAnimatingOut = false
|
||||
if let (size, sideInset, bottomInset, visibleHeight, presentationData) = strongSelf.currentParams {
|
||||
if let currentPaneKey = strongSelf.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey), let paneIndex = availablePanes.firstIndex(of: key), abs(paneIndex - currentIndex) <= 1 {
|
||||
} else {
|
||||
if let pane = strongSelf.currentPanes.removeValue(forKey: key) {
|
||||
pane.node.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let previousPaneKey = previousPaneKey, key == previousPaneKey {
|
||||
pane.node.frame = adjustedFrame
|
||||
let isAnimatingOut = pane.isAnimatingOut
|
||||
pane.isAnimatingOut = true
|
||||
transition.animateFrame(node: pane.node, from: paneFrame, to: paneFrame.offsetBy(dx: -paneSwitchAnimationOffset, dy: 0.0), completion: isAnimatingOut ? nil : { _ in
|
||||
paneCompletion()
|
||||
})
|
||||
} else if let previousPaneKey = previousPaneKey, key == self.currentPaneKey {
|
||||
pane.node.frame = adjustedFrame
|
||||
let isAnimatingOut = pane.isAnimatingOut
|
||||
pane.isAnimatingOut = true
|
||||
transition.animatePositionAdditive(node: pane.node, offset: CGPoint(x: paneSwitchAnimationOffset, y: 0.0), completion: isAnimatingOut ? nil : {
|
||||
paneCompletion()
|
||||
})
|
||||
} else {
|
||||
let isAnimatingOut = pane.isAnimatingOut
|
||||
pane.isAnimatingOut = true
|
||||
paneTransition.updateFrame(node: pane.node, frame: adjustedFrame, completion: isAnimatingOut ? nil : { _ in
|
||||
paneCompletion()
|
||||
})
|
||||
}
|
||||
pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: paneWasAdded, transition: paneTransition)
|
||||
}
|
||||
}
|
||||
|
||||
for (_, pane) in self.pendingPanes {
|
||||
let paneTransition: ContainedViewLayoutTransition = .immediate
|
||||
paneTransition.updateFrame(node: pane.pane.node, frame: paneFrame)
|
||||
pane.pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: paneTransition)
|
||||
}
|
||||
if !self.didSetIsReady {
|
||||
if let currentPaneKey = self.currentPaneKey, let currentPane = self.currentPanes[currentPaneKey] {
|
||||
self.didSetIsReady = true
|
||||
self.isReady.set(currentPane.node.isReady)
|
||||
} else if self.pendingSwitchToPaneKey == nil {
|
||||
self.didSetIsReady = true
|
||||
self.isReady.set(.single(true))
|
||||
}
|
||||
}
|
||||
|
||||
self.currentPaneUpdated?(self.currentPaneKey, self.transitionFraction, transition)
|
||||
}
|
||||
|
||||
func allCurrentMessages() -> [MessageId: Message] {
|
||||
var allMessages: [MessageId: Message] = [:]
|
||||
for (_, pane) in self.currentPanes {
|
||||
if let messages = pane.node.searchCurrentMessages {
|
||||
for message in messages {
|
||||
allMessages[message.id] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
return allMessages
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user