Swiftgram/submodules/TelegramUI/Sources/PeerMediaCollectionControllerNode.swift
Ilya Laktyushin 03a84fda99 Update API
2020-06-16 11:43:15 +03:00

547 lines
31 KiB
Swift

import Foundation
import UIKit
import AsyncDisplayKit
import Postbox
import SwiftSignalKit
import Display
import TelegramCore
import SyncCore
import TelegramPresentationData
import AccountContext
import SearchBarNode
import SearchUI
import ChatListSearchItemNode
struct PeerMediaCollectionMessageForGallery {
let message: Message
let fromSearchResults: Bool
}
private func historyNodeImplForMode(_ mode: PeerMediaCollectionMode, context: AccountContext, theme: PresentationTheme, peerId: PeerId, messageId: MessageId?, controllerInteraction: ChatControllerInteraction, selectedMessages: Signal<Set<MessageId>?, NoError>) -> ChatHistoryNode & ASDisplayNode {
switch mode {
case .photoOrVideo:
let node = ChatHistoryGridNode(context: context, peerId: peerId, messageId: messageId, tagMask: .photoOrVideo, controllerInteraction: controllerInteraction)
node.showVerticalScrollIndicator = true
if theme.list.plainBackgroundColor.argb == 0xffffffff {
node.indicatorStyle = .default
} else {
node.indicatorStyle = .white
}
return node
case .file:
let node = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), tagMask: .file, subject: messageId.flatMap { .message($0) }, controllerInteraction: controllerInteraction, selectedMessages: selectedMessages, mode: .list(search: true, reversed: false, displayHeaders: .all))
node.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
node.didEndScrolling = { [weak node] in
guard let node = node else {
return
}
fixSearchableListNodeScrolling(node)
}
node.preloadPages = true
return node
case .music:
let node = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), tagMask: .music, subject: messageId.flatMap { .message($0) }, controllerInteraction: controllerInteraction, selectedMessages: selectedMessages, mode: .list(search: true, reversed: false, displayHeaders: .all))
node.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
node.didEndScrolling = { [weak node] in
guard let node = node else {
return
}
fixSearchableListNodeScrolling(node)
}
node.preloadPages = true
return node
case .webpage:
let node = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), tagMask: .webPage, subject: messageId.flatMap { .message($0) }, controllerInteraction: controllerInteraction, selectedMessages: selectedMessages, mode: .list(search: true, reversed: false, displayHeaders: .all))
node.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
node.didEndScrolling = { [weak node] in
guard let node = node else {
return
}
fixSearchableListNodeScrolling(node)
}
node.preloadPages = true
return node
}
}
private func updateLoadNodeState(_ node: PeerMediaCollectionEmptyNode, _ loadState: ChatHistoryNodeLoadState?) {
if let loadState = loadState {
switch loadState {
case .messages:
node.isHidden = true
node.isLoading = false
case .empty:
node.isHidden = false
node.isLoading = false
case .loading:
node.isHidden = false
node.isLoading = true
}
} else {
node.isHidden = false
node.isLoading = true
}
}
private func tagMaskForMode(_ mode: PeerMediaCollectionMode) -> MessageTags {
switch mode {
case .photoOrVideo:
return .photoOrVideo
case .file:
return .file
case .music:
return .music
case .webpage:
return .webPage
}
}
class PeerMediaCollectionControllerNode: ASDisplayNode {
private let context: AccountContext
private let peerId: PeerId
private let controllerInteraction: ChatControllerInteraction
private let interfaceInteraction: ChatPanelInterfaceInteraction
private let navigationBar: NavigationBar?
private let sectionsNode: PeerMediaCollectionSectionsNode
private(set) var historyNode: ChatHistoryNode & ASDisplayNode
private var historyEmptyNode: PeerMediaCollectionEmptyNode
private(set) var searchDisplayController: SearchDisplayController?
private let candidateHistoryNodeReadyDisposable = MetaDisposable()
private var candidateHistoryNode: (ASDisplayNode, PeerMediaCollectionMode)?
private var containerLayout: (ContainerViewLayout, CGFloat)?
var requestLayout: (ContainedViewLayoutTransition) -> Void = { _ in }
var requestUpdateMediaCollectionInterfaceState: (Bool, (PeerMediaCollectionInterfaceState) -> PeerMediaCollectionInterfaceState) -> Void = { _, _ in }
let requestDeactivateSearch: () -> Void
private var mediaCollectionInterfaceState: PeerMediaCollectionInterfaceState
private let selectedMessagesPromise = Promise<Set<MessageId>?>(nil)
var selectedMessages: Set<MessageId>? {
didSet {
if self.selectedMessages != oldValue {
self.selectedMessagesPromise.set(.single(self.selectedMessages))
}
}
}
private var selectionPanel: ChatMessageSelectionInputPanelNode?
private var selectionPanelSeparatorNode: ASDisplayNode?
private var selectionPanelBackgroundNode: ASDisplayNode?
private var chatPresentationInterfaceState: ChatPresentationInterfaceState
private var presentationData: PresentationData
init(context: AccountContext, peerId: PeerId, messageId: MessageId?, controllerInteraction: ChatControllerInteraction, interfaceInteraction: ChatPanelInterfaceInteraction, navigationBar: NavigationBar?, requestDeactivateSearch: @escaping () -> Void) {
self.context = context
self.peerId = peerId
self.controllerInteraction = controllerInteraction
self.interfaceInteraction = interfaceInteraction
self.navigationBar = navigationBar
self.requestDeactivateSearch = requestDeactivateSearch
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.mediaCollectionInterfaceState = PeerMediaCollectionInterfaceState(theme: self.presentationData.theme, strings: self.presentationData.strings)
self.sectionsNode = PeerMediaCollectionSectionsNode(theme: self.presentationData.theme, strings: self.presentationData.strings)
self.historyNode = historyNodeImplForMode(self.mediaCollectionInterfaceState.mode, context: context, theme: self.presentationData.theme, peerId: peerId, messageId: messageId, controllerInteraction: controllerInteraction, selectedMessages: self.selectedMessagesPromise.get())
self.historyEmptyNode = PeerMediaCollectionEmptyNode(mode: self.mediaCollectionInterfaceState.mode, theme: self.presentationData.theme, strings: self.presentationData.strings)
self.historyEmptyNode.isHidden = true
self.chatPresentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: .defaultValue, fontSize: self.presentationData.listsFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(self.peerId), isScheduledMessages: false, peerNearbyData: nil)
super.init()
self.setViewBlock({
return UITracingLayerView()
})
self.historyNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
self.addSubnode(self.historyNode)
self.addSubnode(self.historyEmptyNode)
if let navigationBar = navigationBar {
self.addSubnode(navigationBar)
}
if let navigationBar = self.navigationBar {
self.insertSubnode(self.sectionsNode, aboveSubnode: navigationBar)
} else {
self.addSubnode(self.sectionsNode)
}
self.sectionsNode.indexUpdated = { [weak self] index in
if let strongSelf = self {
let mode: PeerMediaCollectionMode
switch index {
case 0:
mode = .photoOrVideo
case 1:
mode = .file
case 2:
mode = .webpage
case 3:
mode = .music
default:
mode = .photoOrVideo
}
strongSelf.requestUpdateMediaCollectionInterfaceState(true, { $0.withMode(mode) })
}
}
updateLoadNodeState(self.historyEmptyNode, self.historyNode.loadState)
self.historyNode.setLoadStateUpdated { [weak self] loadState, _ in
if let strongSelf = self {
updateLoadNodeState(strongSelf.historyEmptyNode, loadState)
}
}
}
deinit {
self.candidateHistoryNodeReadyDisposable.dispose()
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeightAndPrimaryHeight: (CGFloat, CGFloat), transition: ContainedViewLayoutTransition, listViewTransaction: (ListViewUpdateSizeAndInsets) -> Void) {
let navigationBarHeight = navigationBarHeightAndPrimaryHeight.0
let primaryNavigationBarHeight = navigationBarHeightAndPrimaryHeight.1
let navigationBarHeightDelta = (navigationBarHeight - primaryNavigationBarHeight)
self.containerLayout = (layout, navigationBarHeight)
var vanillaInsets = layout.insets(options: [])
vanillaInsets.top += navigationBarHeight
var additionalInset: CGFloat = 0.0
if (navigationBarHeight - (layout.statusBarHeight ?? 0.0)).isLessThanOrEqualTo(44.0) {
} else {
additionalInset += 10.0
}
if let searchDisplayController = self.searchDisplayController {
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
if !searchDisplayController.isDeactivating {
vanillaInsets.top += (layout.statusBarHeight ?? 0.0) - navigationBarHeightDelta
}
}
let sectionsHeight = self.sectionsNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalInset: additionalInset, transition: transition, interfaceState: self.mediaCollectionInterfaceState)
var sectionOffset: CGFloat = 0.0
if primaryNavigationBarHeight.isZero {
sectionOffset = -sectionsHeight - navigationBarHeightDelta
} else {
//layout.statusBarHeight ?? 0.0
//if navigationBarHeightAndPrimaryHeight.0 > navigationBarHeightAndPrimaryHeight.1 {
// sectionOffset += 1.0
//}//
}
transition.updateFrame(node: self.sectionsNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight + sectionOffset), size: CGSize(width: layout.size.width, height: sectionsHeight)))
var insets = vanillaInsets
if !primaryNavigationBarHeight.isZero {
insets.top += sectionsHeight
}
if let inputHeight = layout.inputHeight {
insets.bottom += inputHeight
}
if let selectionState = self.mediaCollectionInterfaceState.selectionState {
let interfaceState = self.chatPresentationInterfaceState.updatedPeer({ _ in self.mediaCollectionInterfaceState.peer.flatMap(RenderedPeer.init) })
if let selectionPanel = self.selectionPanel {
selectionPanel.selectedMessages = selectionState.selectedIds
let panelHeight = selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: 0.0, isSecondary: false, transition: transition, interfaceState: interfaceState, metrics: layout.metrics)
transition.updateFrame(node: selectionPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: panelHeight)))
if let selectionPanelSeparatorNode = self.selectionPanelSeparatorNode {
transition.updateFrame(node: selectionPanelSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
}
if let selectionPanelBackgroundNode = self.selectionPanelBackgroundNode {
transition.updateFrame(node: selectionPanelBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: insets.bottom + panelHeight)))
}
} else {
let selectionPanelBackgroundNode = ASDisplayNode()
selectionPanelBackgroundNode.isLayerBacked = true
selectionPanelBackgroundNode.backgroundColor = self.mediaCollectionInterfaceState.theme.chat.inputPanel.panelBackgroundColor
self.addSubnode(selectionPanelBackgroundNode)
self.selectionPanelBackgroundNode = selectionPanelBackgroundNode
let selectionPanel = ChatMessageSelectionInputPanelNode(theme: self.chatPresentationInterfaceState.theme, strings: self.chatPresentationInterfaceState.strings, peerMedia: true)
selectionPanel.context = self.context
selectionPanel.backgroundColor = self.presentationData.theme.chat.inputPanel.panelBackgroundColor
selectionPanel.interfaceInteraction = self.interfaceInteraction
selectionPanel.selectedMessages = selectionState.selectedIds
let panelHeight = selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, maxHeight: 0.0, isSecondary: false, transition: .immediate, interfaceState: interfaceState, metrics: layout.metrics)
self.selectionPanel = selectionPanel
self.addSubnode(selectionPanel)
let selectionPanelSeparatorNode = ASDisplayNode()
selectionPanelSeparatorNode.isLayerBacked = true
selectionPanelSeparatorNode.backgroundColor = self.mediaCollectionInterfaceState.theme.chat.inputPanel.panelSeparatorColor
self.addSubnode(selectionPanelSeparatorNode)
self.selectionPanelSeparatorNode = selectionPanelSeparatorNode
selectionPanel.frame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height), size: CGSize(width: layout.size.width, height: panelHeight))
selectionPanelBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height), size: CGSize(width: layout.size.width, height: 0.0))
selectionPanelSeparatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height), size: CGSize(width: layout.size.width, height: UIScreenPixel))
transition.updateFrame(node: selectionPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: panelHeight)))
transition.updateFrame(node: selectionPanelBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: insets.bottom + panelHeight)))
transition.updateFrame(node: selectionPanelSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - panelHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
}
} else if let selectionPanel = self.selectionPanel {
self.selectionPanel = nil
transition.updateFrame(node: selectionPanel, frame: selectionPanel.frame.offsetBy(dx: 0.0, dy: selectionPanel.bounds.size.height + insets.bottom), completion: { [weak selectionPanel] _ in
selectionPanel?.removeFromSupernode()
})
if let selectionPanelSeparatorNode = self.selectionPanelSeparatorNode {
transition.updateFrame(node: selectionPanelSeparatorNode, frame: selectionPanelSeparatorNode.frame.offsetBy(dx: 0.0, dy: selectionPanel.bounds.size.height + insets.bottom), completion: { [weak selectionPanelSeparatorNode] _ in
selectionPanelSeparatorNode?.removeFromSupernode()
})
}
if let selectionPanelBackgroundNode = self.selectionPanelBackgroundNode {
transition.updateFrame(node: selectionPanelBackgroundNode, frame: selectionPanelBackgroundNode.frame.offsetBy(dx: 0.0, dy: selectionPanel.bounds.size.height + insets.bottom), completion: { [weak selectionPanelSeparatorNode] _ in
selectionPanelSeparatorNode?.removeFromSupernode()
})
}
}
let previousBounds = self.historyNode.bounds
self.historyNode.bounds = CGRect(x: previousBounds.origin.x, y: previousBounds.origin.y, width: layout.size.width, height: layout.size.height)
self.historyNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)
self.historyNode.backgroundColor = self.mediaCollectionInterfaceState.theme.list.plainBackgroundColor
self.backgroundColor = self.mediaCollectionInterfaceState.theme.list.plainBackgroundColor
self.historyEmptyNode.updateLayout(size: layout.size, insets: vanillaInsets, transition: transition, interfaceState: mediaCollectionInterfaceState)
transition.updateFrame(node: self.historyEmptyNode, frame: CGRect(origin: CGPoint(), size: layout.size))
var additionalBottomInset: CGFloat = 0.0
if let selectionPanel = self.selectionPanel {
additionalBottomInset = selectionPanel.bounds.size.height
}
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
listViewTransaction(ListViewUpdateSizeAndInsets(size: layout.size, insets: UIEdgeInsets(top: insets.top, left:
insets.right + layout.safeInsets.right, bottom: insets.bottom + additionalBottomInset, right: insets.left + layout.safeInsets.right), duration: duration, curve: curve))
if let (candidateHistoryNode, _) = self.candidateHistoryNode {
let previousBounds = candidateHistoryNode.bounds
candidateHistoryNode.bounds = CGRect(x: previousBounds.origin.x, y: previousBounds.origin.y, width: layout.size.width, height: layout.size.height)
candidateHistoryNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)
(candidateHistoryNode as! ChatHistoryNode).updateLayout(transition: transition, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: UIEdgeInsets(top: insets.top, left:
insets.right + layout.safeInsets.right, bottom: insets.bottom + additionalBottomInset, right: insets.left + layout.safeInsets.left), duration: duration, curve: curve))
}
}
func activateSearch() {
guard let (containerLayout, navigationBarHeight) = self.containerLayout, let navigationBar = self.navigationBar else {
return
}
var maybePlaceholderNode: SearchBarPlaceholderNode?
if let listNode = historyNode as? ListView {
listNode.forEachItemNode { node in
if let node = node as? ChatListSearchItemNode {
maybePlaceholderNode = node.searchBarNode
}
}
}
if let _ = self.searchDisplayController {
return
}
if let placeholderNode = maybePlaceholderNode {
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, contentNode: ChatHistorySearchContainerNode(context: self.context, peerId: self.peerId, tagMask: tagMaskForMode(self.mediaCollectionInterfaceState.mode), interfaceInteraction: self.controllerInteraction), cancel: { [weak self] in
self?.requestDeactivateSearch()
})
self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate)
self.searchDisplayController?.activate(insertSubnode: { [weak self] subnode, isSearchBar in
if let strongSelf = self {
strongSelf.insertSubnode(subnode, belowSubnode: navigationBar)
}
}, placeholder: placeholderNode)
}
}
func deactivateSearch() {
if let searchDisplayController = self.searchDisplayController {
self.searchDisplayController = nil
var maybePlaceholderNode: SearchBarPlaceholderNode?
if let listNode = self.historyNode as? ListView {
listNode.forEachItemNode { node in
if let node = node as? ChatListSearchItemNode {
maybePlaceholderNode = node.searchBarNode
}
}
}
searchDisplayController.deactivate(placeholder: maybePlaceholderNode)
}
}
func updateMediaCollectionInterfaceState(_ mediaCollectionInterfaceState: PeerMediaCollectionInterfaceState, animated: Bool) {
if self.mediaCollectionInterfaceState != mediaCollectionInterfaceState {
if self.mediaCollectionInterfaceState.mode != mediaCollectionInterfaceState.mode {
let previousMode = self.mediaCollectionInterfaceState.mode
if let containerLayout = self.containerLayout, self.candidateHistoryNode == nil || self.candidateHistoryNode!.1 != mediaCollectionInterfaceState.mode {
let node = historyNodeImplForMode(mediaCollectionInterfaceState.mode, context: self.context, theme: self.presentationData.theme, peerId: self.peerId, messageId: nil, controllerInteraction: self.controllerInteraction, selectedMessages: self.selectedMessagesPromise.get())
node.backgroundColor = mediaCollectionInterfaceState.theme.list.plainBackgroundColor
self.candidateHistoryNode = (node, mediaCollectionInterfaceState.mode)
var vanillaInsets = containerLayout.0.insets(options: [])
vanillaInsets.top += containerLayout.1
if let searchDisplayController = self.searchDisplayController {
if !searchDisplayController.isDeactivating {
vanillaInsets.top += containerLayout.0.statusBarHeight ?? 0.0
}
}
var insets = vanillaInsets
if !containerLayout.1.isZero {
insets.top += self.sectionsNode.bounds.size.height
}
if let inputHeight = containerLayout.0.inputHeight {
insets.bottom += inputHeight
}
let previousBounds = node.bounds
node.bounds = CGRect(x: previousBounds.origin.x, y: previousBounds.origin.y, width: containerLayout.0.size.width, height: containerLayout.0.size.height)
node.position = CGPoint(x: containerLayout.0.size.width / 2.0, y: containerLayout.0.size.height / 2.0)
var additionalBottomInset: CGFloat = 0.0
if let selectionPanel = self.selectionPanel {
additionalBottomInset = selectionPanel.bounds.size.height
}
node.updateLayout(transition: .immediate, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: containerLayout.0.size, insets: UIEdgeInsets(top: insets.top, left: insets.right + containerLayout.0.safeInsets.right, bottom: insets.bottom + additionalBottomInset, right: insets.left + containerLayout.0.safeInsets.left), duration: 0.0, curve: .Default(duration: nil)))
let historyEmptyNode = PeerMediaCollectionEmptyNode(mode: mediaCollectionInterfaceState.mode, theme: self.mediaCollectionInterfaceState.theme, strings: self.mediaCollectionInterfaceState.strings)
historyEmptyNode.isHidden = true
historyEmptyNode.updateLayout(size: containerLayout.0.size, insets: vanillaInsets, transition: .immediate, interfaceState: self.mediaCollectionInterfaceState)
historyEmptyNode.frame = CGRect(origin: CGPoint(), size: containerLayout.0.size)
self.candidateHistoryNodeReadyDisposable.set((node.historyState.get()
|> deliverOnMainQueue).start(next: { [weak self, weak node] _ in
if let strongSelf = self, let strongNode = node, strongNode == strongSelf.candidateHistoryNode?.0 {
strongSelf.candidateHistoryNode = nil
strongSelf.insertSubnode(strongNode, belowSubnode: strongSelf.historyNode)
strongSelf.insertSubnode(historyEmptyNode, aboveSubnode: strongNode)
let previousNode = strongSelf.historyNode
let previousEmptyNode = strongSelf.historyEmptyNode
strongSelf.historyNode = strongNode
strongSelf.historyEmptyNode = historyEmptyNode
updateLoadNodeState(strongSelf.historyEmptyNode, strongSelf.historyNode.loadState)
strongSelf.historyNode.setLoadStateUpdated { loadState, _ in
if let strongSelf = self {
updateLoadNodeState(strongSelf.historyEmptyNode, loadState)
}
}
let directionMultiplier: CGFloat
if previousMode.rawValue < mediaCollectionInterfaceState.mode.rawValue {
directionMultiplier = 1.0
} else {
directionMultiplier = -1.0
}
previousNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -directionMultiplier * strongSelf.bounds.width, y: 0.0), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { [weak previousNode] _ in
previousNode?.removeFromSupernode()
})
previousEmptyNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -directionMultiplier * strongSelf.bounds.width, y: 0.0), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { [weak previousEmptyNode] _ in
previousEmptyNode?.removeFromSupernode()
})
strongSelf.historyNode.layer.animatePosition(from: CGPoint(x: directionMultiplier * strongSelf.bounds.width, y: 0.0), to: CGPoint(), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
strongSelf.historyEmptyNode.layer.animatePosition(from: CGPoint(x: directionMultiplier * strongSelf.bounds.width, y: 0.0), to: CGPoint(), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
}
}))
}
}
self.mediaCollectionInterfaceState = mediaCollectionInterfaceState
self.requestLayout(animated ? .animated(duration: 0.4, curve: .spring) : .immediate)
}
}
func updateHiddenMedia() {
self.historyNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ChatMessageItemView {
itemNode.updateHiddenMedia()
} else if let itemNode = itemNode as? ListMessageNode {
itemNode.updateHiddenMedia()
} else if let itemNode = itemNode as? GridMessageItemNode {
itemNode.updateHiddenMedia()
}
}
if let searchContentNode = self.searchDisplayController?.contentNode as? ChatHistorySearchContainerNode {
searchContentNode.updateHiddenMedia()
}
}
func messageForGallery(_ id: MessageId) -> PeerMediaCollectionMessageForGallery? {
if let message = self.historyNode.messageInCurrentHistoryView(id) {
return PeerMediaCollectionMessageForGallery(message: message, fromSearchResults: false)
}
if let searchContentNode = self.searchDisplayController?.contentNode as? ChatHistorySearchContainerNode {
if let message = searchContentNode.messageForGallery(id) {
return PeerMediaCollectionMessageForGallery(message: message, fromSearchResults: true)
}
}
return nil
}
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
if let searchContentNode = self.searchDisplayController?.contentNode as? ChatHistorySearchContainerNode {
if let transitionNode = searchContentNode.transitionNodeForGallery(messageId: messageId, media: media) {
return transitionNode
}
}
var transitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
self.historyNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ChatMessageItemView {
if let result = itemNode.transitionNode(id: messageId, media: media) {
transitionNode = result
}
} else if let itemNode = itemNode as? ListMessageNode {
if let result = itemNode.transitionNode(id: messageId, media: media) {
transitionNode = result
}
} else if let itemNode = itemNode as? GridMessageItemNode {
if let result = itemNode.transitionNode(id: messageId, media: media) {
transitionNode = result
}
}
}
if let transitionNode = transitionNode {
return transitionNode
}
return nil
}
func clearHighlightAnimated(_ animated: Bool) {
if let listView = self.historyNode as? ListView {
listView.clearHighlightAnimated(animated)
}
}
}