Voice chat improvements

This commit is contained in:
Ali 2021-03-12 15:21:53 +04:00
parent dd6d223b1c
commit 026ea55ca2
4 changed files with 142 additions and 152 deletions

View File

@ -41,6 +41,7 @@ swift_library(
"//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode", "//submodules/AnimatedCountLabelNode:AnimatedCountLabelNode",
"//submodules/DeviceProximity:DeviceProximity", "//submodules/DeviceProximity:DeviceProximity",
"//submodules/ManagedAnimationNode:ManagedAnimationNode", "//submodules/ManagedAnimationNode:ManagedAnimationNode",
"//submodules/TemporaryCachedPeerDataManager:TemporaryCachedPeerDataManager",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -16,6 +16,7 @@ import UniversalMediaPlayer
import AccountContext import AccountContext
import DeviceProximity import DeviceProximity
import UndoUI import UndoUI
import TemporaryCachedPeerDataManager
private extension GroupCallParticipantsContext.Participant { private extension GroupCallParticipantsContext.Participant {
var allSsrcs: Set<UInt32> { var allSsrcs: Set<UInt32> {
@ -353,10 +354,13 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
public let peerId: PeerId public let peerId: PeerId
private let invite: String? private let invite: String?
private var joinAsPeerId: PeerId private var joinAsPeerId: PeerId
private var ignorePreviousJoinAsPeerId: (PeerId, UInt32)?
public private(set) var isVideo: Bool public private(set) var isVideo: Bool
private let temporaryJoinTimestamp: Int32 private var temporaryJoinTimestamp: Int32
private var temporaryActivityTimestamp: Double?
private var temporaryActivityRank: Int?
private var internalState: InternalState = .requesting private var internalState: InternalState = .requesting
private let internalStatePromise = Promise<InternalState>(.requesting) private let internalStatePromise = Promise<InternalState>(.requesting)
@ -874,8 +878,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
joinTimestamp: strongSelf.temporaryJoinTimestamp, joinTimestamp: strongSelf.temporaryJoinTimestamp,
raiseHandRating: nil, raiseHandRating: nil,
hasRaiseHand: false, hasRaiseHand: false,
activityTimestamp: nil, activityTimestamp: strongSelf.temporaryActivityTimestamp,
activityRank: nil, activityRank: strongSelf.temporaryActivityRank,
muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
volume: nil, volume: nil,
about: about about: about
@ -1005,6 +1009,35 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let peerAdminIds: Signal<[PeerId], NoError>
let peerId = strongSelf.peerId
if strongSelf.peerId.namespace == Namespaces.Peer.CloudChannel {
peerAdminIds = strongSelf.account.postbox.transaction { transaction -> [PeerId] in
var result: [PeerId] = []
if let entry = transaction.retrieveItemCacheEntry(id: cachedChannelAdminRanksEntryId(peerId: peerId)) as? CachedChannelAdminRanks {
result = Array(entry.ranks.keys)
}
return result
}
} else {
peerAdminIds = strongSelf.account.postbox.transaction { transaction -> [PeerId] in
var result: [PeerId] = []
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedGroupData {
if let participants = cachedData.participants {
for participant in participants.participants {
if case .creator = participant {
result.append(participant.peerId)
} else if case .admin = participant {
result.append(participant.peerId)
}
}
}
}
return result
}
}
strongSelf.currentLocalSsrc = ssrc strongSelf.currentLocalSsrc = ssrc
strongSelf.requestDisposable.set((joinGroupCall( strongSelf.requestDisposable.set((joinGroupCall(
account: strongSelf.account, account: strongSelf.account,
@ -1014,6 +1047,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
accessHash: callInfo.accessHash, accessHash: callInfo.accessHash,
preferMuted: true, preferMuted: true,
joinPayload: joinPayload, joinPayload: joinPayload,
peerAdminIds: peerAdminIds,
inviteHash: strongSelf.invite inviteHash: strongSelf.invite
) )
|> deliverOnMainQueue).start(next: { joinCallResult in |> deliverOnMainQueue).start(next: { joinCallResult in
@ -1223,8 +1257,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
self.temporaryParticipantsContext = nil self.temporaryParticipantsContext = nil
self.participantsContext = participantsContext self.participantsContext = participantsContext
let myPeerId = self.joinAsPeerId let myPeerId = self.joinAsPeerId
let myPeer = self.accountContext.account.postbox.transaction { transaction -> Peer? in let myPeer = self.accountContext.account.postbox.transaction { transaction -> (Peer, CachedPeerData?)? in
return transaction.getPeer(myPeerId) if let peer = transaction.getPeer(myPeerId) {
return (peer, transaction.getPeerCachedData(peerId: myPeerId))
} else {
return nil
}
} }
self.participantsContextStateDisposable.set(combineLatest(queue: .mainQueue(), self.participantsContextStateDisposable.set(combineLatest(queue: .mainQueue(),
participantsContext.state, participantsContext.state,
@ -1234,7 +1272,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
myPeer, myPeer,
accountContext.account.postbox.peerView(id: peerId), accountContext.account.postbox.peerView(id: peerId),
self.isReconnectingAsSpeakerPromise.get() self.isReconnectingAsSpeakerPromise.get()
).start(next: { [weak self] state, activeSpeakers, speakingParticipants, adminIds, myPeer, view, isReconnectingAsSpeaker in ).start(next: { [weak self] state, activeSpeakers, speakingParticipants, adminIds, myPeerAndCachedData, view, isReconnectingAsSpeaker in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
@ -1271,10 +1309,29 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
var updatedInvitedPeers = strongSelf.invitedPeersValue var updatedInvitedPeers = strongSelf.invitedPeersValue
var didUpdateInvitedPeers = false var didUpdateInvitedPeers = false
var participants = state.participants var participants = state.participants
if let (ignorePeerId, ignoreSsrc) = strongSelf.ignorePreviousJoinAsPeerId {
for i in 0 ..< participants.count {
if participants[i].peer.id == ignorePeerId && participants[i].ssrc == ignoreSsrc {
participants.remove(at: i)
break
}
}
}
if !participants.contains(where: { $0.peer.id == myPeerId }) { if !participants.contains(where: { $0.peer.id == myPeerId }) {
if let myPeer = myPeer { if let (myPeer, cachedData) = myPeerAndCachedData {
let about: String?
if let cachedData = cachedData as? CachedUserData {
about = cachedData.about
} else if let cachedData = cachedData as? CachedUserData {
about = cachedData.about
} else {
about = nil
}
participants.append(GroupCallParticipantsContext.Participant( participants.append(GroupCallParticipantsContext.Participant(
peer: myPeer, peer: myPeer,
ssrc: nil, ssrc: nil,
@ -1282,11 +1339,11 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
joinTimestamp: strongSelf.temporaryJoinTimestamp, joinTimestamp: strongSelf.temporaryJoinTimestamp,
raiseHandRating: nil, raiseHandRating: nil,
hasRaiseHand: false, hasRaiseHand: false,
activityTimestamp: nil, activityTimestamp: strongSelf.temporaryActivityTimestamp,
activityRank: nil, activityRank: strongSelf.temporaryActivityRank,
muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false),
volume: nil, volume: nil,
about: nil about: about
)) ))
participants.sort() participants.sort()
} }
@ -1601,9 +1658,19 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} }
let previousPeerId = strongSelf.joinAsPeerId let previousPeerId = strongSelf.joinAsPeerId
if let localSsrc = strongSelf.currentLocalSsrc {
strongSelf.ignorePreviousJoinAsPeerId = (previousPeerId, localSsrc)
}
strongSelf.joinAsPeerId = peerId strongSelf.joinAsPeerId = peerId
if let participantsContext = strongSelf.participantsContext, let immediateState = participantsContext.immediateState { if let participantsContext = strongSelf.participantsContext, let immediateState = participantsContext.immediateState {
for participant in immediateState.participants {
if participant.peer.id == previousPeerId {
strongSelf.temporaryJoinTimestamp = participant.joinTimestamp
strongSelf.temporaryActivityTimestamp = participant.activityTimestamp
strongSelf.temporaryActivityRank = participant.activityRank
}
}
strongSelf.switchToTemporaryParticipantsContext(sourceContext: participantsContext, initialState: immediateState, oldMyPeerId: previousPeerId) strongSelf.switchToTemporaryParticipantsContext(sourceContext: participantsContext, initialState: immediateState, oldMyPeerId: previousPeerId)
} else { } else {
strongSelf.stateValue.myPeerId = peerId strongSelf.stateValue.myPeerId = peerId

View File

@ -356,7 +356,7 @@ public struct JoinGroupCallResult {
public var connectionMode: ConnectionMode public var connectionMode: ConnectionMode
} }
public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, callId: Int64, accessHash: Int64, preferMuted: Bool, joinPayload: String, inviteHash: String? = nil) -> Signal<JoinGroupCallResult, JoinGroupCallError> { public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, callId: Int64, accessHash: Int64, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
return account.postbox.transaction { transaction -> Api.InputPeer? in return account.postbox.transaction { transaction -> Api.InputPeer? in
if let joinAs = joinAs { if let joinAs = joinAs {
return transaction.getPeer(joinAs).flatMap(apiInputPeer) return transaction.getPeer(joinAs).flatMap(apiInputPeer)
@ -377,8 +377,8 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal
if let _ = inviteHash { if let _ = inviteHash {
flags |= (1 << 1) flags |= (1 << 1)
} }
return account.network.request(Api.functions.phone.joinGroupCall(flags: flags, call: .inputGroupCall(id: callId, accessHash: accessHash), joinAs: inputJoinAs, inviteHash: inviteHash, params: .dataJSON(data: joinPayload))) let joinRequest = account.network.request(Api.functions.phone.joinGroupCall(flags: flags, call: .inputGroupCall(id: callId, accessHash: accessHash), joinAs: inputJoinAs, inviteHash: inviteHash, params: .dataJSON(data: joinPayload)))
|> mapError { error -> JoinGroupCallError in |> mapError { error -> JoinGroupCallError in
if error.errorDescription == "GROUPCALL_ANONYMOUS_FORBIDDEN" { if error.errorDescription == "GROUPCALL_ANONYMOUS_FORBIDDEN" {
return .anonymousNotAllowed return .anonymousNotAllowed
@ -387,92 +387,32 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal
} }
return .generic return .generic
} }
|> mapToSignal { updates -> Signal<JoinGroupCallResult, JoinGroupCallError> in
let admins: Signal<(Set<PeerId>, [Api.User]), JoinGroupCallError> let getParticipantsRequest = getGroupCallParticipants(account: account, callId: callId, accessHash: accessHash, offset: "", ssrcs: [], limit: 100)
if peerId.namespace == Namespaces.Peer.CloudChannel { |> mapError { _ -> JoinGroupCallError in
admins = account.postbox.transaction { transaction -> Api.InputChannel? in return .generic
return transaction.getPeer(peerId).flatMap(apiInputChannel) }
}
|> castError(JoinGroupCallError.self) return combineLatest(
|> mapToSignal { inputChannel -> Signal<Api.channels.ChannelParticipants, JoinGroupCallError> in joinRequest,
guard let inputChannel = inputChannel else { getParticipantsRequest
return .fail(.generic) )
} |> mapToSignal { updates, participantsState -> Signal<JoinGroupCallResult, JoinGroupCallError> in
return account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsAdmins, offset: 0, limit: 100, hash: 0))
|> `catch` { _ in
return .single(.channelParticipantsNotModified)
}
}
|> map { admins -> (Set<PeerId>, [Api.User]) in
var adminIds = Set<PeerId>()
var apiUsers: [Api.User] = []
switch admins {
case let .channelParticipants(_, participants, users):
apiUsers.append(contentsOf: users)
for participant in participants {
let parsedParticipant = ChannelParticipant(apiParticipant: participant)
switch parsedParticipant {
case .creator:
adminIds.insert(parsedParticipant.peerId)
case let .member(_, _, adminInfo, _, _):
if let adminInfo = adminInfo, adminInfo.rights.rights.contains(.canManageCalls) {
adminIds.insert(parsedParticipant.peerId)
}
}
}
default:
break
}
return (adminIds, apiUsers)
}
} else if peerId.namespace == Namespaces.Peer.CloudGroup {
admins = account.postbox.transaction { transaction -> (Set<PeerId>, [Api.User]) in
var result = Set<PeerId>()
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedGroupData {
if let participants = cachedData.participants {
for participant in participants.participants {
if case .creator = participant {
result.insert(participant.peerId)
} else if case .admin = participant {
result.insert(participant.peerId)
}
}
}
}
return (result, [])
}
|> castError(JoinGroupCallError.self)
} else {
admins = .fail(.generic)
}
let peer = account.postbox.transaction { transaction -> Peer? in let peer = account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(peerId) return transaction.getPeer(peerId)
} }
|> castError(JoinGroupCallError.self) |> castError(JoinGroupCallError.self)
return combineLatest( return combineLatest(
account.network.request(Api.functions.phone.getGroupCall(call: .inputGroupCall(id: callId, accessHash: accessHash))) peerAdminIds |> castError(JoinGroupCallError.self) |> take(1),
|> mapError { _ -> JoinGroupCallError in
return .generic
},
getGroupCallParticipants(account: account, callId: callId, accessHash: accessHash, offset: "", ssrcs: [], limit: 100)
|> mapError { _ -> JoinGroupCallError in
return .generic
},
admins,
peer peer
) )
|> mapToSignal { result, state, admins, peer -> Signal<JoinGroupCallResult, JoinGroupCallError> in |> mapToSignal { peerAdminIds, peer -> Signal<JoinGroupCallResult, JoinGroupCallError> in
guard let peer = peer else { guard let peer = peer else {
return .fail(.generic) return .fail(.generic)
} }
var state = state var state = participantsState
if let channel = peer as? TelegramChannel { if let channel = peer as? TelegramChannel {
state.isCreator = channel.flags.contains(.isCreator) state.isCreator = channel.flags.contains(.isCreator)
} else if let group = peer as? TelegramGroup { } else if let group = peer as? TelegramGroup {
@ -514,71 +454,53 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal
var apiUsers: [Api.User] = [] var apiUsers: [Api.User] = []
let (adminIds, adminUsers) = admins state.adminIds = Set(peerAdminIds)
apiUsers.append(contentsOf: adminUsers)
var peers: [Peer] = []
state.adminIds = adminIds var peerPresences: [PeerId: PeerPresence] = [:]
switch result { for user in apiUsers {
case let .groupCall(call, _, _, chats, users): let telegramUser = TelegramUser(user: user)
guard let _ = GroupCallInfo(call) else { peers.append(telegramUser)
return .fail(.generic) if let presence = TelegramUserPresence(apiUser: user) {
peerPresences[telegramUser.id] = presence
} }
apiUsers.append(contentsOf: users)
var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:]
for user in apiUsers {
let telegramUser = TelegramUser(user: user)
peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) {
peerPresences[telegramUser.id] = presence
}
}
for chat in chats {
if let peer = parseTelegramGroupOrChannel(chat: chat) {
peers.append(peer)
}
}
let connectionMode: JoinGroupCallResult.ConnectionMode
if let clientParams = parsedCall.clientParams, let clientParamsData = clientParams.data(using: .utf8), let dict = (try? JSONSerialization.jsonObject(with: clientParamsData, options: [])) as? [String: Any] {
if let stream = dict["stream"] as? Bool, stream {
connectionMode = .broadcast
} else {
connectionMode = .rtc
}
} else {
connectionMode = .broadcast
}
return account.postbox.transaction { transaction -> JoinGroupCallResult in
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
if let cachedData = cachedData as? CachedChannelData {
return cachedData.withUpdatedCallJoinPeerId(joinAs)
} else if let cachedData = cachedData as? CachedGroupData {
return cachedData.withUpdatedCallJoinPeerId(joinAs)
} else {
return cachedData
}
})
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
return updated
})
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
return JoinGroupCallResult(
callInfo: parsedCall,
state: state,
connectionMode: connectionMode
)
}
|> castError(JoinGroupCallError.self)
} }
let connectionMode: JoinGroupCallResult.ConnectionMode
if let clientParams = parsedCall.clientParams, let clientParamsData = clientParams.data(using: .utf8), let dict = (try? JSONSerialization.jsonObject(with: clientParamsData, options: [])) as? [String: Any] {
if let stream = dict["stream"] as? Bool, stream {
connectionMode = .broadcast
} else {
connectionMode = .rtc
}
} else {
connectionMode = .broadcast
}
return account.postbox.transaction { transaction -> JoinGroupCallResult in
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
if let cachedData = cachedData as? CachedChannelData {
return cachedData.withUpdatedCallJoinPeerId(joinAs)
} else if let cachedData = cachedData as? CachedGroupData {
return cachedData.withUpdatedCallJoinPeerId(joinAs)
} else {
return cachedData
}
})
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
return updated
})
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
return JoinGroupCallResult(
callInfo: parsedCall,
state: state,
connectionMode: connectionMode
)
}
|> castError(JoinGroupCallError.self)
} }
} }
} }

@ -1 +1 @@
Subproject commit 4a953747375b8648f8b66e9572b59b10f7b769a1 Subproject commit 30070e3d277debf4a69e0df001faffe571465614