Swiftgram/submodules/TelegramUI/Sources/ContactMultiselectionController.swift
2024-02-06 00:06:31 +04:00

690 lines
35 KiB
Swift

import Foundation
import UIKit
import Display
import AsyncDisplayKit
import Postbox
import SwiftSignalKit
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
import ProgressNavigationButtonNode
import AccountContext
import AlertUI
import PresentationDataUtils
import ContactListUI
import CounterContollerTitleView
import EditableTokenListNode
import PremiumUI
import UndoUI
private func peerTokenTitle(accountPeerId: PeerId, peer: Peer, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) -> String {
if peer.id == accountPeerId {
return strings.DialogList_SavedMessages
} else if peer.id.isReplies {
return strings.DialogList_Replies
} else {
return EnginePeer(peer).displayTitle(strings: strings, displayOrder: nameDisplayOrder)
}
}
class ContactMultiselectionControllerImpl: ViewController, ContactMultiselectionController {
private let params: ContactMultiselectionControllerParams
private let context: AccountContext
private let mode: ContactMultiselectionControllerMode
private let isPeerEnabled: ((EnginePeer) -> Bool)?
private let attemptDisabledItemSelection: ((EnginePeer, ChatListDisabledPeerReason) -> Void)?
private let titleView: CounterContollerTitleView
private var contactsNode: ContactMultiselectionControllerNode {
return self.displayNode as! ContactMultiselectionControllerNode
}
var dismissed: (() -> Void)?
private let index: PeerNameIndex = .lastNameFirst
private var _ready = Promise<Bool>()
private var _limitsReady = Promise<Bool>()
private var _peersReady = Promise<Bool>()
private var _listReady = Promise<Bool>()
override var ready: Promise<Bool> {
return self._ready
}
private let _result = Promise<ContactMultiselectionResult>()
var result: Signal<ContactMultiselectionResult, NoError> {
return self._result.get()
}
private var rightNavigationButton: UIBarButtonItem?
var displayProgress: Bool = false {
didSet {
if self.displayProgress != oldValue {
if self.displayProgress {
let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.presentationData.theme.rootController.navigationBar.accentTextColor))
self.navigationItem.rightBarButtonItem = item
} else {
self.navigationItem.rightBarButtonItem = self.rightNavigationButton
}
}
}
}
private var didPlayPresentationAnimation = false
private var presentationData: PresentationData
private var presentationDataDisposable: Disposable?
private var limitsConfiguration: LimitsConfiguration?
private var limitsConfigurationDisposable: Disposable?
private var initialPeersDisposable: Disposable?
private let options: [ContactListAdditionalOption]
private let filters: [ContactListFilter]
private let onlyWriteable: Bool
private let limit: Int32?
init(_ params: ContactMultiselectionControllerParams) {
self.params = params
self.context = params.context
self.mode = params.mode
self.isPeerEnabled = params.isPeerEnabled
self.attemptDisabledItemSelection = params.attemptDisabledItemSelection
self.options = params.options
self.filters = params.filters
self.onlyWriteable = params.onlyWriteable
self.limit = params.limit
self.presentationData = params.updatedPresentationData?.initial ?? params.context.sharedContext.currentPresentationData.with { $0 }
self.titleView = CounterContollerTitleView(theme: self.presentationData.theme)
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
self.navigationItem.titleView = self.titleView
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
self.scrollToTop = { [weak self] in
if let strongSelf = self {
strongSelf.contactsNode.scrollToTop()
}
}
self.presentationDataDisposable = ((params.updatedPresentationData?.signal ?? params.context.sharedContext.presentationData)
|> deliverOnMainQueue).startStrict(next: { [weak self] presentationData in
if let strongSelf = self {
let previousTheme = strongSelf.presentationData.theme
let previousStrings = strongSelf.presentationData.strings
strongSelf.presentationData = presentationData
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings {
strongSelf.updateThemeAndStrings()
}
}
})
self.limitsConfigurationDisposable = (context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.Limits())
|> deliverOnMainQueue).startStrict(next: { [weak self] value in
if let strongSelf = self {
strongSelf.limitsConfiguration = value._asLimits()
strongSelf.updateTitle()
strongSelf._limitsReady.set(.single(true))
}
})
switch self.mode {
case let .chatSelection(chatSelection):
let selectedChats = chatSelection.selectedChats
let additionalCategories = chatSelection.additionalCategories
let _ = (self.context.engine.data.get(
EngineDataList(
selectedChats.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
)
)
|> deliverOnMainQueue).startStandalone(next: { [weak self] peerList in
guard let strongSelf = self else {
return
}
let peers = peerList.compactMap { $0 }
if let additionalCategories = additionalCategories {
for i in 0 ..< additionalCategories.categories.count {
if additionalCategories.selectedCategories.contains(additionalCategories.categories[i].id) {
strongSelf.contactsNode.editableTokens.append(EditableTokenListToken(id: additionalCategories.categories[i].id, title: additionalCategories.categories[i].title, fixedPosition: i, subject: .category(additionalCategories.categories[i].smallIcon)))
}
}
}
strongSelf.contactsNode.editableTokens.append(contentsOf: peers.map { peer -> EditableTokenListToken in
return EditableTokenListToken(id: peer.id, title: peerTokenTitle(accountPeerId: params.context.account.peerId, peer: peer._asPeer(), strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder), fixedPosition: nil, subject: .peer(peer))
})
strongSelf._peersReady.set(.single(true))
if strongSelf.isNodeLoaded {
strongSelf.requestLayout(transition: .immediate)
}
})
default:
self._peersReady.set(.single(true))
}
self._ready.set(combineLatest(self._listReady.get(), self._limitsReady.get(), self._peersReady.get()) |> map { $0 && $1 && $2 })
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.presentationDataDisposable?.dispose()
self.limitsConfigurationDisposable?.dispose()
self.initialPeersDisposable?.dispose()
}
private func updateThemeAndStrings() {
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
self.updateTitle()
self.contactsNode.updatePresentationData(self.presentationData)
}
private func updateTitle() {
switch self.mode {
case .groupCreation:
let maxCount: Int32 = self.limitsConfiguration?.maxSupergroupMemberCount ?? 5000
let count: Int
switch self.contactsNode.contentNode {
case let .contacts(contactsNode):
count = contactsNode.selectionState?.selectedPeerIndices.count ?? 0
case let .chats(chatsNode):
count = chatsNode.currentState.selectedPeerIds.count
}
self.titleView.title = CounterContollerTitle(title: self.presentationData.strings.Compose_NewGroupTitle, counter: "\(count)/\(maxCount)")
let rightNavigationButton = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.rightNavigationButtonPressed))
self.rightNavigationButton = rightNavigationButton
self.navigationItem.rightBarButtonItem = self.rightNavigationButton
rightNavigationButton.isEnabled = true //count != 0 || self.params.alwaysEnabled
case .premiumGifting:
let maxCount: Int32 = self.limit ?? 10
var count = 0
if case let .contacts(contactsNode) = self.contactsNode.contentNode {
count = contactsNode.selectionState?.selectedPeerIndices.count ?? 0
}
self.titleView.title = CounterContollerTitle(title: self.presentationData.strings.Premium_Gift_ContactSelection_Title, counter: "\(count)/\(maxCount)")
case .requestedUsersSelection:
let maxCount: Int32 = self.limit ?? 10
var count = 0
if case let .contacts(contactsNode) = self.contactsNode.contentNode {
count = contactsNode.selectionState?.selectedPeerIndices.count ?? 0
}
self.titleView.title = CounterContollerTitle(title: self.presentationData.strings.RequestPeer_SelectUsers, counter: "\(count)/\(maxCount)")
case .channelCreation:
self.titleView.title = CounterContollerTitle(title: self.presentationData.strings.GroupInfo_AddParticipantTitle, counter: "")
let rightNavigationButton = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.rightNavigationButtonPressed))
self.rightNavigationButton = rightNavigationButton
self.navigationItem.rightBarButtonItem = self.rightNavigationButton
rightNavigationButton.isEnabled = true
case .peerSelection:
self.titleView.title = CounterContollerTitle(title: self.presentationData.strings.PrivacyLastSeenSettings_EmpryUsersPlaceholder, counter: "")
let rightNavigationButton = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.rightNavigationButtonPressed))
self.rightNavigationButton = rightNavigationButton
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(cancelPressed))
self.navigationItem.rightBarButtonItem = self.rightNavigationButton
rightNavigationButton.isEnabled = false
case let .chatSelection(chatSelection):
self.titleView.title = CounterContollerTitle(title: chatSelection.title, counter: "")
let rightNavigationButton = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.rightNavigationButtonPressed))
self.rightNavigationButton = rightNavigationButton
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(cancelPressed))
self.navigationItem.rightBarButtonItem = self.rightNavigationButton
rightNavigationButton.isEnabled = self.params.alwaysEnabled
}
}
override func loadDisplayNode() {
self.displayNode = ContactMultiselectionControllerNode(navigationBar: self.navigationBar, context: self.context, presentationData: self.presentationData, mode: self.mode, isPeerEnabled: self.isPeerEnabled, attemptDisabledItemSelection: self.attemptDisabledItemSelection, options: self.options, filters: self.filters, onlyWriteable: self.onlyWriteable, limit: self.limit, reachedSelectionLimit: self.params.reachedLimit)
switch self.contactsNode.contentNode {
case let .contacts(contactsNode):
self._listReady.set(contactsNode.ready)
case let .chats(chatsNode):
self._listReady.set(chatsNode.ready)
}
let accountPeerId = self.context.account.peerId
self.contactsNode.dismiss = { [weak self] in
self?.presentingViewController?.dismiss(animated: true, completion: nil)
}
let limit = self.limit
self.contactsNode.openPeer = { [weak self] peer in
if let strongSelf = self, case let .peer(peer, _, _) = peer {
var updatedCount: Int?
var addedToken: EditableTokenListToken?
var removedTokenId: AnyHashable?
let maxRegularCount: Int32 = strongSelf.limitsConfiguration?.maxGroupMemberCount ?? 200
var displayCountAlert = false
var selectionState: ContactListNodeGroupSelectionState?
let reachedLimit = strongSelf.params.reachedLimit
switch strongSelf.contactsNode.contentNode {
case let .contacts(contactsNode):
contactsNode.updateSelectionState { state in
if let state = state {
var updatedState = state.withToggledPeerId(.peer(peer.id))
if let limit = limit, updatedState.selectedPeerIndices.count > limit {
reachedLimit?(Int32(limit))
updatedCount = nil
removedTokenId = nil
addedToken = nil
return state
}
if updatedState.selectedPeerIndices[.peer(peer.id)] == nil {
removedTokenId = peer.id
} else {
if updatedState.selectedPeerIndices.count >= maxRegularCount {
displayCountAlert = true
updatedState = updatedState.withToggledPeerId(.peer(peer.id))
} else {
addedToken = EditableTokenListToken(id: peer.id, title: peerTokenTitle(accountPeerId: accountPeerId, peer: peer, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder), fixedPosition: nil, subject: .peer(EnginePeer(peer)))
}
}
updatedCount = updatedState.selectedPeerIndices.count
selectionState = updatedState
return updatedState
} else {
return nil
}
}
case let .chats(chatsNode):
chatsNode.updateState { initialState in
var state = initialState
if state.selectedPeerIds.contains(peer.id) {
state.selectedPeerIds.remove(peer.id)
removedTokenId = peer.id
} else {
addedToken = EditableTokenListToken(id: peer.id, title: peerTokenTitle(accountPeerId: accountPeerId, peer: peer, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder), fixedPosition: nil, subject: .peer(EnginePeer(peer)))
state.selectedPeerIds.insert(peer.id)
}
updatedCount = state.selectedPeerIds.count
if let limit = limit, let count = updatedCount, count > limit {
reachedLimit?(Int32(count))
updatedCount = nil
removedTokenId = nil
addedToken = nil
return initialState
}
var updatedState = ContactListNodeGroupSelectionState()
for peerId in state.selectedPeerIds {
updatedState = updatedState.withToggledPeerId(.peer(peerId))
}
selectionState = updatedState
return state
}
break
}
if let searchResultsNode = strongSelf.contactsNode.searchResultsNode {
searchResultsNode.updateSelectionState { _ in
return selectionState
}
}
if let updatedCount = updatedCount {
switch strongSelf.mode {
case .groupCreation, .peerSelection, .chatSelection:
strongSelf.rightNavigationButton?.isEnabled = updatedCount != 0 || strongSelf.params.alwaysEnabled
case .channelCreation, .premiumGifting, .requestedUsersSelection:
break
}
switch strongSelf.mode {
case .groupCreation:
let maxCount: Int32 = strongSelf.limitsConfiguration?.maxSupergroupMemberCount ?? 5000
strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.Compose_NewGroupTitle, counter: "\(updatedCount)/\(maxCount)")
case .premiumGifting:
let maxCount: Int32 = strongSelf.limit ?? 10
strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.Premium_Gift_ContactSelection_Title, counter: "\(updatedCount)/\(maxCount)")
case .requestedUsersSelection:
let maxCount: Int32 = strongSelf.limit ?? 10
strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.RequestPeer_SelectUsers, counter: "\(updatedCount)/\(maxCount)")
case .peerSelection, .channelCreation, .chatSelection:
break
}
}
if let addedToken = addedToken {
strongSelf.contactsNode.editableTokens.append(addedToken)
} else if let removedTokenId = removedTokenId {
strongSelf.contactsNode.editableTokens = strongSelf.contactsNode.editableTokens.filter { token in
return token.id != removedTokenId
}
}
strongSelf.requestLayout(transition: ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring))
if displayCountAlert {
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.CreateGroup_SoftUserLimitAlert, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}
}
self.contactsNode.openDisabledPeer = { [weak self] peer, reason in
guard let self else {
return
}
switch reason {
case .generic:
break
case .premiumRequired:
self.forEachController { c in
if let c = c as? UndoOverlayController {
c.dismiss()
}
return true
}
var hasAction = false
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
if !premiumConfiguration.isPremiumDisabled {
hasAction = true
}
self.present(UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: nil, text: presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Text(peer.compactDisplayTitle).string, customUndoText: hasAction ? self.presentationData.strings.Chat_ToastMessagingRestrictedToPremium_Action : nil, timeout: nil, linkAction: { _ in
}), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] action in
guard let self else {
return false
}
if case .undo = action {
let premiumController = PremiumIntroScreen(context: self.context, source: .settings)
self.push(premiumController)
}
return false
}), in: .current)
}
}
self.contactsNode.removeSelectedPeer = { [weak self] peerId in
if let strongSelf = self {
var updatedCount: Int?
var removedTokenId: AnyHashable?
var selectionState: ContactListNodeGroupSelectionState?
switch strongSelf.contactsNode.contentNode {
case let .contacts(contactsNode):
contactsNode.updateSelectionState { state in
if let state = state {
let updatedState = state.withToggledPeerId(peerId)
if updatedState.selectedPeerIndices[peerId] == nil {
if case let .peer(peerId) = peerId {
removedTokenId = peerId
}
}
updatedCount = updatedState.selectedPeerIndices.count
selectionState = updatedState
return updatedState
} else {
return nil
}
}
case let .chats(chatsNode):
chatsNode.updateState { state in
var state = state
if case let .peer(peerIdValue) = peerId {
if state.selectedPeerIds.contains(peerIdValue) {
state.selectedPeerIds.remove(peerIdValue)
}
removedTokenId = peerIdValue
}
updatedCount = state.selectedPeerIds.count
var updatedState = ContactListNodeGroupSelectionState()
for peerId in state.selectedPeerIds {
updatedState = updatedState.withToggledPeerId(.peer(peerId))
}
selectionState = updatedState
return state
}
}
if let searchResultsNode = strongSelf.contactsNode.searchResultsNode {
searchResultsNode.updateSelectionState { _ in
return selectionState
}
}
if let updatedCount = updatedCount {
switch strongSelf.mode {
case .groupCreation, .peerSelection, .chatSelection:
strongSelf.rightNavigationButton?.isEnabled = updatedCount != 0 || strongSelf.params.alwaysEnabled
case .channelCreation, .premiumGifting, .requestedUsersSelection:
break
}
switch strongSelf.mode {
case .groupCreation:
let maxCount: Int32 = strongSelf.limitsConfiguration?.maxSupergroupMemberCount ?? 5000
strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.Compose_NewGroupTitle, counter: "\(updatedCount)/\(maxCount)")
case .premiumGifting:
let maxCount: Int32 = strongSelf.limit ?? 10
strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.Premium_Gift_ContactSelection_Title, counter: "\(updatedCount)/\(maxCount)")
case .requestedUsersSelection:
let maxCount: Int32 = strongSelf.limit ?? 10
strongSelf.titleView.title = CounterContollerTitle(title: strongSelf.presentationData.strings.RequestPeer_SelectUsers, counter: "\(updatedCount)/\(maxCount)")
case .peerSelection, .channelCreation, .chatSelection:
break
}
}
if let removedTokenId = removedTokenId {
strongSelf.contactsNode.editableTokens = strongSelf.contactsNode.editableTokens.filter { token in
return token.id != removedTokenId
}
}
strongSelf.requestLayout(transition: ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring))
}
}
self.contactsNode.removeSelectedCategory = { [weak self] id in
guard let strongSelf = self else {
return
}
var removedTokenId: AnyHashable?
switch strongSelf.contactsNode.contentNode {
case .contacts:
break
case let .chats(chatsNode):
chatsNode.updateState { state in
var state = state
if state.selectedAdditionalCategoryIds.contains(id) {
state.selectedAdditionalCategoryIds.remove(id)
removedTokenId = id
}
return state
}
if let removedTokenId = removedTokenId {
strongSelf.contactsNode.editableTokens = strongSelf.contactsNode.editableTokens.filter { token in
return token.id != removedTokenId
}
}
strongSelf.requestLayout(transition: ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring))
}
}
self.contactsNode.additionalCategorySelected = { [weak self] id in
guard let strongSelf = self else {
return
}
var addedToken: EditableTokenListToken?
var removedTokenIds: [AnyHashable] = []
switch strongSelf.contactsNode.contentNode {
case .contacts:
break
case let .chats(chatsNode):
var categoryToken: EditableTokenListToken?
if case let .chatSelection(chatSelection) = strongSelf.mode {
if let additionalCategories = chatSelection.additionalCategories {
for i in 0 ..< additionalCategories.categories.count {
if additionalCategories.categories[i].id == id {
categoryToken = EditableTokenListToken(id: id, title: additionalCategories.categories[i].title, fixedPosition: i, subject: .category(additionalCategories.categories[i].smallIcon))
break
}
}
}
}
chatsNode.updateState { state in
var state = state
if state.selectedAdditionalCategoryIds.contains(id) {
state.selectedAdditionalCategoryIds.remove(id)
removedTokenIds.append(id)
} else {
state.selectedAdditionalCategoryIds.insert(id)
addedToken = categoryToken
}
return state
}
if let addedToken = addedToken, let insertFixedIndex = addedToken.fixedPosition {
var added = false
for i in 0 ..< strongSelf.contactsNode.editableTokens.count {
if let fixedIndex = strongSelf.contactsNode.editableTokens[i].fixedPosition {
if fixedIndex > insertFixedIndex {
strongSelf.contactsNode.editableTokens.insert(addedToken, at: i)
added = true
break
}
} else {
strongSelf.contactsNode.editableTokens.insert(addedToken, at: i)
added = true
break
}
}
if !added {
strongSelf.contactsNode.editableTokens.append(addedToken)
}
strongSelf.contactsNode.editableTokens = strongSelf.contactsNode.editableTokens.filter { token in
return !removedTokenIds.contains(token.id)
}
} else if !removedTokenIds.isEmpty {
strongSelf.contactsNode.editableTokens = strongSelf.contactsNode.editableTokens.filter { token in
return !removedTokenIds.contains(token.id)
}
}
strongSelf.requestLayout(transition: ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring))
}
}
self.contactsNode.complete = { [weak self] in
if let strongSelf = self {
var available = true
if let rightBarButtonItem = strongSelf.navigationItem.rightBarButtonItem {
available = rightBarButtonItem.isEnabled
}
if available {
strongSelf.rightNavigationButtonPressed()
}
}
}
switch self.contactsNode.contentNode {
case let .contacts(contactsNode):
contactsNode.deselectedAll = { [weak self] in
guard let self else {
return
}
self.contactsNode.editableTokens = []
self.requestLayout(transition: ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring))
}
case .chats:
break
}
self.displayNodeDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
switch self.contactsNode.contentNode {
case let .contacts(contactsNode):
contactsNode.enableUpdates = true
case .chats:
break
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let presentationArguments = self.presentationArguments as? ViewControllerPresentationArguments, !self.didPlayPresentationAnimation {
self.didPlayPresentationAnimation = true
if case .modalSheet = presentationArguments.presentationAnimation {
self.contactsNode.animateIn()
}
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
switch self.contactsNode.contentNode {
case let .contacts(contactsNode):
contactsNode.enableUpdates = false
case .chats:
break
}
}
private var suspendNavigationBarLayout: Bool = false
private var suspendedNavigationBarLayout: ContainerViewLayout?
private var additionalNavigationBarBackgroundHeight: CGFloat = 0.0
override public func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
if self.suspendNavigationBarLayout {
self.suspendedNavigationBarLayout = layout
return
}
self.applyNavigationBarLayout(layout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: transition)
}
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.suspendNavigationBarLayout = true
super.containerLayoutUpdated(layout, transition: transition)
self.additionalNavigationBarBackgroundHeight = self.contactsNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, actualNavigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
self.suspendNavigationBarLayout = false
if let suspendedNavigationBarLayout = self.suspendedNavigationBarLayout {
self.suspendedNavigationBarLayout = suspendedNavigationBarLayout
self.applyNavigationBarLayout(suspendedNavigationBarLayout, navigationLayout: self.navigationLayout(layout: layout), additionalBackgroundHeight: self.additionalNavigationBarBackgroundHeight, transition: transition)
}
}
@objc func cancelPressed() {
self._result.set(.single(.none))
self.dismiss()
}
@objc func rightNavigationButtonPressed() {
var peerIds: [ContactListPeerId] = []
var additionalOptionIds: [Int] = []
switch self.contactsNode.contentNode {
case let .contacts(contactsNode):
contactsNode.updateSelectionState { state in
if let state = state {
peerIds = Array(state.selectedPeerIndices.keys)
}
return state
}
case let .chats(chatsNode):
for peerId in chatsNode.currentState.selectedPeerIds {
peerIds.append(.peer(peerId))
}
for optionId in chatsNode.currentState.selectedAdditionalCategoryIds {
additionalOptionIds.append(optionId)
}
additionalOptionIds.sort()
}
self._result.set(.single(.result(peerIds: peerIds, additionalOptionIds: additionalOptionIds)))
}
}