diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index 1acf78752c..89191fcbf6 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -108,7 +108,8 @@ public final class CallListController: TelegramBaseController { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style if case .tab = self.mode { - self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed)) + //self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed)) + self.navigationItem.rightBarButtonItem = nil let icon: UIImage? if useSpecialTabBarIcons() { @@ -191,7 +192,7 @@ public final class CallListController: TelegramBaseController { } } - self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed)) + //self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed)) case .navigation: if self.editingMode { self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed)) @@ -383,7 +384,8 @@ public final class CallListController: TelegramBaseController { }) } else { strongSelf.navigationItem.setLeftBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Edit, style: .plain, target: strongSelf, action: #selector(strongSelf.editPressed)), animated: true) - strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(strongSelf.presentationData.theme), style: .plain, target: self, action: #selector(strongSelf.callPressed)), animated: true) + //strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(strongSelf.presentationData.theme), style: .plain, target: self, action: #selector(strongSelf.callPressed)), animated: true) + strongSelf.navigationItem.setRightBarButton(nil, animated: true) } case .navigation: if strongSelf.editingMode { @@ -399,9 +401,9 @@ public final class CallListController: TelegramBaseController { } } } - }, createGroupCall: { [weak self] in + }, openNewCall: { [weak self] in if let strongSelf = self { - strongSelf.createGroupCall(peerIds: [], isVideo: false) + strongSelf.callPressed() } }) @@ -652,7 +654,8 @@ public final class CallListController: TelegramBaseController { switch self.mode { case .tab: self.navigationItem.setLeftBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)), animated: true) - self.navigationItem.setRightBarButton(UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed)), animated: true) + self.navigationItem.setRightBarButton(nil, animated: true) + //self.navigationItem.setRightBarButton(UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed)), animated: true) case .navigation: self.navigationItem.setLeftBarButton(nil, animated: true) self.navigationItem.setRightBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)), animated: true) diff --git a/submodules/CallListUI/Sources/CallListControllerNode.swift b/submodules/CallListUI/Sources/CallListControllerNode.swift index 60be775f9b..05fe89d854 100644 --- a/submodules/CallListUI/Sources/CallListControllerNode.swift +++ b/submodules/CallListUI/Sources/CallListControllerNode.swift @@ -67,16 +67,16 @@ final class CallListNodeInteraction { let delete: ([EngineMessage.Id]) -> Void let updateShowCallsTab: (Bool) -> Void let openGroupCall: (EnginePeer.Id) -> Void - let createGroupCall: () -> Void + let openNewCall: () -> Void - init(setMessageIdWithRevealedOptions: @escaping (EngineMessage.Id?, EngineMessage.Id?) -> Void, call: @escaping (EngineMessage) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, delete: @escaping ([EngineMessage.Id]) -> Void, updateShowCallsTab: @escaping (Bool) -> Void, openGroupCall: @escaping (EnginePeer.Id) -> Void, createGroupCall: @escaping () -> Void) { + init(setMessageIdWithRevealedOptions: @escaping (EngineMessage.Id?, EngineMessage.Id?) -> Void, call: @escaping (EngineMessage) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, delete: @escaping ([EngineMessage.Id]) -> Void, updateShowCallsTab: @escaping (Bool) -> Void, openGroupCall: @escaping (EnginePeer.Id) -> Void, openNewCall: @escaping () -> Void) { self.setMessageIdWithRevealedOptions = setMessageIdWithRevealedOptions self.call = call self.openInfo = openInfo self.delete = delete self.updateShowCallsTab = updateShowCallsTab self.openGroupCall = openGroupCall - self.createGroupCall = createGroupCall + self.openNewCall = openNewCall } } @@ -125,10 +125,10 @@ private func mappedInsertEntries(context: AccountContext, presentationData: Item }), directionHint: entry.directionHint) case let .displayTabInfo(_, text): return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: 0), directionHint: entry.directionHint) - case .createGroupCall: + case .openNewCall: //TODO:localize - let item = ItemListPeerActionItem(presentationData: presentationData, style: showSettings ? .blocks : .plain, icon: PresentationResourcesItemList.linkIcon(presentationData.theme), title: "New Call Link", hasSeparator: false, sectionId: 1, noInsets: true, editing: false, action: { - nodeInteraction.createGroupCall() + let item = ItemListPeerActionItem(presentationData: presentationData, style: showSettings ? .blocks : .plain, icon: .none, title: "New Call", hasSeparator: false, sectionId: 1, height: .generic, noInsets: true, editing: false, action: { + nodeInteraction.openNewCall() }) return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) case let .groupCall(peer, _, isActive): @@ -150,10 +150,10 @@ private func mappedUpdateEntries(context: AccountContext, presentationData: Item }), directionHint: entry.directionHint) case let .displayTabInfo(_, text): return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: 0), directionHint: entry.directionHint) - case .createGroupCall: + case .openNewCall: //TODO:localize - let item = ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.linkIcon(presentationData.theme), title: "New Call Link", sectionId: 1, noInsets: true, editing: false, action: { - nodeInteraction.createGroupCall() + let item = ItemListPeerActionItem(presentationData: presentationData, icon: .none, title: "New Call", sectionId: 1, height: .generic, noInsets: true, editing: false, action: { + nodeInteraction.openNewCall() }) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) case let .groupCall(peer, _, isActive): @@ -224,7 +224,7 @@ final class CallListControllerNode: ASDisplayNode { private let call: (EngineMessage) -> Void private let joinGroupCall: (EnginePeer.Id, EngineGroupCallDescription) -> Void - private let createGroupCall: () -> Void + private let openNewCall: () -> Void private let openInfo: (EnginePeer.Id, [EngineMessage]) -> Void private let emptyStateUpdated: (Bool) -> Void private let emptyStatePromise = Promise() @@ -234,7 +234,7 @@ final class CallListControllerNode: ASDisplayNode { private var previousContentOffset: ListViewVisibleContentOffset? - init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (EngineMessage) -> Void, joinGroupCall: @escaping (EnginePeer.Id, EngineGroupCallDescription) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, emptyStateUpdated: @escaping (Bool) -> Void, createGroupCall: @escaping () -> Void) { + init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (EngineMessage) -> Void, joinGroupCall: @escaping (EnginePeer.Id, EngineGroupCallDescription) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, emptyStateUpdated: @escaping (Bool) -> Void, openNewCall: @escaping () -> Void) { self.controller = controller self.context = context self.mode = mode @@ -243,7 +243,7 @@ final class CallListControllerNode: ASDisplayNode { self.joinGroupCall = joinGroupCall self.openInfo = openInfo self.emptyStateUpdated = emptyStateUpdated - self.createGroupCall = createGroupCall + self.openNewCall = openNewCall self.currentState = CallListNodeState(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, disableAnimations: true, editing: false, messageIdWithRevealedOptions: nil) self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true) @@ -447,11 +447,11 @@ final class CallListControllerNode: ASDisplayNode { strongSelf.joinGroupCall(peerId, activeCall) } })) - }, createGroupCall: { [weak self] in + }, openNewCall: { [weak self] in guard let strongSelf = self else { return } - strongSelf.createGroupCall() + strongSelf.openNewCall() }) let viewProcessingQueue = self.viewProcessingQueue @@ -516,28 +516,18 @@ final class CallListControllerNode: ASDisplayNode { }) } |> distinctUntilChanged - - let canCreateGroupCall = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.App()) - |> map { configuration -> Bool in - var isConferencePossible = true - if let data = configuration.data, let value = data["ios_enable_conference"] as? Double { - isConferencePossible = value != 0.0 - } - return isConferencePossible - } let callListNodeViewTransition = combineLatest( callListViewUpdate, self.statePromise.get(), groupCalls, showCallsTab, - currentGroupCallPeerId, - canCreateGroupCall + currentGroupCallPeerId ) - |> mapToQueue { (updateAndType, state, groupCalls, showCallsTab, currentGroupCallPeerId, canCreateGroupCall) -> Signal in + |> mapToQueue { (updateAndType, state, groupCalls, showCallsTab, currentGroupCallPeerId) -> Signal in let (update, type) = updateAndType - let processedView = CallListNodeView(originalView: update.view, filteredEntries: callListNodeEntriesForView(view: update.view, canCreateGroupCall: canCreateGroupCall && mode == .tab, groupCalls: groupCalls, state: state, showSettings: showSettings, showCallsTab: showCallsTab, isRecentCalls: type == .all, currentGroupCallPeerId: currentGroupCallPeerId), presentationData: state.presentationData) + let processedView = CallListNodeView(originalView: update.view, filteredEntries: callListNodeEntriesForView(view: update.view, displayOpenNewCall: mode == .tab, groupCalls: groupCalls, state: state, showSettings: showSettings, showCallsTab: showCallsTab, isRecentCalls: type == .all, currentGroupCallPeerId: currentGroupCallPeerId), presentationData: state.presentationData) let previous = previousView.swap(processedView) let previousType = previousType.swap(type) diff --git a/submodules/CallListUI/Sources/CallListNodeEntries.swift b/submodules/CallListUI/Sources/CallListNodeEntries.swift index 22356ea608..37d7733834 100644 --- a/submodules/CallListUI/Sources/CallListNodeEntries.swift +++ b/submodules/CallListUI/Sources/CallListNodeEntries.swift @@ -25,7 +25,7 @@ enum CallListNodeEntry: Comparable, Identifiable { enum SortIndex: Comparable { case displayTab case displayTabInfo - case createGroupCall + case openNewCall case groupCall(EnginePeer.Id, String) case message(EngineMessage.Index) case hole(EngineMessage.Index) @@ -41,7 +41,7 @@ enum CallListNodeEntry: Comparable, Identifiable { default: return false } - case .createGroupCall: + case .openNewCall: switch rhs { case .displayTab, .displayTabInfo: return false @@ -50,7 +50,7 @@ enum CallListNodeEntry: Comparable, Identifiable { } case let .groupCall(lhsPeerId, lhsTitle): switch rhs { - case .displayTab, .displayTabInfo, .createGroupCall: + case .displayTab, .displayTabInfo, .openNewCall: return false case let .groupCall(rhsPeerId, rhsTitle): if lhsTitle == rhsTitle { @@ -63,7 +63,7 @@ enum CallListNodeEntry: Comparable, Identifiable { } case let .hole(lhsIndex): switch rhs { - case .displayTab, .displayTabInfo, .groupCall, .createGroupCall: + case .displayTab, .displayTabInfo, .groupCall, .openNewCall: return false case let .hole(rhsIndex): return lhsIndex < rhsIndex @@ -72,7 +72,7 @@ enum CallListNodeEntry: Comparable, Identifiable { } case let .message(lhsIndex): switch rhs { - case .displayTab, .displayTabInfo, .groupCall, .createGroupCall: + case .displayTab, .displayTabInfo, .groupCall, .openNewCall: return false case let .hole(rhsIndex): return lhsIndex < rhsIndex @@ -86,7 +86,7 @@ enum CallListNodeEntry: Comparable, Identifiable { case displayTab(PresentationTheme, String, Bool) case displayTabInfo(PresentationTheme, String) - case createGroupCall + case openNewCall case groupCall(peer: EnginePeer, editing: Bool, isActive: Bool) case messageEntry(topMessage: EngineMessage, messages: [EngineMessage], theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, editing: Bool, hasActiveRevealControls: Bool, displayHeader: Bool, missed: Bool) case holeEntry(index: EngineMessage.Index, theme: PresentationTheme) @@ -97,8 +97,8 @@ enum CallListNodeEntry: Comparable, Identifiable { return .displayTab case .displayTabInfo: return .displayTabInfo - case .createGroupCall: - return .createGroupCall + case .openNewCall: + return .openNewCall case let .groupCall(peer, _, _): return .groupCall(peer.id, peer.compactDisplayTitle) case let .messageEntry(message, _, _, _, _, _, _, _, _): @@ -114,7 +114,7 @@ enum CallListNodeEntry: Comparable, Identifiable { return .setting(0) case .displayTabInfo: return .setting(1) - case .createGroupCall: + case .openNewCall: return .setting(2) case let .groupCall(peer, _, _): return .groupCall(peer.id) @@ -143,8 +143,8 @@ enum CallListNodeEntry: Comparable, Identifiable { } else { return false } - case .createGroupCall: - if case .createGroupCall = rhs { + case .openNewCall: + if case .openNewCall = rhs { return true } else { return false @@ -212,7 +212,7 @@ enum CallListNodeEntry: Comparable, Identifiable { } } -func callListNodeEntriesForView(view: EngineCallList, canCreateGroupCall: Bool, groupCalls: [EnginePeer], state: CallListNodeState, showSettings: Bool, showCallsTab: Bool, isRecentCalls: Bool, currentGroupCallPeerId: EnginePeer.Id?) -> [CallListNodeEntry] { +func callListNodeEntriesForView(view: EngineCallList, displayOpenNewCall: Bool, groupCalls: [EnginePeer], state: CallListNodeState, showSettings: Bool, showCallsTab: Bool, isRecentCalls: Bool, currentGroupCallPeerId: EnginePeer.Id?) -> [CallListNodeEntry] { var result: [CallListNodeEntry] = [] for entry in view.items { switch entry { @@ -237,8 +237,8 @@ func callListNodeEntriesForView(view: EngineCallList, canCreateGroupCall: Bool, } } - if canCreateGroupCall { - result.append(.createGroupCall) + if displayOpenNewCall { + result.append(.openNewCall) } if showSettings { diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 4caf389cbc..08fb8bee08 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -210,6 +210,11 @@ private final class PendingConferenceInvitationContext { case ringing } + enum InvitationError { + case generic + case privacy(peer: EnginePeer?) + } + private let engine: TelegramEngine private var requestDisposable: Disposable? private var stateDisposable: Disposable? @@ -218,19 +223,12 @@ private final class PendingConferenceInvitationContext { private var hadMessage: Bool = false private var didNotifyEnded: Bool = false - init(engine: TelegramEngine, reference: InternalGroupCallReference, peerId: PeerId, isVideo: Bool, onStateUpdated: @escaping (State) -> Void, onEnded: @escaping (Bool) -> Void) { + init(engine: TelegramEngine, reference: InternalGroupCallReference, peerId: PeerId, isVideo: Bool, onStateUpdated: @escaping (State) -> Void, onEnded: @escaping (Bool) -> Void, onError: @escaping (InvitationError) -> Void) { self.engine = engine - self.requestDisposable = (engine.calls.inviteConferenceCallParticipant(reference: reference, peerId: peerId, isVideo: isVideo).startStrict(next: { [weak self] messageId in + self.requestDisposable = ((engine.calls.inviteConferenceCallParticipant(reference: reference, peerId: peerId, isVideo: isVideo) |> deliverOnMainQueue).startStrict(next: { [weak self] messageId in guard let self else { return } - guard let messageId else { - if !self.didNotifyEnded { - self.didNotifyEnded = true - onEnded(false) - } - return - } self.messageId = messageId onStateUpdated(.ringing) @@ -296,6 +294,24 @@ private final class PendingConferenceInvitationContext { } } }) + }, error: { [weak self] error in + guard let self else { + return + } + + if !self.didNotifyEnded { + self.didNotifyEnded = true + onEnded(false) + } + + let mappedError: InvitationError + switch error { + case .privacy(let peer): + mappedError = .privacy(peer: peer) + default: + mappedError = .generic + } + onError(mappedError) })) } @@ -792,6 +808,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { private let e2eContext: ConferenceCallE2EContext? + private var lastErrorAlertTimestamp: Double = 0.0 + init( accountContext: AccountContext, audioSession: ManagedAudioSession, @@ -3536,6 +3554,33 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { onEnded: { success in didEndAlready = true onEnded?(success) + }, + onError: { [weak self] error in + guard let self else { + return + } + + let timestamp = CACurrentMediaTime() + if self.lastErrorAlertTimestamp > timestamp - 1.0 { + return + } + self.lastErrorAlertTimestamp = timestamp + + let presentationData = self.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme) + + //TODO:localize + var errorText = "An error occurred" + switch error { + case let .privacy(peer): + if let peer { + errorText = presentationData.strings.Call_PrivacyErrorMessage(peer.compactDisplayTitle).string + } + default: + break + } + self.accountContext.sharedContext.mainWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [ + TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {}) + ]), on: .root, blockInteraction: false, completion: {}) } ) if !didEndAlready { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift index 3310d935b2..46b0773315 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift @@ -882,13 +882,19 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?, } } -func _internal_inviteConferenceCallParticipant(account: Account, reference: InternalGroupCallReference, peerId: EnginePeer.Id, isVideo: Bool) -> Signal { +public enum InviteConferenceCallParticipantError { + case generic + case privacy(peer: EnginePeer?) +} + +func _internal_inviteConferenceCallParticipant(account: Account, reference: InternalGroupCallReference, peerId: EnginePeer.Id, isVideo: Bool) -> Signal { return account.postbox.transaction { transaction -> Api.InputUser? in return transaction.getPeer(peerId).flatMap(apiInputUser) } - |> mapToSignal { inputPeer -> Signal in + |> castError(InviteConferenceCallParticipantError.self) + |> mapToSignal { inputPeer -> Signal in guard let inputPeer else { - return .complete() + return .fail(.generic) } var flags: Int32 = 0 @@ -897,17 +903,26 @@ func _internal_inviteConferenceCallParticipant(account: Account, reference: Inte } return account.network.request(Api.functions.phone.inviteConferenceCallParticipant(flags: flags, call: reference.apiInputGroupCall, userId: inputPeer)) |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) + |> `catch` { error -> Signal in + if error.errorDescription == "USER_PRIVACY_RESTRICTED" { + return account.postbox.transaction { transaction -> InviteConferenceCallParticipantError in + return .privacy(peer: transaction.getPeer(peerId).flatMap(EnginePeer.init)) + } + |> castError(InviteConferenceCallParticipantError.self) + |> mapToSignal { error -> Signal in + return .fail(error) + } + } + return .fail(.generic) } - |> mapToSignal { result -> Signal in + |> mapToSignal { result -> Signal in if let result { account.stateManager.addUpdates(result) if let message = result.messageIds.first { return .single(message) } } - return .single(nil) + return .fail(.generic) } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift index bc107740dd..77fc5f7ffc 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift @@ -109,7 +109,7 @@ public extension TelegramEngine { return _internal_sendConferenceCallBroadcast(account: self.account, callId: callId, accessHash: accessHash, block: block) } - public func inviteConferenceCallParticipant(reference: InternalGroupCallReference, peerId: EnginePeer.Id, isVideo: Bool) -> Signal { + public func inviteConferenceCallParticipant(reference: InternalGroupCallReference, peerId: EnginePeer.Id, isVideo: Bool) -> Signal { return _internal_inviteConferenceCallParticipant(account: self.account, reference: reference, peerId: peerId, isVideo: isVideo) }