diff --git a/submodules/AccountContext/Sources/PresentationCallManager.swift b/submodules/AccountContext/Sources/PresentationCallManager.swift index 791530e7a5..aae54bbca7 100644 --- a/submodules/AccountContext/Sources/PresentationCallManager.swift +++ b/submodules/AccountContext/Sources/PresentationCallManager.swift @@ -12,8 +12,8 @@ public enum RequestCallResult { case alreadyInProgress(PeerId?) } -public enum RequestOrJoinGroupCallResult { - case requested +public enum JoinGroupCallManagerResult { + case joined case alreadyInProgress(PeerId?) } @@ -278,5 +278,5 @@ public protocol PresentationCallManager: class { var currentGroupCallSignal: Signal { get } func requestCall(context: AccountContext, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult - func requestOrJoinGroupCall(context: AccountContext, peerId: PeerId, initialCall: CachedChannelData.ActiveCall?, endCurrentIfAny: Bool, sourcePanel: ASDisplayNode?) -> RequestOrJoinGroupCallResult + func joinGroupCall(context: AccountContext, peerId: PeerId, initialCall: CachedChannelData.ActiveCall, endCurrentIfAny: Bool, sourcePanel: ASDisplayNode?) -> JoinGroupCallManagerResult } diff --git a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift index 4c8a7e9397..e37b25b02e 100644 --- a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift +++ b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift @@ -262,20 +262,19 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { if let callManager = context.sharedContext.callManager { switch groupCallPanelSource { - case .none: + case .none, .all: break - default: + case let .peer(peerId): let currentGroupCall: Signal = callManager.currentGroupCallSignal |> distinctUntilChanged(isEqual: { lhs, rhs in return lhs?.internalId == rhs?.internalId }) |> mapToSignal { call -> Signal in - guard let call = call else { + guard let call = call, call.peerId == peerId else { return .single(nil) } return call.summaryState |> filter { $0 != nil } - |> take(1) |> map { summary -> GroupCallPanelData? in guard let summary = summary else { return nil @@ -289,6 +288,13 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { groupCall: call ) } + |> take(until: { summary in + if summary != nil { + return SignalTakeAction(passthrough: true, complete: true) + } else { + return SignalTakeAction(passthrough: true, complete: false) + } + }) } let availableGroupCall: Signal @@ -861,7 +867,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { } private func joinGroupCall(peerId: PeerId, info: GroupCallInfo, sourcePanel: GroupCallNavigationAccessoryPanel?) { - let callResult = self.context.sharedContext.callManager?.requestOrJoinGroupCall(context: self.context, peerId: peerId, initialCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash), endCurrentIfAny: false, sourcePanel: sourcePanel) + let callResult = self.context.sharedContext.callManager?.joinGroupCall(context: self.context, peerId: peerId, initialCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash), endCurrentIfAny: false, sourcePanel: sourcePanel) if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult { if currentPeerId == peerId { self.context.sharedContext.navigateToCurrentCall(sourcePanel: sourcePanel) @@ -880,7 +886,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { if let current = current { strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { if let strongSelf = self { - let _ = strongSelf.context.sharedContext.callManager?.requestOrJoinGroupCall(context: strongSelf.context, peerId: peerId, initialCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash), endCurrentIfAny: true, sourcePanel: sourcePanel) + let _ = strongSelf.context.sharedContext.callManager?.joinGroupCall(context: strongSelf.context, peerId: peerId, initialCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash), endCurrentIfAny: true, sourcePanel: sourcePanel) } })]), in: .window(.root)) } else { diff --git a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift index 77657f9d6a..e30ff6ee47 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift @@ -593,7 +593,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { } } - public func requestOrJoinGroupCall(context: AccountContext, peerId: PeerId, initialCall: CachedChannelData.ActiveCall?, endCurrentIfAny: Bool, sourcePanel: ASDisplayNode?) -> RequestOrJoinGroupCallResult { + public func joinGroupCall(context: AccountContext, peerId: PeerId, initialCall: CachedChannelData.ActiveCall, endCurrentIfAny: Bool, sourcePanel: ASDisplayNode?) -> JoinGroupCallManagerResult { let begin: () -> Void = { [weak self] in let _ = self?.startGroupCall(accountContext: context, peerId: peerId, initialCall: initialCall, sourcePanel: sourcePanel).start() } @@ -612,13 +612,13 @@ public final class PresentationCallManagerImpl: PresentationCallManager { } else { begin() } - return .requested + return .joined } private func startGroupCall( accountContext: AccountContext, peerId: PeerId, - initialCall: CachedChannelData.ActiveCall?, + initialCall: CachedChannelData.ActiveCall, internalId: CallSessionInternalId = CallSessionInternalId(), sourcePanel: ASDisplayNode? ) -> Signal { diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index ce8be594d5..02d2965b30 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -142,7 +142,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { private let getDeviceAccessData: () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void) - private let initialCall: CachedChannelData.ActiveCall? + private var initialCall: CachedChannelData.ActiveCall? public let internalId: CallSessionInternalId public let peerId: PeerId public let peer: Peer? @@ -374,12 +374,26 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { var removedSsrc: [UInt32] = [] for (callId, update) in updates { if callId == callInfo.id { - for participantUpdate in update.participantUpdates { - if participantUpdate.isRemoved { - removedSsrc.append(participantUpdate.ssrc) + switch update { + case let .state(update): + for participantUpdate in update.participantUpdates { + if participantUpdate.isRemoved { + removedSsrc.append(participantUpdate.ssrc) + + if participantUpdate.peerId == strongSelf.accountContext.account.peerId { + strongSelf._canBeRemoved.set(.single(true)) + } + } else if participantUpdate.peerId == strongSelf.accountContext.account.peerId { + if case let .estabilished(_, _, ssrc, _) = strongSelf.internalState, ssrc != participantUpdate.ssrc { + strongSelf._canBeRemoved.set(.single(true)) + } + } + } + case let .call(isTerminated): + if isTerminated { + strongSelf._canBeRemoved.set(.single(true)) } } - strongSelf.participantsContext?.addUpdates(updates: [update]) } } if !removedSsrc.isEmpty { @@ -553,7 +567,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { if case let .estabilished(callInfo, clientParams, _, initialState) = internalState { self.summaryInfoState.set(.single(SummaryInfoState(info: callInfo))) - self.stateValue.canManageCall = initialState.isCreator + self.stateValue.canManageCall = initialState.isCreator || initialState.adminIds.contains(self.accountContext.account.peerId) self.ssrcMapping.removeAll() var ssrcs: [UInt32] = [] @@ -780,7 +794,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } let account = self.account - let peerId = self.peerId let currentCall: Signal if let initialCall = self.initialCall { @@ -796,36 +809,27 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } let currentOrRequestedCall = currentCall - |> mapToSignal { callInfo -> Signal in + |> mapToSignal { callInfo -> Signal in if let callInfo = callInfo { return .single(callInfo) } else { - return createGroupCall(account: account, peerId: peerId) - |> mapError { _ -> CallError in - return .generic - } + return .single(nil) } } - /*let restartedCall = currentOrRequestedCall - |> mapToSignal { value -> Signal in - let stopped: Signal = stopGroupCall(account: account, callId: value.id, accessHash: value.accessHash) - |> mapError { _ -> CallError in - return .generic - } - |> map { _ -> GroupCallInfo in - } - - return stopped - |> then(currentOrRequestedCall) - }*/ - self.requestDisposable.set((currentOrRequestedCall |> deliverOnMainQueue).start(next: { [weak self] value in guard let strongSelf = self else { return } - strongSelf.updateSessionState(internalState: .active(value), audioSessionControl: strongSelf.audioSessionControl) + + if let value = value { + strongSelf.initialCall = CachedChannelData.ActiveCall(id: value.id, accessHash: value.accessHash) + + strongSelf.updateSessionState(internalState: .active(value), audioSessionControl: strongSelf.audioSessionControl) + } else { + strongSelf._canBeRemoved.set(.single(true)) + } })) } @@ -868,7 +872,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { if shouldBeSendingTyping != strongSelf.isSendingTyping { strongSelf.isSendingTyping = shouldBeSendingTyping if shouldBeSendingTyping { - strongSelf.typingDisposable.set(strongSelf.accountContext.account.acquireLocalInputActivity(peerId: PeerActivitySpace(peerId: strongSelf.peerId, category: .voiceChat), activity: .speakingInGroupCall)) + strongSelf.typingDisposable.set(strongSelf.accountContext.account.acquireLocalInputActivity(peerId: PeerActivitySpace(peerId: strongSelf.peerId, category: .voiceChat), activity: .speakingInGroupCall(timestamp: 0))) strongSelf.restartMyAudioLevelTimer() } else { strongSelf.typingDisposable.set(nil) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 774178a9f7..023f6e2792 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -223,7 +223,7 @@ public final class VoiceChatController: ViewController { } else { microphoneColor = UIColor(rgb: 0x979797) } - icon = .microphone(true, microphoneColor) + icon = .microphone(self.muteState != nil, microphoneColor) case .speaking: text = .text(presentationData.strings.VoiceChat_StatusSpeaking, .constructive) icon = .microphone(false, UIColor(rgb: 0x34c759)) @@ -411,37 +411,38 @@ public final class VoiceChatController: ViewController { } } - - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_RemovePeer, textColor: .destructive, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.destructiveActionTextColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - guard let strongSelf = self else { - return - } - - let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme)) - var items: [ActionSheetItem] = [] - - items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: peer, chatPeer: peer, action: .removeFromGroup, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) - - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.VoiceChat_RemovePeerRemove, color: .destructive, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() + if let callState = strongSelf.callState, (callState.canManageCall && !callState.adminIds.contains(strongSelf.context.account.peerId)) { + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_RemovePeer, textColor: .destructive, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.destructiveActionTextColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) - - })) + guard let strongSelf = self else { + return + } - actionSheet.setItemGroups([ - ActionSheetItemGroup(items: items), - ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - }) + let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme)) + var items: [ActionSheetItem] = [] + + items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: peer, chatPeer: peer, action: .removeFromGroup, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) + + items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.VoiceChat_RemovePeerRemove, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + + + })) + + actionSheet.setItemGroups([ + ActionSheetItemGroup(items: items), + ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ]) ]) - ]) - strongSelf.controller?.present(actionSheet, in: .window(.root)) - }))) + strongSelf.controller?.present(actionSheet, in: .window(.root)) + }))) + } } } @@ -1139,11 +1140,13 @@ public final class VoiceChatController: ViewController { var callMembers = callMembers + callMembers.sort() + for i in 0 ..< callMembers.count { if callMembers[i].peer.id == self.context.account.peerId { let member = callMembers[i] callMembers.remove(at: i) - callMembers.insert(member, at: i) + callMembers.insert(member, at: 0) break } } diff --git a/submodules/TelegramCore/Sources/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/AccountIntermediateState.swift index 5425e150ae..2ca6472684 100644 --- a/submodules/TelegramCore/Sources/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/AccountIntermediateState.swift @@ -111,6 +111,7 @@ enum AccountStateMutationOperation { case UpdateChatListFilter(id: Int32, filter: Api.DialogFilter?) case UpdateReadThread(threadMessageId: MessageId, readMaxId: Int32, isIncoming: Bool, mainChannelMessage: MessageId?) case UpdateGroupCallParticipants(id: Int64, accessHash: Int64, participants: [Api.GroupCallParticipant], version: Int32) + case UpdateGroupCall(call: Api.GroupCall) } struct HoleFromPreviousState { @@ -281,6 +282,10 @@ struct AccountMutableState { self.addOperation(.UpdateGroupCallParticipants(id: id, accessHash: accessHash, participants: participants, version: version)) } + mutating func updateGroupCall(call: Api.GroupCall) { + self.addOperation(.UpdateGroupCall(call: call)) + } + mutating func readGroupFeedInbox(groupId: PeerGroupId, index: MessageIndex) { self.addOperation(.ReadGroupFeedInbox(groupId, index)) } @@ -489,7 +494,7 @@ struct AccountMutableState { mutating func addOperation(_ operation: AccountStateMutationOperation) { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateMessagesPinned: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned: break case let .AddMessages(messages, location): for message in messages { @@ -607,7 +612,7 @@ struct AccountReplayedFinalState { let updatedWebpages: [MediaId: TelegramMediaWebpage] let updatedCalls: [Api.PhoneCall] let addedCallSignalingData: [(Int64, Data)] - let updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.StateUpdate)] + let updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] let updatedPeersNearby: [PeerNearby]? let isContactUpdates: [(PeerId, Bool)] let delayNotificatonsUntil: Int32? @@ -623,7 +628,7 @@ struct AccountFinalStateEvents { let updatedWebpages: [MediaId: TelegramMediaWebpage] let updatedCalls: [Api.PhoneCall] let addedCallSignalingData: [(Int64, Data)] - let updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.StateUpdate)] + let updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] let updatedPeersNearby: [PeerNearby]? let isContactUpdates: [(PeerId, Bool)] let displayAlerts: [(text: String, isDropAuth: Bool)] @@ -639,7 +644,7 @@ struct AccountFinalStateEvents { return self.addedIncomingMessageIds.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty } - init(addedIncomingMessageIds: [MessageId] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.StateUpdate)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:]) { + init(addedIncomingMessageIds: [MessageId] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:]) { self.addedIncomingMessageIds = addedIncomingMessageIds self.wasScheduledMessageIds = wasScheduledMessageIds self.deletedMessageIds = deletedMessageIds diff --git a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift index 6e8851554f..c75587a9cc 100644 --- a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift @@ -1221,7 +1221,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo updatedState.readSecretOutbox(peerId: PeerId(namespace: Namespaces.Peer.SecretChat, id: chatId), timestamp: maxDate, actionTimestamp: date) case let .updateUserTyping(userId, type): if let date = updatesDate, date + 60 > serverTime { - let activity = PeerInputActivity(apiType: type) + let activity = PeerInputActivity(apiType: type, timestamp: date) var category: PeerActivitySpace.Category = .global if case .speakingInGroupCall = activity { category = .voiceChat @@ -1231,7 +1231,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo } case let .updateChatUserTyping(chatId, userId, type): if let date = updatesDate, date + 60 > serverTime { - let activity = PeerInputActivity(apiType: type) + let activity = PeerInputActivity(apiType: type, timestamp: date) var category: PeerActivitySpace.Category = .global if case .speakingInGroupCall = activity { category = .voiceChat @@ -1244,7 +1244,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo let channelPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) let threadId = topMsgId.flatMap { makeMessageThreadId(MessageId(peerId: channelPeerId, namespace: Namespaces.Message.Cloud, id: $0)) } - let activity = PeerInputActivity(apiType: type) + let activity = PeerInputActivity(apiType: type, timestamp: date) var category: PeerActivitySpace.Category = .global if case .speakingInGroupCall = activity { category = .voiceChat @@ -1332,6 +1332,8 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo case let .inputGroupCall(id, accessHash): updatedState.updateGroupCallParticipants(id: id, accessHash: accessHash, participants: participants, version: version) } + case let .updateGroupCall(call): + updatedState.updateGroupCall(call: call) case let .updateLangPackTooLong(langCode): updatedState.updateLangPack(langCode: langCode, difference: nil) case let .updateLangPack(difference): @@ -2135,7 +2137,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation]) var currentAddScheduledMessages: OptimizeAddMessagesState? for operation in operations { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall: if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty { result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location)) } @@ -2222,7 +2224,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP var updatedWebpages: [MediaId: TelegramMediaWebpage] = [:] var updatedCalls: [Api.PhoneCall] = [] var addedCallSignalingData: [(Int64, Data)] = [] - var updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.StateUpdate)] = [] + var updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [] var updatedPeersNearby: [PeerNearby]? var isContactUpdates: [(PeerId, Bool)] = [] var stickerPackOperations: [AccountStateUpdateStickerPacksOperation] = [] @@ -2955,8 +2957,18 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP case let .UpdateGroupCallParticipants(callId, _, participants, version): updatedGroupCallParticipants.append(( callId, - GroupCallParticipantsContext.StateUpdate(participants: participants, version: version) + .state(update: GroupCallParticipantsContext.Update.StateUpdate(participants: participants, version: version)) )) + case let .UpdateGroupCall(call): + switch call { + case .groupCall: + break + case let .groupCallDiscarded(callId, _, _): + updatedGroupCallParticipants.append(( + callId, + .call(isTerminated: true) + )) + } case let .UpdateLangPack(langCode, difference): if let difference = difference { if langPackDifferences[langCode] == nil { diff --git a/submodules/TelegramCore/Sources/AccountStateManager.swift b/submodules/TelegramCore/Sources/AccountStateManager.swift index 16885dbb7d..00def9e4fb 100644 --- a/submodules/TelegramCore/Sources/AccountStateManager.swift +++ b/submodules/TelegramCore/Sources/AccountStateManager.swift @@ -148,8 +148,8 @@ public final class AccountStateManager { return self.threadReadStateUpdatesPipe.signal() } - private let groupCallParticipantUpdatesPipe = ValuePipe<[(Int64, GroupCallParticipantsContext.StateUpdate)]>() - public var groupCallParticipantUpdates: Signal<[(Int64, GroupCallParticipantsContext.StateUpdate)], NoError> { + private let groupCallParticipantUpdatesPipe = ValuePipe<[(Int64, GroupCallParticipantsContext.Update)]>() + public var groupCallParticipantUpdates: Signal<[(Int64, GroupCallParticipantsContext.Update)], NoError> { return self.groupCallParticipantUpdatesPipe.signal() } diff --git a/submodules/TelegramCore/Sources/GroupCalls.swift b/submodules/TelegramCore/Sources/GroupCalls.swift index 3e6867c6c2..0ce1e3b04e 100644 --- a/submodules/TelegramCore/Sources/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/GroupCalls.swift @@ -467,7 +467,7 @@ private func binaryInsertionIndex(_ inputArr: [GroupCallParticipantsContext.Part } public final class GroupCallParticipantsContext { - public struct Participant: Equatable { + public struct Participant: Equatable, Comparable { public struct MuteState: Equatable { public var canUnmute: Bool @@ -500,6 +500,24 @@ public final class GroupCallParticipantsContext { } return true } + + public static func <(lhs: Participant, rhs: Participant) -> Bool { + if let lhsActivityTimestamp = lhs.activityTimestamp, let rhsActivityTimestamp = rhs.activityTimestamp { + if lhsActivityTimestamp != rhsActivityTimestamp { + return lhsActivityTimestamp > rhsActivityTimestamp + } + } else if lhs.activityTimestamp != nil { + return true + } else if rhs.activityTimestamp != nil { + return false + } + + if lhs.joinTimestamp != rhs.joinTimestamp { + return lhs.joinTimestamp > rhs.joinTimestamp + } + + return lhs.peer.id < rhs.peer.id + } } public struct State: Equatable { @@ -542,20 +560,25 @@ public final class GroupCallParticipantsContext { var overlayState: OverlayState } - public struct StateUpdate { - public struct ParticipantUpdate { - public var peerId: PeerId - public var ssrc: UInt32 - public var joinTimestamp: Int32 - public var activityTimestamp: Int32? - public var muteState: Participant.MuteState? - public var isRemoved: Bool + public enum Update { + public struct StateUpdate { + public struct ParticipantUpdate { + public var peerId: PeerId + public var ssrc: UInt32 + public var joinTimestamp: Int32 + public var activityTimestamp: Int32? + public var muteState: Participant.MuteState? + public var isRemoved: Bool + } + + public var participantUpdates: [ParticipantUpdate] + public var version: Int32 + + public var removePendingMuteStates: Set } - public var participantUpdates: [ParticipantUpdate] - public var version: Int32 - - public var removePendingMuteStates: Set + case state(update: StateUpdate) + case call(isTerminated: Bool) } private let account: Account @@ -597,7 +620,7 @@ public final class GroupCallParticipantsContext { return self.numberOfActiveSpeakersPromise.get() } - private var updateQueue: [StateUpdate] = [] + private var updateQueue: [Update.StateUpdate] = [] private var isProcessingUpdate: Bool = false private let disposable = MetaDisposable() @@ -616,7 +639,7 @@ public final class GroupCallParticipantsContext { guard let strongSelf = self else { return } - var filteredUpdates: [StateUpdate] = [] + var filteredUpdates: [Update] = [] for (callId, update) in updates { if callId == id { filteredUpdates.append(update) @@ -635,6 +658,53 @@ public final class GroupCallParticipantsContext { } strongSelf.numberOfActiveSpeakersValue = activities.count + + var updatedParticipants = strongSelf.stateValue.state.participants + var indexMap: [PeerId: Int] = [:] + for i in 0 ..< updatedParticipants.count { + indexMap[updatedParticipants[i].peer.id] = i + } + var updated = false + + for (activityPeerId, activity) in activities { + if case let .speakingInGroupCall(timestamp) = activity { + if let index = indexMap[activityPeerId] { + if let activityTimestamp = updatedParticipants[index].activityTimestamp { + if activityTimestamp < timestamp { + updatedParticipants[index].activityTimestamp = timestamp + updated = true + } + } else { + updatedParticipants[index].activityTimestamp = timestamp + updated = true + } + } + } + } + + if updated { + updatedParticipants.sort() + for i in 0 ..< updatedParticipants.count { + if updatedParticipants[i].peer.id == strongSelf.account.peerId { + let member = updatedParticipants[i] + updatedParticipants.remove(at: i) + updatedParticipants.insert(member, at: 0) + break + } + } + + strongSelf.stateValue = InternalState( + state: State( + participants: updatedParticipants, + nextParticipantsFetchOffset: strongSelf.stateValue.state.nextParticipantsFetchOffset, + adminIds: strongSelf.stateValue.state.adminIds, + isCreator: strongSelf.stateValue.state.isCreator, + totalCount: strongSelf.stateValue.state.totalCount, + version: strongSelf.stateValue.state.version + ), + overlayState: strongSelf.stateValue.overlayState + ) + } }) } @@ -644,9 +714,18 @@ public final class GroupCallParticipantsContext { self.activitiesDisposable?.dispose() } - public func addUpdates(updates: [StateUpdate]) { - self.updateQueue.append(contentsOf: updates) - self.beginProcessingUpdatesIfNeeded() + public func addUpdates(updates: [Update]) { + var stateUpdates: [Update.StateUpdate] = [] + for update in updates { + if case let .state(update) = update { + stateUpdates.append(update) + } + } + + if !stateUpdates.isEmpty { + self.updateQueue.append(contentsOf: stateUpdates) + self.beginProcessingUpdatesIfNeeded() + } } private func beginProcessingUpdatesIfNeeded() { @@ -667,7 +746,7 @@ public final class GroupCallParticipantsContext { self.beginProcessingUpdatesIfNeeded() } - private func processUpdate(update: StateUpdate) { + private func processUpdate(update: Update.StateUpdate) { if update.version < self.stateValue.state.version { for peerId in update.removePendingMuteStates { self.stateValue.overlayState.pendingMuteStateChanges.removeValue(forKey: peerId) @@ -702,7 +781,7 @@ public final class GroupCallParticipantsContext { return } - var updatedParticipants = Array(strongSelf.stateValue.state.participants.reversed()) + var updatedParticipants = strongSelf.stateValue.state.participants var updatedTotalCount = strongSelf.stateValue.state.totalCount for participantUpdate in update.participantUpdates { @@ -718,20 +797,29 @@ public final class GroupCallParticipantsContext { assertionFailure() continue } + var previousActivityTimestamp: Int32? if let index = updatedParticipants.firstIndex(where: { $0.peer.id == participantUpdate.peerId }) { + previousActivityTimestamp = updatedParticipants[index].activityTimestamp updatedParticipants.remove(at: index) } else { updatedTotalCount += 1 } + var activityTimestamp: Int32? + if let previousActivityTimestamp = previousActivityTimestamp, let updatedActivityTimestamp = participantUpdate.activityTimestamp { + activityTimestamp = max(updatedActivityTimestamp, previousActivityTimestamp) + } else { + activityTimestamp = participantUpdate.activityTimestamp ?? previousActivityTimestamp + } + let participant = Participant( peer: peer, ssrc: participantUpdate.ssrc, joinTimestamp: participantUpdate.joinTimestamp, + activityTimestamp: activityTimestamp, muteState: participantUpdate.muteState ) - let index = binaryInsertionIndex(updatedParticipants, searchItem: participant.joinTimestamp) - updatedParticipants.insert(participant, at: index) + updatedParticipants.append(participant) } } @@ -744,9 +832,19 @@ public final class GroupCallParticipantsContext { let adminIds = strongSelf.stateValue.state.adminIds let isCreator = strongSelf.stateValue.state.isCreator + updatedParticipants.sort() + for i in 0 ..< updatedParticipants.count { + if updatedParticipants[i].peer.id == strongSelf.account.peerId { + let member = updatedParticipants[i] + updatedParticipants.remove(at: i) + updatedParticipants.insert(member, at: 0) + break + } + } + strongSelf.stateValue = InternalState( state: State( - participants: Array(updatedParticipants.reversed()), + participants: updatedParticipants, nextParticipantsFetchOffset: nextParticipantsFetchOffset, adminIds: adminIds, isCreator: isCreator, @@ -782,6 +880,14 @@ public final class GroupCallParticipantsContext { self.stateValue.overlayState.pendingMuteStateChanges.removeValue(forKey: peerId) } + for participant in self.stateValue.state.participants { + if participant.peer.id == peerId { + if participant.muteState == muteState { + return + } + } + } + let disposable = MetaDisposable() self.stateValue.overlayState.pendingMuteStateChanges[peerId] = OverlayState.MuteStateChange( state: muteState, @@ -800,7 +906,7 @@ public final class GroupCallParticipantsContext { return .single(nil) } var flags: Int32 = 0 - if let muteState = muteState, !muteState.canUnmute { + if let muteState = muteState, (!muteState.canUnmute || peerId == account.peerId) { flags |= 1 << 0 } @@ -818,7 +924,7 @@ public final class GroupCallParticipantsContext { } if let updates = updates { - var stateUpdates: [GroupCallParticipantsContext.StateUpdate] = [] + var stateUpdates: [GroupCallParticipantsContext.Update] = [] loop: for update in updates.allUpdates { switch update { @@ -829,7 +935,7 @@ public final class GroupCallParticipantsContext { continue loop } } - stateUpdates.append(GroupCallParticipantsContext.StateUpdate(participants: participants, version: version, removePendingMuteStates: [peerId])) + stateUpdates.append(.state(update: GroupCallParticipantsContext.Update.StateUpdate(participants: participants, version: version, removePendingMuteStates: [peerId]))) default: break } @@ -845,9 +951,9 @@ public final class GroupCallParticipantsContext { } } -extension GroupCallParticipantsContext.StateUpdate { +extension GroupCallParticipantsContext.Update.StateUpdate { init(participants: [Api.GroupCallParticipant], version: Int32, removePendingMuteStates: Set = Set()) { - var participantUpdates: [GroupCallParticipantsContext.StateUpdate.ParticipantUpdate] = [] + var participantUpdates: [GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate] = [] for participant in participants { switch participant { case let .groupCallParticipant(flags, userId, date, activeDate, source): @@ -859,7 +965,7 @@ extension GroupCallParticipantsContext.StateUpdate { muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: canUnmute) } let isRemoved = (flags & (1 << 1)) != 0 - participantUpdates.append(GroupCallParticipantsContext.StateUpdate.ParticipantUpdate( + participantUpdates.append(GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate( peerId: peerId, ssrc: ssrc, joinTimestamp: date, diff --git a/submodules/TelegramCore/Sources/PeerInputActivity.swift b/submodules/TelegramCore/Sources/PeerInputActivity.swift index 369bae488c..d59fa5bb88 100644 --- a/submodules/TelegramCore/Sources/PeerInputActivity.swift +++ b/submodules/TelegramCore/Sources/PeerInputActivity.swift @@ -10,7 +10,7 @@ public enum PeerInputActivity: Comparable { case playingGame case recordingInstantVideo case uploadingInstantVideo(progress: Int32) - case speakingInGroupCall + case speakingInGroupCall(timestamp: Int32) public var key: Int32 { switch self { @@ -41,7 +41,7 @@ public enum PeerInputActivity: Comparable { } extension PeerInputActivity { - init?(apiType: Api.SendMessageAction) { + init?(apiType: Api.SendMessageAction, timestamp: Int32) { switch apiType { case .sendMessageCancelAction, .sendMessageChooseContactAction, .sendMessageGeoLocationAction, .sendMessageRecordVideoAction: return nil @@ -62,7 +62,7 @@ extension PeerInputActivity { case let .sendMessageUploadRoundAction(progress): self = .uploadingInstantVideo(progress: progress) case .speakingInGroupCallAction: - self = .speakingInGroupCall + self = .speakingInGroupCall(timestamp: timestamp) } } } diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index 4879b3c2f4..8725437af7 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -408,9 +408,9 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, //TODO:localize let titleString: String if let duration = duration { - titleString = "Group Call \(duration)s" + titleString = "Voice chat ended (\(duration)s)" } else { - titleString = "Group Call" + titleString = "Voice chat started" } attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor) case let .customText(text, entities): diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index d9689bbc88..c6a5e9ca65 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -5986,7 +5986,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { return } - let callResult = strongSelf.context.sharedContext.callManager?.requestOrJoinGroupCall(context: strongSelf.context, peerId: peer.id, initialCall: activeCall, endCurrentIfAny: false, sourcePanel: nil) + let callResult = strongSelf.context.sharedContext.callManager?.joinGroupCall(context: strongSelf.context, peerId: peer.id, initialCall: activeCall, endCurrentIfAny: false, sourcePanel: nil) if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult { if currentPeerId == peer.id { strongSelf.context.sharedContext.navigateToCurrentCall(sourcePanel: nil) @@ -5999,7 +5999,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let strongSelf = self, let current = current { strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { if let strongSelf = self { - let _ = strongSelf.context.sharedContext.callManager?.requestOrJoinGroupCall(context: strongSelf.context, peerId: peer.id, initialCall: activeCall, endCurrentIfAny: true, sourcePanel: nil) + let _ = strongSelf.context.sharedContext.callManager?.joinGroupCall(context: strongSelf.context, peerId: peer.id, initialCall: activeCall, endCurrentIfAny: true, sourcePanel: nil) } })]), in: .window(.root)) } else { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift index ff273acc3e..510dee6cb2 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift @@ -949,7 +949,6 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro displayLeave = false if channel.flags.contains(.isCreator) || channel.hasPermission(.inviteMembers) { result.append(.addMember) - result.append(.call) } } switch channel.participationStatus { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index c0f359f47f..b439c6799c 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -2966,6 +2966,16 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } } } else if let channel = peer as? TelegramChannel { + if case .group = channel.info, !channel.flags.contains(.hasVoiceChat) { + if channel.flags.contains(.isCreator) || channel.hasPermission(.manageCalls) { + //TODO:localize + items.append(ActionSheetButtonItem(title: "Create Voice Chat", color: .accent, action: { [weak self] in + dismissAction() + self?.requestCall(isVideo: false) + })) + } + } + if let cachedData = self.data?.cachedData as? CachedChannelData, cachedData.flags.contains(.canViewStats) { items.append(ActionSheetButtonItem(title: presentationData.strings.ChannelInfo_Stats, color: .accent, action: { [weak self] in dismissAction() @@ -3169,7 +3179,41 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD guard let cachedChannelData = self.data?.cachedData as? CachedChannelData else { return } - let _ = self.context.sharedContext.callManager?.requestOrJoinGroupCall(context: self.context, peerId: peer.id, initialCall: cachedChannelData.activeCall, endCurrentIfAny: false, sourcePanel: nil) + + if let activeCall = cachedChannelData.activeCall { + let _ = self.context.sharedContext.callManager?.joinGroupCall(context: self.context, peerId: peer.id, initialCall: activeCall, endCurrentIfAny: false, sourcePanel: nil) + } else { + var dismissStatus: (() -> Void)? + let statusController = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: { + dismissStatus?() + })) + dismissStatus = { [weak self, weak statusController] in + self?.activeActionDisposable.set(nil) + statusController?.dismiss() + } + self.controller?.present(statusController, in: .window(.root)) + self.activeActionDisposable.set((createGroupCall(account: self.context.account, peerId: peer.id) + |> deliverOnMainQueue).start(next: { [weak self] info in + guard let strongSelf = self else { + return + } + let _ = strongSelf.context.sharedContext.callManager?.joinGroupCall(context: strongSelf.context, peerId: peer.id, initialCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash), endCurrentIfAny: false, sourcePanel: nil) + }, error: { [weak self] _ in + dismissStatus?() + + guard let strongSelf = self else { + return + } + strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel) + }, completed: { [weak self] in + dismissStatus?() + + guard let strongSelf = self else { + return + } + strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel) + })) + } return } diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index 353184e3e5..7f20227807 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit 353184e3e5c5e560a59cb836ca8de496c0cc95bb +Subproject commit 7f2022780754c0b3a23f8ce8a940042101120077