mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
256 lines
9.0 KiB
Swift
256 lines
9.0 KiB
Swift
import Foundation
|
|
import Display
|
|
import AsyncDisplayKit
|
|
import Postbox
|
|
import SwiftSignalKit
|
|
import TelegramCore
|
|
|
|
private enum ContactsControllerEntryId: Hashable {
|
|
case search
|
|
case vcard
|
|
case peerId(Int64)
|
|
|
|
var hashValue: Int {
|
|
switch self {
|
|
case .search:
|
|
return 0
|
|
case .vcard:
|
|
return 1
|
|
case let .peerId(peerId):
|
|
return peerId.hashValue
|
|
}
|
|
}
|
|
}
|
|
|
|
private func <(lhs: ContactsControllerEntryId, rhs: ContactsControllerEntryId) -> Bool {
|
|
return lhs.hashValue < rhs.hashValue
|
|
}
|
|
|
|
private func ==(lhs: ContactsControllerEntryId, rhs: ContactsControllerEntryId) -> Bool {
|
|
switch lhs {
|
|
case .search:
|
|
switch rhs {
|
|
case .search:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
case .vcard:
|
|
switch rhs {
|
|
case .vcard:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
case let .peerId(lhsId):
|
|
switch rhs {
|
|
case let .peerId(rhsId):
|
|
return lhsId == rhsId
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
private enum ContactsEntry: Comparable, Identifiable {
|
|
case search
|
|
case vcard(Peer)
|
|
case peer(Peer)
|
|
|
|
var stableId: ContactsControllerEntryId {
|
|
switch self {
|
|
case .search:
|
|
return .search
|
|
case .vcard:
|
|
return .vcard
|
|
case let .peer(peer):
|
|
return .peerId(peer.id.toInt64())
|
|
}
|
|
}
|
|
}
|
|
|
|
private func ==(lhs: ContactsEntry, rhs: ContactsEntry) -> Bool {
|
|
switch lhs {
|
|
case .search:
|
|
switch rhs {
|
|
case .search:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
case let .vcard(lhsPeer):
|
|
switch rhs {
|
|
case let .vcard(rhsPeer):
|
|
return lhsPeer.id == rhsPeer.id
|
|
default:
|
|
return false
|
|
}
|
|
case let .peer(lhsPeer):
|
|
switch rhs {
|
|
case let .peer(rhsPeer):
|
|
return lhsPeer.id == rhsPeer.id
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
private func <(lhs: ContactsEntry, rhs: ContactsEntry) -> Bool {
|
|
return lhs.stableId < rhs.stableId
|
|
}
|
|
|
|
private func entriesForView(_ view: ContactPeersView) -> [ContactsEntry] {
|
|
var entries: [ContactsEntry] = []
|
|
entries.append(.search)
|
|
if let peer = view.accountPeer {
|
|
entries.append(.vcard(peer))
|
|
}
|
|
for peer in view.peers {
|
|
entries.append(.peer(peer))
|
|
}
|
|
return entries
|
|
}
|
|
|
|
public class ContactsController: ViewController {
|
|
private let queue = Queue()
|
|
|
|
private let account: Account
|
|
private let disposable = MetaDisposable()
|
|
|
|
private var entries: [ContactsEntry] = []
|
|
|
|
private var contactsNode: ContactsControllerNode {
|
|
return self.displayNode as! ContactsControllerNode
|
|
}
|
|
|
|
private let index: PeerNameIndex = .lastNameFirst
|
|
|
|
public init(account: Account) {
|
|
self.account = account
|
|
|
|
super.init()
|
|
|
|
self.title = "Contacts"
|
|
self.tabBarItem.title = "Contacts"
|
|
self.tabBarItem.image = UIImage(bundleImageName: "Chat List/Tabs/IconContacts")
|
|
self.tabBarItem.selectedImage = UIImage(bundleImageName: "Chat List/Tabs/IconContactsSelected")
|
|
|
|
self.disposable.set((account.postbox.contactPeersView(index: self.index, accountPeerId: account.peerId) |> deliverOn(self.queue)).start(next: { [weak self] view in
|
|
self?.updateView(view)
|
|
}))
|
|
|
|
self.scrollToTop = { [weak self] in
|
|
if let strongSelf = self, !strongSelf.entries.isEmpty {
|
|
strongSelf.contactsNode.listView.deleteAndInsertItems(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .Top, animated: true, curve: .Default, directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, completion: { _ in })
|
|
}
|
|
}
|
|
}
|
|
|
|
required public init(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
deinit {
|
|
self.disposable.dispose()
|
|
}
|
|
|
|
override public func loadDisplayNode() {
|
|
self.displayNode = ContactsControllerNode(account: self.account)
|
|
|
|
self.contactsNode.navigationBar = self.navigationBar
|
|
|
|
self.contactsNode.requestDeactivateSearch = { [weak self] in
|
|
self?.deactivateSearch()
|
|
}
|
|
|
|
self.contactsNode.requestOpenPeerFromSearch = { [weak self] peerId in
|
|
if let strongSelf = self {
|
|
(strongSelf.navigationController as? NavigationController)?.pushViewController(ChatController(account: strongSelf.account, peerId: peerId))
|
|
}
|
|
}
|
|
|
|
self.displayNodeDidLoad()
|
|
}
|
|
|
|
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
|
super.containerLayoutUpdated(layout, transition: transition)
|
|
|
|
self.contactsNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationBar.frame.maxY, transition: transition)
|
|
}
|
|
|
|
private func updateView(_ view: ContactPeersView) {
|
|
assert(self.queue.isCurrent())
|
|
|
|
let previousEntries = self.entries
|
|
let updatedEntries = entriesForView(view)
|
|
|
|
let (deleteIndices, indicesAndItems) = mergeListsStable(leftList: previousEntries, rightList: updatedEntries)
|
|
|
|
self.entries = updatedEntries
|
|
|
|
var adjustedDeleteIndices: [ListViewDeleteItem] = []
|
|
if deleteIndices.count != 0 {
|
|
for index in deleteIndices {
|
|
adjustedDeleteIndices.append(ListViewDeleteItem(index: index, directionHint: nil))
|
|
}
|
|
}
|
|
|
|
var adjustedIndicesAndItems: [ListViewInsertItem] = []
|
|
for (index, entry, previousIndex) in indicesAndItems {
|
|
switch entry {
|
|
case .search:
|
|
adjustedIndicesAndItems.append(ListViewInsertItem(index: index, previousIndex: previousIndex, item: ChatListSearchItem(placeholder: "Search contacts", activate: { [weak self] in
|
|
self?.activateSearch()
|
|
}), directionHint: nil))
|
|
case let .vcard(peer):
|
|
adjustedIndicesAndItems.append(ListViewInsertItem(index: index, previousIndex: previousIndex, item: ContactsVCardItem(account: self.account, peer: peer, action: { [weak self] _ in
|
|
if let strongSelf = self {
|
|
strongSelf.entrySelected(entry)
|
|
strongSelf.contactsNode.listView.clearHighlightAnimated(true)
|
|
}
|
|
}), directionHint: nil))
|
|
case let .peer(peer):
|
|
adjustedIndicesAndItems.append(ListViewInsertItem(index: index, previousIndex: previousIndex, item: ContactsPeerItem(account: self.account, peer: peer, index: self.index, action: { [weak self] _ in
|
|
if let strongSelf = self {
|
|
strongSelf.entrySelected(entry)
|
|
strongSelf.contactsNode.listView.clearHighlightAnimated(true)
|
|
}
|
|
}), directionHint: nil))
|
|
}
|
|
}
|
|
|
|
DispatchQueue.main.async {
|
|
let options: ListViewDeleteAndInsertOptions = []
|
|
|
|
self.contactsNode.listView.deleteAndInsertItems(deleteIndices: adjustedDeleteIndices, insertIndicesAndItems: adjustedIndicesAndItems, updateIndicesAndItems: [], options: options, scrollToItem: nil, completion: { _ in
|
|
})
|
|
}
|
|
}
|
|
|
|
private func entrySelected(_ entry: ContactsEntry) {
|
|
if case let .peer(peer) = entry {
|
|
(self.navigationController as? NavigationController)?.pushViewController(ChatController(account: self.account, peerId: peer.id))
|
|
}
|
|
if case let .vcard(peer) = entry {
|
|
(self.navigationController as? NavigationController)?.pushViewController(ChatController(account: self.account, peerId: peer.id))
|
|
}
|
|
}
|
|
|
|
private func activateSearch() {
|
|
if self.displayNavigationBar {
|
|
if let scrollToTop = self.scrollToTop {
|
|
scrollToTop()
|
|
}
|
|
self.contactsNode.activateSearch()
|
|
self.setDisplayNavigationBar(false, transition: .animated(duration: 0.5, curve: .spring))
|
|
}
|
|
}
|
|
|
|
private func deactivateSearch() {
|
|
if !self.displayNavigationBar {
|
|
self.contactsNode.deactivateSearch()
|
|
self.setDisplayNavigationBar(true, transition: .animated(duration: 0.5, curve: .spring))
|
|
}
|
|
}
|
|
}
|