Home telegram - peerInfoMembersContext
Post
Cancel

telegram - peerInfoMembersContext

PeerInfoMembersContext

enum PeerInfoMemberRole

1
2
3
case creator
case admin
case member

enum PeerInfoMember

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
case channelMember(RenderedChannelParticipant)
case legacyGroupMember(peer: RenderedPeer, role: PeerInfoMemberRole, invitedBy: PeerId?, presence: TelegramUserPresence?)
case account(peer: RenderedPeer)

var id: PeerId {
    switch self {
    case let .channelMember(channelMember):
        return channelMember.peer.id
    case let .legacyGroupMember(peer, _, _, _):
        return peer.peerId
    case let .account(peer):
        return peer.peerId
    }
}

var peer: Peer {
    switch self {
    case let .channelMember(channelMember):
        return channelMember.peer
    case let .legacyGroupMember(peer, _, _, _):
        return peer.peers[peer.peerId]!
    case let .account(peer):
        return peer.peers[peer.peerId]!
    }
}

var presence: TelegramUserPresence? {
    switch self {
    case let .channelMember(channelMember):
        return channelMember.presences[channelMember.peer.id] as? TelegramUserPresence
    case let .legacyGroupMember(_, _, _, presence):
        return presence
    case .account:
        return nil
    }
}

var role: PeerInfoMemberRole {
    switch self {
    case let .channelMember(channelMember):
        switch channelMember.participant {
        case .creator:
            return .creator
        case let .member(_, _, adminInfo, _, _):
            if adminInfo != nil {
                return .admin
            } else {
                return .member
            }
        }
    case let .legacyGroupMember(_, role, _, _):
        return role
    case .account:
        return .member
    }
}

enum PeerInfoMembersDataState

1
2
case loading(isInitial: Bool)
case ready(canLoadMore: Bool)

struct PeerInfoMembersState

1
2
3
var canAddMembers: Bool
var members: [PeerInfoMember]
var dataState: PeerInfoMembersDataState

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 PeerInfoMembersContextImpl

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
private let queue: Queue
private let context: AccountContext
private let peerId: PeerId

// can add members
private var canAddMembers = false

// members
private var members: [PeerInfoMember] = []

// data state
private var dataState: PeerInfoMembersDataState = .loading(isInitial: true)
private var removingMemberIds: [PeerId: Disposable] = [:]

// state
private let stateValue = Promise<PeerInfoMembersState>()
var state: Signal<PeerInfoMembersState, NoError> {
    return self.stateValue.get()
}

private let disposable = MetaDisposable()
private let peerDisposable = MetaDisposable()
private var channelMembersControl: PeerChannelMemberCategoryControl?

init(queue: Queue, context: AccountContext, peerId: PeerId) {
    self.queue = queue
    self.context = context
    self.peerId = peerId

    self.pushState()

    if peerId.namespace == Namespaces.Peer.CloudChannel {
      // route to `peerChannelMemberCategoriesContextsManager` when `channel`
        let (disposable, control) = context.peerChannelMemberCategoriesContextsManager.recent(engine: context.engine, postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId, updated: { [weak self] state in
            queue.async {
                guard let strongSelf = self else {
                    return
                }
                let unsortedMembers = state.list.map(PeerInfoMember.channelMember)
                let members: [PeerInfoMember]
                if unsortedMembers.count <= 50 {
                    members = membersSortedByPresence(unsortedMembers, accountPeerId: strongSelf.context.account.peerId)
                } else {
                    members = unsortedMembers
                }
                strongSelf.members = members
                switch state.loadingState {
                case let .loading(initial):
                    strongSelf.dataState = .loading(isInitial: initial)
                case let .ready(hasMore):
                    strongSelf.dataState = .ready(canLoadMore: hasMore)
                }
                strongSelf.pushState()
            }
        })
        self.disposable.set(disposable)
        self.channelMembersControl = control

        self.peerDisposable.set((context.account.postbox.peerView(id: peerId)
        |> deliverOn(self.queue)).start(next: { [weak self] view in
            guard let strongSelf = self else {
                return
            }
            if let channel = peerViewMainPeer(view) as? TelegramChannel {
                var canAddMembers = false
                switch channel.info {
                case .broadcast:
                    break
                case .group:
                    if channel.flags.contains(.isCreator) || channel.hasPermission(.inviteMembers) {
                        canAddMembers = true
                    }
                }
                strongSelf.canAddMembers = canAddMembers
                strongSelf.pushState()
            }
        }))
    } else if peerId.namespace == Namespaces.Peer.CloudGroup {
      // cached data when group
        self.disposable.set((context.account.postbox.peerView(id: peerId)
        |> deliverOn(self.queue)).start(next: { [weak self] view in
            guard let strongSelf = self, let cachedData = view.cachedData as? CachedGroupData, let participantsData = cachedData.participants else {
                return
            }
            var unsortedMembers: [PeerInfoMember] = []
            for participant in participantsData.participants {
                if let peer = view.peers[participant.peerId] {
                    let role: PeerInfoMemberRole
                    let invitedBy: PeerId?
                    switch participant {
                    case .creator:
                        role = .creator
                        invitedBy = nil
                    case let .admin(_, invitedByValue, _):
                        role = .admin
                        invitedBy = invitedByValue
                    case let .member(_, invitedByValue, _):
                        role = .member
                        invitedBy = invitedByValue
                    }
                    unsortedMembers.append(.legacyGroupMember(peer: RenderedPeer(peer: peer), role: role, invitedBy: invitedBy, presence: view.peerPresences[participant.peerId] as? TelegramUserPresence))
                }
            }

            if let group = peerViewMainPeer(view) as? TelegramGroup {
                var canAddMembers = false
                switch group.role {
                    case .admin, .creator:
                        canAddMembers = true
                    case .member:
                        break
                }
                if !group.hasBannedPermission(.banAddMembers) {
                    canAddMembers = true
                }
                strongSelf.canAddMembers = canAddMembers
            }

            strongSelf.members = membersSortedByPresence(unsortedMembers, accountPeerId: strongSelf.context.account.peerId)
            strongSelf.dataState = .ready(canLoadMore: false)
            strongSelf.pushState()
        }))
    } else {
        self.dataState = .ready(canLoadMore: false)
        self.pushState()
    }
}

deinit {
    self.disposable.dispose()
    self.peerDisposable.dispose()
}

// push state
private func pushState() {
    if self.removingMemberIds.isEmpty {
        self.stateValue.set(.single(PeerInfoMembersState(canAddMembers: self.canAddMembers, members: self.members, dataState: self.dataState)))
    } else {
        self.stateValue.set(.single(PeerInfoMembersState(canAddMembers: self.canAddMembers, members: self.members.filter { member in
            return self.removingMemberIds[member.id] == nil
        }, dataState: self.dataState)))
    }
}

// load more
func loadMore() {
    if case .ready(true) = self.dataState, let channelMembersControl = self.channelMembersControl {
        self.context.peerChannelMemberCategoriesContextsManager.loadMore(peerId: self.peerId, control: channelMembersControl)
    }
}

// remove member
func removeMember(memberId: PeerId) {
    if removingMemberIds[memberId] == nil {
        let signal: Signal<Never, NoError>
        if self.peerId.namespace == Namespaces.Peer.CloudChannel {
          // ban read message right when channel
            signal = context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: self.context.engine, peerId: self.peerId, memberId: memberId, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max))
            |> ignoreValues
        } else {
          // remove member when group
            signal = self.context.engine.peers.removePeerMember(peerId: self.peerId, memberId: memberId)
            |> ignoreValues
        }
        let completed: () -> Void = { [weak self] in
            guard let strongSelf = self else {
                return
            }
                                     // push state when complete
            if let _ = strongSelf.removingMemberIds.removeValue(forKey: memberId) {
                strongSelf.pushState()
            }
        }
        let disposable = MetaDisposable()
        self.removingMemberIds[memberId] = disposable

        self.pushState()

        disposable.set((signal
        |> deliverOn(self.queue)).start(completed: {
            completed()
        }))
    }
}

class PeerInfoMembersContext

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
private let queue = Queue.mainQueue()
private let impl: QueueLocalObject<PeerInfoMembersContextImpl>

var state: Signal<PeerInfoMembersState, NoError> {
    return Signal { subscriber in
        let disposable = MetaDisposable()
        self.impl.with { impl in
            disposable.set(impl.state.start(next: { value in
                subscriber.putNext(value)
            }))
        }
        return disposable
    }
}

init(context: AccountContext, peerId: PeerId) {
    let queue = self.queue
    self.impl = QueueLocalObject(queue: queue, generate: {
        return PeerInfoMembersContextImpl(queue: queue, context: context, peerId: peerId)
    })
}

// wrapped load more
func loadMore() {
    self.impl.with { impl in
        impl.loadMore()
    }
}

// wrapped remove member
func removeMember(memberId: PeerId) {
    self.impl.with { impl in
        impl.removeMember(memberId: memberId)
    }
}
This post is licensed under CC BY 4.0 by the author.