mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Voice chat UI changes
This commit is contained in:
@@ -20,6 +20,7 @@ import DeleteChatPeerActionSheetItem
|
||||
import UndoUI
|
||||
import AlertUI
|
||||
import PresentationDataUtils
|
||||
import PeerInfoUI
|
||||
|
||||
private final class VoiceChatControllerTitleView: UIView {
|
||||
private var theme: PresentationTheme
|
||||
@@ -164,6 +165,7 @@ public final class VoiceChatController: ViewController {
|
||||
enum State {
|
||||
case listening
|
||||
case speaking
|
||||
case invited
|
||||
}
|
||||
|
||||
var peer: Peer
|
||||
@@ -288,7 +290,7 @@ public final class VoiceChatController: ViewController {
|
||||
switch self {
|
||||
case let .invite(_, _, text):
|
||||
return VoiceChatActionItem(presentationData: ItemListPresentationData(presentationData), title: text, icon: .generic(UIImage(bundleImageName: "Chat/Context Menu/AddUser")!), action: {
|
||||
|
||||
interaction.openInvite()
|
||||
})
|
||||
case let .peer(peerEntry):
|
||||
let peer = peerEntry.peer
|
||||
@@ -308,6 +310,9 @@ public final class VoiceChatController: ViewController {
|
||||
case .speaking:
|
||||
text = .text(presentationData.strings.VoiceChat_StatusSpeaking, .constructive)
|
||||
icon = .microphone(false, UIColor(rgb: 0x34c759))
|
||||
case .invited:
|
||||
text = .text(presentationData.strings.VoiceChat_StatusInvited, .generic)
|
||||
icon = .invite(true)
|
||||
}
|
||||
|
||||
let revealOptions: [VoiceChatParticipantItem.RevealOption] = []
|
||||
@@ -362,6 +367,7 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
private var currentGroupMembers: [RenderedChannelParticipant]?
|
||||
private var currentCallMembers: [GroupCallParticipantsContext.Participant]?
|
||||
private var currentInvitedPeers: [Peer]?
|
||||
private var currentSpeakingPeers: Set<PeerId>?
|
||||
private var accountPeer: Peer?
|
||||
|
||||
@@ -395,6 +401,8 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
private var itemInteraction: Interaction?
|
||||
|
||||
private let inviteDisposable = MetaDisposable()
|
||||
|
||||
init(controller: VoiceChatController, sharedContext: SharedAccountContext, call: PresentationGroupCall) {
|
||||
self.controller = controller
|
||||
self.sharedContext = sharedContext
|
||||
@@ -442,11 +450,124 @@ public final class VoiceChatController: ViewController {
|
||||
self?.call.updateMuteState(peerId: peerId, isMuted: isMuted)
|
||||
}, openPeer: { [weak self] peerId in
|
||||
if let strongSelf = self, let navigationController = strongSelf.controller?.parentNavigationController {
|
||||
strongSelf.controller?.dismiss()
|
||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), keepStack: .always, purposefulAction: {}, peekData: nil))
|
||||
let context = strongSelf.context
|
||||
strongSelf.controller?.dismiss(completion: {
|
||||
Queue.mainQueue().justDispatch {
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), keepStack: .always, purposefulAction: {}, peekData: nil))
|
||||
}
|
||||
})
|
||||
}
|
||||
}, openInvite: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
}, openInvite: {
|
||||
|
||||
let groupPeerId = strongSelf.call.peerId
|
||||
let _ = (strongSelf.context.account.postbox.transaction { transaction -> Peer? in
|
||||
return transaction.getPeer(groupPeerId)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { groupPeer in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
guard let groupPeer = groupPeer as? TelegramChannel else {
|
||||
return
|
||||
}
|
||||
|
||||
var filters: [ChannelMembersSearchFilter] = []
|
||||
if let currentCallMembers = strongSelf.currentCallMembers {
|
||||
filters.append(.disable(Array(currentCallMembers.map { $0.peer.id })))
|
||||
}
|
||||
if !groupPeer.hasPermission(.inviteMembers) {
|
||||
filters.append(.excludeNonMembers)
|
||||
}
|
||||
|
||||
var dismissController: (() -> Void)?
|
||||
let controller = ChannelMembersSearchController(context: strongSelf.context, peerId: groupPeer.id, forceTheme: strongSelf.darkTheme, mode: .inviteToCall, filters: filters, openPeer: { peer, participant in
|
||||
guard let strongSelf = self else {
|
||||
dismissController?()
|
||||
return
|
||||
}
|
||||
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
if peer.id == strongSelf.context.account.peerId {
|
||||
return
|
||||
}
|
||||
if let participant = participant {
|
||||
strongSelf.call.invitePeer(participant.peer.id)
|
||||
dismissController?()
|
||||
} else {
|
||||
let selfController = strongSelf.controller
|
||||
let inviteDisposable = strongSelf.inviteDisposable
|
||||
var inviteSignal = strongSelf.context.peerChannelMemberCategoriesContextsManager.addMembers(account: strongSelf.context.account, peerId: groupPeer.id, memberIds: [peer.id])
|
||||
var cancelImpl: (() -> Void)?
|
||||
let progressSignal = Signal<Never, NoError> { [weak selfController] subscriber in
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
||||
cancelImpl?()
|
||||
}))
|
||||
selfController?.present(controller, in: .window(.root))
|
||||
return ActionDisposable { [weak controller] in
|
||||
Queue.mainQueue().async() {
|
||||
controller?.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|> runOn(Queue.mainQueue())
|
||||
|> delay(0.15, queue: Queue.mainQueue())
|
||||
let progressDisposable = progressSignal.start()
|
||||
|
||||
inviteSignal = inviteSignal
|
||||
|> afterDisposed {
|
||||
Queue.mainQueue().async {
|
||||
progressDisposable.dispose()
|
||||
}
|
||||
}
|
||||
cancelImpl = {
|
||||
inviteDisposable.set(nil)
|
||||
}
|
||||
|
||||
inviteDisposable.set((inviteSignal |> deliverOnMainQueue).start(next: { _ in
|
||||
guard let strongSelf = self else {
|
||||
dismissController?()
|
||||
return
|
||||
}
|
||||
strongSelf.call.invitePeer(peer.id)
|
||||
dismissController?()
|
||||
}, error: { error in
|
||||
dismissController?()
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let text: String
|
||||
switch error {
|
||||
case .limitExceeded:
|
||||
text = presentationData.strings.Channel_ErrorAddTooMuch
|
||||
case .tooMuchJoined:
|
||||
text = presentationData.strings.Invite_ChannelsTooMuch
|
||||
case .generic:
|
||||
text = presentationData.strings.Login_UnknownError
|
||||
case .restricted:
|
||||
text = presentationData.strings.Channel_ErrorAddBlocked
|
||||
case .notMutualContact:
|
||||
text = presentationData.strings.GroupInfo_AddUserLeftError
|
||||
case .botDoesntSupportGroups:
|
||||
text = presentationData.strings.Channel_BotDoesntSupportGroups
|
||||
case .tooMuchBots:
|
||||
text = presentationData.strings.Channel_TooMuchBots
|
||||
case .bot:
|
||||
text = presentationData.strings.Login_UnknownError
|
||||
}
|
||||
strongSelf.controller?.present(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}))
|
||||
}
|
||||
})
|
||||
dismissController = { [weak controller] in
|
||||
controller?.dismiss()
|
||||
}
|
||||
strongSelf.controller?.push(controller)
|
||||
})
|
||||
}, peerContextAction: { [weak self] entry, sourceNode, gesture in
|
||||
guard let strongSelf = self, let controller = strongSelf.controller, let sourceNode = sourceNode as? ContextExtractedContentContainingNode else {
|
||||
return
|
||||
@@ -542,16 +663,27 @@ public final class VoiceChatController: ViewController {
|
||||
self.addSubnode(self.dimNode)
|
||||
self.addSubnode(self.contentContainer)
|
||||
self.contentContainer.addSubnode(self.backgroundNode)
|
||||
|
||||
self.memberStatesDisposable = (self.call.members
|
||||
|> deliverOnMainQueue).start(next: { [weak self] callMembers in
|
||||
|
||||
let context = self.context
|
||||
let invitedPeers: Signal<[Peer], NoError> = self.call.invitedPeers
|
||||
|> mapToSignal { ids -> Signal<[Peer], NoError> in
|
||||
return context.account.postbox.transaction { transaction -> [Peer] in
|
||||
return ids.compactMap(transaction.getPeer)
|
||||
}
|
||||
}
|
||||
|
||||
self.memberStatesDisposable = combineLatest(queue: .mainQueue(),
|
||||
self.call.members,
|
||||
invitedPeers
|
||||
).start(next: { [weak self] callMembers, invitedPeers in
|
||||
guard let strongSelf = self, let callMembers = callMembers else {
|
||||
return
|
||||
}
|
||||
if let groupMembers = strongSelf.currentGroupMembers {
|
||||
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, groupMembers: groupMembers, callMembers: callMembers.participants, speakingPeers: callMembers.speakingParticipants)
|
||||
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, groupMembers: groupMembers, callMembers: callMembers.participants, invitedPeers: invitedPeers, speakingPeers: callMembers.speakingParticipants)
|
||||
} else {
|
||||
strongSelf.currentCallMembers = callMembers.participants
|
||||
strongSelf.currentInvitedPeers = invitedPeers
|
||||
}
|
||||
|
||||
let subtitle = strongSelf.presentationData.strings.VoiceChat_Panel_Members(Int32(max(1, callMembers.totalCount)))
|
||||
@@ -574,7 +706,7 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
if !strongSelf.didSetDataReady {
|
||||
strongSelf.accountPeer = accountPeer
|
||||
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, groupMembers: [], callMembers: strongSelf.currentCallMembers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? Set())
|
||||
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, groupMembers: [], callMembers: strongSelf.currentCallMembers ?? [], invitedPeers: strongSelf.currentInvitedPeers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? Set())
|
||||
|
||||
if let peer = peerViewMainPeer(view), let channel = peer as? TelegramChannel {
|
||||
let addressName = channel.addressName ?? ""
|
||||
@@ -612,7 +744,7 @@ public final class VoiceChatController: ViewController {
|
||||
}
|
||||
|
||||
if wasMuted != (state.muteState != nil), let groupMembers = strongSelf.currentGroupMembers {
|
||||
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, groupMembers: groupMembers, callMembers: strongSelf.currentCallMembers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? Set())
|
||||
strongSelf.updateMembers(muteState: strongSelf.effectiveMuteState, groupMembers: groupMembers, callMembers: strongSelf.currentCallMembers ?? [], invitedPeers: strongSelf.currentInvitedPeers ?? [], speakingPeers: strongSelf.currentSpeakingPeers ?? Set())
|
||||
}
|
||||
|
||||
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||
@@ -781,6 +913,7 @@ public final class VoiceChatController: ViewController {
|
||||
self.memberStatesDisposable?.dispose()
|
||||
self.audioLevelsDisposable?.dispose()
|
||||
self.myAudioLevelDisposable?.dispose()
|
||||
self.inviteDisposable.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
@@ -860,7 +993,7 @@ public final class VoiceChatController: ViewController {
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring))
|
||||
}
|
||||
self.updateMembers(muteState: self.effectiveMuteState, groupMembers: self.currentGroupMembers ?? [], callMembers: self.currentCallMembers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set())
|
||||
self.updateMembers(muteState: self.effectiveMuteState, groupMembers: self.currentGroupMembers ?? [], callMembers: self.currentCallMembers ?? [], invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set())
|
||||
case .ended, .cancelled:
|
||||
self.hapticFeedback.impact(.light)
|
||||
|
||||
@@ -875,7 +1008,7 @@ public final class VoiceChatController: ViewController {
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .spring))
|
||||
}
|
||||
self.updateMembers(muteState: self.effectiveMuteState, groupMembers: self.currentGroupMembers ?? [], callMembers: self.currentCallMembers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set())
|
||||
self.updateMembers(muteState: self.effectiveMuteState, groupMembers: self.currentGroupMembers ?? [], callMembers: self.currentCallMembers ?? [], invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set())
|
||||
default:
|
||||
break
|
||||
}
|
||||
@@ -1195,7 +1328,7 @@ public final class VoiceChatController: ViewController {
|
||||
})
|
||||
}
|
||||
|
||||
private func updateMembers(muteState: GroupCallParticipantsContext.Participant.MuteState?, groupMembers: [RenderedChannelParticipant], callMembers: [GroupCallParticipantsContext.Participant], speakingPeers: Set<PeerId>) {
|
||||
private func updateMembers(muteState: GroupCallParticipantsContext.Participant.MuteState?, groupMembers: [RenderedChannelParticipant], callMembers: [GroupCallParticipantsContext.Participant], invitedPeers: [Peer], speakingPeers: Set<PeerId>) {
|
||||
var sortedCallMembers = callMembers
|
||||
sortedCallMembers.sort()
|
||||
|
||||
@@ -1213,6 +1346,7 @@ public final class VoiceChatController: ViewController {
|
||||
self.currentGroupMembers = groupMembers
|
||||
self.currentCallMembers = callMembers
|
||||
self.currentSpeakingPeers = speakingPeers
|
||||
self.currentInvitedPeers = invitedPeers
|
||||
|
||||
let previousEntries = self.currentEntries
|
||||
var entries: [ListEntry] = []
|
||||
@@ -1221,7 +1355,7 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
var processedPeerIds = Set<PeerId>()
|
||||
|
||||
entries.append(.invite(self.presentationData.theme, self.presentationData.strings, "Invite Member"))
|
||||
entries.append(.invite(self.presentationData.theme, self.presentationData.strings, self.presentationData.strings.VoiceChat_InviteMember))
|
||||
|
||||
for member in callMembers {
|
||||
if processedPeerIds.contains(member.peer.id) {
|
||||
@@ -1265,6 +1399,23 @@ public final class VoiceChatController: ViewController {
|
||||
)))
|
||||
}
|
||||
|
||||
for peer in invitedPeers {
|
||||
if processedPeerIds.contains(peer.id) {
|
||||
continue
|
||||
}
|
||||
processedPeerIds.insert(peer.id)
|
||||
|
||||
entries.append(.peer(PeerEntry(
|
||||
peer: peer,
|
||||
presence: nil,
|
||||
activityTimestamp: Int32.max - 1 - index,
|
||||
state: .invited,
|
||||
muteState: nil,
|
||||
canManageCall: false
|
||||
)))
|
||||
index += 1
|
||||
}
|
||||
|
||||
self.currentEntries = entries
|
||||
|
||||
let presentationData = self.presentationData.withUpdated(theme: self.darkTheme)
|
||||
@@ -1394,7 +1545,7 @@ public final class VoiceChatController: ViewController {
|
||||
self.didAppearOnce = false
|
||||
|
||||
completion?()
|
||||
self.presentingViewController?.dismiss(animated: false)
|
||||
self.dismiss(animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1405,7 +1556,7 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
self.controllerNode.animateOut(completion: { [weak self] in
|
||||
completion?()
|
||||
self?.presentingViewController?.dismiss(animated: false)
|
||||
self?.dismiss(animated: false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user