mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Add peer categories in members lists
This commit is contained in:
parent
47f3dde265
commit
cc5de9372f
@ -7242,3 +7242,11 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"Conversation.CopyProtectionForwardingDisabledBot" = "Forwards from this bot are restricted";
|
"Conversation.CopyProtectionForwardingDisabledBot" = "Forwards from this bot are restricted";
|
||||||
"Conversation.CopyProtectionSavingDisabledBot" = "Saving from this bot is restricted";
|
"Conversation.CopyProtectionSavingDisabledBot" = "Saving from this bot is restricted";
|
||||||
|
|
||||||
|
"Channel.ChannelSubscribersHeader" = "CHANNEL SUBSCRIBERS";
|
||||||
|
|
||||||
|
"Channel.Members.Contacts" = "CONTACTS IN THIS CHANNEL";
|
||||||
|
"Channel.Members.Other" = "OTHERS SUBSCRIBERS";
|
||||||
|
|
||||||
|
"Group.Members.Contacts" = "CONTACTS IN THIS GROUP";
|
||||||
|
"Group.Members.Other" = "OTHERS MEMBERS";
|
||||||
|
@ -26,6 +26,7 @@ public enum ChatListSearchItemHeaderType {
|
|||||||
case activeVoiceChats
|
case activeVoiceChats
|
||||||
case recentCalls
|
case recentCalls
|
||||||
case orImportIntoAnExistingGroup
|
case orImportIntoAnExistingGroup
|
||||||
|
case subscribers
|
||||||
|
|
||||||
fileprivate func title(strings: PresentationStrings) -> String {
|
fileprivate func title(strings: PresentationStrings) -> String {
|
||||||
switch self {
|
switch self {
|
||||||
@ -71,6 +72,8 @@ public enum ChatListSearchItemHeaderType {
|
|||||||
return strings.CallList_RecentCallsHeader
|
return strings.CallList_RecentCallsHeader
|
||||||
case .orImportIntoAnExistingGroup:
|
case .orImportIntoAnExistingGroup:
|
||||||
return strings.ChatList_HeaderImportIntoAnExistingGroup
|
return strings.ChatList_HeaderImportIntoAnExistingGroup
|
||||||
|
case .subscribers:
|
||||||
|
return strings.Channel_ChannelSubscribersHeader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,6 +121,8 @@ public enum ChatListSearchItemHeaderType {
|
|||||||
return .recentCalls
|
return .recentCalls
|
||||||
case .orImportIntoAnExistingGroup:
|
case .orImportIntoAnExistingGroup:
|
||||||
return .orImportIntoAnExistingGroup
|
return .orImportIntoAnExistingGroup
|
||||||
|
case .subscribers:
|
||||||
|
return .subscribers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,6 +153,7 @@ private enum ChatListSearchItemHeaderId: Int32 {
|
|||||||
case activeVoiceChats
|
case activeVoiceChats
|
||||||
case recentCalls
|
case recentCalls
|
||||||
case orImportIntoAnExistingGroup
|
case orImportIntoAnExistingGroup
|
||||||
|
case subscribers
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class ChatListSearchItemHeader: ListViewItemHeader {
|
public final class ChatListSearchItemHeader: ListViewItemHeader {
|
||||||
|
@ -36,6 +36,7 @@ private final class ChannelMembersControllerArguments {
|
|||||||
|
|
||||||
private enum ChannelMembersSection: Int32 {
|
private enum ChannelMembersSection: Int32 {
|
||||||
case addMembers
|
case addMembers
|
||||||
|
case contacts
|
||||||
case peers
|
case peers
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,14 +49,20 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
|
|||||||
case addMember(PresentationTheme, String)
|
case addMember(PresentationTheme, String)
|
||||||
case addMemberInfo(PresentationTheme, String)
|
case addMemberInfo(PresentationTheme, String)
|
||||||
case inviteLink(PresentationTheme, String)
|
case inviteLink(PresentationTheme, String)
|
||||||
case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, RenderedChannelParticipant, ItemListPeerItemEditing, Bool)
|
case contactsTitle(PresentationTheme, String)
|
||||||
|
case peersTitle(PresentationTheme, String)
|
||||||
|
case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, RenderedChannelParticipant, ItemListPeerItemEditing, Bool, Bool)
|
||||||
|
|
||||||
var section: ItemListSectionId {
|
var section: ItemListSectionId {
|
||||||
switch self {
|
switch self {
|
||||||
case .addMember, .addMemberInfo, .inviteLink:
|
case .addMember, .addMemberInfo, .inviteLink:
|
||||||
return ChannelMembersSection.addMembers.rawValue
|
return ChannelMembersSection.addMembers.rawValue
|
||||||
case .peerItem:
|
case .contactsTitle:
|
||||||
|
return ChannelMembersSection.contacts.rawValue
|
||||||
|
case .peersTitle:
|
||||||
return ChannelMembersSection.peers.rawValue
|
return ChannelMembersSection.peers.rawValue
|
||||||
|
case let .peerItem(_, _, _, _, _, _, _, _, isContact):
|
||||||
|
return isContact ? ChannelMembersSection.contacts.rawValue : ChannelMembersSection.peers.rawValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,9 +72,13 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
|
|||||||
return .index(0)
|
return .index(0)
|
||||||
case .addMemberInfo:
|
case .addMemberInfo:
|
||||||
return .index(1)
|
return .index(1)
|
||||||
case .inviteLink:
|
case .inviteLink:
|
||||||
return .index(2)
|
return .index(2)
|
||||||
case let .peerItem(_, _, _, _, _, participant, _, _):
|
case .contactsTitle:
|
||||||
|
return .index(3)
|
||||||
|
case .peersTitle:
|
||||||
|
return .index(4)
|
||||||
|
case let .peerItem(_, _, _, _, _, participant, _, _, _):
|
||||||
return .peer(participant.peer.id)
|
return .peer(participant.peer.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,14 +97,26 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .inviteLink(lhsTheme, lhsText):
|
case let .inviteLink(lhsTheme, lhsText):
|
||||||
if case let .inviteLink(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .inviteLink(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsParticipant, lhsEditing, lhsEnabled):
|
case let .contactsTitle(lhsTheme, lhsText):
|
||||||
if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsParticipant, rhsEditing, rhsEnabled) = rhs {
|
if case let .contactsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .peersTitle(lhsTheme, lhsText):
|
||||||
|
if case let .peersTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsParticipant, lhsEditing, lhsEnabled, lhsIsContact):
|
||||||
|
if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsParticipant, rhsEditing, rhsEnabled, rhsIsContact) = rhs {
|
||||||
if lhsIndex != rhsIndex {
|
if lhsIndex != rhsIndex {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -118,6 +141,9 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
|
|||||||
if lhsEnabled != rhsEnabled {
|
if lhsEnabled != rhsEnabled {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhsIsContact != rhsIsContact {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -143,11 +169,30 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
|
|||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
case .contactsTitle:
|
||||||
case let .peerItem(index, _, _, _, _, _, _, _):
|
|
||||||
switch rhs {
|
switch rhs {
|
||||||
case let .peerItem(rhsIndex, _, _, _, _, _, _, _):
|
case .addMember, .addMemberInfo, .inviteLink:
|
||||||
return index < rhsIndex
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case .peersTitle:
|
||||||
|
switch rhs {
|
||||||
|
case .addMember, .addMemberInfo, .inviteLink, .contactsTitle:
|
||||||
|
return false
|
||||||
|
case let .peerItem(_, _, _, _, _, _, _, _, isContact):
|
||||||
|
return !isContact
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case let .peerItem(lhsIndex, _, _, _, _, _, _, _, lhsIsContact):
|
||||||
|
switch rhs {
|
||||||
|
case .contactsTitle:
|
||||||
|
return false
|
||||||
|
case .peersTitle:
|
||||||
|
return lhsIsContact
|
||||||
|
case let .peerItem(rhsIndex, _, _, _, _, _, _, _, _):
|
||||||
|
return lhsIndex < rhsIndex
|
||||||
case .addMember, .addMemberInfo, .inviteLink:
|
case .addMember, .addMemberInfo, .inviteLink:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -167,7 +212,9 @@ private enum ChannelMembersEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
case let .addMemberInfo(_, text):
|
case let .addMemberInfo(_, text):
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
case let .peerItem(_, _, strings, dateTimeFormat, nameDisplayOrder, participant, editing, enabled):
|
case let .contactsTitle(_, text), let .peersTitle(_, text):
|
||||||
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
|
case let .peerItem(_, _, strings, dateTimeFormat, nameDisplayOrder, participant, editing, enabled, _):
|
||||||
let text: ItemListPeerItemText
|
let text: ItemListPeerItemText
|
||||||
if let user = participant.peer as? TelegramUser, let _ = user.botInfo {
|
if let user = participant.peer as? TelegramUser, let _ = user.botInfo {
|
||||||
text = .text(strings.Bot_GenericBotStatus, .secondary)
|
text = .text(strings.Bot_GenericBotStatus, .secondary)
|
||||||
@ -238,7 +285,7 @@ private struct ChannelMembersControllerState: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func channelMembersControllerEntries(context: AccountContext, presentationData: PresentationData, view: PeerView, state: ChannelMembersControllerState, participants: [RenderedChannelParticipant]?, isGroup: Bool) -> [ChannelMembersEntry] {
|
private func channelMembersControllerEntries(context: AccountContext, presentationData: PresentationData, view: PeerView, state: ChannelMembersControllerState, contacts: [RenderedChannelParticipant]?, participants: [RenderedChannelParticipant]?, isGroup: Bool) -> [ChannelMembersEntry] {
|
||||||
if participants == nil || participants?.count == nil {
|
if participants == nil || participants?.count == nil {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@ -251,6 +298,11 @@ private func channelMembersControllerEntries(context: AccountContext, presentati
|
|||||||
canAddMember = peer.hasPermission(.inviteMembers)
|
canAddMember = peer.hasPermission(.inviteMembers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var canEditMembers = false
|
||||||
|
if let peer = view.peers[view.peerId] as? TelegramChannel {
|
||||||
|
canEditMembers = peer.hasPermission(.banMembers)
|
||||||
|
}
|
||||||
|
|
||||||
if canAddMember {
|
if canAddMember {
|
||||||
entries.append(.addMember(presentationData.theme, isGroup ? presentationData.strings.Group_Members_AddMembers : presentationData.strings.Channel_Members_AddMembers))
|
entries.append(.addMember(presentationData.theme, isGroup ? presentationData.strings.Group_Members_AddMembers : presentationData.strings.Channel_Members_AddMembers))
|
||||||
if let peer = view.peers[view.peerId] as? TelegramChannel, peer.addressName == nil {
|
if let peer = view.peers[view.peerId] as? TelegramChannel, peer.addressName == nil {
|
||||||
@ -267,14 +319,44 @@ private func channelMembersControllerEntries(context: AccountContext, presentati
|
|||||||
|
|
||||||
|
|
||||||
var index: Int32 = 0
|
var index: Int32 = 0
|
||||||
let sortedParticipants = participants
|
var existingPeerIds = Set<PeerId>()
|
||||||
for participant in sortedParticipants {
|
|
||||||
var editable = true
|
var addedContactsHeader = false
|
||||||
var canEditMembers = false
|
if let contacts = contacts, !contacts.isEmpty {
|
||||||
if let peer = view.peers[view.peerId] as? TelegramChannel {
|
addedContactsHeader = true
|
||||||
canEditMembers = peer.hasPermission(.banMembers)
|
|
||||||
|
entries.append(.contactsTitle(presentationData.theme, isGroup ? presentationData.strings.Group_Members_Contacts : presentationData.strings.Channel_Members_Contacts))
|
||||||
|
|
||||||
|
for participant in contacts {
|
||||||
|
var editable = true
|
||||||
|
if participant.peer.id == context.account.peerId {
|
||||||
|
editable = false
|
||||||
|
} else {
|
||||||
|
switch participant.participant {
|
||||||
|
case .creator:
|
||||||
|
editable = false
|
||||||
|
case .member:
|
||||||
|
editable = canEditMembers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, true))
|
||||||
|
existingPeerIds.insert(participant.peer.id)
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var addedOtherHeader = false
|
||||||
|
for participant in participants {
|
||||||
|
if existingPeerIds.contains(participant.peer.id) {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if addedContactsHeader && !addedOtherHeader {
|
||||||
|
addedOtherHeader = true
|
||||||
|
entries.append(.peersTitle(presentationData.theme, isGroup ? presentationData.strings.Group_Members_Other : presentationData.strings.Channel_Members_Other))
|
||||||
|
}
|
||||||
|
|
||||||
|
var editable = true
|
||||||
if participant.peer.id == context.account.peerId {
|
if participant.peer.id == context.account.peerId {
|
||||||
editable = false
|
editable = false
|
||||||
} else {
|
} else {
|
||||||
@ -285,7 +367,7 @@ private func channelMembersControllerEntries(context: AccountContext, presentati
|
|||||||
editable = canEditMembers
|
editable = canEditMembers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id))
|
entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, false))
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -315,6 +397,7 @@ public func channelMembersController(context: AccountContext, updatedPresentatio
|
|||||||
actionsDisposable.add(removePeerDisposable)
|
actionsDisposable.add(removePeerDisposable)
|
||||||
|
|
||||||
let peersPromise = Promise<[RenderedChannelParticipant]?>(nil)
|
let peersPromise = Promise<[RenderedChannelParticipant]?>(nil)
|
||||||
|
let contactsPromise = Promise<[RenderedChannelParticipant]?>(nil)
|
||||||
|
|
||||||
let arguments = ChannelMembersControllerArguments(context: context, addMember: {
|
let arguments = ChannelMembersControllerArguments(context: context, addMember: {
|
||||||
actionsDisposable.add((peersPromise.get()
|
actionsDisposable.add((peersPromise.get()
|
||||||
@ -437,17 +520,22 @@ public func channelMembersController(context: AccountContext, updatedPresentatio
|
|||||||
|
|
||||||
let peerView = context.account.viewTracker.peerView(peerId)
|
let peerView = context.account.viewTracker.peerView(peerId)
|
||||||
|
|
||||||
|
let (contactsDisposable, _) = context.peerChannelMemberCategoriesContextsManager.contacts(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: nil, updated: { state in
|
||||||
|
contactsPromise.set(.single(state.list))
|
||||||
|
})
|
||||||
let (disposable, loadMoreControl) = context.peerChannelMemberCategoriesContextsManager.recent(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, updated: { state in
|
let (disposable, loadMoreControl) = context.peerChannelMemberCategoriesContextsManager.recent(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, updated: { state in
|
||||||
peersPromise.set(.single(state.list))
|
peersPromise.set(.single(state.list))
|
||||||
})
|
})
|
||||||
actionsDisposable.add(disposable)
|
actionsDisposable.add(disposable)
|
||||||
|
actionsDisposable.add(contactsDisposable)
|
||||||
|
|
||||||
var previousPeers: [RenderedChannelParticipant]?
|
var currentContacts: [RenderedChannelParticipant]?
|
||||||
|
var currentPeers: [RenderedChannelParticipant]?
|
||||||
|
|
||||||
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
|
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
|
||||||
let signal = combineLatest(queue: .mainQueue(), presentationData, statePromise.get(), peerView, peersPromise.get())
|
let signal = combineLatest(queue: .mainQueue(), presentationData, statePromise.get(), peerView, contactsPromise.get(), peersPromise.get())
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> map { presentationData, state, view, peers -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
|> map { presentationData, state, view, contacts, peers -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
var isGroup = true
|
var isGroup = true
|
||||||
if let peer = peerViewMainPeer(view) as? TelegramChannel, case .broadcast = peer.info {
|
if let peer = peerViewMainPeer(view) as? TelegramChannel, case .broadcast = peer.info {
|
||||||
isGroup = false
|
isGroup = false
|
||||||
@ -455,7 +543,14 @@ public func channelMembersController(context: AccountContext, updatedPresentatio
|
|||||||
|
|
||||||
var rightNavigationButton: ItemListNavigationButton?
|
var rightNavigationButton: ItemListNavigationButton?
|
||||||
var secondaryRightNavigationButton: ItemListNavigationButton?
|
var secondaryRightNavigationButton: ItemListNavigationButton?
|
||||||
if let peers = peers, !peers.isEmpty {
|
|
||||||
|
var isEmpty = true
|
||||||
|
if let contacts = contacts, !contacts.isEmpty {
|
||||||
|
isEmpty = false
|
||||||
|
} else if let peers = peers, !peers.isEmpty {
|
||||||
|
isEmpty = false
|
||||||
|
}
|
||||||
|
if !isEmpty {
|
||||||
if state.editing {
|
if state.editing {
|
||||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
|
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
|
||||||
updateState { state in
|
updateState { state in
|
||||||
@ -497,15 +592,26 @@ public func channelMembersController(context: AccountContext, updatedPresentatio
|
|||||||
}
|
}
|
||||||
|
|
||||||
var emptyStateItem: ItemListControllerEmptyStateItem?
|
var emptyStateItem: ItemListControllerEmptyStateItem?
|
||||||
if peers == nil || peers?.count == 0 {
|
if isEmpty {
|
||||||
emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme)
|
emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
let previous = previousPeers
|
let previousContacts = currentContacts
|
||||||
previousPeers = peers
|
currentContacts = contacts
|
||||||
|
|
||||||
|
let previousPeers = currentPeers
|
||||||
|
currentPeers = peers
|
||||||
|
|
||||||
|
var animateChanges = false
|
||||||
|
if let previousContacts = previousContacts, let contacts = contacts, previousContacts.count >= contacts.count {
|
||||||
|
animateChanges = true
|
||||||
|
}
|
||||||
|
if let previousPeers = previousPeers, let peers = peers, previousPeers.count >= peers.count {
|
||||||
|
animateChanges = true
|
||||||
|
}
|
||||||
|
|
||||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(isGroup ? presentationData.strings.Group_Members_Title : presentationData.strings.Channel_Subscribers_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(isGroup ? presentationData.strings.Group_Members_Title : presentationData.strings.Channel_Subscribers_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelMembersControllerEntries(context: context, presentationData: presentationData, view: view, state: state, participants: peers, isGroup: isGroup), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && peers != nil && previous!.count >= peers!.count)
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelMembersControllerEntries(context: context, presentationData: presentationData, view: view, state: state, contacts: contacts, participants: peers, isGroup: isGroup), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: animateChanges)
|
||||||
|
|
||||||
return (controllerState, (listState, arguments))
|
return (controllerState, (listState, arguments))
|
||||||
}
|
}
|
||||||
|
@ -38,14 +38,14 @@ private enum ChannelMembersSearchEntryId: Hashable {
|
|||||||
|
|
||||||
private enum ChannelMembersSearchEntry: Comparable, Identifiable {
|
private enum ChannelMembersSearchEntry: Comparable, Identifiable {
|
||||||
case copyInviteLink
|
case copyInviteLink
|
||||||
case peer(Int, RenderedChannelParticipant, ContactsPeerItemEditing, String?, Bool)
|
case peer(Int, RenderedChannelParticipant, ContactsPeerItemEditing, String?, Bool, Bool, Bool)
|
||||||
case contact(Int, Peer, TelegramUserPresence?)
|
case contact(Int, Peer, TelegramUserPresence?)
|
||||||
|
|
||||||
var stableId: ChannelMembersSearchEntryId {
|
var stableId: ChannelMembersSearchEntryId {
|
||||||
switch self {
|
switch self {
|
||||||
case .copyInviteLink:
|
case .copyInviteLink:
|
||||||
return .copyInviteLink
|
return .copyInviteLink
|
||||||
case let .peer(_, participant, _, _, _):
|
case let .peer(_, participant, _, _, _, _, _):
|
||||||
return .peer(participant.peer.id)
|
return .peer(participant.peer.id)
|
||||||
case let .contact(_, peer, _):
|
case let .contact(_, peer, _):
|
||||||
return .contact(peer.id)
|
return .contact(peer.id)
|
||||||
@ -60,8 +60,8 @@ private enum ChannelMembersSearchEntry: Comparable, Identifiable {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .peer(lhsIndex, lhsParticipant, lhsEditing, lhsLabel, lhsEnabled):
|
case let .peer(lhsIndex, lhsParticipant, lhsEditing, lhsLabel, lhsEnabled, lhsIsChannel, lhsIsContact):
|
||||||
if case .peer(lhsIndex, lhsParticipant, lhsEditing, lhsLabel, lhsEnabled) = rhs {
|
if case .peer(lhsIndex, lhsParticipant, lhsEditing, lhsLabel, lhsEnabled, lhsIsChannel, lhsIsContact) = rhs {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -92,10 +92,10 @@ private enum ChannelMembersSearchEntry: Comparable, Identifiable {
|
|||||||
} else {
|
} else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
case let .peer(lhsIndex, _, _, _, _):
|
case let .peer(lhsIndex, _, _, _, _, _, _):
|
||||||
if case .copyInviteLink = rhs {
|
if case .copyInviteLink = rhs {
|
||||||
return false
|
return false
|
||||||
} else if case let .peer(rhsIndex, _, _, _, _) = rhs {
|
} else if case let .peer(rhsIndex, _, _, _, _, _, _) = rhs {
|
||||||
return lhsIndex < rhsIndex
|
return lhsIndex < rhsIndex
|
||||||
} else if case .contact = rhs {
|
} else if case .contact = rhs {
|
||||||
return true
|
return true
|
||||||
@ -127,7 +127,7 @@ private enum ChannelMembersSearchEntry: Comparable, Identifiable {
|
|||||||
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: presentationData.strings.VoiceChat_CopyInviteLink, icon: icon, clearHighlightAutomatically: true, header: nil, action: {
|
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: presentationData.strings.VoiceChat_CopyInviteLink, icon: icon, clearHighlightAutomatically: true, header: nil, action: {
|
||||||
interaction.copyInviteLink()
|
interaction.copyInviteLink()
|
||||||
})
|
})
|
||||||
case let .peer(_, participant, editing, label, enabled):
|
case let .peer(_, participant, editing, label, enabled, isChannel, isContact):
|
||||||
let status: ContactsPeerItemStatus
|
let status: ContactsPeerItemStatus
|
||||||
if let label = label {
|
if let label = label {
|
||||||
status = .custom(string: label, multiline: false)
|
status = .custom(string: label, multiline: false)
|
||||||
@ -138,7 +138,14 @@ private enum ChannelMembersSearchEntry: Comparable, Identifiable {
|
|||||||
status = .none
|
status = .none
|
||||||
}
|
}
|
||||||
|
|
||||||
return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .peer, peer: .peer(peer: EnginePeer(participant.peer), chatPeer: nil), status: status, enabled: enabled, selection: .none, editing: editing, index: nil, header: ChatListSearchItemHeader(type: .groupMembers, theme: presentationData.theme, strings: presentationData.strings), action: { _ in
|
let headerType: ChatListSearchItemHeaderType
|
||||||
|
if isContact {
|
||||||
|
headerType = .contacts
|
||||||
|
} else {
|
||||||
|
headerType = isChannel ? .subscribers : .groupMembers
|
||||||
|
}
|
||||||
|
|
||||||
|
return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .peer, peer: .peer(peer: EnginePeer(participant.peer), chatPeer: nil), status: status, enabled: enabled, selection: .none, editing: editing, index: nil, header: ChatListSearchItemHeader(type: headerType, theme: presentationData.theme, strings: presentationData.strings), action: { _ in
|
||||||
interaction.openPeer(participant.peer, participant)
|
interaction.openPeer(participant.peer, participant)
|
||||||
})
|
})
|
||||||
case let .contact(_, peer, presence):
|
case let .contact(_, peer, presence):
|
||||||
@ -239,6 +246,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
|||||||
let previousEntries = Atomic<[ChannelMembersSearchEntry]?>(value: nil)
|
let previousEntries = Atomic<[ChannelMembersSearchEntry]?>(value: nil)
|
||||||
|
|
||||||
let disposableAndLoadMoreControl: (Disposable, PeerChannelMemberCategoryControl?)
|
let disposableAndLoadMoreControl: (Disposable, PeerChannelMemberCategoryControl?)
|
||||||
|
let contactsDisposableAndLoadMoreControl: (Disposable, PeerChannelMemberCategoryControl?)?
|
||||||
let additionalDisposable = MetaDisposable()
|
let additionalDisposable = MetaDisposable()
|
||||||
|
|
||||||
if peerId.namespace == Namespaces.Peer.CloudGroup {
|
if peerId.namespace == Namespaces.Peer.CloudGroup {
|
||||||
@ -398,7 +406,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
|||||||
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: nil), peer: peer, peers: peers, presences: peerView.peerPresences)
|
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: nil), peer: peer, peers: peers, presences: peerView.peerPresences)
|
||||||
}
|
}
|
||||||
|
|
||||||
entries.append(.peer(index, renderedParticipant, ContactsPeerItemEditing(editable: false, editing: false, revealed: false), label, enabled))
|
entries.append(.peer(index, renderedParticipant, ContactsPeerItemEditing(editable: false, editing: false, revealed: false), label, enabled, false, false))
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,6 +428,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
|||||||
strongSelf.enqueueTransition(preparedTransition(from: previous, to: entries, context: context, presentationData: strongSelf.presentationData, nameSortOrder: strongSelf.presentationData.nameSortOrder, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder, interaction: interaction))
|
strongSelf.enqueueTransition(preparedTransition(from: previous, to: entries, context: context, presentationData: strongSelf.presentationData, nameSortOrder: strongSelf.presentationData.nameSortOrder, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder, interaction: interaction))
|
||||||
})
|
})
|
||||||
disposableAndLoadMoreControl = (disposable, nil)
|
disposableAndLoadMoreControl = (disposable, nil)
|
||||||
|
contactsDisposableAndLoadMoreControl = nil
|
||||||
} else {
|
} else {
|
||||||
let membersState = Promise<ChannelMemberListState>()
|
let membersState = Promise<ChannelMemberListState>()
|
||||||
|
|
||||||
@ -427,19 +436,26 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
|||||||
membersState.set(.single(state))
|
membersState.set(.single(state))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let contactsState = Promise<ChannelMemberListState>()
|
||||||
|
contactsDisposableAndLoadMoreControl = context.peerChannelMemberCategoriesContextsManager.contacts(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, searchQuery: nil, updated: { state in
|
||||||
|
contactsState.set(.single(state))
|
||||||
|
})
|
||||||
|
|
||||||
additionalDisposable.set((combineLatest(queue: .mainQueue(),
|
additionalDisposable.set((combineLatest(queue: .mainQueue(),
|
||||||
membersState.get(),
|
membersState.get(),
|
||||||
|
contactsState.get(),
|
||||||
context.account.postbox.peerView(id: peerId),
|
context.account.postbox.peerView(id: peerId),
|
||||||
context.engine.data.subscribe(
|
context.engine.data.subscribe(
|
||||||
TelegramEngine.EngineData.Item.Contacts.List(includePresences: true)
|
TelegramEngine.EngineData.Item.Contacts.List(includePresences: true)
|
||||||
)
|
)
|
||||||
).start(next: { [weak self] state, peerView, contactsView in
|
).start(next: { [weak self] state, contactsState, peerView, contactsView in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var entries: [ChannelMembersSearchEntry] = []
|
var entries: [ChannelMembersSearchEntry] = []
|
||||||
|
|
||||||
var canInviteByLink = false
|
var canInviteByLink = false
|
||||||
|
var isChannel = false
|
||||||
if let peer = peerViewMainPeer(peerView) {
|
if let peer = peerViewMainPeer(peerView) {
|
||||||
if !(peer.addressName?.isEmpty ?? true) {
|
if !(peer.addressName?.isEmpty ?? true) {
|
||||||
canInviteByLink = true
|
canInviteByLink = true
|
||||||
@ -447,6 +463,9 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
|||||||
if peer.flags.contains(.isCreator) || (peer.adminRights?.rights.contains(.canInviteUsers) == true) {
|
if peer.flags.contains(.isCreator) || (peer.adminRights?.rights.contains(.canInviteUsers) == true) {
|
||||||
canInviteByLink = true
|
canInviteByLink = true
|
||||||
}
|
}
|
||||||
|
if case .broadcast = peer.info {
|
||||||
|
isChannel = true
|
||||||
|
}
|
||||||
} else if let peer = peer as? TelegramGroup {
|
} else if let peer = peer as? TelegramGroup {
|
||||||
if case .creator = peer.role {
|
if case .creator = peer.role {
|
||||||
canInviteByLink = true
|
canInviteByLink = true
|
||||||
@ -456,6 +475,8 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var index = 0
|
||||||
|
var existingPeersIds = Set<PeerId>()
|
||||||
if case .inviteToCall = mode, canInviteByLink, !filters.contains(where: { filter in
|
if case .inviteToCall = mode, canInviteByLink, !filters.contains(where: { filter in
|
||||||
if case .excludeNonMembers = filter {
|
if case .excludeNonMembers = filter {
|
||||||
return true
|
return true
|
||||||
@ -464,18 +485,53 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
entries.append(.copyInviteLink)
|
entries.append(.copyInviteLink)
|
||||||
|
} else {
|
||||||
|
contactsLoop: for participant in contactsState.list {
|
||||||
|
if participant.peer.isDeleted {
|
||||||
|
continue contactsLoop
|
||||||
|
}
|
||||||
|
|
||||||
|
var label: String?
|
||||||
|
var enabled = true
|
||||||
|
for filter in filters {
|
||||||
|
switch filter {
|
||||||
|
case let .exclude(ids):
|
||||||
|
if ids.contains(participant.peer.id) {
|
||||||
|
continue contactsLoop
|
||||||
|
}
|
||||||
|
case let .disable(ids):
|
||||||
|
if ids.contains(participant.peer.id) {
|
||||||
|
enabled = false
|
||||||
|
}
|
||||||
|
case .excludeNonMembers:
|
||||||
|
break
|
||||||
|
case .excludeBots:
|
||||||
|
if let user = participant.peer as? TelegramUser, user.botInfo != nil {
|
||||||
|
continue contactsLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if case .promote = mode, case .creator = participant.participant {
|
||||||
|
label = strongSelf.presentationData.strings.Channel_Management_LabelOwner
|
||||||
|
enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.append(.peer(index, participant, ContactsPeerItemEditing(editable: false, editing: false, revealed: false), label, enabled, isChannel, true))
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
existingPeersIds.insert(participant.peer.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var index = 0
|
|
||||||
participantsLoop: for participant in state.list {
|
participantsLoop: for participant in state.list {
|
||||||
if participant.peer.isDeleted {
|
if participant.peer.isDeleted || existingPeersIds.contains(participant.peer.id) {
|
||||||
continue participantsLoop
|
continue participantsLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
var label: String?
|
var label: String?
|
||||||
var enabled = true
|
var enabled = true
|
||||||
switch mode {
|
switch mode {
|
||||||
case .ban:
|
case .ban, .promote:
|
||||||
if participant.peer.id == context.account.peerId {
|
if participant.peer.id == context.account.peerId {
|
||||||
continue participantsLoop
|
continue participantsLoop
|
||||||
}
|
}
|
||||||
@ -497,29 +553,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case .promote:
|
if case .promote = mode, case .creator = participant.participant {
|
||||||
if participant.peer.id == context.account.peerId {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for filter in filters {
|
|
||||||
switch filter {
|
|
||||||
case let .exclude(ids):
|
|
||||||
if ids.contains(participant.peer.id) {
|
|
||||||
continue participantsLoop
|
|
||||||
}
|
|
||||||
case let .disable(ids):
|
|
||||||
if ids.contains(participant.peer.id) {
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
case .excludeNonMembers:
|
|
||||||
break
|
|
||||||
case .excludeBots:
|
|
||||||
if let user = participant.peer as? TelegramUser, user.botInfo != nil {
|
|
||||||
continue participantsLoop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if case .creator = participant.participant {
|
|
||||||
label = strongSelf.presentationData.strings.Channel_Management_LabelOwner
|
label = strongSelf.presentationData.strings.Channel_Management_LabelOwner
|
||||||
enabled = false
|
enabled = false
|
||||||
}
|
}
|
||||||
@ -549,7 +583,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entries.append(.peer(index, participant, ContactsPeerItemEditing(editable: false, editing: false, revealed: false), label, enabled))
|
entries.append(.peer(index, participant, ContactsPeerItemEditing(editable: false, editing: false, revealed: false), label, enabled, isChannel, false))
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -575,6 +609,9 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
|
|||||||
let combinedDisposable = DisposableSet()
|
let combinedDisposable = DisposableSet()
|
||||||
combinedDisposable.add(disposableAndLoadMoreControl.0)
|
combinedDisposable.add(disposableAndLoadMoreControl.0)
|
||||||
combinedDisposable.add(additionalDisposable)
|
combinedDisposable.add(additionalDisposable)
|
||||||
|
if let disposable = contactsDisposableAndLoadMoreControl?.0 {
|
||||||
|
combinedDisposable.add(disposable)
|
||||||
|
}
|
||||||
|
|
||||||
self.disposable = combinedDisposable
|
self.disposable = combinedDisposable
|
||||||
self.listControl = disposableAndLoadMoreControl.1
|
self.listControl = disposableAndLoadMoreControl.1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user