Swiftgram/TelegramUI/ChatListSearchRecentPeersNode.swift
2018-06-29 20:19:01 +03:00

213 lines
9.2 KiB
Swift

import Foundation
import AsyncDisplayKit
import Display
import SwiftSignalKit
import Postbox
import TelegramCore
private func calculateItemCustomWidth(width: CGFloat) -> CGFloat {
let itemInsets = UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 6.0)
let minimalItemWidth: CGFloat = width > 301.0 ? 70.0 : 60.0
let effectiveWidth1 = width - 12.0 - 12.0
let effectiveWidth = width - itemInsets.left - itemInsets.right
let itemsPerRow = Int(effectiveWidth1 / minimalItemWidth)
let itemWidth = floor(effectiveWidth1 / CGFloat(itemsPerRow))
let itemsInRow = Int(effectiveWidth / itemWidth)
let itemsInRowWidth = CGFloat(itemsInRow) * itemWidth
let remainingWidth = max(0.0, effectiveWidth - itemsInRowWidth)
let itemSpacing = floorToScreenPixels(remainingWidth / CGFloat(itemsInRow + 1))
return itemWidth + itemSpacing
}
private struct ChatListSearchRecentPeersEntry: Comparable, Identifiable {
let index: Int
let peer: Peer
var stableId: PeerId {
return self.peer.id
}
static func ==(lhs: ChatListSearchRecentPeersEntry, rhs: ChatListSearchRecentPeersEntry) -> Bool {
if lhs.index != rhs.index {
return false
}
if !lhs.peer.isEqual(rhs.peer) {
return false
}
return true
}
static func <(lhs: ChatListSearchRecentPeersEntry, rhs: ChatListSearchRecentPeersEntry) -> Bool {
return lhs.index < rhs.index
}
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, mode: HorizontalPeerItemMode, peerSelected: @escaping (Peer) -> Void, peerLongTapped: @escaping (Peer) -> Void, isPeerSelected: @escaping (PeerId) -> Bool, itemCustomWidth: CGFloat?) -> ListViewItem {
return HorizontalPeerItem(theme: theme, strings: strings, mode: mode, account: account, peer: self.peer, action: peerSelected, longTapAction: { peer in
peerLongTapped(peer)
}, isPeerSelected: isPeerSelected, customWidth: itemCustomWidth)
}
}
final class ChatListSearchRecentPeersNode: ASDisplayNode {
private var theme: PresentationTheme
private var strings: PresentationStrings
private let mode: HorizontalPeerItemMode
private let sectionHeaderNode: ListSectionHeaderNode
private let listView: ListView
private let share: Bool
private let peerSelected: (Peer) -> Void
private let peerLongTapped: (Peer) -> Void
private let isPeerSelected: (PeerId) -> Bool
private let disposable = MetaDisposable()
private var items: [ListViewItem] = []
private var itemCustomWidth: CGFloat?
init(account: Account, theme: PresentationTheme, mode: HorizontalPeerItemMode, strings: PresentationStrings, peerSelected: @escaping (Peer) -> Void, peerLongTapped: @escaping (Peer) -> Void, isPeerSelected: @escaping (PeerId) -> Bool, share: Bool = false) {
self.theme = theme
self.strings = strings
self.mode = mode
self.share = share
self.peerSelected = peerSelected
self.peerLongTapped = peerLongTapped
self.isPeerSelected = isPeerSelected
self.sectionHeaderNode = ListSectionHeaderNode(theme: theme)
self.sectionHeaderNode.title = strings.DialogList_RecentTitlePeople.uppercased()
self.listView = ListView()
self.listView.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
super.init()
self.addSubnode(self.sectionHeaderNode)
self.addSubnode(self.listView)
let peersDisposable = DisposableSet()
peersDisposable.add((recentPeers(account: account)
|> filter { value -> Bool in
switch value {
case let .peers(peers):
return !peers.isEmpty
case .disabled:
return true
}
}
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] peers in
if let strongSelf = self {
var entries: [ChatListSearchRecentPeersEntry] = []
switch peers {
case let .peers(peers):
for peer in peers {
entries.append(ChatListSearchRecentPeersEntry(index: entries.count, peer: peer))
}
case .disabled:
break
}
var items: [ListViewItem] = []
for entry in entries {
items.append(entry.item(account: account, theme: strongSelf.theme, strings: strongSelf.strings, mode: mode, peerSelected: peerSelected, peerLongTapped: { peer in
peerLongTapped(peer)
}, isPeerSelected: isPeerSelected, itemCustomWidth: strongSelf.itemCustomWidth))
}
strongSelf.items = items
strongSelf.listView.transaction(deleteIndices: [], insertIndicesAndItems: (0 ..< items.count).map({ ListViewInsertItem(index: $0, previousIndex: nil, item: items[$0], directionHint: .Down) }), updateIndicesAndItems: [], options: [], updateOpaqueState: nil)
}
}))
if case .actionSheet = mode {
peersDisposable.add(managedUpdatedRecentPeers(postbox: account.postbox, network: account.network).start())
}
self.disposable.set(peersDisposable)
}
deinit {
self.disposable.dispose()
}
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {
if self.theme !== theme || self.strings !== strings {
self.theme = theme
self.strings = strings
self.sectionHeaderNode.title = strings.DialogList_RecentTitlePeople
self.sectionHeaderNode.updateTheme(theme: theme)
}
}
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
return CGSize(width: constrainedSize.width, height: 120.0)
}
func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat) {
self.sectionHeaderNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: 29.0))
self.sectionHeaderNode.updateLayout(size: CGSize(width: size.width, height: 29.0), leftInset: leftInset, rightInset: rightInset)
var insets = UIEdgeInsets()
insets.top += leftInset
insets.bottom += rightInset
var itemCustomWidth: CGFloat?
if self.share {
insets.top = 7.0
insets.bottom = 7.0
itemCustomWidth = calculateItemCustomWidth(width: size.width)
}
var updateItems: [ListViewUpdateItem] = []
if itemCustomWidth != self.itemCustomWidth {
self.itemCustomWidth = itemCustomWidth
for i in 0 ..< self.items.count {
if let item = self.items[i] as? HorizontalPeerItem {
self.items[i] = HorizontalPeerItem(theme: self.theme, strings: self.strings, mode: self.mode, account: item.account, peer: item.peer, action: self.peerSelected, longTapAction: self.peerLongTapped, isPeerSelected: self.isPeerSelected, customWidth: itemCustomWidth)
updateItems.append(ListViewUpdateItem(index: i, previousIndex: i, item: self.items[i], directionHint: nil))
}
}
}
self.listView.bounds = CGRect(x: 0.0, y: 0.0, width: 92.0, height: size.width)
self.listView.position = CGPoint(x: size.width / 2.0, y: 92.0 / 2.0 + 29.0)
self.listView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: updateItems, options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: CGSize(width: 92.0, height: size.width), insets: insets, duration: 0.0, curve: .Default), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
}
func viewAndPeerAtPoint(_ point: CGPoint) -> (UIView, PeerId)? {
let adjustedPoint = self.view.convert(point, to: self.listView.view)
var selectedItemNode: ASDisplayNode?
self.listView.forEachItemNode { itemNode in
if itemNode.frame.contains(adjustedPoint) {
selectedItemNode = itemNode
}
}
if let selectedItemNode = selectedItemNode as? HorizontalPeerItemNode, let peer = selectedItemNode.item?.peer {
return (selectedItemNode.view, peer.id)
}
return nil
}
func removePeer(_ peerId: PeerId) {
for i in 0 ..< self.items.count {
if let item = self.items[i] as? HorizontalPeerItem, item.peer.id == peerId {
self.items.remove(at: i)
self.listView.transaction(deleteIndices: [ListViewDeleteItem(index: i, directionHint: nil)], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.AnimateInsertion], updateOpaqueState: nil)
break
}
}
}
func updateSelectedPeers(animated: Bool) {
self.listView.forEachItemNode { itemNode in
if let itemNode = itemNode as? HorizontalPeerItemNode {
itemNode.updateSelection(animated: animated)
}
}
}
}