Swiftgram/TelegramUI/MediaNavigationAccessoryItemListNode.swift
2018-04-11 23:43:31 +04:00

207 lines
13 KiB
Swift

import Foundation
import AsyncDisplayKit
import Display
import TelegramCore
import Postbox
final class MediaNavigationAccessoryItemListNode: ASDisplayNode {
static let minimizedPanelHeight: CGFloat = 31.0
private var theme: PresentationTheme
var collapse: (() -> Void)?
private var previousMaximizedHeight: CGFloat?
private let account: Account
private let topSeparatorNode: ASDisplayNode
private let bottomSeparatorNode: ASDisplayNode
private let separatorNode: ASDisplayNode
private let panelNode: HighlightTrackingButtonNode
private let panelHandleNode: ASImageNode
private let contentNode: ASDisplayNode
private var listNode: ChatHistoryListNode?
var stateAndStatus: AudioPlaylistStateAndStatus? {
didSet {
if self.stateAndStatus != oldValue {
let previousPlaylistPeerId = (oldValue?.state.playlistId as? PeerMessageHistoryAudioPlaylistId)?.peerId
let updatedPlaylistPeerId = (self.stateAndStatus?.state.playlistId as? PeerMessageHistoryAudioPlaylistId)?.peerId
if previousPlaylistPeerId != updatedPlaylistPeerId {
if let listNode = self.listNode {
listNode.removeFromSupernode()
self.listNode = nil
}
if let updatedPlaylistPeerId = updatedPlaylistPeerId {
let controllerInteraction = ChatControllerInteraction(openMessage: { [weak self] message in
if let strongSelf = self, let listNode = strongSelf.listNode {
var galleryMedia: Media?
if let message = listNode.messageInCurrentHistoryView(message.id) {
for media in message.media {
if let file = media as? TelegramMediaFile {
galleryMedia = file
} else if let image = media as? TelegramMediaImage {
galleryMedia = image
} else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
if let file = content.file {
galleryMedia = file
} else if let image = content.image {
galleryMedia = image
}
}
}
}
if let galleryMedia = galleryMedia {
if let file = galleryMedia as? TelegramMediaFile, file.isMusic || file.isVoice {
if let applicationContext = strongSelf.account.applicationContext as? TelegramApplicationContext {
let player = ManagedAudioPlaylistPlayer(audioSessionManager: (strongSelf.account.applicationContext as! TelegramApplicationContext).mediaManager.audioSession, overlayMediaManager: (strongSelf.account.applicationContext as! TelegramApplicationContext).mediaManager.overlayMediaManager, mediaManager: (strongSelf.account.applicationContext as! TelegramApplicationContext).mediaManager, account: strongSelf.account, postbox: strongSelf.account.postbox, playlist: peerMessageHistoryAudioPlaylist(account: strongSelf.account, messageId: message.id))
applicationContext.mediaManager.setPlaylistPlayer(player)
player.control(.navigation(.next))
}
}
}
}
return false
}, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _ in }, sendGif: { _ in }, requestMessageActionCallback: { _, _, _ in }, openUrl: { _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, openMessageShareMenu: { _ in
}, presentController: { _, _ in }, presentGlobalOverlayController: { _, _ in }, callPeer: { _ in }, longTap: { _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in }, canSetupReply: { _ in return false }, requestMessageUpdate: { _ in }, automaticMediaDownloadSettings: AutomaticMediaDownloadSettings.defaultSettings)
let listNode = ChatHistoryListNode(account: account, chatLocation: .peer(updatedPlaylistPeerId), tagMask: .music, messageId: nil, controllerInteraction: controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: false))
listNode.preloadPages = true
self.listNode = listNode
self.contentNode.addSubnode(listNode)
if let previousMaximizedHeight = self.previousMaximizedHeight {
self.updateLayout(size: self.bounds.size, maximizedHeight: previousMaximizedHeight, transition: .immediate)
}
}
} else {
let previousPlaylistMessageId = (oldValue?.state.item?.id as? PeerMessageHistoryAudioPlaylistItemId)?.id
let updatedPlaylistMessageId = (self.stateAndStatus?.state.item?.id as? PeerMessageHistoryAudioPlaylistItemId)?.id
if let updatedPlaylistMessageId = updatedPlaylistMessageId, previousPlaylistMessageId != updatedPlaylistMessageId {
if let listNode = self.listNode {
var foundItemNode: ListMessageFileItemNode?
listNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ListMessageFileItemNode, let message = itemNode.message, message.id == updatedPlaylistMessageId {
foundItemNode = itemNode
}
}
if let foundItemNode = foundItemNode {
listNode.ensureItemNodeVisible(foundItemNode)
} else if let message = listNode.messageInCurrentHistoryView(updatedPlaylistMessageId) {
listNode.scrollToMessage(from: MessageIndex(message), to: MessageIndex(message), animated: true)
}
}
}
}
}
}
}
init(account: Account) {
self.account = account
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
self.theme = presentationData.theme
self.topSeparatorNode = ASDisplayNode()
self.topSeparatorNode.isLayerBacked = true
self.topSeparatorNode.backgroundColor = self.theme.rootController.navigationBar.separatorColor
self.bottomSeparatorNode = ASDisplayNode()
self.bottomSeparatorNode.isLayerBacked = true
self.bottomSeparatorNode.backgroundColor = self.theme.rootController.navigationBar.separatorColor
self.separatorNode = ASDisplayNode()
self.separatorNode.isLayerBacked = true
self.separatorNode.backgroundColor = self.theme.rootController.navigationBar.separatorColor
self.panelNode = HighlightTrackingButtonNode()
self.panelNode.backgroundColor = self.theme.rootController.navigationBar.backgroundColor
self.panelHandleNode = ASImageNode()
self.panelHandleNode.displaysAsynchronously = false
self.panelHandleNode.displayWithoutProcessing = true
self.panelHandleNode.image = PresentationResourcesRootController.navigationPlayerHandleIcon(self.theme)
self.contentNode = ASDisplayNode()
self.contentNode.backgroundColor = self.theme.chatList.backgroundColor
self.contentNode.clipsToBounds = true
super.init()
self.addSubnode(self.contentNode)
self.addSubnode(self.topSeparatorNode)
self.addSubnode(self.panelNode)
self.panelNode.addSubnode(self.panelHandleNode)
self.addSubnode(self.bottomSeparatorNode)
self.addSubnode(self.separatorNode)
self.panelNode.addTarget(self, action: #selector(self.panelPressed), forControlEvents: .touchUpInside)
self.panelNode.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if highlighted {
//strongSelf.panelNode.layer.removeAnimation(forKey: "opacity")
//strongSelf.panelNode.alpha = 0.55
} else {
//strongSelf.panelNode.alpha = 0.35
//strongSelf.panelNode.layer.animateAlpha(from: 0.55, to: 0.35, duration: 0.2)
}
}
}
}
func updateLayout(size: CGSize, maximizedHeight: CGFloat, transition: ContainedViewLayoutTransition) {
self.previousMaximizedHeight = maximizedHeight
let separatorAlpha: CGFloat = size.height.isLessThanOrEqualTo(MediaNavigationAccessoryItemListNode.minimizedPanelHeight) ? 0.0 : 1.0
transition.updateAlpha(node: self.separatorNode, alpha: separatorAlpha)
transition.updateAlpha(node: self.panelHandleNode, alpha: min(1.0, max(0.0, size.height / MediaNavigationAccessoryItemListNode.minimizedPanelHeight)))
transition.updateFrame(node: self.topSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: UIScreenPixel)))
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: size.height), size: CGSize(width: size.width, height: UIScreenPixel)))
transition.updateFrame(node: self.panelNode, frame: CGRect(origin: CGPoint(x: 0.0, y: size.height - MediaNavigationAccessoryItemListNode.minimizedPanelHeight), size: CGSize(width: size.width, height: MediaNavigationAccessoryItemListNode.minimizedPanelHeight)))
transition.updateFrame(node: self.panelHandleNode, frame: CGRect(origin: CGPoint(x: floor((size.width - 36.0) / 2.0), y: (size.height - 19.0) - (size.height - MediaNavigationAccessoryItemListNode.minimizedPanelHeight)), size: CGSize(width: 36.0, height: 7.0)))
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: size.height - MediaNavigationAccessoryItemListNode.minimizedPanelHeight - UIScreenPixel), size: CGSize(width: size.width, height: UIScreenPixel)))
transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: max(0.0, size.height - MediaNavigationAccessoryItemListNode.minimizedPanelHeight))))
if let listNode = listNode {
let listNodeSize = CGSize(width: size.width, height: max(10.0, maximizedHeight - MediaNavigationAccessoryItemListNode.minimizedPanelHeight))
listNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: listNodeSize)
var duration: Double = 0.0
var curve: UInt = 0
switch transition {
case .immediate:
break
case let .animated(animationDuration, animationCurve):
duration = animationDuration
switch animationCurve {
case .easeInOut:
break
case .spring:
curve = 7
}
}
let listViewCurve: ListViewAnimationCurve
if curve == 7 {
listViewCurve = .Spring(duration: duration)
} else {
listViewCurve = .Default
}
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: UIEdgeInsets(top: 0.0, left:
0.0, bottom: 0.0, right: 0.0), duration: duration, curve: listViewCurve)
listNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets)
}
//transition.updateFrame(node: self.contentNode, frame: ))
}
@objc func panelPressed() {
self.collapse?()
}
}