Improved online member count in large groups

This commit is contained in:
Peter 2019-04-02 18:49:29 +04:00
parent a628968ffb
commit 19431267a3
2 changed files with 161 additions and 50 deletions

View File

@ -1306,7 +1306,23 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
peerView.set(context.account.viewTracker.peerView(peerId)) peerView.set(context.account.viewTracker.peerView(peerId))
var onlineMemberCount: Signal<Int32?, NoError> = .single(nil) var onlineMemberCount: Signal<Int32?, NoError> = .single(nil)
if peerId.namespace == Namespaces.Peer.CloudChannel { if peerId.namespace == Namespaces.Peer.CloudChannel {
onlineMemberCount = context.peerChannelMemberCategoriesContextsManager.recentOnline(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId) let recentOnlineSignal: Signal<Int32, NoError> = context.account.viewTracker.peerView(peerId)
|> map { view -> Bool in
if let cachedData = view.cachedData as? CachedChannelData, let memberCount = cachedData.participantsSummary.memberCount, memberCount > 50 {
return true
} else {
return false
}
}
|> distinctUntilChanged
|> mapToSignal { isLarge -> Signal<Int32, NoError> in
if isLarge {
return context.peerChannelMemberCategoriesContextsManager.recentOnline(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId)
} else {
return context.peerChannelMemberCategoriesContextsManager.recentOnlineSmall(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId)
}
}
onlineMemberCount = recentOnlineSignal
|> map(Optional.init) |> map(Optional.init)
} }
self.peerDisposable.set((combineLatest(queue: Queue.mainQueue(), peerView.get(), onlineMemberCount) self.peerDisposable.set((combineLatest(queue: Queue.mainQueue(), peerView.get(), onlineMemberCount)

View File

@ -29,8 +29,20 @@ enum PeerChannelMemberContextKey: Equatable, Hashable {
} }
} }
private final class PeerChannelMembersOnlineContext {
let subscribers = Bag<(Int32) -> Void>()
let disposable: Disposable
var value: Int32?
var emptyTimer: SwiftSignalKit.Timer?
init(disposable: Disposable) {
self.disposable = disposable
}
}
private final class PeerChannelMemberCategoriesContextsManagerImpl { private final class PeerChannelMemberCategoriesContextsManagerImpl {
fileprivate var contexts: [PeerId: PeerChannelMemberCategoriesContext] = [:] fileprivate var contexts: [PeerId: PeerChannelMemberCategoriesContext] = [:]
fileprivate var onlineContexts: [PeerId: PeerChannelMembersOnlineContext] = [:]
func getContext(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, key: PeerChannelMemberContextKey, requestUpdate: Bool, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl) { func getContext(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, key: PeerChannelMemberContextKey, requestUpdate: Bool, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl) {
if let current = self.contexts[peerId] { if let current = self.contexts[peerId] {
@ -53,6 +65,70 @@ private final class PeerChannelMemberCategoriesContextsManagerImpl {
} }
} }
func recentOnline(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, updated: @escaping (Int32) -> Void) -> Disposable {
let context: PeerChannelMembersOnlineContext
if let current = self.onlineContexts[peerId] {
context = current
} else {
let disposable = MetaDisposable()
context = PeerChannelMembersOnlineContext(disposable: disposable)
self.onlineContexts[peerId] = context
let signal = (
chatOnlineMembers(postbox: postbox, network: network, peerId: peerId)
|> then(
.complete()
|> delay(30.0, queue: .mainQueue())
)
) |> restart
disposable.set(signal.start(next: { [weak context] value in
guard let context = context else {
return
}
context.value = value
for f in context.subscribers.copyItems() {
f(value)
}
}))
}
if let emptyTimer = context.emptyTimer {
emptyTimer.invalidate()
context.emptyTimer = nil
}
let index = context.subscribers.add({ next in
updated(next)
})
updated(context.value ?? 0)
return ActionDisposable { [weak self, weak context] in
Queue.mainQueue().async {
guard let strongSelf = self else {
return
}
if let current = strongSelf.onlineContexts[peerId], let context = context, current === context {
current.subscribers.remove(index)
if current.subscribers.isEmpty {
if current.emptyTimer == nil {
let timer = SwiftSignalKit.Timer(timeout: 60.0, repeat: false, completion: { [weak context] in
if let current = strongSelf.onlineContexts[peerId], let context = context, current === context {
if current.subscribers.isEmpty {
strongSelf.onlineContexts.removeValue(forKey: peerId)
current.disposable.dispose()
}
}
}, queue: Queue.mainQueue())
current.emptyTimer = timer
timer.start()
}
}
}
}
}
}
func loadMore(peerId: PeerId, control: PeerChannelMemberCategoryControl) { func loadMore(peerId: PeerId, control: PeerChannelMemberCategoryControl) {
if let context = self.contexts[peerId] { if let context = self.contexts[peerId] {
context.loadMore(control) context.loadMore(control)
@ -108,55 +184,7 @@ final class PeerChannelMemberCategoriesContextsManager {
return self.getContext(postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, key: key, requestUpdate: requestUpdate, updated: updated) return self.getContext(postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, key: key, requestUpdate: requestUpdate, updated: updated)
} }
func recentOnline(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId) -> Signal<Int32, NoError> {
return Signal { [weak self] subscriber in
var previousIds: Set<PeerId>?
let statusesDisposable = MetaDisposable()
let disposableAndControl = self?.recent(postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, updated: { state in
var idList: [PeerId] = []
for item in state.list {
idList.append(item.peer.id)
if idList.count >= 200 {
break
}
}
let updatedIds = Set(idList)
if previousIds != updatedIds {
previousIds = updatedIds
let key: PostboxViewKey = .peerPresences(peerIds: updatedIds)
statusesDisposable.set((postbox.combinedView(keys: [key])
|> map { view -> Int32 in
var count: Int32 = 0
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
if let presences = (view.views[key] as? PeerPresencesView)?.presences {
for (_, presence) in presences {
if let presence = presence as? TelegramUserPresence {
let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: Int32(timestamp))
switch relativeStatus {
case .online:
count += 1
default:
break
}
}
}
}
return count
}
|> distinctUntilChanged
|> deliverOnMainQueue).start(next: { count in
subscriber.putNext(count)
}))
}
})
return ActionDisposable {
disposableAndControl?.0.dispose()
statusesDisposable.dispose()
}
}
|> runOn(Queue.mainQueue())
}
func admins(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, searchQuery: String? = nil, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl?) { func admins(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, searchQuery: String? = nil, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl?) {
return self.getContext(postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, key: .admins(searchQuery), requestUpdate: true, updated: updated) return self.getContext(postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, key: .admins(searchQuery), requestUpdate: true, updated: updated)
@ -285,4 +313,71 @@ final class PeerChannelMemberCategoriesContextsManager {
return .single(Void()) return .single(Void())
}*/ }*/
} }
func recentOnline(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId) -> Signal<Int32, NoError> {
return Signal { [weak self] subscriber in
guard let strongSelf = self else {
subscriber.putNext(0)
subscriber.putCompletion()
return EmptyDisposable
}
let disposable = strongSelf.impl.syncWith({ impl -> Disposable in
return impl.recentOnline(postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, updated: { value in
subscriber.putNext(value)
})
})
return disposable ?? EmptyDisposable
}
|> runOn(Queue.mainQueue())
}
func recentOnlineSmall(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId) -> Signal<Int32, NoError> {
return Signal { [weak self] subscriber in
var previousIds: Set<PeerId>?
let statusesDisposable = MetaDisposable()
let disposableAndControl = self?.recent(postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, updated: { state in
var idList: [PeerId] = []
for item in state.list {
idList.append(item.peer.id)
if idList.count >= 200 {
break
}
}
let updatedIds = Set(idList)
if previousIds != updatedIds {
previousIds = updatedIds
let key: PostboxViewKey = .peerPresences(peerIds: updatedIds)
statusesDisposable.set((postbox.combinedView(keys: [key])
|> map { view -> Int32 in
var count: Int32 = 0
let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970
if let presences = (view.views[key] as? PeerPresencesView)?.presences {
for (_, presence) in presences {
if let presence = presence as? TelegramUserPresence {
let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: Int32(timestamp))
switch relativeStatus {
case .online:
count += 1
default:
break
}
}
}
}
return count
}
|> distinctUntilChanged
|> deliverOnMainQueue).start(next: { count in
subscriber.putNext(count)
}))
}
})
return ActionDisposable {
disposableAndControl?.0.dispose()
statusesDisposable.dispose()
}
}
|> runOn(Queue.mainQueue())
}
} }