mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
Voice chat improvements
This commit is contained in:
parent
dd6d223b1c
commit
026ea55ca2
@ -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",
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
@ -1273,8 +1311,27 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
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
|
||||||
|
|||||||
@ -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)
|
||||||
@ -378,7 +378,7 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal
|
|||||||
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)
|
|
||||||
|> mapToSignal { inputChannel -> Signal<Api.channels.ChannelParticipants, JoinGroupCallError> in
|
|
||||||
guard let inputChannel = inputChannel else {
|
|
||||||
return .fail(.generic)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return combineLatest(
|
||||||
|
joinRequest,
|
||||||
|
getParticipantsRequest
|
||||||
|
)
|
||||||
|
|> mapToSignal { updates, participantsState -> Signal<JoinGroupCallResult, JoinGroupCallError> in
|
||||||
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,18 +454,7 @@ 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)
|
|
||||||
|
|
||||||
state.adminIds = adminIds
|
|
||||||
|
|
||||||
switch result {
|
|
||||||
case let .groupCall(call, _, _, chats, users):
|
|
||||||
guard let _ = GroupCallInfo(call) else {
|
|
||||||
return .fail(.generic)
|
|
||||||
}
|
|
||||||
|
|
||||||
apiUsers.append(contentsOf: users)
|
|
||||||
|
|
||||||
var peers: [Peer] = []
|
var peers: [Peer] = []
|
||||||
var peerPresences: [PeerId: PeerPresence] = [:]
|
var peerPresences: [PeerId: PeerPresence] = [:]
|
||||||
@ -538,12 +467,6 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for chat in chats {
|
|
||||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
|
||||||
peers.append(peer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let connectionMode: JoinGroupCallResult.ConnectionMode
|
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 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 {
|
if let stream = dict["stream"] as? Bool, stream {
|
||||||
@ -581,7 +504,6 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit 4a953747375b8648f8b66e9572b59b10f7b769a1
|
Subproject commit 30070e3d277debf4a69e0df001faffe571465614
|
||||||
Loading…
x
Reference in New Issue
Block a user