PeerChannelMemberCategoriesContextsManager
struct
TelegramChatAdminRightsFlags:
OptionSet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public var rawValue: Int32
public static let canChangeInfo = TelegramChatAdminRightsFlags(rawValue: 1 << 0)
public static let canPostMessages = TelegramChatAdminRightsFlags(rawValue: 1 << 1)
public static let canEditMessages = TelegramChatAdminRightsFlags(rawValue: 1 << 2)
public static let canDeleteMessages = TelegramChatAdminRightsFlags(rawValue: 1 << 3)
public static let canBanUsers = TelegramChatAdminRightsFlags(rawValue: 1 << 4)
public static let canInviteUsers = TelegramChatAdminRightsFlags(rawValue: 1 << 5)
public static let canPinMessages = TelegramChatAdminRightsFlags(rawValue: 1 << 7)
public static let canAddAdmins = TelegramChatAdminRightsFlags(rawValue: 1 << 9)
public static let canBeAnonymous = TelegramChatAdminRightsFlags(rawValue: 1 << 10)
public static let canManageCalls = TelegramChatAdminRightsFlags(rawValue: 1 << 11)
public static var all: TelegramChatAdminRightsFlags {
return [.canChangeInfo, .canPostMessages, .canEditMessages, .canDeleteMessages, .canBanUsers, .canInviteUsers, .canPinMessages, .canAddAdmins, .canBeAnonymous, .canManageCalls]
}
public static var allChannel: TelegramChatAdminRightsFlags {
return [.canChangeInfo, .canPostMessages, .canEditMessages, .canDeleteMessages, .canBanUsers, .canInviteUsers, .canPinMessages, .canAddAdmins, .canManageCalls]
}
public static var groupSpecific: TelegramChatAdminRightsFlags = [
.canChangeInfo,
.canDeleteMessages,
.canBanUsers,
.canInviteUsers,
.canPinMessages,
.canManageCalls,
.canBeAnonymous,
.canAddAdmins
]
public static var broadcastSpecific: TelegramChatAdminRightsFlags = [
.canChangeInfo,
.canPostMessages,
.canEditMessages,
.canDeleteMessages,
.canManageCalls,
.canInviteUsers,
.canAddAdmins
]
public var count: Int {
var result = 0
var index = 0
while index < 31 {
let currentValue = self.rawValue >> Int32(index)
index += 1
if currentValue == 0 {
break
}
if (currentValue & 1) != 0 {
result += 1
}
}
return result
}
struct
TelegramChatAdminRights:
PostboxCoding
1
public let rights: TelegramChatAdminRightsFlags
struct
ChannelParticipantAdminInfo
1
2
3
public let rights: TelegramChatAdminRights
public let promotedBy: PeerId
public let canBeEditedByAccountPeer: Bool
struct
TelegramChatBannedRightsFlags:
OptionSet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public var rawValue: Int32
public static let banReadMessages = TelegramChatBannedRightsFlags(rawValue: 1 << 0)
public static let banSendMessages = TelegramChatBannedRightsFlags(rawValue: 1 << 1)
public static let banSendMedia = TelegramChatBannedRightsFlags(rawValue: 1 << 2)
public static let banSendStickers = TelegramChatBannedRightsFlags(rawValue: 1 << 3)
public static let banSendGifs = TelegramChatBannedRightsFlags(rawValue: 1 << 4)
public static let banSendGames = TelegramChatBannedRightsFlags(rawValue: 1 << 5)
public static let banSendInline = TelegramChatBannedRightsFlags(rawValue: 1 << 6)
public static let banEmbedLinks = TelegramChatBannedRightsFlags(rawValue: 1 << 7)
public static let banSendPolls = TelegramChatBannedRightsFlags(rawValue: 1 << 8)
public static let banChangeInfo = TelegramChatBannedRightsFlags(rawValue: 1 << 10)
public static let banAddMembers = TelegramChatBannedRightsFlags(rawValue: 1 << 15)
public static let banPinMessages = TelegramChatBannedRightsFlags(rawValue: 1 << 17)
struct
TelegramChatBannedRights
1
2
public let flags: TelegramChatBannedRightsFlags
public let untilDate: Int32
struct
ChannelParticipantBannedInfo
1
2
3
4
public let rights: TelegramChatBannedRights
public let restrictedBy: PeerId
public let timestamp: Int32
public let isMember: Bool
enum
ChannelParticipant
1
2
case creator(id: PeerId, adminInfo: ChannelParticipantAdminInfo?, rank: String?)
case member(id: PeerId, invitedAt: Int32, adminInfo: ChannelParticipantAdminInfo?, banInfo: ChannelParticipantBannedInfo?, rank: String?)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public 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
}
}
func canBeBannedBy(peerId: PeerId) -> Bool {
switch self {
case .creator:
return false
case let .member(_, _, adminInfo, _, _):
if let adminInfo = adminInfo {
if adminInfo.promotedBy != peerId {
return false
}
}
}
return true
}
}
struct
RenderedChannelParticipant
1
2
3
4
public let participant: ChannelParticipant
public let peer: Peer
public let peers: [PeerId: Peer]
public let presences: [PeerId: PeerPresence]
enum
ChannelMemberListLoadingState
1
2
case loading(initial: Bool)
case ready(hasMore: Bool)
struct
ChannelMemberListState
1
2
public let list: [RenderedChannelParticipant]
public let loadingState: ChannelMemberListLoadingState
protocol
ChannelMemberCategoryListContext
1
2
3
4
5
6
var listStateValue: ChannelMemberListState { get }
var listState: Signal<ChannelMemberListState, NoError> { get }
func loadMore()
func reset(_ force: Bool)
func replayUpdates(_ updates: [(ChannelParticipant?, RenderedChannelParticipant?, Bool?)])
func forceUpdateHead()
class
PeerChannelMemberContextWithSubscribers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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)
}
}
}))
}
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 {// became empty when subscribers are empty
strongSelf.becameEmpty()
}
}
}, queue: Queue.mainQueue())
self.emptyTimer = emptyTimer
emptyTimer.start()
}
func subscribe(requestUpdate: Bool, updated: @escaping (ChannelMemberListState) -> Void) -> Disposable {
let wasEmpty = self.subscribers.isEmpty
// listen
let index = self.subscribers.add(updated)
// update with the latest value immediately
updated(self.context.listStateValue)
if wasEmpty {
self.emptyTimer?.invalidate()
if requestUpdate {
// force context update
self.context.forceUpdateHead()
}
}
return ActionDisposable { [weak self] in
Queue.mainQueue().async {
if let strongSelf = self {
strongSelf.subscribers.remove(index)
if strongSelf.subscribers.isEmpty {
// reset begin empty timer
strongSelf.resetAndBeginEmptyTimer()
}
}
}
}
}
enum
PeerChannelMemberContextKey
1
2
3
4
5
6
7
8
9
case recent
case recentSearch(String)
case mentions(threadId: MessageId?, query: String?)
case admins(String?)
case contacts(String?)
case bots(String?)
case restrictedAndBanned(String?)
case restricted(String?)
case banned(String?)
struct
PeerChannelMemberCategoryControl
1
fileprivate let key: PeerChannelMemberContextKey
class
PeerChannelMemberCategoriesContext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
private let engine: TelegramEngine
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] = [:]
// reset context key
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), .recent, .contacts:
emptyTimeout = defaultEmptyTimeout
default:
emptyTimeout = 0.0
}
switch key {
case .recent, .recentSearch, .admins, .contacts, .bots, .mentions:
let mappedCategory: ChannelMemberListCategory
switch key {
case .recent:
mappedCategory = .recent
case let .recentSearch(query):
mappedCategory = .recentSearch(query)
case let .admins(query):
mappedCategory = .admins(query)
case let .contacts(query):
mappedCategory = .contacts(query)
case let .bots(query):
mappedCategory = .bots(query)
case let .mentions(threadId, query):
mappedCategory = .mentions(threadId, query)
default:
mappedCategory = .recent
}
context = ChannelMemberSingleCategoryListContext(engine: self.engine, postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, peerId: self.peerId, category: mappedCategory)
case let .restrictedAndBanned(query):
context = ChannelMemberMultiCategoryListContext(engine: self.engine, postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, peerId: self.peerId, categories: [.restricted(query), .banned(query)])
case let .restricted(query):
context = ChannelMemberSingleCategoryListContext(engine: self.engine, postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, peerId: self.peerId, category: .restricted(query))
case let .banned(query):
context = ChannelMemberSingleCategoryListContext(engine: self.engine, 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)
}
}
class
PeerChannelMembersOnlineContext
1
2
3
4
5
6
7
8
let subscribers = Bag<(Int32) -> Void>()
let disposable: Disposable
var value: Int32?
var emptyTimer: SwiftSignalKit.Timer?
init(disposable: Disposable) {
self.disposable = disposable
}
class
ProfileDataPreloadContext
1
2
3
4
5
6
7
8
let subscribers = Bag<() -> Void>()
let disposable: Disposable
var emptyTimer: SwiftSignalKit.Timer?
init(disposable: Disposable) {
self.disposable = disposable
}
class
ProfileDataPhotoPreloadContext
1
2
3
4
5
let subscribers = Bag<(Any?) -> Void>()
let disposable: Disposable
var value: Any?
var emptyTimer: SwiftSignalKit.Timer?
class
PeerChannelMemberCategoriesContextsManagerImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
fileprivate var contexts: [PeerId: PeerChannelMemberCategoriesContext] = [:]
fileprivate var onlineContexts: [PeerId: PeerChannelMembersOnlineContext] = [:]
fileprivate var profileDataPreloadContexts: [PeerId: ProfileDataPreloadContext] = [:]
fileprivate var profileDataPhotoPreloadContexts: [PeerId: ProfileDataPhotoPreloadContext] = [:]
func getContext(engine: TelegramEngine, postbox: Postbox, network: Network, accountPeerId: PeerId, 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(engine: engine, postbox: postbox, network: network, accountPeerId: accountPeerId, 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)
}
}
// recent online
func recentOnline(account: Account, 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 = (
// request online members
TelegramEngine(account: account).peers.chatOnlineMembers(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 {
// check subscriber empty after 60.0 seconds after disposed
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()
}
}
}
}
}
}
// load more
func loadMore(peerId: PeerId, control: PeerChannelMemberCategoryControl) {
if let context = self.contexts[peerId] {
context.loadMore(control)
}
}
// profile data
func profileData(postbox: Postbox, network: Network, peerId: PeerId, customData: Signal<Never, NoError>?) -> Disposable {
let context: ProfileDataPreloadContext
if let current = self.profileDataPreloadContexts[peerId] {
context = current
} else {
let disposable = DisposableSet()
context = ProfileDataPreloadContext(disposable: disposable)
self.profileDataPreloadContexts[peerId] = context
if let customData = customData {
disposable.add(customData.start())
}
/*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({
})
//updated(context.value ?? 0)
return ActionDisposable { [weak self, weak context] in
Queue.mainQueue().async {
guard let strongSelf = self else {
return
}
if let current = strongSelf.profileDataPreloadContexts[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.profileDataPreloadContexts[peerId], let context = context, current === context {
if current.subscribers.isEmpty {
strongSelf.profileDataPreloadContexts.removeValue(forKey: peerId)
current.disposable.dispose()
}
}
}, queue: Queue.mainQueue())
current.emptyTimer = timer
timer.start()
}
}
}
}
}
}
// profile photos
func profilePhotos(postbox: Postbox, network: Network, peerId: PeerId, fetch: Signal<Any, NoError>, updated: @escaping (Any?) -> Void) -> Disposable {
let context: ProfileDataPhotoPreloadContext
if let current = self.profileDataPhotoPreloadContexts[peerId] {
context = current
} else {
let disposable = MetaDisposable()
context = ProfileDataPhotoPreloadContext(disposable: disposable)
self.profileDataPhotoPreloadContexts[peerId] = context
disposable.set(fetch.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)
return ActionDisposable { [weak self, weak context] in
Queue.mainQueue().async {
guard let strongSelf = self else {
return
}
if let current = strongSelf.profileDataPhotoPreloadContexts[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.profileDataPhotoPreloadContexts[peerId], let context = context, current === context {
if current.subscribers.isEmpty {
strongSelf.profileDataPhotoPreloadContexts.removeValue(forKey: peerId)
current.disposable.dispose()
}
}
}, queue: Queue.mainQueue())
current.emptyTimer = timer
timer.start()
}
}
}
}
}
}
class
PeerChannelMemberCategoriesContextsManager
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
private let impl: QueueLocalObject<PeerChannelMemberCategoriesContextsManagerImpl>
private let removedChannelMembersPipe = ValuePipe<[(PeerId, PeerId)]>()
public var removedChannelMembers: Signal<[(PeerId, PeerId)], NoError> {
return self.removedChannelMembersPipe.signal()
}
public init() {
self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: {
return PeerChannelMemberCategoriesContextsManagerImpl()
})
}
public func loadMore(peerId: PeerId, control: PeerChannelMemberCategoryControl?) {
if let control = control {
self.impl.with { impl in
impl.loadMore(peerId: peerId, control: control)
}
}
}
// get context
private func getContext(engine: TelegramEngine, postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, key: PeerChannelMemberContextKey, requestUpdate: Bool, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl?) {
assert(Queue.mainQueue().isCurrent())
let (disposable, control) = self.impl.syncWith({ impl in
return impl.getContext(engine: engine, postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, key: key, requestUpdate: requestUpdate, updated: updated)
})
return (disposable, control)
}
// externally added
public func externallyAdded(peerId: PeerId, participant: RenderedChannelParticipant) {
self.impl.with { impl in
for (contextPeerId, context) in impl.contexts {
if contextPeerId == peerId {
context.replayUpdates([(nil, participant, nil)])
}
}
}
}
// externally removed
public func externallyRemoved(peerId: PeerId, memberId: PeerId) {
self.impl.with { impl in
for (contextPeerId, context) in impl.contexts {
if contextPeerId == peerId {
context.replayUpdates([(.member(id: memberId, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: nil), nil, nil)])
}
}
}
}
// recent
public func recent(engine: TelegramEngine, postbox: Postbox, network: Network, accountPeerId: PeerId, 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(engine: engine, postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, key: key, requestUpdate: requestUpdate, updated: updated)
}
// mentions
public func mentions(engine: TelegramEngine, postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, threadMessageId: MessageId?, searchQuery: String? = nil, requestUpdate: Bool = true, updated: @escaping
(ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl?) {
let key: PeerChannelMemberContextKey = .mentions(threadId: threadMessageId, query: searchQuery)
return self.getContext(engine: engine, postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, key: key, requestUpdate: requestUpdate, updated: updated)
}
// admins
public func admins(engine: TelegramEngine, postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, searchQuery: String? = nil, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable,
PeerChannelMemberCategoryControl?) {
return self.getContext(engine: engine, postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, key: .admins(searchQuery), requestUpdate: true, updated: updated)
}
// contacts
public func contacts(engine: TelegramEngine, postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, searchQuery: String? = nil, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl?) {
return self.getContext(engine: engine, postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, key: .contacts(searchQuery), requestUpdate: true, updated: updated)
}
// bots
public func bots(engine: TelegramEngine, postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, searchQuery: String? = nil, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl?) {
return self.getContext(engine: engine, postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, key: .bots(searchQuery), requestUpdate: true, updated: updated)
}
// restricted
public func restricted(engine: TelegramEngine, postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, searchQuery: String? = nil, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl?) {
return self.getContext(engine: engine, postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, key: .restricted(searchQuery), requestUpdate: true, updated: updated)
}
// banned
public func banned(engine: TelegramEngine, postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, searchQuery: String? = nil, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl?) {
return self.getContext(engine: engine, postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, key: .banned(searchQuery), requestUpdate: true, updated: updated)
}
// restricted and banned
public func restrictedAndBanned(engine: TelegramEngine, postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, searchQuery: String? = nil, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl?) {
return self.getContext(engine: engine, postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId, key: .restrictedAndBanned(searchQuery), requestUpdate: true, updated: updated)
}
// update member banned rights
public func updateMemberBannedRights(engine: TelegramEngine, peerId: PeerId, memberId: PeerId, bannedRights: TelegramChatBannedRights?) -> Signal<Void, NoError> {
return engine.peers.updateChannelMemberBannedRights(peerId: peerId, memberId: memberId, rights: bannedRights)
|> deliverOnMainQueue
|> beforeNext { [weak self] (previous, updated, isMember) in
if let strongSelf = self {
strongSelf.impl.with { impl in
for (contextPeerId, context) in impl.contexts {
if peerId == contextPeerId {
context.replayUpdates([(previous, updated, isMember)])
}
}
}
if !isMember {
strongSelf.removedChannelMembersPipe.putNext([(peerId, memberId)])
}
}
}
|> mapToSignal { _ -> Signal<Void, NoError> in
return .complete()
}
}
// update member admin rights
public func updateMemberAdminRights(engine: TelegramEngine, peerId: PeerId, memberId: PeerId, adminRights: TelegramChatAdminRights?, rank: String?) -> Signal<Void, UpdateChannelAdminRightsError> {
return engine.peers.updateChannelAdminRights(peerId: peerId, adminId: memberId, rights: adminRights, rank: rank)
|> map(Optional.init)
|> 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, nil)])
}
}
}
}
}
|> mapToSignal { _ -> Signal<Void, UpdateChannelAdminRightsError> in
return .complete()
}
}
// transfer ownership to memberId
public func transferOwnership(engine: TelegramEngine, peerId: PeerId, memberId: PeerId, password: String) -> Signal<Void, ChannelOwnershipTransferError> {
return engine.peers.updateChannelOwnership(channelId: peerId, memberId: memberId, password: password)
|> map(Optional.init)
|> deliverOnMainQueue
|> beforeNext { [weak self] results in
if let strongSelf = self, let results = results {
strongSelf.impl.with { impl in
for (contextPeerId, context) in impl.contexts {
if peerId == contextPeerId {
context.replayUpdates(results.map { ($0.0, $0.1, nil) })
}
}
}
}
}
|> mapToSignal { _ -> Signal<Void, ChannelOwnershipTransferError> in
return .complete()
}
}
// join channel
public func join(engine: TelegramEngine, peerId: PeerId, hash: String?) -> Signal<Never, JoinChannelError> {
// join channel or import chat invite
return engine.peers.joinChannel(peerId: peerId, hash: hash)
|> deliverOnMainQueue
|> beforeNext { [weak self] result in
if let strongSelf = self, let updated = result {
strongSelf.impl.with { impl in
for (contextPeerId, context) in impl.contexts {
if peerId == contextPeerId {
context.replayUpdates([(nil, updated, nil)])
}
}
}
}
}
|> ignoreValues
}
// add member
public func addMember(engine: TelegramEngine, peerId: PeerId, memberId: PeerId) -> Signal<Never, AddChannelMemberError> {
return engine.peers.addChannelMember(peerId: peerId, memberId: memberId)
|> 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, nil)])
}
}
}
}
}
|> ignoreValues
}
// add members
public func addMembers(engine: TelegramEngine, peerId: PeerId, memberIds: [PeerId]) -> Signal<Void, AddChannelMemberError> {
let signals: [Signal<(ChannelParticipant?, RenderedChannelParticipant)?, AddChannelMemberError>] = memberIds.map({ memberId in
return engine.peers.addChannelMember(peerId: peerId, memberId: memberId)
|> map(Optional.init)
|> `catch` { error -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, AddChannelMemberError> in
return .fail(error)
}
})
return combineLatest(signals)
|> deliverOnMainQueue
|> beforeNext { [weak self] results in
if let strongSelf = self {
strongSelf.impl.with { impl in
for result in results {
if let (previous, updated) = result {
for (contextPeerId, context) in impl.contexts {
if peerId == contextPeerId {
context.replayUpdates([(previous, updated, nil)])
}
}
}
}
}
}
}
|> mapToSignal { _ -> Signal<Void, AddChannelMemberError> in
return .complete()
}
}
// recent online
public func recentOnline(account: Account, 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(account: account, accountPeerId: accountPeerId, peerId: peerId, updated: { value in
subscriber.putNext(value)
})
})
return disposable
}
|> runOn(Queue.mainQueue())
}
// recent-online small
public func recentOnlineSmall(engine: TelegramEngine, 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(engine: engine, 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(EnginePeer.Presence(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())
}
// profile data
public func profileData(postbox: Postbox, network: Network, peerId: PeerId, customData: Signal<Never, NoError>?) -> Signal<Never, NoError> {
return Signal { [weak self] subscriber in
guard let strongSelf = self else {
subscriber.putCompletion()
return EmptyDisposable
}
let disposable = strongSelf.impl.syncWith({ impl -> Disposable in
return impl.profileData(postbox: postbox, network: network, peerId: peerId, customData: customData)
})
return disposable
}
|> runOn(Queue.mainQueue())
}
// profile photos
public func profilePhotos(postbox: Postbox, network: Network, peerId: PeerId, fetch: Signal<Any, NoError>) -> Signal<Any?, 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.profilePhotos(postbox: postbox, network: network, peerId: peerId, fetch: fetch, updated: { value in
subscriber.putNext(value)
})
})
return disposable
}
|> runOn(Queue.mainQueue())
}