mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Conference calls
This commit is contained in:
parent
5deca3fac7
commit
1a31d83699
@ -423,6 +423,7 @@ public protocol PresentationGroupCall: AnyObject {
|
||||
var internalId: CallSessionInternalId { get }
|
||||
var peerId: EnginePeer.Id? { get }
|
||||
var callId: Int64? { get }
|
||||
var currentReference: InternalGroupCallReference? { get }
|
||||
|
||||
var hasVideo: Bool { get }
|
||||
var hasScreencast: Bool { get }
|
||||
|
@ -138,16 +138,7 @@ class CallListCallItem: ListViewItem {
|
||||
|
||||
func selected(listView: ListView) {
|
||||
listView.clearHighlightAnimated(true)
|
||||
var isVideo = false
|
||||
for media in self.topMessage.media {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
if case let .phoneCall(_, _, _, isVideoValue) = action.action {
|
||||
isVideo = isVideoValue
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
self.interaction.call(self.topMessage.id.peerId, isVideo)
|
||||
self.interaction.call(self.topMessage)
|
||||
}
|
||||
|
||||
static func mergeType(item: CallListCallItem, previousItem: ListViewItem?, nextItem: ListViewItem?) -> (first: Bool, last: Bool, firstWithHeader: Bool) {
|
||||
@ -262,15 +253,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
||||
guard let item = self?.layoutParams?.0 else {
|
||||
return false
|
||||
}
|
||||
var isVideo = false
|
||||
for media in item.topMessage.media {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
if case let .phoneCall(_, _, _, isVideoValue) = action.action {
|
||||
isVideo = isVideoValue
|
||||
}
|
||||
}
|
||||
}
|
||||
item.interaction.call(item.topMessage.id.peerId, isVideo)
|
||||
item.interaction.call(item.topMessage)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -390,6 +373,9 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
||||
var hadDuration = false
|
||||
var callDuration: Int32?
|
||||
|
||||
var isConference = false
|
||||
var conferenceIsDeclined = false
|
||||
|
||||
for message in item.messages {
|
||||
inner: for media in message.media {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
@ -411,6 +397,36 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
||||
} else {
|
||||
callDuration = nil
|
||||
}
|
||||
} else if case let .conferenceCall(conferenceCall) = action.action {
|
||||
isConference = true
|
||||
|
||||
isVideo = conferenceCall.flags.contains(.isVideo)
|
||||
if message.flags.contains(.Incoming) {
|
||||
hasIncoming = true
|
||||
//TODO:localize
|
||||
let missedTimeout: Int32
|
||||
#if DEBUG
|
||||
missedTimeout = 5
|
||||
#else
|
||||
missedTimeout = 30
|
||||
#endif
|
||||
let currentTime = Int32(Date().timeIntervalSince1970)
|
||||
if conferenceCall.flags.contains(.isMissed) {
|
||||
titleColor = item.presentationData.theme.list.itemDestructiveColor
|
||||
conferenceIsDeclined = true
|
||||
} else if message.timestamp < currentTime - missedTimeout {
|
||||
titleColor = item.presentationData.theme.list.itemDestructiveColor
|
||||
hasMissed = true
|
||||
}
|
||||
} else {
|
||||
hasOutgoing = true
|
||||
}
|
||||
if callDuration == nil && !hadDuration {
|
||||
hadDuration = true
|
||||
callDuration = conferenceCall.duration
|
||||
} else {
|
||||
callDuration = nil
|
||||
}
|
||||
}
|
||||
break inner
|
||||
}
|
||||
@ -441,7 +457,18 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
||||
titleAttributedString = NSAttributedString(string: channel.title, font: titleFont, textColor: titleColor)
|
||||
}
|
||||
|
||||
if hasMissed {
|
||||
if isConference {
|
||||
//TODO:localize
|
||||
if conferenceIsDeclined {
|
||||
statusAttributedString = NSAttributedString(string: "Declined Group Call", font: statusFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
||||
} else if hasMissed {
|
||||
statusAttributedString = NSAttributedString(string: "Missed Group Call", font: statusFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
||||
} else {
|
||||
statusAttributedString = NSAttributedString(string: "Group call", font: statusFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
||||
}
|
||||
|
||||
statusAccessibilityString = statusAttributedString?.string ?? ""
|
||||
} else if hasMissed {
|
||||
statusAttributedString = NSAttributedString(string: item.presentationData.strings.Notification_CallMissedShort, font: statusFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
||||
statusAccessibilityString = isVideo ? item.presentationData.strings.Call_VoiceOver_VideoCallMissed : item.presentationData.strings.Call_VoiceOver_VoiceCallMissed
|
||||
} else if hasIncoming && hasOutgoing {
|
||||
|
@ -92,6 +92,7 @@ public final class CallListController: TelegramBaseController {
|
||||
|
||||
private let createActionDisposable = MetaDisposable()
|
||||
private let clearDisposable = MetaDisposable()
|
||||
private var createConferenceCallDisposable: Disposable?
|
||||
|
||||
public init(context: AccountContext, mode: CallListControllerMode) {
|
||||
self.context = context
|
||||
@ -163,6 +164,7 @@ public final class CallListController: TelegramBaseController {
|
||||
self.presentationDataDisposable?.dispose()
|
||||
self.peerViewDisposable.dispose()
|
||||
self.clearDisposable.dispose()
|
||||
self.createConferenceCallDisposable?.dispose()
|
||||
}
|
||||
|
||||
private func updateThemeAndStrings() {
|
||||
@ -210,11 +212,16 @@ public final class CallListController: TelegramBaseController {
|
||||
guard !self.presentAccountFrozenInfoIfNeeded() else {
|
||||
return
|
||||
}
|
||||
let _ = (self.context.engine.calls.createConferenceCall()
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] call in
|
||||
if self.createConferenceCallDisposable != nil {
|
||||
return
|
||||
}
|
||||
self.createConferenceCallDisposable = (self.context.engine.calls.createConferenceCall()
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] call in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.createConferenceCallDisposable?.dispose()
|
||||
self.createConferenceCallDisposable = nil
|
||||
|
||||
let openCall: () -> Void = { [weak self] in
|
||||
guard let self else {
|
||||
@ -235,7 +242,13 @@ public final class CallListController: TelegramBaseController {
|
||||
)
|
||||
}
|
||||
|
||||
let controller = InviteLinkInviteController(context: self.context, updatedPresentationData: nil, mode: .groupCall(link: call.link, isRecentlyCreated: true), parentNavigationController: self.navigationController as? NavigationController, completed: { [weak self] result in
|
||||
let controller = InviteLinkInviteController(
|
||||
context: self.context,
|
||||
updatedPresentationData: nil,
|
||||
mode: .groupCall(InviteLinkInviteController.Mode.GroupCall(callId: call.callInfo.id, accessHash: call.callInfo.accessHash, isRecentlyCreated: true, canRevoke: true)),
|
||||
initialInvite: .link(link: call.link, title: nil, isPermanent: true, requestApproval: false, isRevoked: false, adminId: self.context.account.peerId, date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil, pricing: nil),
|
||||
parentNavigationController: self.navigationController as? NavigationController,
|
||||
completed: { [weak self] result in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
@ -254,15 +267,26 @@ public final class CallListController: TelegramBaseController {
|
||||
openCall()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
self.present(controller, in: .window(.root), with: nil)
|
||||
})
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = CallListControllerNode(controller: self, context: self.context, mode: self.mode, presentationData: self.presentationData, call: { [weak self] peerId, isVideo in
|
||||
if let strongSelf = self {
|
||||
strongSelf.call(peerId, isVideo: isVideo)
|
||||
self.displayNode = CallListControllerNode(controller: self, context: self.context, mode: self.mode, presentationData: self.presentationData, call: { [weak self] message in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
for media in message.media {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
if case let .phoneCall(_, _, _, isVideo) = action.action {
|
||||
self.call(message.id.peerId, isVideo: isVideo)
|
||||
} else if case .conferenceCall = action.action {
|
||||
self.openGroupCall(message: message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, joinGroupCall: { [weak self] peerId, activeCall in
|
||||
if let self {
|
||||
@ -573,6 +597,48 @@ public final class CallListController: TelegramBaseController {
|
||||
}))
|
||||
}
|
||||
|
||||
private func openGroupCall(message: EngineMessage) {
|
||||
var action: TelegramMediaAction?
|
||||
for media in message.media {
|
||||
if let media = media as? TelegramMediaAction {
|
||||
action = media
|
||||
break
|
||||
}
|
||||
}
|
||||
guard case let .conferenceCall(conferenceCall) = action?.action else {
|
||||
return
|
||||
}
|
||||
if conferenceCall.duration != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if let currentGroupCallController = self.context.sharedContext as? VoiceChatController, case let .group(groupCall) = currentGroupCallController.call, let currentCallId = groupCall.callId, currentCallId == conferenceCall.callId {
|
||||
self.context.sharedContext.navigateToCurrentCall()
|
||||
return
|
||||
}
|
||||
|
||||
let signal = self.context.engine.peers.joinCallInvitationInformation(messageId: message.id)
|
||||
let _ = (signal
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] resolvedCallLink in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.context.sharedContext.callManager?.joinConferenceCall(
|
||||
accountContext: self.context,
|
||||
initialCall: EngineGroupCallDescription(
|
||||
id: resolvedCallLink.id,
|
||||
accessHash: resolvedCallLink.accessHash,
|
||||
title: nil,
|
||||
scheduleTimestamp: nil,
|
||||
subscribedToScheduled: false,
|
||||
isStream: false
|
||||
),
|
||||
reference: .message(id: message.id),
|
||||
beginWithVideo: conferenceCall.flags.contains(.isVideo)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
override public func tabBarItemContextAction(sourceNode: ContextExtractedContentContainingNode, gesture: ContextGesture) {
|
||||
var items: [ContextMenuItem] = []
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Calls_StartNewCall, icon: { theme in
|
||||
|
@ -62,14 +62,14 @@ private extension EngineCallList.Item {
|
||||
|
||||
final class CallListNodeInteraction {
|
||||
let setMessageIdWithRevealedOptions: (EngineMessage.Id?, EngineMessage.Id?) -> Void
|
||||
let call: (EnginePeer.Id, Bool) -> Void
|
||||
let call: (EngineMessage) -> Void
|
||||
let openInfo: (EnginePeer.Id, [EngineMessage]) -> Void
|
||||
let delete: ([EngineMessage.Id]) -> Void
|
||||
let updateShowCallsTab: (Bool) -> Void
|
||||
let openGroupCall: (EnginePeer.Id) -> Void
|
||||
let createGroupCall: () -> Void
|
||||
|
||||
init(setMessageIdWithRevealedOptions: @escaping (EngineMessage.Id?, EngineMessage.Id?) -> Void, call: @escaping (EnginePeer.Id, Bool) -> 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, createGroupCall: @escaping () -> Void) {
|
||||
self.setMessageIdWithRevealedOptions = setMessageIdWithRevealedOptions
|
||||
self.call = call
|
||||
self.openInfo = openInfo
|
||||
@ -222,7 +222,7 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
private let emptyButtonIconNode: ASImageNode
|
||||
private let emptyButtonTextNode: ImmediateTextNode
|
||||
|
||||
private let call: (EnginePeer.Id, Bool) -> Void
|
||||
private let call: (EngineMessage) -> Void
|
||||
private let joinGroupCall: (EnginePeer.Id, EngineGroupCallDescription) -> Void
|
||||
private let createGroupCall: () -> Void
|
||||
private let openInfo: (EnginePeer.Id, [EngineMessage]) -> Void
|
||||
@ -234,7 +234,7 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
|
||||
private var previousContentOffset: ListViewVisibleContentOffset?
|
||||
|
||||
init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (EnginePeer.Id, Bool) -> 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, createGroupCall: @escaping () -> Void) {
|
||||
self.controller = controller
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
@ -333,8 +333,8 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, call: { [weak self] peerId, isVideo in
|
||||
self?.call(peerId, isVideo)
|
||||
}, call: { [weak self] message in
|
||||
self?.call(message)
|
||||
}, openInfo: { [weak self] peerId, messages in
|
||||
self?.openInfo(peerId, messages)
|
||||
}, delete: { [weak self] messageIds in
|
||||
|
@ -154,14 +154,32 @@ private func preparedTransition(from fromEntries: [InviteLinkInviteEntry], to to
|
||||
return InviteLinkInviteTransaction(deletions: deletions, insertions: insertions, updates: updates, isLoading: isLoading)
|
||||
}
|
||||
|
||||
private func getBackgroundColor(theme: PresentationTheme) -> UIColor {
|
||||
return theme.actionSheet.opaqueItemBackgroundColor
|
||||
}
|
||||
|
||||
public final class InviteLinkInviteController: ViewController {
|
||||
private var controllerNode: Node {
|
||||
return self.displayNode as! Node
|
||||
}
|
||||
|
||||
public enum Mode {
|
||||
public struct GroupCall {
|
||||
public let callId: Int64
|
||||
public let accessHash: Int64
|
||||
public let isRecentlyCreated: Bool
|
||||
public let canRevoke: Bool
|
||||
|
||||
public init(callId: Int64, accessHash: Int64, isRecentlyCreated: Bool, canRevoke: Bool) {
|
||||
self.callId = callId
|
||||
self.accessHash = accessHash
|
||||
self.isRecentlyCreated = isRecentlyCreated
|
||||
self.canRevoke = canRevoke
|
||||
}
|
||||
}
|
||||
|
||||
case groupOrChannel(peerId: EnginePeer.Id)
|
||||
case groupCall(link: String, isRecentlyCreated: Bool)
|
||||
case groupCall(GroupCall)
|
||||
}
|
||||
|
||||
public enum CompletionResult {
|
||||
@ -173,6 +191,7 @@ public final class InviteLinkInviteController: ViewController {
|
||||
|
||||
private let context: AccountContext
|
||||
private let mode: Mode
|
||||
private let initialInvite: ExportedInvitation?
|
||||
private weak var parentNavigationController: NavigationController?
|
||||
|
||||
private var presentationData: PresentationData
|
||||
@ -180,9 +199,10 @@ public final class InviteLinkInviteController: ViewController {
|
||||
|
||||
fileprivate let completed: ((CompletionResult?) -> Void)?
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: Mode, parentNavigationController: NavigationController?, completed: ((CompletionResult?) -> Void)? = nil) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: Mode, initialInvite: ExportedInvitation?, parentNavigationController: NavigationController?, completed: ((CompletionResult?) -> Void)? = nil) {
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
self.initialInvite = initialInvite
|
||||
self.parentNavigationController = parentNavigationController
|
||||
self.completed = completed
|
||||
|
||||
@ -215,7 +235,7 @@ public final class InviteLinkInviteController: ViewController {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = Node(context: self.context, presentationData: self.presentationData, mode: self.mode, controller: self)
|
||||
self.displayNode = Node(context: self.context, presentationData: self.presentationData, mode: self.mode, controller: self, initialInvite: self.initialInvite)
|
||||
}
|
||||
|
||||
private var didAppearOnce: Bool = false
|
||||
@ -296,7 +316,7 @@ public final class InviteLinkInviteController: ViewController {
|
||||
|
||||
private var revokeDisposable = MetaDisposable()
|
||||
|
||||
init(context: AccountContext, presentationData: PresentationData, mode: InviteLinkInviteController.Mode, controller: InviteLinkInviteController) {
|
||||
init(context: AccountContext, presentationData: PresentationData, mode: InviteLinkInviteController.Mode, controller: InviteLinkInviteController, initialInvite: ExportedInvitation?) {
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
|
||||
@ -319,7 +339,7 @@ public final class InviteLinkInviteController: ViewController {
|
||||
self.headerNode.clipsToBounds = false
|
||||
|
||||
self.headerBackgroundNode = ASDisplayNode()
|
||||
self.headerBackgroundNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
self.headerBackgroundNode.backgroundColor = getBackgroundColor(theme: self.presentationData.theme)
|
||||
self.headerBackgroundNode.cornerRadius = 16.0
|
||||
self.headerBackgroundNode.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
|
||||
@ -338,7 +358,7 @@ public final class InviteLinkInviteController: ViewController {
|
||||
|
||||
self.historyBackgroundContentNode = ASDisplayNode()
|
||||
self.historyBackgroundContentNode.isLayerBacked = true
|
||||
self.historyBackgroundContentNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
self.historyBackgroundContentNode.backgroundColor = getBackgroundColor(theme: self.presentationData.theme)
|
||||
|
||||
self.historyBackgroundNode.addSubnode(self.historyBackgroundContentNode)
|
||||
|
||||
@ -354,7 +374,7 @@ public final class InviteLinkInviteController: ViewController {
|
||||
self.backgroundColor = nil
|
||||
self.isOpaque = false
|
||||
|
||||
let mainInvitePromise = ValuePromise<ExportedInvitation?>(nil)
|
||||
let mainInvitePromise = ValuePromise<ExportedInvitation?>(initialInvite)
|
||||
|
||||
self.interaction = InviteLinkInviteInteraction(context: context, mainLinkContextAction: { [weak self] invite, node, gesture in
|
||||
guard let self else {
|
||||
@ -363,7 +383,6 @@ public final class InviteLinkInviteController: ViewController {
|
||||
guard let node = node as? ContextReferenceContentNode else {
|
||||
return
|
||||
}
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextCopy, icon: { theme in
|
||||
@ -381,7 +400,6 @@ public final class InviteLinkInviteController: ViewController {
|
||||
}
|
||||
})))
|
||||
|
||||
if case let .groupOrChannel(peerId) = self.mode {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] _, f in
|
||||
@ -391,7 +409,8 @@ public final class InviteLinkInviteController: ViewController {
|
||||
return
|
||||
}
|
||||
|
||||
if let invite = invite {
|
||||
if let invite {
|
||||
if case let .groupOrChannel(peerId) = self.mode {
|
||||
let _ = (context.account.postbox.loadedPeerWithId(peerId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let strongSelf = self else {
|
||||
@ -404,12 +423,17 @@ public final class InviteLinkInviteController: ViewController {
|
||||
isGroup = true
|
||||
}
|
||||
let updatedPresentationData = (strongSelf.presentationData, strongSelf.presentationDataPromise.get())
|
||||
let controller = QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, isGroup: isGroup))
|
||||
let controller = QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, type: isGroup ? .group : .channel))
|
||||
strongSelf.controller?.present(controller, in: .window(.root))
|
||||
})
|
||||
} else if case .groupCall = self.mode {
|
||||
let controller = QrCodeScreen(context: context, updatedPresentationData: (self.presentationData, self.presentationDataPromise.get()), subject: .invite(invite: invite, type: .channel))
|
||||
self.controller?.present(controller, in: .window(.root))
|
||||
}
|
||||
}
|
||||
})))
|
||||
|
||||
if case let .groupOrChannel(peerId) = self.mode {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextRevoke, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
|
||||
}, action: { [ weak self] _, f in
|
||||
@ -454,7 +478,45 @@ public final class InviteLinkInviteController: ViewController {
|
||||
self?.controller?.present(controller, in: .window(.root))
|
||||
})
|
||||
})))
|
||||
} else if case let .groupCall(groupCall) = self.mode, groupCall.canRevoke {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextRevoke, textColor: .destructive, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
|
||||
}, action: { [ weak self] _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let controller = ActionSheetController(presentationData: presentationData)
|
||||
let dismissAction: () -> Void = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
//TODO:localize
|
||||
controller.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetTextItem(title: "Revoke Link"),
|
||||
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: { [weak self] in
|
||||
dismissAction()
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
if let inviteLink = invite?.link {
|
||||
let _ = (context.engine.calls.revokeConferenceInviteLink(reference: .id(id: groupCall.callId, accessHash: groupCall.accessHash), link: inviteLink) |> deliverOnMainQueue).start(next: { result in
|
||||
mainInvitePromise.set(.link(link: result.listenerLink, title: nil, isPermanent: true, requestApproval: false, isRevoked: false, adminId: context.account.peerId, date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil, pricing: nil))
|
||||
})
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkRevoked(text: presentationData.strings.InviteLink_InviteLinkRevoked), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
|
||||
}
|
||||
})
|
||||
]),
|
||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||
])
|
||||
self.controller?.present(controller, in: .window(.root))
|
||||
})))
|
||||
}
|
||||
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
@ -597,15 +659,16 @@ public final class InviteLinkInviteController: ViewController {
|
||||
strongSelf.enqueueTransition(transition)
|
||||
}
|
||||
})
|
||||
case let .groupCall(link, isRecentlyCreated):
|
||||
//TODO:release
|
||||
let tempInfo: Signal<Void, NoError> = .single(Void()) |> delay(0.0, queue: .mainQueue())
|
||||
case let .groupCall(groupCall):
|
||||
// A workaround to skip the first run of the event cycle
|
||||
let delayOfZero = Signal<Void, NoError>.single(()) |> delay(0.0, queue: .mainQueue())
|
||||
|
||||
self.disposable = (combineLatest(queue: .mainQueue(),
|
||||
self.presentationDataPromise.get(),
|
||||
tempInfo
|
||||
mainInvitePromise.get(),
|
||||
delayOfZero
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData, _ in
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData, mainInvite, _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
@ -615,9 +678,9 @@ public final class InviteLinkInviteController: ViewController {
|
||||
let helpText: String = "Anyone on Telegram can join your call by following the link below."
|
||||
entries.append(.header(title: "Call Link", text: helpText))
|
||||
|
||||
let mainInvite: ExportedInvitation = .link(link: link, title: nil, isPermanent: true, requestApproval: false, isRevoked: false, adminId: self.context.account.peerId, date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil, pricing: nil)
|
||||
let mainInvite: ExportedInvitation = .link(link: mainInvite?.link ?? "", title: nil, isPermanent: true, requestApproval: false, isRevoked: false, adminId: self.context.account.peerId, date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil, pricing: nil)
|
||||
|
||||
entries.append(.mainLink(invitation: mainInvite, isCall: true, isRecentlyCreated: isRecentlyCreated))
|
||||
entries.append(.mainLink(invitation: mainInvite, isCall: true, isRecentlyCreated: groupCall.isRecentlyCreated))
|
||||
|
||||
let previousEntries = previousEntries.swap(entries)
|
||||
|
||||
@ -675,8 +738,8 @@ public final class InviteLinkInviteController: ViewController {
|
||||
self.presentationData = presentationData
|
||||
self.presentationDataPromise.set(.single(presentationData))
|
||||
|
||||
self.historyBackgroundContentNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
self.headerBackgroundNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
self.historyBackgroundContentNode.backgroundColor = getBackgroundColor(theme: self.presentationData.theme)
|
||||
self.headerBackgroundNode.backgroundColor = getBackgroundColor(theme: self.presentationData.theme)
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.InviteLink_InviteLink, font: Font.bold(17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor)
|
||||
|
||||
self.doneButtonIconNode.image = generateCloseButtonImage(backgroundColor: self.presentationData.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.05), foregroundColor: self.presentationData.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.4))!
|
||||
|
@ -530,7 +530,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
||||
} else {
|
||||
isGroup = true
|
||||
}
|
||||
presentControllerImpl?(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, isGroup: isGroup)), nil)
|
||||
presentControllerImpl?(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, type: isGroup ? .group : .channel)), nil)
|
||||
})
|
||||
})))
|
||||
|
||||
@ -719,7 +719,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
||||
isGroup = true
|
||||
}
|
||||
Queue.mainQueue().after(0.2) {
|
||||
presentControllerImpl?(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, isGroup: isGroup)), nil)
|
||||
presentControllerImpl?(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, type: isGroup ? .group : .channel)), nil)
|
||||
}
|
||||
})
|
||||
})))
|
||||
|
@ -755,7 +755,7 @@ public final class InviteLinkViewController: ViewController {
|
||||
isGroup = true
|
||||
}
|
||||
let updatedPresentationData = (strongSelf.presentationData, parentController.presentationDataPromise.get())
|
||||
strongSelf.controller?.present(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, isGroup: isGroup)), in: .window(.root))
|
||||
strongSelf.controller?.present(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, type: isGroup ? .group : .channel)), in: .window(.root))
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
@ -655,7 +655,7 @@ public func channelMembersController(context: AccountContext, updatedPresentatio
|
||||
}, inviteViaLink: {
|
||||
if let controller = getControllerImpl?() {
|
||||
dismissInputImpl?()
|
||||
presentControllerImpl?(InviteLinkInviteController(context: context, updatedPresentationData: updatedPresentationData, mode: .groupOrChannel(peerId: peerId), parentNavigationController: controller.navigationController as? NavigationController), nil)
|
||||
presentControllerImpl?(InviteLinkInviteController(context: context, updatedPresentationData: updatedPresentationData, mode: .groupOrChannel(peerId: peerId), initialInvite: nil, parentNavigationController: controller.navigationController as? NavigationController), nil)
|
||||
}
|
||||
}, updateHideMembers: { value in
|
||||
let _ = context.engine.peers.updateChannelMembersHidden(peerId: peerId, value: value).start()
|
||||
|
@ -1609,7 +1609,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
|
||||
} else {
|
||||
isGroup = true
|
||||
}
|
||||
presentControllerImpl?(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, isGroup: isGroup)), nil)
|
||||
presentControllerImpl?(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, type: isGroup ? .group : .channel)), nil)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -35,9 +35,15 @@ private func shareQrCode(context: AccountContext, link: String, ecl: String, vie
|
||||
}
|
||||
|
||||
public final class QrCodeScreen: ViewController {
|
||||
public enum SubjectType {
|
||||
case group
|
||||
case channel
|
||||
case groupCall
|
||||
}
|
||||
|
||||
public enum Subject {
|
||||
case peer(peer: EnginePeer)
|
||||
case invite(invite: ExportedInvitation, isGroup: Bool)
|
||||
case invite(invite: ExportedInvitation, type: SubjectType)
|
||||
case chatFolder(slug: String)
|
||||
|
||||
var link: String {
|
||||
@ -239,9 +245,17 @@ public final class QrCodeScreen: ViewController {
|
||||
let title: String
|
||||
let text: String
|
||||
switch subject {
|
||||
case let .invite(_, isGroup):
|
||||
case let .invite(_, type):
|
||||
title = self.presentationData.strings.InviteLink_QRCode_Title
|
||||
text = isGroup ? self.presentationData.strings.InviteLink_QRCode_Info : self.presentationData.strings.InviteLink_QRCode_InfoChannel
|
||||
switch type {
|
||||
case .group:
|
||||
text = self.presentationData.strings.InviteLink_QRCode_Info
|
||||
case .channel:
|
||||
text = self.presentationData.strings.InviteLink_QRCode_InfoChannel
|
||||
case .groupCall:
|
||||
//TODO:localize
|
||||
text = "Everyone on Telegram can scan this code to join your group call."
|
||||
}
|
||||
case .chatFolder:
|
||||
title = self.presentationData.strings.InviteLink_QRCodeFolder_Title
|
||||
text = self.presentationData.strings.InviteLink_QRCodeFolder_Text
|
||||
|
@ -161,7 +161,8 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
||||
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
|
||||
isVideoEnabled: state.isVideoEnabled,
|
||||
unmutedVideoLimit: state.unmutedVideoLimit,
|
||||
isStream: state.isStream
|
||||
isStream: state.isStream,
|
||||
isCreator: state.isCreator
|
||||
),
|
||||
topParticipants: topParticipants,
|
||||
participantCount: state.totalCount,
|
||||
@ -888,6 +889,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
private let getDeviceAccessData: () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void)
|
||||
|
||||
private(set) var initialCall: (description: EngineGroupCallDescription, reference: InternalGroupCallReference)?
|
||||
public var currentReference: InternalGroupCallReference?
|
||||
public let internalId: CallSessionInternalId
|
||||
public let peerId: EnginePeer.Id?
|
||||
private let isChannel: Bool
|
||||
@ -1208,6 +1210,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
self.getDeviceAccessData = getDeviceAccessData
|
||||
|
||||
self.initialCall = initialCall
|
||||
self.currentReference = initialCall?.reference
|
||||
self.callId = initialCall?.description.id
|
||||
|
||||
self.internalId = internalId
|
||||
@ -1963,7 +1966,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
defaultParticipantsAreMuted: callInfo.defaultParticipantsAreMuted ?? state.defaultParticipantsAreMuted,
|
||||
isVideoEnabled: callInfo.isVideoEnabled,
|
||||
unmutedVideoLimit: callInfo.unmutedVideoLimit,
|
||||
isStream: callInfo.isStream
|
||||
isStream: callInfo.isStream,
|
||||
isCreator: callInfo.isCreator
|
||||
)), audioSessionControl: self.audioSessionControl)
|
||||
} else {
|
||||
self.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo(
|
||||
@ -1979,7 +1983,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
|
||||
isVideoEnabled: state.isVideoEnabled,
|
||||
unmutedVideoLimit: state.unmutedVideoLimit,
|
||||
isStream: callInfo.isStream
|
||||
isStream: callInfo.isStream,
|
||||
isCreator: callInfo.isCreator
|
||||
))))
|
||||
|
||||
self.summaryParticipantsState.set(.single(SummaryParticipantsState(
|
||||
@ -2301,6 +2306,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.currentReference = .id(id: joinCallResult.callInfo.id, accessHash: joinCallResult.callInfo.accessHash)
|
||||
|
||||
let clientParams = joinCallResult.jsonParams
|
||||
if let data = clientParams.data(using: .utf8), let dict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] {
|
||||
if let video = dict["video"] as? [String: Any] {
|
||||
@ -2910,7 +2918,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
|
||||
isVideoEnabled: state.isVideoEnabled,
|
||||
unmutedVideoLimit: state.unmutedVideoLimit,
|
||||
isStream: callInfo.isStream
|
||||
isStream: callInfo.isStream,
|
||||
isCreator: callInfo.isCreator
|
||||
))))
|
||||
|
||||
self.summaryParticipantsState.set(.single(SummaryParticipantsState(
|
||||
|
@ -25,6 +25,7 @@ import LegacyComponents
|
||||
import TooltipUI
|
||||
import BlurredBackgroundComponent
|
||||
import CallsEmoji
|
||||
import InviteLinksUI
|
||||
|
||||
extension VideoChatCall {
|
||||
var myAudioLevelAndSpeaking: Signal<(Float, Bool), NoError> {
|
||||
@ -653,6 +654,51 @@ final class VideoChatScreenComponent: Component {
|
||||
return
|
||||
}
|
||||
|
||||
if groupCall.isConference {
|
||||
guard let navigationController = self.environment?.controller()?.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
guard let currentReference = groupCall.currentReference, case let .id(callId, accessHash) = currentReference else {
|
||||
return
|
||||
}
|
||||
guard let callState = self.callState else {
|
||||
return
|
||||
}
|
||||
var presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with { $0 }
|
||||
presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||
let controller = InviteLinkInviteController(
|
||||
context: groupCall.accountContext,
|
||||
updatedPresentationData: (initial: presentationData, signal: .single(presentationData)),
|
||||
mode: .groupCall(InviteLinkInviteController.Mode.GroupCall(
|
||||
callId: callId,
|
||||
accessHash: accessHash,
|
||||
isRecentlyCreated: false,
|
||||
canRevoke: callState.canManageCall
|
||||
)),
|
||||
initialInvite: .link(link: inviteLinks.listenerLink, title: nil, isPermanent: true, requestApproval: false, isRevoked: false, adminId: groupCall.accountContext.account.peerId, date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil, pricing: nil),
|
||||
parentNavigationController: navigationController,
|
||||
completed: { [weak self] result in
|
||||
guard let self, case let .group(groupCall) = self.currentCall else {
|
||||
return
|
||||
}
|
||||
if let result {
|
||||
switch result {
|
||||
case .linkCopied:
|
||||
//TODO:localize
|
||||
let presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with { $0 }
|
||||
self.environment?.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: "Call link copied.", customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||
return false
|
||||
}), in: .current)
|
||||
case .openCall:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
self.environment?.controller()?.present(controller, in: .window(.root), with: nil)
|
||||
return
|
||||
}
|
||||
|
||||
let formatSendTitle: (String) -> String = { string in
|
||||
var string = string
|
||||
if string.contains("[") && string.contains("]") {
|
||||
|
@ -16,31 +16,6 @@ extension VideoChatScreenComponent.View {
|
||||
return
|
||||
}
|
||||
|
||||
/*if groupCall.accountContext.sharedContext.immediateExperimentalUISettings.conferenceDebug {
|
||||
guard let navigationController = self.environment?.controller()?.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
var presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with { $0 }
|
||||
presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||
let controller = InviteLinkInviteController(context: groupCall.accountContext, updatedPresentationData: (initial: presentationData, signal: .single(presentationData)), mode: .groupCall(link: "https://t.me/call/+abbfbffll123", isRecentlyCreated: false), parentNavigationController: navigationController, completed: { [weak self] result in
|
||||
guard let self, case let .group(groupCall) = self.currentCall else {
|
||||
return
|
||||
}
|
||||
if let result {
|
||||
switch result {
|
||||
case .linkCopied:
|
||||
//TODO:localize
|
||||
let presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with { $0 }
|
||||
self.environment?.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: "Call link copied.", customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||
return false
|
||||
}), in: .current)
|
||||
}
|
||||
}
|
||||
})
|
||||
self.environment?.controller()?.present(controller, in: .window(.root), with: nil)
|
||||
return
|
||||
}*/
|
||||
|
||||
if groupCall.isConference {
|
||||
var disablePeerIds: [EnginePeer.Id] = []
|
||||
disablePeerIds.append(groupCall.accountContext.account.peerId)
|
||||
|
@ -98,6 +98,11 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
|
||||
if incoming, let discardReason = discardReason, case .missed = discardReason {
|
||||
globalTags.insert(.MissedCalls)
|
||||
}
|
||||
case let .conferenceCall(conferenceCall):
|
||||
globalTags.insert(.Calls)
|
||||
if incoming, conferenceCall.flags.contains(.isMissed) {
|
||||
globalTags.insert(.MissedCalls)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -118,9 +123,6 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
|
||||
}
|
||||
}
|
||||
|
||||
if !incoming {
|
||||
assert(true)
|
||||
}
|
||||
return (tags, globalTags)
|
||||
}
|
||||
|
||||
|
@ -96,6 +96,7 @@ public struct GroupCallInfo: Equatable {
|
||||
public var isVideoEnabled: Bool
|
||||
public var unmutedVideoLimit: Int
|
||||
public var isStream: Bool
|
||||
public var isCreator: Bool
|
||||
|
||||
public init(
|
||||
id: Int64,
|
||||
@ -110,7 +111,8 @@ public struct GroupCallInfo: Equatable {
|
||||
defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?,
|
||||
isVideoEnabled: Bool,
|
||||
unmutedVideoLimit: Int,
|
||||
isStream: Bool
|
||||
isStream: Bool,
|
||||
isCreator: Bool
|
||||
) {
|
||||
self.id = id
|
||||
self.accessHash = accessHash
|
||||
@ -125,6 +127,7 @@ public struct GroupCallInfo: Equatable {
|
||||
self.isVideoEnabled = isVideoEnabled
|
||||
self.unmutedVideoLimit = unmutedVideoLimit
|
||||
self.isStream = isStream
|
||||
self.isCreator = isCreator
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,7 +153,8 @@ extension GroupCallInfo {
|
||||
defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: (flags & (1 << 1)) != 0, canChange: (flags & (1 << 2)) != 0),
|
||||
isVideoEnabled: (flags & (1 << 9)) != 0,
|
||||
unmutedVideoLimit: Int(unmutedVideoLimit),
|
||||
isStream: (flags & (1 << 12)) != 0
|
||||
isStream: (flags & (1 << 12)) != 0,
|
||||
isCreator: (flags & (1 << 15)) != 0
|
||||
)
|
||||
case .groupCallDiscarded:
|
||||
return nil
|
||||
@ -454,17 +458,17 @@ public enum GetGroupCallParticipantsError {
|
||||
func _internal_getGroupCallParticipants(account: Account, reference: InternalGroupCallReference, offset: String, ssrcs: [UInt32], limit: Int32, sortAscending: Bool?) -> Signal<GroupCallParticipantsContext.State, GetGroupCallParticipantsError> {
|
||||
let accountPeerId = account.peerId
|
||||
|
||||
let sortAscendingValue: Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool), GetGroupCallParticipantsError>
|
||||
let sortAscendingValue: Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool, Bool), GetGroupCallParticipantsError>
|
||||
|
||||
sortAscendingValue = _internal_getCurrentGroupCall(account: account, reference: reference)
|
||||
|> mapError { _ -> GetGroupCallParticipantsError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool), GetGroupCallParticipantsError> in
|
||||
|> mapToSignal { result -> Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool, Bool), GetGroupCallParticipantsError> in
|
||||
guard let result = result else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
return .single((sortAscending ?? result.info.sortAscending, result.info.scheduleTimestamp, result.info.subscribedToScheduled, result.info.defaultParticipantsAreMuted, result.info.isVideoEnabled, result.info.unmutedVideoLimit, result.info.isStream))
|
||||
return .single((sortAscending ?? result.info.sortAscending, result.info.scheduleTimestamp, result.info.subscribedToScheduled, result.info.defaultParticipantsAreMuted, result.info.isVideoEnabled, result.info.unmutedVideoLimit, result.info.isStream, result.info.isCreator))
|
||||
}
|
||||
|
||||
return combineLatest(
|
||||
@ -481,7 +485,7 @@ func _internal_getGroupCallParticipants(account: Account, reference: InternalGro
|
||||
let version: Int32
|
||||
let nextParticipantsFetchOffset: String?
|
||||
|
||||
let (sortAscendingValue, scheduleTimestamp, subscribedToScheduled, defaultParticipantsAreMuted, isVideoEnabled, unmutedVideoLimit, isStream) = sortAscendingAndScheduleTimestamp
|
||||
let (sortAscendingValue, scheduleTimestamp, subscribedToScheduled, defaultParticipantsAreMuted, isVideoEnabled, unmutedVideoLimit, isStream, isCreator) = sortAscendingAndScheduleTimestamp
|
||||
|
||||
switch result {
|
||||
case let .groupParticipants(count, participants, nextOffset, chats, users, apiVersion):
|
||||
@ -506,7 +510,7 @@ func _internal_getGroupCallParticipants(account: Account, reference: InternalGro
|
||||
participants: parsedParticipants,
|
||||
nextParticipantsFetchOffset: nextParticipantsFetchOffset,
|
||||
adminIds: Set(),
|
||||
isCreator: false,
|
||||
isCreator: isCreator,
|
||||
defaultParticipantsAreMuted: defaultParticipantsAreMuted ?? GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: false, canChange: false),
|
||||
sortAscending: sortAscendingValue,
|
||||
recordingStartTimestamp: nil,
|
||||
@ -2959,6 +2963,29 @@ func _internal_createConferenceCall(postbox: Postbox, network: Network, accountP
|
||||
}
|
||||
}
|
||||
|
||||
public enum RevokeConferenceInviteLinkError {
|
||||
case generic
|
||||
}
|
||||
|
||||
func _internal_revokeConferenceInviteLink(account: Account, reference: InternalGroupCallReference, link: String) -> Signal<GroupCallInviteLinks, RevokeConferenceInviteLinkError> {
|
||||
return account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 1, call: reference.apiInputGroupCall, joinMuted: .boolFalse))
|
||||
|> mapError { _ -> RevokeConferenceInviteLinkError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<GroupCallInviteLinks, RevokeConferenceInviteLinkError> in
|
||||
account.stateManager.addUpdates(result)
|
||||
|
||||
return _internal_groupCallInviteLinks(account: account, reference: reference, isConference: true)
|
||||
|> castError(RevokeConferenceInviteLinkError.self)
|
||||
|> mapToSignal { result -> Signal<GroupCallInviteLinks, RevokeConferenceInviteLinkError> in
|
||||
guard let result = result else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum ConfirmAddConferenceParticipantError {
|
||||
case generic
|
||||
}
|
||||
|
@ -97,6 +97,10 @@ public extension TelegramEngine {
|
||||
return _internal_createConferenceCall(postbox: self.account.postbox, network: self.account.network, accountPeerId: self.account.peerId)
|
||||
}
|
||||
|
||||
public func revokeConferenceInviteLink(reference: InternalGroupCallReference, link: String) -> Signal<GroupCallInviteLinks, RevokeConferenceInviteLinkError> {
|
||||
return _internal_revokeConferenceInviteLink(account: self.account, reference: reference, link: link)
|
||||
}
|
||||
|
||||
public func pollConferenceCallBlockchain(reference: InternalGroupCallReference, subChainId: Int, offset: Int, limit: Int) -> Signal<(blocks: [Data], nextOffset: Int)?, NoError> {
|
||||
return _internal_pollConferenceCallBlockchain(network: self.account.network, reference: reference, subChainId: subChainId, offset: offset, limit: limit)
|
||||
}
|
||||
|
@ -14124,7 +14124,7 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa
|
||||
|
||||
createInviteLinkImpl = { [weak contactsController] in
|
||||
contactsController?.view.window?.endEditing(true)
|
||||
contactsController?.present(InviteLinkInviteController(context: context, updatedPresentationData: updatedPresentationData, mode: .groupOrChannel(peerId: groupPeer.id), parentNavigationController: contactsController?.navigationController as? NavigationController), in: .window(.root))
|
||||
contactsController?.present(InviteLinkInviteController(context: context, updatedPresentationData: updatedPresentationData, mode: .groupOrChannel(peerId: groupPeer.id), initialInvite: nil, parentNavigationController: contactsController?.navigationController as? NavigationController), in: .window(.root))
|
||||
}
|
||||
|
||||
parentController?.push(contactsController)
|
||||
|
@ -402,6 +402,15 @@ func openResolvedUrlImpl(
|
||||
members: resolvedCallLink.members,
|
||||
totalMemberCount: resolvedCallLink.totalMemberCount
|
||||
))))
|
||||
}, error: { _ in
|
||||
var elevatedLayout = true
|
||||
if case .chat = urlContext {
|
||||
elevatedLayout = false
|
||||
}
|
||||
//TODO:localize
|
||||
present(UndoOverlayController(presentationData: presentationData, content: .linkRevoked(text: "This link is no longer active"), elevatedLayout: elevatedLayout, animateInAsReplacement: false, action: { _ in
|
||||
return true
|
||||
}), nil)
|
||||
})
|
||||
case let .localization(identifier):
|
||||
dismissInput()
|
||||
@ -788,6 +797,7 @@ func openResolvedUrlImpl(
|
||||
}
|
||||
if let currentState = starsContext.currentState, currentState.balance >= StarsAmount(value: amount, nanos: 0) {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
//TODO:localize
|
||||
let controller = UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .universal(
|
||||
|
Loading…
x
Reference in New Issue
Block a user