mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
661 lines
26 KiB
Swift
661 lines
26 KiB
Swift
import Foundation
|
|
import TelegramCore
|
|
import Postbox
|
|
import SwiftSignalKit
|
|
|
|
private let initialBatchSize: Int32 = 64
|
|
private let defaultEmptyTimeout: Double = 2.0 * 60.0
|
|
private let headUpdateTimeout: Double = 30.0
|
|
private let requestBatchSize: Int32 = 64
|
|
|
|
enum ChannelMemberListLoadingState: Equatable {
|
|
case loading(initial: Bool)
|
|
case ready(hasMore: Bool)
|
|
}
|
|
|
|
extension ChannelParticipant {
|
|
var adminInfo: ChannelParticipantAdminInfo? {
|
|
switch self {
|
|
case .creator:
|
|
return nil
|
|
case let .member(_, _, adminInfo, _):
|
|
return adminInfo
|
|
}
|
|
}
|
|
|
|
var banInfo: ChannelParticipantBannedInfo? {
|
|
switch self {
|
|
case .creator:
|
|
return nil
|
|
case let .member(_, _, _, banInfo):
|
|
return banInfo
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ChannelMemberListState {
|
|
let list: [RenderedChannelParticipant]
|
|
let loadingState: ChannelMemberListLoadingState
|
|
|
|
func withUpdatedList(_ list: [RenderedChannelParticipant]) -> ChannelMemberListState {
|
|
return ChannelMemberListState(list: list, loadingState: self.loadingState)
|
|
}
|
|
|
|
func withUpdatedLoadingState(_ loadingState: ChannelMemberListLoadingState) -> ChannelMemberListState {
|
|
return ChannelMemberListState(list: self.list, loadingState: loadingState)
|
|
}
|
|
}
|
|
|
|
enum ChannelMemberListCategory {
|
|
case recent
|
|
case recentSearch(String)
|
|
case admins(String?)
|
|
case restricted(String?)
|
|
case banned(String?)
|
|
}
|
|
|
|
private protocol ChannelMemberCategoryListContext {
|
|
var listStateValue: ChannelMemberListState { get }
|
|
var listState: Signal<ChannelMemberListState, NoError> { get }
|
|
func loadMore()
|
|
func reset(_ force: Bool)
|
|
func replayUpdates(_ updates: [(ChannelParticipant?, RenderedChannelParticipant?, Bool?)])
|
|
func forceUpdateHead()
|
|
}
|
|
|
|
private func isParticipantMember(_ participant: ChannelParticipant, infoIsMember: Bool?) -> Bool {
|
|
if let banInfo = participant.banInfo {
|
|
return !banInfo.rights.flags.contains(.banReadMessages) && banInfo.isMember
|
|
} else if let infoIsMember = infoIsMember {
|
|
return infoIsMember
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
private final class ChannelMemberSingleCategoryListContext: ChannelMemberCategoryListContext {
|
|
private let postbox: Postbox
|
|
private let network: Network
|
|
private let accountPeerId: PeerId
|
|
private let peerId: PeerId
|
|
private let category: ChannelMemberListCategory
|
|
|
|
var listStateValue: ChannelMemberListState {
|
|
didSet {
|
|
self.listStatePromise.set(.single(self.listStateValue))
|
|
if case .admins(nil) = self.category, case .ready = self.listStateValue.loadingState {
|
|
let ids: Set<PeerId> = Set(self.listStateValue.list.map { $0.peer.id })
|
|
let previousIds: Set<PeerId> = Set(oldValue.list.map { $0.peer.id })
|
|
if ids != previousIds {
|
|
let _ = updateCachedChannelAdminIds(postbox: self.postbox, peerId: self.peerId, ids: ids).start()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private var listStatePromise: Promise<ChannelMemberListState>
|
|
var listState: Signal<ChannelMemberListState, NoError> {
|
|
return self.listStatePromise.get()
|
|
}
|
|
|
|
private let loadingDisposable = MetaDisposable()
|
|
private let headUpdateDisposable = MetaDisposable()
|
|
|
|
private var headUpdateTimer: SwiftSignalKit.Timer?
|
|
|
|
init(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, category: ChannelMemberListCategory) {
|
|
self.postbox = postbox
|
|
self.network = network
|
|
self.accountPeerId = accountPeerId
|
|
self.peerId = peerId
|
|
self.category = category
|
|
|
|
self.listStateValue = ChannelMemberListState(list: [], loadingState: .ready(hasMore: true))
|
|
self.listStatePromise = Promise(self.listStateValue)
|
|
self.loadMoreInternal(initial: true)
|
|
}
|
|
|
|
deinit {
|
|
self.loadingDisposable.dispose()
|
|
self.headUpdateDisposable.dispose()
|
|
self.headUpdateTimer?.invalidate()
|
|
}
|
|
|
|
func loadMore() {
|
|
self.loadMoreInternal(initial: false)
|
|
}
|
|
|
|
private func loadMoreInternal(initial: Bool) {
|
|
guard case .ready(true) = self.listStateValue.loadingState else {
|
|
return
|
|
}
|
|
|
|
let loadCount: Int32
|
|
if case .ready(true) = self.listStateValue.loadingState, self.listStateValue.list.isEmpty {
|
|
loadCount = initialBatchSize
|
|
} else {
|
|
loadCount = requestBatchSize
|
|
}
|
|
|
|
self.listStateValue = self.listStateValue.withUpdatedLoadingState(.loading(initial: initial))
|
|
|
|
self.loadingDisposable.set((self.loadMoreSignal(count: loadCount)
|
|
|> deliverOnMainQueue).start(next: { [weak self] members in
|
|
self?.appendMembersAndFinishLoading(members)
|
|
}))
|
|
}
|
|
|
|
func reset(_ force: Bool) {
|
|
if case .loading = self.listStateValue.loadingState, self.listStateValue.list.isEmpty {
|
|
} else {
|
|
var list = self.listStateValue.list
|
|
var loadingState: ChannelMemberListLoadingState = .ready(hasMore: true)
|
|
if list.count > Int(initialBatchSize) && !force {
|
|
list.removeSubrange(Int(initialBatchSize) ..< list.count)
|
|
loadingState = .ready(hasMore: true)
|
|
}
|
|
|
|
self.loadingDisposable.set(nil)
|
|
self.listStateValue = self.listStateValue.withUpdatedLoadingState(loadingState).withUpdatedList(list)
|
|
}
|
|
}
|
|
|
|
|
|
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 let .admins(query):
|
|
requestCategory = .admins
|
|
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, accountPeerId: self.accountPeerId, 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
|
|
}
|
|
}
|
|
|
|
private func loadMoreSignal(count: Int32) -> Signal<[RenderedChannelParticipant], NoError> {
|
|
return self.loadSignal(offset: Int32(self.listStateValue.list.count), count: count, hash: 0)
|
|
|> map { value -> [RenderedChannelParticipant] in
|
|
return value ?? []
|
|
}
|
|
}
|
|
|
|
private func updateHeadMembers(_ headMembers: [RenderedChannelParticipant]?) {
|
|
if let headMembers = headMembers {
|
|
var existingIds = Set<PeerId>()
|
|
var list = headMembers
|
|
for member in list {
|
|
existingIds.insert(member.peer.id)
|
|
}
|
|
for member in self.listStateValue.list {
|
|
if !existingIds.contains(member.peer.id) {
|
|
list.append(member)
|
|
}
|
|
}
|
|
self.loadingDisposable.set(nil)
|
|
self.listStateValue = self.listStateValue.withUpdatedList(list)
|
|
if case .loading = self.listStateValue.loadingState {
|
|
self.loadMore()
|
|
}
|
|
}
|
|
|
|
self.headUpdateTimer?.invalidate()
|
|
self.headUpdateTimer = nil
|
|
self.checkUpdateHead()
|
|
}
|
|
|
|
private func appendMembersAndFinishLoading(_ members: [RenderedChannelParticipant]) {
|
|
var firstLoad = false
|
|
if case .loading = self.listStateValue.loadingState, self.listStateValue.list.isEmpty {
|
|
firstLoad = true
|
|
}
|
|
var existingIds = Set<PeerId>()
|
|
var list = self.listStateValue.list
|
|
for member in list {
|
|
existingIds.insert(member.peer.id)
|
|
}
|
|
for member in members {
|
|
if !existingIds.contains(member.peer.id) {
|
|
list.append(member)
|
|
}
|
|
}
|
|
self.listStateValue = self.listStateValue.withUpdatedList(list).withUpdatedLoadingState(.ready(hasMore: members.count >= requestBatchSize))
|
|
if firstLoad {
|
|
self.checkUpdateHead()
|
|
}
|
|
}
|
|
|
|
func forceUpdateHead() {
|
|
self.headUpdateTimer = nil
|
|
self.checkUpdateHead()
|
|
}
|
|
|
|
private func checkUpdateHead() {
|
|
if self.listStateValue.list.isEmpty {
|
|
return
|
|
}
|
|
|
|
if self.headUpdateTimer == nil {
|
|
let headUpdateTimer = SwiftSignalKit.Timer(timeout: headUpdateTimeout, repeat: false, completion: { [weak self] in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
|
|
var hash: UInt32 = 0
|
|
|
|
for i in 0 ..< min(strongSelf.listStateValue.list.count, Int(initialBatchSize)) {
|
|
let peerId = strongSelf.listStateValue.list[i].peer.id
|
|
hash = (hash &* 20261) &+ UInt32(peerId.id)
|
|
}
|
|
hash = hash % 0x7FFFFFFF
|
|
strongSelf.headUpdateDisposable.set((strongSelf.loadSignal(offset: 0, count: initialBatchSize, hash: Int32(bitPattern: hash))
|
|
|> deliverOnMainQueue).start(next: { members in
|
|
self?.updateHeadMembers(members)
|
|
}))
|
|
}, queue: Queue.mainQueue())
|
|
self.headUpdateTimer = headUpdateTimer
|
|
headUpdateTimer.start()
|
|
}
|
|
}
|
|
|
|
fileprivate func replayUpdates(_ updates: [(ChannelParticipant?, RenderedChannelParticipant?, Bool?)]) {
|
|
var list = self.listStateValue.list
|
|
var updatedList = false
|
|
for (maybePrevious, updated, infoIsMember) in updates {
|
|
var previous: ChannelParticipant? = maybePrevious
|
|
if let participantId = maybePrevious?.peerId ?? updated?.peer.id {
|
|
inner: for participant in list {
|
|
if participant.peer.id == participantId {
|
|
previous = participant.participant
|
|
break inner
|
|
}
|
|
}
|
|
}
|
|
switch self.category {
|
|
case let .admins(query):
|
|
if let updated = updated, let _ = updated.participant.adminInfo, (query == nil || updated.peer.indexName.matchesByTokens(query!)) {
|
|
var found = false
|
|
loop: for i in 0 ..< list.count {
|
|
if list[i].peer.id == updated.peer.id {
|
|
list[i] = updated
|
|
found = true
|
|
updatedList = true
|
|
break loop
|
|
}
|
|
}
|
|
if !found {
|
|
list.insert(updated, at: 0)
|
|
updatedList = true
|
|
}
|
|
} else if let previous = previous, let _ = previous.adminInfo {
|
|
loop: for i in 0 ..< list.count {
|
|
if list[i].peer.id == previous.peerId {
|
|
list.remove(at: i)
|
|
updatedList = true
|
|
break loop
|
|
}
|
|
}
|
|
}
|
|
case .restricted:
|
|
if let updated = updated, let banInfo = updated.participant.banInfo, !banInfo.rights.flags.contains(.banReadMessages) {
|
|
var found = false
|
|
loop: for i in 0 ..< list.count {
|
|
if list[i].peer.id == updated.peer.id {
|
|
list[i] = updated
|
|
found = true
|
|
updatedList = true
|
|
break loop
|
|
}
|
|
}
|
|
if !found {
|
|
list.insert(updated, at: 0)
|
|
updatedList = true
|
|
}
|
|
} else if let previous = previous, let banInfo = previous.banInfo, !banInfo.rights.flags.contains(.banReadMessages) {
|
|
loop: for i in 0 ..< list.count {
|
|
if list[i].peer.id == previous.peerId {
|
|
list.remove(at: i)
|
|
updatedList = true
|
|
break loop
|
|
}
|
|
}
|
|
}
|
|
case .banned:
|
|
if let updated = updated, let banInfo = updated.participant.banInfo, banInfo.rights.flags.contains(.banReadMessages) {
|
|
var found = false
|
|
loop: for i in 0 ..< list.count {
|
|
if list[i].peer.id == updated.peer.id {
|
|
list[i] = updated
|
|
found = true
|
|
updatedList = true
|
|
break loop
|
|
}
|
|
}
|
|
if !found {
|
|
list.insert(updated, at: 0)
|
|
updatedList = true
|
|
}
|
|
} else if let previous = previous, let banInfo = previous.banInfo, banInfo.rights.flags.contains(.banReadMessages) {
|
|
loop: for i in 0 ..< list.count {
|
|
if list[i].peer.id == previous.peerId {
|
|
list.remove(at: i)
|
|
updatedList = true
|
|
break loop
|
|
}
|
|
}
|
|
}
|
|
case .recent:
|
|
if let updated = updated, isParticipantMember(updated.participant, infoIsMember: infoIsMember) {
|
|
var found = false
|
|
loop: for i in 0 ..< list.count {
|
|
if list[i].peer.id == updated.peer.id {
|
|
list[i] = updated
|
|
found = true
|
|
updatedList = true
|
|
break loop
|
|
}
|
|
}
|
|
if !found {
|
|
list.insert(updated, at: 0)
|
|
updatedList = true
|
|
}
|
|
} else if let previous = previous, isParticipantMember(previous, infoIsMember: nil) {
|
|
loop: for i in 0 ..< list.count {
|
|
if list[i].peer.id == previous.peerId {
|
|
list.remove(at: i)
|
|
updatedList = true
|
|
break loop
|
|
}
|
|
}
|
|
}
|
|
case let .recentSearch(query):
|
|
if let updated = updated, isParticipantMember(updated.participant, infoIsMember: infoIsMember), updated.peer.indexName.matchesByTokens(query) {
|
|
var found = false
|
|
loop: for i in 0 ..< list.count {
|
|
if list[i].peer.id == updated.peer.id {
|
|
list[i] = updated
|
|
found = true
|
|
updatedList = true
|
|
break loop
|
|
}
|
|
}
|
|
if !found {
|
|
list.insert(updated, at: 0)
|
|
updatedList = true
|
|
}
|
|
} else if let previous = previous, isParticipantMember(previous, infoIsMember: nil) {
|
|
loop: for i in 0 ..< list.count {
|
|
if list[i].peer.id == previous.peerId {
|
|
list.remove(at: i)
|
|
updatedList = true
|
|
break loop
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if updatedList {
|
|
self.listStateValue = self.listStateValue.withUpdatedList(list)
|
|
}
|
|
}
|
|
}
|
|
|
|
private final class ChannelMemberMultiCategoryListContext: ChannelMemberCategoryListContext {
|
|
private var contexts: [ChannelMemberSingleCategoryListContext] = []
|
|
|
|
var listStateValue: ChannelMemberListState {
|
|
return ChannelMemberMultiCategoryListContext.reduceListStates(self.contexts.map { $0.listStateValue })
|
|
}
|
|
|
|
private static func reduceListStates(_ listStates: [ChannelMemberListState]) -> ChannelMemberListState {
|
|
var allReady = true
|
|
for listState in listStates {
|
|
if case .loading(true) = listState.loadingState, listState.list.isEmpty {
|
|
allReady = false
|
|
break
|
|
}
|
|
}
|
|
if !allReady {
|
|
return ChannelMemberListState(list: [], loadingState: .loading(initial: true))
|
|
}
|
|
|
|
var list: [RenderedChannelParticipant] = []
|
|
var existingIds = Set<PeerId>()
|
|
var loadingState: ChannelMemberListLoadingState = .ready(hasMore: false)
|
|
loop: for i in 0 ..< listStates.count {
|
|
for item in listStates[i].list {
|
|
if !existingIds.contains(item.peer.id) {
|
|
existingIds.insert(item.peer.id)
|
|
list.append(item)
|
|
}
|
|
}
|
|
switch listStates[i].loadingState {
|
|
case let .loading(initial):
|
|
loadingState = .loading(initial: initial)
|
|
break loop
|
|
case let .ready(hasMore):
|
|
if hasMore {
|
|
loadingState = .ready(hasMore: true)
|
|
break loop
|
|
}
|
|
}
|
|
}
|
|
return ChannelMemberListState(list: list, loadingState: loadingState)
|
|
}
|
|
|
|
var listState: Signal<ChannelMemberListState, NoError> {
|
|
let signals: [Signal<ChannelMemberListState, NoError>] = self.contexts.map { context in
|
|
return context.listState
|
|
}
|
|
return combineLatest(signals) |> map { listStates -> ChannelMemberListState in
|
|
return ChannelMemberMultiCategoryListContext.reduceListStates(listStates)
|
|
}
|
|
}
|
|
|
|
init(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, categories: [ChannelMemberListCategory]) {
|
|
self.contexts = categories.map { category in
|
|
return ChannelMemberSingleCategoryListContext(postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, category: category)
|
|
}
|
|
}
|
|
|
|
func loadMore() {
|
|
loop: for context in self.contexts {
|
|
switch context.listStateValue.loadingState {
|
|
case .loading:
|
|
break loop
|
|
case let .ready(hasMore):
|
|
if hasMore {
|
|
context.loadMore()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func reset(_ force: Bool) {
|
|
for context in self.contexts {
|
|
context.reset(force)
|
|
}
|
|
}
|
|
|
|
func forceUpdateHead() {
|
|
for context in self.contexts {
|
|
context.forceUpdateHead()
|
|
}
|
|
}
|
|
|
|
func replayUpdates(_ updates: [(ChannelParticipant?, RenderedChannelParticipant?, Bool?)]) {
|
|
for context in self.contexts {
|
|
context.replayUpdates(updates)
|
|
}
|
|
}
|
|
}
|
|
|
|
struct PeerChannelMemberCategoryControl {
|
|
fileprivate let key: PeerChannelMemberContextKey
|
|
}
|
|
|
|
private final class PeerChannelMemberContextWithSubscribers {
|
|
let context: ChannelMemberCategoryListContext
|
|
private let emptyTimeout: Double
|
|
private let subscribers = Bag<(ChannelMemberListState) -> Void>()
|
|
private let disposable = MetaDisposable()
|
|
private let becameEmpty: () -> Void
|
|
|
|
private var emptyTimer: SwiftSignalKit.Timer?
|
|
|
|
init(context: ChannelMemberCategoryListContext, emptyTimeout: Double, becameEmpty: @escaping () -> Void) {
|
|
self.context = context
|
|
self.emptyTimeout = emptyTimeout
|
|
self.becameEmpty = becameEmpty
|
|
self.disposable.set((context.listState
|
|
|> deliverOnMainQueue).start(next: { [weak self] value in
|
|
if let strongSelf = self {
|
|
for f in strongSelf.subscribers.copyItems() {
|
|
f(value)
|
|
}
|
|
}
|
|
}))
|
|
}
|
|
|
|
deinit {
|
|
self.disposable.dispose()
|
|
self.emptyTimer?.invalidate()
|
|
}
|
|
|
|
private func resetAndBeginEmptyTimer() {
|
|
self.context.reset(false)
|
|
self.emptyTimer?.invalidate()
|
|
let emptyTimer = SwiftSignalKit.Timer(timeout: self.emptyTimeout, repeat: false, completion: { [weak self] in
|
|
if let strongSelf = self {
|
|
if strongSelf.subscribers.isEmpty {
|
|
strongSelf.becameEmpty()
|
|
}
|
|
}
|
|
}, queue: Queue.mainQueue())
|
|
self.emptyTimer = emptyTimer
|
|
emptyTimer.start()
|
|
}
|
|
|
|
func subscribe(requestUpdate: Bool, updated: @escaping (ChannelMemberListState) -> Void) -> Disposable {
|
|
let wasEmpty = self.subscribers.isEmpty
|
|
let index = self.subscribers.add(updated)
|
|
updated(self.context.listStateValue)
|
|
if wasEmpty {
|
|
self.emptyTimer?.invalidate()
|
|
if requestUpdate {
|
|
self.context.forceUpdateHead()
|
|
}
|
|
}
|
|
return ActionDisposable { [weak self] in
|
|
Queue.mainQueue().async {
|
|
if let strongSelf = self {
|
|
strongSelf.subscribers.remove(index)
|
|
if strongSelf.subscribers.isEmpty {
|
|
strongSelf.resetAndBeginEmptyTimer()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
final class PeerChannelMemberCategoriesContext {
|
|
private let postbox: Postbox
|
|
private let network: Network
|
|
private let accountPeerId: PeerId
|
|
private let peerId: PeerId
|
|
private var becameEmpty: (Bool) -> Void
|
|
|
|
private var contexts: [PeerChannelMemberContextKey: PeerChannelMemberContextWithSubscribers] = [:]
|
|
|
|
init(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, becameEmpty: @escaping (Bool) -> Void) {
|
|
self.postbox = postbox
|
|
self.network = network
|
|
self.accountPeerId = accountPeerId
|
|
self.peerId = peerId
|
|
self.becameEmpty = becameEmpty
|
|
}
|
|
|
|
func reset(_ key: PeerChannelMemberContextKey) {
|
|
for (contextKey, context) in contexts {
|
|
if contextKey == key {
|
|
context.context.reset(true)
|
|
context.context.loadMore()
|
|
}
|
|
}
|
|
}
|
|
|
|
func getContext(key: PeerChannelMemberContextKey, requestUpdate: Bool, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl) {
|
|
assert(Queue.mainQueue().isCurrent())
|
|
if let current = self.contexts[key] {
|
|
return (current.subscribe(requestUpdate: requestUpdate, updated: updated), PeerChannelMemberCategoryControl(key: key))
|
|
}
|
|
let context: ChannelMemberCategoryListContext
|
|
let emptyTimeout: Double
|
|
switch key {
|
|
case .admins(nil), .banned(nil), .recentSearch(nil), .restricted(nil), .restrictedAndBanned(nil):
|
|
emptyTimeout = defaultEmptyTimeout
|
|
default:
|
|
emptyTimeout = 0.0
|
|
}
|
|
switch key {
|
|
case .recent, .recentSearch, .admins:
|
|
let mappedCategory: ChannelMemberListCategory
|
|
switch key {
|
|
case .recent:
|
|
mappedCategory = .recent
|
|
case let .recentSearch(query):
|
|
mappedCategory = .recentSearch(query)
|
|
case let .admins(query):
|
|
mappedCategory = .admins(query)
|
|
default:
|
|
mappedCategory = .recent
|
|
}
|
|
context = ChannelMemberSingleCategoryListContext(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, peerId: self.peerId, category: mappedCategory)
|
|
case let .restrictedAndBanned(query):
|
|
context = ChannelMemberMultiCategoryListContext(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, peerId: self.peerId, categories: [.restricted(query), .banned(query)])
|
|
case let .restricted(query):
|
|
context = ChannelMemberSingleCategoryListContext(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, peerId: self.peerId, category: .restricted(query))
|
|
case let .banned(query):
|
|
context = ChannelMemberSingleCategoryListContext(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, peerId: self.peerId, category: .banned(query))
|
|
}
|
|
let contextWithSubscribers = PeerChannelMemberContextWithSubscribers(context: context, emptyTimeout: emptyTimeout, becameEmpty: { [weak self] in
|
|
assert(Queue.mainQueue().isCurrent())
|
|
if let strongSelf = self {
|
|
strongSelf.contexts.removeValue(forKey: key)
|
|
}
|
|
})
|
|
self.contexts[key] = contextWithSubscribers
|
|
return (contextWithSubscribers.subscribe(requestUpdate: requestUpdate, updated: updated), PeerChannelMemberCategoryControl(key: key))
|
|
}
|
|
|
|
func loadMore(_ control: PeerChannelMemberCategoryControl) {
|
|
assert(Queue.mainQueue().isCurrent())
|
|
if let context = self.contexts[control.key] {
|
|
context.context.loadMore()
|
|
}
|
|
}
|
|
|
|
func replayUpdates(_ updates: [(ChannelParticipant?, RenderedChannelParticipant?, Bool?)]) {
|
|
for (_, context) in self.contexts {
|
|
context.context.replayUpdates(updates)
|
|
}
|
|
}
|
|
}
|