From fab42fd01d47f0b913b9fc5472f3dfdf96aa1e52 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 28 Jan 2021 23:03:39 +0500 Subject: [PATCH] Voice chat fixes --- .../Sources/PresentationCallManager.swift | 2 + .../Sources/PresentationGroupCall.swift | 6 +- .../Sources/VoiceChatController.swift | 41 +++-- .../TelegramCore/Sources/GroupCalls.swift | 144 +++++++++++++++++- 4 files changed, 173 insertions(+), 20 deletions(-) diff --git a/submodules/AccountContext/Sources/PresentationCallManager.swift b/submodules/AccountContext/Sources/PresentationCallManager.swift index 7eeaf57df7..78ee08745c 100644 --- a/submodules/AccountContext/Sources/PresentationCallManager.swift +++ b/submodules/AccountContext/Sources/PresentationCallManager.swift @@ -302,6 +302,8 @@ public protocol PresentationGroupCall: class { func invitePeer(_ peerId: PeerId) -> Bool func removedPeer(_ peerId: PeerId) var invitedPeers: Signal<[PeerId], NoError> { get } + + func loadMoreMembers(token: String) } public protocol PresentationCallManager: class { diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 8a56be0d56..cf59f718c2 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -57,7 +57,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext { groupCall: nil ))) - self.disposable = (getGroupCallParticipants(account: account, callId: call.id, accessHash: call.accessHash, offset: "", limit: 100) + self.disposable = (getGroupCallParticipants(account: account, callId: call.id, accessHash: call.accessHash, offset: "", peerIds: [], limit: 100) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) @@ -1476,4 +1476,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { public func updateDefaultParticipantsAreMuted(isMuted: Bool) { self.participantsContext?.updateDefaultParticipantsAreMuted(isMuted: isMuted) } + + public func loadMoreMembers(token: String) { + self.participantsContext?.loadMore(token: token) + } } diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 262955ad44..fd2d948424 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -375,14 +375,14 @@ public final class VoiceChatController: ViewController { } } - private func preparedTransition(from fromEntries: [ListEntry], to toEntries: [ListEntry], isLoading: Bool, isEmpty: Bool, crossFade: Bool, context: AccountContext, presentationData: PresentationData, interaction: Interaction) -> ListTransition { + private func preparedTransition(from fromEntries: [ListEntry], to toEntries: [ListEntry], isLoading: Bool, isEmpty: Bool, crossFade: Bool, animated: Bool, context: AccountContext, presentationData: PresentationData, interaction: Interaction) -> ListTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) } let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) } - return ListTransition(deletions: deletions, insertions: insertions, updates: updates, isLoading: isLoading, isEmpty: isEmpty, crossFade: crossFade, count: toEntries.count, animated: true) + return ListTransition(deletions: deletions, insertions: insertions, updates: updates, isLoading: isLoading, isEmpty: isEmpty, crossFade: crossFade, count: toEntries.count, animated: animated) } private weak var controller: VoiceChatController? @@ -424,7 +424,7 @@ public final class VoiceChatController: ViewController { private var currentTitle: String = "" private var currentSubtitle: String = "" - private var currentCallMembers: [GroupCallParticipantsContext.Participant]? + private var currentCallMembers: ([GroupCallParticipantsContext.Participant], String?)? private var currentInvitedPeers: [Peer]? private var currentSpeakingPeers: Set? private var currentContentOffset: CGFloat? @@ -445,6 +445,8 @@ public final class VoiceChatController: ViewController { private var callState: PresentationGroupCallState? + private var currentLoadToken: String? + private var effectiveMuteState: GroupCallParticipantsContext.Participant.MuteState? { if self.pushingToTalk { return nil @@ -577,7 +579,7 @@ public final class VoiceChatController: ViewController { } var filters: [ChannelMembersSearchFilter] = [] - if let currentCallMembers = strongSelf.currentCallMembers { + if let (currentCallMembers, _) = strongSelf.currentCallMembers { filters.append(.disable(Array(currentCallMembers.map { $0.peer.id }))) } if let groupPeer = groupPeer as? TelegramChannel { @@ -1030,7 +1032,7 @@ public final class VoiceChatController: ViewController { } } - strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: callMembers?.participants ?? [], invitedPeers: invitedPeers, speakingPeers: callMembers?.speakingParticipants ?? []) + strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: (callMembers?.participants ?? [], callMembers?.loadMoreToken), invitedPeers: invitedPeers, speakingPeers: callMembers?.speakingParticipants ?? []) let subtitle = strongSelf.presentationData.strings.VoiceChat_Panel_Members(Int32(max(1, callMembers?.totalCount ?? 0))) strongSelf.currentSubtitle = subtitle @@ -1059,7 +1061,7 @@ public final class VoiceChatController: ViewController { } if !strongSelf.didSetDataReady { strongSelf.accountPeer = accountPeer - strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: strongSelf.currentCallMembers ?? [], invitedPeers: strongSelf.currentInvitedPeers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? Set()) + strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, callMembers: strongSelf.currentCallMembers ?? ([], nil), invitedPeers: strongSelf.currentInvitedPeers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? Set()) strongSelf.didSetDataReady = true strongSelf.controller?.dataReady.set(true) @@ -1223,6 +1225,18 @@ public final class VoiceChatController: ViewController { } } + self.listNode.visibleBottomContentOffsetChanged = { [weak self] offset in + guard let strongSelf = self else { + return + } + if case let .known(value) = offset, value < 100.0 { + if let loadMoreToken = strongSelf.currentCallMembers?.1 { + strongSelf.currentLoadToken = loadMoreToken + strongSelf.call.loadMoreMembers(token: loadMoreToken) + } + } + } + self.memberEventsDisposable.set((self.call.memberEvents |> deliverOnMainQueue).start(next: { [weak self] event in guard let strongSelf = self else { @@ -1336,7 +1350,7 @@ public final class VoiceChatController: ViewController { self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring)) } - self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? [], invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set()) + self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? ([], nil), invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set()) } @objc private func actionButtonPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer) { @@ -1381,7 +1395,7 @@ public final class VoiceChatController: ViewController { if let (layout, navigationHeight) = self.validLayout { self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring)) } - self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? [], invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set()) + self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? ([], nil), invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set()) default: break } @@ -2030,7 +2044,12 @@ public final class VoiceChatController: ViewController { } } - private func updateMembers(muteState: GroupCallParticipantsContext.Participant.MuteState?, callMembers: [GroupCallParticipantsContext.Participant], invitedPeers: [Peer], speakingPeers: Set) { + private func updateMembers(muteState: GroupCallParticipantsContext.Participant.MuteState?, callMembers: ([GroupCallParticipantsContext.Participant], String?), invitedPeers: [Peer], speakingPeers: Set) { + var disableAnimation = false + if self.currentCallMembers?.1 != callMembers.1 { + disableAnimation = true + } + self.currentCallMembers = callMembers self.currentSpeakingPeers = speakingPeers self.currentInvitedPeers = invitedPeers @@ -2044,7 +2063,7 @@ public final class VoiceChatController: ViewController { entries.append(.invite(self.presentationData.theme, self.presentationData.strings, self.presentationData.strings.VoiceChat_InviteMember)) - for member in callMembers { + for member in callMembers.0 { if processedPeerIds.contains(member.peer.id) { continue } @@ -2109,7 +2128,7 @@ public final class VoiceChatController: ViewController { self.currentEntries = entries let presentationData = self.presentationData.withUpdated(theme: self.darkTheme) - let transition = preparedTransition(from: previousEntries, to: entries, isLoading: false, isEmpty: false, crossFade: false, context: self.context, presentationData: presentationData, interaction: self.itemInteraction!) + let transition = preparedTransition(from: previousEntries, to: entries, isLoading: false, isEmpty: false, crossFade: false, animated: !disableAnimation, context: self.context, presentationData: presentationData, interaction: self.itemInteraction!) self.enqueueTransition(transition) } diff --git a/submodules/TelegramCore/Sources/GroupCalls.swift b/submodules/TelegramCore/Sources/GroupCalls.swift index 8cd16a414d..8a02f438c9 100644 --- a/submodules/TelegramCore/Sources/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/GroupCalls.swift @@ -187,8 +187,8 @@ public enum GetGroupCallParticipantsError { case generic } -public func getGroupCallParticipants(account: Account, callId: Int64, accessHash: Int64, offset: String, limit: Int32) -> Signal { - return account.network.request(Api.functions.phone.getGroupParticipants(call: .inputGroupCall(id: callId, accessHash: accessHash), ids: [], sources: [], offset: offset, limit: limit)) +public func getGroupCallParticipants(account: Account, callId: Int64, accessHash: Int64, offset: String, peerIds: [PeerId], limit: Int32) -> Signal { + return account.network.request(Api.functions.phone.getGroupParticipants(call: .inputGroupCall(id: callId, accessHash: accessHash), ids: peerIds.map { $0.id }, sources: [], offset: offset, limit: limit)) |> mapError { _ -> GetGroupCallParticipantsError in return .generic } @@ -368,7 +368,7 @@ public func joinGroupCall(account: Account, peerId: PeerId, callId: Int64, acces |> mapError { _ -> JoinGroupCallError in return .generic }, - getGroupCallParticipants(account: account, callId: callId, accessHash: accessHash, offset: "", limit: 100) + getGroupCallParticipants(account: account, callId: callId, accessHash: accessHash, offset: "", peerIds: [], limit: 100) |> mapError { _ -> JoinGroupCallError in return .generic }, @@ -718,7 +718,7 @@ public final class GroupCallParticipantsContext { private let id: Int64 private let accessHash: Int64 - private var hasReceivedSpeackingParticipantsReport: Bool = false + private var hasReceivedSpeakingParticipantsReport: Bool = false private var stateValue: InternalState { didSet { @@ -770,6 +770,10 @@ public final class GroupCallParticipantsContext { private let updatesDisposable = MetaDisposable() private var activitiesDisposable: Disposable? + private var isLoadingMore: Bool = false + private var shouldResetStateFromServer: Bool = false + private var missingPeerIds = Set() + private let updateDefaultMuteDisposable = MetaDisposable() public init(account: Account, peerId: PeerId, id: Int64, accessHash: Int64, state: State) { @@ -802,11 +806,14 @@ public final class GroupCallParticipantsContext { return } - strongSelf.activeSpeakersValue = Set(activities.map { item -> PeerId in + let peerIds = Set(activities.map { item -> PeerId in item.0 }) + strongSelf.activeSpeakersValue = peerIds - if !strongSelf.hasReceivedSpeackingParticipantsReport { + strongSelf.ensureHaveParticipants(peerIds: peerIds) + + if !strongSelf.hasReceivedSpeakingParticipantsReport { var updatedParticipants = strongSelf.stateValue.state.participants var indexMap: [PeerId: Int] = [:] for i in 0 ..< updatedParticipants.count { @@ -885,7 +892,7 @@ public final class GroupCallParticipantsContext { public func reportSpeakingParticipants(ids: [PeerId]) { if !ids.isEmpty { - self.hasReceivedSpeackingParticipantsReport = true + self.hasReceivedSpeakingParticipantsReport = true } let strongSelf = self @@ -942,6 +949,75 @@ public final class GroupCallParticipantsContext { } } + private func ensureHaveParticipants(peerIds: Set) { + var missingPeerIds = Set() + + var existingParticipantIds = Set() + for participant in self.stateValue.state.participants { + existingParticipantIds.insert(participant.peer.id) + } + + for peerId in peerIds { + if !existingParticipantIds.contains(peerId) { + missingPeerIds.insert(peerId) + } + } + + if !missingPeerIds.isEmpty { + self.missingPeerIds.formUnion(missingPeerIds) + self.loadMissingPeers() + } + } + + private func loadMissingPeers() { + if self.missingPeerIds.isEmpty { + return + } + if self.isLoadingMore { + return + } + self.isLoadingMore = true + + let peerIds = self.missingPeerIds + + self.disposable.set((getGroupCallParticipants(account: self.account, callId: self.id, accessHash: self.accessHash, offset: "", peerIds: Array(peerIds), limit: 100) + |> deliverOnMainQueue).start(next: { [weak self] state in + guard let strongSelf = self else { + return + } + strongSelf.isLoadingMore = false + + strongSelf.missingPeerIds.subtract(peerIds) + + var updatedState = strongSelf.stateValue.state + + var existingParticipantIds = Set() + for participant in updatedState.participants { + existingParticipantIds.insert(participant.peer.id) + } + for participant in state.participants { + if existingParticipantIds.contains(participant.peer.id) { + continue + } + existingParticipantIds.insert(participant.peer.id) + updatedState.participants.append(participant) + } + + updatedState.participants.sort() + + updatedState.totalCount = max(updatedState.totalCount, state.totalCount) + updatedState.version = max(updatedState.version, updatedState.version) + + strongSelf.stateValue.state = updatedState + + if strongSelf.shouldResetStateFromServer { + strongSelf.resetStateFromServer() + } else { + strongSelf.loadMissingPeers() + } + })) + } + private func beginProcessingUpdatesIfNeeded() { if self.isProcessingUpdate { return @@ -1080,13 +1156,22 @@ public final class GroupCallParticipantsContext { } private func resetStateFromServer() { + if self.isLoadingMore { + self.shouldResetStateFromServer = true + return + } + + self.isLoadingMore = true + self.updateQueue.removeAll() - self.disposable.set((getGroupCallParticipants(account: self.account, callId: self.id, accessHash: self.accessHash, offset: "", limit: 100) + self.disposable.set((getGroupCallParticipants(account: self.account, callId: self.id, accessHash: self.accessHash, offset: "", peerIds: [], limit: 100) |> deliverOnMainQueue).start(next: { [weak self] state in guard let strongSelf = self else { return } + strongSelf.isLoadingMore = false + strongSelf.shouldResetStateFromServer = false strongSelf.stateValue.state = state strongSelf.endedProcessingUpdate() })) @@ -1189,6 +1274,49 @@ public final class GroupCallParticipantsContext { strongSelf.account.stateManager.addUpdates(updates) })) } + + public func loadMore(token: String) { + if token != self.stateValue.state.nextParticipantsFetchOffset { + Logger.shared.log("GroupCallParticipantsContext", "loadMore called with an invalid token \(token) (the valid one is \(String(describing: self.stateValue.state.nextParticipantsFetchOffset)))") + return + } + if self.isLoadingMore { + return + } + self.isLoadingMore = true + + self.disposable.set((getGroupCallParticipants(account: self.account, callId: self.id, accessHash: self.accessHash, offset: token, peerIds: [], limit: 100) + |> deliverOnMainQueue).start(next: { [weak self] state in + guard let strongSelf = self else { + return + } + strongSelf.isLoadingMore = false + + var updatedState = strongSelf.stateValue.state + + var existingParticipantIds = Set() + for participant in updatedState.participants { + existingParticipantIds.insert(participant.peer.id) + } + for participant in state.participants { + if existingParticipantIds.contains(participant.peer.id) { + continue + } + existingParticipantIds.insert(participant.peer.id) + updatedState.participants.append(participant) + } + + updatedState.nextParticipantsFetchOffset = state.nextParticipantsFetchOffset + updatedState.totalCount = max(updatedState.totalCount, state.totalCount) + updatedState.version = max(updatedState.version, updatedState.version) + + strongSelf.stateValue.state = updatedState + + if strongSelf.shouldResetStateFromServer { + strongSelf.resetStateFromServer() + } + })) + } } extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate {