mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00

Added ability to download music without streaming Added progress indicators for various blocking tasks Fixed image gallery swipe to dismiss after zooming Added online member count indication in supergroups Fixed contact statuses in contact search
246 lines
11 KiB
Swift
246 lines
11 KiB
Swift
import Foundation
|
|
import Postbox
|
|
import TelegramCore
|
|
import SwiftSignalKit
|
|
|
|
enum PeerChannelMemberContextKey: Equatable, Hashable {
|
|
case recent
|
|
case recentSearch(String)
|
|
case admins(String?)
|
|
case restrictedAndBanned(String?)
|
|
|
|
var hashValue: Int {
|
|
switch self {
|
|
case .recent:
|
|
return 1
|
|
case let .recentSearch(query):
|
|
return query.hashValue
|
|
case let .admins(query):
|
|
return query?.hashValue ?? 2
|
|
case let .restrictedAndBanned(query):
|
|
return query?.hashValue ?? 3
|
|
}
|
|
}
|
|
}
|
|
|
|
private final class PeerChannelMemberCategoriesContextsManagerImpl {
|
|
fileprivate var contexts: [PeerId: PeerChannelMemberCategoriesContext] = [:]
|
|
|
|
func getContext(postbox: Postbox, network: Network, peerId: PeerId, key: PeerChannelMemberContextKey, requestUpdate: Bool, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl) {
|
|
if let current = self.contexts[peerId] {
|
|
return current.getContext(key: key, requestUpdate: requestUpdate, updated: updated)
|
|
} else {
|
|
var becameEmptyImpl: ((Bool) -> Void)?
|
|
let context = PeerChannelMemberCategoriesContext(postbox: postbox, network: network, peerId: peerId, becameEmpty: { value in
|
|
becameEmptyImpl?(value)
|
|
})
|
|
becameEmptyImpl = { [weak self, weak context] value in
|
|
assert(Queue.mainQueue().isCurrent())
|
|
if let strongSelf = self {
|
|
if let current = strongSelf.contexts[peerId], current === context {
|
|
strongSelf.contexts.removeValue(forKey: peerId)
|
|
}
|
|
}
|
|
}
|
|
self.contexts[peerId] = context
|
|
return context.getContext(key: key, requestUpdate: requestUpdate, updated: updated)
|
|
}
|
|
}
|
|
|
|
func loadMore(peerId: PeerId, control: PeerChannelMemberCategoryControl) {
|
|
if let context = self.contexts[peerId] {
|
|
context.loadMore(control)
|
|
}
|
|
}
|
|
}
|
|
|
|
final class PeerChannelMemberCategoriesContextsManager {
|
|
private let impl: QueueLocalObject<PeerChannelMemberCategoriesContextsManagerImpl>
|
|
|
|
init() {
|
|
self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: {
|
|
return PeerChannelMemberCategoriesContextsManagerImpl()
|
|
})
|
|
}
|
|
|
|
func loadMore(peerId: PeerId, control: PeerChannelMemberCategoryControl?) {
|
|
if let control = control {
|
|
self.impl.with { impl in
|
|
impl.loadMore(peerId: peerId, control: control)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func getContext(postbox: Postbox, network: Network, peerId: PeerId, key: PeerChannelMemberContextKey, requestUpdate: Bool, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl?) {
|
|
assert(Queue.mainQueue().isCurrent())
|
|
if let (disposable, control) = self.impl.syncWith({ impl in
|
|
return impl.getContext(postbox: postbox, network: network, peerId: peerId, key: key, requestUpdate: requestUpdate, updated: updated)
|
|
}) {
|
|
return (disposable, control)
|
|
} else {
|
|
return (EmptyDisposable, nil)
|
|
}
|
|
}
|
|
|
|
func externallyAdded(peerId: PeerId, participant: RenderedChannelParticipant) {
|
|
self.impl.with { impl in
|
|
for (contextPeerId, context) in impl.contexts {
|
|
if contextPeerId == peerId {
|
|
context.replayUpdates([(nil, participant)])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func recent(postbox: Postbox, network: Network, peerId: PeerId, searchQuery: String? = nil, requestUpdate: Bool = true, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl?) {
|
|
let key: PeerChannelMemberContextKey
|
|
if let searchQuery = searchQuery {
|
|
key = .recentSearch(searchQuery)
|
|
} else {
|
|
key = .recent
|
|
}
|
|
return self.getContext(postbox: postbox, network: network, peerId: peerId, key: key, requestUpdate: requestUpdate, updated: updated)
|
|
}
|
|
|
|
func recentOnline(postbox: Postbox, network: Network, 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, 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, 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, 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<Void, NoError> {
|
|
return updateChannelMemberBannedRights(account: account, peerId: peerId, memberId: memberId, rights: bannedRights)
|
|
|> deliverOnMainQueue
|
|
|> beforeNext { [weak self] (previous, updated) in
|
|
if let strongSelf = self {
|
|
strongSelf.impl.with { impl in
|
|
for (contextPeerId, context) in impl.contexts {
|
|
if peerId == contextPeerId {
|
|
context.replayUpdates([(previous, updated)])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|> mapToSignal { _ -> Signal<Void, NoError> in
|
|
return .complete()
|
|
}
|
|
}
|
|
|
|
func updateMemberAdminRights(account: Account, peerId: PeerId, memberId: PeerId, adminRights: TelegramChannelAdminRights) -> Signal<Void, NoError> {
|
|
return updatePeerAdminRights(account: account, peerId: peerId, adminId: memberId, rights: adminRights)
|
|
|> map(Optional.init)
|
|
|> `catch` { _ -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, NoError> in
|
|
return .single(nil)
|
|
}
|
|
|> deliverOnMainQueue
|
|
|> beforeNext { [weak self] result in
|
|
if let strongSelf = self, let (previous, updated) = result {
|
|
strongSelf.impl.with { impl in
|
|
for (contextPeerId, context) in impl.contexts {
|
|
if peerId == contextPeerId {
|
|
context.replayUpdates([(previous, updated)])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|> mapToSignal { _ -> Signal<Void, NoError> in
|
|
return .complete()
|
|
}
|
|
}
|
|
|
|
func addMember(account: Account, peerId: PeerId, memberId: PeerId) -> Signal<Void, NoError> {
|
|
return addChannelMember(account: account, peerId: peerId, memberId: memberId)
|
|
|> map(Optional.init)
|
|
|> `catch` { _ -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, NoError> in
|
|
return .single(nil)
|
|
}
|
|
|> deliverOnMainQueue
|
|
|> beforeNext { [weak self] result in
|
|
if let strongSelf = self, let (previous, updated) = result {
|
|
strongSelf.impl.with { impl in
|
|
for (contextPeerId, context) in impl.contexts {
|
|
if peerId == contextPeerId {
|
|
context.replayUpdates([(previous, updated)])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|> mapToSignal { _ -> Signal<Void, NoError> in
|
|
return .complete()
|
|
}
|
|
}
|
|
|
|
func addMembers(account: Account, peerId: PeerId, memberIds: [PeerId]) -> Signal<Void, AddChannelMemberError> {
|
|
return addChannelMembers(account: account, peerId: peerId, memberIds: memberIds) |> deliverOnMainQueue
|
|
|> beforeNext { [weak self] result in
|
|
if let strongSelf = self {
|
|
strongSelf.impl.with { impl in
|
|
for (contextPeerId, context) in impl.contexts {
|
|
if peerId == contextPeerId {
|
|
context.reset(.recent)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|> mapToSignal { _ -> Signal<Void, AddChannelMemberError> in
|
|
return .single(Void())
|
|
}
|
|
}
|
|
}
|