diff --git a/TelegramUI/ChannelAdminsController.swift b/TelegramUI/ChannelAdminsController.swift index af55c32279..6269a74b53 100644 --- a/TelegramUI/ChannelAdminsController.swift +++ b/TelegramUI/ChannelAdminsController.swift @@ -275,7 +275,8 @@ private struct ChannelAdminsControllerState: Equatable { let removingPeerId: PeerId? let removedPeerIds: Set let temporaryAdmins: [RenderedChannelParticipant] - + let searchingMembers: Bool + init() { self.selectedType = nil self.editing = false @@ -283,15 +284,17 @@ private struct ChannelAdminsControllerState: Equatable { self.removingPeerId = nil self.removedPeerIds = Set() self.temporaryAdmins = [] + self.searchingMembers = false } - init(selectedType: CurrentAdministrationType?, editing: Bool, peerIdWithRevealedOptions: PeerId?, removingPeerId: PeerId?, removedPeerIds: Set, temporaryAdmins: [RenderedChannelParticipant]) { + init(selectedType: CurrentAdministrationType?, editing: Bool, peerIdWithRevealedOptions: PeerId?, removingPeerId: PeerId?, removedPeerIds: Set, temporaryAdmins: [RenderedChannelParticipant], searchingMembers: Bool) { self.selectedType = selectedType self.editing = editing self.peerIdWithRevealedOptions = peerIdWithRevealedOptions self.removingPeerId = removingPeerId self.removedPeerIds = removedPeerIds self.temporaryAdmins = temporaryAdmins + self.searchingMembers = searchingMembers } static func ==(lhs: ChannelAdminsControllerState, rhs: ChannelAdminsControllerState) -> Bool { @@ -313,32 +316,39 @@ private struct ChannelAdminsControllerState: Equatable { if lhs.temporaryAdmins != rhs.temporaryAdmins { return false } + if lhs.searchingMembers != rhs.searchingMembers { + return false + } return true } + func withUpdatedSearchingMembers(_ searchingMembers: Bool) -> ChannelAdminsControllerState { + return ChannelAdminsControllerState(selectedType: self.selectedType, editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, removedPeerIds: self.removedPeerIds, temporaryAdmins: self.temporaryAdmins, searchingMembers: searchingMembers) + } + func withUpdatedSelectedType(_ selectedType: CurrentAdministrationType?) -> ChannelAdminsControllerState { - return ChannelAdminsControllerState(selectedType: selectedType, editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, removedPeerIds: self.removedPeerIds, temporaryAdmins: self.temporaryAdmins) + return ChannelAdminsControllerState(selectedType: selectedType, editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, removedPeerIds: self.removedPeerIds, temporaryAdmins: self.temporaryAdmins, searchingMembers: self.searchingMembers) } func withUpdatedEditing(_ editing: Bool) -> ChannelAdminsControllerState { - return ChannelAdminsControllerState(selectedType: self.selectedType, editing: editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, removedPeerIds: self.removedPeerIds, temporaryAdmins: self.temporaryAdmins) + return ChannelAdminsControllerState(selectedType: self.selectedType, editing: editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, removedPeerIds: self.removedPeerIds, temporaryAdmins: self.temporaryAdmins, searchingMembers: self.searchingMembers) } func withUpdatedPeerIdWithRevealedOptions(_ peerIdWithRevealedOptions: PeerId?) -> ChannelAdminsControllerState { - return ChannelAdminsControllerState(selectedType: self.selectedType, editing: self.editing, peerIdWithRevealedOptions: peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, removedPeerIds: self.removedPeerIds, temporaryAdmins: self.temporaryAdmins) + return ChannelAdminsControllerState(selectedType: self.selectedType, editing: self.editing, peerIdWithRevealedOptions: peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, removedPeerIds: self.removedPeerIds, temporaryAdmins: self.temporaryAdmins, searchingMembers: self.searchingMembers) } func withUpdatedRemovingPeerId(_ removingPeerId: PeerId?) -> ChannelAdminsControllerState { - return ChannelAdminsControllerState(selectedType: self.selectedType, editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: removingPeerId, removedPeerIds: self.removedPeerIds, temporaryAdmins: self.temporaryAdmins) + return ChannelAdminsControllerState(selectedType: self.selectedType, editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: removingPeerId, removedPeerIds: self.removedPeerIds, temporaryAdmins: self.temporaryAdmins, searchingMembers: self.searchingMembers) } func withUpdatedRemovedPeerIds(_ removedPeerIds: Set) -> ChannelAdminsControllerState { - return ChannelAdminsControllerState(selectedType: self.selectedType, editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, removedPeerIds: removedPeerIds, temporaryAdmins: self.temporaryAdmins) + return ChannelAdminsControllerState(selectedType: self.selectedType, editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, removedPeerIds: removedPeerIds, temporaryAdmins: self.temporaryAdmins, searchingMembers: self.searchingMembers) } func withUpdatedTemporaryAdmins(_ temporaryAdmins: [RenderedChannelParticipant]) -> ChannelAdminsControllerState { - return ChannelAdminsControllerState(selectedType: self.selectedType, editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, removedPeerIds: self.removedPeerIds, temporaryAdmins: temporaryAdmins) + return ChannelAdminsControllerState(selectedType: self.selectedType, editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, removedPeerIds: self.removedPeerIds, temporaryAdmins: temporaryAdmins, searchingMembers: self.searchingMembers) } } @@ -593,6 +603,7 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon |> deliverOnMainQueue |> map { presentationData, state, view, admins -> (ItemListControllerState, (ItemListNodeState, ChannelAdminsEntry.ItemGenerationArguments)) in var rightNavigationButton: ItemListNavigationButton? + var secondaryRightNavigationButton: ItemListNavigationButton? if let admins = admins, admins.count > 1 { if state.editing { rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: { @@ -606,6 +617,22 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon return state.withUpdatedEditing(true) } }) + + } + if !state.editing { + if rightNavigationButton == nil { + rightNavigationButton = ItemListNavigationButton(content: .icon(.search), style: .regular, enabled: true, action: { + updateState { state in + return state.withUpdatedSearchingMembers(true) + } + }) + } else { + secondaryRightNavigationButton = ItemListNavigationButton(content: .icon(.search), style: .regular, enabled: true, action: { + updateState { state in + return state.withUpdatedSearchingMembers(true) + } + }) + } } } @@ -617,8 +644,22 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon isGroup = false } - let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(isGroup ? presentationData.strings.ChatAdmins_Title : presentationData.strings.Channel_Management_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) - let listState = ItemListNodeState(entries: channelAdminsControllerEntries(presentationData: presentationData, accountPeerId: account.peerId, view: view, state: state, participants: admins), style: .blocks, animateChanges: previous != nil && admins != nil && previous!.count >= admins!.count) + var searchItem: ItemListControllerSearch? + if state.searchingMembers { + searchItem = ChannelMembersSearchItem(account: account, peerId: peerId, searchMode: .searchAdmins, cancel: { + updateState { state in + return state.withUpdatedSearchingMembers(false) + } + }, openPeer: { _, participant in + if let participant = participant?.participant, case .member = participant { + presentControllerImpl?(channelAdminController(account: account, peerId: peerId, adminId: participant.peerId, initialParticipant: participant, updated: { _ in + }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + } + }) + } + + let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(isGroup ? presentationData.strings.ChatAdmins_Title : presentationData.strings.Channel_Management_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) + let listState = ItemListNodeState(entries: channelAdminsControllerEntries(presentationData: presentationData, accountPeerId: account.peerId, view: view, state: state, participants: admins), style: .blocks, searchItem: searchItem, animateChanges: previous != nil && admins != nil && previous!.count >= admins!.count) return (controllerState, (listState, arguments)) } |> afterDisposed { diff --git a/TelegramUI/ChannelBlacklistController.swift b/TelegramUI/ChannelBlacklistController.swift index e905fb0472..0406126ffe 100644 --- a/TelegramUI/ChannelBlacklistController.swift +++ b/TelegramUI/ChannelBlacklistController.swift @@ -210,17 +210,20 @@ private struct ChannelBlacklistControllerState: Equatable { let editing: Bool let peerIdWithRevealedOptions: PeerId? let removingPeerId: PeerId? - + let searchingMembers: Bool + init() { self.editing = false self.peerIdWithRevealedOptions = nil self.removingPeerId = nil + self.searchingMembers = false } - init(editing: Bool, peerIdWithRevealedOptions: PeerId?, removingPeerId: PeerId?) { + init(editing: Bool, peerIdWithRevealedOptions: PeerId?, removingPeerId: PeerId?, searchingMembers: Bool) { self.editing = editing self.peerIdWithRevealedOptions = peerIdWithRevealedOptions self.removingPeerId = removingPeerId + self.searchingMembers = searchingMembers } static func ==(lhs: ChannelBlacklistControllerState, rhs: ChannelBlacklistControllerState) -> Bool { @@ -233,20 +236,28 @@ private struct ChannelBlacklistControllerState: Equatable { if lhs.removingPeerId != rhs.removingPeerId { return false } + if lhs.searchingMembers != rhs.searchingMembers { + return false + } return true } - func withUpdatedEditing(_ editing: Bool) -> ChannelBlacklistControllerState { - return ChannelBlacklistControllerState(editing: editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: self.removingPeerId) + func withUpdatedSearchingMembers(_ searchingMembers: Bool) -> ChannelBlacklistControllerState { + return ChannelBlacklistControllerState(editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, searchingMembers: searchingMembers) } + func withUpdatedEditing(_ editing: Bool) -> ChannelBlacklistControllerState { + return ChannelBlacklistControllerState(editing: editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, searchingMembers: self.searchingMembers) + } + + func withUpdatedPeerIdWithRevealedOptions(_ peerIdWithRevealedOptions: PeerId?) -> ChannelBlacklistControllerState { - return ChannelBlacklistControllerState(editing: self.editing, peerIdWithRevealedOptions: peerIdWithRevealedOptions, removingPeerId: self.removingPeerId) + return ChannelBlacklistControllerState(editing: self.editing, peerIdWithRevealedOptions: peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, searchingMembers: self.searchingMembers) } func withUpdatedRemovingPeerId(_ removingPeerId: PeerId?) -> ChannelBlacklistControllerState { - return ChannelBlacklistControllerState(editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: removingPeerId) + return ChannelBlacklistControllerState(editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: removingPeerId, searchingMembers: self.searchingMembers) } } @@ -406,6 +417,7 @@ public func channelBlacklistController(account: Account, peerId: PeerId) -> View |> deliverOnMainQueue |> map { presentationData, state, view, blacklist -> (ItemListControllerState, (ItemListNodeState, ChannelBlacklistEntry.ItemGenerationArguments)) in var rightNavigationButton: ItemListNavigationButton? + var secondaryRightNavigationButton: ItemListNavigationButton? if let blacklist = blacklist, !blacklist.isEmpty { if state.editing { rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: { @@ -414,12 +426,28 @@ public func channelBlacklistController(account: Account, peerId: PeerId) -> View } }) } else { - rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { + rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: { updateState { state in return state.withUpdatedEditing(true) } }) } + + if !state.editing { + if rightNavigationButton == nil { + rightNavigationButton = ItemListNavigationButton(content: .icon(.search), style: .regular, enabled: true, action: { + updateState { state in + return state.withUpdatedSearchingMembers(true) + } + }) + } else { + secondaryRightNavigationButton = ItemListNavigationButton(content: .icon(.search), style: .regular, enabled: true, action: { + updateState { state in + return state.withUpdatedSearchingMembers(true) + } + }) + } + } } var emptyStateItem: ItemListControllerEmptyStateItem? @@ -430,8 +458,22 @@ public func channelBlacklistController(account: Account, peerId: PeerId) -> View let previous = previousBlacklist previousBlacklist = blacklist - let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Channel_BlackList_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) - let listState = ItemListNodeState(entries: channelBlacklistControllerEntries(presentationData: presentationData, view: view, state: state, blacklist: blacklist), style: .blocks, emptyStateItem: emptyStateItem, animateChanges: previous != nil && blacklist != nil && (previous!.restricted.count + previous!.banned.count) >= (blacklist!.restricted.count + blacklist!.banned.count)) + var searchItem: ItemListControllerSearch? + if state.searchingMembers { + searchItem = ChannelMembersSearchItem(account: account, peerId: peerId, searchMode: .searchBanned, cancel: { + updateState { state in + return state.withUpdatedSearchingMembers(false) + } + }, openPeer: { _, participant in + if let participant = participant?.participant, case .member = participant { + presentControllerImpl?(channelBannedMemberController(account: account, peerId: peerId, memberId: participant.peerId, initialParticipant: participant, updated: { _ in + }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + } + }) + } + + let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Channel_BlackList_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) + let listState = ItemListNodeState(entries: channelBlacklistControllerEntries(presentationData: presentationData, view: view, state: state, blacklist: blacklist), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && blacklist != nil && (previous!.restricted.count + previous!.banned.count) >= (blacklist!.restricted.count + blacklist!.banned.count)) return (controllerState, (listState, arguments)) } |> afterDisposed { diff --git a/TelegramUI/ChannelInfoController.swift b/TelegramUI/ChannelInfoController.swift index cc7670182b..5433479f9b 100644 --- a/TelegramUI/ChannelInfoController.swift +++ b/TelegramUI/ChannelInfoController.swift @@ -105,11 +105,11 @@ private enum ChannelInfoEntry: ItemListNodeEntry { switch self { case .info: return 0 - case .addressName: - return 1 - case .about: - return 2 case .channelPhotoSetup: + return 1 + case .addressName: + return 2 + case .about: return 3 case .channelTypeSetup: return 4 @@ -454,7 +454,7 @@ private func channelInfoEntries(account: Account, presentationData: Presentation entries.append(.channelTypeSetup(theme: presentationData.theme, text: presentationData.strings.Channel_Edit_LinkItem, value: linkText)) - } else if let username = peer.username, !username.isEmpty { + } else if let username = peer.username, !username.isEmpty, state.editingState == nil { entries.append(.addressName(theme: presentationData.theme, text: presentationData.strings.Channel_LinkItem, value: username)) } diff --git a/TelegramUI/ChannelMemberCategoryListContext.swift b/TelegramUI/ChannelMemberCategoryListContext.swift index 86bb94e0b6..9d63557a0f 100644 --- a/TelegramUI/ChannelMemberCategoryListContext.swift +++ b/TelegramUI/ChannelMemberCategoryListContext.swift @@ -49,9 +49,9 @@ struct ChannelMemberListState { enum ChannelMemberListCategory { case recent case recentSearch(String) - case admins - case restricted - case banned + case admins(String?) + case restricted(String?) + case banned(String?) } private protocol ChannelMemberCategoryListContext { @@ -153,19 +153,31 @@ private final class ChannelMemberSingleCategoryListContext: ChannelMemberCategor private func loadSignal(offset: Int32, count: Int32, hash: Int32) -> Signal<[RenderedChannelParticipant]?, NoError> { let requestCategory: ChannelMembersCategory + var adminQuery: String? = nil switch self.category { case .recent: requestCategory = .recent(.all) case let .recentSearch(query): requestCategory = .recent(.search(query)) - case .admins: + case let .admins(query): requestCategory = .admins - case .restricted: - requestCategory = .restricted(.all) - case .banned: - requestCategory = .banned(.all) + adminQuery = query + case let .restricted(query): + requestCategory = .restricted(query != nil ? .search(query!) : .all) + case let .banned(query): + requestCategory = .banned(query != nil ? .search(query!) : .all) + } + return channelMembers(postbox: self.postbox, network: self.network, peerId: self.peerId, category: requestCategory, offset: offset, limit: count, hash: hash) |> map { members in + switch requestCategory { + case .admins: + if let query = adminQuery { + return members?.filter({$0.peer.displayTitle.lowercased().components(separatedBy: " ").contains(where: {$0.hasPrefix(query.lowercased())})}) + } + default: + break + } + return members } - return channelMembers(postbox: self.postbox, network: self.network, peerId: self.peerId, category: requestCategory, offset: offset, limit: count, hash: hash) } private func loadMoreSignal(count: Int32) -> Signal<[RenderedChannelParticipant], NoError> { @@ -561,14 +573,14 @@ final class PeerChannelMemberCategoriesContext { mappedCategory = .recent case let .recentSearch(query): mappedCategory = .recentSearch(query) - case .admins: - mappedCategory = .admins + case let .admins(query): + mappedCategory = .admins(query) default: mappedCategory = .recent } context = ChannelMemberSingleCategoryListContext(postbox: self.postbox, network: self.network, peerId: self.peerId, category: mappedCategory) - case .restrictedAndBanned: - context = ChannelMemberMultiCategoryListContext(postbox: self.postbox, network: self.network, peerId: self.peerId, categories: [.restricted, .banned]) + case let .restrictedAndBanned(query): + context = ChannelMemberMultiCategoryListContext(postbox: self.postbox, network: self.network, peerId: self.peerId, categories: [.restricted(query), .banned(query)]) } let contextWithSubscribers = PeerChannelMemberContextWithSubscribers(context: context, becameEmpty: { [weak self] in assert(Queue.mainQueue().isCurrent()) diff --git a/TelegramUI/ChannelMembersController.swift b/TelegramUI/ChannelMembersController.swift index fa4f159c39..40ece32655 100644 --- a/TelegramUI/ChannelMembersController.swift +++ b/TelegramUI/ChannelMembersController.swift @@ -190,17 +190,20 @@ private struct ChannelMembersControllerState: Equatable { let editing: Bool let peerIdWithRevealedOptions: PeerId? let removingPeerId: PeerId? - + let searchingMembers: Bool + init() { self.editing = false self.peerIdWithRevealedOptions = nil self.removingPeerId = nil + self.searchingMembers = false } - init(editing: Bool, peerIdWithRevealedOptions: PeerId?, removingPeerId: PeerId?) { + init(editing: Bool, peerIdWithRevealedOptions: PeerId?, removingPeerId: PeerId?, searchingMembers: Bool) { self.editing = editing self.peerIdWithRevealedOptions = peerIdWithRevealedOptions self.removingPeerId = removingPeerId + self.searchingMembers = searchingMembers } static func ==(lhs: ChannelMembersControllerState, rhs: ChannelMembersControllerState) -> Bool { @@ -213,20 +216,26 @@ private struct ChannelMembersControllerState: Equatable { if lhs.removingPeerId != rhs.removingPeerId { return false } - + if lhs.searchingMembers != rhs.searchingMembers { + return false + } return true } + func withUpdatedSearchingMembers(_ searchingMembers: Bool) -> ChannelMembersControllerState { + return ChannelMembersControllerState(editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, searchingMembers: searchingMembers) + } + func withUpdatedEditing(_ editing: Bool) -> ChannelMembersControllerState { - return ChannelMembersControllerState(editing: editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: self.removingPeerId) + return ChannelMembersControllerState(editing: editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, searchingMembers: self.searchingMembers) } func withUpdatedPeerIdWithRevealedOptions(_ peerIdWithRevealedOptions: PeerId?) -> ChannelMembersControllerState { - return ChannelMembersControllerState(editing: self.editing, peerIdWithRevealedOptions: peerIdWithRevealedOptions, removingPeerId: self.removingPeerId) + return ChannelMembersControllerState(editing: self.editing, peerIdWithRevealedOptions: peerIdWithRevealedOptions, removingPeerId: self.removingPeerId, searchingMembers: self.searchingMembers) } func withUpdatedRemovingPeerId(_ removingPeerId: PeerId?) -> ChannelMembersControllerState { - return ChannelMembersControllerState(editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: removingPeerId) + return ChannelMembersControllerState(editing: self.editing, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, removingPeerId: removingPeerId, searchingMembers: self.searchingMembers) } } @@ -472,14 +481,31 @@ public func channelMembersController(account: Account, peerId: PeerId) -> ViewCo return state.withUpdatedEditing(true) } }) - secondaryRightNavigationButton = ItemListNavigationButton(content: .icon(.search), style: .regular, enabled: true, action: { - // updateState { state in - // return state.withUpdatedSearchingMembers(true) - // } - }) + if let cachedData = view.cachedData as? CachedChannelData, cachedData.participantsSummary.memberCount ?? 0 >= 200 { + secondaryRightNavigationButton = ItemListNavigationButton(content: .icon(.search), style: .regular, enabled: true, action: { + updateState { state in + return state.withUpdatedSearchingMembers(true) + } + }) + } + } } + var searchItem: ItemListControllerSearch? + if state.searchingMembers { + searchItem = ChannelMembersSearchItem(account: account, peerId: peerId, cancel: { + updateState { state in + return state.withUpdatedSearchingMembers(false) + } + }, openPeer: { peer, _ in + if let infoController = peerInfoController(account: account, peer: peer) { + pushControllerImpl?(infoController) + // arguments.pushController(infoController) + } + }) + } + var emptyStateItem: ItemListControllerEmptyStateItem? if peers == nil { emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme) @@ -488,8 +514,8 @@ public func channelMembersController(account: Account, peerId: PeerId) -> ViewCo let previous = previousPeers previousPeers = peers - let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Channel_Subscribers_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) - let listState = ItemListNodeState(entries: ChannelMembersControllerEntries(account: account, presentationData: presentationData, view: view, state: state, participants: peers), style: .blocks, emptyStateItem: emptyStateItem, animateChanges: previous != nil && peers != nil && previous!.count >= peers!.count) + let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Channel_Subscribers_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) + let listState = ItemListNodeState(entries: ChannelMembersControllerEntries(account: account, presentationData: presentationData, view: view, state: state, participants: peers), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && peers != nil && previous!.count >= peers!.count) return (controllerState, (listState, arguments)) } |> afterDisposed { diff --git a/TelegramUI/ChannelMembersSearchContainerNode.swift b/TelegramUI/ChannelMembersSearchContainerNode.swift index 2dfc5b3958..9a499ee5fb 100644 --- a/TelegramUI/ChannelMembersSearchContainerNode.swift +++ b/TelegramUI/ChannelMembersSearchContainerNode.swift @@ -7,6 +7,8 @@ import TelegramCore enum ChannelMembersSearchMode { case searchMembers + case searchAdmins + case searchBanned case banAndPromoteActions case inviteActions } @@ -184,6 +186,28 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod foundGroupMembers = .single([]) foundMembers = channelMembers(postbox: account.postbox, network: account.network, peerId: peerId, category: .recent(.search(query))) |> map { $0 ?? [] } + case .searchAdmins: + foundGroupMembers = Signal { subscriber in + let (disposable, listControl) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.admins(postbox: account.postbox, network: account.network, peerId: peerId, searchQuery: query, updated: { state in + if case .ready = state.loadingState { + subscriber.putNext(state.list) + subscriber.putCompletion() + } + }) + return disposable + } |> runOn(Queue.mainQueue()) + foundMembers = .single([]) + case .searchBanned: + foundGroupMembers = Signal { subscriber in + let (disposable, listControl) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.restrictedAndBanned(postbox: account.postbox, network: account.network, peerId: peerId, searchQuery: query, updated: { state in + if case .ready = state.loadingState { + subscriber.putNext(state.list) + subscriber.putCompletion() + } + }) + return disposable + } |> runOn(Queue.mainQueue()) + foundMembers = .single([]) } let foundContacts: Signal<[Peer], NoError> @@ -193,7 +217,7 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod foundContacts = account.postbox.searchContacts(query: query.lowercased()) foundRemotePeers = .single(([], [])) |> then(searchPeers(account: account, query: query) |> delay(0.2, queue: Queue.concurrentDefaultQueue())) - case .searchMembers: + case .searchMembers, .searchBanned, .searchAdmins: foundContacts = .single([]) foundRemotePeers = .single(([], [])) } @@ -206,7 +230,7 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod switch mode { case .inviteActions, .banAndPromoteActions: existingPeerIds.insert(account.peerId) - case .searchMembers: + case .searchMembers, .searchAdmins, .searchBanned: break } @@ -219,7 +243,7 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod switch mode { case .inviteActions, .banAndPromoteActions: section = .members - case .searchMembers: + case .searchMembers, .searchBanned, .searchAdmins: section = .none } @@ -231,6 +255,30 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod enabled = false } } + switch mode { + case .searchAdmins: + switch participant.participant { + case .creator: + label = themeAndStrings.1.Channel_Management_LabelCreator + case let .member(_, _, adminInfo, _): + if let adminInfo = adminInfo { + if let peer = participant.peers[adminInfo.promotedBy] { + label = themeAndStrings.1.Channel_Management_PromotedBy(peer.displayTitle).0 + } + } + } + case .searchBanned: + switch participant.participant { + case let .member(_, _, _, banInfo): + if let banInfo = banInfo, let peer = participant.peers[banInfo.restrictedBy] { + label = themeAndStrings.1.Channel_Management_RestrictedBy(peer.displayTitle).0 + } + default: + break + } + default: + break + } entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant, label, enabled), section: section)) index += 1 } @@ -243,7 +291,7 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod switch mode { case .inviteActions, .banAndPromoteActions: section = .members - case .searchMembers: + case .searchMembers, .searchBanned, .searchAdmins: section = .none } @@ -255,6 +303,8 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod enabled = false } } + + entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant, label, enabled), section: section)) index += 1 } diff --git a/TelegramUI/ChatRecentActionsControllerNode.swift b/TelegramUI/ChatRecentActionsControllerNode.swift index cb2ec74e65..f3f099b121 100644 --- a/TelegramUI/ChatRecentActionsControllerNode.swift +++ b/TelegramUI/ChatRecentActionsControllerNode.swift @@ -115,7 +115,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { self.panelButtonNode.addTarget(self, action: #selector(self.infoButtonPressed), forControlEvents: .touchUpInside) - let (adminsDisposable, _) = self.account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.admins(postbox: self.account.postbox, network: self.account.network, peerId: self.peer.id, updated: { [weak self] state in + let (adminsDisposable, _) = self.account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.admins(postbox: self.account.postbox, network: self.account.network, peerId: self.peer.id, searchQuery: nil, updated: { [weak self] state in self?.adminsState = state }) self.adminsDisposable = adminsDisposable diff --git a/TelegramUI/GroupInfoController.swift b/TelegramUI/GroupInfoController.swift index 52e920765b..299f3e59a6 100644 --- a/TelegramUI/GroupInfoController.swift +++ b/TelegramUI/GroupInfoController.swift @@ -1639,11 +1639,11 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl var searchItem: ItemListControllerSearch? if state.searchingMembers { - searchItem = GroupInfoSearchItem(account: account, peerId: peerId, cancel: { + searchItem = ChannelMembersSearchItem(account: account, peerId: peerId, cancel: { updateState { state in return state.withUpdatedSearchingMembers(false) } - }, openPeer: { peer in + }, openPeer: { peer, _ in if let infoController = peerInfoController(account: account, peer: peer) { arguments.pushController(infoController) } diff --git a/TelegramUI/GroupInfoSearchItem.swift b/TelegramUI/GroupInfoSearchItem.swift index fa67cc3d51..6e46967b84 100644 --- a/TelegramUI/GroupInfoSearchItem.swift +++ b/TelegramUI/GroupInfoSearchItem.swift @@ -5,21 +5,22 @@ import Postbox import TelegramCore import SwiftSignalKit -final class GroupInfoSearchItem: ItemListControllerSearch { +final class ChannelMembersSearchItem: ItemListControllerSearch { let account: Account let peerId: PeerId let cancel: () -> Void - let openPeer: (Peer) -> Void - - init(account: Account, peerId: PeerId, cancel: @escaping () -> Void, openPeer: @escaping (Peer) -> Void) { + let openPeer: (Peer, RenderedChannelParticipant?) -> Void + let searchMode: ChannelMembersSearchMode + init(account: Account, peerId: PeerId, searchMode: ChannelMembersSearchMode = .searchMembers, cancel: @escaping () -> Void, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void) { self.account = account self.peerId = peerId self.cancel = cancel self.openPeer = openPeer + self.searchMode = searchMode } func isEqual(to: ItemListControllerSearch) -> Bool { - if let to = to as? GroupInfoSearchItem { + if let to = to as? ChannelMembersSearchItem { if self.account !== to.account { return false } @@ -42,16 +43,16 @@ final class GroupInfoSearchItem: ItemListControllerSearch { } func node(current: ItemListControllerSearchNode?) -> ItemListControllerSearchNode { - return GroupInfoSearchItemNode(account: self.account, peerId: self.peerId, openPeer: self.openPeer, cancel: self.cancel) + return ChannelMembersSearchItemNode(account: self.account, peerId: self.peerId, searchMode: self.searchMode, openPeer: self.openPeer, cancel: self.cancel) } } -private final class GroupInfoSearchItemNode: ItemListControllerSearchNode { +private final class ChannelMembersSearchItemNode: ItemListControllerSearchNode { private let containerNode: ChannelMembersSearchContainerNode - init(account: Account, peerId: PeerId, openPeer: @escaping (Peer) -> Void, cancel: @escaping () -> Void) { - self.containerNode = ChannelMembersSearchContainerNode(account: account, peerId: peerId, mode: .searchMembers, openPeer: { peer, _ in - openPeer(peer) + init(account: Account, peerId: PeerId, searchMode: ChannelMembersSearchMode, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, cancel: @escaping () -> Void) { + self.containerNode = ChannelMembersSearchContainerNode(account: account, peerId: peerId, mode: searchMode, openPeer: { peer, participant in + openPeer(peer, participant) }) self.containerNode.cancel = { cancel() diff --git a/TelegramUI/PeerChannelMemberCategoriesContextsManager.swift b/TelegramUI/PeerChannelMemberCategoriesContextsManager.swift index b2af6b60bb..f90827aea2 100644 --- a/TelegramUI/PeerChannelMemberCategoriesContextsManager.swift +++ b/TelegramUI/PeerChannelMemberCategoriesContextsManager.swift @@ -6,8 +6,8 @@ import SwiftSignalKit enum PeerChannelMemberContextKey: Hashable { case recent case recentSearch(String) - case admins - case restrictedAndBanned + case admins(String?) + case restrictedAndBanned(String?) } private final class PeerChannelMemberCategoriesContextsManagerImpl { @@ -89,12 +89,12 @@ final class PeerChannelMemberCategoriesContextsManager { return self.getContext(postbox: postbox, network: network, peerId: peerId, key: key, requestUpdate: requestUpdate, updated: updated) } - func admins(postbox: Postbox, network: Network, peerId: PeerId, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl?) { - return self.getContext(postbox: postbox, network: network, peerId: peerId, key: .admins, requestUpdate: true, updated: updated) + func admins(postbox: Postbox, network: Network, peerId: PeerId, searchQuery: String? = nil, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl?) { + return self.getContext(postbox: postbox, network: network, peerId: peerId, key: .admins(searchQuery), requestUpdate: true, updated: updated) } - func restrictedAndBanned(postbox: Postbox, network: Network, peerId: PeerId, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl?) { - return self.getContext(postbox: postbox, network: network, peerId: peerId, key: .restrictedAndBanned, requestUpdate: true, updated: updated) + func restrictedAndBanned(postbox: Postbox, network: Network, peerId: PeerId, searchQuery: String? = nil, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl?) { + return self.getContext(postbox: postbox, network: network, peerId: peerId, key: .restrictedAndBanned(searchQuery), requestUpdate: true, updated: updated) } func updateMemberBannedRights(account: Account, peerId: PeerId, memberId: PeerId, bannedRights: TelegramChannelBannedRights?) -> Signal {