Merge branch 'beta'

This commit is contained in:
Isaac 2025-04-14 21:16:36 +04:00
commit 77bfc86c6f
24 changed files with 458 additions and 7064 deletions

View File

@ -389,7 +389,10 @@ public extension GroupCallParticipantsContext.Participant {
guard let videoDescription = self.videoDescription else {
return nil
}
return PresentationGroupCallRequestedVideo(audioSsrc: audioSsrc, peerId: self.peer.id.id._internalGetInt64Value(), endpointId: videoDescription.endpointId, ssrcGroups: videoDescription.ssrcGroups.map { group in
guard let peer = self.peer else {
return nil
}
return PresentationGroupCallRequestedVideo(audioSsrc: audioSsrc, peerId: peer.id.id._internalGetInt64Value(), endpointId: videoDescription.endpointId, ssrcGroups: videoDescription.ssrcGroups.map { group in
PresentationGroupCallRequestedVideo.SsrcGroup(semantics: group.semantics, ssrcs: group.ssrcs)
}, minQuality: minQuality, maxQuality: maxQuality)
}
@ -401,7 +404,10 @@ public extension GroupCallParticipantsContext.Participant {
guard let presentationDescription = self.presentationDescription else {
return nil
}
return PresentationGroupCallRequestedVideo(audioSsrc: audioSsrc, peerId: self.peer.id.id._internalGetInt64Value(), endpointId: presentationDescription.endpointId, ssrcGroups: presentationDescription.ssrcGroups.map { group in
guard let peer = self.peer else {
return nil
}
return PresentationGroupCallRequestedVideo(audioSsrc: audioSsrc, peerId: peer.id.id._internalGetInt64Value(), endpointId: presentationDescription.endpointId, ssrcGroups: presentationDescription.ssrcGroups.map { group in
PresentationGroupCallRequestedVideo.SsrcGroup(semantics: group.semantics, ssrcs: group.ssrcs)
}, minQuality: minQuality, maxQuality: maxQuality)
}

View File

@ -57,7 +57,8 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
id: call.id,
reference: .id(id: call.id, accessHash: call.accessHash),
state: state,
previousServiceState: nil
previousServiceState: nil,
e2eContext: nil
)
self.participantsContext = context

View File

@ -425,8 +425,8 @@ public class CallStatusBarNodeImpl: CallStatusBarNode {
if let members = currentMembers {
var speakingPeers: [Peer] = []
for member in members.participants {
if members.speakingParticipants.contains(member.peer.id) {
speakingPeers.append(member.peer)
if let memberPeer = member.peer, members.speakingParticipants.contains(memberPeer.id) {
speakingPeers.append(memberPeer._asPeer())
}
}
speakingPeer = speakingPeers.first

View File

@ -395,7 +395,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
if data.info.isStream {
self.avatarsContent = self.avatarsContext.update(peers: [], animated: false)
} else {
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { EnginePeer($0.peer) }, animated: false)
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.compactMap { $0.peer }, animated: false)
if let imageDisposable = self.imageDisposable {
self.imageDisposable = nil
@ -430,7 +430,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
if let info = summaryState.info, info.isStream {
strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: [], animated: false)
} else {
strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.map { EnginePeer($0.peer) }, animated: false)
strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.compactMap { $0.peer }, animated: false)
}
if let (size, leftInset, rightInset, isHidden) = strongSelf.validLayout {
@ -513,7 +513,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
if data.info.isStream {
self.avatarsContent = self.avatarsContext.update(peers: [], animated: false)
} else {
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { EnginePeer($0.peer) }, animated: false)
self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.compactMap { $0.peer }, animated: false)
}
updateAudioLevels = true

View File

@ -908,7 +908,7 @@ public final class PresentationCallImpl: PresentationCall {
var found = false
if let members {
for participant in members.participants {
if participant.peer.id == waitForRemotePeerId {
if participant.id == .peer(waitForRemotePeerId) {
found = true
break
}
@ -921,7 +921,7 @@ public final class PresentationCallImpl: PresentationCall {
if waitForLocalVideo {
if let members {
for participant in members.participants {
if participant.peer.id == state.myPeerId {
if participant.id == .peer(state.myPeerId) {
if participant.videoDescription == nil {
return false
}
@ -932,7 +932,7 @@ public final class PresentationCallImpl: PresentationCall {
if let waitForRemoteVideo {
if let members {
for participant in members.participants {
if participant.peer.id == waitForRemoteVideo {
if participant.id == .peer(waitForRemoteVideo) {
if participant.videoDescription == nil {
return false
}

View File

@ -331,9 +331,13 @@ private final class ConferenceCallE2EContextStateImpl: ConferenceCallE2EContextS
func getEmojiState() -> Data? {
return self.call.emojiState()
}
func getParticipants() -> [ConferenceCallE2EContext.BlockchainParticipant] {
return self.call.participants().map { ConferenceCallE2EContext.BlockchainParticipant(userId: $0.userId, internalId: $0.internalId) }
}
func getParticipantIds() -> [Int64] {
return self.call.participantIds().compactMap { $0.int64Value }
return self.call.participants().map { $0.userId }
}
func applyBlock(block: Data) {
@ -1246,7 +1250,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
if let sourceContext = sourceContext, let initialState = sourceContext.immediateState {
let temporaryParticipantsContext = self.accountContext.engine.calls.groupCall(peerId: self.peerId, myPeerId: myPeerId, id: sourceContext.id, reference: sourceContext.reference, state: initialState, previousServiceState: sourceContext.serviceState)
let temporaryParticipantsContext = self.accountContext.engine.calls.groupCall(peerId: self.peerId, myPeerId: myPeerId, id: sourceContext.id, reference: sourceContext.reference, state: initialState, previousServiceState: sourceContext.serviceState, e2eContext: self.e2eContext)
self.temporaryParticipantsContext = temporaryParticipantsContext
self.participantsContextStateDisposable.set((combineLatest(queue: .mainQueue(),
myPeerData,
@ -1274,14 +1278,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
if oldMyPeerId != myPeerId {
for i in 0 ..< participants.count {
if participants[i].peer.id == oldMyPeerId {
if participants[i].id == .peer(oldMyPeerId) {
participants.remove(at: i)
break
}
}
}
if !participants.contains(where: { $0.peer.id == myPeerId }) {
if !participants.contains(where: { $0.id == .peer(myPeerId) }) {
if let (myPeer, aboutText) = myPeerData {
let about: String?
if let aboutText = aboutText {
@ -1290,7 +1294,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
about = " "
}
participants.append(GroupCallParticipantsContext.Participant(
peer: myPeer._asPeer(),
id: .peer(myPeer.id),
peer: myPeer,
ssrc: nil,
videoDescription: nil,
presentationDescription: nil,
@ -1315,7 +1320,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
topParticipants.append(participant)
}
if let index = updatedInvitedPeers.firstIndex(where: { $0.id == participant.peer.id }) {
if let index = updatedInvitedPeers.firstIndex(where: { participant.id == .peer($0.id) }) {
updatedInvitedPeers.remove(at: index)
didUpdateInvitedPeers = true
}
@ -1370,7 +1375,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
about = " "
}
participants.append(GroupCallParticipantsContext.Participant(
peer: myPeer._asPeer(),
id: .peer(myPeer.id),
peer: myPeer,
ssrc: nil,
videoDescription: nil,
presentationDescription: nil,
@ -1495,7 +1501,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
isStream: callInfo.isStream,
version: 0
),
previousServiceState: nil
previousServiceState: nil,
e2eContext: self.e2eContext
)
self.temporaryParticipantsContext = nil
self.participantsContext = participantsContext
@ -1555,7 +1562,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
about = " "
}
participants.append(GroupCallParticipantsContext.Participant(
peer: myPeer._asPeer(),
id: .peer(myPeer.id),
peer: myPeer,
ssrc: nil,
videoDescription: nil,
presentationDescription: nil,
@ -1967,11 +1975,11 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
self.ssrcMapping.removeAll()
for participant in joinCallResult.state.participants {
if let ssrc = participant.ssrc {
self.ssrcMapping[ssrc] = SsrcMapping(peerId: participant.peer.id, isPresentation: false)
if let ssrc = participant.ssrc, let participantPeer = participant.peer {
self.ssrcMapping[ssrc] = SsrcMapping(peerId: participantPeer.id, isPresentation: false)
}
if let presentationSsrc = participant.presentationDescription?.audioSsrc {
self.ssrcMapping[presentationSsrc] = SsrcMapping(peerId: participant.peer.id, isPresentation: true)
if let presentationSsrc = participant.presentationDescription?.audioSsrc, let participantPeer = participant.peer {
self.ssrcMapping[presentationSsrc] = SsrcMapping(peerId: participantPeer.id, isPresentation: true)
}
}
@ -2268,7 +2276,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
id: callInfo.id,
reference: reference,
state: initialState,
previousServiceState: serviceState
previousServiceState: serviceState,
e2eContext: self.e2eContext
)
self.temporaryParticipantsContext = nil
self.participantsContext = participantsContext
@ -2367,14 +2376,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
if let (ignorePeerId, ignoreSsrc) = self.ignorePreviousJoinAsPeerId {
for i in 0 ..< participants.count {
if participants[i].peer.id == ignorePeerId && participants[i].ssrc == ignoreSsrc {
if participants[i].id == .peer(ignorePeerId) && participants[i].ssrc == ignoreSsrc {
participants.remove(at: i)
break
}
}
}
if !participants.contains(where: { $0.peer.id == myPeerId }) && !self.leaving {
if !participants.contains(where: { $0.id == .peer(myPeerId) }) && !self.leaving {
if let (myPeer, cachedData) = myPeerAndCachedData {
let about: String?
if let cachedData = cachedData as? CachedUserData {
@ -2386,7 +2395,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
participants.append(GroupCallParticipantsContext.Participant(
peer: myPeer,
id: .peer(myPeer.id),
peer: EnginePeer(myPeer),
ssrc: nil,
videoDescription: nil,
presentationDescription: nil,
@ -2415,13 +2425,17 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
if let ssrc = participant.ssrc {
self.ssrcMapping[ssrc] = SsrcMapping(peerId: participant.peer.id, isPresentation: false)
if let participantPeer = participant.peer {
self.ssrcMapping[ssrc] = SsrcMapping(peerId: participantPeer.id, isPresentation: false)
}
}
if let presentationSsrc = participant.presentationDescription?.audioSsrc {
self.ssrcMapping[presentationSsrc] = SsrcMapping(peerId: participant.peer.id, isPresentation: true)
if let participantPeer = participant.peer {
self.ssrcMapping[presentationSsrc] = SsrcMapping(peerId: participantPeer.id, isPresentation: true)
}
}
if participant.peer.id == self.joinAsPeerId {
if participant.id == .peer(self.joinAsPeerId) {
if let (myPeer, cachedData) = myPeerAndCachedData {
let about: String?
if let cachedData = cachedData as? CachedUserData {
@ -2431,7 +2445,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} else {
about = " "
}
participant.peer = myPeer
participant.peer = EnginePeer(myPeer)
participant.about = about
}
@ -2531,7 +2545,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}
}
if let index = updatedInvitedPeers.firstIndex(where: { $0.id == participant.peer.id }) {
if let index = updatedInvitedPeers.firstIndex(where: { participant.id == .peer($0.id) }) {
updatedInvitedPeers.remove(at: index)
didUpdateInvitedPeers = true
}
@ -2646,24 +2660,28 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
if remainingSsrcs.contains(audioSsrc) {
remainingSsrcs.remove(audioSsrc)
result.append(OngoingGroupCallContext.MediaChannelDescription(
kind: .audio,
peerId: participant.peer.id.id._internalGetInt64Value(),
audioSsrc: audioSsrc,
videoDescription: nil
))
if let participantPeer = participant.peer {
result.append(OngoingGroupCallContext.MediaChannelDescription(
kind: .audio,
peerId: participantPeer.id.id._internalGetInt64Value(),
audioSsrc: audioSsrc,
videoDescription: nil
))
}
}
if let screencastSsrc = participant.presentationDescription?.audioSsrc {
if remainingSsrcs.contains(screencastSsrc) {
remainingSsrcs.remove(screencastSsrc)
result.append(OngoingGroupCallContext.MediaChannelDescription(
kind: .audio,
peerId: participant.peer.id.id._internalGetInt64Value(),
audioSsrc: screencastSsrc,
videoDescription: nil
))
if let participantPeer = participant.peer {
result.append(OngoingGroupCallContext.MediaChannelDescription(
kind: .audio,
peerId: participantPeer.id.id._internalGetInt64Value(),
audioSsrc: screencastSsrc,
videoDescription: nil
))
}
}
}
}
@ -2849,7 +2867,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
if let participantsContext = self.participantsContext, let immediateState = participantsContext.immediateState {
for participant in immediateState.participants {
if participant.peer.id == previousPeerId {
if participant.id == .peer(previousPeerId) {
self.temporaryJoinTimestamp = participant.joinTimestamp
self.temporaryActivityTimestamp = participant.activityTimestamp
self.temporaryActivityRank = participant.activityRank
@ -3008,7 +3026,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
return
}
for participant in membersValue.participants {
if participant.peer.id == self.joinAsPeerId {
if participant.id == .peer(self.joinAsPeerId) {
if participant.hasRaiseHand {
return
}
@ -3024,7 +3042,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
return
}
for participant in membersValue.participants {
if participant.peer.id == self.joinAsPeerId {
if participant.id == .peer(self.joinAsPeerId) {
if !participant.hasRaiseHand {
return
}

View File

@ -143,7 +143,9 @@ final class VideoChatParticipantThumbnailComponent: Component {
gesture.cancel()
return
}
component.contextAction?(EnginePeer(component.participant.peer), self.extractedContainerView, gesture)
if let participantPeer = component.participant.peer {
component.contextAction?(participantPeer, self.extractedContainerView, gesture)
}
}
}
@ -213,10 +215,10 @@ final class VideoChatParticipantThumbnailComponent: Component {
let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - avatarSize.width) * 0.5), y: 7.0), size: avatarSize)
transition.setFrame(view: avatarNode.view, frame: avatarFrame)
avatarNode.updateSize(size: avatarSize)
if component.participant.peer.smallProfileImage != nil {
avatarNode.setPeerV2(context: component.call.accountContext, theme: component.theme, peer: EnginePeer(component.participant.peer), displayDimensions: avatarSize)
if component.participant.peer?.smallProfileImage != nil {
avatarNode.setPeerV2(context: component.call.accountContext, theme: component.theme, peer: component.participant.peer, displayDimensions: avatarSize)
} else {
avatarNode.setPeer(context: component.call.accountContext, theme: component.theme, peer: EnginePeer(component.participant.peer), displayDimensions: avatarSize)
avatarNode.setPeer(context: component.call.accountContext, theme: component.theme, peer: component.participant.peer, displayDimensions: avatarSize)
}
let muteStatusSize = self.muteStatus.update(
@ -241,7 +243,7 @@ final class VideoChatParticipantThumbnailComponent: Component {
let titleSize = self.title.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: EnginePeer(component.participant.peer).compactDisplayTitle, font: Font.semibold(13.0), textColor: .white))
text: .plain(NSAttributedString(string: component.participant.peer?.compactDisplayTitle ?? "User \(component.participant.id)", font: Font.semibold(13.0), textColor: .white))
)),
environment: {},
containerSize: CGSize(width: availableSize.width - 6.0 * 2.0 - 12.0, height: 100.0)
@ -436,10 +438,10 @@ final class VideoChatParticipantThumbnailComponent: Component {
final class VideoChatExpandedParticipantThumbnailsComponent: Component {
final class Participant: Equatable {
struct Key: Hashable {
var id: EnginePeer.Id
var id: GroupCallParticipantsContext.Participant.Id
var isPresentation: Bool
init(id: EnginePeer.Id, isPresentation: Bool) {
init(id: GroupCallParticipantsContext.Participant.Id, isPresentation: Bool) {
self.id = id
self.isPresentation = isPresentation
}
@ -449,7 +451,7 @@ final class VideoChatExpandedParticipantThumbnailsComponent: Component {
let isPresentation: Bool
var key: Key {
return Key(id: self.participant.peer.id, isPresentation: self.isPresentation)
return Key(id: self.participant.id, isPresentation: self.isPresentation)
}
init(
@ -657,6 +659,12 @@ final class VideoChatExpandedParticipantThumbnailsComponent: Component {
let itemFrame = itemLayout.frame(at: i)
let participantKey = participant.key
var isSpeaking = false
if let participantPeer = participant.participant.peer {
isSpeaking = component.speakingParticipants.contains(participantPeer.id)
}
let _ = itemView.view.update(
transition: itemTransition,
component: AnyComponent(VideoChatParticipantThumbnailComponent(
@ -665,7 +673,7 @@ final class VideoChatExpandedParticipantThumbnailsComponent: Component {
participant: participant.participant,
isPresentation: participant.isPresentation,
isSelected: component.selectedParticipant == participant.key,
isSpeaking: component.speakingParticipants.contains(participant.participant.peer.id),
isSpeaking: isSpeaking,
displayVideo: component.displayVideo,
interfaceOrientation: component.interfaceOrientation,
action: { [weak self] in

View File

@ -133,14 +133,14 @@ private final class BlobView: UIView {
final class VideoChatParticipantAvatarComponent: Component {
let call: VideoChatCall
let peer: EnginePeer
let peer: EnginePeer?
let myPeerId: EnginePeer.Id
let isSpeaking: Bool
let theme: PresentationTheme
init(
call: VideoChatCall,
peer: EnginePeer,
peer: EnginePeer?,
myPeerId: EnginePeer.Id,
isSpeaking: Bool,
theme: PresentationTheme
@ -267,7 +267,7 @@ final class VideoChatParticipantAvatarComponent: Component {
tintTransition.setTintColor(layer: blobView.blobsLayer, color: component.isSpeaking ? UIColor(rgb: 0x33C758) : component.theme.list.itemAccentColor)
}
if component.peer.smallProfileImage != nil {
if component.peer?.smallProfileImage != nil {
avatarNode.setPeerV2(
context: component.call.accountContext,
theme: component.theme,
@ -296,7 +296,7 @@ final class VideoChatParticipantAvatarComponent: Component {
var isSpeaking: Bool
}
let peerId = component.peer.id
let peerId = component.peer?.id
let levelSignal: Signal<Level?, NoError>
if peerId == component.myPeerId {
levelSignal = component.call.myAudioLevelAndSpeaking

View File

@ -245,7 +245,9 @@ final class VideoChatParticipantVideoComponent: Component {
gesture.cancel()
return
}
component.contextAction?(EnginePeer(component.participant.peer), self.extractedContainerView, gesture)
if let participantPeer = component.participant.peer {
component.contextAction?(participantPeer, self.extractedContainerView, gesture)
}
}
}
@ -316,12 +318,12 @@ final class VideoChatParticipantVideoComponent: Component {
let controlsAlpha: CGFloat = component.isUIHidden ? 0.0 : 1.0
if previousComponent == nil {
let colors = calculateAvatarColors(context: component.call.accountContext, explicitColorIndex: nil, peerId: component.participant.peer.id, nameColor: component.participant.peer.nameColor, icon: .none, theme: component.theme)
let colors = calculateAvatarColors(context: component.call.accountContext, explicitColorIndex: nil, peerId: component.participant.peer?.id, nameColor: component.participant.peer?.nameColor, icon: .none, theme: component.theme)
self.backgroundGradientView.image = generateGradientImage(size: CGSize(width: 8.0, height: 32.0), colors: colors.reversed(), locations: [0.0, 1.0], direction: .vertical)
}
if let smallProfileImage = component.participant.peer.smallProfileImage {
if let smallProfileImage = component.participant.peer?.smallProfileImage {
let blurredAvatarView: UIImageView
if let current = self.blurredAvatarView {
blurredAvatarView = current
@ -338,8 +340,8 @@ final class VideoChatParticipantVideoComponent: Component {
if self.blurredAvatarDisposable == nil {
//TODO:release synchronous
if let imageCache = component.call.accountContext.imageCache as? DirectMediaImageCache, let peerReference = PeerReference(component.participant.peer) {
if let result = imageCache.getAvatarImage(peer: peerReference, resource: MediaResourceReference.avatar(peer: peerReference, resource: smallProfileImage.resource), immediateThumbnail: component.participant.peer.profileImageRepresentations.first?.immediateThumbnailData, size: 64, synchronous: false) {
if let participantPeer = component.participant.peer, let imageCache = component.call.accountContext.imageCache as? DirectMediaImageCache, let peerReference = PeerReference(participantPeer._asPeer()) {
if let result = imageCache.getAvatarImage(peer: peerReference, resource: MediaResourceReference.avatar(peer: peerReference, resource: smallProfileImage.resource), immediateThumbnail: participantPeer.profileImageRepresentations.first?.immediateThumbnailData, size: 64, synchronous: false) {
if let image = result.image {
blurredAvatarView.image = blurredAvatarImage(image)
}
@ -402,7 +404,7 @@ final class VideoChatParticipantVideoComponent: Component {
let titleSize = self.title.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: component.participant.peer.debugDisplayTitle, font: Font.semibold(16.0), textColor: .white)),
text: .plain(NSAttributedString(string: component.participant.peer?.debugDisplayTitle ?? "User \(component.participant.id)", font: Font.semibold(16.0), textColor: .white)),
insets: titleInnerInsets,
textShadowColor: UIColor(white: 0.0, alpha: 0.7),
textShadowBlur: 8.0

View File

@ -92,10 +92,10 @@ final class VideoChatParticipantsComponent: Component {
}
struct VideoParticipantKey: Hashable {
var id: EnginePeer.Id
var id: GroupCallParticipantsContext.Participant.Id
var isPresentation: Bool
init(id: EnginePeer.Id, isPresentation: Bool) {
init(id: GroupCallParticipantsContext.Participant.Id, isPresentation: Bool) {
self.id = id
self.isPresentation = isPresentation
}
@ -608,7 +608,7 @@ final class VideoChatParticipantsComponent: Component {
let isPresentation: Bool
var key: VideoParticipantKey {
return VideoParticipantKey(id: self.participant.peer.id, isPresentation: self.isPresentation)
return VideoParticipantKey(id: self.participant.id, isPresentation: self.isPresentation)
}
init(participant: GroupCallParticipantsContext.Participant, isPresentation: Bool) {
@ -673,7 +673,7 @@ final class VideoChatParticipantsComponent: Component {
private var expandedThumbnailsView: ComponentView<Empty>?
private var expandedSpeakingToast: ComponentView<Empty>?
private var listItemViews: [EnginePeer.Id: ListItem] = [:]
private var listItemViews: [GroupCallParticipantsContext.Participant.Id: ListItem] = [:]
private let listItemViewContainer: UIView
private let listItemViewSeparatorContainer: SimpleLayer
private let listItemsBackground = ComponentView<Empty>()
@ -959,7 +959,7 @@ final class VideoChatParticipantsComponent: Component {
}
}
var visibleParticipants: [EnginePeer.Id] = []
var visibleParticipants: [GroupCallParticipantsContext.Participant.Id] = []
for index in validGridItemIndices {
let videoParticipant = self.gridParticipants[index]
@ -988,7 +988,7 @@ final class VideoChatParticipantsComponent: Component {
}
if isItemExpanded || (index >= clippedVisibleGridItemRange.minIndex && index <= clippedVisibleGridItemRange.maxIndex) {
visibleParticipants.append(videoParticipant.key.id)
visibleParticipants.append(videoParticipant.participant.id)
}
var suppressItemExpansionCollapseAnimation = false
@ -1048,6 +1048,11 @@ final class VideoChatParticipantsComponent: Component {
} else {
itemAlpha = 1.0
}
var isSpeaking = false
if let participantPeer = videoParticipant.participant.peer {
isSpeaking = component.speakingParticipants.contains(participantPeer.id)
}
let _ = itemView.view.update(
transition: itemTransition,
@ -1056,9 +1061,9 @@ final class VideoChatParticipantsComponent: Component {
strings: component.strings,
call: component.call,
participant: videoParticipant.participant,
isMyPeer: videoParticipant.participant.peer.id == component.participants?.myPeerId,
isMyPeer: videoParticipant.participant.peer?.id == component.participants?.myPeerId,
isPresentation: videoParticipant.isPresentation,
isSpeaking: component.speakingParticipants.contains(videoParticipant.participant.peer.id),
isSpeaking: isSpeaking,
maxVideoQuality: component.maxVideoQuality,
isExpanded: isItemExpanded,
isUIHidden: isItemUIHidden || self.isPinchToZoomActive,
@ -1205,23 +1210,23 @@ final class VideoChatParticipantsComponent: Component {
self.gridItemViews.removeValue(forKey: itemId)
}
var validListItemIds: [EnginePeer.Id] = []
var validListItemIds: [GroupCallParticipantsContext.Participant.Id] = []
let visibleListItemRange = itemLayout.visibleListItemRange(for: self.scrollView.bounds)
let clippedVisibleListItemRange = itemLayout.visibleListItemRange(for: clippedScrollViewBounds)
if visibleListItemRange.maxIndex >= visibleListItemRange.minIndex {
for i in visibleListItemRange.minIndex ... visibleListItemRange.maxIndex {
let itemFrame = itemLayout.listItemFrame(at: i)
let participantPeerId: EnginePeer.Id
let participantItemId: GroupCallParticipantsContext.Participant.Id
let peerItemComponent: PeerListItemComponent
if i < self.listParticipants.count {
let participant = self.listParticipants[i]
participantPeerId = participant.peer.id
participantItemId = participant.id
let subtitle: PeerListItemComponent.Subtitle
if participant.peer.id == component.call.accountContext.account.peerId {
if participant.id == .peer(component.call.accountContext.account.peerId) {
subtitle = PeerListItemComponent.Subtitle(text: component.strings.VoiceChat_You, color: .accent)
} else if component.speakingParticipants.contains(participant.peer.id) {
} else if let participantPeer = participant.peer, component.speakingParticipants.contains(participantPeer.id) {
if let volume = participant.volume, volume / 100 != 100 {
subtitle = PeerListItemComponent.Subtitle(text: component.strings.VoiceChat_StatusSpeakingVolume("\(volume / 100)%").string, color: .constructive)
} else {
@ -1233,10 +1238,14 @@ final class VideoChatParticipantsComponent: Component {
subtitle = PeerListItemComponent.Subtitle(text: component.strings.VoiceChat_StatusListening, color: .neutral)
}
var isSpeaking = false
if let participantPeer = participant.peer {
isSpeaking = component.speakingParticipants.contains(participantPeer.id)
}
let rightAccessoryComponent: AnyComponent<Empty> = AnyComponent(VideoChatParticipantStatusComponent(
muteState: participant.muteState,
hasRaiseHand: participant.hasRaiseHand,
isSpeaking: component.speakingParticipants.contains(participant.peer.id),
isSpeaking: isSpeaking,
theme: component.theme
))
@ -1246,15 +1255,15 @@ final class VideoChatParticipantsComponent: Component {
strings: component.strings,
style: .generic,
sideInset: 0.0,
title: EnginePeer(participant.peer).displayTitle(strings: component.strings, displayOrder: .firstLast),
title: participant.peer?.displayTitle(strings: component.strings, displayOrder: .firstLast) ?? "User \(participant.id)",
avatarComponent: AnyComponent(VideoChatParticipantAvatarComponent(
call: component.call,
peer: EnginePeer(participant.peer),
peer: participant.peer,
myPeerId: component.participants?.myPeerId ?? component.call.accountContext.account.peerId,
isSpeaking: component.speakingParticipants.contains(participant.peer.id),
isSpeaking: isSpeaking,
theme: component.theme
)),
peer: EnginePeer(participant.peer),
peer: participant.peer,
subtitle: subtitle,
subtitleAccessory: .none,
presence: nil,
@ -1280,7 +1289,7 @@ final class VideoChatParticipantsComponent: Component {
)
} else {
let invitedPeer = component.invitedPeers[i - self.listParticipants.count]
participantPeerId = invitedPeer.peer.id
participantItemId = .peer(invitedPeer.peer.id)
let subtitle: PeerListItemComponent.Subtitle = PeerListItemComponent.Subtitle(text: component.strings.VoiceChat_StatusInvited, color: .neutral)
@ -1328,20 +1337,20 @@ final class VideoChatParticipantsComponent: Component {
)
}
validListItemIds.append(participantPeerId)
validListItemIds.append(participantItemId)
if i >= clippedVisibleListItemRange.minIndex && i <= clippedVisibleListItemRange.maxIndex {
visibleParticipants.append(participantPeerId)
visibleParticipants.append(participantItemId)
}
var itemTransition = transition
let itemView: ListItem
if let current = self.listItemViews[participantPeerId] {
if let current = self.listItemViews[participantItemId] {
itemView = current
} else {
itemTransition = itemTransition.withAnimation(.none)
itemView = ListItem()
self.listItemViews[participantPeerId] = itemView
self.listItemViews[participantItemId] = itemView
}
let _ = itemView.view.update(
@ -1375,7 +1384,7 @@ final class VideoChatParticipantsComponent: Component {
}
}
var removedListItemIds: [EnginePeer.Id] = []
var removedListItemIds: [GroupCallParticipantsContext.Participant.Id] = []
for (itemId, itemView) in self.listItemViews {
if !validListItemIds.contains(itemId) {
removedListItemIds.append(itemId)
@ -1512,7 +1521,7 @@ final class VideoChatParticipantsComponent: Component {
expandedThumbnailsComponentView.alpha = expandedThumbnailsAlpha
let fromReferenceFrame: CGRect
if let index = self.gridParticipants.firstIndex(where: { $0.participant.peer.id == expandedVideoState.mainParticipant.id && $0.isPresentation == expandedVideoState.mainParticipant.isPresentation }) {
if let index = self.gridParticipants.firstIndex(where: { $0.participant.id == expandedVideoState.mainParticipant.id && $0.isPresentation == expandedVideoState.mainParticipant.isPresentation }) {
fromReferenceFrame = self.gridItemViewContainer.convert(itemLayout.gridItemFrame(at: index), to: self.expandedGridItemContainer)
} else {
fromReferenceFrame = previousExpandedGridItemContainerFrame
@ -1571,7 +1580,7 @@ final class VideoChatParticipantsComponent: Component {
expandedControlsComponentView.alpha = expandedControlsAlpha
let fromReferenceFrame: CGRect
if let index = self.gridParticipants.firstIndex(where: { $0.participant.peer.id == expandedVideoState.mainParticipant.id && $0.isPresentation == expandedVideoState.mainParticipant.isPresentation }) {
if let index = self.gridParticipants.firstIndex(where: { $0.participant.id == expandedVideoState.mainParticipant.id && $0.isPresentation == expandedVideoState.mainParticipant.isPresentation }) {
fromReferenceFrame = self.gridItemViewContainer.convert(itemLayout.gridItemFrame(at: index), to: self.expandedGridItemContainer)
} else {
fromReferenceFrame = previousExpandedGridItemContainerFrame
@ -1591,7 +1600,7 @@ final class VideoChatParticipantsComponent: Component {
self.expandedThumbnailsView = nil
if transition.containedViewLayoutTransition.isAnimated, let expandedThumbnailsComponentView = expandedThumbnailsView.view {
if let collapsingItemView = self.gridItemViews.values.first(where: { $0.isCollapsing }), let index = self.gridParticipants.firstIndex(where: { $0.participant.peer.id == collapsingItemView.key.id && $0.isPresentation == collapsingItemView.key.isPresentation }) {
if let collapsingItemView = self.gridItemViews.values.first(where: { $0.isCollapsing }), let index = self.gridParticipants.firstIndex(where: { $0.participant.id == collapsingItemView.key.id && $0.isPresentation == collapsingItemView.key.isPresentation }) {
let targetLocalItemFrame = itemLayout.gridItemFrame(at: index)
var targetItemFrame = self.gridItemViewContainer.convert(targetLocalItemFrame, to: self)
targetItemFrame.origin.y -= expandedGridItemContainerFrame.minY
@ -1612,7 +1621,7 @@ final class VideoChatParticipantsComponent: Component {
self.expandedControlsView = nil
if transition.containedViewLayoutTransition.isAnimated, let expandedControlsComponentView = expandedControlsView.view {
if let collapsingItemView = self.gridItemViews.values.first(where: { $0.isCollapsing }), let index = self.gridParticipants.firstIndex(where: { $0.participant.peer.id == collapsingItemView.key.id && $0.isPresentation == collapsingItemView.key.isPresentation }) {
if let collapsingItemView = self.gridItemViews.values.first(where: { $0.isCollapsing }), let index = self.gridParticipants.firstIndex(where: { $0.participant.id == collapsingItemView.key.id && $0.isPresentation == collapsingItemView.key.isPresentation }) {
let targetLocalItemFrame = itemLayout.gridItemFrame(at: index)
var targetItemFrame = self.gridItemViewContainer.convert(targetLocalItemFrame, to: self)
targetItemFrame.origin.y -= expandedGridItemContainerFrame.minY
@ -1630,7 +1639,7 @@ final class VideoChatParticipantsComponent: Component {
}
}
if let expandedVideoState = component.expandedVideoState, expandedVideoState.isMainParticipantPinned, let participants = component.participants, !component.speakingParticipants.isEmpty, let firstOther = component.speakingParticipants.first(where: { $0 != expandedVideoState.mainParticipant.id }), let speakingPeer = participants.participants.first(where: { $0.peer.id == firstOther })?.peer {
if let expandedVideoState = component.expandedVideoState, expandedVideoState.isMainParticipantPinned, let participants = component.participants, !component.speakingParticipants.isEmpty, let firstOther = component.speakingParticipants.first(where: { expandedVideoState.mainParticipant.id != .peer($0) }), let speakingPeer = participants.participants.first(where: { $0.id == .peer(firstOther) })?.peer {
let expandedSpeakingToast: ComponentView<Empty>
var expandedSpeakingToastTransition = transition
if let current = self.expandedSpeakingToast {
@ -1644,21 +1653,21 @@ final class VideoChatParticipantsComponent: Component {
transition: expandedSpeakingToastTransition,
component: AnyComponent(VideoChatExpandedSpeakingToastComponent(
context: component.call.accountContext,
peer: EnginePeer(speakingPeer),
peer: speakingPeer,
strings: component.strings,
theme: component.theme,
action: { [weak self] peer in
guard let self, let component = self.component, let participants = component.participants else {
return
}
guard let participant = participants.participants.first(where: { $0.peer.id == peer.id }) else {
guard let participant = participants.participants.first(where: { $0.id == .peer(peer.id) }) else {
return
}
var key: VideoParticipantKey?
if participant.presentationDescription != nil {
key = VideoParticipantKey(id: peer.id, isPresentation: true)
key = VideoParticipantKey(id: .peer(peer.id), isPresentation: true)
} else if participant.videoDescription != nil {
key = VideoParticipantKey(id: peer.id, isPresentation: false)
key = VideoParticipantKey(id: .peer(peer.id), isPresentation: false)
}
if let key {
component.updateMainParticipant(key, nil)
@ -1701,7 +1710,13 @@ final class VideoChatParticipantsComponent: Component {
}
}
component.visibleParticipantsUpdated(Set(visibleParticipants))
component.visibleParticipantsUpdated(Set(visibleParticipants.compactMap {
if case let .peer(id) = $0 {
return id
} else {
return nil
}
}))
}
func setEventCycleState(scrollView: UIScrollView, eventCycleState: EventCycleState?) {
@ -1712,7 +1727,7 @@ final class VideoChatParticipantsComponent: Component {
}
}
func itemFrame(peerId: EnginePeer.Id, isPresentation: Bool) -> CGRect? {
func itemFrame(peerId: GroupCallParticipantsContext.Participant.Id, isPresentation: Bool) -> CGRect? {
for (key, itemView) in self.gridItemViews {
if key.id == peerId && key.isPresentation == isPresentation {
if let itemComponentView = itemView.view.view {
@ -1723,7 +1738,7 @@ final class VideoChatParticipantsComponent: Component {
return nil
}
func updateItemPlaceholder(peerId: EnginePeer.Id, isPresentation: Bool, placeholder: VideoSource.Output) {
func updateItemPlaceholder(peerId: GroupCallParticipantsContext.Participant.Id, isPresentation: Bool, placeholder: VideoSource.Output) {
for (key, itemView) in self.gridItemViews {
if key.id == peerId && key.isPresentation == isPresentation {
if let itemComponentView = itemView.view.view as? VideoChatParticipantVideoComponent.View {
@ -1781,7 +1796,7 @@ final class VideoChatParticipantsComponent: Component {
if participant.videoDescription != nil {
hasVideo = true
let videoParticipant = VideoParticipant(participant: participant, isPresentation: false)
if participant.peer.id == participants.myPeerId {
if participant.id == .peer(participants.myPeerId) {
gridParticipants.insert(videoParticipant, at: 0)
} else {
gridParticipants.append(videoParticipant)
@ -1790,14 +1805,14 @@ final class VideoChatParticipantsComponent: Component {
if participant.presentationDescription != nil {
hasVideo = true
let videoParticipant = VideoParticipant(participant: participant, isPresentation: true)
if participant.peer.id == participants.myPeerId {
if participant.id == .peer(participants.myPeerId) {
gridParticipants.insert(videoParticipant, at: 0)
} else {
gridParticipants.append(videoParticipant)
}
}
if !hasVideo || component.layout.videoColumn != nil {
if participant.peer.id == participants.myPeerId && !isFullyMuted {
if participant.id == .peer(participants.myPeerId) && !isFullyMuted {
listParticipants.insert(participant, at: 0)
} else {
listParticipants.append(participant)
@ -1927,7 +1942,7 @@ final class VideoChatParticipantsComponent: Component {
for participant in participants.participants {
var maxVideoQuality: PresentationGroupCallRequestedVideo.Quality = .medium
if let expandedVideoState = component.expandedVideoState {
if expandedVideoState.mainParticipant.id == participant.peer.id, !expandedVideoState.mainParticipant.isPresentation {
if expandedVideoState.mainParticipant.id == participant.id, !expandedVideoState.mainParticipant.isPresentation {
if component.maxVideoQuality == Int.max {
maxVideoQuality = .full
} else if component.maxVideoQuality == 360 {
@ -1942,7 +1957,7 @@ final class VideoChatParticipantsComponent: Component {
var maxPresentationQuality: PresentationGroupCallRequestedVideo.Quality = .medium
if let expandedVideoState = component.expandedVideoState {
if expandedVideoState.mainParticipant.id == participant.peer.id, expandedVideoState.mainParticipant.isPresentation {
if expandedVideoState.mainParticipant.id == participant.id, expandedVideoState.mainParticipant.isPresentation {
if component.maxVideoQuality == Int.max {
maxVideoQuality = .full
} else if component.maxVideoQuality == 360 {

View File

@ -343,17 +343,17 @@ final class VideoChatScreenComponent: Component {
sourceCallControllerView?.removeFromSuperview()
}
var expandedPeer: (id: EnginePeer.Id, isPresentation: Bool)?
var expandedPeer: (id: GroupCallParticipantsContext.Participant.Id, isPresentation: Bool)?
if let animateOutData, animateOutData.incomingVideoLayer != nil, let members = self.members {
if let participant = members.participants.first(where: { $0.peer.id == animateOutData.incomingPeerId }) {
if let participant = members.participants.first(where: { $0.id == .peer(animateOutData.incomingPeerId) }) {
if let _ = participant.videoDescription {
expandedPeer = (participant.peer.id, false)
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.peer.id, isPresentation: false), isMainParticipantPinned: false, isUIHidden: true)
expandedPeer = (participant.id, false)
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.id, isPresentation: false), isMainParticipantPinned: false, isUIHidden: true)
}
} else if let participant = members.participants.first(where: { $0.peer.id == sourceCallController.call.context.account.peerId }) {
} else if let participant = members.participants.first(where: { $0.id == .peer(sourceCallController.call.context.account.peerId) }) {
if let _ = participant.videoDescription {
expandedPeer = (participant.peer.id, false)
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.peer.id, isPresentation: false), isMainParticipantPinned: false, isUIHidden: true)
expandedPeer = (participant.id, false)
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.id, isPresentation: false), isMainParticipantPinned: false, isUIHidden: true)
}
}
}
@ -1164,7 +1164,8 @@ final class VideoChatScreenComponent: Component {
}
participants.append(GroupCallParticipantsContext.Participant(
peer: myPeer._asPeer(),
id: .peer(myPeer.id),
peer: myPeer,
ssrc: nil,
videoDescription: myVideoDescription,
presentationDescription: nil,
@ -1189,7 +1190,8 @@ final class VideoChatScreenComponent: Component {
}
participants.append(GroupCallParticipantsContext.Participant(
peer: remotePeer._asPeer(),
id: .peer(remotePeer.id),
peer: remotePeer,
ssrc: nil,
videoDescription: remoteVideoDescription,
presentationDescription: nil,
@ -1235,7 +1237,7 @@ final class VideoChatScreenComponent: Component {
self.members = component.initialData.members
self.invitedPeers = component.initialData.invitedPeers
if let members = self.members {
self.invitedPeers.removeAll(where: { invitedPeer in members.participants.contains(where: { $0.peer.id == invitedPeer.peer.id }) })
self.invitedPeers.removeAll(where: { invitedPeer in members.participants.contains(where: { $0.id == .peer(invitedPeer.peer.id) }) })
}
self.callState = component.initialData.callState
}
@ -1276,7 +1278,7 @@ final class VideoChatScreenComponent: Component {
self.members = members
if let members {
self.invitedPeers.removeAll(where: { invitedPeer in members.participants.contains(where: { $0.peer.id == invitedPeer.peer.id }) })
self.invitedPeers.removeAll(where: { invitedPeer in members.participants.contains(where: { $0.id == .peer(invitedPeer.peer.id) }) })
}
if let members, let expandedParticipantsVideoState = self.expandedParticipantsVideoState, !expandedParticipantsVideoState.isUIHidden {
@ -1299,28 +1301,28 @@ final class VideoChatScreenComponent: Component {
if let expandedParticipantsVideoState = self.expandedParticipantsVideoState, let members {
if CFAbsoluteTimeGetCurrent() > self.focusedSpeakerAutoSwitchDeadline, !expandedParticipantsVideoState.isMainParticipantPinned, let participant = members.participants.first(where: { participant in
if let callState = self.callState, participant.peer.id == callState.myPeerId {
if let callState = self.callState, participant.id == .peer(callState.myPeerId) {
return false
}
if participant.videoDescription != nil || participant.presentationDescription != nil {
if members.speakingParticipants.contains(participant.peer.id) {
if let participantPeer = participant.peer, members.speakingParticipants.contains(participantPeer.id) {
return true
}
}
return false
}) {
if participant.peer.id != expandedParticipantsVideoState.mainParticipant.id {
if participant.id != expandedParticipantsVideoState.mainParticipant.id {
if participant.presentationDescription != nil {
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.peer.id, isPresentation: true), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.id, isPresentation: true), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
} else {
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.peer.id, isPresentation: false), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.id, isPresentation: false), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
}
self.focusedSpeakerAutoSwitchDeadline = CFAbsoluteTimeGetCurrent() + 1.0
}
}
if let _ = members.participants.first(where: { participant in
if participant.peer.id == expandedParticipantsVideoState.mainParticipant.id {
if participant.id == expandedParticipantsVideoState.mainParticipant.id {
if expandedParticipantsVideoState.mainParticipant.isPresentation {
if participant.presentationDescription == nil {
return false
@ -1344,9 +1346,9 @@ final class VideoChatScreenComponent: Component {
return false
}) {
if participant.presentationDescription != nil {
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.peer.id, isPresentation: true), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.id, isPresentation: true), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
} else {
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.peer.id, isPresentation: false), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.id, isPresentation: false), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
}
self.focusedSpeakerAutoSwitchDeadline = CFAbsoluteTimeGetCurrent() + 1.0
} else {
@ -1365,8 +1367,8 @@ final class VideoChatScreenComponent: Component {
var speakingParticipantPeers: [EnginePeer] = []
if let members, !members.speakingParticipants.isEmpty {
for participant in members.participants {
if members.speakingParticipants.contains(participant.peer.id) {
speakingParticipantPeers.append(EnginePeer(participant.peer))
if let participantPeer = participant.peer, members.speakingParticipants.contains(participantPeer.id) {
speakingParticipantPeers.append(participantPeer)
}
}
}
@ -1401,7 +1403,7 @@ final class VideoChatScreenComponent: Component {
var invitedPeers = invitedPeers
if let members {
invitedPeers.removeAll(where: { invitedPeer in members.participants.contains(where: { $0.peer.id == invitedPeer.peer.id }) })
invitedPeers.removeAll(where: { invitedPeer in members.participants.contains(where: { $0.id == .peer(invitedPeer.peer.id) }) })
}
if self.invitedPeers != invitedPeers {
@ -1612,28 +1614,28 @@ final class VideoChatScreenComponent: Component {
if let expandedParticipantsVideoState = self.expandedParticipantsVideoState {
if CFAbsoluteTimeGetCurrent() > self.focusedSpeakerAutoSwitchDeadline, !expandedParticipantsVideoState.isMainParticipantPinned, let participant = members.participants.first(where: { participant in
if let callState = self.callState, participant.peer.id == callState.myPeerId {
if let callState = self.callState, participant.id == .peer(callState.myPeerId) {
return false
}
if participant.videoDescription != nil || participant.presentationDescription != nil {
if members.speakingParticipants.contains(participant.peer.id) {
if let participantPeer = participant.peer, members.speakingParticipants.contains(participantPeer.id) {
return true
}
}
return false
}) {
if participant.peer.id != expandedParticipantsVideoState.mainParticipant.id {
if participant.id != expandedParticipantsVideoState.mainParticipant.id {
if participant.presentationDescription != nil {
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.peer.id, isPresentation: true), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.id, isPresentation: true), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
} else {
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.peer.id, isPresentation: false), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.id, isPresentation: false), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
}
self.focusedSpeakerAutoSwitchDeadline = CFAbsoluteTimeGetCurrent() + 1.0
}
}
if let _ = members.participants.first(where: { participant in
if participant.peer.id == expandedParticipantsVideoState.mainParticipant.id {
if participant.id == expandedParticipantsVideoState.mainParticipant.id {
if expandedParticipantsVideoState.mainParticipant.isPresentation {
if participant.presentationDescription == nil {
return false
@ -1657,9 +1659,9 @@ final class VideoChatScreenComponent: Component {
return false
}) {
if participant.presentationDescription != nil {
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.peer.id, isPresentation: true), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.id, isPresentation: true), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
} else {
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.peer.id, isPresentation: false), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
self.expandedParticipantsVideoState = VideoChatParticipantsComponent.ExpandedVideoState(mainParticipant: VideoChatParticipantsComponent.VideoParticipantKey(id: participant.id, isPresentation: false), isMainParticipantPinned: false, isUIHidden: expandedParticipantsVideoState.isUIHidden)
}
self.focusedSpeakerAutoSwitchDeadline = CFAbsoluteTimeGetCurrent() + 1.0
} else {
@ -1678,8 +1680,8 @@ final class VideoChatScreenComponent: Component {
var speakingParticipantPeers: [EnginePeer] = []
if !members.speakingParticipants.isEmpty {
for participant in members.participants {
if members.speakingParticipants.contains(participant.peer.id) {
speakingParticipantPeers.append(EnginePeer(participant.peer))
if let participantPeer = participant.peer, members.speakingParticipants.contains(participantPeer.id) {
speakingParticipantPeers.append(participantPeer)
}
}
}

View File

@ -21,8 +21,8 @@ extension VideoChatScreenComponent.View {
disablePeerIds.append(groupCall.accountContext.account.peerId)
if let members = self.members {
for participant in members.participants {
if !disablePeerIds.contains(participant.peer.id) {
disablePeerIds.append(participant.peer.id)
if let participantPeer = participant.peer, !disablePeerIds.contains(participantPeer.id) {
disablePeerIds.append(participantPeer.id)
}
}
}
@ -99,7 +99,7 @@ extension VideoChatScreenComponent.View {
var filters: [ChannelMembersSearchFilter] = []
if let members = self.members {
filters.append(.disable(Array(members.participants.map { $0.peer.id })))
filters.append(.disable(Array(members.participants.compactMap { $0.peer?.id })))
}
if case let .channel(groupPeer) = groupPeer {
if !groupPeer.hasPermission(.inviteMembers) && inviteLinks?.listenerLink == nil {

View File

@ -18,7 +18,7 @@ extension VideoChatScreenComponent.View {
guard let environment = self.environment else {
return
}
guard let members = self.members, let participant = members.participants.first(where: { $0.peer.id == id }) else {
guard let members = self.members, let participant = members.participants.first(where: { $0.id == .peer(id) }) else {
return
}
guard let currentCall = self.currentCall else {
@ -35,10 +35,13 @@ extension VideoChatScreenComponent.View {
return []
}
var items: [ContextMenuItem] = []
guard let peer = participant.peer else {
return []
}
var items: [ContextMenuItem] = []
var hasVolumeSlider = false
let peer = participant.peer
if let muteState = muteState, !muteState.canUnmute || muteState.mutedByYou {
} else {
if callState.canManageCall || callState.myPeerId != id {
@ -65,7 +68,7 @@ extension VideoChatScreenComponent.View {
}
}
if callState.myPeerId == id && !hasVolumeSlider && ((participant.about?.isEmpty ?? true) || participant.peer.smallProfileImage == nil) {
if callState.myPeerId == id && !hasVolumeSlider && ((participant.about?.isEmpty ?? true) || participant.peer?.smallProfileImage == nil) {
items.append(.custom(VoiceChatInfoContextItem(text: environment.strings.VoiceChat_ImproveYourProfileText, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Tip"), color: theme.actionSheet.primaryTextColor)
}), true))
@ -134,7 +137,7 @@ extension VideoChatScreenComponent.View {
}
})))
if let peer = peer as? TelegramUser {
if case let .user(peer) = peer {
items.append(.action(ContextMenuActionItem(text: environment.strings.VoiceChat_ChangeName, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Call/Context Menu/ChangeName"), color: theme.actionSheet.primaryTextColor)
}, action: { [weak self] _, f in
@ -184,7 +187,9 @@ extension VideoChatScreenComponent.View {
let _ = groupCall.updateMuteState(peerId: peer.id, isMuted: false)
f(.default)
self.presentUndoOverlay(content: .voiceChatCanSpeak(text: environment.strings.VoiceChat_UserCanNowSpeak(EnginePeer(participant.peer).displayTitle(strings: environment.strings, displayOrder: groupCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).nameDisplayOrder)).string), action: { _ in return true })
if let participantPeer = participant.peer {
self.presentUndoOverlay(content: .voiceChatCanSpeak(text: environment.strings.VoiceChat_UserCanNowSpeak(participantPeer.displayTitle(strings: environment.strings, displayOrder: groupCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).nameDisplayOrder)).string), action: { _ in return true })
}
})))
} else {
items.append(.action(ContextMenuActionItem(text: environment.strings.VoiceChat_MutePeer, icon: { theme in
@ -207,7 +212,6 @@ extension VideoChatScreenComponent.View {
guard let self, case let .group(groupCall) = self.currentCall else {
return
}
let _ = groupCall.updateMuteState(peerId: peer.id, isMuted: false)
f(.default)
})))
@ -228,7 +232,7 @@ extension VideoChatScreenComponent.View {
let openTitle: String
let openIcon: UIImage?
if [Namespaces.Peer.CloudChannel, Namespaces.Peer.CloudGroup].contains(peer.id.namespace) {
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
if case let .channel(peer) = peer, case .broadcast = peer.info {
openTitle = environment.strings.VoiceChat_OpenChannel
openIcon = UIImage(bundleImageName: "Chat/Context Menu/Channels")
} else {
@ -256,7 +260,7 @@ extension VideoChatScreenComponent.View {
guard let navigationController else {
return
}
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(EnginePeer(peer)), keepStack: .always, purposefulAction: {}, peekData: nil))
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer), keepStack: .always, purposefulAction: {}, peekData: nil))
}
})
@ -293,7 +297,7 @@ extension VideoChatScreenComponent.View {
let nameDisplayOrder = presentationData.nameDisplayOrder
if let chatPeer {
items.append(DeleteChatPeerActionSheetItem(context: groupCall.accountContext, peer: EnginePeer(peer), chatPeer: chatPeer, action: .removeFromGroup, strings: environment.strings, nameDisplayOrder: nameDisplayOrder))
items.append(DeleteChatPeerActionSheetItem(context: groupCall.accountContext, peer: peer, chatPeer: chatPeer, action: .removeFromGroup, strings: environment.strings, nameDisplayOrder: nameDisplayOrder))
}
items.append(ActionSheetButtonItem(title: environment.strings.VoiceChat_RemovePeerRemove, color: .destructive, action: { [weak self, weak actionSheet] in
@ -312,7 +316,7 @@ extension VideoChatScreenComponent.View {
}
}
self.presentUndoOverlay(content: .banned(text: environment.strings.VoiceChat_RemovedPeerText(EnginePeer(peer).displayTitle(strings: environment.strings, displayOrder: nameDisplayOrder)).string), action: { _ in return false })
self.presentUndoOverlay(content: .banned(text: environment.strings.VoiceChat_RemovedPeerText(peer.displayTitle(strings: environment.strings, displayOrder: nameDisplayOrder)).string), action: { _ in return false })
}))
actionSheet.setItemGroups([

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@ import SwiftSignalKit
public protocol ConferenceCallE2EContextState: AnyObject {
func getEmojiState() -> Data?
func getParticipantIds() -> [Int64]
func getParticipants() -> [ConferenceCallE2EContext.BlockchainParticipant]
func applyBlock(block: Data)
func applyBroadcastBlock(block: Data)
@ -25,6 +26,16 @@ public final class ConferenceCallE2EContext {
}
}
public struct BlockchainParticipant: Equatable {
public let userId: Int64
public let internalId: String
public init(userId: Int64, internalId: String) {
self.userId = userId
self.internalId = internalId
}
}
private final class Impl {
private let queue: Queue
@ -38,6 +49,7 @@ public final class ConferenceCallE2EContext {
private let keyPair: TelegramKeyPair
let e2eEncryptionKeyHashValue = ValuePromise<Data?>(nil)
let blockchainParticipantsValue = ValuePromise<[BlockchainParticipant]>([])
private var e2ePoll0Offset: Int?
private var e2ePoll0Timer: Foundation.Timer?
@ -154,7 +166,7 @@ public final class ConferenceCallE2EContext {
let keyPair = self.keyPair
let userId = self.userId
let initializeState = self.initializeState
let (outBlocks, outEmoji) = self.state.with({ callState -> ([Data], Data) in
let (outBlocks, outEmoji, outBlockchainParticipants) = self.state.with({ callState -> ([Data], Data, [BlockchainParticipant]) in
if let state = callState.state {
for block in blocks {
if subChainId == 0 {
@ -163,30 +175,31 @@ public final class ConferenceCallE2EContext {
state.applyBroadcastBlock(block: block)
}
}
return (state.takeOutgoingBroadcastBlocks(), state.getEmojiState() ?? Data())
return (state.takeOutgoingBroadcastBlocks(), state.getEmojiState() ?? Data(), state.getParticipants())
} else {
if subChainId == 0 {
guard let block = blocks.last else {
return ([], Data())
return ([], Data(), [])
}
guard let state = initializeState(keyPair, userId, block) else {
return ([], Data())
return ([], Data(), [])
}
callState.state = state
for block in callState.pendingIncomingBroadcastBlocks {
state.applyBroadcastBlock(block: block)
}
callState.pendingIncomingBroadcastBlocks.removeAll()
return (state.takeOutgoingBroadcastBlocks(), state.getEmojiState() ?? Data())
return (state.takeOutgoingBroadcastBlocks(), state.getEmojiState() ?? Data(), state.getParticipants())
} else if subChainId == 1 {
callState.pendingIncomingBroadcastBlocks.append(contentsOf: blocks)
return ([], Data())
return ([], Data(), [])
} else {
return ([], Data())
return ([], Data(), [])
}
}
})
self.e2eEncryptionKeyHashValue.set(outEmoji.isEmpty ? nil : outEmoji)
self.blockchainParticipantsValue.set(outBlockchainParticipants)
for outBlock in outBlocks {
let _ = self.engine.calls.sendConferenceCallBroadcast(callId: self.callId, accessHash: self.accessHash, block: outBlock).startStandalone()
@ -278,6 +291,7 @@ public final class ConferenceCallE2EContext {
let state = self.state
let callId = self.callId
let accessHash = self.accessHash
let accountPeerId = engine.account.peerId
if !self.pendingKickPeers.isEmpty {
let pendingKickPeers = self.pendingKickPeers
@ -363,9 +377,16 @@ public final class ConferenceCallE2EContext {
}
// Peer ids that are in the blockchain but not in the server list
let removedPeerIds = blockchainPeerIds.filter { blockchainPeerId in
return !result.participants.contains(where: { $0.peer.id.id._internalGetInt64Value() == blockchainPeerId })
var removedPeerIds = blockchainPeerIds.filter { blockchainPeerId in
return !result.participants.contains(where: { participant in
if case let .peer(id) = participant.id, id.namespace == Namespaces.Peer.CloudChannel, id.id._internalGetInt64Value() == blockchainPeerId {
return true
} else {
return false
}
})
}
removedPeerIds.removeAll(where: { $0 == accountPeerId.id._internalGetInt64Value() })
if removedPeerIds.isEmpty {
return .single(false)
@ -422,6 +443,12 @@ public final class ConferenceCallE2EContext {
return impl.e2eEncryptionKeyHashValue.get().start(next: subscriber.putNext)
}
}
public var blockchainParticipants: Signal<[BlockchainParticipant], NoError> {
return self.impl.signalWith { impl, subscriber in
return impl.blockchainParticipantsValue.get().start(next: subscriber.putNext)
}
}
public init(engine: TelegramEngine, callId: Int64, accessHash: Int64, userId: Int64, reference: InternalGroupCallReference, keyPair: TelegramKeyPair, initializeState: @escaping (TelegramKeyPair, Int64, Data) -> ConferenceCallE2EContextState?) {
let queue = Queue.mainQueue()

View File

@ -280,7 +280,7 @@ func _internal_getCurrentGroupCallInfo(account: Account, reference: InternalGrou
let parsedParticipants = participants.compactMap { GroupCallParticipantsContext.Participant($0, transaction: transaction) }
return (
parsedParticipants.map(\.peer.id),
parsedParticipants.compactMap(\.peer?.id),
nil
)
}
@ -847,9 +847,10 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
presentationDescription = nil
}
let joinedVideo = (flags & (1 << 15)) != 0
if !state.participants.contains(where: { $0.peer.id == peer.id }) {
if !state.participants.contains(where: { $0.id == .peer(peer.id) }) {
state.participants.append(GroupCallParticipantsContext.Participant(
peer: peer,
id: .peer(peer.id),
peer: EnginePeer(peer),
ssrc: ssrc,
videoDescription: videoDescription,
presentationDescription: presentationDescription,
@ -1175,7 +1176,41 @@ public final class GroupCallParticipantsContext {
}
}
public var peer: Peer
public enum Id: Hashable, Comparable, CustomStringConvertible {
case peer(EnginePeer.Id)
case blockchain(String)
public var description: String {
switch self {
case let .peer(id):
return "\(id)"
case let .blockchain(internalId):
return internalId
}
}
public static func <(lhs: Id, rhs: Id) -> Bool {
switch lhs {
case let .peer(lhsId):
switch rhs {
case let .peer(rhsId):
return lhsId < rhsId
case .blockchain:
return true
}
case let .blockchain(lhsData):
switch rhs {
case .peer:
return false
case let .blockchain(rhsData):
return lhsData < rhsData
}
}
}
}
public var id: Id
public var peer: EnginePeer?
public var ssrc: UInt32?
public var videoDescription: VideoDescription?
public var presentationDescription: VideoDescription?
@ -1190,7 +1225,8 @@ public final class GroupCallParticipantsContext {
public var joinedVideo: Bool
public init(
peer: Peer,
id: Id,
peer: EnginePeer?,
ssrc: UInt32?,
videoDescription: VideoDescription?,
presentationDescription: VideoDescription?,
@ -1204,6 +1240,7 @@ public final class GroupCallParticipantsContext {
about: String?,
joinedVideo: Bool
) {
self.id = id
self.peer = peer
self.ssrc = ssrc
self.videoDescription = videoDescription
@ -1220,7 +1257,7 @@ public final class GroupCallParticipantsContext {
}
public var description: String {
return "Participant(peer: \(peer.id): \(peer.debugDisplayTitle), ssrc: \(String(describing: self.ssrc))"
return "Participant(peer: \(self.id): \(peer?.debugDisplayTitle ?? "User \(self.id)"), ssrc: \(String(describing: self.ssrc))"
}
public mutating func mergeActivity(from other: Participant, mergeActivityTimestamp: Bool) {
@ -1231,7 +1268,10 @@ public final class GroupCallParticipantsContext {
}
public static func ==(lhs: Participant, rhs: Participant) -> Bool {
if !lhs.peer.isEqual(rhs.peer) {
if lhs.id != rhs.id {
return false
}
if lhs.peer != rhs.peer {
return false
}
if lhs.ssrc != rhs.ssrc {
@ -1318,7 +1358,7 @@ public final class GroupCallParticipantsContext {
}
}
return lhs.peer.id < rhs.peer.id
return lhs.id < rhs.id
}
}
@ -1352,13 +1392,15 @@ public final class GroupCallParticipantsContext {
public mutating func mergeActivity(from other: State, myPeerId: PeerId?, previousMyPeerId: PeerId?, mergeActivityTimestamps: Bool) {
var indexMap: [PeerId: Int] = [:]
for i in 0 ..< other.participants.count {
indexMap[other.participants[i].peer.id] = i
if let otherParticipantPeer = other.participants[i].peer {
indexMap[otherParticipantPeer.id] = i
}
}
for i in 0 ..< self.participants.count {
if let index = indexMap[self.participants[i].peer.id] {
if let selfParticipantPeer = self.participants[i].peer, let index = indexMap[selfParticipantPeer.id] {
self.participants[i].mergeActivity(from: other.participants[index], mergeActivityTimestamp: mergeActivityTimestamps)
if self.participants[i].peer.id == myPeerId || self.participants[i].peer.id == previousMyPeerId {
if selfParticipantPeer.id == myPeerId || selfParticipantPeer.id == previousMyPeerId {
self.participants[i].joinTimestamp = other.participants[index].joinTimestamp
}
}
@ -1437,9 +1479,28 @@ public final class GroupCallParticipantsContext {
}
}
private final class ResolvedBlockchainParticipant: Equatable {
let participant: ConferenceCallE2EContext.BlockchainParticipant
let peer: EnginePeer?
init(participant: ConferenceCallE2EContext.BlockchainParticipant, peer: EnginePeer?) {
self.participant = participant
self.peer = peer
}
static func ==(lhs: ResolvedBlockchainParticipant, rhs: ResolvedBlockchainParticipant) -> Bool {
return lhs.participant == rhs.participant && lhs.peer == rhs.peer
}
}
private struct BlockchainState: Equatable {
var blockchainParticipants: [ResolvedBlockchainParticipant]
}
private struct InternalState: Equatable {
var state: State
var overlayState: OverlayState
var blockchainState: BlockchainState
}
public enum Update {
@ -1547,7 +1608,7 @@ public final class GroupCallParticipantsContext {
var sortAgain = false
var canSeeHands = state.state.isCreator || state.state.adminIds.contains(accountPeerId)
for participant in publicState.participants {
if participant.peer.id == myPeerId {
if participant.id == .peer(myPeerId) {
if let muteState = participant.muteState {
if muteState.canUnmute {
canSeeHands = true
@ -1559,7 +1620,7 @@ public final class GroupCallParticipantsContext {
}
}
for i in 0 ..< publicState.participants.count {
if let pendingMuteState = state.overlayState.pendingMuteStateChanges[publicState.participants[i].peer.id] {
if let participantPeer = publicState.participants[i].peer, let pendingMuteState = state.overlayState.pendingMuteStateChanges[participantPeer.id] {
publicState.participants[i].muteState = pendingMuteState.state
publicState.participants[i].volume = pendingMuteState.volume
}
@ -1578,6 +1639,27 @@ public final class GroupCallParticipantsContext {
if sortAgain {
publicState.participants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: publicState.sortAscending) })
}
for blockchainParticipant in state.blockchainState.blockchainParticipants {
let blockchainParticipantPeerId = EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(blockchainParticipant.participant.userId))
if !publicState.participants.contains(where: { $0.id == .peer(blockchainParticipantPeerId) }) {
publicState.participants.append(Participant(
id: .peer(blockchainParticipantPeerId),
peer: blockchainParticipant.peer,
ssrc: nil,
videoDescription: nil,
presentationDescription: nil,
joinTimestamp: 0,
raiseHandRating: nil,
hasRaiseHand: false,
activityTimestamp: nil,
activityRank: nil,
muteState: nil,
volume: nil,
about: nil,
joinedVideo: false
))
}
}
return publicState
}
|> beforeNext { [weak self] next in
@ -1631,13 +1713,17 @@ public final class GroupCallParticipantsContext {
public private(set) var serviceState: ServiceState
init(account: Account, peerId: PeerId?, myPeerId: PeerId, id: Int64, reference: InternalGroupCallReference, state: State, previousServiceState: ServiceState?) {
private var e2eStateUpdateDisposable: Disposable?
private var pendingBlockchainState: [ResolvedBlockchainParticipant]?
private var pendingApplyBlockchainStateTimer: Foundation.Timer?
init(account: Account, peerId: PeerId?, myPeerId: PeerId, id: Int64, reference: InternalGroupCallReference, state: State, previousServiceState: ServiceState?, e2eContext: ConferenceCallE2EContext?) {
self.account = account
self.peerId = peerId
self.myPeerId = myPeerId
self.id = id
self.reference = reference
self.stateValue = InternalState(state: state, overlayState: OverlayState())
self.stateValue = InternalState(state: state, overlayState: OverlayState(), blockchainState: BlockchainState(blockchainParticipants: []))
self.statePromise = ValuePromise<InternalState>(self.stateValue)
self.serviceState = previousServiceState ?? ServiceState()
@ -1660,7 +1746,7 @@ public final class GroupCallParticipantsContext {
if let peerId {
let activityCategory: PeerActivitySpace.Category = .voiceChat
self.activitiesDisposable = (self.account.peerInputActivities(peerId: PeerActivitySpace(peerId: peerId, category: activityCategory))
|> deliverOnMainQueue).start(next: { [weak self] activities in
|> deliverOnMainQueue).start(next: { [weak self] activities in
guard let strongSelf = self else {
return
}
@ -1674,7 +1760,9 @@ public final class GroupCallParticipantsContext {
var updatedParticipants = strongSelf.stateValue.state.participants
var indexMap: [PeerId: Int] = [:]
for i in 0 ..< updatedParticipants.count {
indexMap[updatedParticipants[i].peer.id] = i
if let participantPeer = updatedParticipants[i].peer {
indexMap[participantPeer.id] = i
}
}
var updated = false
@ -1717,7 +1805,8 @@ public final class GroupCallParticipantsContext {
isStream: strongSelf.stateValue.state.isStream,
version: strongSelf.stateValue.state.version
),
overlayState: strongSelf.stateValue.overlayState
overlayState: strongSelf.stateValue.overlayState,
blockchainState: strongSelf.stateValue.blockchainState
)
}
}
@ -1753,6 +1842,53 @@ public final class GroupCallParticipantsContext {
}
}, queue: .mainQueue())
self.activityRankResetTimer?.start()
if let e2eContext {
let postbox = self.account.postbox
self.e2eStateUpdateDisposable = (e2eContext.blockchainParticipants
|> mapToSignal { value -> Signal<[ResolvedBlockchainParticipant], NoError> in
return postbox.transaction { transaction -> [ResolvedBlockchainParticipant] in
var result: [ResolvedBlockchainParticipant] = []
for participant in value {
let blockchainParticipantPeerId = EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(participant.userId))
if let peer = transaction.getPeer(blockchainParticipantPeerId) {
result.append(ResolvedBlockchainParticipant(participant: participant, peer: EnginePeer(peer)))
} else {
result.append(ResolvedBlockchainParticipant(participant: participant, peer: nil))
}
}
return result
}
}
|> deliverOnMainQueue).startStrict(next: { [weak self] blockchainParticipants in
guard let self else {
return
}
self.pendingBlockchainState = blockchainParticipants
self.pendingApplyBlockchainStateTimer?.invalidate()
self.pendingApplyBlockchainStateTimer = nil
var hasUnknownParticipants: Bool = false
for blockchainParticipant in blockchainParticipants {
if !self.stateValue.state.participants.contains(where: { $0.id == .peer(EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(blockchainParticipant.participant.userId))) }) {
hasUnknownParticipants = true
break
}
}
if hasUnknownParticipants {
self.pendingApplyBlockchainStateTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false, block: { [weak self] _ in
guard let self else {
return
}
self.applyPendingBlockchainState()
})
} else {
self.applyPendingBlockchainState()
}
})
}
}
deinit {
@ -1764,6 +1900,19 @@ public final class GroupCallParticipantsContext {
self.activityRankResetTimer?.invalidate()
self.resetInviteLinksDisposable.dispose()
self.subscribeDisposable.dispose()
self.e2eStateUpdateDisposable?.dispose()
self.pendingApplyBlockchainStateTimer?.invalidate()
}
private func applyPendingBlockchainState() {
self.pendingApplyBlockchainStateTimer?.invalidate()
self.pendingApplyBlockchainStateTimer = nil
if let pendingBlockchainState = self.pendingBlockchainState {
self.pendingBlockchainState = nil
self.stateValue.blockchainState = BlockchainState(blockchainParticipants: pendingBlockchainState)
}
}
public func addUpdates(updates: [Update]) {
@ -1795,7 +1944,7 @@ public final class GroupCallParticipantsContext {
public func removeLocalPeerId() {
var state = self.stateValue.state
state.participants.removeAll(where: { $0.peer.id == self.myPeerId })
state.participants.removeAll(where: { $0.id == .peer(self.myPeerId) })
self.stateValue.state = state
}
@ -1822,7 +1971,9 @@ public final class GroupCallParticipantsContext {
var updatedParticipants = strongSelf.stateValue.state.participants
var indexMap: [PeerId: Int] = [:]
for i in 0 ..< updatedParticipants.count {
indexMap[updatedParticipants[i].peer.id] = i
if let participantPeer = updatedParticipants[i].peer {
indexMap[participantPeer.id] = i
}
}
var updated = false
@ -1869,7 +2020,8 @@ public final class GroupCallParticipantsContext {
isStream: strongSelf.stateValue.state.isStream,
version: strongSelf.stateValue.state.version
),
overlayState: strongSelf.stateValue.overlayState
overlayState: strongSelf.stateValue.overlayState,
blockchainState: strongSelf.stateValue.blockchainState
)
}
@ -2000,7 +2152,7 @@ public final class GroupCallParticipantsContext {
for participantUpdate in update.participantUpdates {
if case .left = participantUpdate.participationStatusChange {
if let index = updatedParticipants.firstIndex(where: { $0.peer.id == participantUpdate.peerId }) {
if let index = updatedParticipants.firstIndex(where: { $0.id == .peer(participantUpdate.peerId) }) {
updatedParticipants.remove(at: index)
updatedTotalCount = max(0, updatedTotalCount - 1)
strongSelf.memberEventsPipe.putNext(MemberEvent(peerId: participantUpdate.peerId, canUnmute: false, joined: false))
@ -2017,7 +2169,7 @@ public final class GroupCallParticipantsContext {
var previousActivityRank: Int?
var previousMuteState: GroupCallParticipantsContext.Participant.MuteState?
var previousVolume: Int32?
if let index = updatedParticipants.firstIndex(where: { $0.peer.id == participantUpdate.peerId }) {
if let index = updatedParticipants.firstIndex(where: { $0.id == .peer(participantUpdate.peerId) }) {
previousJoinTimestamp = updatedParticipants[index].joinTimestamp
previousActivityTimestamp = updatedParticipants[index].activityTimestamp
previousActivityRank = updatedParticipants[index].activityRank
@ -2054,7 +2206,8 @@ public final class GroupCallParticipantsContext {
}
}
let participant = Participant(
peer: peer,
id: .peer(peer.id),
peer: EnginePeer(peer),
ssrc: participantUpdate.ssrc,
videoDescription: participantUpdate.videoDescription,
presentationDescription: participantUpdate.presentationDescription,
@ -2111,7 +2264,8 @@ public final class GroupCallParticipantsContext {
isStream: isStream,
version: update.version
),
overlayState: updatedOverlayState
overlayState: updatedOverlayState,
blockchainState: strongSelf.stateValue.blockchainState
)
strongSelf.endedProcessingUpdate()
@ -2158,7 +2312,7 @@ public final class GroupCallParticipantsContext {
}
for participant in self.stateValue.state.participants {
if participant.peer.id == peerId {
if participant.id == .peer(peerId) {
var raiseHandEqual: Bool = true
if let raiseHand = raiseHand {
raiseHandEqual = (participant.raiseHandRating == nil && !raiseHand) ||
@ -2744,14 +2898,14 @@ func _internal_updatedCurrentPeerGroupCall(postbox: Postbox, network: Network, a
private func mergeAndSortParticipants(current currentParticipants: [GroupCallParticipantsContext.Participant], with updatedParticipants: [GroupCallParticipantsContext.Participant], sortAscending: Bool) -> [GroupCallParticipantsContext.Participant] {
var mergedParticipants = currentParticipants
var existingParticipantIndices: [PeerId: Int] = [:]
var existingParticipantIndices: [GroupCallParticipantsContext.Participant.Id: Int] = [:]
for i in 0 ..< mergedParticipants.count {
existingParticipantIndices[mergedParticipants[i].peer.id] = i
existingParticipantIndices[mergedParticipants[i].id] = i
}
for participant in updatedParticipants {
if let _ = existingParticipantIndices[participant.peer.id] {
if let _ = existingParticipantIndices[participant.id] {
} else {
existingParticipantIndices[participant.peer.id] = mergedParticipants.count
existingParticipantIndices[participant.id] = mergedParticipants.count
mergedParticipants.append(participant)
}
}
@ -2942,7 +3096,8 @@ extension GroupCallParticipantsContext.Participant {
let joinedVideo = (flags & (1 << 15)) != 0
self.init(
peer: peer,
id: .peer(peer.id),
peer: EnginePeer(peer),
ssrc: ssrc,
videoDescription: videoDescription,
presentationDescription: presentationDescription,

View File

@ -144,8 +144,8 @@ public extension TelegramEngine {
return _internal_getVideoBroadcastPart(dataSource: dataSource, callId: callId, accessHash: accessHash, timestampIdMilliseconds: timestampIdMilliseconds, durationMilliseconds: durationMilliseconds, channelId: channelId, quality: quality)
}
public func groupCall(peerId: PeerId?, myPeerId: PeerId, id: Int64, reference: InternalGroupCallReference, state: GroupCallParticipantsContext.State, previousServiceState: GroupCallParticipantsContext.ServiceState?) -> GroupCallParticipantsContext {
return GroupCallParticipantsContext(account: self.account, peerId: peerId, myPeerId: myPeerId, id: id, reference: reference, state: state, previousServiceState: previousServiceState)
public func groupCall(peerId: PeerId?, myPeerId: PeerId, id: Int64, reference: InternalGroupCallReference, state: GroupCallParticipantsContext.State, previousServiceState: GroupCallParticipantsContext.ServiceState?, e2eContext: ConferenceCallE2EContext?) -> GroupCallParticipantsContext {
return GroupCallParticipantsContext(account: self.account, peerId: peerId, myPeerId: myPeerId, id: id, reference: reference, state: state, previousServiceState: previousServiceState, e2eContext: e2eContext)
}
public func serverTime() -> Signal<Int64, NoError> {

View File

@ -172,7 +172,9 @@ func _internal_joinCallLinkInformation(_ hash: String, account: Account) -> Sign
}
var members: [EnginePeer] = []
for participant in call.topParticipants {
members.append(EnginePeer(participant.peer))
if let peer = participant.peer {
members.append(peer)
}
}
return .single(JoinCallLinkInformation(id: call.info.id, accessHash: call.info.accessHash, inviter: nil, members: members, totalMemberCount: call.info.participantCount))
}
@ -198,7 +200,9 @@ func _internal_joinCallInvitationInformation(account: Account, messageId: Messag
}
var members: [EnginePeer] = []
for participant in call.topParticipants {
members.append(EnginePeer(participant.peer))
if let peer = participant.peer {
members.append(peer)
}
}
return .single(JoinCallLinkInformation(id: call.info.id, accessHash: call.info.accessHash, inviter: nil, members: members, totalMemberCount: call.info.participantCount))
}

View File

@ -4,41 +4,30 @@ import SwiftSignalKit
public struct CallListSettings: Codable, Equatable {
public var _showTab: Bool?
public var defaultShowTab: Bool?
public static var defaultSettings: CallListSettings {
return CallListSettings(showTab: false)
return CallListSettings(showTab: nil)
}
public var showTab: Bool {
get {
if let value = self._showTab {
return value
} else if let defaultValue = self.defaultShowTab {
return defaultValue
} else {
return CallListSettings.defaultSettings.showTab
return true
}
} set {
self._showTab = newValue
}
}
public init(showTab: Bool) {
public init(showTab: Bool?) {
self._showTab = showTab
}
public init(showTab: Bool?, defaultShowTab: Bool?) {
self._showTab = showTab
self.defaultShowTab = defaultShowTab
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
if let alternativeDefaultValue = try container.decodeIfPresent(Int32.self, forKey: "defaultShowTab") {
self.defaultShowTab = alternativeDefaultValue != 0
}
if let value = try container.decodeIfPresent(Int32.self, forKey: "showTab") {
self._showTab = value != 0
}
@ -47,11 +36,6 @@ public struct CallListSettings: Codable, Equatable {
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
if let defaultShowTab = self.defaultShowTab {
try container.encode((defaultShowTab ? 1 : 0) as Int32, forKey: "defaultShowTab")
} else {
try container.encodeNil(forKey: "defaultShowTab")
}
if let showTab = self._showTab {
try container.encode((showTab ? 1 : 0) as Int32, forKey: "showTab")
} else {
@ -60,11 +44,11 @@ public struct CallListSettings: Codable, Equatable {
}
public static func ==(lhs: CallListSettings, rhs: CallListSettings) -> Bool {
return lhs._showTab == rhs._showTab && lhs.defaultShowTab == rhs.defaultShowTab
return lhs._showTab == rhs._showTab
}
public func withUpdatedShowTab(_ showTab: Bool) -> CallListSettings {
return CallListSettings(showTab: showTab, defaultShowTab: self.defaultShowTab)
return CallListSettings(showTab: showTab)
}
}

@ -1 +1 @@
Subproject commit 579cae3c3c70c6ed8bc0d88cc41de28bd50a7f1b
Subproject commit 36161286bd7fae1b9bb2e8dad817ae9af6d68055

View File

@ -22,10 +22,10 @@ NS_ASSUME_NONNULL_BEGIN
@interface TdCallParticipant : NSObject
@property (nonatomic, strong, readonly) NSData *publicKey;
@property (nonatomic, strong, readonly) NSString *internalId;
@property (nonatomic, readonly) int64_t userId;
- (nullable instancetype)initWithPublicKey:(NSData *)publicKey userId:(int64_t)userId;
- (nullable instancetype)initWithInternalId:(NSString *)internalId userId:(int64_t)userId;
@end
@ -35,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
- (NSArray<NSData *> *)takeOutgoingBroadcastBlocks;
- (NSData *)emojiState;
- (NSArray<NSNumber *> *)participantIds;
- (NSArray<TdCallParticipant *> *)participants;
- (void)applyBlock:(NSData *)block;
- (void)applyBroadcastBlock:(NSData *)block;

View File

@ -58,10 +58,10 @@ static NSString *hexStringFromData(NSData *data) {
@implementation TdCallParticipant
- (nullable instancetype)initWithPublicKey:(NSData *)publicKey userId:(int64_t)userId {
- (nullable instancetype)initWithInternalId:(NSString *)internalId userId:(int64_t)userId {
self = [super init];
if (self != nil) {
_publicKey = publicKey;
_internalId = internalId;
_userId = userId;
}
return self;
@ -176,17 +176,18 @@ static NSString *hexStringFromData(NSData *data) {
return outEmojiHash;
}
- (NSArray<NSNumber *> *)participantIds {
- (NSArray<TdCallParticipant *> *)participants {
auto result = tde2e_api::call_get_state(_callId);
if (!result.is_ok()) {
return @[];
}
auto state = result.value();
NSMutableArray<NSNumber *> *participantIds = [[NSMutableArray alloc] init];
NSMutableArray<TdCallParticipant *> *participants = [[NSMutableArray alloc] init];
for (const auto &it : state.participants) {
[participantIds addObject:[NSNumber numberWithLongLong:it.user_id]];
NSString *internalId = [[NSString alloc] initWithFormat:@"%lld", it.public_key_id];
[participants addObject:[[TdCallParticipant alloc] initWithInternalId:internalId userId:it.user_id]];
}
return participantIds;
return participants;
}
- (void)applyBlock:(NSData *)block {

2
third-party/td/td vendored

@ -1 +1 @@
Subproject commit a03a90470d6fca9a5a3db747ba3f3e4a465b5fe7
Subproject commit 04adfc87deea4c804def118e88c89a08c388b32b

View File

@ -1,5 +1,5 @@
{
"app": "11.9.1",
"app": "11.10",
"xcode": "16.2",
"bazel": "7.3.1:981f82a470bad1349322b6f51c9c6ffa0aa291dab1014fac411543c12e661dff",
"macos": "15"