diff --git a/submodules/AccountContext/Sources/PresentationCallManager.swift b/submodules/AccountContext/Sources/PresentationCallManager.swift index aae54bbca7..7c12ee423b 100644 --- a/submodules/AccountContext/Sources/PresentationCallManager.swift +++ b/submodules/AccountContext/Sources/PresentationCallManager.swift @@ -163,21 +163,29 @@ public struct PresentationGroupCallState: Equatable { case connected } + public enum DefaultParticipantMuteState { + case unmuted + case muted + } + public var networkState: NetworkState public var canManageCall: Bool public var adminIds: Set public var muteState: GroupCallParticipantsContext.Participant.MuteState? + public var defaultParticipantMuteState: DefaultParticipantMuteState? public init( networkState: NetworkState, canManageCall: Bool, adminIds: Set, - muteState: GroupCallParticipantsContext.Participant.MuteState? + muteState: GroupCallParticipantsContext.Participant.MuteState?, + defaultParticipantMuteState: DefaultParticipantMuteState? ) { self.networkState = networkState self.canManageCall = canManageCall self.adminIds = adminIds self.muteState = muteState + self.defaultParticipantMuteState = defaultParticipantMuteState } } @@ -263,6 +271,7 @@ public protocol PresentationGroupCall: class { func toggleIsMuted() func setIsMuted(action: PresentationGroupCallMuteAction) + func updateDefaultParticipantsAreMuted(isMuted: Bool) func setCurrentAudioOutput(_ output: AudioSessionOutput) func updateMuteState(peerId: PeerId, isMuted: Bool) diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index fed09e9f16..0b2ae4c1cb 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -21,7 +21,8 @@ private extension PresentationGroupCallState { networkState: .connecting, canManageCall: false, adminIds: Set(), - muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true) + muteState: GroupCallParticipantsContext.Participant.MuteState(canUnmute: true), + defaultParticipantMuteState: nil ) } } @@ -389,7 +390,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } } - case let .call(isTerminated): + case let .call(isTerminated, _): if isTerminated { strongSelf._canBeRemoved.set(.single(true)) } @@ -579,6 +580,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.summaryInfoState.set(.single(SummaryInfoState(info: callInfo))) self.stateValue.canManageCall = initialState.isCreator || initialState.adminIds.contains(self.accountContext.account.peerId) + if self.stateValue.canManageCall && initialState.defaultParticipantsAreMuted.canChange { + self.stateValue.defaultParticipantMuteState = initialState.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted + } self.ssrcMapping.removeAll() var ssrcs: [UInt32] = [] @@ -640,6 +644,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { strongSelf.stateValue.adminIds = state.adminIds + if (state.isCreator || state.adminIds.contains(strongSelf.accountContext.account.peerId)) && state.defaultParticipantsAreMuted.canChange { + strongSelf.stateValue.defaultParticipantMuteState = state.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted + } + strongSelf.summaryParticipantsState.set(.single(SummaryParticipantsState( participantCount: state.totalCount, topParticipants: topParticipants, @@ -921,4 +929,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } } + + public func updateDefaultParticipantsAreMuted(isMuted: Bool) { + self.participantsContext?.updateDefaultParticipantsAreMuted(isMuted: isMuted) + } } diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 9eb374b9d6..f984fa4915 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -614,16 +614,36 @@ public final class VoiceChatController: ViewController { var items: [ContextMenuItem] = [] - if let callState = strongSelf.callState, callState.canManageCall { + if let callState = strongSelf.callState, callState.canManageCall, let defaultParticipantMuteState = callState.defaultParticipantMuteState { + let isMuted = defaultParticipantMuteState == .muted + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_SpeakPermissionEveryone, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.actionSheet.primaryTextColor) + if isMuted { + return nil + } else { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.actionSheet.primaryTextColor) + } }, action: { _, f in f(.dismissWithoutContent) + guard let strongSelf = self else { + return + } + strongSelf.call.updateDefaultParticipantsAreMuted(isMuted: false) }))) - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_SpeakPermissionAdmin, icon: { _ in return nil}, action: { _, f in + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_SpeakPermissionAdmin, icon: { theme in + if !isMuted { + return nil + } else { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.actionSheet.primaryTextColor) + } + }, action: { _, f in f(.dismissWithoutContent) + guard let strongSelf = self else { + return + } + strongSelf.call.updateDefaultParticipantsAreMuted(isMuted: true) }))) } diff --git a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift index f32c0b2ef0..c42b9760f5 100644 --- a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift @@ -2970,11 +2970,24 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP return current } }) + + switch call { + case let .groupCall(flags, _, _, _, _, _): + let isMuted = (flags & (1 << 1)) != 0 + let canChange = (flags & (1 << 2)) != 0 + let defaultParticipantsAreMuted = GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: isMuted, canChange: isMuted) + updatedGroupCallParticipants.append(( + info.id, + .call(isTerminated: false, defaultParticipantsAreMuted: defaultParticipantsAreMuted) + )) + default: + break + } } case let .groupCallDiscarded(callId, _, _): updatedGroupCallParticipants.append(( callId, - .call(isTerminated: true) + .call(isTerminated: true, defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: false, canChange: false)) )) transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in diff --git a/submodules/TelegramCore/Sources/GroupCalls.swift b/submodules/TelegramCore/Sources/GroupCalls.swift index 55a845ec94..7781c12750 100644 --- a/submodules/TelegramCore/Sources/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/GroupCalls.swift @@ -244,6 +244,7 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash nextParticipantsFetchOffset: nextParticipantsFetchOffset, adminIds: Set(), isCreator: false, + defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: false, canChange: false), totalCount: totalCount, version: version ) @@ -322,6 +323,16 @@ public func joinGroupCall(account: Account, peerId: PeerId, callId: Int64, acces switch update { case let .updateGroupCall(_, call): maybeParsedCall = GroupCallInfo(call) + + switch call { + case let .groupCall(flags, _, _, _, _, _): + let isMuted = (flags & (1 << 1)) != 0 + let canChange = (flags & (1 << 2)) != 0 + state.defaultParticipantsAreMuted = GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: isMuted, canChange: canChange) + default: + break + } + break loop default: break @@ -525,10 +536,16 @@ public final class GroupCallParticipantsContext { } public struct State: Equatable { + public struct DefaultParticipantsAreMuted: Equatable { + public var isMuted: Bool + public var canChange: Bool + } + public var participants: [Participant] public var nextParticipantsFetchOffset: String? public var adminIds: Set public var isCreator: Bool + public var defaultParticipantsAreMuted: DefaultParticipantsAreMuted public var totalCount: Int public var version: Int32 } @@ -582,7 +599,7 @@ public final class GroupCallParticipantsContext { } case state(update: StateUpdate) - case call(isTerminated: Bool) + case call(isTerminated: Bool, defaultParticipantsAreMuted: State.DefaultParticipantsAreMuted) } private let account: Account @@ -631,6 +648,8 @@ public final class GroupCallParticipantsContext { private let updatesDisposable = MetaDisposable() private var activitiesDisposable: Disposable? + private let updateDefaultMuteDisposable = MetaDisposable() + public init(account: Account, peerId: PeerId, id: Int64, accessHash: Int64, state: State) { self.account = account self.id = id @@ -703,6 +722,7 @@ public final class GroupCallParticipantsContext { nextParticipantsFetchOffset: strongSelf.stateValue.state.nextParticipantsFetchOffset, adminIds: strongSelf.stateValue.state.adminIds, isCreator: strongSelf.stateValue.state.isCreator, + defaultParticipantsAreMuted: strongSelf.stateValue.state.defaultParticipantsAreMuted, totalCount: strongSelf.stateValue.state.totalCount, version: strongSelf.stateValue.state.version ), @@ -716,6 +736,7 @@ public final class GroupCallParticipantsContext { self.disposable.dispose() self.updatesDisposable.dispose() self.activitiesDisposable?.dispose() + self.updateDefaultMuteDisposable.dispose() } public func addUpdates(updates: [Update]) { @@ -723,6 +744,8 @@ public final class GroupCallParticipantsContext { for update in updates { if case let .state(update) = update { stateUpdates.append(update) + } else if case let .call(_, defaultParticipantsAreMuted) = update { + self.stateValue.state.defaultParticipantsAreMuted = defaultParticipantsAreMuted } } @@ -835,6 +858,7 @@ public final class GroupCallParticipantsContext { let nextParticipantsFetchOffset = strongSelf.stateValue.state.nextParticipantsFetchOffset let adminIds = strongSelf.stateValue.state.adminIds let isCreator = strongSelf.stateValue.state.isCreator + let defaultParticipantsAreMuted = strongSelf.stateValue.state.defaultParticipantsAreMuted updatedParticipants.sort() for i in 0 ..< updatedParticipants.count { @@ -852,6 +876,7 @@ public final class GroupCallParticipantsContext { nextParticipantsFetchOffset: nextParticipantsFetchOffset, adminIds: adminIds, isCreator: isCreator, + defaultParticipantsAreMuted: defaultParticipantsAreMuted, totalCount: updatedTotalCount, version: update.version ), @@ -953,6 +978,22 @@ public final class GroupCallParticipantsContext { } })) } + + public func updateDefaultParticipantsAreMuted(isMuted: Bool) { + if isMuted == self.stateValue.state.defaultParticipantsAreMuted.isMuted { + return + } + self.stateValue.state.defaultParticipantsAreMuted.isMuted = isMuted + + + self.updateDefaultMuteDisposable.set((self.account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 0, call: .inputGroupCall(id: self.id, accessHash: self.accessHash), joinMuted: isMuted ? .boolTrue : .boolFalse)) + |> deliverOnMainQueue).start(next: { [weak self] updates in + guard let strongSelf = self else { + return + } + strongSelf.account.stateManager.addUpdates(updates) + })) + } } extension GroupCallParticipantsContext.Update.StateUpdate {