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
66c244f540
commit
aaf52d4282
@ -166,11 +166,11 @@ private final class EmbeddedBroadcastUploadImpl: BroadcastUploadImpl {
|
|||||||
preferX264: false,
|
preferX264: false,
|
||||||
logPath: "",
|
logPath: "",
|
||||||
onMutedSpeechActivityDetected: { _ in },
|
onMutedSpeechActivityDetected: { _ in },
|
||||||
encryptionKey: nil,
|
|
||||||
isConference: false,
|
isConference: false,
|
||||||
audioIsActiveByDefault: true,
|
audioIsActiveByDefault: true,
|
||||||
isStream: false,
|
isStream: false,
|
||||||
sharedAudioDevice: nil
|
sharedAudioDevice: nil,
|
||||||
|
encryptionContext: nil
|
||||||
)
|
)
|
||||||
self.callContext = callContext
|
self.callContext = callContext
|
||||||
self.joinPayloadDisposable = (callContext.joinPayload
|
self.joinPayloadDisposable = (callContext.joinPayload
|
||||||
|
@ -23,7 +23,6 @@ swift_library(
|
|||||||
"//submodules/PersistentStringHash:PersistentStringHash",
|
"//submodules/PersistentStringHash:PersistentStringHash",
|
||||||
"//submodules/Utils/RangeSet",
|
"//submodules/Utils/RangeSet",
|
||||||
"//submodules/Media/ConvertOpusToAAC",
|
"//submodules/Media/ConvertOpusToAAC",
|
||||||
|
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -926,7 +926,13 @@ private final class NotificationServiceHandler {
|
|||||||
var localContactId: String?
|
var localContactId: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ConferenceCallData {
|
||||||
|
var id: Int64
|
||||||
|
var updates: String
|
||||||
|
}
|
||||||
|
|
||||||
var callData: CallData?
|
var callData: CallData?
|
||||||
|
var conferenceCallData: ConferenceCallData?
|
||||||
|
|
||||||
if let messageIdString = payloadJson["msg_id"] as? String {
|
if let messageIdString = payloadJson["msg_id"] as? String {
|
||||||
messageId = Int32(messageIdString)
|
messageId = Int32(messageIdString)
|
||||||
@ -953,7 +959,24 @@ private final class NotificationServiceHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let callIdString = payloadJson["call_id"] as? String, let callAccessHashString = payloadJson["call_ah"] as? String, let peerId = peerId, let updates = payloadJson["updates"] as? String {
|
if let locKey = payloadJson["loc-key"] as? String, (locKey == "CONF_CALL_REQUEST" || locKey == "CONF_CALL_MISSED"), let callIdString = payloadJson["call_id"] as? String {
|
||||||
|
if let callId = Int64(callIdString) {
|
||||||
|
|
||||||
|
if let updates = payloadJson["updates"] as? String {
|
||||||
|
var updateString = updates
|
||||||
|
updateString = updateString.replacingOccurrences(of: "-", with: "+")
|
||||||
|
updateString = updateString.replacingOccurrences(of: "_", with: "/")
|
||||||
|
while updateString.count % 4 != 0 {
|
||||||
|
updateString.append("=")
|
||||||
|
}
|
||||||
|
if let updateData = Data(base64Encoded: updateString) {
|
||||||
|
if let callUpdate = AccountStateManager.extractIncomingCallUpdate(data: updateData) {
|
||||||
|
let _ = callUpdate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let callIdString = payloadJson["call_id"] as? String, let callAccessHashString = payloadJson["call_ah"] as? String, let peerId = peerId, let updates = payloadJson["updates"] as? String {
|
||||||
if let callId = Int64(callIdString), let callAccessHash = Int64(callAccessHashString) {
|
if let callId = Int64(callIdString), let callAccessHash = Int64(callAccessHashString) {
|
||||||
var peer: EnginePeer?
|
var peer: EnginePeer?
|
||||||
|
|
||||||
|
@ -955,11 +955,17 @@ public enum JoinSubjectScreenMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class GroupCall {
|
public final class GroupCall {
|
||||||
|
public let id: Int64
|
||||||
|
public let accessHash: Int64
|
||||||
|
public let slug: String
|
||||||
public let inviter: EnginePeer?
|
public let inviter: EnginePeer?
|
||||||
public let members: [EnginePeer]
|
public let members: [EnginePeer]
|
||||||
public let totalMemberCount: Int
|
public let totalMemberCount: Int
|
||||||
|
|
||||||
public init(inviter: EnginePeer?, members: [EnginePeer], totalMemberCount: Int) {
|
public init(id: Int64, accessHash: Int64, slug: String, inviter: EnginePeer?, members: [EnginePeer], totalMemberCount: Int) {
|
||||||
|
self.id = id
|
||||||
|
self.accessHash = accessHash
|
||||||
|
self.slug = slug
|
||||||
self.inviter = inviter
|
self.inviter = inviter
|
||||||
self.members = members
|
self.members = members
|
||||||
self.totalMemberCount = totalMemberCount
|
self.totalMemberCount = totalMemberCount
|
||||||
|
@ -38,6 +38,7 @@ public final class OpenChatMessageParams {
|
|||||||
public let openUrl: (String) -> Void
|
public let openUrl: (String) -> Void
|
||||||
public let openPeer: (Peer, ChatControllerInteractionNavigateToPeer) -> Void
|
public let openPeer: (Peer, ChatControllerInteractionNavigateToPeer) -> Void
|
||||||
public let callPeer: (PeerId, Bool) -> Void
|
public let callPeer: (PeerId, Bool) -> Void
|
||||||
|
public let openConferenceCall: (Message) -> Void
|
||||||
public let enqueueMessage: (EnqueueMessage) -> Void
|
public let enqueueMessage: (EnqueueMessage) -> Void
|
||||||
public let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?
|
public let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?
|
||||||
public let sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?
|
public let sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?
|
||||||
@ -70,6 +71,7 @@ public final class OpenChatMessageParams {
|
|||||||
openUrl: @escaping (String) -> Void,
|
openUrl: @escaping (String) -> Void,
|
||||||
openPeer: @escaping (Peer, ChatControllerInteractionNavigateToPeer) -> Void,
|
openPeer: @escaping (Peer, ChatControllerInteractionNavigateToPeer) -> Void,
|
||||||
callPeer: @escaping (PeerId, Bool) -> Void,
|
callPeer: @escaping (PeerId, Bool) -> Void,
|
||||||
|
openConferenceCall: @escaping (Message) -> Void,
|
||||||
enqueueMessage: @escaping (EnqueueMessage) -> Void,
|
enqueueMessage: @escaping (EnqueueMessage) -> Void,
|
||||||
sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?,
|
sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?,
|
||||||
sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?,
|
sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?,
|
||||||
@ -100,6 +102,7 @@ public final class OpenChatMessageParams {
|
|||||||
self.openUrl = openUrl
|
self.openUrl = openUrl
|
||||||
self.openPeer = openPeer
|
self.openPeer = openPeer
|
||||||
self.callPeer = callPeer
|
self.callPeer = callPeer
|
||||||
|
self.openConferenceCall = openConferenceCall
|
||||||
self.enqueueMessage = enqueueMessage
|
self.enqueueMessage = enqueueMessage
|
||||||
self.sendSticker = sendSticker
|
self.sendSticker = sendSticker
|
||||||
self.sendEmoji = sendEmoji
|
self.sendEmoji = sendEmoji
|
||||||
|
@ -422,6 +422,7 @@ public protocol PresentationGroupCall: AnyObject {
|
|||||||
var accountContext: AccountContext { get }
|
var accountContext: AccountContext { get }
|
||||||
var internalId: CallSessionInternalId { get }
|
var internalId: CallSessionInternalId { get }
|
||||||
var peerId: EnginePeer.Id? { get }
|
var peerId: EnginePeer.Id? { get }
|
||||||
|
var callId: Int64? { get }
|
||||||
|
|
||||||
var hasVideo: Bool { get }
|
var hasVideo: Bool { get }
|
||||||
var hasScreencast: Bool { get }
|
var hasScreencast: Bool { get }
|
||||||
@ -431,7 +432,6 @@ public protocol PresentationGroupCall: AnyObject {
|
|||||||
var isStream: Bool { get }
|
var isStream: Bool { get }
|
||||||
var isConference: Bool { get }
|
var isConference: Bool { get }
|
||||||
var conferenceSource: CallSessionInternalId? { get }
|
var conferenceSource: CallSessionInternalId? { get }
|
||||||
var encryptionKeyValue: Data? { get }
|
|
||||||
|
|
||||||
var audioOutputState: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> { get }
|
var audioOutputState: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> { get }
|
||||||
|
|
||||||
@ -447,6 +447,8 @@ public protocol PresentationGroupCall: AnyObject {
|
|||||||
var isMuted: Signal<Bool, NoError> { get }
|
var isMuted: Signal<Bool, NoError> { get }
|
||||||
var isNoiseSuppressionEnabled: Signal<Bool, NoError> { get }
|
var isNoiseSuppressionEnabled: Signal<Bool, NoError> { get }
|
||||||
|
|
||||||
|
var e2eEncryptionKeyHash: Signal<Data?, NoError> { get }
|
||||||
|
|
||||||
var memberEvents: Signal<PresentationGroupCallMemberEvent, NoError> { get }
|
var memberEvents: Signal<PresentationGroupCallMemberEvent, NoError> { get }
|
||||||
var reconnectedAsEvents: Signal<EnginePeer, NoError> { get }
|
var reconnectedAsEvents: Signal<EnginePeer, NoError> { get }
|
||||||
|
|
||||||
@ -548,6 +550,10 @@ public enum PresentationCurrentCall: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum JoinConferenceCallMode {
|
||||||
|
case joining
|
||||||
|
}
|
||||||
|
|
||||||
public protocol PresentationCallManager: AnyObject {
|
public protocol PresentationCallManager: AnyObject {
|
||||||
var currentCallSignal: Signal<PresentationCall?, NoError> { get }
|
var currentCallSignal: Signal<PresentationCall?, NoError> { get }
|
||||||
var currentGroupCallSignal: Signal<VideoChatCall?, NoError> { get }
|
var currentGroupCallSignal: Signal<VideoChatCall?, NoError> { get }
|
||||||
@ -557,4 +563,11 @@ public protocol PresentationCallManager: AnyObject {
|
|||||||
func requestCall(context: AccountContext, peerId: EnginePeer.Id, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult
|
func requestCall(context: AccountContext, peerId: EnginePeer.Id, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult
|
||||||
func joinGroupCall(context: AccountContext, peerId: EnginePeer.Id, invite: String?, requestJoinAsPeerId: ((@escaping (EnginePeer.Id?) -> Void) -> Void)?, initialCall: EngineGroupCallDescription, endCurrentIfAny: Bool) -> JoinGroupCallManagerResult
|
func joinGroupCall(context: AccountContext, peerId: EnginePeer.Id, invite: String?, requestJoinAsPeerId: ((@escaping (EnginePeer.Id?) -> Void) -> Void)?, initialCall: EngineGroupCallDescription, endCurrentIfAny: Bool) -> JoinGroupCallManagerResult
|
||||||
func scheduleGroupCall(context: AccountContext, peerId: EnginePeer.Id, endCurrentIfAny: Bool, parentController: ViewController) -> RequestScheduleGroupCallResult
|
func scheduleGroupCall(context: AccountContext, peerId: EnginePeer.Id, endCurrentIfAny: Bool, parentController: ViewController) -> RequestScheduleGroupCallResult
|
||||||
|
|
||||||
|
func joinConferenceCall(
|
||||||
|
accountContext: AccountContext,
|
||||||
|
initialCall: EngineGroupCallDescription,
|
||||||
|
reference: InternalGroupCallReference,
|
||||||
|
mode: JoinConferenceCallMode
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,7 @@ public final class BrowserBookmarksScreen: ViewController {
|
|||||||
return nil
|
return nil
|
||||||
}, presentGlobalOverlayController: { _, _ in
|
}, presentGlobalOverlayController: { _, _ in
|
||||||
}, callPeer: { _, _ in
|
}, callPeer: { _, _ in
|
||||||
|
}, openConferenceCall: { _ in
|
||||||
}, longTap: { _, _ in
|
}, longTap: { _, _ in
|
||||||
}, openCheckoutOrReceipt: { _, _ in
|
}, openCheckoutOrReceipt: { _, _ in
|
||||||
}, openSearch: {
|
}, openSearch: {
|
||||||
|
@ -33,6 +33,7 @@ swift_library(
|
|||||||
"//submodules/ItemListPeerActionItem",
|
"//submodules/ItemListPeerActionItem",
|
||||||
"//submodules/InviteLinksUI",
|
"//submodules/InviteLinksUI",
|
||||||
"//submodules/UndoUI",
|
"//submodules/UndoUI",
|
||||||
|
"//submodules/TelegramCallsUI",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -15,6 +15,7 @@ import ContextUI
|
|||||||
import TelegramBaseController
|
import TelegramBaseController
|
||||||
import InviteLinksUI
|
import InviteLinksUI
|
||||||
import UndoUI
|
import UndoUI
|
||||||
|
import TelegramCallsUI
|
||||||
|
|
||||||
public enum CallListControllerMode {
|
public enum CallListControllerMode {
|
||||||
case tab
|
case tab
|
||||||
@ -206,7 +207,32 @@ public final class CallListController: TelegramBaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func createGroupCall() {
|
private func createGroupCall() {
|
||||||
let controller = InviteLinkInviteController(context: self.context, updatedPresentationData: nil, mode: .groupCall(link: "https://t.me/call/+abbfbffll123", isRecentlyCreated: true), parentNavigationController: self.navigationController as? NavigationController, completed: { [weak self] result in
|
let _ = (self.context.engine.calls.createConferenceCall()
|
||||||
|
|> deliverOnMainQueue).startStandalone(next: { [weak self] call in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let openCall: () -> Void = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.context.sharedContext.callManager?.joinConferenceCall(
|
||||||
|
accountContext: self.context,
|
||||||
|
initialCall: EngineGroupCallDescription(
|
||||||
|
id: call.callInfo.id,
|
||||||
|
accessHash: call.callInfo.accessHash,
|
||||||
|
title: call.callInfo.title,
|
||||||
|
scheduleTimestamp: nil,
|
||||||
|
subscribedToScheduled: false,
|
||||||
|
isStream: false
|
||||||
|
),
|
||||||
|
reference: .id(id: call.callInfo.id, accessHash: call.callInfo.accessHash),
|
||||||
|
mode: .joining
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -217,14 +243,17 @@ public final class CallListController: TelegramBaseController {
|
|||||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
self.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: "View Call", timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
self.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: "View Call", timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||||
if case .undo = action {
|
if case .undo = action {
|
||||||
//TODO:release
|
openCall()
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}), in: .window(.root))
|
}), in: .window(.root))
|
||||||
|
case .openCall:
|
||||||
|
openCall()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
self.present(controller, in: .window(.root), with: nil)
|
self.present(controller, in: .window(.root), with: nil)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
|
@ -117,13 +117,6 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
|||||||
switch search {
|
switch search {
|
||||||
case .recentPeers:
|
case .recentPeers:
|
||||||
break
|
break
|
||||||
/*items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_RemoveFromRecents, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
|
|
||||||
let _ = (context.engine.peers.removeRecentPeer(peerId: peerId)
|
|
||||||
|> deliverOnMainQueue).startStandalone(completed: {
|
|
||||||
f(.default)
|
|
||||||
})
|
|
||||||
})))
|
|
||||||
items.append(.separator)*/
|
|
||||||
case .recentSearch:
|
case .recentSearch:
|
||||||
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_RemoveFromRecents, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
|
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_RemoveFromRecents, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
|
||||||
let _ = (context.engine.peers.removeRecentlySearchedPeer(peerId: peerId)
|
let _ = (context.engine.peers.removeRecentlySearchedPeer(peerId: peerId)
|
||||||
@ -539,9 +532,30 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch
|
|||||||
} else if case let .search(search) = source {
|
} else if case let .search(search) = source {
|
||||||
switch search {
|
switch search {
|
||||||
case .recentPeers, .search:
|
case .recentPeers, .search:
|
||||||
|
var addedSeparator = false
|
||||||
|
if case let .recentPeers(isTopPeer) = search {
|
||||||
|
if isTopPeer {
|
||||||
|
if !items.isEmpty {
|
||||||
|
if !addedSeparator {
|
||||||
|
items.append(.separator)
|
||||||
|
addedSeparator = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_RemoveFromRecents, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
|
||||||
|
let _ = (context.engine.peers.removeRecentPeer(peerId: peerId)
|
||||||
|
|> deliverOnMainQueue).startStandalone(completed: {
|
||||||
|
f(.default)
|
||||||
|
})
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if peerGroup != nil {
|
if peerGroup != nil {
|
||||||
if !items.isEmpty {
|
if !items.isEmpty {
|
||||||
|
if !addedSeparator {
|
||||||
items.append(.separator)
|
items.append(.separator)
|
||||||
|
addedSeparator = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
|
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
|
||||||
if let chatListController = chatListController {
|
if let chatListController = chatListController {
|
||||||
|
@ -133,7 +133,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
|
|||||||
peerSelected(peer, nil, false, .generic)
|
peerSelected(peer, nil, false, .generic)
|
||||||
}, peerContextAction: { peer, node, gesture, location in
|
}, peerContextAction: { peer, node, gesture, location in
|
||||||
if let peerContextAction = peerContextAction {
|
if let peerContextAction = peerContextAction {
|
||||||
peerContextAction(peer, .recentPeers, node, gesture, location)
|
peerContextAction(peer, .recentPeers(isTopPeer: true), node, gesture, location)
|
||||||
} else {
|
} else {
|
||||||
gesture?.cancel()
|
gesture?.cancel()
|
||||||
}
|
}
|
||||||
@ -1409,7 +1409,7 @@ private struct ChatListSearchMessagesContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum ChatListSearchContextActionSource {
|
public enum ChatListSearchContextActionSource {
|
||||||
case recentPeers
|
case recentPeers(isTopPeer: Bool)
|
||||||
case recentSearch
|
case recentSearch
|
||||||
case recentApps
|
case recentApps
|
||||||
case popularApps
|
case popularApps
|
||||||
@ -3212,6 +3212,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
interaction.openUrl(url)
|
interaction.openUrl(url)
|
||||||
}, openPeer: { _, _ in
|
}, openPeer: { _, _ in
|
||||||
}, callPeer: { _, _ in
|
}, callPeer: { _, _ in
|
||||||
|
}, openConferenceCall: { _ in
|
||||||
}, enqueueMessage: { _ in
|
}, enqueueMessage: { _ in
|
||||||
}, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, gallerySource: .custom(messages: foundMessages |> map { message, a, b in
|
}, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, gallerySource: .custom(messages: foundMessages |> map { message, a, b in
|
||||||
return (message.map { $0._asMessage() }, a, b)
|
return (message.map { $0._asMessage() }, a, b)
|
||||||
@ -3406,6 +3407,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
interaction.openUrl(url)
|
interaction.openUrl(url)
|
||||||
}, openPeer: { peer, navigation in
|
}, openPeer: { peer, navigation in
|
||||||
}, callPeer: { _, _ in
|
}, callPeer: { _, _ in
|
||||||
|
}, openConferenceCall: { _ in
|
||||||
}, enqueueMessage: { _ in
|
}, enqueueMessage: { _ in
|
||||||
}, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: playlistLocation, gallerySource: gallerySource))
|
}, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: playlistLocation, gallerySource: gallerySource))
|
||||||
}, openMessageContextMenu: { [weak self] message, _, node, rect, gesture in
|
}, openMessageContextMenu: { [weak self] message, _, node, rect, gesture in
|
||||||
|
@ -298,6 +298,9 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
|
|||||||
messageText = invoice.title
|
messageText = invoice.title
|
||||||
case let action as TelegramMediaAction:
|
case let action as TelegramMediaAction:
|
||||||
switch action.action {
|
switch action.action {
|
||||||
|
case .conferenceCall:
|
||||||
|
//TODO:localize
|
||||||
|
messageText = "Group call"
|
||||||
case let .phoneCall(_, discardReason, _, isVideo):
|
case let .phoneCall(_, discardReason, _, isVideo):
|
||||||
hideAuthor = !isPeerGroup
|
hideAuthor = !isPeerGroup
|
||||||
let incoming = message.flags.contains(.Incoming)
|
let incoming = message.flags.contains(.Incoming)
|
||||||
|
@ -162,6 +162,7 @@ public final class InviteLinkInviteController: ViewController {
|
|||||||
|
|
||||||
public enum CompletionResult {
|
public enum CompletionResult {
|
||||||
case linkCopied
|
case linkCopied
|
||||||
|
case openCall
|
||||||
}
|
}
|
||||||
|
|
||||||
private var animatedIn = false
|
private var animatedIn = false
|
||||||
|
@ -360,7 +360,7 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
let justCreatedCallTextAttributedString = parseMarkdownIntoAttributedString("Be the first to join the call and add people from there. [Open Call >]()", attributes: markdownAttributes).mutableCopy() as! NSMutableAttributedString
|
let justCreatedCallTextAttributedString = parseMarkdownIntoAttributedString("Be the first to join the call and add people from there. [Open Call >](open_call)", attributes: markdownAttributes).mutableCopy() as! NSMutableAttributedString
|
||||||
if let range = justCreatedCallTextAttributedString.string.range(of: ">"), let chevronImage {
|
if let range = justCreatedCallTextAttributedString.string.range(of: ">"), let chevronImage {
|
||||||
justCreatedCallTextAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: justCreatedCallTextAttributedString.string))
|
justCreatedCallTextAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: justCreatedCallTextAttributedString.string))
|
||||||
}
|
}
|
||||||
@ -575,6 +575,9 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
|
|||||||
if strongSelf.justCreatedCallTextNode !== justCreatedCallTextNode {
|
if strongSelf.justCreatedCallTextNode !== justCreatedCallTextNode {
|
||||||
strongSelf.justCreatedCallTextNode?.removeFromSupernode()
|
strongSelf.justCreatedCallTextNode?.removeFromSupernode()
|
||||||
strongSelf.justCreatedCallTextNode = justCreatedCallTextNode
|
strongSelf.justCreatedCallTextNode = justCreatedCallTextNode
|
||||||
|
|
||||||
|
//justCreatedCallTextNode.highlig
|
||||||
|
|
||||||
strongSelf.addSubnode(justCreatedCallTextNode)
|
strongSelf.addSubnode(justCreatedCallTextNode)
|
||||||
}
|
}
|
||||||
let justCreatedCallTextNodeFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - justCreatedCallTextNodeLayout.0.size.width) / 2.0), y: shareButtonNode.frame.maxY + justCreatedCallTextSpacing), size: CGSize(width: justCreatedCallTextNodeLayout.0.size.width, height: justCreatedCallTextNodeLayout.0.size.height))
|
let justCreatedCallTextNodeFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - justCreatedCallTextNodeLayout.0.size.width) / 2.0), y: shareButtonNode.frame.maxY + justCreatedCallTextSpacing), size: CGSize(width: justCreatedCallTextNodeLayout.0.size.width, height: justCreatedCallTextNodeLayout.0.size.height))
|
||||||
|
@ -29,6 +29,7 @@ swift_library(
|
|||||||
"//submodules/ComponentFlow",
|
"//submodules/ComponentFlow",
|
||||||
"//submodules/Components/ComponentDisplayAdapters",
|
"//submodules/Components/ComponentDisplayAdapters",
|
||||||
"//submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent",
|
"//submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent",
|
||||||
|
"//submodules/Components/HierarchyTrackingLayer",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -19,6 +19,7 @@ import AvatarVideoNode
|
|||||||
import ComponentFlow
|
import ComponentFlow
|
||||||
import ComponentDisplayAdapters
|
import ComponentDisplayAdapters
|
||||||
import StorySetIndicatorComponent
|
import StorySetIndicatorComponent
|
||||||
|
import HierarchyTrackingLayer
|
||||||
|
|
||||||
private class PeerInfoAvatarListLoadingStripNode: ASImageNode {
|
private class PeerInfoAvatarListLoadingStripNode: ASImageNode {
|
||||||
private var currentInHierarchy = false
|
private var currentInHierarchy = false
|
||||||
@ -225,6 +226,8 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
|||||||
private var loadingProgressDisposable = MetaDisposable()
|
private var loadingProgressDisposable = MetaDisposable()
|
||||||
private var hasProgress = false
|
private var hasProgress = false
|
||||||
|
|
||||||
|
private let hierarchyTrackingLayer = HierarchyTrackingLayer()
|
||||||
|
|
||||||
public let isReady = Promise<Bool>()
|
public let isReady = Promise<Bool>()
|
||||||
private var didSetReady: Bool = false
|
private var didSetReady: Bool = false
|
||||||
|
|
||||||
@ -312,6 +315,20 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
self.hierarchyTrackingLayer.isInHierarchyUpdated = { [weak self] value in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if value {
|
||||||
|
self.setupVideoPlayback()
|
||||||
|
} else {
|
||||||
|
self.videoNode?.removeFromSupernode()
|
||||||
|
self.videoNode = nil
|
||||||
|
self.videoContent = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.layer.addSublayer(self.hierarchyTrackingLayer)
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -364,6 +381,9 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
|||||||
guard let videoContent = self.videoContent, let isCentral = self.isCentral, isCentral, self.videoNode == nil else {
|
guard let videoContent = self.videoContent, let isCentral = self.isCentral, isCentral, self.videoNode == nil else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !self.hierarchyTrackingLayer.isInHierarchy {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let mediaManager = self.context.sharedContext.mediaManager
|
let mediaManager = self.context.sharedContext.mediaManager
|
||||||
let videoNode = UniversalVideoNode(context: self.context, postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .secondaryOverlay)
|
let videoNode = UniversalVideoNode(context: self.context, postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .secondaryOverlay)
|
||||||
|
@ -219,3 +219,45 @@ public func restartIfError<T, E>(_ signal: Signal<T, E>) -> Signal<T, NoError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum RestartOrMapErrorCondition<E> {
|
||||||
|
case restart
|
||||||
|
case error(E)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func restartOrMapError<T, E, E2>(condition: @escaping (E) -> RestartOrMapErrorCondition<E2>) -> (Signal<T, E>) -> Signal<T, E2> {
|
||||||
|
return { signal in
|
||||||
|
return Signal<T, E2> { subscriber in
|
||||||
|
let shouldRetry = Atomic(value: true)
|
||||||
|
let currentDisposable = MetaDisposable()
|
||||||
|
|
||||||
|
let start = recursiveFunction { recurse in
|
||||||
|
let currentShouldRetry = shouldRetry.with { value in
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
if currentShouldRetry {
|
||||||
|
let disposable = signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
switch condition(error) {
|
||||||
|
case .restart:
|
||||||
|
recurse()
|
||||||
|
case let .error(e2):
|
||||||
|
subscriber.putError(e2)
|
||||||
|
}
|
||||||
|
}, completed: {
|
||||||
|
let _ = shouldRetry.swap(false)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
currentDisposable.set(disposable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start()
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
currentDisposable.dispose()
|
||||||
|
let _ = shouldRetry.swap(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -52,6 +52,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[-1471112230] = { return $0.readInt32() }
|
dict[-1471112230] = { return $0.readInt32() }
|
||||||
dict[570911930] = { return $0.readInt64() }
|
dict[570911930] = { return $0.readInt64() }
|
||||||
dict[571523412] = { return $0.readDouble() }
|
dict[571523412] = { return $0.readDouble() }
|
||||||
|
dict[0x0929C32F] = { return parseInt256($0) }
|
||||||
dict[-1255641564] = { return parseString($0) }
|
dict[-1255641564] = { return parseString($0) }
|
||||||
dict[-1194283041] = { return Api.AccountDaysTTL.parse_accountDaysTTL($0) }
|
dict[-1194283041] = { return Api.AccountDaysTTL.parse_accountDaysTTL($0) }
|
||||||
dict[-653423106] = { return Api.AttachMenuBot.parse_attachMenuBot($0) }
|
dict[-653423106] = { return Api.AttachMenuBot.parse_attachMenuBot($0) }
|
||||||
@ -301,7 +302,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[286776671] = { return Api.GeoPoint.parse_geoPointEmpty($0) }
|
dict[286776671] = { return Api.GeoPoint.parse_geoPointEmpty($0) }
|
||||||
dict[-565420653] = { return Api.GeoPointAddress.parse_geoPointAddress($0) }
|
dict[-565420653] = { return Api.GeoPointAddress.parse_geoPointAddress($0) }
|
||||||
dict[-29248689] = { return Api.GlobalPrivacySettings.parse_globalPrivacySettings($0) }
|
dict[-29248689] = { return Api.GlobalPrivacySettings.parse_globalPrivacySettings($0) }
|
||||||
dict[-839330845] = { return Api.GroupCall.parse_groupCall($0) }
|
dict[-711498484] = { return Api.GroupCall.parse_groupCall($0) }
|
||||||
dict[2004925620] = { return Api.GroupCall.parse_groupCallDiscarded($0) }
|
dict[2004925620] = { return Api.GroupCall.parse_groupCallDiscarded($0) }
|
||||||
dict[-341428482] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) }
|
dict[-341428482] = { return Api.GroupCallParticipant.parse_groupCallParticipant($0) }
|
||||||
dict[1735736008] = { return Api.GroupCallParticipantVideo.parse_groupCallParticipantVideo($0) }
|
dict[1735736008] = { return Api.GroupCallParticipantVideo.parse_groupCallParticipantVideo($0) }
|
||||||
@ -381,6 +382,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[1210199983] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) }
|
dict[1210199983] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) }
|
||||||
dict[-457104426] = { return Api.InputGeoPoint.parse_inputGeoPointEmpty($0) }
|
dict[-457104426] = { return Api.InputGeoPoint.parse_inputGeoPointEmpty($0) }
|
||||||
dict[-659913713] = { return Api.InputGroupCall.parse_inputGroupCall($0) }
|
dict[-659913713] = { return Api.InputGroupCall.parse_inputGroupCall($0) }
|
||||||
|
dict[-1945083841] = { return Api.InputGroupCall.parse_inputGroupCallInviteMessage($0) }
|
||||||
|
dict[-33127873] = { return Api.InputGroupCall.parse_inputGroupCallSlug($0) }
|
||||||
dict[887591921] = { return Api.InputInvoice.parse_inputInvoiceChatInviteSubscription($0) }
|
dict[887591921] = { return Api.InputInvoice.parse_inputInvoiceChatInviteSubscription($0) }
|
||||||
dict[-977967015] = { return Api.InputInvoice.parse_inputInvoiceMessage($0) }
|
dict[-977967015] = { return Api.InputInvoice.parse_inputInvoiceMessage($0) }
|
||||||
dict[-1734841331] = { return Api.InputInvoice.parse_inputInvoicePremiumGiftCode($0) }
|
dict[-1734841331] = { return Api.InputInvoice.parse_inputInvoicePremiumGiftCode($0) }
|
||||||
@ -564,6 +567,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[51520707] = { return Api.MessageAction.parse_messageActionChatJoinedByLink($0) }
|
dict[51520707] = { return Api.MessageAction.parse_messageActionChatJoinedByLink($0) }
|
||||||
dict[-339958837] = { return Api.MessageAction.parse_messageActionChatJoinedByRequest($0) }
|
dict[-339958837] = { return Api.MessageAction.parse_messageActionChatJoinedByRequest($0) }
|
||||||
dict[-519864430] = { return Api.MessageAction.parse_messageActionChatMigrateTo($0) }
|
dict[-519864430] = { return Api.MessageAction.parse_messageActionChatMigrateTo($0) }
|
||||||
|
dict[805187450] = { return Api.MessageAction.parse_messageActionConferenceCall($0) }
|
||||||
dict[-202219658] = { return Api.MessageAction.parse_messageActionContactSignUp($0) }
|
dict[-202219658] = { return Api.MessageAction.parse_messageActionContactSignUp($0) }
|
||||||
dict[-85549226] = { return Api.MessageAction.parse_messageActionCustomAction($0) }
|
dict[-85549226] = { return Api.MessageAction.parse_messageActionCustomAction($0) }
|
||||||
dict[-1230047312] = { return Api.MessageAction.parse_messageActionEmpty($0) }
|
dict[-1230047312] = { return Api.MessageAction.parse_messageActionEmpty($0) }
|
||||||
@ -741,16 +745,16 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[-1721619444] = { return Api.PeerNotifySettings.parse_peerNotifySettings($0) }
|
dict[-1721619444] = { return Api.PeerNotifySettings.parse_peerNotifySettings($0) }
|
||||||
dict[-193510921] = { return Api.PeerSettings.parse_peerSettings($0) }
|
dict[-193510921] = { return Api.PeerSettings.parse_peerSettings($0) }
|
||||||
dict[-1707742823] = { return Api.PeerStories.parse_peerStories($0) }
|
dict[-1707742823] = { return Api.PeerStories.parse_peerStories($0) }
|
||||||
dict[1000707084] = { return Api.PhoneCall.parse_phoneCall($0) }
|
dict[810769141] = { return Api.PhoneCall.parse_phoneCall($0) }
|
||||||
dict[587035009] = { return Api.PhoneCall.parse_phoneCallAccepted($0) }
|
dict[912311057] = { return Api.PhoneCall.parse_phoneCallAccepted($0) }
|
||||||
dict[-103656189] = { return Api.PhoneCall.parse_phoneCallDiscarded($0) }
|
dict[1355435489] = { return Api.PhoneCall.parse_phoneCallDiscarded($0) }
|
||||||
dict[1399245077] = { return Api.PhoneCall.parse_phoneCallEmpty($0) }
|
dict[1399245077] = { return Api.PhoneCall.parse_phoneCallEmpty($0) }
|
||||||
dict[1161174115] = { return Api.PhoneCall.parse_phoneCallRequested($0) }
|
dict[347139340] = { return Api.PhoneCall.parse_phoneCallRequested($0) }
|
||||||
dict[-288085928] = { return Api.PhoneCall.parse_phoneCallWaiting($0) }
|
dict[-987599081] = { return Api.PhoneCall.parse_phoneCallWaiting($0) }
|
||||||
dict[-1344096199] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonAllowGroupCall($0) }
|
|
||||||
dict[-84416311] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonBusy($0) }
|
dict[-84416311] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonBusy($0) }
|
||||||
dict[-527056480] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonDisconnect($0) }
|
dict[-527056480] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonDisconnect($0) }
|
||||||
dict[1471006352] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonHangup($0) }
|
dict[1471006352] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonHangup($0) }
|
||||||
|
dict[-1615072777] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonMigrateConferenceCall($0) }
|
||||||
dict[-2048646399] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonMissed($0) }
|
dict[-2048646399] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonMissed($0) }
|
||||||
dict[-58224696] = { return Api.PhoneCallProtocol.parse_phoneCallProtocol($0) }
|
dict[-58224696] = { return Api.PhoneCallProtocol.parse_phoneCallProtocol($0) }
|
||||||
dict[-1665063993] = { return Api.PhoneConnection.parse_phoneConnection($0) }
|
dict[-1665063993] = { return Api.PhoneConnection.parse_phoneConnection($0) }
|
||||||
@ -1061,6 +1065,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[422972864] = { return Api.Update.parse_updateFolderPeers($0) }
|
dict[422972864] = { return Api.Update.parse_updateFolderPeers($0) }
|
||||||
dict[-2027964103] = { return Api.Update.parse_updateGeoLiveViewed($0) }
|
dict[-2027964103] = { return Api.Update.parse_updateGeoLiveViewed($0) }
|
||||||
dict[-1747565759] = { return Api.Update.parse_updateGroupCall($0) }
|
dict[-1747565759] = { return Api.Update.parse_updateGroupCall($0) }
|
||||||
|
dict[-1535694705] = { return Api.Update.parse_updateGroupCallChainBlocks($0) }
|
||||||
dict[192428418] = { return Api.Update.parse_updateGroupCallConnection($0) }
|
dict[192428418] = { return Api.Update.parse_updateGroupCallConnection($0) }
|
||||||
dict[-219423922] = { return Api.Update.parse_updateGroupCallParticipants($0) }
|
dict[-219423922] = { return Api.Update.parse_updateGroupCallParticipants($0) }
|
||||||
dict[1763610706] = { return Api.Update.parse_updateInlineBotCallbackQuery($0) }
|
dict[1763610706] = { return Api.Update.parse_updateInlineBotCallbackQuery($0) }
|
||||||
|
@ -169,6 +169,8 @@ public extension Api {
|
|||||||
public extension Api {
|
public extension Api {
|
||||||
enum InputGroupCall: TypeConstructorDescription {
|
enum InputGroupCall: TypeConstructorDescription {
|
||||||
case inputGroupCall(id: Int64, accessHash: Int64)
|
case inputGroupCall(id: Int64, accessHash: Int64)
|
||||||
|
case inputGroupCallInviteMessage(msgId: Int32)
|
||||||
|
case inputGroupCallSlug(slug: String)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
@ -179,6 +181,18 @@ public extension Api {
|
|||||||
serializeInt64(id, buffer: buffer, boxed: false)
|
serializeInt64(id, buffer: buffer, boxed: false)
|
||||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
|
case .inputGroupCallInviteMessage(let msgId):
|
||||||
|
if boxed {
|
||||||
|
buffer.appendInt32(-1945083841)
|
||||||
|
}
|
||||||
|
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||||
|
break
|
||||||
|
case .inputGroupCallSlug(let slug):
|
||||||
|
if boxed {
|
||||||
|
buffer.appendInt32(-33127873)
|
||||||
|
}
|
||||||
|
serializeString(slug, buffer: buffer, boxed: false)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,6 +200,10 @@ public extension Api {
|
|||||||
switch self {
|
switch self {
|
||||||
case .inputGroupCall(let id, let accessHash):
|
case .inputGroupCall(let id, let accessHash):
|
||||||
return ("inputGroupCall", [("id", id as Any), ("accessHash", accessHash as Any)])
|
return ("inputGroupCall", [("id", id as Any), ("accessHash", accessHash as Any)])
|
||||||
|
case .inputGroupCallInviteMessage(let msgId):
|
||||||
|
return ("inputGroupCallInviteMessage", [("msgId", msgId as Any)])
|
||||||
|
case .inputGroupCallSlug(let slug):
|
||||||
|
return ("inputGroupCallSlug", [("slug", slug as Any)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,6 +221,28 @@ public extension Api {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public static func parse_inputGroupCallInviteMessage(_ reader: BufferReader) -> InputGroupCall? {
|
||||||
|
var _1: Int32?
|
||||||
|
_1 = reader.readInt32()
|
||||||
|
let _c1 = _1 != nil
|
||||||
|
if _c1 {
|
||||||
|
return Api.InputGroupCall.inputGroupCallInviteMessage(msgId: _1!)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static func parse_inputGroupCallSlug(_ reader: BufferReader) -> InputGroupCall? {
|
||||||
|
var _1: String?
|
||||||
|
_1 = parseString(reader)
|
||||||
|
let _c1 = _1 != nil
|
||||||
|
if _c1 {
|
||||||
|
return Api.InputGroupCall.inputGroupCallSlug(slug: _1!)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -349,6 +349,7 @@ public extension Api {
|
|||||||
case messageActionChatJoinedByLink(inviterId: Int64)
|
case messageActionChatJoinedByLink(inviterId: Int64)
|
||||||
case messageActionChatJoinedByRequest
|
case messageActionChatJoinedByRequest
|
||||||
case messageActionChatMigrateTo(channelId: Int64)
|
case messageActionChatMigrateTo(channelId: Int64)
|
||||||
|
case messageActionConferenceCall(flags: Int32, callId: Int64, duration: Int32?, otherParticipants: [Api.Peer]?)
|
||||||
case messageActionContactSignUp
|
case messageActionContactSignUp
|
||||||
case messageActionCustomAction(message: String)
|
case messageActionCustomAction(message: String)
|
||||||
case messageActionEmpty
|
case messageActionEmpty
|
||||||
@ -479,6 +480,19 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
serializeInt64(channelId, buffer: buffer, boxed: false)
|
serializeInt64(channelId, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
|
case .messageActionConferenceCall(let flags, let callId, let duration, let otherParticipants):
|
||||||
|
if boxed {
|
||||||
|
buffer.appendInt32(805187450)
|
||||||
|
}
|
||||||
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
|
serializeInt64(callId, buffer: buffer, boxed: false)
|
||||||
|
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)}
|
||||||
|
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
|
||||||
|
buffer.appendInt32(Int32(otherParticipants!.count))
|
||||||
|
for item in otherParticipants! {
|
||||||
|
item.serialize(buffer, true)
|
||||||
|
}}
|
||||||
|
break
|
||||||
case .messageActionContactSignUp:
|
case .messageActionContactSignUp:
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(-202219658)
|
buffer.appendInt32(-202219658)
|
||||||
@ -834,6 +848,8 @@ public extension Api {
|
|||||||
return ("messageActionChatJoinedByRequest", [])
|
return ("messageActionChatJoinedByRequest", [])
|
||||||
case .messageActionChatMigrateTo(let channelId):
|
case .messageActionChatMigrateTo(let channelId):
|
||||||
return ("messageActionChatMigrateTo", [("channelId", channelId as Any)])
|
return ("messageActionChatMigrateTo", [("channelId", channelId as Any)])
|
||||||
|
case .messageActionConferenceCall(let flags, let callId, let duration, let otherParticipants):
|
||||||
|
return ("messageActionConferenceCall", [("flags", flags as Any), ("callId", callId as Any), ("duration", duration as Any), ("otherParticipants", otherParticipants as Any)])
|
||||||
case .messageActionContactSignUp:
|
case .messageActionContactSignUp:
|
||||||
return ("messageActionContactSignUp", [])
|
return ("messageActionContactSignUp", [])
|
||||||
case .messageActionCustomAction(let message):
|
case .messageActionCustomAction(let message):
|
||||||
@ -1058,6 +1074,28 @@ public extension Api {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public static func parse_messageActionConferenceCall(_ reader: BufferReader) -> MessageAction? {
|
||||||
|
var _1: Int32?
|
||||||
|
_1 = reader.readInt32()
|
||||||
|
var _2: Int64?
|
||||||
|
_2 = reader.readInt64()
|
||||||
|
var _3: Int32?
|
||||||
|
if Int(_1!) & Int(1 << 2) != 0 {_3 = reader.readInt32() }
|
||||||
|
var _4: [Api.Peer]?
|
||||||
|
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
|
||||||
|
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self)
|
||||||
|
} }
|
||||||
|
let _c1 = _1 != nil
|
||||||
|
let _c2 = _2 != nil
|
||||||
|
let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil
|
||||||
|
let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil
|
||||||
|
if _c1 && _c2 && _c3 && _c4 {
|
||||||
|
return Api.MessageAction.messageActionConferenceCall(flags: _1!, callId: _2!, duration: _3, otherParticipants: _4)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
public static func parse_messageActionContactSignUp(_ reader: BufferReader) -> MessageAction? {
|
public static func parse_messageActionContactSignUp(_ reader: BufferReader) -> MessageAction? {
|
||||||
return Api.MessageAction.messageActionContactSignUp
|
return Api.MessageAction.messageActionContactSignUp
|
||||||
}
|
}
|
||||||
|
@ -1092,18 +1092,18 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
public extension Api {
|
public extension Api {
|
||||||
enum PhoneCall: TypeConstructorDescription {
|
enum PhoneCall: TypeConstructorDescription {
|
||||||
case phoneCall(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gAOrB: Buffer, keyFingerprint: Int64, protocol: Api.PhoneCallProtocol, connections: [Api.PhoneConnection], startDate: Int32, customParameters: Api.DataJSON?, conferenceCall: Api.InputGroupCall?)
|
case phoneCall(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gAOrB: Buffer, keyFingerprint: Int64, protocol: Api.PhoneCallProtocol, connections: [Api.PhoneConnection], startDate: Int32, customParameters: Api.DataJSON?)
|
||||||
case phoneCallAccepted(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gB: Buffer, protocol: Api.PhoneCallProtocol, conferenceCall: Api.InputGroupCall?)
|
case phoneCallAccepted(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gB: Buffer, protocol: Api.PhoneCallProtocol)
|
||||||
case phoneCallDiscarded(flags: Int32, id: Int64, reason: Api.PhoneCallDiscardReason?, duration: Int32?, conferenceCall: Api.InputGroupCall?)
|
case phoneCallDiscarded(flags: Int32, id: Int64, reason: Api.PhoneCallDiscardReason?, duration: Int32?)
|
||||||
case phoneCallEmpty(id: Int64)
|
case phoneCallEmpty(id: Int64)
|
||||||
case phoneCallRequested(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gAHash: Buffer, protocol: Api.PhoneCallProtocol, conferenceCall: Api.InputGroupCall?)
|
case phoneCallRequested(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, gAHash: Buffer, protocol: Api.PhoneCallProtocol)
|
||||||
case phoneCallWaiting(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, protocol: Api.PhoneCallProtocol, receiveDate: Int32?, conferenceCall: Api.InputGroupCall?)
|
case phoneCallWaiting(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int64, participantId: Int64, protocol: Api.PhoneCallProtocol, receiveDate: Int32?)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
case .phoneCall(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint, let `protocol`, let connections, let startDate, let customParameters, let conferenceCall):
|
case .phoneCall(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint, let `protocol`, let connections, let startDate, let customParameters):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(1000707084)
|
buffer.appendInt32(810769141)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt64(id, buffer: buffer, boxed: false)
|
serializeInt64(id, buffer: buffer, boxed: false)
|
||||||
@ -1121,11 +1121,10 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
serializeInt32(startDate, buffer: buffer, boxed: false)
|
serializeInt32(startDate, buffer: buffer, boxed: false)
|
||||||
if Int(flags) & Int(1 << 7) != 0 {customParameters!.serialize(buffer, true)}
|
if Int(flags) & Int(1 << 7) != 0 {customParameters!.serialize(buffer, true)}
|
||||||
if Int(flags) & Int(1 << 8) != 0 {conferenceCall!.serialize(buffer, true)}
|
|
||||||
break
|
break
|
||||||
case .phoneCallAccepted(let flags, let id, let accessHash, let date, let adminId, let participantId, let gB, let `protocol`, let conferenceCall):
|
case .phoneCallAccepted(let flags, let id, let accessHash, let date, let adminId, let participantId, let gB, let `protocol`):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(587035009)
|
buffer.appendInt32(912311057)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt64(id, buffer: buffer, boxed: false)
|
serializeInt64(id, buffer: buffer, boxed: false)
|
||||||
@ -1135,17 +1134,15 @@ public extension Api {
|
|||||||
serializeInt64(participantId, buffer: buffer, boxed: false)
|
serializeInt64(participantId, buffer: buffer, boxed: false)
|
||||||
serializeBytes(gB, buffer: buffer, boxed: false)
|
serializeBytes(gB, buffer: buffer, boxed: false)
|
||||||
`protocol`.serialize(buffer, true)
|
`protocol`.serialize(buffer, true)
|
||||||
if Int(flags) & Int(1 << 8) != 0 {conferenceCall!.serialize(buffer, true)}
|
|
||||||
break
|
break
|
||||||
case .phoneCallDiscarded(let flags, let id, let reason, let duration, let conferenceCall):
|
case .phoneCallDiscarded(let flags, let id, let reason, let duration):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(-103656189)
|
buffer.appendInt32(1355435489)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt64(id, buffer: buffer, boxed: false)
|
serializeInt64(id, buffer: buffer, boxed: false)
|
||||||
if Int(flags) & Int(1 << 0) != 0 {reason!.serialize(buffer, true)}
|
if Int(flags) & Int(1 << 0) != 0 {reason!.serialize(buffer, true)}
|
||||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 8) != 0 {conferenceCall!.serialize(buffer, true)}
|
|
||||||
break
|
break
|
||||||
case .phoneCallEmpty(let id):
|
case .phoneCallEmpty(let id):
|
||||||
if boxed {
|
if boxed {
|
||||||
@ -1153,9 +1150,9 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
serializeInt64(id, buffer: buffer, boxed: false)
|
serializeInt64(id, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
case .phoneCallRequested(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAHash, let `protocol`, let conferenceCall):
|
case .phoneCallRequested(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAHash, let `protocol`):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(1161174115)
|
buffer.appendInt32(347139340)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt64(id, buffer: buffer, boxed: false)
|
serializeInt64(id, buffer: buffer, boxed: false)
|
||||||
@ -1165,11 +1162,10 @@ public extension Api {
|
|||||||
serializeInt64(participantId, buffer: buffer, boxed: false)
|
serializeInt64(participantId, buffer: buffer, boxed: false)
|
||||||
serializeBytes(gAHash, buffer: buffer, boxed: false)
|
serializeBytes(gAHash, buffer: buffer, boxed: false)
|
||||||
`protocol`.serialize(buffer, true)
|
`protocol`.serialize(buffer, true)
|
||||||
if Int(flags) & Int(1 << 8) != 0 {conferenceCall!.serialize(buffer, true)}
|
|
||||||
break
|
break
|
||||||
case .phoneCallWaiting(let flags, let id, let accessHash, let date, let adminId, let participantId, let `protocol`, let receiveDate, let conferenceCall):
|
case .phoneCallWaiting(let flags, let id, let accessHash, let date, let adminId, let participantId, let `protocol`, let receiveDate):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(-288085928)
|
buffer.appendInt32(-987599081)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt64(id, buffer: buffer, boxed: false)
|
serializeInt64(id, buffer: buffer, boxed: false)
|
||||||
@ -1179,25 +1175,24 @@ public extension Api {
|
|||||||
serializeInt64(participantId, buffer: buffer, boxed: false)
|
serializeInt64(participantId, buffer: buffer, boxed: false)
|
||||||
`protocol`.serialize(buffer, true)
|
`protocol`.serialize(buffer, true)
|
||||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(receiveDate!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(receiveDate!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 8) != 0 {conferenceCall!.serialize(buffer, true)}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
switch self {
|
||||||
case .phoneCall(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint, let `protocol`, let connections, let startDate, let customParameters, let conferenceCall):
|
case .phoneCall(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint, let `protocol`, let connections, let startDate, let customParameters):
|
||||||
return ("phoneCall", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gAOrB", gAOrB as Any), ("keyFingerprint", keyFingerprint as Any), ("`protocol`", `protocol` as Any), ("connections", connections as Any), ("startDate", startDate as Any), ("customParameters", customParameters as Any), ("conferenceCall", conferenceCall as Any)])
|
return ("phoneCall", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gAOrB", gAOrB as Any), ("keyFingerprint", keyFingerprint as Any), ("`protocol`", `protocol` as Any), ("connections", connections as Any), ("startDate", startDate as Any), ("customParameters", customParameters as Any)])
|
||||||
case .phoneCallAccepted(let flags, let id, let accessHash, let date, let adminId, let participantId, let gB, let `protocol`, let conferenceCall):
|
case .phoneCallAccepted(let flags, let id, let accessHash, let date, let adminId, let participantId, let gB, let `protocol`):
|
||||||
return ("phoneCallAccepted", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gB", gB as Any), ("`protocol`", `protocol` as Any), ("conferenceCall", conferenceCall as Any)])
|
return ("phoneCallAccepted", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gB", gB as Any), ("`protocol`", `protocol` as Any)])
|
||||||
case .phoneCallDiscarded(let flags, let id, let reason, let duration, let conferenceCall):
|
case .phoneCallDiscarded(let flags, let id, let reason, let duration):
|
||||||
return ("phoneCallDiscarded", [("flags", flags as Any), ("id", id as Any), ("reason", reason as Any), ("duration", duration as Any), ("conferenceCall", conferenceCall as Any)])
|
return ("phoneCallDiscarded", [("flags", flags as Any), ("id", id as Any), ("reason", reason as Any), ("duration", duration as Any)])
|
||||||
case .phoneCallEmpty(let id):
|
case .phoneCallEmpty(let id):
|
||||||
return ("phoneCallEmpty", [("id", id as Any)])
|
return ("phoneCallEmpty", [("id", id as Any)])
|
||||||
case .phoneCallRequested(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAHash, let `protocol`, let conferenceCall):
|
case .phoneCallRequested(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAHash, let `protocol`):
|
||||||
return ("phoneCallRequested", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gAHash", gAHash as Any), ("`protocol`", `protocol` as Any), ("conferenceCall", conferenceCall as Any)])
|
return ("phoneCallRequested", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("gAHash", gAHash as Any), ("`protocol`", `protocol` as Any)])
|
||||||
case .phoneCallWaiting(let flags, let id, let accessHash, let date, let adminId, let participantId, let `protocol`, let receiveDate, let conferenceCall):
|
case .phoneCallWaiting(let flags, let id, let accessHash, let date, let adminId, let participantId, let `protocol`, let receiveDate):
|
||||||
return ("phoneCallWaiting", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("`protocol`", `protocol` as Any), ("receiveDate", receiveDate as Any), ("conferenceCall", conferenceCall as Any)])
|
return ("phoneCallWaiting", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("date", date as Any), ("adminId", adminId as Any), ("participantId", participantId as Any), ("`protocol`", `protocol` as Any), ("receiveDate", receiveDate as Any)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1232,10 +1227,6 @@ public extension Api {
|
|||||||
if Int(_1!) & Int(1 << 7) != 0 {if let signature = reader.readInt32() {
|
if Int(_1!) & Int(1 << 7) != 0 {if let signature = reader.readInt32() {
|
||||||
_12 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
_12 = Api.parse(reader, signature: signature) as? Api.DataJSON
|
||||||
} }
|
} }
|
||||||
var _13: Api.InputGroupCall?
|
|
||||||
if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() {
|
|
||||||
_13 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
|
|
||||||
} }
|
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = _3 != nil
|
||||||
@ -1248,9 +1239,8 @@ public extension Api {
|
|||||||
let _c10 = _10 != nil
|
let _c10 = _10 != nil
|
||||||
let _c11 = _11 != nil
|
let _c11 = _11 != nil
|
||||||
let _c12 = (Int(_1!) & Int(1 << 7) == 0) || _12 != nil
|
let _c12 = (Int(_1!) & Int(1 << 7) == 0) || _12 != nil
|
||||||
let _c13 = (Int(_1!) & Int(1 << 8) == 0) || _13 != nil
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 {
|
return Api.PhoneCall.phoneCall(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gAOrB: _7!, keyFingerprint: _8!, protocol: _9!, connections: _10!, startDate: _11!, customParameters: _12)
|
||||||
return Api.PhoneCall.phoneCall(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gAOrB: _7!, keyFingerprint: _8!, protocol: _9!, connections: _10!, startDate: _11!, customParameters: _12, conferenceCall: _13)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
@ -1275,10 +1265,6 @@ public extension Api {
|
|||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
_8 = Api.parse(reader, signature: signature) as? Api.PhoneCallProtocol
|
_8 = Api.parse(reader, signature: signature) as? Api.PhoneCallProtocol
|
||||||
}
|
}
|
||||||
var _9: Api.InputGroupCall?
|
|
||||||
if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() {
|
|
||||||
_9 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
|
|
||||||
} }
|
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = _3 != nil
|
||||||
@ -1287,9 +1273,8 @@ public extension Api {
|
|||||||
let _c6 = _6 != nil
|
let _c6 = _6 != nil
|
||||||
let _c7 = _7 != nil
|
let _c7 = _7 != nil
|
||||||
let _c8 = _8 != nil
|
let _c8 = _8 != nil
|
||||||
let _c9 = (Int(_1!) & Int(1 << 8) == 0) || _9 != nil
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
return Api.PhoneCall.phoneCallAccepted(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gB: _7!, protocol: _8!)
|
||||||
return Api.PhoneCall.phoneCallAccepted(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gB: _7!, protocol: _8!, conferenceCall: _9)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
@ -1306,17 +1291,12 @@ public extension Api {
|
|||||||
} }
|
} }
|
||||||
var _4: Int32?
|
var _4: Int32?
|
||||||
if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() }
|
if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() }
|
||||||
var _5: Api.InputGroupCall?
|
|
||||||
if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() {
|
|
||||||
_5 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
|
|
||||||
} }
|
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||||
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
|
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
|
||||||
let _c5 = (Int(_1!) & Int(1 << 8) == 0) || _5 != nil
|
if _c1 && _c2 && _c3 && _c4 {
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
return Api.PhoneCall.phoneCallDiscarded(flags: _1!, id: _2!, reason: _3, duration: _4)
|
||||||
return Api.PhoneCall.phoneCallDiscarded(flags: _1!, id: _2!, reason: _3, duration: _4, conferenceCall: _5)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
@ -1352,10 +1332,6 @@ public extension Api {
|
|||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
_8 = Api.parse(reader, signature: signature) as? Api.PhoneCallProtocol
|
_8 = Api.parse(reader, signature: signature) as? Api.PhoneCallProtocol
|
||||||
}
|
}
|
||||||
var _9: Api.InputGroupCall?
|
|
||||||
if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() {
|
|
||||||
_9 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
|
|
||||||
} }
|
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = _3 != nil
|
||||||
@ -1364,9 +1340,8 @@ public extension Api {
|
|||||||
let _c6 = _6 != nil
|
let _c6 = _6 != nil
|
||||||
let _c7 = _7 != nil
|
let _c7 = _7 != nil
|
||||||
let _c8 = _8 != nil
|
let _c8 = _8 != nil
|
||||||
let _c9 = (Int(_1!) & Int(1 << 8) == 0) || _9 != nil
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
return Api.PhoneCall.phoneCallRequested(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gAHash: _7!, protocol: _8!)
|
||||||
return Api.PhoneCall.phoneCallRequested(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gAHash: _7!, protocol: _8!, conferenceCall: _9)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
@ -1391,10 +1366,6 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
var _8: Int32?
|
var _8: Int32?
|
||||||
if Int(_1!) & Int(1 << 0) != 0 {_8 = reader.readInt32() }
|
if Int(_1!) & Int(1 << 0) != 0 {_8 = reader.readInt32() }
|
||||||
var _9: Api.InputGroupCall?
|
|
||||||
if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() {
|
|
||||||
_9 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
|
|
||||||
} }
|
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = _3 != nil
|
||||||
@ -1403,9 +1374,8 @@ public extension Api {
|
|||||||
let _c6 = _6 != nil
|
let _c6 = _6 != nil
|
||||||
let _c7 = _7 != nil
|
let _c7 = _7 != nil
|
||||||
let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil
|
let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil
|
||||||
let _c9 = (Int(_1!) & Int(1 << 8) == 0) || _9 != nil
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
return Api.PhoneCall.phoneCallWaiting(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, protocol: _7!, receiveDate: _8)
|
||||||
return Api.PhoneCall.phoneCallWaiting(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, protocol: _7!, receiveDate: _8, conferenceCall: _9)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1,19 +1,13 @@
|
|||||||
public extension Api {
|
public extension Api {
|
||||||
enum PhoneCallDiscardReason: TypeConstructorDescription {
|
enum PhoneCallDiscardReason: TypeConstructorDescription {
|
||||||
case phoneCallDiscardReasonAllowGroupCall(encryptedKey: Buffer)
|
|
||||||
case phoneCallDiscardReasonBusy
|
case phoneCallDiscardReasonBusy
|
||||||
case phoneCallDiscardReasonDisconnect
|
case phoneCallDiscardReasonDisconnect
|
||||||
case phoneCallDiscardReasonHangup
|
case phoneCallDiscardReasonHangup
|
||||||
|
case phoneCallDiscardReasonMigrateConferenceCall(slug: String)
|
||||||
case phoneCallDiscardReasonMissed
|
case phoneCallDiscardReasonMissed
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
case .phoneCallDiscardReasonAllowGroupCall(let encryptedKey):
|
|
||||||
if boxed {
|
|
||||||
buffer.appendInt32(-1344096199)
|
|
||||||
}
|
|
||||||
serializeBytes(encryptedKey, buffer: buffer, boxed: false)
|
|
||||||
break
|
|
||||||
case .phoneCallDiscardReasonBusy:
|
case .phoneCallDiscardReasonBusy:
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(-84416311)
|
buffer.appendInt32(-84416311)
|
||||||
@ -31,6 +25,12 @@ public extension Api {
|
|||||||
buffer.appendInt32(1471006352)
|
buffer.appendInt32(1471006352)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
case .phoneCallDiscardReasonMigrateConferenceCall(let slug):
|
||||||
|
if boxed {
|
||||||
|
buffer.appendInt32(-1615072777)
|
||||||
|
}
|
||||||
|
serializeString(slug, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
case .phoneCallDiscardReasonMissed:
|
case .phoneCallDiscardReasonMissed:
|
||||||
if boxed {
|
if boxed {
|
||||||
@ -43,30 +43,19 @@ public extension Api {
|
|||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
switch self {
|
||||||
case .phoneCallDiscardReasonAllowGroupCall(let encryptedKey):
|
|
||||||
return ("phoneCallDiscardReasonAllowGroupCall", [("encryptedKey", encryptedKey as Any)])
|
|
||||||
case .phoneCallDiscardReasonBusy:
|
case .phoneCallDiscardReasonBusy:
|
||||||
return ("phoneCallDiscardReasonBusy", [])
|
return ("phoneCallDiscardReasonBusy", [])
|
||||||
case .phoneCallDiscardReasonDisconnect:
|
case .phoneCallDiscardReasonDisconnect:
|
||||||
return ("phoneCallDiscardReasonDisconnect", [])
|
return ("phoneCallDiscardReasonDisconnect", [])
|
||||||
case .phoneCallDiscardReasonHangup:
|
case .phoneCallDiscardReasonHangup:
|
||||||
return ("phoneCallDiscardReasonHangup", [])
|
return ("phoneCallDiscardReasonHangup", [])
|
||||||
|
case .phoneCallDiscardReasonMigrateConferenceCall(let slug):
|
||||||
|
return ("phoneCallDiscardReasonMigrateConferenceCall", [("slug", slug as Any)])
|
||||||
case .phoneCallDiscardReasonMissed:
|
case .phoneCallDiscardReasonMissed:
|
||||||
return ("phoneCallDiscardReasonMissed", [])
|
return ("phoneCallDiscardReasonMissed", [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func parse_phoneCallDiscardReasonAllowGroupCall(_ reader: BufferReader) -> PhoneCallDiscardReason? {
|
|
||||||
var _1: Buffer?
|
|
||||||
_1 = parseBytes(reader)
|
|
||||||
let _c1 = _1 != nil
|
|
||||||
if _c1 {
|
|
||||||
return Api.PhoneCallDiscardReason.phoneCallDiscardReasonAllowGroupCall(encryptedKey: _1!)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static func parse_phoneCallDiscardReasonBusy(_ reader: BufferReader) -> PhoneCallDiscardReason? {
|
public static func parse_phoneCallDiscardReasonBusy(_ reader: BufferReader) -> PhoneCallDiscardReason? {
|
||||||
return Api.PhoneCallDiscardReason.phoneCallDiscardReasonBusy
|
return Api.PhoneCallDiscardReason.phoneCallDiscardReasonBusy
|
||||||
}
|
}
|
||||||
@ -76,6 +65,17 @@ public extension Api {
|
|||||||
public static func parse_phoneCallDiscardReasonHangup(_ reader: BufferReader) -> PhoneCallDiscardReason? {
|
public static func parse_phoneCallDiscardReasonHangup(_ reader: BufferReader) -> PhoneCallDiscardReason? {
|
||||||
return Api.PhoneCallDiscardReason.phoneCallDiscardReasonHangup
|
return Api.PhoneCallDiscardReason.phoneCallDiscardReasonHangup
|
||||||
}
|
}
|
||||||
|
public static func parse_phoneCallDiscardReasonMigrateConferenceCall(_ reader: BufferReader) -> PhoneCallDiscardReason? {
|
||||||
|
var _1: String?
|
||||||
|
_1 = parseString(reader)
|
||||||
|
let _c1 = _1 != nil
|
||||||
|
if _c1 {
|
||||||
|
return Api.PhoneCallDiscardReason.phoneCallDiscardReasonMigrateConferenceCall(slug: _1!)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
public static func parse_phoneCallDiscardReasonMissed(_ reader: BufferReader) -> PhoneCallDiscardReason? {
|
public static func parse_phoneCallDiscardReasonMissed(_ reader: BufferReader) -> PhoneCallDiscardReason? {
|
||||||
return Api.PhoneCallDiscardReason.phoneCallDiscardReasonMissed
|
return Api.PhoneCallDiscardReason.phoneCallDiscardReasonMissed
|
||||||
}
|
}
|
||||||
|
@ -1048,6 +1048,7 @@ public extension Api {
|
|||||||
case updateFolderPeers(folderPeers: [Api.FolderPeer], pts: Int32, ptsCount: Int32)
|
case updateFolderPeers(folderPeers: [Api.FolderPeer], pts: Int32, ptsCount: Int32)
|
||||||
case updateGeoLiveViewed(peer: Api.Peer, msgId: Int32)
|
case updateGeoLiveViewed(peer: Api.Peer, msgId: Int32)
|
||||||
case updateGroupCall(flags: Int32, chatId: Int64?, call: Api.GroupCall)
|
case updateGroupCall(flags: Int32, chatId: Int64?, call: Api.GroupCall)
|
||||||
|
case updateGroupCallChainBlocks(call: Api.InputGroupCall, subChainId: Int32, blocks: [Buffer], nextOffset: Int32)
|
||||||
case updateGroupCallConnection(flags: Int32, params: Api.DataJSON)
|
case updateGroupCallConnection(flags: Int32, params: Api.DataJSON)
|
||||||
case updateGroupCallParticipants(call: Api.InputGroupCall, participants: [Api.GroupCallParticipant], version: Int32)
|
case updateGroupCallParticipants(call: Api.InputGroupCall, participants: [Api.GroupCallParticipant], version: Int32)
|
||||||
case updateInlineBotCallbackQuery(flags: Int32, queryId: Int64, userId: Int64, msgId: Api.InputBotInlineMessageID, chatInstance: Int64, data: Buffer?, gameShortName: String?)
|
case updateInlineBotCallbackQuery(flags: Int32, queryId: Int64, userId: Int64, msgId: Api.InputBotInlineMessageID, chatInstance: Int64, data: Buffer?, gameShortName: String?)
|
||||||
@ -1738,6 +1739,19 @@ public extension Api {
|
|||||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(chatId!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(chatId!, buffer: buffer, boxed: false)}
|
||||||
call.serialize(buffer, true)
|
call.serialize(buffer, true)
|
||||||
break
|
break
|
||||||
|
case .updateGroupCallChainBlocks(let call, let subChainId, let blocks, let nextOffset):
|
||||||
|
if boxed {
|
||||||
|
buffer.appendInt32(-1535694705)
|
||||||
|
}
|
||||||
|
call.serialize(buffer, true)
|
||||||
|
serializeInt32(subChainId, buffer: buffer, boxed: false)
|
||||||
|
buffer.appendInt32(481674261)
|
||||||
|
buffer.appendInt32(Int32(blocks.count))
|
||||||
|
for item in blocks {
|
||||||
|
serializeBytes(item, buffer: buffer, boxed: false)
|
||||||
|
}
|
||||||
|
serializeInt32(nextOffset, buffer: buffer, boxed: false)
|
||||||
|
break
|
||||||
case .updateGroupCallConnection(let flags, let params):
|
case .updateGroupCallConnection(let flags, let params):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(192428418)
|
buffer.appendInt32(192428418)
|
||||||
@ -2499,6 +2513,8 @@ public extension Api {
|
|||||||
return ("updateGeoLiveViewed", [("peer", peer as Any), ("msgId", msgId as Any)])
|
return ("updateGeoLiveViewed", [("peer", peer as Any), ("msgId", msgId as Any)])
|
||||||
case .updateGroupCall(let flags, let chatId, let call):
|
case .updateGroupCall(let flags, let chatId, let call):
|
||||||
return ("updateGroupCall", [("flags", flags as Any), ("chatId", chatId as Any), ("call", call as Any)])
|
return ("updateGroupCall", [("flags", flags as Any), ("chatId", chatId as Any), ("call", call as Any)])
|
||||||
|
case .updateGroupCallChainBlocks(let call, let subChainId, let blocks, let nextOffset):
|
||||||
|
return ("updateGroupCallChainBlocks", [("call", call as Any), ("subChainId", subChainId as Any), ("blocks", blocks as Any), ("nextOffset", nextOffset as Any)])
|
||||||
case .updateGroupCallConnection(let flags, let params):
|
case .updateGroupCallConnection(let flags, let params):
|
||||||
return ("updateGroupCallConnection", [("flags", flags as Any), ("params", params as Any)])
|
return ("updateGroupCallConnection", [("flags", flags as Any), ("params", params as Any)])
|
||||||
case .updateGroupCallParticipants(let call, let participants, let version):
|
case .updateGroupCallParticipants(let call, let participants, let version):
|
||||||
@ -3939,6 +3955,30 @@ public extension Api {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public static func parse_updateGroupCallChainBlocks(_ reader: BufferReader) -> Update? {
|
||||||
|
var _1: Api.InputGroupCall?
|
||||||
|
if let signature = reader.readInt32() {
|
||||||
|
_1 = Api.parse(reader, signature: signature) as? Api.InputGroupCall
|
||||||
|
}
|
||||||
|
var _2: Int32?
|
||||||
|
_2 = reader.readInt32()
|
||||||
|
var _3: [Buffer]?
|
||||||
|
if let _ = reader.readInt32() {
|
||||||
|
_3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self)
|
||||||
|
}
|
||||||
|
var _4: Int32?
|
||||||
|
_4 = reader.readInt32()
|
||||||
|
let _c1 = _1 != nil
|
||||||
|
let _c2 = _2 != nil
|
||||||
|
let _c3 = _3 != nil
|
||||||
|
let _c4 = _4 != nil
|
||||||
|
if _c1 && _c2 && _c3 && _c4 {
|
||||||
|
return Api.Update.updateGroupCallChainBlocks(call: _1!, subChainId: _2!, blocks: _3!, nextOffset: _4!)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
public static func parse_updateGroupCallConnection(_ reader: BufferReader) -> Update? {
|
public static func parse_updateGroupCallConnection(_ reader: BufferReader) -> Update? {
|
||||||
var _1: Int32?
|
var _1: Int32?
|
||||||
_1 = reader.readInt32()
|
_1 = reader.readInt32()
|
||||||
|
@ -9855,16 +9855,15 @@ public extension Api.functions.phone {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public extension Api.functions.phone {
|
public extension Api.functions.phone {
|
||||||
static func createConferenceCall(peer: Api.InputPhoneCall, keyFingerprint: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.PhoneCall>) {
|
static func createConferenceCall(randomId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.GroupCall>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(-540472917)
|
buffer.appendInt32(-70320410)
|
||||||
peer.serialize(buffer, true)
|
serializeInt32(randomId, buffer: buffer, boxed: false)
|
||||||
serializeInt64(keyFingerprint, buffer: buffer, boxed: false)
|
return (FunctionDescription(name: "phone.createConferenceCall", parameters: [("randomId", String(describing: randomId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.GroupCall? in
|
||||||
return (FunctionDescription(name: "phone.createConferenceCall", parameters: [("peer", String(describing: peer)), ("keyFingerprint", String(describing: keyFingerprint))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.PhoneCall? in
|
|
||||||
let reader = BufferReader(buffer)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.phone.PhoneCall?
|
var result: Api.phone.GroupCall?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
result = Api.parse(reader, signature: signature) as? Api.phone.PhoneCall
|
result = Api.parse(reader, signature: signature) as? Api.phone.GroupCall
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
@ -9889,6 +9888,42 @@ public extension Api.functions.phone {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public extension Api.functions.phone {
|
||||||
|
static func declineConferenceCallInvite(msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||||
|
let buffer = Buffer()
|
||||||
|
buffer.appendInt32(1011325297)
|
||||||
|
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||||
|
return (FunctionDescription(name: "phone.declineConferenceCallInvite", parameters: [("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||||
|
let reader = BufferReader(buffer)
|
||||||
|
var result: Api.Updates?
|
||||||
|
if let signature = reader.readInt32() {
|
||||||
|
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public extension Api.functions.phone {
|
||||||
|
static func deleteConferenceCallParticipants(call: Api.InputGroupCall, ids: [Api.InputPeer], block: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||||
|
let buffer = Buffer()
|
||||||
|
buffer.appendInt32(585451463)
|
||||||
|
call.serialize(buffer, true)
|
||||||
|
buffer.appendInt32(481674261)
|
||||||
|
buffer.appendInt32(Int32(ids.count))
|
||||||
|
for item in ids {
|
||||||
|
item.serialize(buffer, true)
|
||||||
|
}
|
||||||
|
serializeBytes(block, buffer: buffer, boxed: false)
|
||||||
|
return (FunctionDescription(name: "phone.deleteConferenceCallParticipants", parameters: [("call", String(describing: call)), ("ids", String(describing: ids)), ("block", String(describing: block))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||||
|
let reader = BufferReader(buffer)
|
||||||
|
var result: Api.Updates?
|
||||||
|
if let signature = reader.readInt32() {
|
||||||
|
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
public extension Api.functions.phone {
|
public extension Api.functions.phone {
|
||||||
static func discardCall(flags: Int32, peer: Api.InputPhoneCall, duration: Int32, reason: Api.PhoneCallDiscardReason, connectionId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
static func discardCall(flags: Int32, peer: Api.InputPhoneCall, duration: Int32, reason: Api.PhoneCallDiscardReason, connectionId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
@ -10009,6 +10044,24 @@ public extension Api.functions.phone {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public extension Api.functions.phone {
|
||||||
|
static func getGroupCallChainBlocks(call: Api.InputGroupCall, subChainId: Int32, offset: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||||
|
let buffer = Buffer()
|
||||||
|
buffer.appendInt32(-291534682)
|
||||||
|
call.serialize(buffer, true)
|
||||||
|
serializeInt32(subChainId, buffer: buffer, boxed: false)
|
||||||
|
serializeInt32(offset, buffer: buffer, boxed: false)
|
||||||
|
serializeInt32(limit, buffer: buffer, boxed: false)
|
||||||
|
return (FunctionDescription(name: "phone.getGroupCallChainBlocks", parameters: [("call", String(describing: call)), ("subChainId", String(describing: subChainId)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||||
|
let reader = BufferReader(buffer)
|
||||||
|
var result: Api.Updates?
|
||||||
|
if let signature = reader.readInt32() {
|
||||||
|
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
public extension Api.functions.phone {
|
public extension Api.functions.phone {
|
||||||
static func getGroupCallJoinAs(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.JoinAsPeers>) {
|
static func getGroupCallJoinAs(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.JoinAsPeers>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
@ -10082,6 +10135,22 @@ public extension Api.functions.phone {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public extension Api.functions.phone {
|
||||||
|
static func inviteConferenceCallParticipant(call: Api.InputGroupCall, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||||
|
let buffer = Buffer()
|
||||||
|
buffer.appendInt32(1050474478)
|
||||||
|
call.serialize(buffer, true)
|
||||||
|
userId.serialize(buffer, true)
|
||||||
|
return (FunctionDescription(name: "phone.inviteConferenceCallParticipant", parameters: [("call", String(describing: call)), ("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||||
|
let reader = BufferReader(buffer)
|
||||||
|
var result: Api.Updates?
|
||||||
|
if let signature = reader.readInt32() {
|
||||||
|
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
public extension Api.functions.phone {
|
public extension Api.functions.phone {
|
||||||
static func inviteToGroupCall(call: Api.InputGroupCall, users: [Api.InputUser]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
static func inviteToGroupCall(call: Api.InputGroupCall, users: [Api.InputUser]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
@ -10103,16 +10172,18 @@ public extension Api.functions.phone {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public extension Api.functions.phone {
|
public extension Api.functions.phone {
|
||||||
static func joinGroupCall(flags: Int32, call: Api.InputGroupCall, joinAs: Api.InputPeer, inviteHash: String?, keyFingerprint: Int64?, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
static func joinGroupCall(flags: Int32, call: Api.InputGroupCall, joinAs: Api.InputPeer, inviteHash: String?, publicKey: Int256?, block: Buffer?, inviteMsgId: Int32?, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(-702669325)
|
buffer.appendInt32(-624854114)
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
call.serialize(buffer, true)
|
call.serialize(buffer, true)
|
||||||
joinAs.serialize(buffer, true)
|
joinAs.serialize(buffer, true)
|
||||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(inviteHash!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 1) != 0 {serializeString(inviteHash!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 3) != 0 {serializeInt64(keyFingerprint!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 3) != 0 {serializeInt256(publicKey!, buffer: buffer, boxed: false)}
|
||||||
|
if Int(flags) & Int(1 << 3) != 0 {serializeBytes(block!, buffer: buffer, boxed: false)}
|
||||||
|
if Int(flags) & Int(1 << 4) != 0 {serializeInt32(inviteMsgId!, buffer: buffer, boxed: false)}
|
||||||
params.serialize(buffer, true)
|
params.serialize(buffer, true)
|
||||||
return (FunctionDescription(name: "phone.joinGroupCall", parameters: [("flags", String(describing: flags)), ("call", String(describing: call)), ("joinAs", String(describing: joinAs)), ("inviteHash", String(describing: inviteHash)), ("keyFingerprint", String(describing: keyFingerprint)), ("params", String(describing: params))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
return (FunctionDescription(name: "phone.joinGroupCall", parameters: [("flags", String(describing: flags)), ("call", String(describing: call)), ("joinAs", String(describing: joinAs)), ("inviteHash", String(describing: inviteHash)), ("publicKey", String(describing: publicKey)), ("block", String(describing: block)), ("inviteMsgId", String(describing: inviteMsgId)), ("params", String(describing: params))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||||
let reader = BufferReader(buffer)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.Updates?
|
var result: Api.Updates?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
@ -10185,16 +10256,15 @@ public extension Api.functions.phone {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public extension Api.functions.phone {
|
public extension Api.functions.phone {
|
||||||
static func requestCall(flags: Int32, userId: Api.InputUser, conferenceCall: Api.InputGroupCall?, randomId: Int32, gAHash: Buffer, `protocol`: Api.PhoneCallProtocol) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.PhoneCall>) {
|
static func requestCall(flags: Int32, userId: Api.InputUser, randomId: Int32, gAHash: Buffer, `protocol`: Api.PhoneCallProtocol) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.PhoneCall>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(-1497079796)
|
buffer.appendInt32(1124046573)
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
userId.serialize(buffer, true)
|
userId.serialize(buffer, true)
|
||||||
if Int(flags) & Int(1 << 1) != 0 {conferenceCall!.serialize(buffer, true)}
|
|
||||||
serializeInt32(randomId, buffer: buffer, boxed: false)
|
serializeInt32(randomId, buffer: buffer, boxed: false)
|
||||||
serializeBytes(gAHash, buffer: buffer, boxed: false)
|
serializeBytes(gAHash, buffer: buffer, boxed: false)
|
||||||
`protocol`.serialize(buffer, true)
|
`protocol`.serialize(buffer, true)
|
||||||
return (FunctionDescription(name: "phone.requestCall", parameters: [("flags", String(describing: flags)), ("userId", String(describing: userId)), ("conferenceCall", String(describing: conferenceCall)), ("randomId", String(describing: randomId)), ("gAHash", String(describing: gAHash)), ("`protocol`", String(describing: `protocol`))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.PhoneCall? in
|
return (FunctionDescription(name: "phone.requestCall", parameters: [("flags", String(describing: flags)), ("userId", String(describing: userId)), ("randomId", String(describing: randomId)), ("gAHash", String(describing: gAHash)), ("`protocol`", String(describing: `protocol`))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.PhoneCall? in
|
||||||
let reader = BufferReader(buffer)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.phone.PhoneCall?
|
var result: Api.phone.PhoneCall?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
@ -10252,6 +10322,22 @@ public extension Api.functions.phone {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public extension Api.functions.phone {
|
||||||
|
static func sendConferenceCallBroadcast(call: Api.InputGroupCall, block: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||||
|
let buffer = Buffer()
|
||||||
|
buffer.appendInt32(-965732096)
|
||||||
|
call.serialize(buffer, true)
|
||||||
|
serializeBytes(block, buffer: buffer, boxed: false)
|
||||||
|
return (FunctionDescription(name: "phone.sendConferenceCallBroadcast", parameters: [("call", String(describing: call)), ("block", String(describing: block))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||||
|
let reader = BufferReader(buffer)
|
||||||
|
var result: Api.Updates?
|
||||||
|
if let signature = reader.readInt32() {
|
||||||
|
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
public extension Api.functions.phone {
|
public extension Api.functions.phone {
|
||||||
static func sendSignalingData(peer: Api.InputPhoneCall, data: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
static func sendSignalingData(peer: Api.InputPhoneCall, data: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
|
@ -992,14 +992,14 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
public extension Api {
|
public extension Api {
|
||||||
enum GroupCall: TypeConstructorDescription {
|
enum GroupCall: TypeConstructorDescription {
|
||||||
case groupCall(flags: Int32, id: Int64, accessHash: Int64, participantsCount: Int32, title: String?, streamDcId: Int32?, recordStartDate: Int32?, scheduleDate: Int32?, unmutedVideoCount: Int32?, unmutedVideoLimit: Int32, version: Int32, conferenceFromCall: Int64?)
|
case groupCall(flags: Int32, id: Int64, accessHash: Int64, participantsCount: Int32, title: String?, streamDcId: Int32?, recordStartDate: Int32?, scheduleDate: Int32?, unmutedVideoCount: Int32?, unmutedVideoLimit: Int32, version: Int32)
|
||||||
case groupCallDiscarded(id: Int64, accessHash: Int64, duration: Int32)
|
case groupCallDiscarded(id: Int64, accessHash: Int64, duration: Int32)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
case .groupCall(let flags, let id, let accessHash, let participantsCount, let title, let streamDcId, let recordStartDate, let scheduleDate, let unmutedVideoCount, let unmutedVideoLimit, let version, let conferenceFromCall):
|
case .groupCall(let flags, let id, let accessHash, let participantsCount, let title, let streamDcId, let recordStartDate, let scheduleDate, let unmutedVideoCount, let unmutedVideoLimit, let version):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(-839330845)
|
buffer.appendInt32(-711498484)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt64(id, buffer: buffer, boxed: false)
|
serializeInt64(id, buffer: buffer, boxed: false)
|
||||||
@ -1012,7 +1012,6 @@ public extension Api {
|
|||||||
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(unmutedVideoCount!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(unmutedVideoCount!, buffer: buffer, boxed: false)}
|
||||||
serializeInt32(unmutedVideoLimit, buffer: buffer, boxed: false)
|
serializeInt32(unmutedVideoLimit, buffer: buffer, boxed: false)
|
||||||
serializeInt32(version, buffer: buffer, boxed: false)
|
serializeInt32(version, buffer: buffer, boxed: false)
|
||||||
if Int(flags) & Int(1 << 14) != 0 {serializeInt64(conferenceFromCall!, buffer: buffer, boxed: false)}
|
|
||||||
break
|
break
|
||||||
case .groupCallDiscarded(let id, let accessHash, let duration):
|
case .groupCallDiscarded(let id, let accessHash, let duration):
|
||||||
if boxed {
|
if boxed {
|
||||||
@ -1027,8 +1026,8 @@ public extension Api {
|
|||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
switch self {
|
||||||
case .groupCall(let flags, let id, let accessHash, let participantsCount, let title, let streamDcId, let recordStartDate, let scheduleDate, let unmutedVideoCount, let unmutedVideoLimit, let version, let conferenceFromCall):
|
case .groupCall(let flags, let id, let accessHash, let participantsCount, let title, let streamDcId, let recordStartDate, let scheduleDate, let unmutedVideoCount, let unmutedVideoLimit, let version):
|
||||||
return ("groupCall", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("participantsCount", participantsCount as Any), ("title", title as Any), ("streamDcId", streamDcId as Any), ("recordStartDate", recordStartDate as Any), ("scheduleDate", scheduleDate as Any), ("unmutedVideoCount", unmutedVideoCount as Any), ("unmutedVideoLimit", unmutedVideoLimit as Any), ("version", version as Any), ("conferenceFromCall", conferenceFromCall as Any)])
|
return ("groupCall", [("flags", flags as Any), ("id", id as Any), ("accessHash", accessHash as Any), ("participantsCount", participantsCount as Any), ("title", title as Any), ("streamDcId", streamDcId as Any), ("recordStartDate", recordStartDate as Any), ("scheduleDate", scheduleDate as Any), ("unmutedVideoCount", unmutedVideoCount as Any), ("unmutedVideoLimit", unmutedVideoLimit as Any), ("version", version as Any)])
|
||||||
case .groupCallDiscarded(let id, let accessHash, let duration):
|
case .groupCallDiscarded(let id, let accessHash, let duration):
|
||||||
return ("groupCallDiscarded", [("id", id as Any), ("accessHash", accessHash as Any), ("duration", duration as Any)])
|
return ("groupCallDiscarded", [("id", id as Any), ("accessHash", accessHash as Any), ("duration", duration as Any)])
|
||||||
}
|
}
|
||||||
@ -1057,8 +1056,6 @@ public extension Api {
|
|||||||
_10 = reader.readInt32()
|
_10 = reader.readInt32()
|
||||||
var _11: Int32?
|
var _11: Int32?
|
||||||
_11 = reader.readInt32()
|
_11 = reader.readInt32()
|
||||||
var _12: Int64?
|
|
||||||
if Int(_1!) & Int(1 << 14) != 0 {_12 = reader.readInt64() }
|
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = _3 != nil
|
||||||
@ -1070,9 +1067,8 @@ public extension Api {
|
|||||||
let _c9 = (Int(_1!) & Int(1 << 10) == 0) || _9 != nil
|
let _c9 = (Int(_1!) & Int(1 << 10) == 0) || _9 != nil
|
||||||
let _c10 = _10 != nil
|
let _c10 = _10 != nil
|
||||||
let _c11 = _11 != nil
|
let _c11 = _11 != nil
|
||||||
let _c12 = (Int(_1!) & Int(1 << 14) == 0) || _12 != nil
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
|
return Api.GroupCall.groupCall(flags: _1!, id: _2!, accessHash: _3!, participantsCount: _4!, title: _5, streamDcId: _6, recordStartDate: _7, scheduleDate: _8, unmutedVideoCount: _9, unmutedVideoLimit: _10!, version: _11!)
|
||||||
return Api.GroupCall.groupCall(flags: _1!, id: _2!, accessHash: _3!, participantsCount: _4!, title: _5, streamDcId: _6, recordStartDate: _7, scheduleDate: _8, unmutedVideoCount: _9, unmutedVideoLimit: _10!, version: _11!, conferenceFromCall: _12)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -3,13 +3,48 @@ import Foundation
|
|||||||
public struct Int128 {
|
public struct Int128 {
|
||||||
public var _0: Int64
|
public var _0: Int64
|
||||||
public var _1: Int64
|
public var _1: Int64
|
||||||
|
|
||||||
|
public init(_0: Int64, _1: Int64) {
|
||||||
|
self._0 = _0
|
||||||
|
self._1 = _1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Int256 {
|
public struct Int256: Equatable, CustomStringConvertible {
|
||||||
public var _0: Int64
|
public var _0: Int64
|
||||||
public var _1: Int64
|
public var _1: Int64
|
||||||
public var _2: Int64
|
public var _2: Int64
|
||||||
public var _3: Int64
|
public var _3: Int64
|
||||||
|
|
||||||
|
public init(_0: Int64, _1: Int64, _2: Int64, _3: Int64) {
|
||||||
|
self._0 = _0
|
||||||
|
self._1 = _1
|
||||||
|
self._2 = _2
|
||||||
|
self._3 = _3
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
var data = Data(count: 32)
|
||||||
|
data.withUnsafeMutableBytes { buffer in
|
||||||
|
if let baseAddress = buffer.baseAddress {
|
||||||
|
let int64Buffer = baseAddress.assumingMemoryBound(to: Int64.self)
|
||||||
|
int64Buffer[0] = self._0
|
||||||
|
int64Buffer[1] = self._1
|
||||||
|
int64Buffer[2] = self._2
|
||||||
|
int64Buffer[3] = self._3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let hexString = NSMutableString()
|
||||||
|
data.withUnsafeBytes { rawBytes -> Void in
|
||||||
|
let bytes = rawBytes.baseAddress!.assumingMemoryBound(to: UInt8.self)
|
||||||
|
for i in 0 ..< data.count {
|
||||||
|
hexString.appendFormat("%02x", UInt(bytes.advanced(by: i).pointee))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hexString as String
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serializeInt32(_ value: Int32, buffer: Buffer, boxed: Bool) {
|
func serializeInt32(_ value: Int32, buffer: Buffer, boxed: Bool) {
|
||||||
|
@ -39,6 +39,7 @@ private func presentLiveLocationController(context: AccountContext, peerId: Peer
|
|||||||
}, openUrl: { _ in
|
}, openUrl: { _ in
|
||||||
}, openPeer: { peer, navigation in
|
}, openPeer: { peer, navigation in
|
||||||
}, callPeer: { _, _ in
|
}, callPeer: { _, _ in
|
||||||
|
}, openConferenceCall: { _ in
|
||||||
}, enqueueMessage: { message in
|
}, enqueueMessage: { message in
|
||||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
|
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
|
||||||
}, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in
|
}, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in
|
||||||
@ -469,7 +470,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if groupCallPanelData.info.scheduleTimestamp != nil && !groupCallPanelData.info.subscribedToScheduled {
|
if groupCallPanelData.info.scheduleTimestamp != nil && !groupCallPanelData.info.subscribedToScheduled {
|
||||||
let _ = self.context.engine.calls.toggleScheduledGroupCallSubscription(peerId: groupCallPanelData.peerId, callId: groupCallPanelData.info.id, accessHash: groupCallPanelData.info.accessHash, subscribe: true).startStandalone()
|
let _ = self.context.engine.calls.toggleScheduledGroupCallSubscription(peerId: groupCallPanelData.peerId, reference: .id(id: groupCallPanelData.info.id, accessHash: groupCallPanelData.info.accessHash), subscribe: true).startStandalone()
|
||||||
|
|
||||||
let controller = UndoOverlayController(
|
let controller = UndoOverlayController(
|
||||||
presentationData: presentationData,
|
presentationData: presentationData,
|
||||||
|
@ -120,6 +120,7 @@ swift_library(
|
|||||||
"//submodules/DirectMediaImageCache",
|
"//submodules/DirectMediaImageCache",
|
||||||
"//submodules/FastBlur",
|
"//submodules/FastBlur",
|
||||||
"//submodules/InviteLinksUI",
|
"//submodules/InviteLinksUI",
|
||||||
|
"//third-party/td:TdBinding",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -233,7 +233,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
public let internalId: CallSessionInternalId
|
public let internalId: CallSessionInternalId
|
||||||
public let peerId: EnginePeer.Id
|
public let peerId: EnginePeer.Id
|
||||||
public let isOutgoing: Bool
|
public let isOutgoing: Bool
|
||||||
private let isIncomingConference: Bool
|
private let incomingConferenceSource: EngineMessage.Id?
|
||||||
public var isVideo: Bool
|
public var isVideo: Bool
|
||||||
public var isVideoPossible: Bool
|
public var isVideoPossible: Bool
|
||||||
private let enableStunMarking: Bool
|
private let enableStunMarking: Bool
|
||||||
@ -354,6 +354,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
private var conferenceCallDisposable: Disposable?
|
private var conferenceCallDisposable: Disposable?
|
||||||
private var upgradedToConferenceCompletions = Bag<(PresentationGroupCall) -> Void>()
|
private var upgradedToConferenceCompletions = Bag<(PresentationGroupCall) -> Void>()
|
||||||
|
|
||||||
|
private var isAcceptingIncomingConference: Bool = false
|
||||||
private var waitForConferenceCallReadyDisposable: Disposable?
|
private var waitForConferenceCallReadyDisposable: Disposable?
|
||||||
private let conferenceStatePromise = ValuePromise<PresentationCallConferenceState?>(nil)
|
private let conferenceStatePromise = ValuePromise<PresentationCallConferenceState?>(nil)
|
||||||
public private(set) var conferenceStateValue: PresentationCallConferenceState? {
|
public private(set) var conferenceStateValue: PresentationCallConferenceState? {
|
||||||
@ -386,7 +387,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
internalId: CallSessionInternalId,
|
internalId: CallSessionInternalId,
|
||||||
peerId: EnginePeer.Id,
|
peerId: EnginePeer.Id,
|
||||||
isOutgoing: Bool,
|
isOutgoing: Bool,
|
||||||
isIncomingConference: Bool,
|
incomingConferenceSource: EngineMessage.Id?,
|
||||||
peer: EnginePeer?,
|
peer: EnginePeer?,
|
||||||
proxyServer: ProxyServerSettings?,
|
proxyServer: ProxyServerSettings?,
|
||||||
auxiliaryServers: [CallAuxiliaryServer],
|
auxiliaryServers: [CallAuxiliaryServer],
|
||||||
@ -421,7 +422,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
self.internalId = internalId
|
self.internalId = internalId
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.isOutgoing = isOutgoing
|
self.isOutgoing = isOutgoing
|
||||||
self.isIncomingConference = isIncomingConference
|
self.incomingConferenceSource = incomingConferenceSource
|
||||||
self.isVideo = initialState?.type == .video
|
self.isVideo = initialState?.type == .video
|
||||||
self.isVideoPossible = isVideoPossible
|
self.isVideoPossible = isVideoPossible
|
||||||
self.enableStunMarking = enableStunMarking
|
self.enableStunMarking = enableStunMarking
|
||||||
@ -731,8 +732,11 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
switch previous.state {
|
switch previous.state {
|
||||||
case .active:
|
case .active:
|
||||||
wasActive = true
|
wasActive = true
|
||||||
case .terminated, .dropping:
|
case let .terminated(_, reason, _):
|
||||||
|
if case .ended(.switchedToConference) = reason {
|
||||||
|
} else {
|
||||||
wasTerminated = true
|
wasTerminated = true
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -858,22 +862,23 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
self.callWasActive = true
|
self.callWasActive = true
|
||||||
presentationState = PresentationCallState(state: .connecting(nil), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .connecting(nil), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
||||||
case let .dropping(reason):
|
case let .dropping(reason):
|
||||||
|
if case .ended(.switchedToConference) = reason {
|
||||||
|
} else {
|
||||||
presentationState = PresentationCallState(state: .terminating(reason), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .terminating(reason), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
||||||
|
}
|
||||||
case let .terminated(id, reason, options):
|
case let .terminated(id, reason, options):
|
||||||
presentationState = PresentationCallState(state: .terminated(id, reason, self.callWasActive && (options.contains(.reportRating) || self.shouldPresentCallRating)), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .terminated(id, reason, self.callWasActive && (options.contains(.reportRating) || self.shouldPresentCallRating)), videoState: mappedVideoState, remoteVideoState: .inactive, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
||||||
case let .requesting(ringing, _):
|
case let .requesting(ringing):
|
||||||
presentationState = PresentationCallState(state: .requesting(ringing), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .requesting(ringing), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
||||||
case let .active(_, _, keyVisualHash, _, _, _, _, _, _, _), let .switchedToConference(_, keyVisualHash, _):
|
case .active(_, _, _, _, _, _, _, _), .switchedToConference:
|
||||||
self.callWasActive = true
|
self.callWasActive = true
|
||||||
|
|
||||||
var isConference = false
|
var isConference = false
|
||||||
if case let .active(_, _, _, _, _, _, _, _, conferenceCall, _) = sessionState.state {
|
if case .switchedToConference = sessionState.state {
|
||||||
isConference = conferenceCall != nil
|
|
||||||
} else if case .switchedToConference = sessionState.state {
|
|
||||||
isConference = true
|
isConference = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if let callContextState = callContextState, !isConference {
|
if let callContextState = callContextState, !isConference, case let .active(_, _, keyVisualHash, _, _, _, _, _) = sessionState.state {
|
||||||
switch callContextState.state {
|
switch callContextState.state {
|
||||||
case .initializing:
|
case .initializing:
|
||||||
presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
||||||
@ -899,54 +904,67 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
}
|
}
|
||||||
presentationState = PresentationCallState(state: .reconnecting(timestamp, reception, keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .reconnecting(timestamp, reception, keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
||||||
}
|
}
|
||||||
} else if !isConference {
|
} else if !isConference, case let .active(_, _, keyVisualHash, _, _, _, _, _) = sessionState.state {
|
||||||
presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
presentationState = PresentationCallState(state: .connecting(keyVisualHash), videoState: mappedVideoState, remoteVideoState: mappedRemoteVideoState, remoteAudioState: mappedRemoteAudioState, remoteBatteryLevel: mappedRemoteBatteryLevel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var conferenceCallData: (key: Data, keyVisualHash: Data, conferenceCall: GroupCallReference)?
|
var conferenceCallData: InternalGroupCallReference?
|
||||||
var conferenceFromCallId: CallId?
|
if let incomingConferenceSource = self.incomingConferenceSource {
|
||||||
switch sessionState.state {
|
if self.isAcceptingIncomingConference {
|
||||||
case let .active(id, key, keyVisualHash, _, _, _, _, _, conferenceCall, isIncomingConference):
|
conferenceCallData = .message(id: incomingConferenceSource)
|
||||||
if let conferenceCall, !isIncomingConference {
|
|
||||||
conferenceFromCallId = id
|
|
||||||
conferenceCallData = (key, keyVisualHash, conferenceCall)
|
|
||||||
}
|
}
|
||||||
case let .switchedToConference(key, keyVisualHash, conferenceCall):
|
} else {
|
||||||
conferenceCallData = (key, keyVisualHash, conferenceCall)
|
switch sessionState.state {
|
||||||
|
case let .switchedToConference(slug):
|
||||||
|
conferenceCallData = .link(slug: slug)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let (key, _, conferenceCall) = conferenceCallData {
|
if let conferenceCallData {
|
||||||
if self.conferenceCallDisposable == nil {
|
if self.conferenceCallDisposable == nil {
|
||||||
self.conferenceCallDisposable = EmptyDisposable
|
let conferenceCallSignal = self.context.engine.calls.getCurrentGroupCall(reference: conferenceCallData)
|
||||||
|
self.conferenceCallDisposable = (conferenceCallSignal
|
||||||
#if DEBUG
|
|> deliverOnMainQueue).startStrict(next: { [weak self] groupCall in
|
||||||
print("Switching to conference call with encryption key: \(key.base64EncodedString())")
|
guard let self else {
|
||||||
#endif
|
return
|
||||||
|
}
|
||||||
|
let keyPair: TelegramKeyPair? = TelegramE2EEncryptionProviderImpl.shared.generateKeyPair()
|
||||||
|
guard let keyPair, let groupCall else {
|
||||||
|
self.updateSessionState(sessionState: CallSession(
|
||||||
|
id: self.internalId,
|
||||||
|
stableId: nil,
|
||||||
|
isOutgoing: false,
|
||||||
|
type: .audio,
|
||||||
|
state: .terminated(id: nil, reason: .error(.generic), options: CallTerminationOptions()),
|
||||||
|
isVideoPossible: true
|
||||||
|
),
|
||||||
|
callContextState: nil, reception: nil, audioSessionControl: self.audioSessionControl)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let conferenceCall = PresentationGroupCallImpl(
|
let conferenceCall = PresentationGroupCallImpl(
|
||||||
accountContext: self.context,
|
accountContext: self.context,
|
||||||
audioSession: self.audioSession,
|
audioSession: self.audioSession,
|
||||||
callKitIntegration: self.callKitIntegration,
|
callKitIntegration: self.callKitIntegration,
|
||||||
getDeviceAccessData: self.getDeviceAccessData,
|
getDeviceAccessData: self.getDeviceAccessData,
|
||||||
initialCall: EngineGroupCallDescription(
|
initialCall: (EngineGroupCallDescription(
|
||||||
id: conferenceCall.id,
|
id: groupCall.info.id,
|
||||||
accessHash: conferenceCall.accessHash,
|
accessHash: groupCall.info.accessHash,
|
||||||
title: nil,
|
title: nil,
|
||||||
scheduleTimestamp: nil,
|
scheduleTimestamp: nil,
|
||||||
subscribedToScheduled: false,
|
subscribedToScheduled: false,
|
||||||
isStream: false
|
isStream: false
|
||||||
),
|
), conferenceCallData),
|
||||||
internalId: CallSessionInternalId(),
|
internalId: CallSessionInternalId(),
|
||||||
peerId: nil,
|
peerId: nil,
|
||||||
isChannel: false,
|
isChannel: false,
|
||||||
invite: nil,
|
invite: nil,
|
||||||
joinAsPeerId: nil,
|
joinAsPeerId: nil,
|
||||||
isStream: false,
|
isStream: false,
|
||||||
encryptionKey: (key, 1),
|
keyPair: keyPair,
|
||||||
conferenceFromCallId: conferenceFromCallId,
|
|
||||||
conferenceSourceId: self.internalId,
|
conferenceSourceId: self.internalId,
|
||||||
isConference: true,
|
isConference: true,
|
||||||
sharedAudioContext: self.sharedAudioContext
|
sharedAudioContext: self.sharedAudioContext
|
||||||
@ -1045,6 +1063,20 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
f(conferenceCall)
|
f(conferenceCall)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}, error: { [weak self] _ in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.updateSessionState(sessionState: CallSession(
|
||||||
|
id: self.internalId,
|
||||||
|
stableId: nil,
|
||||||
|
isOutgoing: false,
|
||||||
|
type: .audio,
|
||||||
|
state: .terminated(id: nil, reason: .error(.generic), options: CallTerminationOptions()),
|
||||||
|
isVideoPossible: true
|
||||||
|
),
|
||||||
|
callContextState: nil, reception: nil, audioSessionControl: self.audioSessionControl)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1053,7 +1085,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
if let _ = audioSessionControl {
|
if let _ = audioSessionControl {
|
||||||
self.audioSessionShouldBeActive.set(true)
|
self.audioSessionShouldBeActive.set(true)
|
||||||
}
|
}
|
||||||
case let .active(id, key, _, connections, maxLayer, version, customParameters, allowsP2P, _, _):
|
case let .active(id, key, _, connections, maxLayer, version, customParameters, allowsP2P):
|
||||||
self.audioSessionShouldBeActive.set(true)
|
self.audioSessionShouldBeActive.set(true)
|
||||||
|
|
||||||
if conferenceCallData != nil {
|
if conferenceCallData != nil {
|
||||||
@ -1150,9 +1182,14 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
var terminating = false
|
var terminating = false
|
||||||
if case .terminated = sessionState.state {
|
if case .terminated = sessionState.state {
|
||||||
terminating = true
|
terminating = true
|
||||||
} else if case .dropping = sessionState.state {
|
} else if case let .dropping(reason) = sessionState.state {
|
||||||
|
switch reason {
|
||||||
|
case .ended(.switchedToConference):
|
||||||
|
break
|
||||||
|
default:
|
||||||
terminating = true
|
terminating = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if terminating, !wasTerminated {
|
if terminating, !wasTerminated {
|
||||||
if !self.didSetCanBeRemoved {
|
if !self.didSetCanBeRemoved {
|
||||||
@ -1180,9 +1217,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var isConference = false
|
var isConference = false
|
||||||
if case let .active(_, _, _, _, _, _, _, _, conferenceCall, _) = sessionState.state {
|
if case .switchedToConference = sessionState.state {
|
||||||
isConference = conferenceCall != nil
|
|
||||||
} else if case .switchedToConference = sessionState.state {
|
|
||||||
isConference = true
|
isConference = true
|
||||||
}
|
}
|
||||||
if self.conferenceCallImpl != nil {
|
if self.conferenceCallImpl != nil {
|
||||||
@ -1191,7 +1226,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
if self.conferenceStateValue != nil {
|
if self.conferenceStateValue != nil {
|
||||||
isConference = true
|
isConference = true
|
||||||
}
|
}
|
||||||
if self.isIncomingConference {
|
if self.incomingConferenceSource != nil {
|
||||||
isConference = true
|
isConference = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1303,11 +1338,22 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if value {
|
if value {
|
||||||
if strongSelf.isIncomingConference {
|
if strongSelf.incomingConferenceSource != nil {
|
||||||
strongSelf.conferenceStateValue = .preparing
|
strongSelf.conferenceStateValue = .preparing
|
||||||
|
strongSelf.isAcceptingIncomingConference = true
|
||||||
|
strongSelf.updateSessionState(sessionState: CallSession(
|
||||||
|
id: strongSelf.internalId,
|
||||||
|
stableId: nil,
|
||||||
|
isOutgoing: false,
|
||||||
|
type: .audio,
|
||||||
|
state: .ringing,
|
||||||
|
isVideoPossible: true
|
||||||
|
),
|
||||||
|
callContextState: nil, reception: nil, audioSessionControl: strongSelf.audioSessionControl)
|
||||||
|
} else {
|
||||||
|
strongSelf.callSessionManager.accept(internalId: strongSelf.internalId)
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.callSessionManager.accept(internalId: strongSelf.internalId)
|
|
||||||
if !fromCallKitAction {
|
if !fromCallKitAction {
|
||||||
strongSelf.callKitIntegration?.answerCall(uuid: strongSelf.internalId)
|
strongSelf.callKitIntegration?.answerCall(uuid: strongSelf.internalId)
|
||||||
}
|
}
|
||||||
@ -1316,11 +1362,22 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
if strongSelf.isIncomingConference {
|
if strongSelf.incomingConferenceSource != nil {
|
||||||
strongSelf.conferenceStateValue = .preparing
|
strongSelf.conferenceStateValue = .preparing
|
||||||
|
strongSelf.isAcceptingIncomingConference = true
|
||||||
|
strongSelf.updateSessionState(sessionState: CallSession(
|
||||||
|
id: strongSelf.internalId,
|
||||||
|
stableId: nil,
|
||||||
|
isOutgoing: false,
|
||||||
|
type: .audio,
|
||||||
|
state: .ringing,
|
||||||
|
isVideoPossible: true
|
||||||
|
),
|
||||||
|
callContextState: nil, reception: nil, audioSessionControl: strongSelf.audioSessionControl)
|
||||||
|
} else {
|
||||||
|
strongSelf.callSessionManager.accept(internalId: strongSelf.internalId)
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.callSessionManager.accept(internalId: strongSelf.internalId)
|
|
||||||
if !fromCallKitAction {
|
if !fromCallKitAction {
|
||||||
strongSelf.callKitIntegration?.answerCall(uuid: strongSelf.internalId)
|
strongSelf.callKitIntegration?.answerCall(uuid: strongSelf.internalId)
|
||||||
}
|
}
|
||||||
@ -1336,6 +1393,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
return .single(true)
|
return .single(true)
|
||||||
}
|
}
|
||||||
let debugLogValue = Promise<String?>()
|
let debugLogValue = Promise<String?>()
|
||||||
|
|
||||||
self.callSessionManager.drop(internalId: self.internalId, reason: .hangUp, debugLog: debugLogValue.get())
|
self.callSessionManager.drop(internalId: self.internalId, reason: .hangUp, debugLog: debugLogValue.get())
|
||||||
self.ongoingContext?.stop(debugLogValue: debugLogValue)
|
self.ongoingContext?.stop(debugLogValue: debugLogValue)
|
||||||
|
|
||||||
|
@ -348,7 +348,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
internalId: firstState.2.id,
|
internalId: firstState.2.id,
|
||||||
peerId: firstState.2.peerId,
|
peerId: firstState.2.peerId,
|
||||||
isOutgoing: false,
|
isOutgoing: false,
|
||||||
isIncomingConference: firstState.2.isIncomingConference,
|
incomingConferenceSource: firstState.2.conferenceSource,
|
||||||
peer: EnginePeer(firstState.1),
|
peer: EnginePeer(firstState.1),
|
||||||
proxyServer: strongSelf.proxyServer,
|
proxyServer: strongSelf.proxyServer,
|
||||||
auxiliaryServers: [],
|
auxiliaryServers: [],
|
||||||
@ -571,7 +571,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
|> mapToSignal { areVideoCallsAvailable -> Signal<CallSessionInternalId, NoError> in
|
|> mapToSignal { areVideoCallsAvailable -> Signal<CallSessionInternalId, NoError> in
|
||||||
let isVideoPossible: Bool = areVideoCallsAvailable
|
let isVideoPossible: Bool = areVideoCallsAvailable
|
||||||
|
|
||||||
return context.account.callSessionManager.request(peerId: peerId, isVideo: isVideo, enableVideo: isVideoPossible, conferenceCall: nil, internalId: internalId)
|
return context.account.callSessionManager.request(peerId: peerId, isVideo: isVideo, enableVideo: isVideoPossible, internalId: internalId)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (combineLatest(queue: .mainQueue(),
|
return (combineLatest(queue: .mainQueue(),
|
||||||
@ -616,7 +616,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
internalId: internalId,
|
internalId: internalId,
|
||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
isOutgoing: true,
|
isOutgoing: true,
|
||||||
isIncomingConference: false,
|
incomingConferenceSource: nil,
|
||||||
peer: nil,
|
peer: nil,
|
||||||
proxyServer: strongSelf.proxyServer,
|
proxyServer: strongSelf.proxyServer,
|
||||||
auxiliaryServers: [],
|
auxiliaryServers: [],
|
||||||
@ -847,8 +847,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
invite: nil,
|
invite: nil,
|
||||||
joinAsPeerId: nil,
|
joinAsPeerId: nil,
|
||||||
isStream: false,
|
isStream: false,
|
||||||
encryptionKey: nil,
|
keyPair: nil,
|
||||||
conferenceFromCallId: nil,
|
|
||||||
conferenceSourceId: nil,
|
conferenceSourceId: nil,
|
||||||
isConference: false,
|
isConference: false,
|
||||||
sharedAudioContext: nil
|
sharedAudioContext: nil
|
||||||
@ -1067,19 +1066,53 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
audioSession: self.audioSession,
|
audioSession: self.audioSession,
|
||||||
callKitIntegration: nil,
|
callKitIntegration: nil,
|
||||||
getDeviceAccessData: self.getDeviceAccessData,
|
getDeviceAccessData: self.getDeviceAccessData,
|
||||||
initialCall: initialCall,
|
initialCall: (initialCall, .id(id: initialCall.id, accessHash: initialCall.accessHash)),
|
||||||
internalId: internalId,
|
internalId: internalId,
|
||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
isChannel: isChannel,
|
isChannel: isChannel,
|
||||||
invite: invite,
|
invite: invite,
|
||||||
joinAsPeerId: joinAsPeerId,
|
joinAsPeerId: joinAsPeerId,
|
||||||
isStream: initialCall.isStream ?? false,
|
isStream: initialCall.isStream ?? false,
|
||||||
encryptionKey: nil,
|
keyPair: nil,
|
||||||
conferenceFromCallId: nil,
|
|
||||||
conferenceSourceId: nil,
|
conferenceSourceId: nil,
|
||||||
isConference: false,
|
isConference: false,
|
||||||
sharedAudioContext: nil
|
sharedAudioContext: nil
|
||||||
)
|
)
|
||||||
self.updateCurrentGroupCall(.group(call))
|
self.updateCurrentGroupCall(.group(call))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func joinConferenceCall(
|
||||||
|
accountContext: AccountContext,
|
||||||
|
initialCall: EngineGroupCallDescription,
|
||||||
|
reference: InternalGroupCallReference,
|
||||||
|
mode: JoinConferenceCallMode
|
||||||
|
) {
|
||||||
|
let keyPair: TelegramKeyPair
|
||||||
|
switch mode {
|
||||||
|
case .joining:
|
||||||
|
guard let keyPairValue = TelegramE2EEncryptionProviderImpl.shared.generateKeyPair() else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
keyPair = keyPairValue
|
||||||
|
}
|
||||||
|
|
||||||
|
let call = PresentationGroupCallImpl(
|
||||||
|
accountContext: accountContext,
|
||||||
|
audioSession: self.audioSession,
|
||||||
|
callKitIntegration: nil,
|
||||||
|
getDeviceAccessData: self.getDeviceAccessData,
|
||||||
|
initialCall: (initialCall, reference),
|
||||||
|
internalId: CallSessionInternalId(),
|
||||||
|
peerId: nil,
|
||||||
|
isChannel: false,
|
||||||
|
invite: nil,
|
||||||
|
joinAsPeerId: nil,
|
||||||
|
isStream: false,
|
||||||
|
keyPair: keyPair,
|
||||||
|
conferenceSourceId: nil,
|
||||||
|
isConference: true,
|
||||||
|
sharedAudioContext: nil
|
||||||
|
)
|
||||||
|
self.updateCurrentGroupCall(.group(call))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import DeviceProximity
|
|||||||
import UndoUI
|
import UndoUI
|
||||||
import TemporaryCachedPeerDataManager
|
import TemporaryCachedPeerDataManager
|
||||||
import CallsEmoji
|
import CallsEmoji
|
||||||
|
import TdBinding
|
||||||
|
|
||||||
private extension GroupCallParticipantsContext.Participant {
|
private extension GroupCallParticipantsContext.Participant {
|
||||||
var allSsrcs: Set<UInt32> {
|
var allSsrcs: Set<UInt32> {
|
||||||
@ -94,7 +95,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
|||||||
|
|
||||||
public init(account: Account, engine: TelegramEngine, peerId: PeerId?, isChannel: Bool, call: EngineGroupCallDescription) {
|
public init(account: Account, engine: TelegramEngine, peerId: PeerId?, isChannel: Bool, call: EngineGroupCallDescription) {
|
||||||
self.panelDataPromise.set(.single(nil))
|
self.panelDataPromise.set(.single(nil))
|
||||||
let state = engine.calls.getGroupCallParticipants(callId: call.id, accessHash: call.accessHash, offset: "", ssrcs: [], limit: 100, sortAscending: nil)
|
let state = engine.calls.getGroupCallParticipants(reference: .id(id: call.id, accessHash: call.accessHash), offset: "", ssrcs: [], limit: 100, sortAscending: nil)
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<GroupCallParticipantsContext.State?, NoError> in
|
|> `catch` { _ -> Signal<GroupCallParticipantsContext.State?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
@ -118,7 +119,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
|||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
myPeerId: account.peerId,
|
myPeerId: account.peerId,
|
||||||
id: call.id,
|
id: call.id,
|
||||||
accessHash: call.accessHash,
|
reference: .id(id: call.id, accessHash: call.accessHash),
|
||||||
state: state,
|
state: state,
|
||||||
previousServiceState: nil
|
previousServiceState: nil
|
||||||
)
|
)
|
||||||
@ -147,7 +148,21 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
|||||||
return GroupCallPanelData(
|
return GroupCallPanelData(
|
||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
isChannel: isChannel,
|
isChannel: isChannel,
|
||||||
info: GroupCallInfo(id: call.id, accessHash: call.accessHash, participantCount: state.totalCount, streamDcId: nil, title: state.title, scheduleTimestamp: state.scheduleTimestamp, subscribedToScheduled: state.subscribedToScheduled, recordingStartTimestamp: nil, sortAscending: state.sortAscending, defaultParticipantsAreMuted: state.defaultParticipantsAreMuted, isVideoEnabled: state.isVideoEnabled, unmutedVideoLimit: state.unmutedVideoLimit, isStream: state.isStream, upgradedPrivateCallId: state.upgradedPrivateCallId),
|
info: GroupCallInfo(
|
||||||
|
id: call.id,
|
||||||
|
accessHash: call.accessHash,
|
||||||
|
participantCount: state.totalCount,
|
||||||
|
streamDcId: nil,
|
||||||
|
title: state.title,
|
||||||
|
scheduleTimestamp: state.scheduleTimestamp,
|
||||||
|
subscribedToScheduled: state.subscribedToScheduled,
|
||||||
|
recordingStartTimestamp: nil,
|
||||||
|
sortAscending: state.sortAscending,
|
||||||
|
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
|
||||||
|
isVideoEnabled: state.isVideoEnabled,
|
||||||
|
unmutedVideoLimit: state.unmutedVideoLimit,
|
||||||
|
isStream: state.isStream
|
||||||
|
),
|
||||||
topParticipants: topParticipants,
|
topParticipants: topParticipants,
|
||||||
participantCount: state.totalCount,
|
participantCount: state.totalCount,
|
||||||
activeSpeakers: activeSpeakers,
|
activeSpeakers: activeSpeakers,
|
||||||
@ -537,11 +552,11 @@ private final class ScreencastInProcessIPCContext: ScreencastIPCContext {
|
|||||||
preferX264: false,
|
preferX264: false,
|
||||||
logPath: "",
|
logPath: "",
|
||||||
onMutedSpeechActivityDetected: { _ in },
|
onMutedSpeechActivityDetected: { _ in },
|
||||||
encryptionKey: nil,
|
|
||||||
isConference: self.isConference,
|
isConference: self.isConference,
|
||||||
audioIsActiveByDefault: true,
|
audioIsActiveByDefault: true,
|
||||||
isStream: false,
|
isStream: false,
|
||||||
sharedAudioDevice: nil
|
sharedAudioDevice: nil,
|
||||||
|
encryptionContext: nil
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.screencastCallContext = screencastCallContext
|
self.screencastCallContext = screencastCallContext
|
||||||
@ -613,10 +628,12 @@ private final class PendingConferenceInvitationContext {
|
|||||||
|
|
||||||
private var didNotifyEnded: Bool = false
|
private var didNotifyEnded: Bool = false
|
||||||
|
|
||||||
init(callSessionManager: CallSessionManager, groupCall: GroupCallReference, encryptionKey: Data, peerId: PeerId, onStateUpdated: @escaping (State) -> Void, onEnded: @escaping (Bool) -> Void) {
|
init(callSessionManager: CallSessionManager, groupCall: GroupCallReference, peerId: PeerId, onStateUpdated: @escaping (State) -> Void, onEnded: @escaping (Bool) -> Void) {
|
||||||
self.callSessionManager = callSessionManager
|
self.callSessionManager = callSessionManager
|
||||||
|
|
||||||
self.requestDisposable = (callSessionManager.request(peerId: peerId, isVideo: false, enableVideo: true, conferenceCall: (groupCall, encryptionKey))
|
preconditionFailure()
|
||||||
|
|
||||||
|
/*self.requestDisposable = (callSessionManager.request(peerId: peerId, isVideo: false, enableVideo: true, conferenceCall: (groupCall, encryptionKey))
|
||||||
|> deliverOnMainQueue).startStrict(next: { [weak self] internalId in
|
|> deliverOnMainQueue).startStrict(next: { [weak self] internalId in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -642,7 +659,7 @@ private final class PendingConferenceInvitationContext {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})*/
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -786,7 +803,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
|
|
||||||
private let getDeviceAccessData: () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void)
|
private let getDeviceAccessData: () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void)
|
||||||
|
|
||||||
private(set) var initialCall: EngineGroupCallDescription?
|
private(set) var initialCall: (description: EngineGroupCallDescription, reference: InternalGroupCallReference)?
|
||||||
public let internalId: CallSessionInternalId
|
public let internalId: CallSessionInternalId
|
||||||
public let peerId: EnginePeer.Id?
|
public let peerId: EnginePeer.Id?
|
||||||
private let isChannel: Bool
|
private let isChannel: Bool
|
||||||
@ -795,10 +812,28 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
private var ignorePreviousJoinAsPeerId: (PeerId, UInt32)?
|
private var ignorePreviousJoinAsPeerId: (PeerId, UInt32)?
|
||||||
private var reconnectingAsPeer: EnginePeer?
|
private var reconnectingAsPeer: EnginePeer?
|
||||||
|
|
||||||
|
public private(set) var callId: Int64?
|
||||||
|
|
||||||
public private(set) var hasVideo: Bool
|
public private(set) var hasVideo: Bool
|
||||||
public private(set) var hasScreencast: Bool
|
public private(set) var hasScreencast: Bool
|
||||||
private let isVideoEnabled: Bool
|
private let isVideoEnabled: Bool
|
||||||
|
|
||||||
|
private let keyPair: TelegramKeyPair?
|
||||||
|
|
||||||
|
private final class E2ECallState {
|
||||||
|
var call: TdCall?
|
||||||
|
var pendingIncomingBroadcastBlocks: [Data] = []
|
||||||
|
}
|
||||||
|
private let e2eCall = Atomic<E2ECallState>(value: E2ECallState())
|
||||||
|
|
||||||
|
private var e2ePoll0Offset: Int?
|
||||||
|
private var e2ePoll0Timer: Foundation.Timer?
|
||||||
|
private var e2ePoll0Disposable: Disposable?
|
||||||
|
|
||||||
|
private var e2ePoll1Offset: Int?
|
||||||
|
private var e2ePoll1Timer: Foundation.Timer?
|
||||||
|
private var e2ePoll1Disposable: Disposable?
|
||||||
|
|
||||||
private var temporaryJoinTimestamp: Int32
|
private var temporaryJoinTimestamp: Int32
|
||||||
private var temporaryActivityTimestamp: Double?
|
private var temporaryActivityTimestamp: Double?
|
||||||
private var temporaryActivityRank: Int?
|
private var temporaryActivityRank: Int?
|
||||||
@ -865,6 +900,11 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
private let isNoiseSuppressionEnabledDisposable = MetaDisposable()
|
private let isNoiseSuppressionEnabledDisposable = MetaDisposable()
|
||||||
|
|
||||||
|
private let e2eEncryptionKeyHashValue = ValuePromise<Data?>(nil)
|
||||||
|
public var e2eEncryptionKeyHash: Signal<Data?, NoError> {
|
||||||
|
return self.e2eEncryptionKeyHashValue.get()
|
||||||
|
}
|
||||||
|
|
||||||
private var isVideoMuted: Bool = false
|
private var isVideoMuted: Bool = false
|
||||||
private let isVideoMutedDisposable = MetaDisposable()
|
private let isVideoMutedDisposable = MetaDisposable()
|
||||||
|
|
||||||
@ -1053,18 +1093,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
private var screencastStateDisposable: Disposable?
|
private var screencastStateDisposable: Disposable?
|
||||||
|
|
||||||
public let isStream: Bool
|
public let isStream: Bool
|
||||||
private let encryptionKey: (key: Data, fingerprint: Int64)?
|
|
||||||
private let sharedAudioContext: SharedCallAudioContext?
|
private let sharedAudioContext: SharedCallAudioContext?
|
||||||
|
|
||||||
private let conferenceFromCallId: CallId?
|
|
||||||
public let isConference: Bool
|
public let isConference: Bool
|
||||||
public var encryptionKeyValue: Data? {
|
|
||||||
if let key = self.encryptionKey?.key {
|
|
||||||
return dataForEmojiRawKey(key)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private let conferenceSourceId: CallSessionInternalId?
|
private let conferenceSourceId: CallSessionInternalId?
|
||||||
public var conferenceSource: CallSessionInternalId? {
|
public var conferenceSource: CallSessionInternalId? {
|
||||||
@ -1085,15 +1116,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
audioSession: ManagedAudioSession,
|
audioSession: ManagedAudioSession,
|
||||||
callKitIntegration: CallKitIntegration?,
|
callKitIntegration: CallKitIntegration?,
|
||||||
getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void),
|
getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void),
|
||||||
initialCall: EngineGroupCallDescription?,
|
initialCall: (description: EngineGroupCallDescription, reference: InternalGroupCallReference)?,
|
||||||
internalId: CallSessionInternalId,
|
internalId: CallSessionInternalId,
|
||||||
peerId: EnginePeer.Id?,
|
peerId: EnginePeer.Id?,
|
||||||
isChannel: Bool,
|
isChannel: Bool,
|
||||||
invite: String?,
|
invite: String?,
|
||||||
joinAsPeerId: EnginePeer.Id?,
|
joinAsPeerId: EnginePeer.Id?,
|
||||||
isStream: Bool,
|
isStream: Bool,
|
||||||
encryptionKey: (key: Data, fingerprint: Int64)?,
|
keyPair: TelegramKeyPair?,
|
||||||
conferenceFromCallId: CallId?,
|
|
||||||
conferenceSourceId: CallSessionInternalId?,
|
conferenceSourceId: CallSessionInternalId?,
|
||||||
isConference: Bool,
|
isConference: Bool,
|
||||||
sharedAudioContext: SharedCallAudioContext?
|
sharedAudioContext: SharedCallAudioContext?
|
||||||
@ -1105,15 +1135,17 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
self.getDeviceAccessData = getDeviceAccessData
|
self.getDeviceAccessData = getDeviceAccessData
|
||||||
|
|
||||||
self.initialCall = initialCall
|
self.initialCall = initialCall
|
||||||
|
self.callId = initialCall?.description.id
|
||||||
|
|
||||||
self.internalId = internalId
|
self.internalId = internalId
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.isChannel = isChannel
|
self.isChannel = isChannel
|
||||||
self.invite = invite
|
self.invite = invite
|
||||||
self.joinAsPeerId = joinAsPeerId ?? accountContext.account.peerId
|
self.joinAsPeerId = joinAsPeerId ?? accountContext.account.peerId
|
||||||
self.schedulePending = initialCall == nil
|
self.schedulePending = initialCall == nil
|
||||||
self.isScheduled = initialCall == nil || initialCall?.scheduleTimestamp != nil
|
self.isScheduled = initialCall == nil || initialCall?.description.scheduleTimestamp != nil
|
||||||
|
|
||||||
self.stateValue = PresentationGroupCallState.initialValue(myPeerId: self.joinAsPeerId, title: initialCall?.title, scheduleTimestamp: initialCall?.scheduleTimestamp, subscribedToScheduled: initialCall?.subscribedToScheduled ?? false)
|
self.stateValue = PresentationGroupCallState.initialValue(myPeerId: self.joinAsPeerId, title: initialCall?.description.title, scheduleTimestamp: initialCall?.description.scheduleTimestamp, subscribedToScheduled: initialCall?.description.subscribedToScheduled ?? false)
|
||||||
self.statePromise = ValuePromise(self.stateValue)
|
self.statePromise = ValuePromise(self.stateValue)
|
||||||
|
|
||||||
self.temporaryJoinTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
self.temporaryJoinTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||||
@ -1122,10 +1154,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
self.hasVideo = false
|
self.hasVideo = false
|
||||||
self.hasScreencast = false
|
self.hasScreencast = false
|
||||||
self.isStream = isStream
|
self.isStream = isStream
|
||||||
self.conferenceFromCallId = conferenceFromCallId
|
|
||||||
self.conferenceSourceId = conferenceSourceId
|
self.conferenceSourceId = conferenceSourceId
|
||||||
self.isConference = isConference
|
self.isConference = isConference
|
||||||
self.encryptionKey = encryptionKey
|
self.keyPair = keyPair
|
||||||
|
|
||||||
var sharedAudioContext = sharedAudioContext
|
var sharedAudioContext = sharedAudioContext
|
||||||
if sharedAudioContext == nil {
|
if sharedAudioContext == nil {
|
||||||
@ -1283,14 +1314,47 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if case .joined = participantUpdate.participationStatusChange {
|
|
||||||
} else if let ssrc = participantUpdate.ssrc, self.ssrcMapping[ssrc] == nil {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .call(isTerminated, _, _, _, _, _, _):
|
case let .call(isTerminated, _, _, _, _, _, _):
|
||||||
if isTerminated {
|
if isTerminated {
|
||||||
self.markAsCanBeRemoved()
|
self.markAsCanBeRemoved()
|
||||||
}
|
}
|
||||||
|
case let .conferenceChainBlocks(subChainId, blocks, nextOffset):
|
||||||
|
if let _ = self.keyPair {
|
||||||
|
var processBlock = true
|
||||||
|
let updateBaseOffset = nextOffset - blocks.count
|
||||||
|
if subChainId == 0 {
|
||||||
|
if let e2ePoll0Offset = self.e2ePoll0Offset {
|
||||||
|
if e2ePoll0Offset == updateBaseOffset {
|
||||||
|
self.e2ePoll0Offset = nextOffset
|
||||||
|
} else if e2ePoll0Offset < updateBaseOffset {
|
||||||
|
self.e2ePoll(subChainId: subChainId)
|
||||||
|
} else {
|
||||||
|
processBlock = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
processBlock = false
|
||||||
|
}
|
||||||
|
} else if subChainId == 1 {
|
||||||
|
if let e2ePoll1Offset = self.e2ePoll1Offset {
|
||||||
|
if e2ePoll1Offset == updateBaseOffset {
|
||||||
|
self.e2ePoll1Offset = nextOffset
|
||||||
|
} else if e2ePoll1Offset < updateBaseOffset {
|
||||||
|
self.e2ePoll(subChainId: subChainId)
|
||||||
|
} else {
|
||||||
|
processBlock = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
processBlock = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
processBlock = false
|
||||||
|
}
|
||||||
|
if processBlock {
|
||||||
|
self.addE2EBlocks(blocks: blocks, subChainId: subChainId)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1321,7 +1385,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if let initialCall = initialCall, let peerId, let temporaryParticipantsContext = (self.accountContext.cachedGroupCallContexts as? AccountGroupCallContextCacheImpl)?.impl.syncWith({ impl in
|
if let initialCall = initialCall, let peerId, let temporaryParticipantsContext = (self.accountContext.cachedGroupCallContexts as? AccountGroupCallContextCacheImpl)?.impl.syncWith({ impl in
|
||||||
impl.get(account: accountContext.account, engine: accountContext.engine, peerId: peerId, isChannel: isChannel, call: EngineGroupCallDescription(id: initialCall.id, accessHash: initialCall.accessHash, title: initialCall.title, scheduleTimestamp: initialCall.scheduleTimestamp, subscribedToScheduled: initialCall.subscribedToScheduled, isStream: initialCall.isStream))
|
impl.get(account: accountContext.account, engine: accountContext.engine, peerId: peerId, isChannel: isChannel, call: EngineGroupCallDescription(id: initialCall.description.id, accessHash: initialCall.description.accessHash, title: initialCall.description.title, scheduleTimestamp: initialCall.description.scheduleTimestamp, subscribedToScheduled: initialCall.description.subscribedToScheduled, isStream: initialCall.description.isStream))
|
||||||
}) {
|
}) {
|
||||||
self.switchToTemporaryParticipantsContext(sourceContext: temporaryParticipantsContext.context.participantsContext, oldMyPeerId: self.joinAsPeerId)
|
self.switchToTemporaryParticipantsContext(sourceContext: temporaryParticipantsContext.context.participantsContext, oldMyPeerId: self.joinAsPeerId)
|
||||||
} else {
|
} else {
|
||||||
@ -1447,6 +1511,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
self.peerUpdatesSubscription?.dispose()
|
self.peerUpdatesSubscription?.dispose()
|
||||||
self.screencastStateDisposable?.dispose()
|
self.screencastStateDisposable?.dispose()
|
||||||
self.pendingDisconnedUpgradedConferenceCallTimer?.invalidate()
|
self.pendingDisconnedUpgradedConferenceCallTimer?.invalidate()
|
||||||
|
self.e2ePoll0Timer?.invalidate()
|
||||||
|
self.e2ePoll0Disposable?.dispose()
|
||||||
|
self.e2ePoll1Timer?.invalidate()
|
||||||
|
self.e2ePoll1Disposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func switchToTemporaryParticipantsContext(sourceContext: GroupCallParticipantsContext?, oldMyPeerId: PeerId) {
|
private func switchToTemporaryParticipantsContext(sourceContext: GroupCallParticipantsContext?, oldMyPeerId: PeerId) {
|
||||||
@ -1471,7 +1539,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let sourceContext = sourceContext, let initialState = sourceContext.immediateState {
|
if let sourceContext = sourceContext, let initialState = sourceContext.immediateState {
|
||||||
let temporaryParticipantsContext = self.accountContext.engine.calls.groupCall(peerId: self.peerId, myPeerId: myPeerId, id: sourceContext.id, accessHash: sourceContext.accessHash, state: initialState, previousServiceState: sourceContext.serviceState)
|
let temporaryParticipantsContext = self.accountContext.engine.calls.groupCall(peerId: self.peerId, myPeerId: myPeerId, id: sourceContext.id, reference: sourceContext.reference, state: initialState, previousServiceState: sourceContext.serviceState)
|
||||||
self.temporaryParticipantsContext = temporaryParticipantsContext
|
self.temporaryParticipantsContext = temporaryParticipantsContext
|
||||||
self.participantsContextStateDisposable.set((combineLatest(queue: .mainQueue(),
|
self.participantsContextStateDisposable.set((combineLatest(queue: .mainQueue(),
|
||||||
myPeerData,
|
myPeerData,
|
||||||
@ -1702,7 +1770,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
peerId: self.peerId,
|
peerId: self.peerId,
|
||||||
myPeerId: self.joinAsPeerId,
|
myPeerId: self.joinAsPeerId,
|
||||||
id: callInfo.id,
|
id: callInfo.id,
|
||||||
accessHash: callInfo.accessHash,
|
reference: .id(id: callInfo.id, accessHash: callInfo.accessHash),
|
||||||
state: GroupCallParticipantsContext.State(
|
state: GroupCallParticipantsContext.State(
|
||||||
participants: [],
|
participants: [],
|
||||||
nextParticipantsFetchOffset: nil,
|
nextParticipantsFetchOffset: nil,
|
||||||
@ -1718,7 +1786,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
isVideoEnabled: callInfo.isVideoEnabled,
|
isVideoEnabled: callInfo.isVideoEnabled,
|
||||||
unmutedVideoLimit: callInfo.unmutedVideoLimit,
|
unmutedVideoLimit: callInfo.unmutedVideoLimit,
|
||||||
isStream: callInfo.isStream,
|
isStream: callInfo.isStream,
|
||||||
upgradedPrivateCallId: callInfo.upgradedPrivateCallId,
|
|
||||||
version: 0
|
version: 0
|
||||||
),
|
),
|
||||||
previousServiceState: nil
|
previousServiceState: nil
|
||||||
@ -1817,7 +1884,21 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
self.stateValue.subscribedToScheduled = state.subscribedToScheduled
|
self.stateValue.subscribedToScheduled = state.subscribedToScheduled
|
||||||
self.stateValue.scheduleTimestamp = self.isScheduledStarted ? nil : state.scheduleTimestamp
|
self.stateValue.scheduleTimestamp = self.isScheduledStarted ? nil : state.scheduleTimestamp
|
||||||
if state.scheduleTimestamp == nil && !self.isScheduledStarted {
|
if state.scheduleTimestamp == nil && !self.isScheduledStarted {
|
||||||
self.updateSessionState(internalState: .active(GroupCallInfo(id: callInfo.id, accessHash: callInfo.accessHash, participantCount: state.totalCount, streamDcId: callInfo.streamDcId, title: state.title, scheduleTimestamp: nil, subscribedToScheduled: false, recordingStartTimestamp: nil, sortAscending: true, defaultParticipantsAreMuted: callInfo.defaultParticipantsAreMuted ?? state.defaultParticipantsAreMuted, isVideoEnabled: callInfo.isVideoEnabled, unmutedVideoLimit: callInfo.unmutedVideoLimit, isStream: callInfo.isStream, upgradedPrivateCallId: callInfo.upgradedPrivateCallId)), audioSessionControl: self.audioSessionControl)
|
self.updateSessionState(internalState: .active(GroupCallInfo(
|
||||||
|
id: callInfo.id,
|
||||||
|
accessHash: callInfo.accessHash,
|
||||||
|
participantCount: state.totalCount,
|
||||||
|
streamDcId: callInfo.streamDcId,
|
||||||
|
title: state.title,
|
||||||
|
scheduleTimestamp: nil,
|
||||||
|
subscribedToScheduled: false,
|
||||||
|
recordingStartTimestamp: nil,
|
||||||
|
sortAscending: true,
|
||||||
|
defaultParticipantsAreMuted: callInfo.defaultParticipantsAreMuted ?? state.defaultParticipantsAreMuted,
|
||||||
|
isVideoEnabled: callInfo.isVideoEnabled,
|
||||||
|
unmutedVideoLimit: callInfo.unmutedVideoLimit,
|
||||||
|
isStream: callInfo.isStream
|
||||||
|
)), audioSessionControl: self.audioSessionControl)
|
||||||
} else {
|
} else {
|
||||||
self.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo(
|
self.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo(
|
||||||
id: callInfo.id,
|
id: callInfo.id,
|
||||||
@ -1832,8 +1913,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
|
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
|
||||||
isVideoEnabled: state.isVideoEnabled,
|
isVideoEnabled: state.isVideoEnabled,
|
||||||
unmutedVideoLimit: state.unmutedVideoLimit,
|
unmutedVideoLimit: state.unmutedVideoLimit,
|
||||||
isStream: callInfo.isStream,
|
isStream: callInfo.isStream
|
||||||
upgradedPrivateCallId: callInfo.upgradedPrivateCallId
|
|
||||||
))))
|
))))
|
||||||
|
|
||||||
self.summaryParticipantsState.set(.single(SummaryParticipantsState(
|
self.summaryParticipantsState.set(.single(SummaryParticipantsState(
|
||||||
@ -1929,9 +2009,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
outgoingAudioBitrateKbit = Int32(value)
|
outgoingAudioBitrateKbit = Int32(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
var encryptionKey: Data?
|
|
||||||
encryptionKey = self.encryptionKey?.key
|
|
||||||
|
|
||||||
let contextAudioSessionActive: Signal<Bool, NoError>
|
let contextAudioSessionActive: Signal<Bool, NoError>
|
||||||
if self.sharedAudioContext != nil {
|
if self.sharedAudioContext != nil {
|
||||||
contextAudioSessionActive = .single(true)
|
contextAudioSessionActive = .single(true)
|
||||||
@ -1944,6 +2021,26 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
audioIsActiveByDefault = false
|
audioIsActiveByDefault = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var encryptionContext: OngoingGroupCallEncryptionContext?
|
||||||
|
if self.isConference {
|
||||||
|
class OngoingGroupCallEncryptionContextImpl: OngoingGroupCallEncryptionContext {
|
||||||
|
private let e2eCall: Atomic<E2ECallState>
|
||||||
|
|
||||||
|
init(e2eCall: Atomic<E2ECallState>) {
|
||||||
|
self.e2eCall = e2eCall
|
||||||
|
}
|
||||||
|
|
||||||
|
func encrypt(message: Data) -> Data? {
|
||||||
|
return self.e2eCall.with({ $0.call?.encrypt(message) })
|
||||||
|
}
|
||||||
|
|
||||||
|
func decrypt(message: Data) -> Data? {
|
||||||
|
return self.e2eCall.with({ $0.call?.decrypt(message) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
encryptionContext = OngoingGroupCallEncryptionContextImpl(e2eCall: self.e2eCall)
|
||||||
|
}
|
||||||
|
|
||||||
genericCallContext = .call(OngoingGroupCallContext(audioSessionActive: contextAudioSessionActive, video: self.videoCapturer, requestMediaChannelDescriptions: { [weak self] ssrcs, completion in
|
genericCallContext = .call(OngoingGroupCallContext(audioSessionActive: contextAudioSessionActive, video: self.videoCapturer, requestMediaChannelDescriptions: { [weak self] ssrcs, completion in
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
@ -1969,7 +2066,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
self.onMutedSpeechActivityDetected?(value)
|
self.onMutedSpeechActivityDetected?(value)
|
||||||
}
|
}
|
||||||
}, encryptionKey: encryptionKey, isConference: self.isConference, audioIsActiveByDefault: audioIsActiveByDefault, isStream: self.isStream, sharedAudioDevice: self.sharedAudioContext?.audioDevice))
|
}, isConference: self.isConference, audioIsActiveByDefault: audioIsActiveByDefault, isStream: self.isStream, sharedAudioDevice: self.sharedAudioContext?.audioDevice, encryptionContext: encryptionContext))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.genericCallContext = genericCallContext
|
self.genericCallContext = genericCallContext
|
||||||
@ -2085,17 +2182,50 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
peerAdminIds = .single([])
|
peerAdminIds = .single([])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var generateE2EData: ((Data?) -> JoinGroupCallE2E?)?
|
||||||
|
if let keyPair = self.keyPair {
|
||||||
|
if let mappedKeyPair = TdKeyPair(keyId: keyPair.id, publicKey: keyPair.publicKey.data) {
|
||||||
|
let userId = self.joinAsPeerId.id._internalGetInt64Value()
|
||||||
|
generateE2EData = { block -> JoinGroupCallE2E? in
|
||||||
|
if let block {
|
||||||
|
guard let resultBlock = tdGenerateSelfAddBlock(mappedKeyPair, userId, block) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return JoinGroupCallE2E(
|
||||||
|
publicKey: keyPair.publicKey,
|
||||||
|
block: resultBlock
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
guard let resultBlock = tdGenerateZeroBlock(mappedKeyPair, userId) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return JoinGroupCallE2E(
|
||||||
|
publicKey: keyPair.publicKey,
|
||||||
|
block: resultBlock
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let reference: InternalGroupCallReference
|
||||||
|
if let initialCall = self.initialCall {
|
||||||
|
reference = initialCall.reference
|
||||||
|
} else {
|
||||||
|
reference = .id(id: callInfo.id, accessHash: callInfo.accessHash)
|
||||||
|
}
|
||||||
|
|
||||||
self.currentLocalSsrc = ssrc
|
self.currentLocalSsrc = ssrc
|
||||||
self.requestDisposable.set((self.accountContext.engine.calls.joinGroupCall(
|
self.requestDisposable.set((self.accountContext.engine.calls.joinGroupCall(
|
||||||
peerId: self.peerId,
|
peerId: self.peerId,
|
||||||
joinAs: self.joinAsPeerId,
|
joinAs: self.joinAsPeerId,
|
||||||
callId: callInfo.id,
|
callId: callInfo.id,
|
||||||
accessHash: callInfo.accessHash,
|
reference: reference,
|
||||||
preferMuted: true,
|
preferMuted: true,
|
||||||
joinPayload: joinPayload,
|
joinPayload: joinPayload,
|
||||||
peerAdminIds: peerAdminIds,
|
peerAdminIds: peerAdminIds,
|
||||||
inviteHash: self.invite,
|
inviteHash: self.invite,
|
||||||
keyFingerprint: self.encryptionKey?.fingerprint
|
generateE2E: generateE2EData
|
||||||
)
|
)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] joinCallResult in
|
|> deliverOnMainQueue).start(next: { [weak self] joinCallResult in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -2153,6 +2283,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.updateSessionState(internalState: .established(info: joinCallResult.callInfo, connectionMode: joinCallResult.connectionMode, clientParams: clientParams, localSsrc: ssrc, initialState: joinCallResult.state), audioSessionControl: self.audioSessionControl)
|
self.updateSessionState(internalState: .established(info: joinCallResult.callInfo, connectionMode: joinCallResult.connectionMode, clientParams: clientParams, localSsrc: ssrc, initialState: joinCallResult.state), audioSessionControl: self.audioSessionControl)
|
||||||
|
|
||||||
|
self.e2ePoll(subChainId: 0)
|
||||||
|
self.e2ePoll(subChainId: 1)
|
||||||
}, error: { [weak self] error in
|
}, error: { [weak self] error in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -2394,11 +2527,18 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
serviceState = participantsContext.serviceState
|
serviceState = participantsContext.serviceState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let reference: InternalGroupCallReference
|
||||||
|
if let initialCall = self.initialCall {
|
||||||
|
reference = initialCall.reference
|
||||||
|
} else {
|
||||||
|
reference = .id(id: callInfo.id, accessHash: callInfo.accessHash)
|
||||||
|
}
|
||||||
|
|
||||||
let participantsContext = self.accountContext.engine.calls.groupCall(
|
let participantsContext = self.accountContext.engine.calls.groupCall(
|
||||||
peerId: self.peerId,
|
peerId: self.peerId,
|
||||||
myPeerId: self.joinAsPeerId,
|
myPeerId: self.joinAsPeerId,
|
||||||
id: callInfo.id,
|
id: callInfo.id,
|
||||||
accessHash: callInfo.accessHash,
|
reference: reference,
|
||||||
state: initialState,
|
state: initialState,
|
||||||
previousServiceState: serviceState
|
previousServiceState: serviceState
|
||||||
)
|
)
|
||||||
@ -2701,8 +2841,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
|
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
|
||||||
isVideoEnabled: state.isVideoEnabled,
|
isVideoEnabled: state.isVideoEnabled,
|
||||||
unmutedVideoLimit: state.unmutedVideoLimit,
|
unmutedVideoLimit: state.unmutedVideoLimit,
|
||||||
isStream: callInfo.isStream,
|
isStream: callInfo.isStream
|
||||||
upgradedPrivateCallId: callInfo.upgradedPrivateCallId
|
|
||||||
))))
|
))))
|
||||||
|
|
||||||
self.summaryParticipantsState.set(.single(SummaryParticipantsState(
|
self.summaryParticipantsState.set(.single(SummaryParticipantsState(
|
||||||
@ -2759,6 +2898,117 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func addE2EBlocks(blocks: [Data], subChainId: Int) {
|
||||||
|
guard let initialCall = self.initialCall, let keyPair = self.keyPair else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let (outBlocks, outEmoji) = self.e2eCall.with({ callState -> ([Data], Data) in
|
||||||
|
if let call = callState.call {
|
||||||
|
for block in blocks {
|
||||||
|
if subChainId == 0 {
|
||||||
|
call.applyBlock(block)
|
||||||
|
} else if subChainId == 1 {
|
||||||
|
call.applyBroadcastBlock(block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (call.takeOutgoingBroadcastBlocks(), call.emojiState())
|
||||||
|
} else {
|
||||||
|
if subChainId == 0 {
|
||||||
|
guard let block = blocks.last else {
|
||||||
|
return ([], Data())
|
||||||
|
}
|
||||||
|
guard let keyPair = TdKeyPair(keyId: keyPair.id, publicKey: keyPair.publicKey.data) else {
|
||||||
|
return ([], Data())
|
||||||
|
}
|
||||||
|
guard let call = TdCall.make(with: keyPair, latestBlock: block) else {
|
||||||
|
return ([], Data())
|
||||||
|
}
|
||||||
|
callState.call = call
|
||||||
|
for block in callState.pendingIncomingBroadcastBlocks {
|
||||||
|
call.applyBroadcastBlock(block)
|
||||||
|
}
|
||||||
|
callState.pendingIncomingBroadcastBlocks.removeAll()
|
||||||
|
return (call.takeOutgoingBroadcastBlocks(), call.emojiState())
|
||||||
|
} else if subChainId == 1 {
|
||||||
|
callState.pendingIncomingBroadcastBlocks.append(contentsOf: blocks)
|
||||||
|
return ([], Data())
|
||||||
|
} else {
|
||||||
|
return ([], Data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self.e2eEncryptionKeyHashValue.set(outEmoji.isEmpty ? nil : outEmoji)
|
||||||
|
|
||||||
|
//TODO:release queue
|
||||||
|
for outBlock in outBlocks {
|
||||||
|
let _ = self.accountContext.engine.calls.sendConferenceCallBroadcast(callId: initialCall.description.id, accessHash: initialCall.description.accessHash, block: outBlock).startStandalone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func e2ePoll(subChainId: Int) {
|
||||||
|
guard let initialCall = self.initialCall else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset: Int?
|
||||||
|
if subChainId == 0 {
|
||||||
|
offset = self.e2ePoll0Offset
|
||||||
|
self.e2ePoll0Disposable?.dispose()
|
||||||
|
} else if subChainId == 1 {
|
||||||
|
offset = self.e2ePoll1Offset
|
||||||
|
self.e2ePoll1Disposable?.dispose()
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let disposable = (self.accountContext.engine.calls.pollConferenceCallBlockchain(reference: initialCall.reference, subChainId: subChainId, offset: offset ?? 0, limit: 10)
|
||||||
|
|> deliverOnMainQueue).startStrict(next: { [weak self] result in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var delayPoll = true
|
||||||
|
if let result {
|
||||||
|
if subChainId == 0 {
|
||||||
|
if self.e2ePoll0Offset != result.nextOffset {
|
||||||
|
self.e2ePoll0Offset = result.nextOffset
|
||||||
|
delayPoll = false
|
||||||
|
}
|
||||||
|
} else if subChainId == 1 {
|
||||||
|
if self.e2ePoll1Offset != result.nextOffset {
|
||||||
|
self.e2ePoll1Offset = result.nextOffset
|
||||||
|
delayPoll = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.addE2EBlocks(blocks: result.blocks, subChainId: subChainId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if subChainId == 0 {
|
||||||
|
self.e2ePoll0Timer?.invalidate()
|
||||||
|
self.e2ePoll0Timer = Foundation.Timer.scheduledTimer(withTimeInterval: delayPoll ? 1.0 : 0.0, repeats: false, block: { [weak self] _ in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.e2ePoll(subChainId: 0)
|
||||||
|
})
|
||||||
|
} else if subChainId == 1 {
|
||||||
|
self.e2ePoll1Timer?.invalidate()
|
||||||
|
self.e2ePoll1Timer = Foundation.Timer.scheduledTimer(withTimeInterval: delayPoll ? 1.0 : 0.0, repeats: false, block: { [weak self] _ in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.e2ePoll(subChainId: 1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if subChainId == 0 {
|
||||||
|
self.e2ePoll0Disposable = disposable
|
||||||
|
} else if subChainId == 1 {
|
||||||
|
self.e2ePoll1Disposable = disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func activateIncomingAudioIfNeeded() {
|
private func activateIncomingAudioIfNeeded() {
|
||||||
if let genericCallContext = self.genericCallContext, case let .call(groupCall) = genericCallContext {
|
if let genericCallContext = self.genericCallContext, case let .call(groupCall) = genericCallContext {
|
||||||
groupCall.activateIncomingAudio()
|
groupCall.activateIncomingAudio()
|
||||||
@ -2807,7 +3057,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !remainingSsrcs.isEmpty, let callInfo = self.internalState.callInfo {
|
if !remainingSsrcs.isEmpty, let callInfo = self.internalState.callInfo {
|
||||||
return (self.accountContext.engine.calls.getGroupCallParticipants(callId: callInfo.id, accessHash: callInfo.accessHash, offset: "", ssrcs: Array(remainingSsrcs), limit: 100, sortAscending: callInfo.sortAscending)
|
return (self.accountContext.engine.calls.getGroupCallParticipants(reference: .id(id: callInfo.id, accessHash: callInfo.accessHash), offset: "", ssrcs: Array(remainingSsrcs), limit: 100, sortAscending: callInfo.sortAscending)
|
||||||
|> deliverOnMainQueue).start(next: { state in
|
|> deliverOnMainQueue).start(next: { state in
|
||||||
extractMediaChannelDescriptions(remainingSsrcs: &remainingSsrcs, participants: state.participants, into: &result)
|
extractMediaChannelDescriptions(remainingSsrcs: &remainingSsrcs, participants: state.participants, into: &result)
|
||||||
|
|
||||||
@ -2913,7 +3163,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
self.markedAsCanBeRemoved = true
|
self.markedAsCanBeRemoved = true
|
||||||
|
|
||||||
self.genericCallContext?.stop(account: self.account, reportCallId: self.conferenceFromCallId, debugLog: self.debugLog)
|
self.genericCallContext?.stop(account: self.account, reportCallId: nil, debugLog: self.debugLog)
|
||||||
self.screencastIPCContext?.disableScreencast(account: self.account)
|
self.screencastIPCContext?.disableScreencast(account: self.account)
|
||||||
|
|
||||||
self._canBeRemoved.set(.single(true))
|
self._canBeRemoved.set(.single(true))
|
||||||
@ -3615,7 +3865,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
let context = self.accountContext
|
let context = self.accountContext
|
||||||
let currentCall: Signal<GroupCallInfo?, CallError>
|
let currentCall: Signal<GroupCallInfo?, CallError>
|
||||||
if let initialCall = self.initialCall {
|
if let initialCall = self.initialCall {
|
||||||
currentCall = context.engine.calls.getCurrentGroupCall(callId: initialCall.id, accessHash: initialCall.accessHash)
|
currentCall = context.engine.calls.getCurrentGroupCall(reference: initialCall.reference)
|
||||||
|> mapError { _ -> CallError in
|
|> mapError { _ -> CallError in
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
@ -3623,7 +3873,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
return summary?.info
|
return summary?.info
|
||||||
}
|
}
|
||||||
} else if case let .active(callInfo) = self.internalState {
|
} else if case let .active(callInfo) = self.internalState {
|
||||||
currentCall = context.engine.calls.getCurrentGroupCall(callId: callInfo.id, accessHash: callInfo.accessHash)
|
currentCall = context.engine.calls.getCurrentGroupCall(reference: .id(id: callInfo.id, accessHash: callInfo.accessHash))
|
||||||
|> mapError { _ -> CallError in
|
|> mapError { _ -> CallError in
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
@ -3662,7 +3912,17 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let value = value {
|
if let value = value {
|
||||||
self.initialCall = EngineGroupCallDescription(id: value.id, accessHash: value.accessHash, title: value.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: value.isStream)
|
var reference: InternalGroupCallReference = .id(id: value.id, accessHash: value.accessHash)
|
||||||
|
if let current = self.initialCall {
|
||||||
|
switch current.reference {
|
||||||
|
case .message, .link:
|
||||||
|
reference = current.reference
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.initialCall = (EngineGroupCallDescription(id: value.id, accessHash: value.accessHash, title: value.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: value.isStream), reference)
|
||||||
|
self.callId = value.id
|
||||||
|
|
||||||
self.updateSessionState(internalState: .active(value), audioSessionControl: self.audioSessionControl)
|
self.updateSessionState(internalState: .active(value), audioSessionControl: self.audioSessionControl)
|
||||||
} else {
|
} else {
|
||||||
@ -3673,7 +3933,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
|
|
||||||
public func invitePeer(_ peerId: PeerId) -> Bool {
|
public func invitePeer(_ peerId: PeerId) -> Bool {
|
||||||
if self.isConference {
|
if self.isConference {
|
||||||
guard let initialCall = self.initialCall, let encryptionKey = self.encryptionKey else {
|
guard let initialCall = self.initialCall else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO:release
|
||||||
|
let _ = self.accountContext.engine.calls.inviteConferenceCallParticipant(callId: initialCall.description.id, accessHash: initialCall.description.accessHash, peerId: peerId).start()
|
||||||
|
return false
|
||||||
|
/*guard let initialCall = self.initialCall else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if conferenceInvitationContexts[peerId] != nil {
|
if conferenceInvitationContexts[peerId] != nil {
|
||||||
@ -3685,7 +3952,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
let invitationContext = PendingConferenceInvitationContext(
|
let invitationContext = PendingConferenceInvitationContext(
|
||||||
callSessionManager: self.accountContext.account.callSessionManager,
|
callSessionManager: self.accountContext.account.callSessionManager,
|
||||||
groupCall: GroupCallReference(id: initialCall.id, accessHash: initialCall.accessHash),
|
groupCall: GroupCallReference(id: initialCall.id, accessHash: initialCall.accessHash),
|
||||||
encryptionKey: encryptionKey.key,
|
|
||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
onStateUpdated: { state in
|
onStateUpdated: { state in
|
||||||
onStateUpdated?(state)
|
onStateUpdated?(state)
|
||||||
@ -3733,7 +3999,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false*/
|
||||||
} else {
|
} else {
|
||||||
guard let callInfo = self.internalState.callInfo, !self.invitedPeersValue.contains(where: { $0.id == peerId }) else {
|
guard let callInfo = self.internalState.callInfo, !self.invitedPeersValue.contains(where: { $0.id == peerId }) else {
|
||||||
return false
|
return false
|
||||||
@ -3750,9 +4016,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setConferenceInvitedPeers(_ peerIds: [PeerId]) {
|
func setConferenceInvitedPeers(_ peerIds: [PeerId]) {
|
||||||
self.invitedPeersValue = peerIds.map {
|
//TODO:release
|
||||||
|
/*self.invitedPeersValue = peerIds.map {
|
||||||
PresentationGroupCallInvitedPeer(id: $0, state: .requesting)
|
PresentationGroupCallInvitedPeer(id: $0, state: .requesting)
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public func removedPeer(_ peerId: PeerId) {
|
public func removedPeer(_ peerId: PeerId) {
|
||||||
@ -3771,6 +4038,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
|
|
||||||
public var inviteLinks: Signal<GroupCallInviteLinks?, NoError> {
|
public var inviteLinks: Signal<GroupCallInviteLinks?, NoError> {
|
||||||
let engine = self.accountContext.engine
|
let engine = self.accountContext.engine
|
||||||
|
let initialCall = self.initialCall
|
||||||
|
let isConference = self.isConference
|
||||||
|
|
||||||
return self.state
|
return self.state
|
||||||
|> map { state -> PeerId in
|
|> map { state -> PeerId in
|
||||||
@ -3788,7 +4057,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
|> mapToSignal { state in
|
|> mapToSignal { state in
|
||||||
if let callInfo = state.callInfo {
|
if let callInfo = state.callInfo {
|
||||||
return engine.calls.groupCallInviteLinks(callId: callInfo.id, accessHash: callInfo.accessHash)
|
let reference: InternalGroupCallReference
|
||||||
|
if let initialCall = initialCall {
|
||||||
|
reference = initialCall.reference
|
||||||
|
} else {
|
||||||
|
reference = .id(id: callInfo.id, accessHash: callInfo.accessHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
return engine.calls.groupCallInviteLinks(reference: reference, isConference: isConference)
|
||||||
} else {
|
} else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
@ -3975,3 +4251,24 @@ private final class InProcessScreencastContext: ScreencastContext {
|
|||||||
self.context.setJoinResponse(payload: clientParams)
|
self.context.setJoinResponse(payload: clientParams)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class TelegramE2EEncryptionProviderImpl: TelegramE2EEncryptionProvider {
|
||||||
|
public static let shared = TelegramE2EEncryptionProviderImpl()
|
||||||
|
|
||||||
|
public func generateKeyPair() -> TelegramKeyPair? {
|
||||||
|
guard let keyPair = TdKeyPair.generate() else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let publicKey = TelegramPublicKey(data: keyPair.publicKey) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return TelegramKeyPair(id: keyPair.keyId, publicKey: publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func generateCallZeroBlock(keyPair: TelegramKeyPair, userId: Int64) -> Data? {
|
||||||
|
guard let keyPair = TdKeyPair(keyId: keyPair.id, publicKey: keyPair.publicKey.data) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return tdGenerateZeroBlock(keyPair, userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -24,6 +24,7 @@ import TelegramAudio
|
|||||||
import LegacyComponents
|
import LegacyComponents
|
||||||
import TooltipUI
|
import TooltipUI
|
||||||
import BlurredBackgroundComponent
|
import BlurredBackgroundComponent
|
||||||
|
import CallsEmoji
|
||||||
|
|
||||||
extension VideoChatCall {
|
extension VideoChatCall {
|
||||||
var myAudioLevelAndSpeaking: Signal<(Float, Bool), NoError> {
|
var myAudioLevelAndSpeaking: Signal<(Float, Bool), NoError> {
|
||||||
@ -262,6 +263,9 @@ final class VideoChatScreenComponent: Component {
|
|||||||
var speakingParticipantPeers: [EnginePeer] = []
|
var speakingParticipantPeers: [EnginePeer] = []
|
||||||
var visibleParticipants: Set<EnginePeer.Id> = Set()
|
var visibleParticipants: Set<EnginePeer.Id> = Set()
|
||||||
|
|
||||||
|
var encryptionKeyEmoji: [String]?
|
||||||
|
var encryptionKeyEmojiDisposable: Disposable?
|
||||||
|
|
||||||
let isPresentedValue = ValuePromise<Bool>(false, ignoreRepeated: true)
|
let isPresentedValue = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
var applicationStateDisposable: Disposable?
|
var applicationStateDisposable: Disposable?
|
||||||
|
|
||||||
@ -313,6 +317,7 @@ final class VideoChatScreenComponent: Component {
|
|||||||
self.updateAvatarDisposable.dispose()
|
self.updateAvatarDisposable.dispose()
|
||||||
self.inviteDisposable.dispose()
|
self.inviteDisposable.dispose()
|
||||||
self.conferenceCallStateDisposable?.dispose()
|
self.conferenceCallStateDisposable?.dispose()
|
||||||
|
self.encryptionKeyEmojiDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateIn() {
|
func animateIn() {
|
||||||
@ -647,9 +652,6 @@ final class VideoChatScreenComponent: Component {
|
|||||||
guard case let .group(groupCall) = self.currentCall else {
|
guard case let .group(groupCall) = self.currentCall else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let peerId = groupCall.peerId else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let formatSendTitle: (String) -> String = { string in
|
let formatSendTitle: (String) -> String = { string in
|
||||||
var string = string
|
var string = string
|
||||||
@ -663,6 +665,7 @@ final class VideoChatScreenComponent: Component {
|
|||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let peerId = groupCall.peerId {
|
||||||
let _ = (groupCall.accountContext.account.postbox.loadedPeerWithId(peerId)
|
let _ = (groupCall.accountContext.account.postbox.loadedPeerWithId(peerId)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||||
guard let self, let environment = self.environment, case let .group(groupCall) = self.currentCall else {
|
guard let self, let environment = self.environment, case let .group(groupCall) = self.currentCall else {
|
||||||
@ -739,6 +742,58 @@ final class VideoChatScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
environment.controller()?.present(shareController, in: .window(.root))
|
environment.controller()?.present(shareController, in: .window(.root))
|
||||||
})
|
})
|
||||||
|
} else if groupCall.isConference {
|
||||||
|
guard let environment = self.environment else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let shareController = ShareController(context: groupCall.accountContext, subject: .url(inviteLinks.listenerLink), forceTheme: environment.theme, forcedActionTitle: environment.strings.VoiceChat_CopyInviteLink)
|
||||||
|
shareController.completed = { [weak self] peerIds in
|
||||||
|
guard let self, case let .group(groupCall) = self.currentCall else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = (groupCall.accountContext.engine.data.get(
|
||||||
|
EngineDataList(
|
||||||
|
peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] peerList in
|
||||||
|
guard let self, let environment = self.environment, case let .group(groupCall) = self.currentCall else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let peers = peerList.compactMap { $0 }
|
||||||
|
let presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme)
|
||||||
|
|
||||||
|
let text: String
|
||||||
|
var isSavedMessages = false
|
||||||
|
if peers.count == 1, let peer = peers.first {
|
||||||
|
isSavedMessages = peer.id == groupCall.accountContext.account.peerId
|
||||||
|
let peerName = peer.id == groupCall.accountContext.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
|
text = presentationData.strings.VoiceChat_ForwardTooltip_Chat(peerName).string
|
||||||
|
} else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last {
|
||||||
|
let firstPeerName = firstPeer.id == groupCall.accountContext.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
|
let secondPeerName = secondPeer.id == groupCall.accountContext.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
|
text = presentationData.strings.VoiceChat_ForwardTooltip_TwoChats(firstPeerName, secondPeerName).string
|
||||||
|
} else if let peer = peers.first {
|
||||||
|
let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||||
|
text = presentationData.strings.VoiceChat_ForwardTooltip_ManyChats(peerName, "\(peers.count - 1)").string
|
||||||
|
} else {
|
||||||
|
text = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
environment.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: isSavedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
shareController.actionCompleted = { [weak self] in
|
||||||
|
guard let self, let environment = self.environment, case let .group(groupCall) = self.currentCall else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme)
|
||||||
|
environment.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.VoiceChat_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
|
||||||
|
}
|
||||||
|
environment.controller()?.present(shareController, in: .window(.root))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func onCameraPressed() {
|
private func onCameraPressed() {
|
||||||
@ -1325,6 +1380,28 @@ final class VideoChatScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.encryptionKeyEmojiDisposable?.dispose()
|
||||||
|
self.encryptionKeyEmojiDisposable = (groupCall.e2eEncryptionKeyHash
|
||||||
|
|> deliverOnMainQueue).startStrict(next: { [weak self] e2eEncryptionKeyHash in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var encryptionKeyEmoji: [String]?
|
||||||
|
if let e2eEncryptionKeyHash, e2eEncryptionKeyHash.count >= 32 {
|
||||||
|
if let value = stringForEmojiHashOfData(e2eEncryptionKeyHash.prefix(32), 4) {
|
||||||
|
if !value.isEmpty {
|
||||||
|
encryptionKeyEmoji = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.encryptionKeyEmoji != encryptionKeyEmoji {
|
||||||
|
self.encryptionKeyEmoji = encryptionKeyEmoji
|
||||||
|
if !self.isUpdating {
|
||||||
|
self.state?.updated(transition: .spring(duration: 0.4))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
self.conferenceCallStateDisposable?.dispose()
|
self.conferenceCallStateDisposable?.dispose()
|
||||||
self.conferenceCallStateDisposable = nil
|
self.conferenceCallStateDisposable = nil
|
||||||
|
|
||||||
@ -1961,7 +2038,7 @@ final class VideoChatScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var encryptionKeyFrame: CGRect?
|
var encryptionKeyFrame: CGRect?
|
||||||
if component.initialCall.accountContext.sharedContext.immediateExperimentalUISettings.conferenceDebug {
|
if let encryptionKeyEmoji = self.encryptionKeyEmoji {
|
||||||
navigationHeight -= 2.0
|
navigationHeight -= 2.0
|
||||||
let encryptionKey: ComponentView<Empty>
|
let encryptionKey: ComponentView<Empty>
|
||||||
var encryptionKeyTransition = transition
|
var encryptionKeyTransition = transition
|
||||||
@ -1978,7 +2055,7 @@ final class VideoChatScreenComponent: Component {
|
|||||||
component: AnyComponent(VideoChatEncryptionKeyComponent(
|
component: AnyComponent(VideoChatEncryptionKeyComponent(
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
strings: environment.strings,
|
strings: environment.strings,
|
||||||
emoji: ["👌", "🧡", "🌹", "🤷"],
|
emoji: encryptionKeyEmoji,
|
||||||
isExpanded: self.isEncryptionKeyExpanded,
|
isExpanded: self.isEncryptionKeyExpanded,
|
||||||
tapAction: { [weak self] in
|
tapAction: { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -2283,10 +2360,17 @@ final class VideoChatScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let encryptionKeyView = self.encryptionKey?.view, let encryptionKeyFrame {
|
if let encryptionKeyView = self.encryptionKey?.view, let encryptionKeyFrame {
|
||||||
|
var encryptionKeyTransition = transition
|
||||||
if encryptionKeyView.superview == nil {
|
if encryptionKeyView.superview == nil {
|
||||||
|
encryptionKeyTransition = encryptionKeyTransition.withAnimation(.none)
|
||||||
self.containerView.addSubview(encryptionKeyView)
|
self.containerView.addSubview(encryptionKeyView)
|
||||||
|
|
||||||
|
ComponentTransition.immediate.setScale(view: encryptionKeyView, scale: 0.001)
|
||||||
|
encryptionKeyView.alpha = 0.0
|
||||||
}
|
}
|
||||||
transition.setFrame(view: encryptionKeyView, frame: encryptionKeyFrame)
|
encryptionKeyTransition.setPosition(view: encryptionKeyView, position: encryptionKeyFrame.center)
|
||||||
|
encryptionKeyTransition.setBounds(view: encryptionKeyView, bounds: CGRect(origin: CGPoint(), size: encryptionKeyFrame.size))
|
||||||
|
transition.setScale(view: encryptionKeyView, scale: 1.0)
|
||||||
alphaTransition.setAlpha(view: encryptionKeyView, alpha: self.isAnimatedOutFromPrivateCall ? 0.0 : 1.0)
|
alphaTransition.setAlpha(view: encryptionKeyView, alpha: self.isAnimatedOutFromPrivateCall ? 0.0 : 1.0)
|
||||||
|
|
||||||
if self.isEncryptionKeyExpanded {
|
if self.isEncryptionKeyExpanded {
|
||||||
|
@ -16,7 +16,7 @@ extension VideoChatScreenComponent.View {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if groupCall.accountContext.sharedContext.immediateExperimentalUISettings.conferenceDebug {
|
/*if groupCall.accountContext.sharedContext.immediateExperimentalUISettings.conferenceDebug {
|
||||||
guard let navigationController = self.environment?.controller()?.navigationController as? NavigationController else {
|
guard let navigationController = self.environment?.controller()?.navigationController as? NavigationController else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -39,7 +39,7 @@ extension VideoChatScreenComponent.View {
|
|||||||
})
|
})
|
||||||
self.environment?.controller()?.present(controller, in: .window(.root), with: nil)
|
self.environment?.controller()?.present(controller, in: .window(.root), with: nil)
|
||||||
return
|
return
|
||||||
}
|
}*/
|
||||||
|
|
||||||
if groupCall.isConference {
|
if groupCall.isConference {
|
||||||
var disablePeerIds: [EnginePeer.Id] = []
|
var disablePeerIds: [EnginePeer.Id] = []
|
||||||
|
@ -175,7 +175,7 @@ extension VideoChatScreenComponent.View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if case let .group(groupCall) = currentCall, let encryptionKey = groupCall.encryptionKeyValue {
|
/*if case let .group(groupCall) = currentCall, let encryptionKey = groupCall.encryptionKeyValue {
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
let emojiKey = resolvedEmojiKey(data: encryptionKey)
|
let emojiKey = resolvedEmojiKey(data: encryptionKey)
|
||||||
items.append(.action(ContextMenuActionItem(text: "Encryption Key", textLayout: .secondLineWithValue(emojiKey.joined(separator: "")), icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: "Encryption Key", textLayout: .secondLineWithValue(emojiKey.joined(separator: "")), icon: { theme in
|
||||||
@ -202,7 +202,7 @@ extension VideoChatScreenComponent.View {
|
|||||||
environment.controller()?.present(alertController, in: .window(.root))
|
environment.controller()?.present(alertController, in: .window(.root))
|
||||||
})))
|
})))
|
||||||
items.append(.separator)
|
items.append(.separator)
|
||||||
}
|
}*/
|
||||||
|
|
||||||
if let (availableOutputs, currentOutput) = self.audioOutputState, availableOutputs.count > 1 {
|
if let (availableOutputs, currentOutput) = self.audioOutputState, availableOutputs.count > 1 {
|
||||||
var currentOutputTitle = ""
|
var currentOutputTitle = ""
|
||||||
|
@ -77,7 +77,7 @@ public final class VoiceChatJoinScreen: ViewController {
|
|||||||
if let call = call {
|
if let call = call {
|
||||||
let peer = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
let peer = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||||
|> castError(GetCurrentGroupCallError.self)
|
|> castError(GetCurrentGroupCallError.self)
|
||||||
return combineLatest(peer, context.engine.calls.getCurrentGroupCall(callId: call.id, accessHash: call.accessHash))
|
return combineLatest(peer, context.engine.calls.getCurrentGroupCall(reference: .id(id: call.id, accessHash: call.accessHash)))
|
||||||
|> map { peer, call -> (EnginePeer, GroupCallSummary)? in
|
|> map { peer, call -> (EnginePeer, GroupCallSummary)? in
|
||||||
if let peer = peer, let call = call {
|
if let peer = peer, let call = call {
|
||||||
return (peer, call)
|
return (peer, call)
|
||||||
|
@ -116,6 +116,7 @@ enum AccountStateMutationOperation {
|
|||||||
case UpdateReadThread(threadMessageId: MessageId, readMaxId: Int32, isIncoming: Bool, mainChannelMessage: MessageId?)
|
case UpdateReadThread(threadMessageId: MessageId, readMaxId: Int32, isIncoming: Bool, mainChannelMessage: MessageId?)
|
||||||
case UpdateGroupCallParticipants(id: Int64, accessHash: Int64, participants: [Api.GroupCallParticipant], version: Int32)
|
case UpdateGroupCallParticipants(id: Int64, accessHash: Int64, participants: [Api.GroupCallParticipant], version: Int32)
|
||||||
case UpdateGroupCall(peerId: PeerId?, call: Api.GroupCall)
|
case UpdateGroupCall(peerId: PeerId?, call: Api.GroupCall)
|
||||||
|
case UpdateGroupCallChainBlocks(id: Int64, accessHash: Int64, subChainId: Int32, blocks: [Data], nextOffset: Int32)
|
||||||
case UpdateAutoremoveTimeout(peer: Api.Peer, value: CachedPeerAutoremoveTimeout.Value?)
|
case UpdateAutoremoveTimeout(peer: Api.Peer, value: CachedPeerAutoremoveTimeout.Value?)
|
||||||
case UpdateAttachMenuBots
|
case UpdateAttachMenuBots
|
||||||
case UpdateAudioTranscription(messageId: MessageId, id: Int64, isPending: Bool, text: String)
|
case UpdateAudioTranscription(messageId: MessageId, id: Int64, isPending: Bool, text: String)
|
||||||
@ -403,6 +404,10 @@ struct AccountMutableState {
|
|||||||
self.addOperation(.UpdateGroupCall(peerId: peerId, call: call))
|
self.addOperation(.UpdateGroupCall(peerId: peerId, call: call))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func updateGroupCallChainBlocks(id: Int64, accessHash: Int64, subChainId: Int32, blocks: [Data], nextOffset: Int32) {
|
||||||
|
self.addOperation(.UpdateGroupCallChainBlocks(id: id, accessHash: accessHash, subChainId: subChainId, blocks: blocks, nextOffset: nextOffset))
|
||||||
|
}
|
||||||
|
|
||||||
mutating func updateAutoremoveTimeout(peer: Api.Peer, value: CachedPeerAutoremoveTimeout.Value?) {
|
mutating func updateAutoremoveTimeout(peer: Api.Peer, value: CachedPeerAutoremoveTimeout.Value?) {
|
||||||
self.addOperation(.UpdateAutoremoveTimeout(peer: peer, value: value))
|
self.addOperation(.UpdateAutoremoveTimeout(peer: peer, value: value))
|
||||||
}
|
}
|
||||||
@ -709,7 +714,7 @@ struct AccountMutableState {
|
|||||||
|
|
||||||
mutating func addOperation(_ operation: AccountStateMutationOperation) {
|
mutating func addOperation(_ operation: AccountStateMutationOperation) {
|
||||||
switch operation {
|
switch operation {
|
||||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .UpdateWallpaper, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateRevenueBalances, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery:
|
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .UpdateWallpaper, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateRevenueBalances, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery:
|
||||||
break
|
break
|
||||||
case let .AddMessages(messages, location):
|
case let .AddMessages(messages, location):
|
||||||
for message in messages {
|
for message in messages {
|
||||||
@ -858,6 +863,7 @@ struct AccountReplayedFinalState {
|
|||||||
let updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances]
|
let updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances]
|
||||||
let sentScheduledMessageIds: Set<MessageId>
|
let sentScheduledMessageIds: Set<MessageId>
|
||||||
let reportMessageDelivery: Set<MessageId>
|
let reportMessageDelivery: Set<MessageId>
|
||||||
|
let addedConferenceInvitationMessagesIds: [MessageId]
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AccountFinalStateEvents {
|
struct AccountFinalStateEvents {
|
||||||
@ -889,12 +895,13 @@ struct AccountFinalStateEvents {
|
|||||||
let updatedStarsBalance: [PeerId: StarsAmount]
|
let updatedStarsBalance: [PeerId: StarsAmount]
|
||||||
let updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances]
|
let updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances]
|
||||||
let reportMessageDelivery: Set<MessageId>
|
let reportMessageDelivery: Set<MessageId>
|
||||||
|
let addedConferenceInvitationMessagesIds: [MessageId]
|
||||||
|
|
||||||
var isEmpty: Bool {
|
var isEmpty: Bool {
|
||||||
return self.addedIncomingMessageIds.isEmpty && self.addedReactionEvents.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.sentScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.storyUpdates.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.dismissBotWebViews.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty && !self.updateConfig && !self.isPremiumUpdated && self.updatedRevenueBalances.isEmpty && self.updatedStarsBalance.isEmpty && self.updatedStarsRevenueStatus.isEmpty && self.reportMessageDelivery.isEmpty
|
return self.addedIncomingMessageIds.isEmpty && self.addedReactionEvents.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.sentScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.storyUpdates.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.dismissBotWebViews.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty && !self.updateConfig && !self.isPremiumUpdated && self.updatedRevenueBalances.isEmpty && self.updatedStarsBalance.isEmpty && self.updatedStarsRevenueStatus.isEmpty && self.reportMessageDelivery.isEmpty && self.addedConferenceInvitationMessagesIds.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, reaction: MessageReaction.Reaction, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], storyUpdates: [InternalStoryUpdate] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], dismissBotWebViews: [Int64] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:], updateConfig: Bool = false, isPremiumUpdated: Bool = false, updatedRevenueBalances: [PeerId: RevenueStats.Balances] = [:], updatedStarsBalance: [PeerId: StarsAmount] = [:], updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances] = [:], sentScheduledMessageIds: Set<MessageId> = Set(), reportMessageDelivery: Set<MessageId> = Set()) {
|
init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, reaction: MessageReaction.Reaction, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], storyUpdates: [InternalStoryUpdate] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], dismissBotWebViews: [Int64] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:], updateConfig: Bool = false, isPremiumUpdated: Bool = false, updatedRevenueBalances: [PeerId: RevenueStats.Balances] = [:], updatedStarsBalance: [PeerId: StarsAmount] = [:], updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances] = [:], sentScheduledMessageIds: Set<MessageId> = Set(), reportMessageDelivery: Set<MessageId> = Set(), addedConferenceInvitationMessagesIds: [MessageId] = []) {
|
||||||
self.addedIncomingMessageIds = addedIncomingMessageIds
|
self.addedIncomingMessageIds = addedIncomingMessageIds
|
||||||
self.addedReactionEvents = addedReactionEvents
|
self.addedReactionEvents = addedReactionEvents
|
||||||
self.wasScheduledMessageIds = wasScheduledMessageIds
|
self.wasScheduledMessageIds = wasScheduledMessageIds
|
||||||
@ -923,6 +930,7 @@ struct AccountFinalStateEvents {
|
|||||||
self.updatedStarsRevenueStatus = updatedStarsRevenueStatus
|
self.updatedStarsRevenueStatus = updatedStarsRevenueStatus
|
||||||
self.sentScheduledMessageIds = sentScheduledMessageIds
|
self.sentScheduledMessageIds = sentScheduledMessageIds
|
||||||
self.reportMessageDelivery = reportMessageDelivery
|
self.reportMessageDelivery = reportMessageDelivery
|
||||||
|
self.addedConferenceInvitationMessagesIds = addedConferenceInvitationMessagesIds
|
||||||
}
|
}
|
||||||
|
|
||||||
init(state: AccountReplayedFinalState) {
|
init(state: AccountReplayedFinalState) {
|
||||||
@ -954,6 +962,7 @@ struct AccountFinalStateEvents {
|
|||||||
self.updatedStarsRevenueStatus = state.updatedStarsRevenueStatus
|
self.updatedStarsRevenueStatus = state.updatedStarsRevenueStatus
|
||||||
self.sentScheduledMessageIds = state.sentScheduledMessageIds
|
self.sentScheduledMessageIds = state.sentScheduledMessageIds
|
||||||
self.reportMessageDelivery = state.reportMessageDelivery
|
self.reportMessageDelivery = state.reportMessageDelivery
|
||||||
|
self.addedConferenceInvitationMessagesIds = state.addedConferenceInvitationMessagesIds
|
||||||
}
|
}
|
||||||
|
|
||||||
func union(with other: AccountFinalStateEvents) -> AccountFinalStateEvents {
|
func union(with other: AccountFinalStateEvents) -> AccountFinalStateEvents {
|
||||||
@ -989,6 +998,37 @@ struct AccountFinalStateEvents {
|
|||||||
var reportMessageDelivery = self.reportMessageDelivery
|
var reportMessageDelivery = self.reportMessageDelivery
|
||||||
reportMessageDelivery.formUnion(other.reportMessageDelivery)
|
reportMessageDelivery.formUnion(other.reportMessageDelivery)
|
||||||
|
|
||||||
return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, addedReactionEvents: self.addedReactionEvents + other.addedReactionEvents, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, deletedMessageIds: self.deletedMessageIds + other.deletedMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData, updatedGroupCallParticipants: self.updatedGroupCallParticipants + other.updatedGroupCallParticipants, storyUpdates: self.storyUpdates + other.storyUpdates, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, dismissBotWebViews: self.dismissBotWebViews + other.dismissBotWebViews, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated, updatedIncomingThreadReadStates: self.updatedIncomingThreadReadStates.merging(other.updatedIncomingThreadReadStates, uniquingKeysWith: { lhs, _ in lhs }), updateConfig: updateConfig, isPremiumUpdated: isPremiumUpdated, updatedRevenueBalances: self.updatedRevenueBalances.merging(other.updatedRevenueBalances, uniquingKeysWith: { lhs, _ in lhs }), updatedStarsBalance: self.updatedStarsBalance.merging(other.updatedStarsBalance, uniquingKeysWith: { lhs, _ in lhs }), updatedStarsRevenueStatus: self.updatedStarsRevenueStatus.merging(other.updatedStarsRevenueStatus, uniquingKeysWith: { lhs, _ in lhs }), sentScheduledMessageIds: sentScheduledMessageIds, reportMessageDelivery: reportMessageDelivery)
|
var addedConferenceInvitationMessagesIds = self.addedConferenceInvitationMessagesIds
|
||||||
|
addedConferenceInvitationMessagesIds.append(contentsOf: other.addedConferenceInvitationMessagesIds)
|
||||||
|
|
||||||
|
return AccountFinalStateEvents(
|
||||||
|
addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds,
|
||||||
|
addedReactionEvents: self.addedReactionEvents + other.addedReactionEvents,
|
||||||
|
wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds,
|
||||||
|
deletedMessageIds: self.deletedMessageIds + other.deletedMessageIds,
|
||||||
|
updatedTypingActivities: self.updatedTypingActivities,
|
||||||
|
updatedWebpages: self.updatedWebpages,
|
||||||
|
updatedCalls: self.updatedCalls + other.updatedCalls,
|
||||||
|
addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData,
|
||||||
|
updatedGroupCallParticipants: self.updatedGroupCallParticipants + other.updatedGroupCallParticipants,
|
||||||
|
storyUpdates: self.storyUpdates + other.storyUpdates,
|
||||||
|
isContactUpdates: self.isContactUpdates + other.isContactUpdates,
|
||||||
|
displayAlerts: self.displayAlerts + other.displayAlerts,
|
||||||
|
dismissBotWebViews: self.dismissBotWebViews + other.dismissBotWebViews,
|
||||||
|
delayNotificatonsUntil: delayNotificatonsUntil,
|
||||||
|
updatedMaxMessageId: updatedMaxMessageId,
|
||||||
|
updatedQts: updatedQts,
|
||||||
|
externallyUpdatedPeerId: externallyUpdatedPeerId,
|
||||||
|
authorizationListUpdated: authorizationListUpdated,
|
||||||
|
updatedIncomingThreadReadStates: self.updatedIncomingThreadReadStates.merging(other.updatedIncomingThreadReadStates,uniquingKeysWith: { lhs, _ in lhs }),
|
||||||
|
updateConfig: updateConfig,
|
||||||
|
isPremiumUpdated: isPremiumUpdated,
|
||||||
|
updatedRevenueBalances: self.updatedRevenueBalances.merging(other.updatedRevenueBalances, uniquingKeysWith: { lhs, _ in lhs }),
|
||||||
|
updatedStarsBalance: self.updatedStarsBalance.merging(other.updatedStarsBalance, uniquingKeysWith: { lhs, _ in lhs }),
|
||||||
|
updatedStarsRevenueStatus: self.updatedStarsRevenueStatus.merging(other.updatedStarsRevenueStatus, uniquingKeysWith: { lhs, _ in lhs }),
|
||||||
|
sentScheduledMessageIds: sentScheduledMessageIds,
|
||||||
|
reportMessageDelivery: reportMessageDelivery,
|
||||||
|
addedConferenceInvitationMessagesIds: addedConferenceInvitationMessagesIds
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,6 +262,10 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
|||||||
result.append(boostPeer.peerId)
|
result.append(boostPeer.peerId)
|
||||||
case let .messageActionPaymentRefunded(_, peer, _, _, _, _):
|
case let .messageActionPaymentRefunded(_, peer, _, _, _, _):
|
||||||
result.append(peer.peerId)
|
result.append(peer.peerId)
|
||||||
|
case let .messageActionConferenceCall(_, _, _, otherParticipants):
|
||||||
|
if let otherParticipants {
|
||||||
|
result.append(contentsOf: otherParticipants.map(\.peerId))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -80,6 +80,8 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
|||||||
switch call {
|
switch call {
|
||||||
case let .inputGroupCall(id, accessHash):
|
case let .inputGroupCall(id, accessHash):
|
||||||
return TelegramMediaAction(action: .groupPhoneCall(callId: id, accessHash: accessHash, scheduleDate: nil, duration: duration))
|
return TelegramMediaAction(action: .groupPhoneCall(callId: id, accessHash: accessHash, scheduleDate: nil, duration: duration))
|
||||||
|
case .inputGroupCallSlug, .inputGroupCallInviteMessage:
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
case let .messageActionInviteToGroupCall(call, userIds):
|
case let .messageActionInviteToGroupCall(call, userIds):
|
||||||
switch call {
|
switch call {
|
||||||
@ -87,6 +89,8 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
|||||||
return TelegramMediaAction(action: .inviteToGroupPhoneCall(callId: id, accessHash: accessHash, peerIds: userIds.map { userId in
|
return TelegramMediaAction(action: .inviteToGroupPhoneCall(callId: id, accessHash: accessHash, peerIds: userIds.map { userId in
|
||||||
PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
|
PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
|
||||||
}))
|
}))
|
||||||
|
case .inputGroupCallSlug, .inputGroupCallInviteMessage:
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
case let .messageActionSetMessagesTTL(_, period, autoSettingFrom):
|
case let .messageActionSetMessagesTTL(_, period, autoSettingFrom):
|
||||||
return TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: period, autoSettingSource: autoSettingFrom.flatMap { PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value($0)) }))
|
return TelegramMediaAction(action: .messageAutoremoveTimeoutUpdated(period: period, autoSettingSource: autoSettingFrom.flatMap { PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value($0)) }))
|
||||||
@ -94,6 +98,8 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
|||||||
switch call {
|
switch call {
|
||||||
case let .inputGroupCall(id, accessHash):
|
case let .inputGroupCall(id, accessHash):
|
||||||
return TelegramMediaAction(action: .groupPhoneCall(callId: id, accessHash: accessHash, scheduleDate: scheduleDate, duration: nil))
|
return TelegramMediaAction(action: .groupPhoneCall(callId: id, accessHash: accessHash, scheduleDate: scheduleDate, duration: nil))
|
||||||
|
case .inputGroupCallSlug, .inputGroupCallInviteMessage:
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
case let .messageActionSetChatTheme(emoji):
|
case let .messageActionSetChatTheme(emoji):
|
||||||
return TelegramMediaAction(action: .setChatTheme(emoji: emoji))
|
return TelegramMediaAction(action: .setChatTheme(emoji: emoji))
|
||||||
@ -195,6 +201,12 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
|||||||
return TelegramMediaAction(action: .paidMessagesRefunded(count: count, stars: stars))
|
return TelegramMediaAction(action: .paidMessagesRefunded(count: count, stars: stars))
|
||||||
case let .messageActionPaidMessagesPrice(stars):
|
case let .messageActionPaidMessagesPrice(stars):
|
||||||
return TelegramMediaAction(action: .paidMessagesPriceEdited(stars: stars))
|
return TelegramMediaAction(action: .paidMessagesPriceEdited(stars: stars))
|
||||||
|
case let .messageActionConferenceCall(_, callId, duration, otherParticipants):
|
||||||
|
return TelegramMediaAction(action: .conferenceCall(
|
||||||
|
callId: callId,
|
||||||
|
duration: duration,
|
||||||
|
otherParticipants: otherParticipants.flatMap({ return $0.map(\.peerId) }) ?? []
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,7 +221,7 @@ extension PhoneCallDiscardReason {
|
|||||||
self = .hangup
|
self = .hangup
|
||||||
case .phoneCallDiscardReasonMissed:
|
case .phoneCallDiscardReasonMissed:
|
||||||
self = .missed
|
self = .missed
|
||||||
case .phoneCallDiscardReasonAllowGroupCall:
|
case .phoneCallDiscardReasonMigrateConferenceCall:
|
||||||
self = .hangup
|
self = .hangup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1629,10 +1629,16 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
|
|||||||
switch call {
|
switch call {
|
||||||
case let .inputGroupCall(id, accessHash):
|
case let .inputGroupCall(id, accessHash):
|
||||||
updatedState.updateGroupCallParticipants(id: id, accessHash: accessHash, participants: participants, version: version)
|
updatedState.updateGroupCallParticipants(id: id, accessHash: accessHash, participants: participants, version: version)
|
||||||
|
case .inputGroupCallSlug, .inputGroupCallInviteMessage:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
case let .updateGroupCall(_, channelId, call):
|
case let .updateGroupCall(_, channelId, call):
|
||||||
updatedState.updateGroupCall(peerId: channelId.flatMap { PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value($0)) }, call: call)
|
updatedState.updateGroupCall(peerId: channelId.flatMap { PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value($0)) }, call: call)
|
||||||
updatedState.updateGroupCall(peerId: channelId.flatMap { PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value($0)) }, call: call)
|
updatedState.updateGroupCall(peerId: channelId.flatMap { PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value($0)) }, call: call)
|
||||||
|
case let .updateGroupCallChainBlocks(call, subChainId, blocks, nextOffset):
|
||||||
|
if case let .inputGroupCall(id, accessHash) = call {
|
||||||
|
updatedState.updateGroupCallChainBlocks(id: id, accessHash: accessHash, subChainId: subChainId, blocks: blocks.map { $0.makeData() }, nextOffset: nextOffset)
|
||||||
|
}
|
||||||
case let .updatePeerHistoryTTL(_, peer, ttl):
|
case let .updatePeerHistoryTTL(_, peer, ttl):
|
||||||
updatedState.updateAutoremoveTimeout(peer: peer, value: CachedPeerAutoremoveTimeout.Value(ttl))
|
updatedState.updateAutoremoveTimeout(peer: peer, value: CachedPeerAutoremoveTimeout.Value(ttl))
|
||||||
case let .updateLangPackTooLong(langCode):
|
case let .updateLangPackTooLong(langCode):
|
||||||
@ -3322,7 +3328,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
|
|||||||
var currentAddQuickReplyMessages: OptimizeAddMessagesState?
|
var currentAddQuickReplyMessages: OptimizeAddMessagesState?
|
||||||
for operation in operations {
|
for operation in operations {
|
||||||
switch operation {
|
switch operation {
|
||||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateWallpaper, .UpdateRevenueBalances, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery:
|
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateWallpaper, .UpdateRevenueBalances, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery:
|
||||||
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
|
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
|
||||||
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
|
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
|
||||||
}
|
}
|
||||||
@ -3505,6 +3511,8 @@ func replayFinalState(
|
|||||||
var readInboxCloudMessageIds: [PeerId: Int32] = [:]
|
var readInboxCloudMessageIds: [PeerId: Int32] = [:]
|
||||||
|
|
||||||
var addedOperationIncomingMessageIds: [MessageId] = []
|
var addedOperationIncomingMessageIds: [MessageId] = []
|
||||||
|
var addedConferenceInvitationMessagesIds: [MessageId] = []
|
||||||
|
|
||||||
for operation in finalState.state.operations {
|
for operation in finalState.state.operations {
|
||||||
switch operation {
|
switch operation {
|
||||||
case let .AddMessages(messages, location):
|
case let .AddMessages(messages, location):
|
||||||
@ -3526,6 +3534,10 @@ func replayFinalState(
|
|||||||
recordPeerActivityTimestamp(peerId: authorId, timestamp: message.timestamp, into: &peerActivityTimestamps)
|
recordPeerActivityTimestamp(peerId: authorId, timestamp: message.timestamp, into: &peerActivityTimestamps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if id.namespace == Namespaces.Message.Cloud && id.peerId.namespace == Namespaces.Peer.CloudUser {
|
||||||
|
addedConferenceInvitationMessagesIds.append(id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if message.flags.contains(.WasScheduled) {
|
if message.flags.contains(.WasScheduled) {
|
||||||
wasOperationScheduledMessageIds.append(id)
|
wasOperationScheduledMessageIds.append(id)
|
||||||
@ -4505,7 +4517,7 @@ func replayFinalState(
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch call {
|
switch call {
|
||||||
case let .groupCall(flags, _, _, participantsCount, title, _, recordStartDate, scheduleDate, _, _, _, _):
|
case let .groupCall(flags, _, _, participantsCount, title, _, recordStartDate, scheduleDate, _, _, _):
|
||||||
let isMuted = (flags & (1 << 1)) != 0
|
let isMuted = (flags & (1 << 1)) != 0
|
||||||
let canChange = (flags & (1 << 2)) != 0
|
let canChange = (flags & (1 << 2)) != 0
|
||||||
let isVideoEnabled = (flags & (1 << 9)) != 0
|
let isVideoEnabled = (flags & (1 << 9)) != 0
|
||||||
@ -4544,6 +4556,11 @@ func replayFinalState(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case let .UpdateGroupCallChainBlocks(id, _, subChainId, blocks, nextOffset):
|
||||||
|
updatedGroupCallParticipants.append((
|
||||||
|
id,
|
||||||
|
.conferenceChainBlocks(subChainId: Int(subChainId), blocks: blocks, nextOffset: Int(nextOffset))
|
||||||
|
))
|
||||||
case let .UpdateAutoremoveTimeout(peer, autoremoveValue):
|
case let .UpdateAutoremoveTimeout(peer, autoremoveValue):
|
||||||
let peerId = peer.peerId
|
let peerId = peer.peerId
|
||||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||||
@ -5420,6 +5437,7 @@ func replayFinalState(
|
|||||||
updatedStarsBalance: updatedStarsBalance,
|
updatedStarsBalance: updatedStarsBalance,
|
||||||
updatedStarsRevenueStatus: updatedStarsRevenueStatus,
|
updatedStarsRevenueStatus: updatedStarsRevenueStatus,
|
||||||
sentScheduledMessageIds: finalState.state.sentScheduledMessageIds,
|
sentScheduledMessageIds: finalState.state.sentScheduledMessageIds,
|
||||||
reportMessageDelivery: reportMessageDelivery
|
reportMessageDelivery: reportMessageDelivery,
|
||||||
|
addedConferenceInvitationMessagesIds: addedConferenceInvitationMessagesIds
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1145,6 +1145,9 @@ public final class AccountStateManager {
|
|||||||
if !events.reportMessageDelivery.isEmpty {
|
if !events.reportMessageDelivery.isEmpty {
|
||||||
strongSelf.reportMessageDeliveryDisposable.add(_internal_reportMessageDelivery(postbox: strongSelf.postbox, network: strongSelf.network, messageIds: Array(events.reportMessageDelivery), fromPushNotification: false).start())
|
strongSelf.reportMessageDeliveryDisposable.add(_internal_reportMessageDelivery(postbox: strongSelf.postbox, network: strongSelf.network, messageIds: Array(events.reportMessageDelivery), fromPushNotification: false).start())
|
||||||
}
|
}
|
||||||
|
if !events.addedConferenceInvitationMessagesIds.isEmpty {
|
||||||
|
strongSelf.callSessionManager?.addConferenceInvitationMessages(ids: events.addedConferenceInvitationMessagesIds)
|
||||||
|
}
|
||||||
if !events.isContactUpdates.isEmpty {
|
if !events.isContactUpdates.isEmpty {
|
||||||
strongSelf.addIsContactUpdates(events.isContactUpdates)
|
strongSelf.addIsContactUpdates(events.isContactUpdates)
|
||||||
}
|
}
|
||||||
@ -2203,7 +2206,7 @@ public final class AccountStateManager {
|
|||||||
switch update {
|
switch update {
|
||||||
case let .updatePhoneCall(phoneCall):
|
case let .updatePhoneCall(phoneCall):
|
||||||
switch phoneCall {
|
switch phoneCall {
|
||||||
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, _, _, conferenceCall):
|
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, _, _):
|
||||||
guard let peer = peers.first(where: { $0.id == PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)) }) else {
|
guard let peer = peers.first(where: { $0.id == PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)) }) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -2213,7 +2216,7 @@ public final class AccountStateManager {
|
|||||||
timestamp: date,
|
timestamp: date,
|
||||||
peer: EnginePeer(peer),
|
peer: EnginePeer(peer),
|
||||||
isVideo: (flags & (1 << 6)) != 0,
|
isVideo: (flags & (1 << 6)) != 0,
|
||||||
isConference: conferenceCall != nil
|
isConference: false
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
|
@ -4,7 +4,6 @@ import MtProtoKit
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import TelegramApi
|
import TelegramApi
|
||||||
|
|
||||||
|
|
||||||
private let minLayer: Int32 = 65
|
private let minLayer: Int32 = 65
|
||||||
|
|
||||||
public enum CallSessionError: Equatable {
|
public enum CallSessionError: Equatable {
|
||||||
@ -15,11 +14,11 @@ public enum CallSessionError: Equatable {
|
|||||||
case disconnected
|
case disconnected
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum CallSessionEndedType {
|
public enum CallSessionEndedType: Equatable {
|
||||||
case hungUp
|
case hungUp
|
||||||
case busy
|
case busy
|
||||||
case missed
|
case missed
|
||||||
case switchedToConference
|
case switchedToConference(slug: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum CallSessionTerminationReason: Equatable {
|
public enum CallSessionTerminationReason: Equatable {
|
||||||
@ -65,41 +64,47 @@ public struct GroupCallReference: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension GroupCallReference {
|
extension GroupCallReference {
|
||||||
init(_ apiGroupCall: Api.InputGroupCall) {
|
init?(_ apiGroupCall: Api.InputGroupCall) {
|
||||||
switch apiGroupCall {
|
switch apiGroupCall {
|
||||||
case let .inputGroupCall(id, accessHash):
|
case let .inputGroupCall(id, accessHash):
|
||||||
self.init(id: id, accessHash: accessHash)
|
self.init(id: id, accessHash: accessHash)
|
||||||
|
case .inputGroupCallSlug, .inputGroupCallInviteMessage:
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var apiInputGroupCall: Api.InputGroupCall {
|
||||||
|
return .inputGroupCall(id: self.id, accessHash: self.accessHash)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CallSessionInternalState {
|
enum CallSessionInternalState {
|
||||||
case ringing(id: Int64, accessHash: Int64, gAHash: Data, b: Data, versions: [String], conferenceCall: GroupCallReference?)
|
case ringing(id: Int64, accessHash: Int64, gAHash: Data, b: Data, versions: [String])
|
||||||
case accepting(id: Int64, accessHash: Int64, gAHash: Data, b: Data, conferenceCall: GroupCallReference?, disposable: Disposable)
|
case accepting(id: Int64, accessHash: Int64, gAHash: Data, b: Data, disposable: Disposable)
|
||||||
case awaitingConfirmation(id: Int64, accessHash: Int64, gAHash: Data, b: Data, config: SecretChatEncryptionConfig)
|
case awaitingConfirmation(id: Int64, accessHash: Int64, gAHash: Data, b: Data, config: SecretChatEncryptionConfig)
|
||||||
case requesting(a: Data, conferenceCall: GroupCallReference?, disposable: Disposable)
|
case requesting(a: Data, disposable: Disposable)
|
||||||
case requested(id: Int64, accessHash: Int64, a: Data, gA: Data, config: SecretChatEncryptionConfig, remoteConfirmationTimestamp: Int32?, conferenceCall: GroupCallReference?)
|
case requested(id: Int64, accessHash: Int64, a: Data, gA: Data, config: SecretChatEncryptionConfig, remoteConfirmationTimestamp: Int32?)
|
||||||
case confirming(id: Int64, accessHash: Int64, key: Data, keyId: Int64, keyVisualHash: Data, conferenceCall: GroupCallReference?, disposable: Disposable)
|
case confirming(id: Int64, accessHash: Int64, key: Data, keyId: Int64, keyVisualHash: Data, disposable: Disposable)
|
||||||
case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?, willSwitchToConference: Bool)
|
case active(id: Int64, accessHash: Int64, beginTimestamp: Int32, key: Data, keyId: Int64, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool)
|
||||||
case switchedToConference(key: Data, keyVisualHash: Data, conferenceCall: GroupCallReference)
|
case switchedToConference(slug: String)
|
||||||
case dropping(reason: CallSessionTerminationReason, disposable: Disposable)
|
case dropping(reason: CallSessionTerminationReason, disposable: Disposable)
|
||||||
case terminated(id: Int64?, accessHash: Int64?, reason: CallSessionTerminationReason, reportRating: Bool, sendDebugLogs: Bool)
|
case terminated(id: Int64?, accessHash: Int64?, reason: CallSessionTerminationReason, reportRating: Bool, sendDebugLogs: Bool)
|
||||||
|
|
||||||
var stableId: Int64? {
|
var stableId: Int64? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .ringing(id, _, _, _, _, _):
|
case let .ringing(id, _, _, _, _):
|
||||||
return id
|
return id
|
||||||
case let .accepting(id, _, _, _, _, _):
|
case let .accepting(id, _, _, _, _):
|
||||||
return id
|
return id
|
||||||
case let .awaitingConfirmation(id, _, _, _, _):
|
case let .awaitingConfirmation(id, _, _, _, _):
|
||||||
return id
|
return id
|
||||||
case .requesting:
|
case .requesting:
|
||||||
return nil
|
return nil
|
||||||
case let .requested(id, _, _, _, _, _, _):
|
case let .requested(id, _, _, _, _, _):
|
||||||
return id
|
return id
|
||||||
case let .confirming(id, _, _, _, _, _, _):
|
case let .confirming(id, _, _, _, _, _):
|
||||||
return id
|
return id
|
||||||
case let .active(id, _, _, _, _, _, _, _, _, _, _, _, _):
|
case let .active(id, _, _, _, _, _, _, _, _, _, _):
|
||||||
return id
|
return id
|
||||||
case .switchedToConference:
|
case .switchedToConference:
|
||||||
return nil
|
return nil
|
||||||
@ -138,7 +143,8 @@ public struct CallSessionRingingState: Equatable {
|
|||||||
public let peerId: PeerId
|
public let peerId: PeerId
|
||||||
public let isVideo: Bool
|
public let isVideo: Bool
|
||||||
public let isVideoPossible: Bool
|
public let isVideoPossible: Bool
|
||||||
public let isIncomingConference: Bool
|
public let conferenceSource: MessageId?
|
||||||
|
public let otherParticipants: [EnginePeer]
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum DropCallReason {
|
public enum DropCallReason {
|
||||||
@ -166,9 +172,9 @@ public struct CallTerminationOptions: OptionSet {
|
|||||||
public enum CallSessionState {
|
public enum CallSessionState {
|
||||||
case ringing
|
case ringing
|
||||||
case accepting
|
case accepting
|
||||||
case requesting(ringing: Bool, conferenceCall: GroupCallReference?)
|
case requesting(ringing: Bool)
|
||||||
case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?, willSwitchToConference: Bool)
|
case active(id: CallId, key: Data, keyVisualHash: Data, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool)
|
||||||
case switchedToConference(key: Data, keyVisualHash: Data, conferenceCall: GroupCallReference)
|
case switchedToConference(slug: String)
|
||||||
case dropping(reason: CallSessionTerminationReason)
|
case dropping(reason: CallSessionTerminationReason)
|
||||||
case terminated(id: CallId?, reason: CallSessionTerminationReason, options: CallTerminationOptions)
|
case terminated(id: CallId?, reason: CallSessionTerminationReason, options: CallTerminationOptions)
|
||||||
|
|
||||||
@ -178,17 +184,20 @@ public enum CallSessionState {
|
|||||||
self = .ringing
|
self = .ringing
|
||||||
case .accepting, .awaitingConfirmation:
|
case .accepting, .awaitingConfirmation:
|
||||||
self = .accepting
|
self = .accepting
|
||||||
case let .requesting(_, conferenceCall, _):
|
case .requesting:
|
||||||
self = .requesting(ringing: false, conferenceCall: conferenceCall)
|
self = .requesting(ringing: false)
|
||||||
case let .confirming(_, _, _, _, _, conferenceCall, _):
|
case .confirming:
|
||||||
self = .requesting(ringing: true, conferenceCall: conferenceCall)
|
self = .requesting(ringing: true)
|
||||||
case let .requested(_, _, _, _, _, remoteConfirmationTimestamp, conferenceCall):
|
case let .requested(_, _, _, _, _, remoteConfirmationTimestamp):
|
||||||
self = .requesting(ringing: remoteConfirmationTimestamp != nil, conferenceCall: conferenceCall)
|
self = .requesting(ringing: remoteConfirmationTimestamp != nil)
|
||||||
case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P, conferenceCall, willSwitchToConference):
|
case let .active(id, accessHash, _, key, _, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P):
|
||||||
self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: conferenceCall, willSwitchToConference: willSwitchToConference)
|
self = .active(id: CallId(id: id, accessHash: accessHash), key: key, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P)
|
||||||
case let .dropping(reason, _):
|
case let .dropping(reason, _):
|
||||||
self = .dropping(reason: reason)
|
self = .dropping(reason: reason)
|
||||||
case let .terminated(id, accessHash, reason, reportRating, sendDebugLogs):
|
case let .terminated(id, accessHash, reason, reportRating, sendDebugLogs):
|
||||||
|
if case let .ended(endedReason) = reason, case let .switchedToConference(slug) = endedReason {
|
||||||
|
self = .switchedToConference(slug: slug)
|
||||||
|
} else {
|
||||||
var options = CallTerminationOptions()
|
var options = CallTerminationOptions()
|
||||||
if reportRating {
|
if reportRating {
|
||||||
options.insert(.reportRating)
|
options.insert(.reportRating)
|
||||||
@ -203,8 +212,9 @@ public enum CallSessionState {
|
|||||||
callId = nil
|
callId = nil
|
||||||
}
|
}
|
||||||
self = .terminated(id: callId, reason: reason, options: options)
|
self = .terminated(id: callId, reason: reason, options: options)
|
||||||
case let .switchedToConference(key, keyVisualHash, conferenceCall):
|
}
|
||||||
self = .switchedToConference(key: key, keyVisualHash: keyVisualHash, conferenceCall: conferenceCall)
|
case let .switchedToConference(slug):
|
||||||
|
self = .switchedToConference(slug: slug)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,7 +232,7 @@ public struct CallSession {
|
|||||||
public let state: CallSessionState
|
public let state: CallSessionState
|
||||||
public let isVideoPossible: Bool
|
public let isVideoPossible: Bool
|
||||||
|
|
||||||
init(
|
public init(
|
||||||
id: CallSessionInternalId,
|
id: CallSessionInternalId,
|
||||||
stableId: Int64?,
|
stableId: Int64?,
|
||||||
isOutgoing: Bool,
|
isOutgoing: Bool,
|
||||||
@ -336,17 +346,15 @@ private func parseConnectionSet(primary: Api.PhoneConnection, alternative: [Api.
|
|||||||
private final class CallSessionContext {
|
private final class CallSessionContext {
|
||||||
let peerId: PeerId
|
let peerId: PeerId
|
||||||
let isOutgoing: Bool
|
let isOutgoing: Bool
|
||||||
let isIncomingConference: Bool
|
|
||||||
var type: CallSession.CallType
|
var type: CallSession.CallType
|
||||||
var isVideoPossible: Bool
|
var isVideoPossible: Bool
|
||||||
let pendingConference: (conference: GroupCallReference, encryptionKey: Data)?
|
|
||||||
var state: CallSessionInternalState
|
var state: CallSessionInternalState
|
||||||
let subscribers = Bag<(CallSession) -> Void>()
|
let subscribers = Bag<(CallSession) -> Void>()
|
||||||
var signalingReceiver: (([Data]) -> Void)?
|
var signalingReceiver: (([Data]) -> Void)?
|
||||||
|
|
||||||
let signalingDisposables = DisposableSet()
|
let signalingDisposables = DisposableSet()
|
||||||
var createConferenceCallDisposable: Disposable?
|
|
||||||
let acknowledgeIncomingCallDisposable = MetaDisposable()
|
let acknowledgeIncomingCallDisposable = MetaDisposable()
|
||||||
|
var createConferenceCallDisposable: Disposable?
|
||||||
|
|
||||||
var isEmpty: Bool {
|
var isEmpty: Bool {
|
||||||
if case .terminated = self.state {
|
if case .terminated = self.state {
|
||||||
@ -356,13 +364,11 @@ private final class CallSessionContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(peerId: PeerId, isOutgoing: Bool, isIncomingConference: Bool, type: CallSession.CallType, isVideoPossible: Bool, pendingConference: (conference: GroupCallReference, encryptionKey: Data)?, state: CallSessionInternalState) {
|
init(peerId: PeerId, isOutgoing: Bool, type: CallSession.CallType, isVideoPossible: Bool, state: CallSessionInternalState) {
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.isOutgoing = isOutgoing
|
self.isOutgoing = isOutgoing
|
||||||
self.isIncomingConference = isIncomingConference
|
|
||||||
self.type = type
|
self.type = type
|
||||||
self.isVideoPossible = isVideoPossible
|
self.isVideoPossible = isVideoPossible
|
||||||
self.pendingConference = pendingConference
|
|
||||||
self.state = state
|
self.state = state
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,6 +379,73 @@ private final class CallSessionContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class IncomingConferenceInvitationContext {
|
||||||
|
enum State: Equatable {
|
||||||
|
case pending
|
||||||
|
case ringing(callId: Int64, otherParticipants: [EnginePeer])
|
||||||
|
case stopped
|
||||||
|
}
|
||||||
|
|
||||||
|
private let queue: Queue
|
||||||
|
private var disposable: Disposable?
|
||||||
|
|
||||||
|
let internalId: CallSessionInternalId
|
||||||
|
|
||||||
|
private(set) var state: State = .pending
|
||||||
|
|
||||||
|
init(queue: Queue, postbox: Postbox, messageId: MessageId, updated: @escaping () -> Void) {
|
||||||
|
self.queue = queue
|
||||||
|
|
||||||
|
self.internalId = CallSessionInternalId()
|
||||||
|
|
||||||
|
let key = PostboxViewKey.messages(Set([messageId]))
|
||||||
|
self.disposable = (postbox.combinedView(keys: [key])
|
||||||
|
|> map { view -> Message? in
|
||||||
|
guard let view = view.views[key] as? MessagesView else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return view.messages[messageId]
|
||||||
|
}
|
||||||
|
|> deliverOn(self.queue)).startStrict(next: { [weak self] message in
|
||||||
|
guard let self = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let state: State
|
||||||
|
if let message = message {
|
||||||
|
var foundAction: TelegramMediaAction?
|
||||||
|
for media in message.media {
|
||||||
|
if let action = media as? TelegramMediaAction {
|
||||||
|
foundAction = action
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let action = foundAction, case let .conferenceCall(callId, duration, otherParticipants) = action.action {
|
||||||
|
if duration != nil {
|
||||||
|
state = .stopped
|
||||||
|
} else {
|
||||||
|
state = .ringing(callId: callId, otherParticipants: otherParticipants.compactMap { id -> EnginePeer? in
|
||||||
|
return message.peers[id].flatMap(EnginePeer.init)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state = .stopped
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state = .stopped
|
||||||
|
}
|
||||||
|
if self.state != state {
|
||||||
|
self.state = state
|
||||||
|
updated()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.disposable?.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func selectVersionOnAccept(localVersions: [CallSessionManagerImplementationVersion], remoteVersions: [String]) -> [String]? {
|
private func selectVersionOnAccept(localVersions: [CallSessionManagerImplementationVersion], remoteVersions: [String]) -> [String]? {
|
||||||
let filteredVersions = localVersions.map({ $0.version }).filter(remoteVersions.contains)
|
let filteredVersions = localVersions.map({ $0.version }).filter(remoteVersions.contains)
|
||||||
if filteredVersions.isEmpty {
|
if filteredVersions.isEmpty {
|
||||||
@ -406,9 +479,12 @@ private final class CallSessionManagerContext {
|
|||||||
private var futureContextSubscribers: [CallSessionInternalId: Bag<(CallSession) -> Void>] = [:]
|
private var futureContextSubscribers: [CallSessionInternalId: Bag<(CallSession) -> Void>] = [:]
|
||||||
private var contextIdByStableId: [CallSessionStableId: CallSessionInternalId] = [:]
|
private var contextIdByStableId: [CallSessionStableId: CallSessionInternalId] = [:]
|
||||||
|
|
||||||
|
private var incomingConferenceInvitationContexts: [MessageId: IncomingConferenceInvitationContext] = [:]
|
||||||
|
|
||||||
private var enqueuedSignalingData: [Int64: [Data]] = [:]
|
private var enqueuedSignalingData: [Int64: [Data]] = [:]
|
||||||
|
|
||||||
private let disposables = DisposableSet()
|
private let disposables = DisposableSet()
|
||||||
|
private let rejectConferenceInvitationDisposables = DisposableSet()
|
||||||
|
|
||||||
init(queue: Queue, postbox: Postbox, network: Network, accountPeerId: PeerId, maxLayer: Int32, versions: [CallSessionManagerImplementationVersion], addUpdates: @escaping (Api.Updates) -> Void) {
|
init(queue: Queue, postbox: Postbox, network: Network, accountPeerId: PeerId, maxLayer: Int32, versions: [CallSessionManagerImplementationVersion], addUpdates: @escaping (Api.Updates) -> Void) {
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
@ -423,6 +499,7 @@ private final class CallSessionManagerContext {
|
|||||||
deinit {
|
deinit {
|
||||||
assert(self.queue.isCurrent())
|
assert(self.queue.isCurrent())
|
||||||
self.disposables.dispose()
|
self.disposables.dispose()
|
||||||
|
self.rejectConferenceInvitationDisposables.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateVersions(versions: [CallSessionManagerImplementationVersion]) {
|
func updateVersions(versions: [CallSessionManagerImplementationVersion]) {
|
||||||
@ -556,7 +633,20 @@ private final class CallSessionManagerContext {
|
|||||||
peerId: context.peerId,
|
peerId: context.peerId,
|
||||||
isVideo: context.type == .video,
|
isVideo: context.type == .video,
|
||||||
isVideoPossible: context.isVideoPossible,
|
isVideoPossible: context.isVideoPossible,
|
||||||
isIncomingConference: context.isIncomingConference
|
conferenceSource: nil,
|
||||||
|
otherParticipants: []
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (id, context) in self.incomingConferenceInvitationContexts {
|
||||||
|
if case let .ringing(_, otherParticipants) = context.state {
|
||||||
|
ringingContexts.append(CallSessionRingingState(
|
||||||
|
id: context.internalId,
|
||||||
|
peerId: id.peerId,
|
||||||
|
isVideo: false,
|
||||||
|
isVideoPossible: true,
|
||||||
|
conferenceSource: id,
|
||||||
|
otherParticipants: otherParticipants
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -584,7 +674,7 @@ private final class CallSessionManagerContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addIncoming(peerId: PeerId, stableId: CallSessionStableId, accessHash: Int64, timestamp: Int32, gAHash: Data, versions: [String], isVideo: Bool, conferenceCall: GroupCallReference?) -> CallSessionInternalId? {
|
private func addIncoming(peerId: PeerId, stableId: CallSessionStableId, accessHash: Int64, timestamp: Int32, gAHash: Data, versions: [String], isVideo: Bool) -> CallSessionInternalId? {
|
||||||
if self.contextIdByStableId[stableId] != nil {
|
if self.contextIdByStableId[stableId] != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -594,13 +684,10 @@ private final class CallSessionManagerContext {
|
|||||||
let b = Data(bytesNoCopy: bBytes, count: 256, deallocator: .free)
|
let b = Data(bytesNoCopy: bBytes, count: 256, deallocator: .free)
|
||||||
|
|
||||||
if randomStatus == 0 {
|
if randomStatus == 0 {
|
||||||
var isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
let isVideoPossible = true
|
||||||
//#if DEBUG
|
|
||||||
isVideoPossible = true
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
let internalId = CallSessionManager.getStableIncomingUUID(stableId: stableId)
|
let internalId = CallSessionManager.getStableIncomingUUID(stableId: stableId)
|
||||||
let context = CallSessionContext(peerId: peerId, isOutgoing: false, isIncomingConference: conferenceCall != nil, type: isVideo ? .video : .audio, isVideoPossible: isVideoPossible, pendingConference: nil, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b, versions: versions, conferenceCall: conferenceCall))
|
let context = CallSessionContext(peerId: peerId, isOutgoing: false, type: isVideo ? .video : .audio, isVideoPossible: isVideoPossible, state: .ringing(id: stableId, accessHash: accessHash, gAHash: gAHash, b: b, versions: versions))
|
||||||
self.contexts[internalId] = context
|
self.contexts[internalId] = context
|
||||||
let queue = self.queue
|
let queue = self.queue
|
||||||
|
|
||||||
@ -632,12 +719,36 @@ private final class CallSessionManagerContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func drop(internalId: CallSessionInternalId, reason: DropCallReason, debugLog: Signal<String?, NoError>) {
|
func drop(internalId: CallSessionInternalId, reason: DropCallReason, debugLog: Signal<String?, NoError>) {
|
||||||
|
for (id, context) in self.incomingConferenceInvitationContexts {
|
||||||
|
if context.internalId == internalId {
|
||||||
|
self.incomingConferenceInvitationContexts.removeValue(forKey: id)
|
||||||
|
self.ringingStatesUpdated()
|
||||||
|
|
||||||
|
let addUpdates = self.addUpdates
|
||||||
|
let rejectSignal = self.network.request(Api.functions.phone.declineConferenceCallInvite(msgId: id.id))
|
||||||
|
|> map(Optional.init)
|
||||||
|
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
|> mapToSignal { updates -> Signal<Never, NoError> in
|
||||||
|
if let updates {
|
||||||
|
addUpdates(updates)
|
||||||
|
}
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.rejectConferenceInvitationDisposables.add(rejectSignal.startStrict())
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let context = self.contexts[internalId] {
|
if let context = self.contexts[internalId] {
|
||||||
var dropData: (CallSessionStableId, Int64, DropCallSessionReason)?
|
var dropData: (CallSessionStableId, Int64, DropCallSessionReason)?
|
||||||
var wasRinging = false
|
var wasRinging = false
|
||||||
let isVideo = context.type == .video
|
let isVideo = context.type == .video
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case let .ringing(id, accessHash, _, _, _, _):
|
case let .ringing(id, accessHash, _, _, _):
|
||||||
wasRinging = true
|
wasRinging = true
|
||||||
let internalReason: DropCallSessionReason
|
let internalReason: DropCallSessionReason
|
||||||
switch reason {
|
switch reason {
|
||||||
@ -651,10 +762,10 @@ private final class CallSessionManagerContext {
|
|||||||
internalReason = .missed
|
internalReason = .missed
|
||||||
}
|
}
|
||||||
dropData = (id, accessHash, internalReason)
|
dropData = (id, accessHash, internalReason)
|
||||||
case let .accepting(id, accessHash, _, _, _, disposable):
|
case let .accepting(id, accessHash, _, _, disposable):
|
||||||
dropData = (id, accessHash, .abort)
|
dropData = (id, accessHash, .abort)
|
||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _, _, _, _, _):
|
case let .active(id, accessHash, beginTimestamp, _, _, _, _, _, _, _, _):
|
||||||
let duration = max(0, Int32(CFAbsoluteTimeGetCurrent()) - beginTimestamp)
|
let duration = max(0, Int32(CFAbsoluteTimeGetCurrent()) - beginTimestamp)
|
||||||
let internalReason: DropCallSessionReason
|
let internalReason: DropCallSessionReason
|
||||||
switch reason {
|
switch reason {
|
||||||
@ -672,10 +783,10 @@ private final class CallSessionManagerContext {
|
|||||||
break
|
break
|
||||||
case let .awaitingConfirmation(id, accessHash, _, _, _):
|
case let .awaitingConfirmation(id, accessHash, _, _, _):
|
||||||
dropData = (id, accessHash, .abort)
|
dropData = (id, accessHash, .abort)
|
||||||
case let .confirming(id, accessHash, _, _, _, _, disposable):
|
case let .confirming(id, accessHash, _, _, _, disposable):
|
||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
dropData = (id, accessHash, .abort)
|
dropData = (id, accessHash, .abort)
|
||||||
case let .requested(id, accessHash, _, _, _, _, _):
|
case let .requested(id, accessHash, _, _, _, _):
|
||||||
let internalReason: DropCallSessionReason
|
let internalReason: DropCallSessionReason
|
||||||
switch reason {
|
switch reason {
|
||||||
case .busy, .hangUp:
|
case .busy, .hangUp:
|
||||||
@ -686,7 +797,7 @@ private final class CallSessionManagerContext {
|
|||||||
internalReason = .missed
|
internalReason = .missed
|
||||||
}
|
}
|
||||||
dropData = (id, accessHash, internalReason)
|
dropData = (id, accessHash, internalReason)
|
||||||
case let .requesting(_, _, disposable):
|
case let .requesting(_, disposable):
|
||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
context.state = .terminated(id: nil, accessHash: nil, reason: .ended(.hungUp), reportRating: false, sendDebugLogs: false)
|
context.state = .terminated(id: nil, accessHash: nil, reason: .ended(.hungUp), reportRating: false, sendDebugLogs: false)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
@ -709,8 +820,8 @@ private final class CallSessionManagerContext {
|
|||||||
mappedReason = .ended(.hungUp)
|
mappedReason = .ended(.hungUp)
|
||||||
case .missed:
|
case .missed:
|
||||||
mappedReason = .ended(.missed)
|
mappedReason = .ended(.missed)
|
||||||
case .switchToConference:
|
case let .switchToConference(slug):
|
||||||
mappedReason = .ended(.switchedToConference)
|
mappedReason = .ended(.switchedToConference(slug: slug))
|
||||||
}
|
}
|
||||||
context.state = .dropping(reason: mappedReason, disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: reason)
|
context.state = .dropping(reason: mappedReason, disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: reason)
|
||||||
|> deliverOn(self.queue)).start(next: { [weak self] reportRating, sendDebugLogs in
|
|> deliverOn(self.queue)).start(next: { [weak self] reportRating, sendDebugLogs in
|
||||||
@ -751,12 +862,12 @@ private final class CallSessionManagerContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func dropToConference(internalId: CallSessionInternalId, encryptedGroupKey: Data) {
|
func dropToConference(internalId: CallSessionInternalId, slug: String) {
|
||||||
if let context = self.contexts[internalId] {
|
if let context = self.contexts[internalId] {
|
||||||
var dropData: (CallSessionStableId, Int64)?
|
var dropData: (CallSessionStableId, Int64)?
|
||||||
let isVideo = context.type == .video
|
let isVideo = context.type == .video
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, _, _):
|
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _):
|
||||||
dropData = (id, accessHash)
|
dropData = (id, accessHash)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
@ -764,11 +875,11 @@ private final class CallSessionManagerContext {
|
|||||||
|
|
||||||
if let (id, accessHash) = dropData {
|
if let (id, accessHash) = dropData {
|
||||||
self.contextIdByStableId.removeValue(forKey: id)
|
self.contextIdByStableId.removeValue(forKey: id)
|
||||||
context.state = .dropping(reason: .ended(.switchedToConference), disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: .switchToConference(encryptedGroupKey: encryptedGroupKey))
|
context.state = .dropping(reason: .ended(.switchedToConference(slug: slug)), disposable: (dropCallSession(network: self.network, addUpdates: self.addUpdates, stableId: id, accessHash: accessHash, isVideo: isVideo, reason: .switchToConference(slug: slug))
|
||||||
|> deliverOn(self.queue)).start(next: { [weak self] reportRating, sendDebugLogs in
|
|> deliverOn(self.queue)).start(next: { [weak self] reportRating, sendDebugLogs in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if let context = strongSelf.contexts[internalId] {
|
if let context = strongSelf.contexts[internalId] {
|
||||||
context.state = .terminated(id: id, accessHash: accessHash, reason: .ended(.hungUp), reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
context.state = .switchedToConference(slug: slug)
|
||||||
strongSelf.contextUpdated(internalId: internalId)
|
strongSelf.contextUpdated(internalId: internalId)
|
||||||
if context.isEmpty {
|
if context.isEmpty {
|
||||||
strongSelf.contexts.removeValue(forKey: internalId)
|
strongSelf.contexts.removeValue(forKey: internalId)
|
||||||
@ -793,9 +904,9 @@ private final class CallSessionManagerContext {
|
|||||||
func accept(internalId: CallSessionInternalId) {
|
func accept(internalId: CallSessionInternalId) {
|
||||||
if let context = self.contexts[internalId] {
|
if let context = self.contexts[internalId] {
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case let .ringing(id, accessHash, gAHash, b, _, conferenceCall):
|
case let .ringing(id, accessHash, gAHash, b, _):
|
||||||
let acceptVersions = self.versions.map({ $0.version })
|
let acceptVersions = self.versions.map({ $0.version })
|
||||||
context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, conferenceCall: conferenceCall, disposable: (acceptCallSession(accountPeerId: self.accountPeerId, postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b, maxLayer: self.maxLayer, versions: acceptVersions) |> deliverOn(self.queue)).start(next: { [weak self] result in
|
context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, disposable: (acceptCallSession(accountPeerId: self.accountPeerId, postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b, maxLayer: self.maxLayer, versions: acceptVersions) |> deliverOn(self.queue)).start(next: { [weak self] result in
|
||||||
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
|
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
|
||||||
if case .accepting = context.state {
|
if case .accepting = context.state {
|
||||||
switch result {
|
switch result {
|
||||||
@ -806,9 +917,9 @@ private final class CallSessionManagerContext {
|
|||||||
case let .waiting(config):
|
case let .waiting(config):
|
||||||
context.state = .awaitingConfirmation(id: id, accessHash: accessHash, gAHash: gAHash, b: b, config: config)
|
context.state = .awaitingConfirmation(id: id, accessHash: accessHash, gAHash: gAHash, b: b, config: config)
|
||||||
strongSelf.contextUpdated(internalId: internalId)
|
strongSelf.contextUpdated(internalId: internalId)
|
||||||
case let .call(config, gA, timestamp, connections, maxLayer, version, customParameters, allowsP2P, conferenceCall):
|
case let .call(config, gA, timestamp, connections, maxLayer, version, customParameters, allowsP2P):
|
||||||
if let (key, keyId, keyVisualHash) = strongSelf.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gA) {
|
if let (key, keyId, keyVisualHash) = strongSelf.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gA) {
|
||||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: timestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: conferenceCall, willSwitchToConference: context.isIncomingConference)
|
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: timestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P)
|
||||||
strongSelf.contextUpdated(internalId: internalId)
|
strongSelf.contextUpdated(internalId: internalId)
|
||||||
} else {
|
} else {
|
||||||
strongSelf.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
strongSelf.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||||
@ -829,7 +940,7 @@ private final class CallSessionManagerContext {
|
|||||||
func sendSignalingData(internalId: CallSessionInternalId, data: Data) {
|
func sendSignalingData(internalId: CallSessionInternalId, data: Data) {
|
||||||
if let context = self.contexts[internalId] {
|
if let context = self.contexts[internalId] {
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, _, _):
|
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _):
|
||||||
context.signalingDisposables.add(self.network.request(Api.functions.phone.sendSignalingData(peer: .inputPhoneCall(id: id, accessHash: accessHash), data: Buffer(data: data))).start())
|
context.signalingDisposables.add(self.network.request(Api.functions.phone.sendSignalingData(peer: .inputPhoneCall(id: id, accessHash: accessHash), data: Buffer(data: data))).start())
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
@ -845,33 +956,29 @@ private final class CallSessionManagerContext {
|
|||||||
|
|
||||||
var idAndAccessHash: (id: Int64, accessHash: Int64)?
|
var idAndAccessHash: (id: Int64, accessHash: Int64)?
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, conferenceCall, _):
|
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _):
|
||||||
if conferenceCall != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
idAndAccessHash = (id, accessHash)
|
idAndAccessHash = (id, accessHash)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (id, accessHash) = idAndAccessHash {
|
if idAndAccessHash != nil {
|
||||||
context.createConferenceCallDisposable = (createConferenceCall(postbox: self.postbox, network: self.network, accountPeerId: self.accountPeerId, callId: CallId(id: id, accessHash: accessHash))
|
context.createConferenceCallDisposable = (_internal_createConferenceCall(
|
||||||
|
postbox: self.postbox,
|
||||||
|
network: self.network,
|
||||||
|
accountPeerId: self.accountPeerId
|
||||||
|
)
|
||||||
|> deliverOn(self.queue)).startStrict(next: { [weak self] result in
|
|> deliverOn(self.queue)).startStrict(next: { [weak self] result in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let context = self.contexts[internalId] else {
|
|
||||||
|
self.dropToConference(internalId: internalId, slug: result.slug)
|
||||||
|
}, error: { [weak self] error in
|
||||||
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let result {
|
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||||
switch context.state {
|
|
||||||
case let .active(id, accessHash, beginTimestamp, key, keyId, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P, _, _):
|
|
||||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: beginTimestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: result, willSwitchToConference: false)
|
|
||||||
self.contextUpdated(internalId: internalId)
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -889,7 +996,7 @@ private final class CallSessionManagerContext {
|
|||||||
switch call {
|
switch call {
|
||||||
case .phoneCallEmpty:
|
case .phoneCallEmpty:
|
||||||
break
|
break
|
||||||
case let .phoneCallAccepted(_, id, _, _, _, _, gB, remoteProtocol, conferenceCall):
|
case let .phoneCallAccepted(_, id, _, _, _, _, gB, remoteProtocol):
|
||||||
let remoteVersions: [String]
|
let remoteVersions: [String]
|
||||||
switch remoteProtocol {
|
switch remoteProtocol {
|
||||||
case let .phoneCallProtocol(_, _, _, versions):
|
case let .phoneCallProtocol(_, _, _, versions):
|
||||||
@ -903,7 +1010,7 @@ private final class CallSessionManagerContext {
|
|||||||
|
|
||||||
if let context = self.contexts[internalId] {
|
if let context = self.contexts[internalId] {
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case let .requested(_, accessHash, a, gA, config, _, _):
|
case let .requested(_, accessHash, a, gA, config, _):
|
||||||
let p = config.p.makeData()
|
let p = config.p.makeData()
|
||||||
if !MTCheckIsSafeGAOrB(self.network.encryptionProvider, gA, p) {
|
if !MTCheckIsSafeGAOrB(self.network.encryptionProvider, gA, p) {
|
||||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||||
@ -928,14 +1035,10 @@ private final class CallSessionManagerContext {
|
|||||||
|
|
||||||
let keyVisualHash = MTSha256(key + gA)
|
let keyVisualHash = MTSha256(key + gA)
|
||||||
|
|
||||||
context.state = .confirming(id: id, accessHash: accessHash, key: key, keyId: keyId, keyVisualHash: keyVisualHash, conferenceCall: conferenceCall.flatMap(GroupCallReference.init), disposable: (confirmCallSession(network: self.network, stableId: id, accessHash: accessHash, gA: gA, keyFingerprint: keyId, maxLayer: self.maxLayer, versions: selectedVersions) |> deliverOnMainQueue).start(next: { [weak self] updatedCall in
|
context.state = .confirming(id: id, accessHash: accessHash, key: key, keyId: keyId, keyVisualHash: keyVisualHash, disposable: (confirmCallSession(network: self.network, stableId: id, accessHash: accessHash, gA: gA, keyFingerprint: keyId, maxLayer: self.maxLayer, versions: selectedVersions) |> deliverOnMainQueue).start(next: { [weak self] updatedCall in
|
||||||
if let strongSelf = self, let context = strongSelf.contexts[internalId], case .confirming = context.state {
|
if let strongSelf = self, let context = strongSelf.contexts[internalId], case .confirming = context.state {
|
||||||
if let updatedCall = updatedCall {
|
if let updatedCall = updatedCall {
|
||||||
strongSelf.updateSession(updatedCall, completion: { _ in })
|
strongSelf.updateSession(updatedCall, completion: { _ in })
|
||||||
|
|
||||||
if let pendingConference = context.pendingConference {
|
|
||||||
strongSelf.dropToConference(internalId: internalId, encryptedGroupKey: pendingConference.encryptionKey)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
strongSelf.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
strongSelf.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||||
}
|
}
|
||||||
@ -949,8 +1052,7 @@ private final class CallSessionManagerContext {
|
|||||||
assertionFailure()
|
assertionFailure()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .phoneCallDiscarded(flags, id, reason, _, conferenceCall):
|
case let .phoneCallDiscarded(flags, id, reason, _):
|
||||||
let _ = conferenceCall
|
|
||||||
let reportRating = (flags & (1 << 2)) != 0
|
let reportRating = (flags & (1 << 2)) != 0
|
||||||
let sendDebugLogs = (flags & (1 << 3)) != 0
|
let sendDebugLogs = (flags & (1 << 3)) != 0
|
||||||
if let internalId = self.contextIdByStableId[id] {
|
if let internalId = self.contextIdByStableId[id] {
|
||||||
@ -963,47 +1065,39 @@ private final class CallSessionManagerContext {
|
|||||||
case .phoneCallDiscardReasonDisconnect:
|
case .phoneCallDiscardReasonDisconnect:
|
||||||
parsedReason = .error(.disconnected)
|
parsedReason = .error(.disconnected)
|
||||||
case .phoneCallDiscardReasonHangup:
|
case .phoneCallDiscardReasonHangup:
|
||||||
if context.pendingConference != nil {
|
|
||||||
parsedReason = .ended(.switchedToConference)
|
|
||||||
} else {
|
|
||||||
parsedReason = .ended(.hungUp)
|
parsedReason = .ended(.hungUp)
|
||||||
}
|
|
||||||
case .phoneCallDiscardReasonMissed:
|
case .phoneCallDiscardReasonMissed:
|
||||||
parsedReason = .ended(.missed)
|
parsedReason = .ended(.missed)
|
||||||
case .phoneCallDiscardReasonAllowGroupCall:
|
case let .phoneCallDiscardReasonMigrateConferenceCall(slug):
|
||||||
parsedReason = .ended(.switchedToConference)
|
parsedReason = .ended(.switchedToConference(slug: slug))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
parsedReason = .ended(.hungUp)
|
parsedReason = .ended(.hungUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case let .accepting(id, accessHash, _, _, _, disposable):
|
case let .accepting(id, accessHash, _, _, disposable):
|
||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _, conferenceCall, _):
|
case let .active(id, accessHash, _, _, _, _, _, _, _, _, _):
|
||||||
if let conferenceCall, case let .phoneCallDiscardReasonAllowGroupCall(encryptedGroupKey) = reason {
|
|
||||||
context.state = .switchedToConference(key: encryptedGroupKey.makeData(), keyVisualHash: MTSha256(encryptedGroupKey.makeData()), conferenceCall: conferenceCall)
|
|
||||||
} else {
|
|
||||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||||
}
|
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
case let .awaitingConfirmation(id, accessHash, _, _, _):
|
case let .awaitingConfirmation(id, accessHash, _, _, _):
|
||||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
case let .requested(id, accessHash, _, _, _, _, _):
|
case let .requested(id, accessHash, _, _, _, _):
|
||||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
case let .confirming(id, accessHash, _, _, _, _, disposable):
|
case let .confirming(id, accessHash, _, _, _, disposable):
|
||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
case let .requesting(_, _, disposable):
|
case let .requesting(_, disposable):
|
||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
context.state = .terminated(id: nil, accessHash: nil, reason: parsedReason, reportRating: false, sendDebugLogs: false)
|
context.state = .terminated(id: nil, accessHash: nil, reason: parsedReason, reportRating: false, sendDebugLogs: false)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
case let .ringing(id, accessHash, _, _, _, _):
|
case let .ringing(id, accessHash, _, _, _):
|
||||||
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
context.state = .terminated(id: id, accessHash: accessHash, reason: parsedReason, reportRating: reportRating, sendDebugLogs: sendDebugLogs)
|
||||||
self.ringingStatesUpdated()
|
self.ringingStatesUpdated()
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
@ -1014,15 +1108,15 @@ private final class CallSessionManagerContext {
|
|||||||
//assertionFailure()
|
//assertionFailure()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .phoneCall(flags, id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connections, startDate, customParameters, conferenceCall):
|
case let .phoneCall(flags, id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connections, startDate, customParameters):
|
||||||
let allowsP2P = (flags & (1 << 5)) != 0
|
let allowsP2P = (flags & (1 << 5)) != 0
|
||||||
if let internalId = self.contextIdByStableId[id] {
|
if let internalId = self.contextIdByStableId[id] {
|
||||||
if let context = self.contexts[internalId] {
|
if let context = self.contexts[internalId] {
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case .accepting, .dropping, .requesting, .ringing, .terminated, .requested, .switchedToConference:
|
case .accepting, .dropping, .requesting, .ringing, .terminated, .requested, .switchedToConference:
|
||||||
break
|
break
|
||||||
case let .active(id, accessHash, beginTimestamp, key, keyId, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P, _, _):
|
case let .active(id, accessHash, beginTimestamp, key, keyId, keyVisualHash, connections, maxLayer, version, customParameters, allowsP2P):
|
||||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: beginTimestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P, conferenceCall: conferenceCall.flatMap(GroupCallReference.init), willSwitchToConference: context.isIncomingConference)
|
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: beginTimestamp, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: connections, maxLayer: maxLayer, version: version, customParameters: customParameters, allowsP2P: allowsP2P)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
case let .awaitingConfirmation(_, accessHash, gAHash, b, config):
|
case let .awaitingConfirmation(_, accessHash, gAHash, b, config):
|
||||||
if let (key, calculatedKeyId, keyVisualHash) = self.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gAOrB.makeData()) {
|
if let (key, calculatedKeyId, keyVisualHash) = self.makeSessionEncryptionKey(config: config, gAHash: gAHash, b: b, gA: gAOrB.makeData()) {
|
||||||
@ -1041,7 +1135,7 @@ private final class CallSessionManagerContext {
|
|||||||
let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
||||||
context.isVideoPossible = isVideoPossible
|
context.isVideoPossible = isVideoPossible
|
||||||
|
|
||||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: calculatedKeyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P, conferenceCall: conferenceCall.flatMap(GroupCallReference.init), willSwitchToConference: context.isIncomingConference)
|
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: calculatedKeyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
} else {
|
} else {
|
||||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||||
@ -1053,7 +1147,7 @@ private final class CallSessionManagerContext {
|
|||||||
} else {
|
} else {
|
||||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||||
}
|
}
|
||||||
case let .confirming(id, accessHash, key, keyId, keyVisualHash, _, _):
|
case let .confirming(id, accessHash, key, keyId, keyVisualHash, _):
|
||||||
switch callProtocol {
|
switch callProtocol {
|
||||||
case let .phoneCallProtocol(_, _, maxLayer, versions):
|
case let .phoneCallProtocol(_, _, maxLayer, versions):
|
||||||
if !versions.isEmpty {
|
if !versions.isEmpty {
|
||||||
@ -1068,7 +1162,7 @@ private final class CallSessionManagerContext {
|
|||||||
let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
let isVideoPossible = self.videoVersions().contains(where: { versions.contains($0) })
|
||||||
context.isVideoPossible = isVideoPossible
|
context.isVideoPossible = isVideoPossible
|
||||||
|
|
||||||
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P, conferenceCall: conferenceCall.flatMap(GroupCallReference.init), willSwitchToConference: context.isIncomingConference)
|
context.state = .active(id: id, accessHash: accessHash, beginTimestamp: startDate, key: key, keyId: keyId, keyVisualHash: keyVisualHash, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: allowsP2P)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
} else {
|
} else {
|
||||||
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
self.drop(internalId: internalId, reason: .disconnect, debugLog: .single(nil))
|
||||||
@ -1079,7 +1173,7 @@ private final class CallSessionManagerContext {
|
|||||||
assertionFailure()
|
assertionFailure()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, requestedProtocol, conferenceCall):
|
case let .phoneCallRequested(flags, id, accessHash, date, adminId, _, gAHash, requestedProtocol):
|
||||||
let isVideo = (flags & (1 << 6)) != 0
|
let isVideo = (flags & (1 << 6)) != 0
|
||||||
let versions: [String]
|
let versions: [String]
|
||||||
switch requestedProtocol {
|
switch requestedProtocol {
|
||||||
@ -1087,7 +1181,7 @@ private final class CallSessionManagerContext {
|
|||||||
versions = libraryVersions
|
versions = libraryVersions
|
||||||
}
|
}
|
||||||
if self.contextIdByStableId[id] == nil {
|
if self.contextIdByStableId[id] == nil {
|
||||||
let internalId = self.addIncoming(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)), stableId: id, accessHash: accessHash, timestamp: date, gAHash: gAHash.makeData(), versions: versions, isVideo: isVideo, conferenceCall: conferenceCall.flatMap(GroupCallReference.init))
|
let internalId = self.addIncoming(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)), stableId: id, accessHash: accessHash, timestamp: date, gAHash: gAHash.makeData(), versions: versions, isVideo: isVideo)
|
||||||
if let internalId = internalId {
|
if let internalId = internalId {
|
||||||
var resultRingingStateValue: CallSessionRingingState?
|
var resultRingingStateValue: CallSessionRingingState?
|
||||||
for ringingState in self.ringingStatesValue() {
|
for ringingState in self.ringingStatesValue() {
|
||||||
@ -1104,13 +1198,13 @@ private final class CallSessionManagerContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .phoneCallWaiting(_, id, _, _, _, _, _, receiveDate, conferenceCall):
|
case let .phoneCallWaiting(_, id, _, _, _, _, _, receiveDate):
|
||||||
if let internalId = self.contextIdByStableId[id] {
|
if let internalId = self.contextIdByStableId[id] {
|
||||||
if let context = self.contexts[internalId] {
|
if let context = self.contexts[internalId] {
|
||||||
switch context.state {
|
switch context.state {
|
||||||
case let .requested(id, accessHash, a, gA, config, remoteConfirmationTimestamp, _):
|
case let .requested(id, accessHash, a, gA, config, remoteConfirmationTimestamp):
|
||||||
if let receiveDate = receiveDate, remoteConfirmationTimestamp == nil {
|
if let receiveDate = receiveDate, remoteConfirmationTimestamp == nil {
|
||||||
context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: receiveDate, conferenceCall: conferenceCall.flatMap(GroupCallReference.init))
|
context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: receiveDate)
|
||||||
self.contextUpdated(internalId: internalId)
|
self.contextUpdated(internalId: internalId)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -1145,6 +1239,29 @@ private final class CallSessionManagerContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addConferenceInvitationMessages(ids: [MessageId]) {
|
||||||
|
for id in ids {
|
||||||
|
if self.incomingConferenceInvitationContexts[id] == nil {
|
||||||
|
let context = IncomingConferenceInvitationContext(queue: self.queue, postbox: self.postbox, messageId: id, updated: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let context = self.incomingConferenceInvitationContexts[id] {
|
||||||
|
switch context.state {
|
||||||
|
case .pending:
|
||||||
|
break
|
||||||
|
case .ringing:
|
||||||
|
self.ringingStatesUpdated()
|
||||||
|
case .stopped:
|
||||||
|
self.incomingConferenceInvitationContexts.removeValue(forKey: id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self.incomingConferenceInvitationContexts[id] = context
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func makeSessionEncryptionKey(config: SecretChatEncryptionConfig, gAHash: Data, b: Data, gA: Data) -> (key: Data, keyId: Int64, keyVisualHash: Data)? {
|
private func makeSessionEncryptionKey(config: SecretChatEncryptionConfig, gAHash: Data, b: Data, gA: Data) -> (key: Data, keyId: Int64, keyVisualHash: Data)? {
|
||||||
var key = MTExp(self.network.encryptionProvider, gA, b, config.p.makeData())!
|
var key = MTExp(self.network.encryptionProvider, gA, b, config.p.makeData())!
|
||||||
|
|
||||||
@ -1173,17 +1290,17 @@ private final class CallSessionManagerContext {
|
|||||||
return (key, keyId, keyVisualHash)
|
return (key, keyId, keyVisualHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func request(peerId: PeerId, internalId: CallSessionInternalId, isVideo: Bool, enableVideo: Bool, conferenceCall: (conference: GroupCallReference, encryptionKey: Data)?) -> CallSessionInternalId? {
|
func request(peerId: PeerId, internalId: CallSessionInternalId, isVideo: Bool, enableVideo: Bool) -> CallSessionInternalId? {
|
||||||
let aBytes = malloc(256)!
|
let aBytes = malloc(256)!
|
||||||
let randomStatus = SecRandomCopyBytes(nil, 256, aBytes.assumingMemoryBound(to: UInt8.self))
|
let randomStatus = SecRandomCopyBytes(nil, 256, aBytes.assumingMemoryBound(to: UInt8.self))
|
||||||
let a = Data(bytesNoCopy: aBytes, count: 256, deallocator: .free)
|
let a = Data(bytesNoCopy: aBytes, count: 256, deallocator: .free)
|
||||||
if randomStatus == 0 {
|
if randomStatus == 0 {
|
||||||
self.contexts[internalId] = CallSessionContext(peerId: peerId, isOutgoing: true, isIncomingConference: false, type: isVideo ? .video : .audio, isVideoPossible: enableVideo || isVideo, pendingConference: conferenceCall, state: .requesting(a: a, conferenceCall: conferenceCall?.conference, disposable: (requestCallSession(postbox: self.postbox, network: self.network, peerId: peerId, a: a, maxLayer: self.maxLayer, versions: self.filteredVersions(enableVideo: true), isVideo: isVideo, conferenceCall: conferenceCall?.conference) |> deliverOn(queue)).start(next: { [weak self] result in
|
self.contexts[internalId] = CallSessionContext(peerId: peerId, isOutgoing: true, type: isVideo ? .video : .audio, isVideoPossible: enableVideo || isVideo, state: .requesting(a: a, disposable: (requestCallSession(postbox: self.postbox, network: self.network, peerId: peerId, a: a, maxLayer: self.maxLayer, versions: self.filteredVersions(enableVideo: true), isVideo: isVideo) |> deliverOn(queue)).start(next: { [weak self] result in
|
||||||
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
|
if let strongSelf = self, let context = strongSelf.contexts[internalId] {
|
||||||
if case .requesting = context.state {
|
if case .requesting = context.state {
|
||||||
switch result {
|
switch result {
|
||||||
case let .success(id, accessHash, config, gA, remoteConfirmationTimestamp):
|
case let .success(id, accessHash, config, gA, remoteConfirmationTimestamp):
|
||||||
context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: remoteConfirmationTimestamp, conferenceCall: conferenceCall?.conference)
|
context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: remoteConfirmationTimestamp)
|
||||||
strongSelf.contextIdByStableId[id] = internalId
|
strongSelf.contextIdByStableId[id] = internalId
|
||||||
strongSelf.contextUpdated(internalId: internalId)
|
strongSelf.contextUpdated(internalId: internalId)
|
||||||
strongSelf.deliverCallSignalingData(id: id)
|
strongSelf.deliverCallSignalingData(id: id)
|
||||||
@ -1254,6 +1371,12 @@ public final class CallSessionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addConferenceInvitationMessages(ids: [MessageId]) {
|
||||||
|
self.withContext { context in
|
||||||
|
context.addConferenceInvitationMessages(ids: ids)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func drop(internalId: CallSessionInternalId, reason: DropCallReason, debugLog: Signal<String?, NoError>) {
|
public func drop(internalId: CallSessionInternalId, reason: DropCallReason, debugLog: Signal<String?, NoError>) {
|
||||||
self.withContext { context in
|
self.withContext { context in
|
||||||
context.drop(internalId: internalId, reason: reason, debugLog: debugLog)
|
context.drop(internalId: internalId, reason: reason, debugLog: debugLog)
|
||||||
@ -1278,12 +1401,12 @@ public final class CallSessionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func request(peerId: PeerId, isVideo: Bool, enableVideo: Bool, conferenceCall: (conference: GroupCallReference, encryptionKey: Data)?, internalId: CallSessionInternalId = CallSessionInternalId()) -> Signal<CallSessionInternalId, NoError> {
|
public func request(peerId: PeerId, isVideo: Bool, enableVideo: Bool, internalId: CallSessionInternalId = CallSessionInternalId()) -> Signal<CallSessionInternalId, NoError> {
|
||||||
return Signal { [weak self] subscriber in
|
return Signal { [weak self] subscriber in
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
self?.withContext { context in
|
self?.withContext { context in
|
||||||
if let internalId = context.request(peerId: peerId, internalId: internalId, isVideo: isVideo, enableVideo: enableVideo, conferenceCall: conferenceCall) {
|
if let internalId = context.request(peerId: peerId, internalId: internalId, isVideo: isVideo, enableVideo: enableVideo) {
|
||||||
subscriber.putNext(internalId)
|
subscriber.putNext(internalId)
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
}
|
}
|
||||||
@ -1354,7 +1477,7 @@ public final class CallSessionManager {
|
|||||||
|
|
||||||
private enum AcceptedCall {
|
private enum AcceptedCall {
|
||||||
case waiting(config: SecretChatEncryptionConfig)
|
case waiting(config: SecretChatEncryptionConfig)
|
||||||
case call(config: SecretChatEncryptionConfig, gA: Data, timestamp: Int32, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool, conferenceCall: GroupCallReference?)
|
case call(config: SecretChatEncryptionConfig, gA: Data, timestamp: Int32, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, customParameters: String?, allowsP2P: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum AcceptCallResult {
|
private enum AcceptCallResult {
|
||||||
@ -1394,7 +1517,7 @@ private func acceptCallSession(accountPeerId: PeerId, postbox: Postbox, network:
|
|||||||
return .failed
|
return .failed
|
||||||
case .phoneCallWaiting:
|
case .phoneCallWaiting:
|
||||||
return .success(.waiting(config: config))
|
return .success(.waiting(config: config))
|
||||||
case let .phoneCall(flags, id, _, _, _, _, gAOrB, _, callProtocol, connections, startDate, customParameters, conferenceCall):
|
case let .phoneCall(flags, id, _, _, _, _, gAOrB, _, callProtocol, connections, startDate, customParameters):
|
||||||
if id == stableId {
|
if id == stableId {
|
||||||
switch callProtocol{
|
switch callProtocol{
|
||||||
case let .phoneCallProtocol(_, _, maxLayer, versions):
|
case let .phoneCallProtocol(_, _, maxLayer, versions):
|
||||||
@ -1407,7 +1530,7 @@ private func acceptCallSession(accountPeerId: PeerId, postbox: Postbox, network:
|
|||||||
customParametersValue = data
|
customParametersValue = data
|
||||||
}
|
}
|
||||||
|
|
||||||
return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: (flags & (1 << 5)) != 0, conferenceCall: conferenceCall.flatMap(GroupCallReference.init)))
|
return .success(.call(config: config, gA: gAOrB.makeData(), timestamp: startDate, connections: parseConnectionSet(primary: connections.first!, alternative: Array(connections[1...])), maxLayer: maxLayer, version: versions[0], customParameters: customParametersValue, allowsP2P: (flags & (1 << 5)) != 0))
|
||||||
} else {
|
} else {
|
||||||
return .failed
|
return .failed
|
||||||
}
|
}
|
||||||
@ -1430,7 +1553,7 @@ private enum RequestCallSessionResult {
|
|||||||
case failed(CallSessionError)
|
case failed(CallSessionError)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func requestCallSession(postbox: Postbox, network: Network, peerId: PeerId, a: Data, maxLayer: Int32, versions: [String], isVideo: Bool, conferenceCall: GroupCallReference?) -> Signal<RequestCallSessionResult, NoError> {
|
private func requestCallSession(postbox: Postbox, network: Network, peerId: PeerId, a: Data, maxLayer: Int32, versions: [String], isVideo: Bool) -> Signal<RequestCallSessionResult, NoError> {
|
||||||
return validatedEncryptionConfig(postbox: postbox, network: network)
|
return validatedEncryptionConfig(postbox: postbox, network: network)
|
||||||
|> mapToSignal { config -> Signal<RequestCallSessionResult, NoError> in
|
|> mapToSignal { config -> Signal<RequestCallSessionResult, NoError> in
|
||||||
return postbox.transaction { transaction -> Signal<RequestCallSessionResult, NoError> in
|
return postbox.transaction { transaction -> Signal<RequestCallSessionResult, NoError> in
|
||||||
@ -1450,18 +1573,15 @@ private func requestCallSession(postbox: Postbox, network: Network, peerId: Peer
|
|||||||
if isVideo {
|
if isVideo {
|
||||||
callFlags |= 1 << 0
|
callFlags |= 1 << 0
|
||||||
}
|
}
|
||||||
if conferenceCall != nil {
|
|
||||||
callFlags |= 1 << 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return network.request(Api.functions.phone.requestCall(flags: callFlags, userId: inputUser, conferenceCall: conferenceCall.flatMap { Api.InputGroupCall.inputGroupCall(id: $0.id, accessHash: $0.accessHash) }, randomId: Int32(bitPattern: arc4random()), gAHash: Buffer(data: gAHash), protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: minLayer, maxLayer: maxLayer, libraryVersions: versions)))
|
return network.request(Api.functions.phone.requestCall(flags: callFlags, userId: inputUser, randomId: Int32(bitPattern: arc4random()), gAHash: Buffer(data: gAHash), protocol: .phoneCallProtocol(flags: (1 << 0) | (1 << 1), minLayer: minLayer, maxLayer: maxLayer, libraryVersions: versions)))
|
||||||
|> map { result -> RequestCallSessionResult in
|
|> map { result -> RequestCallSessionResult in
|
||||||
switch result {
|
switch result {
|
||||||
case let .phoneCall(phoneCall, _):
|
case let .phoneCall(phoneCall, _):
|
||||||
switch phoneCall {
|
switch phoneCall {
|
||||||
case let .phoneCallRequested(_, id, accessHash, _, _, _, _, _, _):
|
case let .phoneCallRequested(_, id, accessHash, _, _, _, _, _):
|
||||||
return .success(id: id, accessHash: accessHash, config: config, gA: ga, remoteConfirmationTimestamp: nil)
|
return .success(id: id, accessHash: accessHash, config: config, gA: ga, remoteConfirmationTimestamp: nil)
|
||||||
case let .phoneCallWaiting(_, id, accessHash, _, _, _, _, receiveDate, _):
|
case let .phoneCallWaiting(_, id, accessHash, _, _, _, _, receiveDate):
|
||||||
return .success(id: id, accessHash: accessHash, config: config, gA: ga, remoteConfirmationTimestamp: receiveDate)
|
return .success(id: id, accessHash: accessHash, config: config, gA: ga, remoteConfirmationTimestamp: receiveDate)
|
||||||
default:
|
default:
|
||||||
return .failed(.generic)
|
return .failed(.generic)
|
||||||
@ -1514,7 +1634,7 @@ private enum DropCallSessionReason {
|
|||||||
case busy
|
case busy
|
||||||
case disconnect
|
case disconnect
|
||||||
case missed
|
case missed
|
||||||
case switchToConference(encryptedGroupKey: Data)
|
case switchToConference(slug: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func dropCallSession(network: Network, addUpdates: @escaping (Api.Updates) -> Void, stableId: CallSessionStableId, accessHash: Int64, isVideo: Bool, reason: DropCallSessionReason) -> Signal<(Bool, Bool), NoError> {
|
private func dropCallSession(network: Network, addUpdates: @escaping (Api.Updates) -> Void, stableId: CallSessionStableId, accessHash: Int64, isVideo: Bool, reason: DropCallSessionReason) -> Signal<(Bool, Bool), NoError> {
|
||||||
@ -1532,8 +1652,8 @@ private func dropCallSession(network: Network, addUpdates: @escaping (Api.Update
|
|||||||
mappedReason = .phoneCallDiscardReasonDisconnect
|
mappedReason = .phoneCallDiscardReasonDisconnect
|
||||||
case .missed:
|
case .missed:
|
||||||
mappedReason = .phoneCallDiscardReasonMissed
|
mappedReason = .phoneCallDiscardReasonMissed
|
||||||
case let .switchToConference(encryptedGroupKey):
|
case let .switchToConference(slug):
|
||||||
mappedReason = .phoneCallDiscardReasonAllowGroupCall(encryptedKey: Buffer(data: encryptedGroupKey))
|
mappedReason = .phoneCallDiscardReasonMigrateConferenceCall(slug: slug)
|
||||||
}
|
}
|
||||||
|
|
||||||
var callFlags: Int32 = 0
|
var callFlags: Int32 = 0
|
||||||
@ -1556,7 +1676,7 @@ private func dropCallSession(network: Network, addUpdates: @escaping (Api.Update
|
|||||||
switch update {
|
switch update {
|
||||||
case .updatePhoneCall(let phoneCall):
|
case .updatePhoneCall(let phoneCall):
|
||||||
switch phoneCall {
|
switch phoneCall {
|
||||||
case let .phoneCallDiscarded(flags, _, _, _, _):
|
case let .phoneCallDiscarded(flags, _, _, _):
|
||||||
reportRating = (flags & (1 << 2)) != 0
|
reportRating = (flags & (1 << 2)) != 0
|
||||||
sendDebugLogs = (flags & (1 << 3)) != 0
|
sendDebugLogs = (flags & (1 << 3)) != 0
|
||||||
default:
|
default:
|
||||||
@ -1578,34 +1698,3 @@ private func dropCallSession(network: Network, addUpdates: @escaping (Api.Update
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createConferenceCall(postbox: Postbox, network: Network, accountPeerId: PeerId, callId: CallId) -> Signal<GroupCallReference?, NoError> {
|
|
||||||
return network.request(Api.functions.phone.createConferenceCall(peer: .inputPhoneCall(id: callId.id, accessHash: callId.accessHash), keyFingerprint: 1))
|
|
||||||
|> map(Optional.init)
|
|
||||||
|> `catch` { _ -> Signal<Api.phone.PhoneCall?, NoError> in
|
|
||||||
return .single(nil)
|
|
||||||
}
|
|
||||||
|> mapToSignal { result -> Signal<GroupCallReference?, NoError> in
|
|
||||||
guard let result else {
|
|
||||||
return .single(nil)
|
|
||||||
}
|
|
||||||
return postbox.transaction { transaction -> GroupCallReference? in
|
|
||||||
switch result {
|
|
||||||
case let .phoneCall(phoneCall, users):
|
|
||||||
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(users: users))
|
|
||||||
|
|
||||||
switch phoneCall {
|
|
||||||
case .phoneCallEmpty, .phoneCallRequested, .phoneCallAccepted, .phoneCallDiscarded:
|
|
||||||
return nil
|
|
||||||
case .phoneCallWaiting:
|
|
||||||
return nil
|
|
||||||
case let .phoneCall(_, _, _, _, _, _, _, _, _, _, _, _, conferenceCall):
|
|
||||||
if let conferenceCall {
|
|
||||||
return GroupCallReference(conferenceCall)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
|||||||
|
|
||||||
public class Serialization: NSObject, MTSerialization {
|
public class Serialization: NSObject, MTSerialization {
|
||||||
public func currentLayer() -> UInt {
|
public func currentLayer() -> UInt {
|
||||||
return 201
|
return 202
|
||||||
}
|
}
|
||||||
|
|
||||||
public func parseMessage(_ data: Data!) -> Any! {
|
public func parseMessage(_ data: Data!) -> Any! {
|
||||||
|
@ -134,6 +134,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
case starGiftUnique(gift: StarGift, isUpgrade: Bool, isTransferred: Bool, savedToProfile: Bool, canExportDate: Int32?, transferStars: Int64?, isRefunded: Bool, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?)
|
case starGiftUnique(gift: StarGift, isUpgrade: Bool, isTransferred: Bool, savedToProfile: Bool, canExportDate: Int32?, transferStars: Int64?, isRefunded: Bool, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?)
|
||||||
case paidMessagesRefunded(count: Int32, stars: Int64)
|
case paidMessagesRefunded(count: Int32, stars: Int64)
|
||||||
case paidMessagesPriceEdited(stars: Int64)
|
case paidMessagesPriceEdited(stars: Int64)
|
||||||
|
case conferenceCall(callId: Int64, duration: Int32?, otherParticipants: [PeerId])
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
let rawValue: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0)
|
let rawValue: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0)
|
||||||
@ -262,6 +263,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
self = .paidMessagesRefunded(count: decoder.decodeInt32ForKey("count", orElse: 0), stars: decoder.decodeInt64ForKey("stars", orElse: 0))
|
self = .paidMessagesRefunded(count: decoder.decodeInt32ForKey("count", orElse: 0), stars: decoder.decodeInt64ForKey("stars", orElse: 0))
|
||||||
case 47:
|
case 47:
|
||||||
self = .paidMessagesPriceEdited(stars: decoder.decodeInt64ForKey("stars", orElse: 0))
|
self = .paidMessagesPriceEdited(stars: decoder.decodeInt64ForKey("stars", orElse: 0))
|
||||||
|
case 48:
|
||||||
|
self = .conferenceCall(callId: decoder.decodeInt64ForKey("cid", orElse: 0), duration: decoder.decodeOptionalInt32ForKey("dur"), otherParticipants: decoder.decodeInt64ArrayForKey("part").map(PeerId.init))
|
||||||
default:
|
default:
|
||||||
self = .unknown
|
self = .unknown
|
||||||
}
|
}
|
||||||
@ -639,6 +642,15 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
case let .paidMessagesPriceEdited(stars):
|
case let .paidMessagesPriceEdited(stars):
|
||||||
encoder.encodeInt32(47, forKey: "_rawValue")
|
encoder.encodeInt32(47, forKey: "_rawValue")
|
||||||
encoder.encodeInt64(stars, forKey: "stars")
|
encoder.encodeInt64(stars, forKey: "stars")
|
||||||
|
case let .conferenceCall(callId, duration, otherParticipants):
|
||||||
|
encoder.encodeInt32(48, forKey: "_rawValue")
|
||||||
|
encoder.encodeInt64(callId, forKey: "cid")
|
||||||
|
if let duration {
|
||||||
|
encoder.encodeInt32(duration, forKey: "dur")
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: "dur")
|
||||||
|
}
|
||||||
|
encoder.encodeInt64Array(otherParticipants.map({ $0.toInt64() }), forKey: "part")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,85 @@ import SwiftSignalKit
|
|||||||
import TelegramApi
|
import TelegramApi
|
||||||
import MtProtoKit
|
import MtProtoKit
|
||||||
|
|
||||||
|
public final class TelegramKeyPair: Equatable {
|
||||||
|
public let id: Int64
|
||||||
|
public let publicKey: TelegramPublicKey
|
||||||
|
|
||||||
|
public init(id: Int64, publicKey: TelegramPublicKey) {
|
||||||
|
self.id = id
|
||||||
|
self.publicKey = publicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: TelegramKeyPair, rhs: TelegramKeyPair) -> Bool {
|
||||||
|
if lhs.id != rhs.id {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.publicKey != rhs.publicKey {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class TelegramPublicKey: Equatable {
|
||||||
|
let value: Int256
|
||||||
|
|
||||||
|
init(value: Int256) {
|
||||||
|
self.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: TelegramPublicKey, rhs: TelegramPublicKey) -> Bool {
|
||||||
|
return lhs.value == rhs.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public extension TelegramPublicKey {
|
||||||
|
convenience init?(data: Data) {
|
||||||
|
guard data.count == 32 else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var int256 = Int256(
|
||||||
|
_0: 0,
|
||||||
|
_1: 0,
|
||||||
|
_2: 0,
|
||||||
|
_3: 0
|
||||||
|
)
|
||||||
|
|
||||||
|
data.withUnsafeBytes { buffer in
|
||||||
|
if let baseAddress = buffer.baseAddress {
|
||||||
|
let int64Buffer = baseAddress.assumingMemoryBound(to: Int64.self)
|
||||||
|
int256._0 = int64Buffer[0]
|
||||||
|
int256._1 = int64Buffer[1]
|
||||||
|
int256._2 = int64Buffer[2]
|
||||||
|
int256._3 = int64Buffer[3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.init(value: int256)
|
||||||
|
|
||||||
|
assert(self.data == data)
|
||||||
|
}
|
||||||
|
|
||||||
|
var data: Data {
|
||||||
|
var data = Data(count: 32)
|
||||||
|
data.withUnsafeMutableBytes { buffer in
|
||||||
|
if let baseAddress = buffer.baseAddress {
|
||||||
|
let int64Buffer = baseAddress.assumingMemoryBound(to: Int64.self)
|
||||||
|
int64Buffer[0] = self.value._0
|
||||||
|
int64Buffer[1] = self.value._1
|
||||||
|
int64Buffer[2] = self.value._2
|
||||||
|
int64Buffer[3] = self.value._3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol TelegramE2EEncryptionProvider: AnyObject {
|
||||||
|
func generateKeyPair() -> TelegramKeyPair?
|
||||||
|
func generateCallZeroBlock(keyPair: TelegramKeyPair, userId: Int64) -> Data?
|
||||||
|
}
|
||||||
|
|
||||||
public struct GroupCallInfo: Equatable {
|
public struct GroupCallInfo: Equatable {
|
||||||
public var id: Int64
|
public var id: Int64
|
||||||
public var accessHash: Int64
|
public var accessHash: Int64
|
||||||
@ -17,7 +96,6 @@ public struct GroupCallInfo: Equatable {
|
|||||||
public var isVideoEnabled: Bool
|
public var isVideoEnabled: Bool
|
||||||
public var unmutedVideoLimit: Int
|
public var unmutedVideoLimit: Int
|
||||||
public var isStream: Bool
|
public var isStream: Bool
|
||||||
public var upgradedPrivateCallId: Int64?
|
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
id: Int64,
|
id: Int64,
|
||||||
@ -32,8 +110,7 @@ public struct GroupCallInfo: Equatable {
|
|||||||
defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?,
|
defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?,
|
||||||
isVideoEnabled: Bool,
|
isVideoEnabled: Bool,
|
||||||
unmutedVideoLimit: Int,
|
unmutedVideoLimit: Int,
|
||||||
isStream: Bool,
|
isStream: Bool
|
||||||
upgradedPrivateCallId: Int64?
|
|
||||||
) {
|
) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.accessHash = accessHash
|
self.accessHash = accessHash
|
||||||
@ -48,7 +125,6 @@ public struct GroupCallInfo: Equatable {
|
|||||||
self.isVideoEnabled = isVideoEnabled
|
self.isVideoEnabled = isVideoEnabled
|
||||||
self.unmutedVideoLimit = unmutedVideoLimit
|
self.unmutedVideoLimit = unmutedVideoLimit
|
||||||
self.isStream = isStream
|
self.isStream = isStream
|
||||||
self.upgradedPrivateCallId = upgradedPrivateCallId
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +136,7 @@ public struct GroupCallSummary: Equatable {
|
|||||||
extension GroupCallInfo {
|
extension GroupCallInfo {
|
||||||
init?(_ call: Api.GroupCall) {
|
init?(_ call: Api.GroupCall) {
|
||||||
switch call {
|
switch call {
|
||||||
case let .groupCall(flags, id, accessHash, participantsCount, title, streamDcId, recordStartDate, scheduleDate, _, unmutedVideoLimit, _, conferenceFromCall):
|
case let .groupCall(flags, id, accessHash, participantsCount, title, streamDcId, recordStartDate, scheduleDate, _, unmutedVideoLimit, _):
|
||||||
self.init(
|
self.init(
|
||||||
id: id,
|
id: id,
|
||||||
accessHash: accessHash,
|
accessHash: accessHash,
|
||||||
@ -74,8 +150,7 @@ extension GroupCallInfo {
|
|||||||
defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: (flags & (1 << 1)) != 0, canChange: (flags & (1 << 2)) != 0),
|
defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: (flags & (1 << 1)) != 0, canChange: (flags & (1 << 2)) != 0),
|
||||||
isVideoEnabled: (flags & (1 << 9)) != 0,
|
isVideoEnabled: (flags & (1 << 9)) != 0,
|
||||||
unmutedVideoLimit: Int(unmutedVideoLimit),
|
unmutedVideoLimit: Int(unmutedVideoLimit),
|
||||||
isStream: (flags & (1 << 12)) != 0,
|
isStream: (flags & (1 << 12)) != 0
|
||||||
upgradedPrivateCallId: conferenceFromCall
|
|
||||||
)
|
)
|
||||||
case .groupCallDiscarded:
|
case .groupCallDiscarded:
|
||||||
return nil
|
return nil
|
||||||
@ -87,9 +162,43 @@ public enum GetCurrentGroupCallError {
|
|||||||
case generic
|
case generic
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int64, peerId: PeerId? = nil) -> Signal<GroupCallSummary?, GetCurrentGroupCallError> {
|
public enum InternalGroupCallReference: Equatable {
|
||||||
|
case id(id: Int64, accessHash: Int64)
|
||||||
|
case link(slug: String)
|
||||||
|
case message(id: MessageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension InternalGroupCallReference {
|
||||||
|
var apiInputGroupCall: Api.InputGroupCall {
|
||||||
|
switch self {
|
||||||
|
case let .id(id, accessHash):
|
||||||
|
return .inputGroupCall(id: id, accessHash: accessHash)
|
||||||
|
case let .link(slug):
|
||||||
|
return .inputGroupCallSlug(slug: slug)
|
||||||
|
case let .message(id):
|
||||||
|
return .inputGroupCallInviteMessage(msgId: id.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _internal_getCurrentGroupCall(account: Account, reference: InternalGroupCallReference, peerId: PeerId? = nil) -> Signal<GroupCallSummary?, GetCurrentGroupCallError> {
|
||||||
let accountPeerId = account.peerId
|
let accountPeerId = account.peerId
|
||||||
return account.network.request(Api.functions.phone.getGroupCall(call: .inputGroupCall(id: callId, accessHash: accessHash), limit: 4))
|
let inputCall: Api.InputGroupCall
|
||||||
|
switch reference {
|
||||||
|
case let .id(id, accessHash):
|
||||||
|
inputCall = .inputGroupCall(id: id, accessHash: accessHash)
|
||||||
|
case let .link(slug):
|
||||||
|
inputCall = .inputGroupCallSlug(slug: slug)
|
||||||
|
case let .message(id):
|
||||||
|
if id.peerId.namespace != Namespaces.Peer.CloudUser {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
if id.namespace != Namespaces.Message.Cloud {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
inputCall = .inputGroupCallInviteMessage(msgId: id.id)
|
||||||
|
}
|
||||||
|
return account.network.request(Api.functions.phone.getGroupCall(call: inputCall, limit: 4))
|
||||||
|> mapError { _ -> GetCurrentGroupCallError in
|
|> mapError { _ -> GetCurrentGroupCallError in
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
@ -247,7 +356,7 @@ public enum ToggleScheduledGroupCallSubscriptionError {
|
|||||||
case generic
|
case generic
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_toggleScheduledGroupCallSubscription(account: Account, peerId: PeerId, callId: Int64, accessHash: Int64, subscribe: Bool) -> Signal<Void, ToggleScheduledGroupCallSubscriptionError> {
|
func _internal_toggleScheduledGroupCallSubscription(account: Account, peerId: PeerId, reference: InternalGroupCallReference, subscribe: Bool) -> Signal<Void, ToggleScheduledGroupCallSubscriptionError> {
|
||||||
return account.postbox.transaction { transaction -> Void in
|
return account.postbox.transaction { transaction -> Void in
|
||||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||||
if let cachedData = cachedData as? CachedChannelData, let activeCall = cachedData.activeCall {
|
if let cachedData = cachedData as? CachedChannelData, let activeCall = cachedData.activeCall {
|
||||||
@ -261,7 +370,7 @@ func _internal_toggleScheduledGroupCallSubscription(account: Account, peerId: Pe
|
|||||||
}
|
}
|
||||||
|> castError(ToggleScheduledGroupCallSubscriptionError.self)
|
|> castError(ToggleScheduledGroupCallSubscriptionError.self)
|
||||||
|> mapToSignal { _ -> Signal<Void, ToggleScheduledGroupCallSubscriptionError> in
|
|> mapToSignal { _ -> Signal<Void, ToggleScheduledGroupCallSubscriptionError> in
|
||||||
return account.network.request(Api.functions.phone.toggleGroupCallStartSubscription(call: .inputGroupCall(id: callId, accessHash: accessHash), subscribed: subscribe ? .boolTrue : .boolFalse))
|
return account.network.request(Api.functions.phone.toggleGroupCallStartSubscription(call: reference.apiInputGroupCall, subscribed: subscribe ? .boolTrue : .boolFalse))
|
||||||
|> mapError { error -> ToggleScheduledGroupCallSubscriptionError in
|
|> mapError { error -> ToggleScheduledGroupCallSubscriptionError in
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
@ -342,24 +451,24 @@ public enum GetGroupCallParticipantsError {
|
|||||||
case generic
|
case generic
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_getGroupCallParticipants(account: Account, callId: Int64, accessHash: Int64, offset: String, ssrcs: [UInt32], limit: Int32, sortAscending: Bool?) -> Signal<GroupCallParticipantsContext.State, 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 accountPeerId = account.peerId
|
||||||
|
|
||||||
let sortAscendingValue: Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool, Int64?), GetGroupCallParticipantsError>
|
let sortAscendingValue: Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool), GetGroupCallParticipantsError>
|
||||||
|
|
||||||
sortAscendingValue = _internal_getCurrentGroupCall(account: account, callId: callId, accessHash: accessHash)
|
sortAscendingValue = _internal_getCurrentGroupCall(account: account, reference: reference)
|
||||||
|> mapError { _ -> GetGroupCallParticipantsError in
|
|> mapError { _ -> GetGroupCallParticipantsError in
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
|> mapToSignal { result -> Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool, Int64?), GetGroupCallParticipantsError> in
|
|> mapToSignal { result -> Signal<(Bool, Int32?, Bool, GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, Bool, Int, Bool), GetGroupCallParticipantsError> in
|
||||||
guard let result = result else {
|
guard let result = result else {
|
||||||
return .fail(.generic)
|
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, result.info.upgradedPrivateCallId))
|
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 combineLatest(
|
return combineLatest(
|
||||||
account.network.request(Api.functions.phone.getGroupParticipants(call: .inputGroupCall(id: callId, accessHash: accessHash), ids: [], sources: ssrcs.map { Int32(bitPattern: $0) }, offset: offset, limit: limit))
|
account.network.request(Api.functions.phone.getGroupParticipants(call: reference.apiInputGroupCall, ids: [], sources: ssrcs.map { Int32(bitPattern: $0) }, offset: offset, limit: limit))
|
||||||
|> mapError { _ -> GetGroupCallParticipantsError in
|
|> mapError { _ -> GetGroupCallParticipantsError in
|
||||||
return .generic
|
return .generic
|
||||||
},
|
},
|
||||||
@ -372,7 +481,7 @@ func _internal_getGroupCallParticipants(account: Account, callId: Int64, accessH
|
|||||||
let version: Int32
|
let version: Int32
|
||||||
let nextParticipantsFetchOffset: String?
|
let nextParticipantsFetchOffset: String?
|
||||||
|
|
||||||
let (sortAscendingValue, scheduleTimestamp, subscribedToScheduled, defaultParticipantsAreMuted, isVideoEnabled, unmutedVideoLimit, isStream, upgradedPrivateCallId) = sortAscendingAndScheduleTimestamp
|
let (sortAscendingValue, scheduleTimestamp, subscribedToScheduled, defaultParticipantsAreMuted, isVideoEnabled, unmutedVideoLimit, isStream) = sortAscendingAndScheduleTimestamp
|
||||||
|
|
||||||
switch result {
|
switch result {
|
||||||
case let .groupParticipants(count, participants, nextOffset, chats, users, apiVersion):
|
case let .groupParticipants(count, participants, nextOffset, chats, users, apiVersion):
|
||||||
@ -408,7 +517,6 @@ func _internal_getGroupCallParticipants(account: Account, callId: Int64, accessH
|
|||||||
isVideoEnabled: isVideoEnabled,
|
isVideoEnabled: isVideoEnabled,
|
||||||
unmutedVideoLimit: unmutedVideoLimit,
|
unmutedVideoLimit: unmutedVideoLimit,
|
||||||
isStream: isStream,
|
isStream: isStream,
|
||||||
upgradedPrivateCallId: upgradedPrivateCallId,
|
|
||||||
version: version
|
version: version
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -435,7 +543,39 @@ public struct JoinGroupCallResult {
|
|||||||
public var jsonParams: String
|
public var jsonParams: String
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?, callId: Int64, accessHash: Int64, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil, keyFingerprint: Int64?) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
|
public class JoinGroupCallE2E {
|
||||||
|
public let publicKey: TelegramPublicKey
|
||||||
|
public let block: Data
|
||||||
|
|
||||||
|
public init(publicKey: TelegramPublicKey, block: Data) {
|
||||||
|
self.publicKey = publicKey
|
||||||
|
self.block = block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?, callId: Int64, reference: InternalGroupCallReference, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil, generateE2E: ((Data?) -> JoinGroupCallE2E?)?) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
|
||||||
|
enum InternalJoinError {
|
||||||
|
case error(JoinGroupCallError)
|
||||||
|
case restart
|
||||||
|
}
|
||||||
|
|
||||||
|
var e2eData: Signal<JoinGroupCallE2E?, NoError> = .single(nil)
|
||||||
|
if let generateE2E {
|
||||||
|
e2eData = _internal_pollConferenceCallBlockchain(network: account.network, reference: reference, subChainId: 0, offset: -1, limit: 1)
|
||||||
|
|> map { result -> JoinGroupCallE2E? in
|
||||||
|
guard let result else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let block = result.blocks.last else {
|
||||||
|
return generateE2E(nil)
|
||||||
|
}
|
||||||
|
return generateE2E(block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let signal: Signal<JoinGroupCallResult, InternalJoinError> = e2eData
|
||||||
|
|> castError(InternalJoinError.self)
|
||||||
|
|> mapToSignal { e2eData -> Signal<JoinGroupCallResult, InternalJoinError> in
|
||||||
return account.postbox.transaction { transaction -> Api.InputPeer? in
|
return account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||||
if let joinAs = joinAs {
|
if let joinAs = joinAs {
|
||||||
return transaction.getPeer(joinAs).flatMap(apiInputPeer)
|
return transaction.getPeer(joinAs).flatMap(apiInputPeer)
|
||||||
@ -443,10 +583,10 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
|||||||
return .inputPeerSelf
|
return .inputPeerSelf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> castError(JoinGroupCallError.self)
|
|> castError(InternalJoinError.self)
|
||||||
|> mapToSignal { inputJoinAs in
|
|> mapToSignal { inputJoinAs -> Signal<JoinGroupCallResult, InternalJoinError> in
|
||||||
guard let inputJoinAs = inputJoinAs else {
|
guard let inputJoinAs = inputJoinAs else {
|
||||||
return .fail(.generic)
|
return .fail(.error(.generic))
|
||||||
}
|
}
|
||||||
|
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
@ -457,16 +597,16 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
|||||||
if let _ = inviteHash {
|
if let _ = inviteHash {
|
||||||
flags |= (1 << 1)
|
flags |= (1 << 1)
|
||||||
}
|
}
|
||||||
if keyFingerprint != nil {
|
if e2eData != nil {
|
||||||
flags |= (1 << 3)
|
flags |= (1 << 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
let joinRequest = account.network.request(Api.functions.phone.joinGroupCall(flags: flags, call: .inputGroupCall(id: callId, accessHash: accessHash), joinAs: inputJoinAs, inviteHash: inviteHash, keyFingerprint: keyFingerprint, params: .dataJSON(data: joinPayload)))
|
let joinRequest = account.network.request(Api.functions.phone.joinGroupCall(flags: flags, call: reference.apiInputGroupCall, joinAs: inputJoinAs, inviteHash: inviteHash, publicKey: e2eData?.publicKey.value, block: (e2eData?.block).flatMap({ Buffer.init(data: $0) }), inviteMsgId: nil, params: .dataJSON(data: joinPayload)))
|
||||||
|> `catch` { error -> Signal<Api.Updates, JoinGroupCallError> in
|
|> `catch` { error -> Signal<Api.Updates, InternalJoinError> in
|
||||||
if error.errorDescription == "GROUPCALL_ANONYMOUS_FORBIDDEN" {
|
if error.errorDescription == "GROUPCALL_ANONYMOUS_FORBIDDEN" {
|
||||||
return .fail(.anonymousNotAllowed)
|
return .fail(.error(.anonymousNotAllowed))
|
||||||
} else if error.errorDescription == "GROUPCALL_PARTICIPANTS_TOO_MUCH" {
|
} else if error.errorDescription == "GROUPCALL_PARTICIPANTS_TOO_MUCH" {
|
||||||
return .fail(.tooManyParticipants)
|
return .fail(.error(.tooManyParticipants))
|
||||||
} else if error.errorDescription == "JOIN_AS_PEER_INVALID" {
|
} else if error.errorDescription == "JOIN_AS_PEER_INVALID" {
|
||||||
if let peerId {
|
if let peerId {
|
||||||
let _ = (account.postbox.transaction { transaction -> Void in
|
let _ = (account.postbox.transaction { transaction -> Void in
|
||||||
@ -482,9 +622,9 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
|||||||
}).start()
|
}).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
return .fail(.invalidJoinAsPeer)
|
return .fail(.error(.invalidJoinAsPeer))
|
||||||
} else if error.errorDescription == "GROUPCALL_INVALID" {
|
} else if error.errorDescription == "GROUPCALL_INVALID" {
|
||||||
return account.postbox.transaction { transaction -> Signal<Api.Updates, JoinGroupCallError> in
|
return account.postbox.transaction { transaction -> Signal<Api.Updates, InternalJoinError> in
|
||||||
if let peerId {
|
if let peerId {
|
||||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||||
if let current = current as? CachedGroupData {
|
if let current = current as? CachedGroupData {
|
||||||
@ -500,35 +640,37 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return .fail(.generic)
|
return .fail(.error(.generic))
|
||||||
}
|
}
|
||||||
|> castError(JoinGroupCallError.self)
|
|> castError(InternalJoinError.self)
|
||||||
|> switchToLatest
|
|> switchToLatest
|
||||||
|
} else if error.errorDescription.hasPrefix("CONF_WRITE_CHAIN_INVALID") {
|
||||||
|
return .fail(.restart)
|
||||||
} else {
|
} else {
|
||||||
return .fail(.generic)
|
return .fail(.error(.generic))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let getParticipantsRequest = _internal_getGroupCallParticipants(account: account, callId: callId, accessHash: accessHash, offset: "", ssrcs: [], limit: 100, sortAscending: true)
|
let getParticipantsRequest = _internal_getGroupCallParticipants(account: account, reference: reference, offset: "", ssrcs: [], limit: 100, sortAscending: true)
|
||||||
|> mapError { _ -> JoinGroupCallError in
|
|> mapError { _ -> InternalJoinError in
|
||||||
return .generic
|
return .error(.generic)
|
||||||
}
|
}
|
||||||
|
|
||||||
return combineLatest(
|
return combineLatest(
|
||||||
joinRequest,
|
joinRequest,
|
||||||
getParticipantsRequest
|
getParticipantsRequest
|
||||||
)
|
)
|
||||||
|> mapToSignal { updates, participantsState -> Signal<JoinGroupCallResult, JoinGroupCallError> in
|
|> mapToSignal { updates, participantsState -> Signal<JoinGroupCallResult, InternalJoinError> in
|
||||||
let peer = account.postbox.transaction { transaction -> Peer? in
|
let peer = account.postbox.transaction { transaction -> Peer? in
|
||||||
return peerId.flatMap(transaction.getPeer)
|
return peerId.flatMap(transaction.getPeer)
|
||||||
}
|
}
|
||||||
|> castError(JoinGroupCallError.self)
|
|> castError(InternalJoinError.self)
|
||||||
|
|
||||||
return combineLatest(
|
return combineLatest(
|
||||||
peerAdminIds |> castError(JoinGroupCallError.self) |> take(1),
|
peerAdminIds |> castError(InternalJoinError.self) |> take(1),
|
||||||
peer
|
peer
|
||||||
)
|
)
|
||||||
|> mapToSignal { peerAdminIds, peer -> Signal<JoinGroupCallResult, JoinGroupCallError> in
|
|> mapToSignal { peerAdminIds, peer -> Signal<JoinGroupCallResult, InternalJoinError> in
|
||||||
var state = participantsState
|
var state = participantsState
|
||||||
if let peer {
|
if let peer {
|
||||||
if let channel = peer as? TelegramChannel {
|
if let channel = peer as? TelegramChannel {
|
||||||
@ -552,7 +694,7 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
|||||||
maybeParsedCall = GroupCallInfo(call)
|
maybeParsedCall = GroupCallInfo(call)
|
||||||
|
|
||||||
switch call {
|
switch call {
|
||||||
case let .groupCall(flags, _, _, _, title, _, recordStartDate, scheduleDate, _, unmutedVideoLimit, _, _):
|
case let .groupCall(flags, _, _, _, title, _, recordStartDate, scheduleDate, _, unmutedVideoLimit, _):
|
||||||
let isMuted = (flags & (1 << 1)) != 0
|
let isMuted = (flags & (1 << 1)) != 0
|
||||||
let canChange = (flags & (1 << 2)) != 0
|
let canChange = (flags & (1 << 2)) != 0
|
||||||
let isVideoEnabled = (flags & (1 << 9)) != 0
|
let isVideoEnabled = (flags & (1 << 9)) != 0
|
||||||
@ -576,7 +718,7 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
|||||||
}
|
}
|
||||||
|
|
||||||
guard let parsedCall = maybeParsedCall, let parsedClientParams = maybeParsedClientParams else {
|
guard let parsedCall = maybeParsedCall, let parsedClientParams = maybeParsedClientParams else {
|
||||||
return .fail(.generic)
|
return .fail(.error(.generic))
|
||||||
}
|
}
|
||||||
|
|
||||||
state.sortAscending = parsedCall.sortAscending
|
state.sortAscending = parsedCall.sortAscending
|
||||||
@ -673,10 +815,93 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
|||||||
jsonParams: parsedClientParams
|
jsonParams: parsedClientParams
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|> castError(JoinGroupCallError.self)
|
|> castError(InternalJoinError.self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return signal |> restartOrMapError { error in
|
||||||
|
switch error {
|
||||||
|
case .restart:
|
||||||
|
return .restart
|
||||||
|
case let .error(e):
|
||||||
|
return .error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _internal_inviteConferenceCallParticipant(account: Account, callId: Int64, accessHash: Int64, peerId: EnginePeer.Id) -> Signal<Never, NoError> {
|
||||||
|
return account.postbox.transaction { transaction -> Api.InputUser? in
|
||||||
|
return transaction.getPeer(peerId).flatMap(apiInputUser)
|
||||||
|
}
|
||||||
|
|> mapToSignal { inputPeer -> Signal<Never, NoError> in
|
||||||
|
guard let inputPeer else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
|
||||||
|
return account.network.request(Api.functions.phone.inviteConferenceCallParticipant(call: .inputGroupCall(id: callId, accessHash: accessHash), userId: inputPeer))
|
||||||
|
|> map(Optional.init)
|
||||||
|
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<Never, NoError> in
|
||||||
|
if let result {
|
||||||
|
account.stateManager.addUpdates(result)
|
||||||
|
}
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum RemoveGroupCallBlockchainParticipantError {
|
||||||
|
case generic
|
||||||
|
case pollBlocksAndRetry
|
||||||
|
}
|
||||||
|
|
||||||
|
func _internal_removeGroupCallBlockchainParticipants(account: Account, callId: Int64, accessHash: Int64, block: @escaping ([EnginePeer.Id]) -> Data?) -> Signal<Never, RemoveGroupCallBlockchainParticipantError> {
|
||||||
|
/*let blockSignal = _internal_getGroupCallParticipants(account: account, callId: callId, accessHash: accessHash, offset: "", ssrcs: [], limit: 1000, sortAscending: nil)
|
||||||
|
|> mapError { _ -> RemoveGroupCallBlockchainParticipantError in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
|> map { result -> Data? in
|
||||||
|
return block(result.participants.map(\.peer.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
let signal: Signal<Never, RemoveGroupCallBlockchainParticipantError> = blockSignal
|
||||||
|
|> mapToSignal { block -> Signal<Never, RemoveGroupCallBlockchainParticipantError> in
|
||||||
|
guard let block else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||||
|
return transaction.getPeer(participantId).flatMap(apiInputPeer)
|
||||||
|
}
|
||||||
|
|> castError(RemoveGroupCallBlockchainParticipantError.self)
|
||||||
|
|> mapToSignal { inputPeer -> Signal<Never, RemoveGroupCallBlockchainParticipantError> in
|
||||||
|
guard let inputPeer else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
return account.network.request(Api.functions.phone.deleteConferenceCallParticipant(call: .inputGroupCall(id: callId, accessHash: accessHash), peer: inputPeer, block: Buffer(data: block)))
|
||||||
|
|> mapError { error -> RemoveGroupCallBlockchainParticipantError in
|
||||||
|
if error.errorDescription.hasPrefix("CONF_WRITE_CHAIN_INVALID") {
|
||||||
|
return .pollBlocksAndRetry
|
||||||
|
} else {
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<Never, RemoveGroupCallBlockchainParticipantError> in
|
||||||
|
account.stateManager.addUpdates(result)
|
||||||
|
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return signal*/
|
||||||
|
//TODO:release
|
||||||
|
return .complete()
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct JoinGroupCallAsScreencastResult {
|
public struct JoinGroupCallAsScreencastResult {
|
||||||
@ -1037,7 +1262,6 @@ public final class GroupCallParticipantsContext {
|
|||||||
public var isVideoEnabled: Bool
|
public var isVideoEnabled: Bool
|
||||||
public var unmutedVideoLimit: Int
|
public var unmutedVideoLimit: Int
|
||||||
public var isStream: Bool
|
public var isStream: Bool
|
||||||
public var upgradedPrivateCallId: Int64?
|
|
||||||
public var version: Int32
|
public var version: Int32
|
||||||
|
|
||||||
public mutating func mergeActivity(from other: State, myPeerId: PeerId?, previousMyPeerId: PeerId?, mergeActivityTimestamps: Bool) {
|
public mutating func mergeActivity(from other: State, myPeerId: PeerId?, previousMyPeerId: PeerId?, mergeActivityTimestamps: Bool) {
|
||||||
@ -1073,7 +1297,6 @@ public final class GroupCallParticipantsContext {
|
|||||||
isVideoEnabled: Bool,
|
isVideoEnabled: Bool,
|
||||||
unmutedVideoLimit: Int,
|
unmutedVideoLimit: Int,
|
||||||
isStream: Bool,
|
isStream: Bool,
|
||||||
upgradedPrivateCallId: Int64?,
|
|
||||||
version: Int32
|
version: Int32
|
||||||
) {
|
) {
|
||||||
self.participants = participants
|
self.participants = participants
|
||||||
@ -1090,7 +1313,6 @@ public final class GroupCallParticipantsContext {
|
|||||||
self.isVideoEnabled = isVideoEnabled
|
self.isVideoEnabled = isVideoEnabled
|
||||||
self.unmutedVideoLimit = unmutedVideoLimit
|
self.unmutedVideoLimit = unmutedVideoLimit
|
||||||
self.isStream = isStream
|
self.isStream = isStream
|
||||||
self.upgradedPrivateCallId = upgradedPrivateCallId
|
|
||||||
self.version = version
|
self.version = version
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1197,6 +1419,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
|
|
||||||
case state(update: StateUpdate)
|
case state(update: StateUpdate)
|
||||||
case call(isTerminated: Bool, defaultParticipantsAreMuted: State.DefaultParticipantsAreMuted, title: String?, recordingStartTimestamp: Int32?, scheduleTimestamp: Int32?, isVideoEnabled: Bool, participantCount: Int?)
|
case call(isTerminated: Bool, defaultParticipantsAreMuted: State.DefaultParticipantsAreMuted, title: String?, recordingStartTimestamp: Int32?, scheduleTimestamp: Int32?, isVideoEnabled: Bool, participantCount: Int?)
|
||||||
|
case conferenceChainBlocks(subChainId: Int, blocks: [Data], nextOffset: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class MemberEvent {
|
public final class MemberEvent {
|
||||||
@ -1215,7 +1438,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
private let peerId: PeerId?
|
private let peerId: PeerId?
|
||||||
public let myPeerId: PeerId
|
public let myPeerId: PeerId
|
||||||
public let id: Int64
|
public let id: Int64
|
||||||
public let accessHash: Int64
|
public let reference: InternalGroupCallReference
|
||||||
|
|
||||||
private var hasReceivedSpeakingParticipantsReport: Bool = false
|
private var hasReceivedSpeakingParticipantsReport: Bool = false
|
||||||
|
|
||||||
@ -1323,12 +1546,12 @@ public final class GroupCallParticipantsContext {
|
|||||||
|
|
||||||
public private(set) var serviceState: ServiceState
|
public private(set) var serviceState: ServiceState
|
||||||
|
|
||||||
init(account: Account, peerId: PeerId?, myPeerId: PeerId, id: Int64, accessHash: Int64, state: State, previousServiceState: ServiceState?) {
|
init(account: Account, peerId: PeerId?, myPeerId: PeerId, id: Int64, reference: InternalGroupCallReference, state: State, previousServiceState: ServiceState?) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.myPeerId = myPeerId
|
self.myPeerId = myPeerId
|
||||||
self.id = id
|
self.id = id
|
||||||
self.accessHash = accessHash
|
self.reference = reference
|
||||||
self.stateValue = InternalState(state: state, overlayState: OverlayState())
|
self.stateValue = InternalState(state: state, overlayState: OverlayState())
|
||||||
self.statePromise = ValuePromise<InternalState>(self.stateValue)
|
self.statePromise = ValuePromise<InternalState>(self.stateValue)
|
||||||
self.serviceState = previousServiceState ?? ServiceState()
|
self.serviceState = previousServiceState ?? ServiceState()
|
||||||
@ -1407,7 +1630,6 @@ public final class GroupCallParticipantsContext {
|
|||||||
isVideoEnabled: strongSelf.stateValue.state.isVideoEnabled,
|
isVideoEnabled: strongSelf.stateValue.state.isVideoEnabled,
|
||||||
unmutedVideoLimit: strongSelf.stateValue.state.unmutedVideoLimit,
|
unmutedVideoLimit: strongSelf.stateValue.state.unmutedVideoLimit,
|
||||||
isStream: strongSelf.stateValue.state.isStream,
|
isStream: strongSelf.stateValue.state.isStream,
|
||||||
upgradedPrivateCallId: strongSelf.stateValue.state.upgradedPrivateCallId,
|
|
||||||
version: strongSelf.stateValue.state.version
|
version: strongSelf.stateValue.state.version
|
||||||
),
|
),
|
||||||
overlayState: strongSelf.stateValue.overlayState
|
overlayState: strongSelf.stateValue.overlayState
|
||||||
@ -1560,7 +1782,6 @@ public final class GroupCallParticipantsContext {
|
|||||||
isVideoEnabled: strongSelf.stateValue.state.isVideoEnabled,
|
isVideoEnabled: strongSelf.stateValue.state.isVideoEnabled,
|
||||||
unmutedVideoLimit: strongSelf.stateValue.state.unmutedVideoLimit,
|
unmutedVideoLimit: strongSelf.stateValue.state.unmutedVideoLimit,
|
||||||
isStream: strongSelf.stateValue.state.isStream,
|
isStream: strongSelf.stateValue.state.isStream,
|
||||||
upgradedPrivateCallId: strongSelf.stateValue.state.upgradedPrivateCallId,
|
|
||||||
version: strongSelf.stateValue.state.version
|
version: strongSelf.stateValue.state.version
|
||||||
),
|
),
|
||||||
overlayState: strongSelf.stateValue.overlayState
|
overlayState: strongSelf.stateValue.overlayState
|
||||||
@ -1608,7 +1829,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
|
|
||||||
Logger.shared.log("GroupCallParticipantsContext", "will request ssrcs=\(ssrcs)")
|
Logger.shared.log("GroupCallParticipantsContext", "will request ssrcs=\(ssrcs)")
|
||||||
|
|
||||||
self.disposable.set((_internal_getGroupCallParticipants(account: self.account, callId: self.id, accessHash: self.accessHash, offset: "", ssrcs: Array(ssrcs), limit: 100, sortAscending: true)
|
self.disposable.set((_internal_getGroupCallParticipants(account: self.account, reference: self.reference, offset: "", ssrcs: Array(ssrcs), limit: 100, sortAscending: true)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -1784,7 +2005,6 @@ public final class GroupCallParticipantsContext {
|
|||||||
let isVideoEnabled = strongSelf.stateValue.state.isVideoEnabled
|
let isVideoEnabled = strongSelf.stateValue.state.isVideoEnabled
|
||||||
let isStream = strongSelf.stateValue.state.isStream
|
let isStream = strongSelf.stateValue.state.isStream
|
||||||
let unmutedVideoLimit = strongSelf.stateValue.state.unmutedVideoLimit
|
let unmutedVideoLimit = strongSelf.stateValue.state.unmutedVideoLimit
|
||||||
let upgradedPrivateCallId = strongSelf.stateValue.state.upgradedPrivateCallId
|
|
||||||
|
|
||||||
updatedParticipants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: strongSelf.stateValue.state.sortAscending) })
|
updatedParticipants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: strongSelf.stateValue.state.sortAscending) })
|
||||||
|
|
||||||
@ -1804,7 +2024,6 @@ public final class GroupCallParticipantsContext {
|
|||||||
isVideoEnabled: isVideoEnabled,
|
isVideoEnabled: isVideoEnabled,
|
||||||
unmutedVideoLimit: unmutedVideoLimit,
|
unmutedVideoLimit: unmutedVideoLimit,
|
||||||
isStream: isStream,
|
isStream: isStream,
|
||||||
upgradedPrivateCallId: upgradedPrivateCallId,
|
|
||||||
version: update.version
|
version: update.version
|
||||||
),
|
),
|
||||||
overlayState: updatedOverlayState
|
overlayState: updatedOverlayState
|
||||||
@ -1824,7 +2043,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
|
|
||||||
self.updateQueue.removeAll()
|
self.updateQueue.removeAll()
|
||||||
|
|
||||||
self.disposable.set((_internal_getGroupCallParticipants(account: self.account, callId: self.id, accessHash: self.accessHash, offset: "", ssrcs: [], limit: 100, sortAscending: self.stateValue.state.sortAscending)
|
self.disposable.set((_internal_getGroupCallParticipants(account: self.account, reference: self.reference, offset: "", ssrcs: [], limit: 100, sortAscending: self.stateValue.state.sortAscending)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -1877,7 +2096,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
|
|
||||||
let account = self.account
|
let account = self.account
|
||||||
let id = self.id
|
let id = self.id
|
||||||
let accessHash = self.accessHash
|
let reference = self.reference
|
||||||
let myPeerId = self.myPeerId
|
let myPeerId = self.myPeerId
|
||||||
|
|
||||||
let signal: Signal<Api.Updates?, NoError> = self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
let signal: Signal<Api.Updates?, NoError> = self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||||
@ -1907,7 +2126,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
raiseHandApi = nil
|
raiseHandApi = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return account.network.request(Api.functions.phone.editGroupCallParticipant(flags: flags, call: .inputGroupCall(id: id, accessHash: accessHash), participant: inputPeer, muted: muted, volume: volume, raiseHand: raiseHandApi, videoStopped: nil, videoPaused: nil, presentationPaused: nil))
|
return account.network.request(Api.functions.phone.editGroupCallParticipant(flags: flags, call: reference.apiInputGroupCall, participant: inputPeer, muted: muted, volume: volume, raiseHand: raiseHandApi, videoStopped: nil, videoPaused: nil, presentationPaused: nil))
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
@ -1931,6 +2150,8 @@ public final class GroupCallParticipantsContext {
|
|||||||
if updateCallId != id {
|
if updateCallId != id {
|
||||||
continue loop
|
continue loop
|
||||||
}
|
}
|
||||||
|
case .inputGroupCallSlug, .inputGroupCallInviteMessage:
|
||||||
|
continue loop
|
||||||
}
|
}
|
||||||
stateUpdates.append(.state(update: GroupCallParticipantsContext.Update.StateUpdate(participants: participants, version: version, removePendingMuteStates: [peerId])))
|
stateUpdates.append(.state(update: GroupCallParticipantsContext.Update.StateUpdate(participants: participants, version: version, removePendingMuteStates: [peerId])))
|
||||||
default:
|
default:
|
||||||
@ -1964,7 +2185,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
|
|
||||||
let account = self.account
|
let account = self.account
|
||||||
let id = self.id
|
let id = self.id
|
||||||
let accessHash = self.accessHash
|
let reference = self.reference
|
||||||
|
|
||||||
let signal: Signal<Api.Updates?, NoError> = self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
let signal: Signal<Api.Updates?, NoError> = self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||||
@ -1993,7 +2214,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
flags |= 1 << 5
|
flags |= 1 << 5
|
||||||
}
|
}
|
||||||
|
|
||||||
return account.network.request(Api.functions.phone.editGroupCallParticipant(flags: flags, call: .inputGroupCall(id: id, accessHash: accessHash), participant: inputPeer, muted: nil, volume: nil, raiseHand: nil, videoStopped: videoMuted, videoPaused: videoPaused, presentationPaused: presentationPaused))
|
return account.network.request(Api.functions.phone.editGroupCallParticipant(flags: flags, call: reference.apiInputGroupCall, participant: inputPeer, muted: nil, volume: nil, raiseHand: nil, videoStopped: videoMuted, videoPaused: videoPaused, presentationPaused: presentationPaused))
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
@ -2017,6 +2238,8 @@ public final class GroupCallParticipantsContext {
|
|||||||
if updateCallId != id {
|
if updateCallId != id {
|
||||||
continue loop
|
continue loop
|
||||||
}
|
}
|
||||||
|
case .inputGroupCallSlug, .inputGroupCallInviteMessage:
|
||||||
|
continue loop
|
||||||
}
|
}
|
||||||
stateUpdates.append(.state(update: GroupCallParticipantsContext.Update.StateUpdate(participants: participants, version: version, removePendingMuteStates: [peerId])))
|
stateUpdates.append(.state(update: GroupCallParticipantsContext.Update.StateUpdate(participants: participants, version: version, removePendingMuteStates: [peerId])))
|
||||||
default:
|
default:
|
||||||
@ -2053,7 +2276,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
videoPortrait = videoOrientation ? .boolTrue : .boolFalse
|
videoPortrait = videoOrientation ? .boolTrue : .boolFalse
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updateShouldBeRecordingDisposable.set((self.account.network.request(Api.functions.phone.toggleGroupCallRecord(flags: flags, call: .inputGroupCall(id: self.id, accessHash: self.accessHash), title: title, videoPortrait: videoPortrait))
|
self.updateShouldBeRecordingDisposable.set((self.account.network.request(Api.functions.phone.toggleGroupCallRecord(flags: flags, call: self.reference.apiInputGroupCall, title: title, videoPortrait: videoPortrait))
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] updates in
|
|> deliverOnMainQueue).start(next: { [weak self] updates in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -2068,7 +2291,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
}
|
}
|
||||||
self.stateValue.state.defaultParticipantsAreMuted.isMuted = isMuted
|
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))
|
self.updateDefaultMuteDisposable.set((self.account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 0, call: self.reference.apiInputGroupCall, joinMuted: isMuted ? .boolTrue : .boolFalse))
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] updates in
|
|> deliverOnMainQueue).start(next: { [weak self] updates in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -2078,7 +2301,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func resetInviteLinks() {
|
public func resetInviteLinks() {
|
||||||
self.resetInviteLinksDisposable.set((self.account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 1, call: .inputGroupCall(id: self.id, accessHash: self.accessHash), joinMuted: nil))
|
self.resetInviteLinksDisposable.set((self.account.network.request(Api.functions.phone.toggleGroupCallSettings(flags: 1 << 1, call: self.reference.apiInputGroupCall, joinMuted: nil))
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] updates in
|
|> deliverOnMainQueue).start(next: { [weak self] updates in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -2096,7 +2319,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
}
|
}
|
||||||
self.stateValue.state.subscribedToScheduled = subscribe
|
self.stateValue.state.subscribedToScheduled = subscribe
|
||||||
|
|
||||||
self.subscribeDisposable.set(_internal_toggleScheduledGroupCallSubscription(account: self.account, peerId: peerId, callId: self.id, accessHash: self.accessHash, subscribe: subscribe).start())
|
self.subscribeDisposable.set(_internal_toggleScheduledGroupCallSubscription(account: self.account, peerId: peerId, reference: self.reference, subscribe: subscribe).start())
|
||||||
}
|
}
|
||||||
|
|
||||||
public func loadMore(token: String) {
|
public func loadMore(token: String) {
|
||||||
@ -2109,7 +2332,7 @@ public final class GroupCallParticipantsContext {
|
|||||||
}
|
}
|
||||||
self.isLoadingMore = true
|
self.isLoadingMore = true
|
||||||
|
|
||||||
self.disposable.set((_internal_getGroupCallParticipants(account: self.account, callId: self.id, accessHash: self.accessHash, offset: token, ssrcs: [], limit: 100, sortAscending: self.stateValue.state.sortAscending)
|
self.disposable.set((_internal_getGroupCallParticipants(account: self.account, reference: self.reference, offset: token, ssrcs: [], limit: 100, sortAscending: self.stateValue.state.sortAscending)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -2236,8 +2459,8 @@ public struct GroupCallInviteLinks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func _internal_groupCallInviteLinks(account: Account, callId: Int64, accessHash: Int64) -> Signal<GroupCallInviteLinks?, NoError> {
|
func _internal_groupCallInviteLinks(account: Account, reference: InternalGroupCallReference, isConference: Bool) -> Signal<GroupCallInviteLinks?, NoError> {
|
||||||
let call = Api.InputGroupCall.inputGroupCall(id: callId, accessHash: accessHash)
|
let call = reference.apiInputGroupCall
|
||||||
let listenerInvite: Signal<String?, NoError> = account.network.request(Api.functions.phone.exportGroupCallInvite(flags: 0, call: call))
|
let listenerInvite: Signal<String?, NoError> = account.network.request(Api.functions.phone.exportGroupCallInvite(flags: 0, call: call))
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<Api.phone.ExportedGroupCallInvite?, NoError> in
|
|> `catch` { _ -> Signal<Api.phone.ExportedGroupCallInvite?, NoError> in
|
||||||
@ -2262,8 +2485,19 @@ func _internal_groupCallInviteLinks(account: Account, callId: Int64, accessHash:
|
|||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isConference {
|
||||||
|
return speakerInvite
|
||||||
|
|> map { speakerLink -> GroupCallInviteLinks? in
|
||||||
|
guard let speakerLink = speakerLink else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return GroupCallInviteLinks(listenerLink: speakerLink, speakerLink: speakerLink)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return combineLatest(listenerInvite, speakerInvite)
|
return combineLatest(listenerInvite, speakerInvite)
|
||||||
|> map { listenerLink, speakerLink in
|
|> map { listenerLink, speakerLink in
|
||||||
|
|
||||||
if let listenerLink = listenerLink {
|
if let listenerLink = listenerLink {
|
||||||
return GroupCallInviteLinks(listenerLink: listenerLink, speakerLink: speakerLink)
|
return GroupCallInviteLinks(listenerLink: listenerLink, speakerLink: speakerLink)
|
||||||
} else {
|
} else {
|
||||||
@ -2689,3 +2923,118 @@ func _internal_getGroupCallStreamCredentials(account: Account, peerId: PeerId, r
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum CreateConferenceCallError {
|
||||||
|
case generic
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class EngineCreatedGroupCall {
|
||||||
|
public let slug: String
|
||||||
|
public let link: String
|
||||||
|
public let callInfo: GroupCallInfo
|
||||||
|
|
||||||
|
public init(slug: String, link: String, callInfo: GroupCallInfo) {
|
||||||
|
self.slug = slug
|
||||||
|
self.link = link
|
||||||
|
self.callInfo = callInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _internal_createConferenceCall(postbox: Postbox, network: Network, accountPeerId: PeerId) -> Signal<EngineCreatedGroupCall, CreateConferenceCallError> {
|
||||||
|
return network.request(Api.functions.phone.createConferenceCall(randomId: Int32.random(in: Int32.min ... Int32.max)))
|
||||||
|
|> mapError { _ -> CreateConferenceCallError in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
|> mapToSignal { result in
|
||||||
|
switch result {
|
||||||
|
case let .groupCall(call, participants, _, chats, users):
|
||||||
|
return postbox.transaction { transaction -> Signal<EngineCreatedGroupCall, CreateConferenceCallError> in
|
||||||
|
guard let info = GroupCallInfo(call) else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
|
||||||
|
let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users)
|
||||||
|
|
||||||
|
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
|
||||||
|
|
||||||
|
let parsedParticipants = participants.compactMap { GroupCallParticipantsContext.Participant($0, transaction: transaction) }
|
||||||
|
let _ = parsedParticipants
|
||||||
|
|
||||||
|
let speakerInvite: Signal<EngineCreatedGroupCall, CreateConferenceCallError> = network.request(Api.functions.phone.exportGroupCallInvite(flags: 1 << 0, call: .inputGroupCall(id: info.id, accessHash: info.accessHash)))
|
||||||
|
|> map(Optional.init)
|
||||||
|
|> `catch` { _ -> Signal<Api.phone.ExportedGroupCallInvite?, NoError> in
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
|> castError(CreateConferenceCallError.self)
|
||||||
|
|> mapToSignal { result -> Signal<EngineCreatedGroupCall, CreateConferenceCallError> in
|
||||||
|
if let result, case let .exportedGroupCallInvite(link) = result {
|
||||||
|
let slug = link.components(separatedBy: "/").last ?? link
|
||||||
|
return .single(EngineCreatedGroupCall(
|
||||||
|
slug: slug,
|
||||||
|
link: link,
|
||||||
|
callInfo: info
|
||||||
|
))
|
||||||
|
}
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
return speakerInvite
|
||||||
|
}
|
||||||
|
|> mapError { _ -> CreateConferenceCallError in
|
||||||
|
}
|
||||||
|
|> switchToLatest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ConfirmAddConferenceParticipantError {
|
||||||
|
case generic
|
||||||
|
}
|
||||||
|
|
||||||
|
func _internal_pollConferenceCallBlockchain(network: Network, reference: InternalGroupCallReference, subChainId: Int, offset: Int, limit: Int) -> Signal<(blocks: [Data], nextOffset: Int)?, NoError> {
|
||||||
|
return network.request(Api.functions.phone.getGroupCallChainBlocks(call: reference.apiInputGroupCall, subChainId: Int32(subChainId), offset: Int32(offset), limit: Int32(limit)))
|
||||||
|
|> map(Optional.init)
|
||||||
|
|> `catch` { error -> Signal<Api.Updates?, NoError> in
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
|> map { result -> (blocks: [Data], nextOffset: Int)? in
|
||||||
|
guard let result = result else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var blocks: [Data] = []
|
||||||
|
var nextOffset: Int?
|
||||||
|
for update in result.allUpdates {
|
||||||
|
switch update {
|
||||||
|
case let .updateGroupCallChainBlocks(_, updateSubChainId, updateBlocks, updateNextOffset):
|
||||||
|
if updateSubChainId == Int32(subChainId) {
|
||||||
|
blocks.append(contentsOf: updateBlocks.map { $0.makeData() })
|
||||||
|
nextOffset = Int(updateNextOffset)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
guard let nextOffset = nextOffset else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return (blocks: blocks, nextOffset: nextOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _internal_sendConferenceCallBroadcast(account: Account, callId: Int64, accessHash: Int64, block: Data) -> Signal<Never, NoError> {
|
||||||
|
return account.network.request(Api.functions.phone.sendConferenceCallBroadcast(call: .inputGroupCall(id: callId, accessHash: accessHash), block: Buffer(data: block)))
|
||||||
|
|> retry(retryOnError: { _ in
|
||||||
|
return true
|
||||||
|
}, delayIncrement: 0.1, maxDelay: 1.0, maxRetries: 5, onQueue: Queue.concurrentDefaultQueue())
|
||||||
|
|> map(Optional.init)
|
||||||
|
|> `catch` { error -> Signal<Api.Updates?, NoError> in
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<Never, NoError> in
|
||||||
|
guard let result = result else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
account.stateManager.addUpdates(result)
|
||||||
|
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -33,8 +33,8 @@ public extension TelegramEngine {
|
|||||||
return _internal_saveCompleteCallDebugLog(account: self.account, callId: callId, logPath: logPath)
|
return _internal_saveCompleteCallDebugLog(account: self.account, callId: callId, logPath: logPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getCurrentGroupCall(callId: Int64, accessHash: Int64, peerId: PeerId? = nil) -> Signal<GroupCallSummary?, GetCurrentGroupCallError> {
|
public func getCurrentGroupCall(reference: InternalGroupCallReference, peerId: PeerId? = nil) -> Signal<GroupCallSummary?, GetCurrentGroupCallError> {
|
||||||
return _internal_getCurrentGroupCall(account: self.account, callId: callId, accessHash: accessHash, peerId: peerId)
|
return _internal_getCurrentGroupCall(account: self.account, reference: reference, peerId: peerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func createGroupCall(peerId: PeerId, title: String?, scheduleDate: Int32?, isExternalStream: Bool) -> Signal<GroupCallInfo, CreateGroupCallError> {
|
public func createGroupCall(peerId: PeerId, title: String?, scheduleDate: Int32?, isExternalStream: Bool) -> Signal<GroupCallInfo, CreateGroupCallError> {
|
||||||
@ -45,20 +45,20 @@ public extension TelegramEngine {
|
|||||||
return _internal_startScheduledGroupCall(account: self.account, peerId: peerId, callId: callId, accessHash: accessHash)
|
return _internal_startScheduledGroupCall(account: self.account, peerId: peerId, callId: callId, accessHash: accessHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func toggleScheduledGroupCallSubscription(peerId: PeerId, callId: Int64, accessHash: Int64, subscribe: Bool) -> Signal<Void, ToggleScheduledGroupCallSubscriptionError> {
|
public func toggleScheduledGroupCallSubscription(peerId: PeerId, reference: InternalGroupCallReference, subscribe: Bool) -> Signal<Void, ToggleScheduledGroupCallSubscriptionError> {
|
||||||
return _internal_toggleScheduledGroupCallSubscription(account: self.account, peerId: peerId, callId: callId, accessHash: accessHash, subscribe: subscribe)
|
return _internal_toggleScheduledGroupCallSubscription(account: self.account, peerId: peerId, reference: reference, subscribe: subscribe)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateGroupCallJoinAsPeer(peerId: PeerId, joinAs: PeerId) -> Signal<Never, UpdateGroupCallJoinAsPeerError> {
|
public func updateGroupCallJoinAsPeer(peerId: PeerId, joinAs: PeerId) -> Signal<Never, UpdateGroupCallJoinAsPeerError> {
|
||||||
return _internal_updateGroupCallJoinAsPeer(account: self.account, peerId: peerId, joinAs: joinAs)
|
return _internal_updateGroupCallJoinAsPeer(account: self.account, peerId: peerId, joinAs: joinAs)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getGroupCallParticipants(callId: Int64, accessHash: Int64, offset: String, ssrcs: [UInt32], limit: Int32, sortAscending: Bool?) -> Signal<GroupCallParticipantsContext.State, GetGroupCallParticipantsError> {
|
public func getGroupCallParticipants(reference: InternalGroupCallReference, offset: String, ssrcs: [UInt32], limit: Int32, sortAscending: Bool?) -> Signal<GroupCallParticipantsContext.State, GetGroupCallParticipantsError> {
|
||||||
return _internal_getGroupCallParticipants(account: self.account, callId: callId, accessHash: accessHash, offset: offset, ssrcs: ssrcs, limit: limit, sortAscending: sortAscending)
|
return _internal_getGroupCallParticipants(account: self.account, reference: reference, offset: offset, ssrcs: ssrcs, limit: limit, sortAscending: sortAscending)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func joinGroupCall(peerId: PeerId?, joinAs: PeerId?, callId: Int64, accessHash: Int64, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil, keyFingerprint: Int64?) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
|
public func joinGroupCall(peerId: PeerId?, joinAs: PeerId?, callId: Int64, reference: InternalGroupCallReference, preferMuted: Bool, joinPayload: String, peerAdminIds: Signal<[PeerId], NoError>, inviteHash: String? = nil, generateE2E: ((Data?) -> JoinGroupCallE2E?)?) -> Signal<JoinGroupCallResult, JoinGroupCallError> {
|
||||||
return _internal_joinGroupCall(account: self.account, peerId: peerId, joinAs: joinAs, callId: callId, accessHash: accessHash, preferMuted: preferMuted, joinPayload: joinPayload, peerAdminIds: peerAdminIds, inviteHash: inviteHash, keyFingerprint: keyFingerprint)
|
return _internal_joinGroupCall(account: self.account, peerId: peerId, joinAs: joinAs, callId: callId, reference: reference, preferMuted: preferMuted, joinPayload: joinPayload, peerAdminIds: peerAdminIds, inviteHash: inviteHash, generateE2E: generateE2E)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func joinGroupCallAsScreencast(callId: Int64, accessHash: Int64, joinPayload: String) -> Signal<JoinGroupCallAsScreencastResult, JoinGroupCallError> {
|
public func joinGroupCallAsScreencast(callId: Int64, accessHash: Int64, joinPayload: String) -> Signal<JoinGroupCallAsScreencastResult, JoinGroupCallError> {
|
||||||
@ -85,17 +85,33 @@ public extension TelegramEngine {
|
|||||||
return _internal_inviteToGroupCall(account: self.account, callId: callId, accessHash: accessHash, peerId: peerId)
|
return _internal_inviteToGroupCall(account: self.account, callId: callId, accessHash: accessHash, peerId: peerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func groupCallInviteLinks(callId: Int64, accessHash: Int64) -> Signal<GroupCallInviteLinks?, NoError> {
|
public func groupCallInviteLinks(reference: InternalGroupCallReference, isConference: Bool) -> Signal<GroupCallInviteLinks?, NoError> {
|
||||||
return _internal_groupCallInviteLinks(account: self.account, callId: callId, accessHash: accessHash)
|
return _internal_groupCallInviteLinks(account: self.account, reference: reference, isConference: isConference)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func editGroupCallTitle(callId: Int64, accessHash: Int64, title: String) -> Signal<Never, EditGroupCallTitleError> {
|
public func editGroupCallTitle(callId: Int64, accessHash: Int64, title: String) -> Signal<Never, EditGroupCallTitleError> {
|
||||||
return _internal_editGroupCallTitle(account: self.account, callId: callId, accessHash: accessHash, title: title)
|
return _internal_editGroupCallTitle(account: self.account, callId: callId, accessHash: accessHash, title: title)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*public func groupCallDisplayAsAvailablePeers(peerId: PeerId) -> Signal<[FoundPeer], NoError> {
|
public func createConferenceCall() -> Signal<EngineCreatedGroupCall, CreateConferenceCallError> {
|
||||||
return _internal_groupCallDisplayAsAvailablePeers(network: self.account.network, postbox: self.account.postbox, peerId: peerId)
|
return _internal_createConferenceCall(postbox: self.account.postbox, network: self.account.network, accountPeerId: self.account.peerId)
|
||||||
}*/
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func sendConferenceCallBroadcast(callId: Int64, accessHash: Int64, block: Data) -> Signal<Never, NoError> {
|
||||||
|
return _internal_sendConferenceCallBroadcast(account: self.account, callId: callId, accessHash: accessHash, block: block)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func inviteConferenceCallParticipant(callId: Int64, accessHash: Int64, peerId: EnginePeer.Id) -> Signal<Never, NoError> {
|
||||||
|
return _internal_inviteConferenceCallParticipant(account: self.account, callId: callId, accessHash: accessHash, peerId: peerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func removeGroupCallBlockchainParticipant(callId: Int64, accessHash: Int64, block: @escaping ([EnginePeer.Id]) -> Data?) -> Signal<Never, RemoveGroupCallBlockchainParticipantError> {
|
||||||
|
return _internal_removeGroupCallBlockchainParticipants(account: self.account, callId: callId, accessHash: accessHash, block: block)
|
||||||
|
}
|
||||||
|
|
||||||
public func clearCachedGroupCallDisplayAsAvailablePeers(peerId: PeerId) -> Signal<Never, NoError> {
|
public func clearCachedGroupCallDisplayAsAvailablePeers(peerId: PeerId) -> Signal<Never, NoError> {
|
||||||
return _internal_clearCachedGroupCallDisplayAsAvailablePeers(account: self.account, peerId: peerId)
|
return _internal_clearCachedGroupCallDisplayAsAvailablePeers(account: self.account, peerId: peerId)
|
||||||
@ -124,8 +140,8 @@ public extension TelegramEngine {
|
|||||||
return _internal_getVideoBroadcastPart(dataSource: dataSource, callId: callId, accessHash: accessHash, timestampIdMilliseconds: timestampIdMilliseconds, durationMilliseconds: durationMilliseconds, channelId: channelId, quality: quality)
|
return _internal_getVideoBroadcastPart(dataSource: dataSource, callId: callId, accessHash: accessHash, timestampIdMilliseconds: timestampIdMilliseconds, durationMilliseconds: durationMilliseconds, channelId: channelId, quality: quality)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func groupCall(peerId: PeerId?, myPeerId: PeerId, id: Int64, accessHash: Int64, state: GroupCallParticipantsContext.State, previousServiceState: GroupCallParticipantsContext.ServiceState?) -> GroupCallParticipantsContext {
|
public func groupCall(peerId: PeerId?, myPeerId: PeerId, id: Int64, reference: InternalGroupCallReference, state: GroupCallParticipantsContext.State, previousServiceState: GroupCallParticipantsContext.ServiceState?) -> GroupCallParticipantsContext {
|
||||||
return GroupCallParticipantsContext(account: self.account, peerId: peerId, myPeerId: myPeerId, id: id, accessHash: accessHash, state: state, previousServiceState: previousServiceState)
|
return GroupCallParticipantsContext(account: self.account, peerId: peerId, myPeerId: myPeerId, id: id, reference: reference, state: state, previousServiceState: previousServiceState)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func serverTime() -> Signal<Int64, NoError> {
|
public func serverTime() -> Signal<Int64, NoError> {
|
||||||
|
@ -143,11 +143,15 @@ func _internal_joinLinkInformation(_ hash: String, account: Account) -> Signal<E
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class JoinCallLinkInformation {
|
public final class JoinCallLinkInformation {
|
||||||
|
public let id: Int64
|
||||||
|
public let accessHash: Int64
|
||||||
public let inviter: EnginePeer?
|
public let inviter: EnginePeer?
|
||||||
public let members: [EnginePeer]
|
public let members: [EnginePeer]
|
||||||
public let totalMemberCount: Int
|
public let totalMemberCount: Int
|
||||||
|
|
||||||
public init(inviter: EnginePeer?, members: [EnginePeer], totalMemberCount: Int) {
|
public init(id: Int64, accessHash: Int64, inviter: EnginePeer?, members: [EnginePeer], totalMemberCount: Int) {
|
||||||
|
self.id = id
|
||||||
|
self.accessHash = accessHash
|
||||||
self.inviter = inviter
|
self.inviter = inviter
|
||||||
self.members = members
|
self.members = members
|
||||||
self.totalMemberCount = totalMemberCount
|
self.totalMemberCount = totalMemberCount
|
||||||
@ -155,29 +159,41 @@ public final class JoinCallLinkInformation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _internal_joinCallLinkInformation(_ hash: String, account: Account) -> Signal<JoinCallLinkInformation, JoinLinkInfoError> {
|
func _internal_joinCallLinkInformation(_ hash: String, account: Account) -> Signal<JoinCallLinkInformation, JoinLinkInfoError> {
|
||||||
//TODO:release
|
return _internal_getCurrentGroupCall(account: account, reference: .link(slug: hash))
|
||||||
|
|> mapError { error -> JoinLinkInfoError in
|
||||||
let invite: Signal<Api.ChatInvite?, JoinLinkInfoError> = account.network.request(Api.functions.messages.checkChatInvite(hash: hash), automaticFloodWait: false)
|
switch error {
|
||||||
|> map(Optional.init)
|
case .generic:
|
||||||
|> `catch` { error -> Signal<Api.ChatInvite?, JoinLinkInfoError> in
|
return .generic
|
||||||
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
|
||||||
return .fail(.flood)
|
|
||||||
} else {
|
|
||||||
return .single(nil)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|> mapToSignal { call -> Signal<JoinCallLinkInformation, JoinLinkInfoError> in
|
||||||
return invite
|
guard let call = call else {
|
||||||
|> mapToSignal { result -> Signal<JoinCallLinkInformation, JoinLinkInfoError> in
|
|
||||||
if let result {
|
|
||||||
switch result {
|
|
||||||
case let .chatInvite(_, _, _, _, participantsCount, participants, _, _, _, _):
|
|
||||||
return .single(JoinCallLinkInformation(inviter: nil, members: participants?.map({ EnginePeer(TelegramUser(user: $0)) }) ?? [], totalMemberCount: Int(participantsCount)))
|
|
||||||
default:
|
|
||||||
return .fail(.generic)
|
return .fail(.generic)
|
||||||
}
|
}
|
||||||
} else {
|
var members: [EnginePeer] = []
|
||||||
return .fail(.generic)
|
for participant in call.topParticipants {
|
||||||
|
members.append(EnginePeer(participant.peer))
|
||||||
}
|
}
|
||||||
|
return .single(JoinCallLinkInformation(id: call.info.id, accessHash: call.info.accessHash, inviter: nil, members: members, totalMemberCount: call.info.participantCount))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _internal_joinCallInvitationInformation(account: Account, messageId: MessageId) -> Signal<JoinCallLinkInformation, JoinLinkInfoError> {
|
||||||
|
return _internal_getCurrentGroupCall(account: account, reference: .message(id: messageId))
|
||||||
|
|> mapError { error -> JoinLinkInfoError in
|
||||||
|
switch error {
|
||||||
|
case .generic:
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> mapToSignal { call -> Signal<JoinCallLinkInformation, JoinLinkInfoError> in
|
||||||
|
guard let call = call else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
var members: [EnginePeer] = []
|
||||||
|
for participant in call.topParticipants {
|
||||||
|
members.append(EnginePeer(participant.peer))
|
||||||
|
}
|
||||||
|
return .single(JoinCallLinkInformation(id: call.info.id, accessHash: call.info.accessHash, inviter: nil, members: members, totalMemberCount: call.info.participantCount))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -842,6 +842,10 @@ public extension TelegramEngine {
|
|||||||
return _internal_joinCallLinkInformation(hash, account: self.account)
|
return _internal_joinCallLinkInformation(hash, account: self.account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func joinCallInvitationInformation(messageId: EngineMessage.Id) -> Signal<JoinCallLinkInformation, JoinLinkInfoError> {
|
||||||
|
return _internal_joinCallInvitationInformation(account: self.account, messageId: messageId)
|
||||||
|
}
|
||||||
|
|
||||||
public func updatePeerTitle(peerId: PeerId, title: String) -> Signal<Void, UpdatePeerTitleError> {
|
public func updatePeerTitle(peerId: PeerId, title: String) -> Signal<Void, UpdatePeerTitleError> {
|
||||||
return _internal_updatePeerTitle(account: self.account, peerId: peerId, title: title)
|
return _internal_updatePeerTitle(account: self.account, peerId: peerId, title: title)
|
||||||
}
|
}
|
||||||
|
@ -534,6 +534,8 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
|
|||||||
switch inputCall {
|
switch inputCall {
|
||||||
case let .inputGroupCall(id, accessHash):
|
case let .inputGroupCall(id, accessHash):
|
||||||
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title, scheduleTimestamp: previous.activeCall?.scheduleTimestamp, subscribedToScheduled: previous.activeCall?.subscribedToScheduled ?? false, isStream: previous.activeCall?.isStream)
|
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title, scheduleTimestamp: previous.activeCall?.scheduleTimestamp, subscribedToScheduled: previous.activeCall?.subscribedToScheduled ?? false, isStream: previous.activeCall?.isStream)
|
||||||
|
case .inputGroupCallSlug, .inputGroupCallInviteMessage:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -787,6 +789,8 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
|
|||||||
switch inputCall {
|
switch inputCall {
|
||||||
case let .inputGroupCall(id, accessHash):
|
case let .inputGroupCall(id, accessHash):
|
||||||
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title, scheduleTimestamp: previous.activeCall?.scheduleTimestamp, subscribedToScheduled: previous.activeCall?.subscribedToScheduled ?? false, isStream: previous.activeCall?.isStream)
|
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title, scheduleTimestamp: previous.activeCall?.scheduleTimestamp, subscribedToScheduled: previous.activeCall?.subscribedToScheduled ?? false, isStream: previous.activeCall?.isStream)
|
||||||
|
case .inputGroupCallSlug, .inputGroupCallInviteMessage:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -616,6 +616,10 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
|
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
|
||||||
|
case .conferenceCall:
|
||||||
|
//TODO:localize
|
||||||
|
let titleString = "Group call"
|
||||||
|
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
|
||||||
case let .groupPhoneCall(_, _, scheduleDate, duration):
|
case let .groupPhoneCall(_, _, scheduleDate, duration):
|
||||||
if let scheduleDate = scheduleDate {
|
if let scheduleDate = scheduleDate {
|
||||||
if message.author?.id.namespace == Namespaces.Peer.CloudChannel {
|
if message.author?.id.namespace == Namespaces.Peer.CloudChannel {
|
||||||
|
@ -175,7 +175,7 @@ public final class ChatBotInfoItemNode: ListViewItemNode {
|
|||||||
break
|
break
|
||||||
case .ignore:
|
case .ignore:
|
||||||
return .fail
|
return .fail
|
||||||
case .url, .phone, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji, .custom:
|
case .url, .phone, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .conferenceCall, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji, .custom:
|
||||||
return .waitForSingleTap
|
return .waitForSingleTap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,6 +151,7 @@ public struct ChatMessageBubbleContentTapAction {
|
|||||||
case wallpaper
|
case wallpaper
|
||||||
case theme
|
case theme
|
||||||
case call(peerId: PeerId, isVideo: Bool)
|
case call(peerId: PeerId, isVideo: Bool)
|
||||||
|
case conferenceCall(message: Message)
|
||||||
case openMessage
|
case openMessage
|
||||||
case timecode(Double, String)
|
case timecode(Double, String)
|
||||||
case tooltip(String, ASDisplayNode?, CGRect?)
|
case tooltip(String, ASDisplayNode?, CGRect?)
|
||||||
|
@ -213,6 +213,8 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
|||||||
isAction = true
|
isAction = true
|
||||||
if case .phoneCall = action.action {
|
if case .phoneCall = action.action {
|
||||||
result.append((message, ChatMessageCallBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
result.append((message, ChatMessageCallBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||||
|
} else if case .conferenceCall = action.action {
|
||||||
|
result.append((message, ChatMessageCallBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||||
} else if case .giftPremium = action.action {
|
} else if case .giftPremium = action.action {
|
||||||
result.append((message, ChatMessageGiftBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
result.append((message, ChatMessageGiftBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||||
} else if case .giftStars = action.action {
|
} else if case .giftStars = action.action {
|
||||||
@ -1251,7 +1253,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
break
|
break
|
||||||
case .ignore:
|
case .ignore:
|
||||||
return .fail
|
return .fail
|
||||||
case .url, .phone, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji, .custom:
|
case .url, .phone, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .conferenceCall, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji, .custom:
|
||||||
return .waitForSingleTap
|
return .waitForSingleTap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1347,7 +1349,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
else if let media = media as? TelegramMediaAction {
|
else if let media = media as? TelegramMediaAction {
|
||||||
if case .phoneCall(_, _, _, _) = media.action {
|
if case .phoneCall = media.action {
|
||||||
|
} else if case .conferenceCall = media.action {
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -2723,6 +2726,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
switch action.action {
|
switch action.action {
|
||||||
case .phoneCall:
|
case .phoneCall:
|
||||||
break
|
break
|
||||||
|
case .conferenceCall:
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
centerAligned = true
|
centerAligned = true
|
||||||
}
|
}
|
||||||
@ -5034,6 +5039,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
return .optionalAction({
|
return .optionalAction({
|
||||||
self.item?.controllerInteraction.callPeer(peerId, isVideo)
|
self.item?.controllerInteraction.callPeer(peerId, isVideo)
|
||||||
})
|
})
|
||||||
|
case let .conferenceCall(message):
|
||||||
|
return .optionalAction({
|
||||||
|
self.item?.controllerInteraction.openConferenceCall(message)
|
||||||
|
})
|
||||||
case .openMessage:
|
case .openMessage:
|
||||||
if let item = self.item {
|
if let item = self.item {
|
||||||
if let type = self.backgroundNode.type, case .none = type {
|
if let type = self.backgroundNode.type, case .none = type {
|
||||||
@ -5221,6 +5230,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
break
|
break
|
||||||
case .call:
|
case .call:
|
||||||
break
|
break
|
||||||
|
case .conferenceCall:
|
||||||
|
break
|
||||||
case .openMessage:
|
case .openMessage:
|
||||||
break
|
break
|
||||||
case let .timecode(timecode, text):
|
case let .timecode(timecode, text):
|
||||||
@ -5516,6 +5527,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
for media in message.media {
|
for media in message.media {
|
||||||
if let action = media as? TelegramMediaAction {
|
if let action = media as? TelegramMediaAction {
|
||||||
if case .phoneCall = action.action {
|
if case .phoneCall = action.action {
|
||||||
|
} else if case .conferenceCall = action.action {
|
||||||
} else {
|
} else {
|
||||||
canHaveSelection = false
|
canHaveSelection = false
|
||||||
break
|
break
|
||||||
|
@ -123,6 +123,12 @@ public class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
} else if let action = media as? TelegramMediaAction, case let .conferenceCall(_, duration, _) = action.action {
|
||||||
|
isVideo = false
|
||||||
|
callDuration = duration
|
||||||
|
//TODO:localize
|
||||||
|
titleString = "Group Call"
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,6 +260,9 @@ public class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
for media in item.message.media {
|
for media in item.message.media {
|
||||||
if let action = media as? TelegramMediaAction, case let .phoneCall(_, _, _, isVideoValue) = action.action {
|
if let action = media as? TelegramMediaAction, case let .phoneCall(_, _, _, isVideoValue) = action.action {
|
||||||
isVideo = isVideoValue
|
isVideo = isVideoValue
|
||||||
|
} else if let action = media as? TelegramMediaAction, case .conferenceCall = action.action {
|
||||||
|
item.controllerInteraction.openConferenceCall(item.message)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item.controllerInteraction.callPeer(item.message.id.peerId, isVideo)
|
item.controllerInteraction.callPeer(item.message.id.peerId, isVideo)
|
||||||
@ -268,6 +277,8 @@ public class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
for media in item.message.media {
|
for media in item.message.media {
|
||||||
if let action = media as? TelegramMediaAction, case let .phoneCall(_, _, _, isVideoValue) = action.action {
|
if let action = media as? TelegramMediaAction, case let .phoneCall(_, _, _, isVideoValue) = action.action {
|
||||||
isVideo = isVideoValue
|
isVideo = isVideoValue
|
||||||
|
} else if let action = media as? TelegramMediaAction, case .conferenceCall = action.action {
|
||||||
|
return ChatMessageBubbleContentTapAction(content: .conferenceCall(message: item.message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ChatMessageBubbleContentTapAction(content: .call(peerId: item.message.id.peerId, isVideo: isVideo))
|
return ChatMessageBubbleContentTapAction(content: .call(peerId: item.message.id.peerId, isVideo: isVideo))
|
||||||
|
@ -1603,8 +1603,18 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
onlyFullSizeVideoThumbnail = isSendingUpdated
|
onlyFullSizeVideoThumbnail = isSendingUpdated
|
||||||
|
let codecConfiguration = HLSCodecConfiguration(context: context)
|
||||||
updateImageSignal = { synchronousLoad, _ in
|
updateImageSignal = { synchronousLoad, _ in
|
||||||
return mediaGridMessageVideo(postbox: context.account.postbox, userLocation: .peer(message.id.peerId), videoReference: .message(message: MessageReference(message), media: file), onlyFullSize: currentMedia?.id?.namespace == Namespaces.Media.LocalFile, autoFetchFullSizeThumbnail: true)
|
let videoReference: FileMediaReference = .message(message: MessageReference(message), media: file)
|
||||||
|
var hlsFiles: [(playlist: TelegramMediaFile, video: TelegramMediaFile)] = []
|
||||||
|
if let qualitySet = HLSQualitySet(baseFile: videoReference, codecConfiguration: codecConfiguration) {
|
||||||
|
for key in qualitySet.playlistFiles.keys.sorted() {
|
||||||
|
if let playlist = qualitySet.playlistFiles[key], let file = qualitySet.qualityFiles[key] {
|
||||||
|
hlsFiles.append((playlist.media, file.media))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mediaGridMessageVideo(postbox: context.account.postbox, userLocation: .peer(message.id.peerId), videoReference: videoReference, hlsFiles: hlsFiles, onlyFullSize: currentMedia?.id?.namespace == Namespaces.Media.LocalFile, autoFetchFullSizeThumbnail: true)
|
||||||
}
|
}
|
||||||
updateBlurredImageSignal = { synchronousLoad, _ in
|
updateBlurredImageSignal = { synchronousLoad, _ in
|
||||||
return chatSecretMessageVideo(account: context.account, userLocation: .peer(message.id.peerId), videoReference: .message(message: MessageReference(message), media: file), synchronousLoad: true)
|
return chatSecretMessageVideo(account: context.account, userLocation: .peer(message.id.peerId), videoReference: .message(message: MessageReference(message), media: file), synchronousLoad: true)
|
||||||
|
@ -590,6 +590,7 @@ public final class ChatMessageAccessibilityData {
|
|||||||
}
|
}
|
||||||
else if let media = media as? TelegramMediaAction {
|
else if let media = media as? TelegramMediaAction {
|
||||||
if case .phoneCall = media.action {
|
if case .phoneCall = media.action {
|
||||||
|
} else if case .conferenceCall = media.action {
|
||||||
} else {
|
} else {
|
||||||
canReply = false
|
canReply = false
|
||||||
}
|
}
|
||||||
|
@ -264,6 +264,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
self?.openPeer(peer: EnginePeer(peer))
|
self?.openPeer(peer: EnginePeer(peer))
|
||||||
}, callPeer: { peerId, isVideo in
|
}, callPeer: { peerId, isVideo in
|
||||||
self?.controllerInteraction?.callPeer(peerId, isVideo)
|
self?.controllerInteraction?.callPeer(peerId, isVideo)
|
||||||
|
}, openConferenceCall: { message in
|
||||||
|
self?.controllerInteraction?.openConferenceCall(message)
|
||||||
}, enqueueMessage: { _ in
|
}, enqueueMessage: { _ in
|
||||||
}, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { signal, media in
|
}, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { signal, media in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
@ -372,7 +374,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
return self?.getNavigationController()
|
return self?.getNavigationController()
|
||||||
}, chatControllerNode: { [weak self] in
|
}, chatControllerNode: { [weak self] in
|
||||||
return self
|
return self
|
||||||
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, longTap: { [weak self] action, params in
|
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, openConferenceCall: { _ in
|
||||||
|
}, longTap: { [weak self] action, params in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
switch action {
|
switch action {
|
||||||
case let .url(url):
|
case let .url(url):
|
||||||
@ -1383,7 +1386,12 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
let _ = (signal
|
let _ = (signal
|
||||||
|> deliverOnMainQueue).startStandalone(next: { [weak navigationController] resolvedCallLink in
|
|> deliverOnMainQueue).startStandalone(next: { [weak navigationController] resolvedCallLink in
|
||||||
navigationController?.pushViewController(context.sharedContext.makeJoinSubjectScreen(context: context, mode: JoinSubjectScreenMode.groupCall(JoinSubjectScreenMode.GroupCall(
|
navigationController?.pushViewController(context.sharedContext.makeJoinSubjectScreen(context: context, mode: JoinSubjectScreenMode.groupCall(JoinSubjectScreenMode.GroupCall(
|
||||||
inviter: resolvedCallLink.inviter, members: resolvedCallLink.members, totalMemberCount: resolvedCallLink.totalMemberCount
|
id: resolvedCallLink.id,
|
||||||
|
accessHash: resolvedCallLink.accessHash,
|
||||||
|
slug: link,
|
||||||
|
inviter: resolvedCallLink.inviter,
|
||||||
|
members: resolvedCallLink.members,
|
||||||
|
totalMemberCount: resolvedCallLink.totalMemberCount
|
||||||
))))
|
))))
|
||||||
})
|
})
|
||||||
case let .localization(identifier):
|
case let .localization(identifier):
|
||||||
|
@ -428,7 +428,8 @@ public final class ChatSendGroupMediaMessageContextPreview: UIView, ChatSendMess
|
|||||||
return nil
|
return nil
|
||||||
}, chatControllerNode: {
|
}, chatControllerNode: {
|
||||||
return nil
|
return nil
|
||||||
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _, _ in }, openSearch: { }, setupReply: { _ in
|
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, openConferenceCall: { _ in
|
||||||
|
}, longTap: { _, _ in }, openCheckoutOrReceipt: { _, _ in }, openSearch: { }, setupReply: { _ in
|
||||||
}, canSetupReply: { _ in
|
}, canSetupReply: { _ in
|
||||||
return .none
|
return .none
|
||||||
}, canSendMessages: {
|
}, canSendMessages: {
|
||||||
|
@ -208,6 +208,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
|
|||||||
public let chatControllerNode: () -> ASDisplayNode?
|
public let chatControllerNode: () -> ASDisplayNode?
|
||||||
public let presentGlobalOverlayController: (ViewController, Any?) -> Void
|
public let presentGlobalOverlayController: (ViewController, Any?) -> Void
|
||||||
public let callPeer: (PeerId, Bool) -> Void
|
public let callPeer: (PeerId, Bool) -> Void
|
||||||
|
public let openConferenceCall: (Message) -> Void
|
||||||
public let longTap: (ChatControllerInteractionLongTapAction, LongTapParams?) -> Void
|
public let longTap: (ChatControllerInteractionLongTapAction, LongTapParams?) -> Void
|
||||||
public let openCheckoutOrReceipt: (MessageId, OpenMessageParams?) -> Void
|
public let openCheckoutOrReceipt: (MessageId, OpenMessageParams?) -> Void
|
||||||
public let openSearch: () -> Void
|
public let openSearch: () -> Void
|
||||||
@ -367,6 +368,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
|
|||||||
chatControllerNode: @escaping () -> ASDisplayNode?,
|
chatControllerNode: @escaping () -> ASDisplayNode?,
|
||||||
presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void,
|
presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void,
|
||||||
callPeer: @escaping (PeerId, Bool) -> Void,
|
callPeer: @escaping (PeerId, Bool) -> Void,
|
||||||
|
openConferenceCall: @escaping (Message) -> Void,
|
||||||
longTap: @escaping (ChatControllerInteractionLongTapAction, LongTapParams?) -> Void,
|
longTap: @escaping (ChatControllerInteractionLongTapAction, LongTapParams?) -> Void,
|
||||||
openCheckoutOrReceipt: @escaping (MessageId, OpenMessageParams?) -> Void,
|
openCheckoutOrReceipt: @escaping (MessageId, OpenMessageParams?) -> Void,
|
||||||
openSearch: @escaping () -> Void,
|
openSearch: @escaping () -> Void,
|
||||||
@ -482,6 +484,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
|
|||||||
self.chatControllerNode = chatControllerNode
|
self.chatControllerNode = chatControllerNode
|
||||||
self.presentGlobalOverlayController = presentGlobalOverlayController
|
self.presentGlobalOverlayController = presentGlobalOverlayController
|
||||||
self.callPeer = callPeer
|
self.callPeer = callPeer
|
||||||
|
self.openConferenceCall = openConferenceCall
|
||||||
self.longTap = longTap
|
self.longTap = longTap
|
||||||
self.openCheckoutOrReceipt = openCheckoutOrReceipt
|
self.openCheckoutOrReceipt = openCheckoutOrReceipt
|
||||||
self.openSearch = openSearch
|
self.openSearch = openSearch
|
||||||
|
@ -394,7 +394,20 @@ private final class JoinSubjectScreenComponent: Component {
|
|||||||
self.environment?.controller()?.dismiss()
|
self.environment?.controller()?.dismiss()
|
||||||
})
|
})
|
||||||
case let .groupCall(groupCall):
|
case let .groupCall(groupCall):
|
||||||
let _ = groupCall
|
component.context.sharedContext.callManager?.joinConferenceCall(
|
||||||
|
accountContext: component.context,
|
||||||
|
initialCall: EngineGroupCallDescription(
|
||||||
|
id: groupCall.id,
|
||||||
|
accessHash: groupCall.accessHash,
|
||||||
|
title: nil,
|
||||||
|
scheduleTimestamp: nil,
|
||||||
|
subscribedToScheduled: false,
|
||||||
|
isStream: false
|
||||||
|
),
|
||||||
|
reference: .link(slug: groupCall.slug),
|
||||||
|
mode: .joining
|
||||||
|
)
|
||||||
|
|
||||||
self.environment?.controller()?.dismiss()
|
self.environment?.controller()?.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,6 +157,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/Gifts/GiftViewScreen",
|
"//submodules/TelegramUI/Components/Gifts/GiftViewScreen",
|
||||||
"//submodules/TelegramUI/Components/BatchVideoRendering",
|
"//submodules/TelegramUI/Components/BatchVideoRendering",
|
||||||
"//submodules/TelegramUI/Components/GifVideoLayer",
|
"//submodules/TelegramUI/Components/GifVideoLayer",
|
||||||
|
"//submodules/Components/HierarchyTrackingLayer",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -17,7 +17,7 @@ import Postbox
|
|||||||
import TelegramCore
|
import TelegramCore
|
||||||
import EmojiStatusComponent
|
import EmojiStatusComponent
|
||||||
import GalleryUI
|
import GalleryUI
|
||||||
|
import HierarchyTrackingLayer
|
||||||
|
|
||||||
final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
@ -32,6 +32,8 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
private var videoContent: NativeVideoContent?
|
private var videoContent: NativeVideoContent?
|
||||||
private var videoStartTimestamp: Double?
|
private var videoStartTimestamp: Double?
|
||||||
|
|
||||||
|
private let hierarchyTrackingLayer = HierarchyTrackingLayer()
|
||||||
|
|
||||||
var isExpanded: Bool = false
|
var isExpanded: Bool = false
|
||||||
var canAttachVideo: Bool = true {
|
var canAttachVideo: Bool = true {
|
||||||
didSet {
|
didSet {
|
||||||
@ -78,6 +80,21 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
tapGestureRecognizer.isEnabled = true
|
tapGestureRecognizer.isEnabled = true
|
||||||
strongSelf.contextAction?(strongSelf.containerNode, gesture)
|
strongSelf.contextAction?(strongSelf.containerNode, gesture)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.hierarchyTrackingLayer.isInHierarchyUpdated = { [weak self] value in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if value {
|
||||||
|
self.updateFromParams()
|
||||||
|
} else {
|
||||||
|
self.videoNode?.removeFromSupernode()
|
||||||
|
self.videoNode = nil
|
||||||
|
self.videoContent = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.layer.addSublayer(self.hierarchyTrackingLayer)
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -190,8 +207,51 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct Params {
|
||||||
|
let peer: Peer?
|
||||||
|
let threadId: Int64?
|
||||||
|
let threadInfo: EngineMessageHistoryThread.Info?
|
||||||
|
let item: PeerInfoAvatarListItem?
|
||||||
|
let theme: PresentationTheme
|
||||||
|
let avatarSize: CGFloat
|
||||||
|
let isExpanded: Bool
|
||||||
|
let isSettings: Bool
|
||||||
|
|
||||||
|
init(peer: Peer?, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool, isSettings: Bool) {
|
||||||
|
self.peer = peer
|
||||||
|
self.threadId = threadId
|
||||||
|
self.threadInfo = threadInfo
|
||||||
|
self.item = item
|
||||||
|
self.theme = theme
|
||||||
|
self.avatarSize = avatarSize
|
||||||
|
self.isExpanded = isExpanded
|
||||||
|
self.isSettings = isSettings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var removedPhotoResourceIds = Set<String>()
|
var removedPhotoResourceIds = Set<String>()
|
||||||
|
private var params: Params?
|
||||||
|
|
||||||
|
private func updateFromParams() {
|
||||||
|
guard let params = self.params else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update(
|
||||||
|
peer: params.peer,
|
||||||
|
threadId: params.threadId,
|
||||||
|
threadInfo: params.threadInfo,
|
||||||
|
item: params.item,
|
||||||
|
theme: params.theme,
|
||||||
|
avatarSize: params.avatarSize,
|
||||||
|
isExpanded: params.isExpanded,
|
||||||
|
isSettings: params.isSettings
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func update(peer: Peer?, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool, isSettings: Bool) {
|
func update(peer: Peer?, threadId: Int64?, threadInfo: EngineMessageHistoryThread.Info?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool, isSettings: Bool) {
|
||||||
|
self.params = Params(peer: peer, threadId: threadId, threadInfo: threadInfo, item: item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded, isSettings: isSettings)
|
||||||
|
|
||||||
if let peer = peer {
|
if let peer = peer {
|
||||||
let previousItem = self.item
|
let previousItem = self.item
|
||||||
var item = item
|
var item = item
|
||||||
@ -345,6 +405,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
if videoContent.id != self.videoContent?.id {
|
if videoContent.id != self.videoContent?.id {
|
||||||
self.videoNode?.removeFromSupernode()
|
self.videoNode?.removeFromSupernode()
|
||||||
|
|
||||||
|
if self.hierarchyTrackingLayer.isInHierarchy {
|
||||||
let mediaManager = self.context.sharedContext.mediaManager
|
let mediaManager = self.context.sharedContext.mediaManager
|
||||||
let videoNode = UniversalVideoNode(context: self.context, postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
|
let videoNode = UniversalVideoNode(context: self.context, postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
|
||||||
videoNode.isUserInteractionEnabled = false
|
videoNode.isUserInteractionEnabled = false
|
||||||
@ -392,6 +453,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
|
|
||||||
self.avatarNode.contentNode.addSubnode(videoNode)
|
self.avatarNode.contentNode.addSubnode(videoNode)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if let markupNode = self.markupNode {
|
if let markupNode = self.markupNode {
|
||||||
self.markupNode = nil
|
self.markupNode = nil
|
||||||
|
@ -196,9 +196,11 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
|
|||||||
self.videoContent = nil
|
self.videoContent = nil
|
||||||
self.videoNode = nil
|
self.videoNode = nil
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
videoNode.removeFromSupernode()
|
videoNode.removeFromSupernode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if let videoNode = self.videoNode {
|
} else if let videoNode = self.videoNode {
|
||||||
self.videoStartTimestamp = nil
|
self.videoStartTimestamp = nil
|
||||||
self.videoContent = nil
|
self.videoContent = nil
|
||||||
|
@ -3617,6 +3617,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
}, chatControllerNode: {
|
}, chatControllerNode: {
|
||||||
return nil
|
return nil
|
||||||
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in
|
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in
|
||||||
|
}, openConferenceCall: { _ in
|
||||||
}, longTap: { [weak self] content, _ in
|
}, longTap: { [weak self] content, _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -5430,7 +5431,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
}, openPeer: { [weak self] peer, navigation in
|
}, openPeer: { [weak self] peer, navigation in
|
||||||
self?.openPeer(peerId: peer.id, navigation: navigation)
|
self?.openPeer(peerId: peer.id, navigation: navigation)
|
||||||
}, callPeer: { peerId, isVideo in
|
}, callPeer: { peerId, isVideo in
|
||||||
//self?.controllerInteraction?.callPeer(peerId)
|
}, openConferenceCall: { _ in
|
||||||
}, enqueueMessage: { _ in
|
}, enqueueMessage: { _ in
|
||||||
}, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, actionInteraction: GalleryControllerActionInteraction(openUrl: { [weak self] url, concealed in
|
}, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, actionInteraction: GalleryControllerActionInteraction(openUrl: { [weak self] url, concealed in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
@ -2793,7 +2793,7 @@ final class StorageUsageScreenComponent: Component {
|
|||||||
let _ = self
|
let _ = self
|
||||||
},
|
},
|
||||||
callPeer: { _, _ in
|
callPeer: { _, _ in
|
||||||
//self?.controllerInteraction?.callPeer(peerId)
|
}, openConferenceCall: { _ in
|
||||||
},
|
},
|
||||||
enqueueMessage: { _ in
|
enqueueMessage: { _ in
|
||||||
},
|
},
|
||||||
|
@ -307,6 +307,8 @@ extension ChatControllerImpl {
|
|||||||
switch action.action {
|
switch action.action {
|
||||||
case .phoneCall:
|
case .phoneCall:
|
||||||
break
|
break
|
||||||
|
case .conferenceCall:
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
hideReactionPanelTail = true
|
hideReactionPanelTail = true
|
||||||
}
|
}
|
||||||
|
@ -177,6 +177,7 @@ extension ChatControllerImpl {
|
|||||||
contextController?.present(c, in: .current)
|
contextController?.present(c, in: .current)
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = self.context.sharedContext.openChatMessage(OpenChatMessageParams(context: self.context, chatLocation: nil, chatFilterTag: nil, chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: nil, dismissInput: { }, present: { _, _, _ in }, transitionNode: { _, _, _ in return nil }, addToTransitionSurface: { _ in }, openUrl: { _ in }, openPeer: { _, _ in }, callPeer: { _, _ in }, enqueueMessage: { _ in }, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: .singleMessage(message.id)))
|
let _ = self.context.sharedContext.openChatMessage(OpenChatMessageParams(context: self.context, chatLocation: nil, chatFilterTag: nil, chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: nil, dismissInput: { }, present: { _, _, _ in }, transitionNode: { _, _, _ in return nil }, addToTransitionSurface: { _ in }, openUrl: { _ in }, openPeer: { _, _ in }, callPeer: { _, _ in }, openConferenceCall: { _ in
|
||||||
|
}, enqueueMessage: { _ in }, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: .singleMessage(message.id)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,6 +133,7 @@ import NotificationPeerExceptionController
|
|||||||
import AdsReportScreen
|
import AdsReportScreen
|
||||||
import AdUI
|
import AdUI
|
||||||
import ChatMessagePaymentAlertController
|
import ChatMessagePaymentAlertController
|
||||||
|
import TelegramCallsUI
|
||||||
|
|
||||||
public enum ChatControllerPeekActions {
|
public enum ChatControllerPeekActions {
|
||||||
case standard
|
case standard
|
||||||
@ -1366,6 +1367,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self?.openPeer(peer: EnginePeer(peer), navigation: navigation, fromMessage: nil)
|
self?.openPeer(peer: EnginePeer(peer), navigation: navigation, fromMessage: nil)
|
||||||
}, callPeer: { [weak self] peerId, isVideo in
|
}, callPeer: { [weak self] peerId, isVideo in
|
||||||
self?.controllerInteraction?.callPeer(peerId, isVideo)
|
self?.controllerInteraction?.callPeer(peerId, isVideo)
|
||||||
|
}, openConferenceCall: { [weak self] message in
|
||||||
|
self?.controllerInteraction?.openConferenceCall(message)
|
||||||
}, enqueueMessage: { [weak self] message in
|
}, enqueueMessage: { [weak self] message in
|
||||||
self?.sendMessages([message])
|
self?.sendMessages([message])
|
||||||
}, sendSticker: canSendMessagesToChat(self.presentationInterfaceState) ? { [weak self] fileReference, sourceNode, sourceRect in
|
}, sendSticker: canSendMessagesToChat(self.presentationInterfaceState) ? { [weak self] fileReference, sourceNode, sourceRect in
|
||||||
@ -2861,6 +2864,50 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}, openConferenceCall: { [weak self] message in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var action: TelegramMediaAction?
|
||||||
|
for media in message.media {
|
||||||
|
if let media = media as? TelegramMediaAction {
|
||||||
|
action = media
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
guard case let .conferenceCall(callId, duration, _) = action?.action else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if duration != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let currentGroupCallController = self.context.sharedContext as? VoiceChatController, case let .group(groupCall) = currentGroupCallController.call, let currentCallId = groupCall.callId, currentCallId == 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),
|
||||||
|
mode: .joining
|
||||||
|
)
|
||||||
|
})
|
||||||
}, longTap: { [weak self] action, params in
|
}, longTap: { [weak self] action, params in
|
||||||
if let self {
|
if let self {
|
||||||
self.openLinkLongTap(action, params: params)
|
self.openLinkLongTap(action, params: params)
|
||||||
|
@ -20,6 +20,7 @@ import TelegramUniversalVideoContent
|
|||||||
import GradientBackground
|
import GradientBackground
|
||||||
import Svg
|
import Svg
|
||||||
import UniversalMediaPlayer
|
import UniversalMediaPlayer
|
||||||
|
import RangeSet
|
||||||
|
|
||||||
public func fetchCachedResourceRepresentation(account: Account, resource: MediaResource, representation: CachedMediaResourceRepresentation) -> Signal<CachedMediaResourceRepresentationResult, NoError> {
|
public func fetchCachedResourceRepresentation(account: Account, resource: MediaResource, representation: CachedMediaResourceRepresentation) -> Signal<CachedMediaResourceRepresentationResult, NoError> {
|
||||||
if let representation = representation as? CachedStickerAJpegRepresentation {
|
if let representation = representation as? CachedStickerAJpegRepresentation {
|
||||||
@ -66,19 +67,49 @@ public func fetchCachedResourceRepresentation(account: Account, resource: MediaR
|
|||||||
return EmptyDisposable
|
return EmptyDisposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*return account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false))
|
} else if let _ = representation as? CachedVideoPrefixFirstFrameRepresentation {
|
||||||
|> mapToSignal { data -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|
return Signal { subscriber in
|
||||||
if data.complete {
|
if let size = resource.size {
|
||||||
return fetchCachedVideoFirstFrameRepresentation(account: account, resource: resource, resourceData: data)
|
let videoSource = UniversalSoftwareVideoSource(mediaBox: account.postbox.mediaBox, source: .direct(resource: resource, size: size), automaticallyFetchHeader: false, hintVP9: false)
|
||||||
|> `catch` { _ -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|
let disposable = videoSource.takeFrame(at: 0.0).start(next: { value in
|
||||||
return .complete()
|
switch value {
|
||||||
|
case let .image(image):
|
||||||
|
if let image {
|
||||||
|
if let imageData = image.jpegData(compressionQuality: 0.6) {
|
||||||
|
subscriber.putNext(.data(imageData))
|
||||||
|
subscriber.putNext(.done)
|
||||||
|
subscriber.putCompletion()
|
||||||
}
|
}
|
||||||
} else if let size = resource.size {
|
|
||||||
return videoFirstFrameData(account: account, resource: resource, chunkSize: min(size, 192 * 1024))
|
|
||||||
} else {
|
|
||||||
return .complete()
|
|
||||||
}
|
}
|
||||||
|
case .waitingForData:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
subscriber.keepAlive(videoSource)
|
||||||
|
|
||||||
|
/*let reader = FFMpegFileReader(source: .resource(mediaBox: account.postbox.mediaBox, resource: resource, resourceSize: size, mappedRanges: [0 ..< size]), useHardwareAcceleration: false, selectedStream: .mediaType(.video), seek: nil, maxReadablePts: nil)
|
||||||
|
reader?.readFrame()
|
||||||
|
|
||||||
|
print("ready to fetch \(representation.prefixLength)")*/
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*let disposable = (account.postbox.mediaBox.resourceRangesStatus(resource)
|
||||||
|
|> filter { ranges in
|
||||||
|
return ranges.isSuperset(of: RangeSet(0 ..< Int64(representation.prefixLength)))
|
||||||
|
}
|
||||||
|
|> take(1)).start(next: { _ in
|
||||||
|
})
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
disposable.dispose()
|
||||||
}*/
|
}*/
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
} else if let representation = representation as? CachedScaledVideoFirstFrameRepresentation {
|
} else if let representation = representation as? CachedScaledVideoFirstFrameRepresentation {
|
||||||
return account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false))
|
return account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false))
|
||||||
|> mapToSignal { data -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|
|> mapToSignal { data -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|
||||||
|
@ -395,7 +395,12 @@ func openResolvedUrlImpl(
|
|||||||
let _ = (signal
|
let _ = (signal
|
||||||
|> deliverOnMainQueue).startStandalone(next: { [weak navigationController] resolvedCallLink in
|
|> deliverOnMainQueue).startStandalone(next: { [weak navigationController] resolvedCallLink in
|
||||||
navigationController?.pushViewController(context.sharedContext.makeJoinSubjectScreen(context: context, mode: JoinSubjectScreenMode.groupCall(JoinSubjectScreenMode.GroupCall(
|
navigationController?.pushViewController(context.sharedContext.makeJoinSubjectScreen(context: context, mode: JoinSubjectScreenMode.groupCall(JoinSubjectScreenMode.GroupCall(
|
||||||
inviter: resolvedCallLink.inviter, members: resolvedCallLink.members, totalMemberCount: resolvedCallLink.totalMemberCount
|
id: resolvedCallLink.id,
|
||||||
|
accessHash: resolvedCallLink.accessHash,
|
||||||
|
slug: link,
|
||||||
|
inviter: resolvedCallLink.inviter,
|
||||||
|
members: resolvedCallLink.members,
|
||||||
|
totalMemberCount: resolvedCallLink.totalMemberCount
|
||||||
))))
|
))))
|
||||||
})
|
})
|
||||||
case let .localization(identifier):
|
case let .localization(identifier):
|
||||||
|
@ -998,6 +998,22 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
convertedUrl = "https://t.me/c/\(channel)?boost"
|
convertedUrl = "https://t.me/c/\(channel)?boost"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if parsedUrl.host == "call" {
|
||||||
|
if let components = URLComponents(string: "/?" + query) {
|
||||||
|
var slug: String?
|
||||||
|
if let queryItems = components.queryItems {
|
||||||
|
for queryItem in queryItems {
|
||||||
|
if let value = queryItem.value {
|
||||||
|
if queryItem.name == "slug" {
|
||||||
|
slug = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let slug = slug {
|
||||||
|
convertedUrl = "https://t.me/call/\(slug)"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if parsedUrl.host == "importStickers" {
|
if parsedUrl.host == "importStickers" {
|
||||||
|
@ -117,6 +117,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
return nil
|
return nil
|
||||||
}, presentGlobalOverlayController: { _, _ in
|
}, presentGlobalOverlayController: { _, _ in
|
||||||
}, callPeer: { _, _ in
|
}, callPeer: { _, _ in
|
||||||
|
}, openConferenceCall: { _ in
|
||||||
}, longTap: { _, _ in
|
}, longTap: { _, _ in
|
||||||
}, openCheckoutOrReceipt: { _, _ in
|
}, openCheckoutOrReceipt: { _, _ in
|
||||||
}, openSearch: {
|
}, openSearch: {
|
||||||
@ -317,7 +318,8 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
if let location = strongSelf.playlistLocation as? PeerMessagesPlaylistLocation, case let .custom(messages, _, loadMore) = location {
|
if let location = strongSelf.playlistLocation as? PeerMessagesPlaylistLocation, case let .custom(messages, _, loadMore) = location {
|
||||||
playlistLocation = .custom(messages: messages, at: id, loadMore: loadMore)
|
playlistLocation = .custom(messages: messages, at: id, loadMore: loadMore)
|
||||||
}
|
}
|
||||||
return strongSelf.context.sharedContext.openChatMessage(OpenChatMessageParams(context: strongSelf.context, chatLocation: nil, chatFilterTag: nil, chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: nil, dismissInput: { }, present: { _, _, _ in }, transitionNode: { _, _, _ in return nil }, addToTransitionSurface: { _ in }, openUrl: { _ in }, openPeer: { _, _ in }, callPeer: { _, _ in }, enqueueMessage: { _ in }, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: playlistLocation))
|
return strongSelf.context.sharedContext.openChatMessage(OpenChatMessageParams(context: strongSelf.context, chatLocation: nil, chatFilterTag: nil, chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: nil, dismissInput: { }, present: { _, _, _ in }, transitionNode: { _, _, _ in return nil }, addToTransitionSurface: { _ in }, openUrl: { _ in }, openPeer: { _, _ in }, callPeer: { _, _ in }, openConferenceCall: { _ in
|
||||||
|
}, enqueueMessage: { _ in }, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: playlistLocation))
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -2113,7 +2113,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
return nil
|
return nil
|
||||||
}, chatControllerNode: {
|
}, chatControllerNode: {
|
||||||
return nil
|
return nil
|
||||||
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _, _ in }, openSearch: { }, setupReply: { _ in
|
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, openConferenceCall: { _ in
|
||||||
|
}, longTap: { _, _ in }, openCheckoutOrReceipt: { _, _ in }, openSearch: { }, setupReply: { _ in
|
||||||
}, canSetupReply: { _ in
|
}, canSetupReply: { _ in
|
||||||
return .none
|
return .none
|
||||||
}, canSendMessages: {
|
}, canSendMessages: {
|
||||||
|
@ -216,6 +216,11 @@ final class OngoingGroupCallBroadcastPartTaskImpl: NSObject, OngoingGroupCallBro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public protocol OngoingGroupCallEncryptionContext: AnyObject {
|
||||||
|
func encrypt(message: Data) -> Data?
|
||||||
|
func decrypt(message: Data) -> Data?
|
||||||
|
}
|
||||||
|
|
||||||
public final class OngoingGroupCallContext {
|
public final class OngoingGroupCallContext {
|
||||||
public struct AudioStreamData {
|
public struct AudioStreamData {
|
||||||
public var engine: TelegramEngine
|
public var engine: TelegramEngine
|
||||||
@ -495,11 +500,11 @@ public final class OngoingGroupCallContext {
|
|||||||
preferX264: Bool,
|
preferX264: Bool,
|
||||||
logPath: String,
|
logPath: String,
|
||||||
onMutedSpeechActivityDetected: @escaping (Bool) -> Void,
|
onMutedSpeechActivityDetected: @escaping (Bool) -> Void,
|
||||||
encryptionKey: Data?,
|
|
||||||
isConference: Bool,
|
isConference: Bool,
|
||||||
audioIsActiveByDefault: Bool,
|
audioIsActiveByDefault: Bool,
|
||||||
isStream: Bool,
|
isStream: Bool,
|
||||||
sharedAudioDevice: OngoingCallContext.AudioDevice?
|
sharedAudioDevice: OngoingCallContext.AudioDevice?,
|
||||||
|
encryptionContext: OngoingGroupCallEncryptionContext?
|
||||||
) {
|
) {
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
|
|
||||||
@ -632,9 +637,17 @@ public final class OngoingGroupCallContext {
|
|||||||
onMutedSpeechActivityDetected(value)
|
onMutedSpeechActivityDetected(value)
|
||||||
},
|
},
|
||||||
audioDevice: audioDevice?.impl,
|
audioDevice: audioDevice?.impl,
|
||||||
encryptionKey: encryptionKey,
|
|
||||||
isConference: isConference,
|
isConference: isConference,
|
||||||
isActiveByDefault: audioIsActiveByDefault
|
isActiveByDefault: audioIsActiveByDefault,
|
||||||
|
encryptDecrypt: encryptionContext.flatMap { encryptionContext in
|
||||||
|
return { data, isEncrypt in
|
||||||
|
if isEncrypt {
|
||||||
|
return encryptionContext.encrypt(message: data)
|
||||||
|
} else {
|
||||||
|
return encryptionContext.decrypt(message: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
#else
|
#else
|
||||||
self.context = GroupCallThreadLocalContext(
|
self.context = GroupCallThreadLocalContext(
|
||||||
@ -733,9 +746,17 @@ public final class OngoingGroupCallContext {
|
|||||||
logPath: logPath,
|
logPath: logPath,
|
||||||
statsLogPath: tempStatsLogPath,
|
statsLogPath: tempStatsLogPath,
|
||||||
audioDevice: nil,
|
audioDevice: nil,
|
||||||
encryptionKey: encryptionKey,
|
|
||||||
isConference: isConference,
|
isConference: isConference,
|
||||||
isActiveByDefault: true
|
isActiveByDefault: true,
|
||||||
|
encryptDecrypt: encryptionContext.flatMap { encryptionContext in
|
||||||
|
return { data, isEncrypt in
|
||||||
|
if isEncrypt {
|
||||||
|
return encryptionContext.encrypt(data)
|
||||||
|
} else {
|
||||||
|
return encryptionContext.decrypt(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1184,10 +1205,10 @@ public final class OngoingGroupCallContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(inputDeviceId: String = "", outputDeviceId: String = "", audioSessionActive: Signal<Bool, NoError>, video: OngoingCallVideoCapturer?, requestMediaChannelDescriptions: @escaping (Set<UInt32>, @escaping ([MediaChannelDescription]) -> Void) -> Disposable, rejoinNeeded: @escaping () -> Void, outgoingAudioBitrateKbit: Int32?, videoContentType: VideoContentType, enableNoiseSuppression: Bool, disableAudioInput: Bool, enableSystemMute: Bool, preferX264: Bool, logPath: String, onMutedSpeechActivityDetected: @escaping (Bool) -> Void, encryptionKey: Data?, isConference: Bool, audioIsActiveByDefault: Bool, isStream: Bool, sharedAudioDevice: OngoingCallContext.AudioDevice?) {
|
public init(inputDeviceId: String = "", outputDeviceId: String = "", audioSessionActive: Signal<Bool, NoError>, video: OngoingCallVideoCapturer?, requestMediaChannelDescriptions: @escaping (Set<UInt32>, @escaping ([MediaChannelDescription]) -> Void) -> Disposable, rejoinNeeded: @escaping () -> Void, outgoingAudioBitrateKbit: Int32?, videoContentType: VideoContentType, enableNoiseSuppression: Bool, disableAudioInput: Bool, enableSystemMute: Bool, preferX264: Bool, logPath: String, onMutedSpeechActivityDetected: @escaping (Bool) -> Void, isConference: Bool, audioIsActiveByDefault: Bool, isStream: Bool, sharedAudioDevice: OngoingCallContext.AudioDevice?, encryptionContext: OngoingGroupCallEncryptionContext?) {
|
||||||
let queue = self.queue
|
let queue = self.queue
|
||||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||||
return Impl(queue: queue, inputDeviceId: inputDeviceId, outputDeviceId: outputDeviceId, audioSessionActive: audioSessionActive, video: video, requestMediaChannelDescriptions: requestMediaChannelDescriptions, rejoinNeeded: rejoinNeeded, outgoingAudioBitrateKbit: outgoingAudioBitrateKbit, videoContentType: videoContentType, enableNoiseSuppression: enableNoiseSuppression, disableAudioInput: disableAudioInput, enableSystemMute: enableSystemMute, preferX264: preferX264, logPath: logPath, onMutedSpeechActivityDetected: onMutedSpeechActivityDetected, encryptionKey: encryptionKey, isConference: isConference, audioIsActiveByDefault: audioIsActiveByDefault, isStream: isStream, sharedAudioDevice: sharedAudioDevice)
|
return Impl(queue: queue, inputDeviceId: inputDeviceId, outputDeviceId: outputDeviceId, audioSessionActive: audioSessionActive, video: video, requestMediaChannelDescriptions: requestMediaChannelDescriptions, rejoinNeeded: rejoinNeeded, outgoingAudioBitrateKbit: outgoingAudioBitrateKbit, videoContentType: videoContentType, enableNoiseSuppression: enableNoiseSuppression, disableAudioInput: disableAudioInput, enableSystemMute: enableSystemMute, preferX264: preferX264, logPath: logPath, onMutedSpeechActivityDetected: onMutedSpeechActivityDetected, isConference: isConference, audioIsActiveByDefault: audioIsActiveByDefault, isStream: isStream, sharedAudioDevice: sharedAudioDevice, encryptionContext: encryptionContext)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +172,7 @@ objc_library(
|
|||||||
"//submodules/ffmpeg:ffmpeg",
|
"//submodules/ffmpeg:ffmpeg",
|
||||||
"//third-party/rnnoise:rnnoise",
|
"//third-party/rnnoise:rnnoise",
|
||||||
"//third-party/libyuv:libyuv",
|
"//third-party/libyuv:libyuv",
|
||||||
|
"//third-party/td:TdBinding",
|
||||||
] + (["//third-party/libx264:libx264"] if enable_x264 else []),
|
] + (["//third-party/libx264:libx264"] if enable_x264 else []),
|
||||||
sdk_frameworks = [
|
sdk_frameworks = [
|
||||||
"Foundation",
|
"Foundation",
|
||||||
|
@ -452,9 +452,9 @@ typedef NS_ENUM(int32_t, OngoingGroupCallRequestedVideoQuality) {
|
|||||||
statsLogPath:(NSString * _Nonnull)statsLogPath
|
statsLogPath:(NSString * _Nonnull)statsLogPath
|
||||||
onMutedSpeechActivityDetected:(void (^ _Nullable)(bool))onMutedSpeechActivityDetected
|
onMutedSpeechActivityDetected:(void (^ _Nullable)(bool))onMutedSpeechActivityDetected
|
||||||
audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice
|
audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice
|
||||||
encryptionKey:(NSData * _Nullable)encryptionKey
|
|
||||||
isConference:(bool)isConference
|
isConference:(bool)isConference
|
||||||
isActiveByDefault:(bool)isActiveByDefault;
|
isActiveByDefault:(bool)isActiveByDefault
|
||||||
|
encryptDecrypt:(NSData * _Nullable (^ _Nullable)(NSData * _Nonnull, bool))encryptDecrypt;
|
||||||
|
|
||||||
- (void)stop:(void (^ _Nullable)())completion;
|
- (void)stop:(void (^ _Nullable)())completion;
|
||||||
|
|
||||||
|
@ -42,6 +42,8 @@
|
|||||||
#import "platform/darwin/TGRTCCVPixelBuffer.h"
|
#import "platform/darwin/TGRTCCVPixelBuffer.h"
|
||||||
#include "rtc_base/logging.h"
|
#include "rtc_base/logging.h"
|
||||||
|
|
||||||
|
#import <TdBinding/TdBinding.h>
|
||||||
|
|
||||||
@implementation OngoingCallConnectionDescription
|
@implementation OngoingCallConnectionDescription
|
||||||
|
|
||||||
- (instancetype _Nonnull)initWithConnectionId:(int64_t)connectionId ip:(NSString * _Nonnull)ip ipv6:(NSString * _Nonnull)ipv6 port:(int32_t)port peerTag:(NSData * _Nonnull)peerTag {
|
- (instancetype _Nonnull)initWithConnectionId:(int64_t)connectionId ip:(NSString * _Nonnull)ip ipv6:(NSString * _Nonnull)ipv6 port:(int32_t)port peerTag:(NSData * _Nonnull)peerTag {
|
||||||
@ -2374,9 +2376,9 @@ private:
|
|||||||
statsLogPath:(NSString * _Nonnull)statsLogPath
|
statsLogPath:(NSString * _Nonnull)statsLogPath
|
||||||
onMutedSpeechActivityDetected:(void (^ _Nullable)(bool))onMutedSpeechActivityDetected
|
onMutedSpeechActivityDetected:(void (^ _Nullable)(bool))onMutedSpeechActivityDetected
|
||||||
audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice
|
audioDevice:(SharedCallAudioDevice * _Nullable)audioDevice
|
||||||
encryptionKey:(NSData * _Nullable)encryptionKey
|
|
||||||
isConference:(bool)isConference
|
isConference:(bool)isConference
|
||||||
isActiveByDefault:(bool)isActiveByDefault {
|
isActiveByDefault:(bool)isActiveByDefault
|
||||||
|
encryptDecrypt:(NSData * _Nullable (^ _Nullable)(NSData * _Nonnull, bool))encryptDecrypt {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self != nil) {
|
if (self != nil) {
|
||||||
_queue = queue;
|
_queue = queue;
|
||||||
@ -2444,16 +2446,17 @@ isActiveByDefault:(bool)isActiveByDefault {
|
|||||||
|
|
||||||
std::string statsLogPathValue(statsLogPath.length == 0 ? "" : statsLogPath.UTF8String);
|
std::string statsLogPathValue(statsLogPath.length == 0 ? "" : statsLogPath.UTF8String);
|
||||||
|
|
||||||
std::optional<tgcalls::EncryptionKey> mappedEncryptionKey;
|
std::function<std::vector<uint8_t>(std::vector<uint8_t> const &, bool)> mappedEncryptDecrypt;
|
||||||
if (encryptionKey) {
|
if (encryptDecrypt) {
|
||||||
auto encryptionKeyValue = std::make_shared<std::array<uint8_t, 256>>();
|
NSData * _Nullable (^encryptDecryptBlock)(NSData * _Nonnull, bool) = [encryptDecrypt copy];
|
||||||
memcpy(encryptionKeyValue->data(), encryptionKey.bytes, encryptionKey.length);
|
mappedEncryptDecrypt = [encryptDecryptBlock](std::vector<uint8_t> const &message, bool isEncrypt) -> std::vector<uint8_t> {
|
||||||
|
NSData *mappedMessage = [[NSData alloc] initWithBytes:message.data() length:message.size()];
|
||||||
#if DEBUG
|
NSData *result = encryptDecryptBlock(mappedMessage, isEncrypt);
|
||||||
NSLog(@"Encryption key: %@", [encryptionKey base64EncodedStringWithOptions:0]);
|
if (!result) {
|
||||||
#endif
|
return std::vector<uint8_t>();
|
||||||
|
}
|
||||||
mappedEncryptionKey = tgcalls::EncryptionKey(encryptionKeyValue, true);
|
return std::vector<uint8_t>((uint8_t *)result.bytes, ((uint8_t *)result.bytes) + result.length);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
__weak GroupCallThreadLocalContext *weakSelf = self;
|
__weak GroupCallThreadLocalContext *weakSelf = self;
|
||||||
@ -2681,7 +2684,7 @@ isActiveByDefault:(bool)isActiveByDefault {
|
|||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
},
|
},
|
||||||
.encryptionKey = mappedEncryptionKey,
|
.e2eEncryptDecrypt = mappedEncryptDecrypt,
|
||||||
.isConference = isConference
|
.isConference = isConference
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
4
third-party/boringssl/BUILD
vendored
4
third-party/boringssl/BUILD
vendored
@ -29,7 +29,9 @@ load(
|
|||||||
|
|
||||||
licenses(["notice"])
|
licenses(["notice"])
|
||||||
|
|
||||||
exports_files(["LICENSE"])
|
exports_files(
|
||||||
|
["LICENSE"] + crypto_headers + ssl_headers,
|
||||||
|
)
|
||||||
|
|
||||||
# By default, the C files will expect assembly files, if any, to be linked in
|
# By default, the C files will expect assembly files, if any, to be linked in
|
||||||
# with the build. This default can be flipped with -DOPENSSL_NO_ASM. If building
|
# with the build. This default can be flipped with -DOPENSSL_NO_ASM. If building
|
||||||
|
150
third-party/td/BUILD
vendored
Normal file
150
third-party/td/BUILD
vendored
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
load(
|
||||||
|
"//third-party/boringssl:BUILD.generated.bzl",
|
||||||
|
"crypto_headers",
|
||||||
|
)
|
||||||
|
|
||||||
|
headers = [
|
||||||
|
"td/e2e/e2e_api.h",
|
||||||
|
"td/e2e/e2e_errors.h",
|
||||||
|
]
|
||||||
|
|
||||||
|
libs = [
|
||||||
|
"tde2e",
|
||||||
|
"tdutils",
|
||||||
|
]
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "td_sources",
|
||||||
|
srcs = glob([
|
||||||
|
"td/**/*"
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
|
||||||
|
genrule(
|
||||||
|
name = "td_build",
|
||||||
|
srcs = [
|
||||||
|
"build-td-bazel.sh",
|
||||||
|
":td_sources",
|
||||||
|
"@cmake_tar_gz//file",
|
||||||
|
"//third-party/boringssl:crypto",
|
||||||
|
] + [
|
||||||
|
"//third-party/boringssl:{}".format(header) for header in crypto_headers
|
||||||
|
],
|
||||||
|
cmd_bash =
|
||||||
|
"""
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
if [ "$(TARGET_CPU)" == "ios_arm64" ]; then
|
||||||
|
BUILD_ARCH="arm64"
|
||||||
|
elif [ "$(TARGET_CPU)" == "ios_sim_arm64" ]; then
|
||||||
|
BUILD_ARCH="sim_arm64"
|
||||||
|
else
|
||||||
|
echo "Unsupported architecture $(TARGET_CPU)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
BUILD_DIR="$(RULEDIR)/build_$${BUILD_ARCH}"
|
||||||
|
rm -rf "$$BUILD_DIR"
|
||||||
|
mkdir -p "$$BUILD_DIR"
|
||||||
|
|
||||||
|
CMAKE_DIR="$$(pwd)/$$BUILD_DIR/cmake"
|
||||||
|
rm -rf "$$CMAKE_DIR"
|
||||||
|
mkdir -p "$$CMAKE_DIR"
|
||||||
|
tar -xzf "$(location @cmake_tar_gz//file)" -C "$$CMAKE_DIR"
|
||||||
|
|
||||||
|
OPENSSL_BASE_DIR="$$(pwd)/$$BUILD_DIR"
|
||||||
|
OPENSSL_DIR="$$OPENSSL_BASE_DIR/openssl"
|
||||||
|
rm -rf "$$OPENSSL_DIR"
|
||||||
|
mkdir -p "$$OPENSSL_DIR"
|
||||||
|
mkdir -p "$$OPENSSL_DIR/lib"
|
||||||
|
mkdir -p "$$OPENSSL_DIR/src/include/openssl"
|
||||||
|
mkdir -p "$$OPENSSL_DIR/src/include/openssl/experimental"
|
||||||
|
|
||||||
|
cp -R "$(location //third-party/boringssl:crypto)" "$$OPENSSL_DIR/lib/libcrypto.a"
|
||||||
|
|
||||||
|
# Copy header files
|
||||||
|
""" +
|
||||||
|
"\n".join([
|
||||||
|
"cp -f \"$(location //third-party/boringssl:{})\" \"$$OPENSSL_DIR/{}\"".format(header, header)
|
||||||
|
for header in crypto_headers
|
||||||
|
]) +
|
||||||
|
"""
|
||||||
|
|
||||||
|
cp $(location :build-td-bazel.sh) "$$BUILD_DIR/"
|
||||||
|
|
||||||
|
SOURCE_PATH="third-party/td/td"
|
||||||
|
|
||||||
|
cp -R "$$SOURCE_PATH" "$$BUILD_DIR/"
|
||||||
|
|
||||||
|
mkdir -p "$$BUILD_DIR/Public/td"
|
||||||
|
|
||||||
|
PATH="$$PATH:$$CMAKE_DIR/cmake-3.23.1-macos-universal/CMake.app/Contents/bin" sh $$BUILD_DIR/build-td-bazel.sh $$BUILD_ARCH "$$BUILD_DIR/td" "$$BUILD_DIR" "$$OPENSSL_DIR"
|
||||||
|
""" +
|
||||||
|
"\n".join([
|
||||||
|
"cp -f \"$$BUILD_DIR/td/tde2e/{}\" \"$(location Public/td/{})\"".format(header, header) for header in headers
|
||||||
|
]) +
|
||||||
|
"\n" +
|
||||||
|
"\n".join([
|
||||||
|
"cp -f \"$$BUILD_DIR/build/tde2e/libtde2e.a\" \"$(location Public/td/lib/libtde2e.a)\"",
|
||||||
|
"cp -f \"$$BUILD_DIR/build/tdutils/libtdutils.a\" \"$(location Public/td/lib/libtdutils.a)\"",
|
||||||
|
]),
|
||||||
|
outs = [
|
||||||
|
"Public/td/" + x for x in headers
|
||||||
|
] +
|
||||||
|
[
|
||||||
|
"Public/td/lib/lib{}.a".format(x) for x in libs
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "td_lib",
|
||||||
|
srcs = [":Public/td/lib/lib" + x + ".a" for x in libs],
|
||||||
|
)
|
||||||
|
|
||||||
|
objc_library(
|
||||||
|
name = "td",
|
||||||
|
module_name = "td",
|
||||||
|
enable_modules = True,
|
||||||
|
hdrs = [":Public/td/" + x for x in headers],
|
||||||
|
includes = [
|
||||||
|
"Public",
|
||||||
|
"Public/td",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
":td_lib",
|
||||||
|
"//third-party/boringssl:crypto",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
objc_library(
|
||||||
|
name = "TdBinding",
|
||||||
|
module_name = "TdBinding",
|
||||||
|
enable_modules = True,
|
||||||
|
srcs = glob([
|
||||||
|
"TdBinding/Sources/**/*.m",
|
||||||
|
"TdBinding/Sources/**/*.mm",
|
||||||
|
"TdBinding/Sources/**/*.h",
|
||||||
|
]),
|
||||||
|
hdrs = glob([
|
||||||
|
"TdBinding/Public/**/*.h",
|
||||||
|
]),
|
||||||
|
copts = [
|
||||||
|
"-Werror",
|
||||||
|
],
|
||||||
|
includes = [
|
||||||
|
"TdBinding/Public",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
":td",
|
||||||
|
],
|
||||||
|
sdk_frameworks = [
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
]
|
||||||
|
)
|
56
third-party/td/TdBinding/Public/TdBinding/TdBinding.h
vendored
Normal file
56
third-party/td/TdBinding/Public/TdBinding/TdBinding.h
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#ifndef TDBINDING_H
|
||||||
|
#define TDBINDING_H
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface TdKeyPair : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic, readonly) int64_t keyId;
|
||||||
|
@property (nonatomic, strong, readonly) NSData *publicKey;
|
||||||
|
|
||||||
|
- (nullable instancetype)initWithKeyId:(int64_t)keyId publicKey:(NSData *)publicKey;
|
||||||
|
|
||||||
|
+ (nullable instancetype)generate;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface TdCallParticipant : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic, strong, readonly) NSData *publicKey;
|
||||||
|
@property (nonatomic, readonly) int64_t userId;
|
||||||
|
|
||||||
|
- (nullable instancetype)initWithPublicKey:(NSData *)publicKey userId:(int64_t)userId;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface TdCall : NSObject
|
||||||
|
|
||||||
|
+ (nullable instancetype)makeWithKeyPair:(TdKeyPair *)keyPair latestBlock:(NSData *)latestBlock;
|
||||||
|
|
||||||
|
- (NSArray<NSData *> *)takeOutgoingBroadcastBlocks;
|
||||||
|
- (NSData *)emojiState;
|
||||||
|
|
||||||
|
- (void)applyBlock:(NSData *)block;
|
||||||
|
- (void)applyBroadcastBlock:(NSData *)block;
|
||||||
|
|
||||||
|
- (nullable NSData *)encrypt:(NSData *)message;
|
||||||
|
- (nullable NSData *)decrypt:(NSData *)message;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NSData * _Nullable tdGenerateZeroBlock(TdKeyPair *keyPair, int64_t userId);
|
||||||
|
NSData * _Nullable tdGenerateSelfAddBlock(TdKeyPair *keyPair, int64_t userId, NSData *previousBlock);
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
275
third-party/td/TdBinding/Sources/TdBinding.mm
vendored
Normal file
275
third-party/td/TdBinding/Sources/TdBinding.mm
vendored
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
#import <TdBinding/TdBinding.h>
|
||||||
|
|
||||||
|
#include <td/e2e/e2e_api.h>
|
||||||
|
|
||||||
|
static NSString *hexStringFromData(NSData *data) {
|
||||||
|
NSMutableString *string = [[NSMutableString alloc] initWithCapacity:data.length * 2];
|
||||||
|
for (NSUInteger i = 0; i < data.length; i++) {
|
||||||
|
[string appendFormat:@"%02x", ((uint8_t *)data.bytes)[i]];
|
||||||
|
}
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@interface TdKeyPair () {
|
||||||
|
tde2e_api::PrivateKeyId _keyId;
|
||||||
|
NSData *_publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation TdKeyPair
|
||||||
|
|
||||||
|
- (nullable instancetype)initWithKeyId:(int64_t)keyId publicKey:(NSData *)publicKey {
|
||||||
|
self = [super init];
|
||||||
|
if (self != nil) {
|
||||||
|
_keyId = keyId;
|
||||||
|
_publicKey = publicKey;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int64_t)keyId {
|
||||||
|
return _keyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSData *)publicKey {
|
||||||
|
return _publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (nullable instancetype)generate {
|
||||||
|
auto privateKey = tde2e_api::key_generate_private_key();
|
||||||
|
if (!privateKey.is_ok()) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
tde2e_api::PrivateKeyId privateKeyId = privateKey.value();
|
||||||
|
auto publicKey = tde2e_api::key_to_public_key(privateKeyId);
|
||||||
|
if (!publicKey.is_ok()) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSData *parsedPublicKey = [[NSData alloc] initWithBytes:publicKey.value().data() length:publicKey.value().size()];
|
||||||
|
|
||||||
|
return [[TdKeyPair alloc] initWithKeyId:privateKeyId publicKey:parsedPublicKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation TdCallParticipant
|
||||||
|
|
||||||
|
- (nullable instancetype)initWithPublicKey:(NSData *)publicKey userId:(int64_t)userId {
|
||||||
|
self = [super init];
|
||||||
|
if (self != nil) {
|
||||||
|
_publicKey = publicKey;
|
||||||
|
_userId = userId;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface TdCall ()
|
||||||
|
|
||||||
|
@property (nonatomic, strong) TdKeyPair *keyPair;
|
||||||
|
@property (nonatomic) int64_t callId;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation TdCall
|
||||||
|
|
||||||
|
- (instancetype)initWithId:(int64_t)callId keyPair:(TdKeyPair *)keyPair {
|
||||||
|
self = [super init];
|
||||||
|
if (self != nil) {
|
||||||
|
_callId = callId;
|
||||||
|
_keyPair = keyPair;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
tde2e_api::call_destroy(_callId);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (nullable instancetype)makeWithKeyPair:(TdKeyPair *)keyPair latestBlock:(NSData *)latestBlock {
|
||||||
|
std::string mappedLatestBlock((uint8_t *)latestBlock.bytes, ((uint8_t *)latestBlock.bytes) + latestBlock.length);
|
||||||
|
#if DEBUG
|
||||||
|
auto describeResult = tde2e_api::call_describe_block(mappedLatestBlock);
|
||||||
|
if (describeResult.is_ok()) {
|
||||||
|
NSString *utf8String = [[NSString alloc] initWithBytes:describeResult.value().data() length:describeResult.value().size() encoding:NSUTF8StringEncoding];
|
||||||
|
if (utf8String) {
|
||||||
|
NSLog(@"TdCall.makeWithKeyPair block: %@", utf8String);
|
||||||
|
} else {
|
||||||
|
NSString *lossyString = [[NSString alloc] initWithData:[NSData dataWithBytes:describeResult.value().data() length:describeResult.value().size()] encoding:NSASCIIStringEncoding];
|
||||||
|
if (lossyString) {
|
||||||
|
NSLog(@"TdCall.makeWithKeyPair block (lossy conversion): %@", lossyString);
|
||||||
|
} else {
|
||||||
|
NSLog(@"TdCall.makeWithKeyPair block: [binary data, length: %lu]", (unsigned long)describeResult.value().size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NSLog(@"TdCall.makeWithKeyPair describe block failed");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto call = tde2e_api::call_create(keyPair.keyId, mappedLatestBlock);
|
||||||
|
if (!call.is_ok()) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [[TdCall alloc] initWithId:call.value() keyPair: keyPair];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<NSData *> *)takeOutgoingBroadcastBlocks {
|
||||||
|
NSMutableArray<NSData *> *outboundBroadcastBlocks = [[NSMutableArray alloc] init];
|
||||||
|
auto outboundMessages = tde2e_api::call_pull_outbound_messages(_callId);
|
||||||
|
if (!outboundMessages.is_ok()) {
|
||||||
|
return @[];
|
||||||
|
}
|
||||||
|
for (const auto &it : outboundMessages.value()) {
|
||||||
|
#if DEBUG
|
||||||
|
auto describeResult = tde2e_api::call_describe_message(it);
|
||||||
|
if (describeResult.is_ok()) {
|
||||||
|
NSLog(@"TdCall.takeOutgoingBroadcastBlocks call_pull_outbound_messages: block %@", [[NSString alloc] initWithBytes:describeResult.value().data() length:describeResult.value().size() encoding:NSUTF8StringEncoding]);
|
||||||
|
} else {
|
||||||
|
NSLog(@"TdCall.takeOutgoingBroadcastBlocks call_pull_outbound_messages: describe block failed");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NSData *outBlock = [[NSData alloc] initWithBytes:it.data() length:it.size()];
|
||||||
|
|
||||||
|
[outboundBroadcastBlocks addObject:outBlock];
|
||||||
|
}
|
||||||
|
return outboundBroadcastBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSData *)emojiState {
|
||||||
|
auto result = tde2e_api::call_get_verification_state(_callId);
|
||||||
|
if (!result.is_ok()) {
|
||||||
|
return [NSData data];
|
||||||
|
}
|
||||||
|
auto emojiHash = result.value().emoji_hash;
|
||||||
|
if (!emojiHash.has_value()) {
|
||||||
|
return [NSData data];
|
||||||
|
}
|
||||||
|
if (emojiHash.value().empty()) {
|
||||||
|
return [NSData data];
|
||||||
|
}
|
||||||
|
NSData *outEmojiHash = [[NSData alloc] initWithBytes:emojiHash.value().data() length:emojiHash.value().size()];
|
||||||
|
return outEmojiHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)applyBlock:(NSData *)block {
|
||||||
|
std::string mappedBlock((uint8_t *)block.bytes, ((uint8_t *)block.bytes) + block.length);
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
auto describeResult = tde2e_api::call_describe_block(mappedBlock);
|
||||||
|
if (describeResult.is_ok()) {
|
||||||
|
NSLog(@"TdCall.applyBlock block: %@", [[NSString alloc] initWithBytes:describeResult.value().data() length:describeResult.value().size() encoding:NSUTF8StringEncoding]);
|
||||||
|
} else {
|
||||||
|
NSLog(@"TdCall.applyBlock block: describe block failed");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto result = tde2e_api::call_apply_block(_callId, mappedBlock);
|
||||||
|
if (!result.is_ok()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)applyBroadcastBlock:(NSData *)block {
|
||||||
|
std::string mappedBlock((uint8_t *)block.bytes, ((uint8_t *)block.bytes) + block.length);
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
auto describeResult = tde2e_api::call_describe_message(mappedBlock);
|
||||||
|
if (describeResult.is_ok()) {
|
||||||
|
NSLog(@"TdCall.applyBroadcastBlock block: %@", [[NSString alloc] initWithBytes:describeResult.value().data() length:describeResult.value().size() encoding:NSUTF8StringEncoding]);
|
||||||
|
} else {
|
||||||
|
NSLog(@"TdCall.applyBroadcastBlock block: describe block failed");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto result = tde2e_api::call_receive_inbound_message(_callId, mappedBlock);
|
||||||
|
if (!result.is_ok()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable NSData *)encrypt:(NSData *)message {
|
||||||
|
std::string mappedMessage((uint8_t *)message.bytes, ((uint8_t *)message.bytes) + message.length);
|
||||||
|
auto result = tde2e_api::call_encrypt(_callId, mappedMessage);
|
||||||
|
if (!result.is_ok()) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return [[NSData alloc] initWithBytes:result.value().data() length:result.value().size()];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable NSData *)decrypt:(NSData *)message {
|
||||||
|
std::string mappedMessage((uint8_t *)message.bytes, ((uint8_t *)message.bytes) + message.length);
|
||||||
|
auto result = tde2e_api::call_decrypt(_callId, mappedMessage);
|
||||||
|
if (!result.is_ok()) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return [[NSData alloc] initWithBytes:result.value().data() length:result.value().size()];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NSData * _Nullable tdGenerateZeroBlock(TdKeyPair *keyPair, int64_t userId) {
|
||||||
|
if (!keyPair) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string mappedPublicKey((uint8_t *)keyPair.publicKey.bytes, ((uint8_t *)keyPair.publicKey.bytes) + keyPair.publicKey.length);
|
||||||
|
|
||||||
|
auto publicKeyId = tde2e_api::key_from_public_key(mappedPublicKey);
|
||||||
|
if (!publicKeyId.is_ok()) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
tde2e_api::CallParticipant initialParticipant;
|
||||||
|
initialParticipant.user_id = userId;
|
||||||
|
initialParticipant.public_key_id = publicKeyId.value();
|
||||||
|
initialParticipant.permissions = (1 << 0) | (1 << 1);
|
||||||
|
|
||||||
|
tde2e_api::CallState initialCallState;
|
||||||
|
initialCallState.participants.push_back(std::move(initialParticipant));
|
||||||
|
auto zeroBlock = tde2e_api::call_create_zero_block(keyPair.keyId, initialCallState);
|
||||||
|
if (!zeroBlock.is_ok()) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSData *zeroBlockData = [[NSData alloc] initWithBytes:zeroBlock.value().data() length:zeroBlock.value().size()];
|
||||||
|
#if DEBUG
|
||||||
|
NSLog(@"Zero block: %@", hexStringFromData(zeroBlockData));
|
||||||
|
#endif
|
||||||
|
return zeroBlockData;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSData * _Nullable tdGenerateSelfAddBlock(TdKeyPair *keyPair, int64_t userId, NSData *previousBlock) {
|
||||||
|
if (!keyPair) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string mappedPublicKey((uint8_t *)keyPair.publicKey.bytes, ((uint8_t *)keyPair.publicKey.bytes) + keyPair.publicKey.length);
|
||||||
|
std::string mappedPreviousBlock((uint8_t *)previousBlock.bytes, ((uint8_t *)previousBlock.bytes) + previousBlock.length);
|
||||||
|
|
||||||
|
auto publicKeyId = tde2e_api::key_from_public_key(mappedPublicKey);
|
||||||
|
if (!publicKeyId.is_ok()) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
tde2e_api::CallParticipant myParticipant;
|
||||||
|
myParticipant.user_id = userId;
|
||||||
|
myParticipant.public_key_id = publicKeyId.value();
|
||||||
|
myParticipant.permissions = (1 << 0) | (1 << 1);
|
||||||
|
|
||||||
|
auto result = tde2e_api::call_create_self_add_block(keyPair.keyId, mappedPreviousBlock, myParticipant);
|
||||||
|
if (!result.is_ok()) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSData *resultBlock = [[NSData alloc] initWithBytes:result.value().data() length:result.value().size()];
|
||||||
|
#if DEBUG
|
||||||
|
NSLog(@"Self add block: %@", hexStringFromData(resultBlock));
|
||||||
|
#endif
|
||||||
|
return resultBlock;
|
||||||
|
}
|
52
third-party/td/build-td-bazel.sh
vendored
Executable file
52
third-party/td/build-td-bazel.sh
vendored
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
ARCH="$1"
|
||||||
|
|
||||||
|
SOURCE_DIR="$2"
|
||||||
|
BUILD_DIR=$(echo "$(cd "$(dirname "$3")"; pwd -P)/$(basename "$3")")
|
||||||
|
OPENSSL_DIR="$4"
|
||||||
|
|
||||||
|
openssl_crypto_library="${OPENSSL_DIR}/lib/libcrypto.a"
|
||||||
|
options=""
|
||||||
|
options="$options -DOPENSSL_FOUND=1"
|
||||||
|
options="$options -DOPENSSL_CRYPTO_LIBRARY=${openssl_crypto_library}"
|
||||||
|
options="$options -DOPENSSL_INCLUDE_DIR=${OPENSSL_DIR}/src/include"
|
||||||
|
#options="$options -DOPENSSL_LIBRARIES=${openssl_crypto_library}"
|
||||||
|
options="$options -DCMAKE_BUILD_TYPE=Release"
|
||||||
|
|
||||||
|
cd "$BUILD_DIR"
|
||||||
|
|
||||||
|
# Generate source files
|
||||||
|
mkdir native-build
|
||||||
|
cd native-build
|
||||||
|
cmake -DTD_GENERATE_SOURCE_FILES=ON ../td
|
||||||
|
cmake --build . -- -j$(sysctl -n hw.ncpu)
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
if [ "$ARCH" = "arm64" ]; then
|
||||||
|
IOS_PLATFORMDIR="$(xcode-select -p)/Platforms/iPhoneOS.platform"
|
||||||
|
IOS_SYSROOT=($IOS_PLATFORMDIR/Developer/SDKs/iPhoneOS*.sdk)
|
||||||
|
export CFLAGS="-arch arm64 -miphoneos-version-min=12.0"
|
||||||
|
elif [ "$ARCH" = "sim_arm64" ]; then
|
||||||
|
IOS_PLATFORMDIR="$(xcode-select -p)/Platforms/iPhoneSimulator.platform"
|
||||||
|
IOS_SYSROOT=($IOS_PLATFORMDIR/Developer/SDKs/iPhoneSimulator*.sdk)
|
||||||
|
export CFLAGS="-arch arm64 --target=arm64-apple-ios12.0-simulator -miphonesimulator-version-min=12.0"
|
||||||
|
else
|
||||||
|
echo "Unsupported architecture $ARCH"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Common build steps
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
|
||||||
|
touch toolchain.cmake
|
||||||
|
echo "set(CMAKE_SYSTEM_NAME Darwin)" >> toolchain.cmake
|
||||||
|
echo "set(CMAKE_SYSTEM_PROCESSOR aarch64)" >> toolchain.cmake
|
||||||
|
echo "set(CMAKE_C_COMPILER $(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang)" >> toolchain.cmake
|
||||||
|
|
||||||
|
cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -DCMAKE_OSX_SYSROOT=${IOS_SYSROOT[0]} ../td $options
|
||||||
|
make tde2e -j$(sysctl -n hw.ncpu)
|
194
third-party/td/td/.clang-format
vendored
Normal file
194
third-party/td/td/.clang-format
vendored
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
# BasedOnStyle: Google
|
||||||
|
AccessModifierOffset: -1
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignArrayOfStructures: None
|
||||||
|
AlignConsecutiveAssignments:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
PadOperators: true
|
||||||
|
AlignConsecutiveBitFields:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
PadOperators: false
|
||||||
|
AlignConsecutiveDeclarations:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
PadOperators: false
|
||||||
|
AlignConsecutiveMacros:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
PadOperators: false
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: Align
|
||||||
|
AlignTrailingComments:
|
||||||
|
Kind: Always
|
||||||
|
OverEmptyLines: 0
|
||||||
|
AllowAllArgumentsOnNextLine: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
AllowShortBlocksOnASingleLine: Never
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortEnumsOnASingleLine: true
|
||||||
|
AllowShortFunctionsOnASingleLine: None # All
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never # WithoutElse
|
||||||
|
AllowShortLambdasOnASingleLine: Inline # All
|
||||||
|
AllowShortLoopsOnASingleLine: false # true
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: true
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
# AttributeMacros:
|
||||||
|
# - __capability
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
BitFieldColonSpacing: Both
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: Never
|
||||||
|
AfterEnum: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
BeforeLambdaBody: false
|
||||||
|
BeforeWhile: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakAfterAttributes: Never
|
||||||
|
# BreakAfterJavaFieldAnnotations: false
|
||||||
|
BreakArrays: true
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
BreakBeforeConceptDeclarations: Always
|
||||||
|
BreakBeforeInlineASMColon: OnlyMultiline
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializers: BeforeComma # BeforeColon
|
||||||
|
BreakInheritanceList: BeforeComma # BeforeColon
|
||||||
|
BreakStringLiterals: true
|
||||||
|
ColumnLimit: 120 # 80
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DerivePointerAlignment: true
|
||||||
|
DisableFormat: false
|
||||||
|
EmptyLineAfterAccessModifier: Never
|
||||||
|
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
FixNamespaceComments: true
|
||||||
|
ForEachMacros:
|
||||||
|
- Q_FOREACH_THIS_LIST_MUST_BE_NON_EMPTY
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 0
|
||||||
|
IndentAccessModifiers: false
|
||||||
|
IndentCaseBlocks: false
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentExternBlock: AfterExternBlock
|
||||||
|
IndentGotoLabels: true
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentRequiresClause: true
|
||||||
|
IndentWidth: 2
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
InsertBraces: false
|
||||||
|
InsertNewlineAtEOF: false
|
||||||
|
# InsertTrailingCommas: None
|
||||||
|
IntegerLiteralSeparator:
|
||||||
|
Binary: 0
|
||||||
|
Decimal: 0
|
||||||
|
Hex: 0
|
||||||
|
# JavaScriptQuotes: Leave
|
||||||
|
# JavaScriptWrapImports: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
LambdaBodyIndentation: Signature
|
||||||
|
LineEnding: DeriveLF
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
# ObjCBinPackProtocolList: Never
|
||||||
|
# ObjCBlockIndentWidth: 2
|
||||||
|
# ObjCBreakBeforeNestedBlockParam: true
|
||||||
|
# ObjCSpaceAfterProperty: false
|
||||||
|
# ObjCSpaceBeforeProtocolList: true
|
||||||
|
PackConstructorInitializers: NextLine
|
||||||
|
PenaltyBreakAssignment: 2
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 1
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakOpenParenthesis: 0
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyBreakTemplateDeclaration: 10
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyIndentedWhitespace: 0
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 200
|
||||||
|
PointerAlignment: Right # Left
|
||||||
|
PPIndentWidth: -1
|
||||||
|
QualifierAlignment: Leave
|
||||||
|
ReferenceAlignment: Pointer
|
||||||
|
ReflowComments: false # true
|
||||||
|
RemoveBracesLLVM: false
|
||||||
|
RemoveSemicolon: false
|
||||||
|
RequiresClausePosition: OwnLine
|
||||||
|
RequiresExpressionIndentation: OuterScope
|
||||||
|
SeparateDefinitionBlocks: Leave
|
||||||
|
ShortNamespaceLines: 0 # 1
|
||||||
|
SortIncludes: CaseInsensitive # CaseSensitive
|
||||||
|
# SortJavaStaticImport: Before
|
||||||
|
SortUsingDeclarations: Lexicographic # LexicographicNumeric
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: true
|
||||||
|
SpaceAroundPointerQualifiers: Default
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCaseColon: false
|
||||||
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeParensOptions:
|
||||||
|
AfterControlStatements: true
|
||||||
|
AfterForeachMacros: true
|
||||||
|
AfterFunctionDefinitionName: false
|
||||||
|
AfterFunctionDeclarationName: false
|
||||||
|
AfterIfMacros: true
|
||||||
|
AfterOverloadedOperator: false
|
||||||
|
AfterRequiresInClause: false
|
||||||
|
AfterRequiresInExpression: false
|
||||||
|
BeforeNonEmptyParentheses: false
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceBeforeSquareBrackets: false
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 2
|
||||||
|
SpacesInAngles: Never
|
||||||
|
SpacesInConditionalStatement: false
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInLineCommentPrefix:
|
||||||
|
Minimum: 1
|
||||||
|
Maximum: 1 # -1
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
Standard: Auto
|
||||||
|
TabWidth: 100 # 8
|
||||||
|
UseTab: Never
|
||||||
|
...
|
39
third-party/td/td/.gitattributes
vendored
Normal file
39
third-party/td/td/.gitattributes
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
* text=auto
|
||||||
|
|
||||||
|
*.cpp text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.hpp text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.h text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.c text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.tl text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.mm text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.txt text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.sh text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent eol=lf
|
||||||
|
*.php text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.ps1 text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent eol=crlf
|
||||||
|
*.cmake text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.md text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.in text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.html text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
|
||||||
|
*.java text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.py text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.js text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.patch text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.swift text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.pbxproj text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.cs text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.xaml text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.appxmanifest text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.vsixmanifest text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.nuspec text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.targets text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.json text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.csproj text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.sln text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.xml text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
*.config text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||||
|
|
||||||
|
sqlite/sqlite/* linguist-vendored
|
||||||
|
|
||||||
|
*.pfx binary
|
||||||
|
*.png binary
|
8
third-party/td/td/.gitignore
vendored
Normal file
8
third-party/td/td/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
**/*build*/
|
||||||
|
**/.*.swp
|
||||||
|
**/.DS_Store
|
||||||
|
**/auto/
|
||||||
|
docs/
|
||||||
|
/tdlib/
|
||||||
|
vcpkg/
|
||||||
|
.clang-tidy
|
1520
third-party/td/td/CHANGELOG.md
vendored
Normal file
1520
third-party/td/td/CHANGELOG.md
vendored
Normal file
File diff suppressed because it is too large
Load Diff
74
third-party/td/td/CMake/AddCXXCompilerFlag.cmake
vendored
Normal file
74
third-party/td/td/CMake/AddCXXCompilerFlag.cmake
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
# - Adds a compiler flag if it is supported by the compiler
|
||||||
|
#
|
||||||
|
# This function checks that the supplied compiler flag is supported and then
|
||||||
|
# adds it to the corresponding compiler flags
|
||||||
|
#
|
||||||
|
# add_cxx_compiler_flag(<FLAG> [<VARIANT>])
|
||||||
|
#
|
||||||
|
# - Example
|
||||||
|
#
|
||||||
|
# include(AddCXXCompilerFlag)
|
||||||
|
# add_cxx_compiler_flag(-Wall)
|
||||||
|
# add_cxx_compiler_flag(-no-strict-aliasing RELEASE)
|
||||||
|
# Requires CMake 2.6+
|
||||||
|
|
||||||
|
if (__add_cxx_compiler_flag)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
set(__add_cxx_compiler_flag INCLUDED)
|
||||||
|
|
||||||
|
include(CheckCXXCompilerFlag)
|
||||||
|
|
||||||
|
function(mangle_compiler_flag FLAG OUTPUT)
|
||||||
|
string(TOUPPER "HAVE_CXX_FLAG_${FLAG}" SANITIZED_FLAG)
|
||||||
|
string(REPLACE "+" "X" SANITIZED_FLAG ${SANITIZED_FLAG})
|
||||||
|
string(REGEX REPLACE "[^A-Za-z_0-9]" "_" SANITIZED_FLAG ${SANITIZED_FLAG})
|
||||||
|
string(REGEX REPLACE "_+" "_" SANITIZED_FLAG ${SANITIZED_FLAG})
|
||||||
|
set(${OUTPUT} "${SANITIZED_FLAG}" PARENT_SCOPE)
|
||||||
|
endfunction(mangle_compiler_flag)
|
||||||
|
|
||||||
|
function(add_cxx_compiler_flag FLAG)
|
||||||
|
string(REPLACE "-Wno-" "-W" MAIN_FLAG ${FLAG})
|
||||||
|
mangle_compiler_flag("${MAIN_FLAG}" MANGLED_FLAG_NAME)
|
||||||
|
if (DEFINED CMAKE_REQUIRED_FLAGS)
|
||||||
|
set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}")
|
||||||
|
else()
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${FLAG}")
|
||||||
|
endif()
|
||||||
|
check_cxx_compiler_flag("${MAIN_FLAG}" ${MANGLED_FLAG_NAME})
|
||||||
|
if (DEFINED OLD_CMAKE_REQUIRED_FLAGS)
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
|
||||||
|
else()
|
||||||
|
unset(CMAKE_REQUIRED_FLAGS)
|
||||||
|
endif()
|
||||||
|
if (${MANGLED_FLAG_NAME})
|
||||||
|
set(VARIANT ${ARGV1})
|
||||||
|
if (ARGV1)
|
||||||
|
string(TOUPPER "_${VARIANT}" VARIANT)
|
||||||
|
endif()
|
||||||
|
set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(add_required_cxx_compiler_flag FLAG)
|
||||||
|
string(REPLACE "-Wno-" "-W" MAIN_FLAG ${FLAG})
|
||||||
|
mangle_compiler_flag("${MAIN_FLAG}" MANGLED_FLAG_NAME)
|
||||||
|
set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}")
|
||||||
|
check_cxx_compiler_flag("${MAIN_FLAG}" ${MANGLED_FLAG_NAME})
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
|
||||||
|
if (${MANGLED_FLAG_NAME})
|
||||||
|
set(VARIANT ${ARGV1})
|
||||||
|
if (ARGV1)
|
||||||
|
string(TOUPPER "_${VARIANT}" VARIANT)
|
||||||
|
endif()
|
||||||
|
set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE)
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
|
||||||
|
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}" PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Required flag '${FLAG}' is not supported by the compiler")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
62
third-party/td/td/CMake/FindAtomics.cmake
vendored
Normal file
62
third-party/td/td/CMake/FindAtomics.cmake
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Original issue:
|
||||||
|
# * https://gitlab.kitware.com/cmake/cmake/-/issues/23021#note_1098733
|
||||||
|
#
|
||||||
|
# For reference:
|
||||||
|
# * https://gcc.gnu.org/wiki/Atomic/GCCMM
|
||||||
|
#
|
||||||
|
# riscv64 specific:
|
||||||
|
# * https://lists.debian.org/debian-riscv/2022/01/msg00009.html
|
||||||
|
#
|
||||||
|
# ATOMICS_FOUND - system has C++ atomics
|
||||||
|
# ATOMICS_LIBRARIES - libraries needed to use C++ atomics
|
||||||
|
|
||||||
|
if (ATOMICS_FOUND)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(CheckCXXSourceCompiles)
|
||||||
|
|
||||||
|
# RISC-V only has 32-bit and 64-bit atomic instructions. GCC is supposed
|
||||||
|
# to convert smaller atomics to those larger ones via masking and
|
||||||
|
# shifting like LLVM, but it's a known bug that it does not. This means
|
||||||
|
# anything that wants to use atomics on 1-byte or 2-byte types needs
|
||||||
|
# to link atomic library, but not 4-byte or 8-byte (though it does no harm).
|
||||||
|
set(ATOMIC_CODE
|
||||||
|
"
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstdint>
|
||||||
|
std::atomic<std::uint8_t> n8{0}; // riscv64
|
||||||
|
std::atomic<std::uint64_t> n64{0}; // armel, mipsel, powerpc
|
||||||
|
int main() {
|
||||||
|
++n8;
|
||||||
|
++n64;
|
||||||
|
}")
|
||||||
|
|
||||||
|
set(ATOMICS_LIBS " " "-latomic")
|
||||||
|
if (CMAKE_SYSTEM_NAME MATCHES "NetBSD")
|
||||||
|
set(ATOMICS_LIBS "${ATOMICS_LIBS}" /usr/pkg/gcc12/x86_64--netbsd/lib/libatomic.so /usr/pkg/gcc12/i486--netbsdelf/lib/libatomic.so)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
foreach (ATOMICS_LIBRARY ${ATOMICS_LIBS})
|
||||||
|
unset(ATOMICS_FOUND CACHE)
|
||||||
|
set(CMAKE_REQUIRED_LIBRARIES "${ATOMICS_LIBRARY}")
|
||||||
|
check_cxx_source_compiles("${ATOMIC_CODE}" ATOMICS_FOUND)
|
||||||
|
unset(CMAKE_REQUIRED_LIBRARIES)
|
||||||
|
if (ATOMICS_FOUND)
|
||||||
|
if (NOT ATOMICS_LIBRARY STREQUAL " ")
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Atomics DEFAULT_MSG ATOMICS_LIBRARY)
|
||||||
|
set(ATOMICS_LIBRARIES "${ATOMICS_LIBRARY}" CACHE STRING "Atomic library" FORCE)
|
||||||
|
else()
|
||||||
|
set(ATOMICS_LIBRARIES "" CACHE STRING "Atomic operations library" FORCE)
|
||||||
|
endif()
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
if (Atomics_FIND_REQUIRED AND NOT ATOMICS_FOUND)
|
||||||
|
message(FATAL_ERROR "Atomic operations library isn't found.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
unset(ATOMICS_LIBRARY)
|
||||||
|
unset(ATOMICS_LIBS)
|
||||||
|
unset(ATOMIC_CODE)
|
25
third-party/td/td/CMake/FindReadline.cmake
vendored
Normal file
25
third-party/td/td/CMake/FindReadline.cmake
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
if (APPLE)
|
||||||
|
find_path(READLINE_INCLUDE_DIR readline/readline.h /opt/homebrew/opt/readline/include /usr/local/opt/readline/include /opt/local/include /opt/include /usr/local/include /usr/include NO_DEFAULT_PATH)
|
||||||
|
endif()
|
||||||
|
find_path(READLINE_INCLUDE_DIR readline/readline.h)
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
find_library(READLINE_LIBRARY readline /opt/homebrew/opt/readline/lib /usr/local/opt/readline/lib /opt/local/lib /opt/lib /usr/local/lib /usr/lib NO_DEFAULT_PATH)
|
||||||
|
endif()
|
||||||
|
find_library(READLINE_LIBRARY readline)
|
||||||
|
|
||||||
|
if (READLINE_INCLUDE_DIR AND READLINE_LIBRARY AND NOT GNU_READLINE_FOUND)
|
||||||
|
set(CMAKE_REQUIRED_INCLUDES "${READLINE_INCLUDE_DIR}")
|
||||||
|
set(CMAKE_REQUIRED_LIBRARIES "${READLINE_LIBRARY}")
|
||||||
|
include(CheckCXXSourceCompiles)
|
||||||
|
unset(GNU_READLINE_FOUND CACHE)
|
||||||
|
check_cxx_source_compiles("#include <stdio.h>\n#include <readline/readline.h>\nint main() { rl_replace_line(\"\", 0); }" GNU_READLINE_FOUND)
|
||||||
|
if (NOT GNU_READLINE_FOUND)
|
||||||
|
unset(READLINE_INCLUDE_DIR CACHE)
|
||||||
|
unset(READLINE_LIBRARY CACHE)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Readline DEFAULT_MSG READLINE_INCLUDE_DIR READLINE_LIBRARY)
|
||||||
|
mark_as_advanced(READLINE_INCLUDE_DIR READLINE_LIBRARY)
|
99
third-party/td/td/CMake/GeneratePkgConfig.cmake
vendored
Normal file
99
third-party/td/td/CMake/GeneratePkgConfig.cmake
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
function(get_relative_link OUTPUT PATH)
|
||||||
|
if (PATH MATCHES "^[$]<[$]<CONFIG:DEBUG>:")
|
||||||
|
set(${OUTPUT} "" PARENT_SCOPE)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
string(REGEX REPLACE "^[$]<[$]<NOT:[$]<CONFIG:DEBUG>>:(.*)>$" "\\1" PATH "${PATH}")
|
||||||
|
|
||||||
|
get_filename_component(NAME "${PATH}" NAME_WE)
|
||||||
|
if (IS_ABSOLUTE ${PATH})
|
||||||
|
get_filename_component(DIRECTORY_NAME "${PATH}" DIRECTORY)
|
||||||
|
if (WIN32)
|
||||||
|
set(${OUTPUT} "-l\"${DIRECTORY_NAME}/${NAME}\"" PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
get_filename_component(FULL_NAME "${PATH}" NAME)
|
||||||
|
set(${OUTPUT} "-L\"${DIRECTORY_NAME}\" -l:${FULL_NAME}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT WIN32 AND NAME MATCHES "^lib")
|
||||||
|
string(REGEX REPLACE "^lib" "-l" LINK "${NAME}")
|
||||||
|
elseif (NAME MATCHES "^-")
|
||||||
|
set(LINK "${NAME}")
|
||||||
|
else()
|
||||||
|
string(CONCAT LINK "-l" "${NAME}")
|
||||||
|
endif()
|
||||||
|
set(${OUTPUT} "${LINK}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(generate_pkgconfig TARGET DESCRIPTION)
|
||||||
|
# message("Generating pkg-config for ${TARGET}")
|
||||||
|
get_filename_component(PREFIX "${CMAKE_INSTALL_PREFIX}" REALPATH)
|
||||||
|
|
||||||
|
get_target_property(LIST "${TARGET}" LINK_LIBRARIES)
|
||||||
|
set(REQS "")
|
||||||
|
set(LIBS "")
|
||||||
|
foreach (LIB ${LIST})
|
||||||
|
if (TARGET "${LIB}")
|
||||||
|
set(HAS_REQS 1)
|
||||||
|
list(APPEND REQS "${LIB}")
|
||||||
|
else()
|
||||||
|
set(HAS_LIBS 1)
|
||||||
|
get_relative_link(LINK "${LIB}")
|
||||||
|
if (NOT LINK EQUAL "")
|
||||||
|
list(APPEND LIBS "${LINK}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if (HAS_REQS)
|
||||||
|
set(REQUIRES "")
|
||||||
|
foreach (REQ ${REQS})
|
||||||
|
set(REQUIRES "${REQUIRES} ${REQ}")
|
||||||
|
endforeach()
|
||||||
|
set(REQUIRES "Requires.private:${REQUIRES}\n")
|
||||||
|
endif()
|
||||||
|
if (HAS_LIBS)
|
||||||
|
set(LIBRARIES "")
|
||||||
|
list(REVERSE LIBS)
|
||||||
|
list(REMOVE_DUPLICATES LIBS)
|
||||||
|
foreach (LIB ${LIBS})
|
||||||
|
set(LIBRARIES " ${LIB}${LIBRARIES}")
|
||||||
|
endforeach()
|
||||||
|
set(LIBRARIES "Libs.private:${LIBRARIES}\n")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
|
set(PKGCONFIG_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
|
else()
|
||||||
|
set(PKGCONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
|
||||||
|
set(PKGCONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}")
|
||||||
|
else()
|
||||||
|
set(PKGCONFIG_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig")
|
||||||
|
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/${TARGET}.pc" CONTENT
|
||||||
|
"prefix=${PREFIX}
|
||||||
|
|
||||||
|
Name: ${TARGET}
|
||||||
|
Description: ${DESCRIPTION}
|
||||||
|
Version: ${PROJECT_VERSION}
|
||||||
|
|
||||||
|
CFlags: -I\"${PKGCONFIG_INCLUDEDIR}\"
|
||||||
|
Libs: -L\"${PKGCONFIG_LIBDIR}\" -l${TARGET}
|
||||||
|
${REQUIRES}${LIBRARIES}")
|
||||||
|
|
||||||
|
get_target_property(LIBRARY_TYPE "${TARGET}" TYPE)
|
||||||
|
if (LIBRARY_TYPE STREQUAL "STATIC_LIBRARY" OR LIBRARY_TYPE STREQUAL "SHARED_LIBRARY")
|
||||||
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/${TARGET}.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||||
|
elseif (LIBRARY_TYPE STREQUAL "INTERFACE_LIBRARY")
|
||||||
|
# TODO: support interface libraries
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Don't know how to handle ${TARGET} of type ${LIBRARY_TYPE}")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
127
third-party/td/td/CMake/GetGitRevisionDescription.cmake
vendored
Normal file
127
third-party/td/td/CMake/GetGitRevisionDescription.cmake
vendored
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
# - Returns a version string from Git
|
||||||
|
#
|
||||||
|
# These functions force a re-configure on each git commit so that you can
|
||||||
|
# trust the values of the variables in your build system.
|
||||||
|
#
|
||||||
|
# get_git_head_revision(<refspecvar> <hashvar>)
|
||||||
|
#
|
||||||
|
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||||
|
#
|
||||||
|
# Original Author:
|
||||||
|
# 2009-2020 Ryan Pavlik <ryan.pavlik@gmail.com> <abiryan@ryand.net>
|
||||||
|
# http://academic.cleardefinition.com
|
||||||
|
#
|
||||||
|
# Copyright 2009-2013, Iowa State University.
|
||||||
|
# Copyright 2013-2020, Ryan Pavlik
|
||||||
|
# Copyright 2013-2020, Contributors
|
||||||
|
# SPDX-License-Identifier: BSL-1.0
|
||||||
|
# Distributed under the Boost Software License, Version 1.0.
|
||||||
|
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
# http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
if (__get_git_revision_description)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
set(__get_git_revision_description YES)
|
||||||
|
|
||||||
|
# We must run the following at "include" time, not at function call time,
|
||||||
|
# to find the path to this module rather than the path to a calling list file
|
||||||
|
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||||
|
|
||||||
|
# Function _git_find_closest_git_dir finds the next closest .git directory
|
||||||
|
# that is part of any directory in the path defined by _start_dir.
|
||||||
|
# The result is returned in the parent scope variable whose name is passed
|
||||||
|
# as variable _git_dir_var. If no .git directory can be found, the
|
||||||
|
# function returns an empty string via _git_dir_var.
|
||||||
|
#
|
||||||
|
# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and
|
||||||
|
# neither foo nor bar contain a file/directory .git. This will return
|
||||||
|
# C:/bla/.git
|
||||||
|
#
|
||||||
|
function(_git_find_closest_git_dir _start_dir _git_dir_var)
|
||||||
|
set(cur_dir "${_start_dir}")
|
||||||
|
set(git_dir "${_start_dir}/.git")
|
||||||
|
while (NOT EXISTS "${git_dir}")
|
||||||
|
# .git dir not found, search parent directories
|
||||||
|
set(git_previous_parent "${cur_dir}")
|
||||||
|
get_filename_component(cur_dir "${cur_dir}" DIRECTORY)
|
||||||
|
if (cur_dir STREQUAL git_previous_parent)
|
||||||
|
# We have reached the root directory, we are not in git
|
||||||
|
set(${_git_dir_var} "" PARENT_SCOPE)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
set(git_dir "${cur_dir}/.git")
|
||||||
|
endwhile()
|
||||||
|
set(${_git_dir_var} "${git_dir}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(get_git_head_revision _refspecvar _hashvar)
|
||||||
|
_git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR)
|
||||||
|
|
||||||
|
if (NOT GIT_DIR STREQUAL "")
|
||||||
|
file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_CURRENT_SOURCE_DIR}" "${GIT_DIR}")
|
||||||
|
if (_relative_to_source_dir MATCHES "^[.][.]")
|
||||||
|
# We've gone above the CMake root dir.
|
||||||
|
set(GIT_DIR "")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if (GIT_DIR STREQUAL "")
|
||||||
|
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||||
|
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(Git QUIET)
|
||||||
|
|
||||||
|
# Check if the current source dir is a git submodule or a worktree.
|
||||||
|
# In both cases .git is a file instead of a directory.
|
||||||
|
#
|
||||||
|
if ((NOT IS_DIRECTORY ${GIT_DIR}) AND Git_FOUND)
|
||||||
|
# The following git command will return a non empty string that
|
||||||
|
# points to the super project working tree if the current
|
||||||
|
# source dir is inside a git submodule.
|
||||||
|
# Otherwise, the command will return an empty string.
|
||||||
|
#
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" rev-parse --show-superproject-working-tree
|
||||||
|
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
OUTPUT_VARIABLE out
|
||||||
|
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
if (NOT out STREQUAL "")
|
||||||
|
# If out is non-empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule
|
||||||
|
file(READ ${GIT_DIR} submodule)
|
||||||
|
string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE ${submodule})
|
||||||
|
string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
|
||||||
|
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
|
||||||
|
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
|
||||||
|
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||||
|
else()
|
||||||
|
# GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree
|
||||||
|
file(READ ${GIT_DIR} worktree_ref)
|
||||||
|
# The .git directory contains a path to the worktree information directory
|
||||||
|
# inside the parent git repo of the worktree.
|
||||||
|
string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir ${worktree_ref})
|
||||||
|
string(STRIP ${git_worktree_dir} git_worktree_dir)
|
||||||
|
_git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR)
|
||||||
|
set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||||
|
endif()
|
||||||
|
if (NOT EXISTS "${HEAD_SOURCE_FILE}")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
|
||||||
|
if (NOT EXISTS "${GIT_DATA}")
|
||||||
|
file(MAKE_DIRECTORY "${GIT_DATA}")
|
||||||
|
endif()
|
||||||
|
set(HEAD_FILE "${GIT_DATA}/HEAD")
|
||||||
|
configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY)
|
||||||
|
|
||||||
|
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" "${GIT_DATA}/grabRef.cmake" @ONLY)
|
||||||
|
include("${GIT_DATA}/grabRef.cmake")
|
||||||
|
|
||||||
|
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
|
||||||
|
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
43
third-party/td/td/CMake/GetGitRevisionDescription.cmake.in
vendored
Normal file
43
third-party/td/td/CMake/GetGitRevisionDescription.cmake.in
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#
|
||||||
|
# Internal file for GetGitRevisionDescription.cmake
|
||||||
|
#
|
||||||
|
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||||
|
#
|
||||||
|
# Original Author:
|
||||||
|
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||||
|
# http://academic.cleardefinition.com
|
||||||
|
# Iowa State University HCI Graduate Program/VRAC
|
||||||
|
#
|
||||||
|
# Copyright 2009-2012, Iowa State University
|
||||||
|
# Copyright 2011-2015, Contributors
|
||||||
|
# Distributed under the Boost Software License, Version 1.0.
|
||||||
|
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
# http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
# SPDX-License-Identifier: BSL-1.0
|
||||||
|
|
||||||
|
set(HEAD_HASH)
|
||||||
|
|
||||||
|
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
|
||||||
|
|
||||||
|
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
|
||||||
|
if (HEAD_CONTENTS MATCHES "ref")
|
||||||
|
# named branch
|
||||||
|
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
|
||||||
|
if (EXISTS "@GIT_DIR@/${HEAD_REF}")
|
||||||
|
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||||
|
else()
|
||||||
|
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
|
||||||
|
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
|
||||||
|
if (PACKED_REFS MATCHES "([0-9a-z]*) ${HEAD_REF}")
|
||||||
|
set(HEAD_HASH "${CMAKE_MATCH_1}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
# detached HEAD
|
||||||
|
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT HEAD_HASH)
|
||||||
|
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||||
|
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||||
|
endif()
|
14
third-party/td/td/CMake/PreventInSourceBuild.cmake
vendored
Normal file
14
third-party/td/td/CMake/PreventInSourceBuild.cmake
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
function(prevent_in_source_build)
|
||||||
|
get_filename_component(REAL_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" REALPATH)
|
||||||
|
get_filename_component(REAL_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}" REALPATH)
|
||||||
|
|
||||||
|
if (REAL_BINARY_DIR STREQUAL REAL_SOURCE_DIR)
|
||||||
|
message(" Out-of-source build must be used. Remove the files already")
|
||||||
|
message(" created by CMake and rerun CMake from a new directory:")
|
||||||
|
message(" rm -rf CMakeFiles CMakeCache.txt")
|
||||||
|
message(" mkdir build")
|
||||||
|
message(" cd build")
|
||||||
|
message(" cmake ..")
|
||||||
|
message(FATAL_ERROR "In-source build failed.")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
185
third-party/td/td/CMake/TdSetUpCompiler.cmake
vendored
Normal file
185
third-party/td/td/CMake/TdSetUpCompiler.cmake
vendored
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
# Configures C++17 compiler, setting TDLib-specific compilation options.
|
||||||
|
|
||||||
|
function(td_set_up_compiler)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS 1 PARENT_SCOPE)
|
||||||
|
|
||||||
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON PARENT_SCOPE)
|
||||||
|
|
||||||
|
include(illumos)
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
|
set(GCC 1)
|
||||||
|
set(GCC 1 PARENT_SCOPE)
|
||||||
|
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
set(CLANG 1)
|
||||||
|
set(CLANG 1 PARENT_SCOPE)
|
||||||
|
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
||||||
|
set(INTEL 1)
|
||||||
|
set(INTEL 1 PARENT_SCOPE)
|
||||||
|
elseif (NOT MSVC)
|
||||||
|
message(FATAL_ERROR "Compiler isn't supported")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(CheckCXXCompilerFlag)
|
||||||
|
|
||||||
|
if (GCC OR CLANG OR INTEL)
|
||||||
|
if (WIN32 AND INTEL)
|
||||||
|
set(STD17_FLAG /Qstd=c++17)
|
||||||
|
else()
|
||||||
|
set(STD17_FLAG -std=c++17)
|
||||||
|
endif()
|
||||||
|
if (GCC AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0))
|
||||||
|
message(FATAL_ERROR "No C++17 support in the compiler. Please upgrade the compiler to at least GCC 7.0.")
|
||||||
|
endif()
|
||||||
|
if (CLANG AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0))
|
||||||
|
message(FATAL_ERROR "No C++17 support in the compiler. Please upgrade the compiler to at least clang 5.0.")
|
||||||
|
endif()
|
||||||
|
check_cxx_compiler_flag(${STD17_FLAG} HAVE_STD17)
|
||||||
|
elseif (MSVC)
|
||||||
|
set(HAVE_STD17 MSVC_VERSION>=1914) # MSVC 2017 version 15.7
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT HAVE_STD17)
|
||||||
|
message(FATAL_ERROR "No C++17 support in the compiler. Please upgrade the compiler.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
if (CMAKE_CXX_FLAGS_DEBUG MATCHES "/RTC1")
|
||||||
|
string(REPLACE "/RTC1" " " CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
|
||||||
|
endif()
|
||||||
|
add_definitions(-D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17 /utf-8 /GR- /W4 /wd4100 /wd4127 /wd4324 /wd4505 /wd4814 /wd4702 /bigobj")
|
||||||
|
elseif (CLANG OR GCC)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${STD17_FLAG} -fno-omit-frame-pointer -fno-exceptions -fno-rtti")
|
||||||
|
if (APPLE)
|
||||||
|
set(TD_LINKER_FLAGS "-Wl,-dead_strip")
|
||||||
|
if (NOT CMAKE_BUILD_TYPE MATCHES "Deb")
|
||||||
|
set(TD_LINKER_FLAGS "${TD_LINKER_FLAGS},-x,-S")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections")
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL "SunOS")
|
||||||
|
set(TD_LINKER_FLAGS "-Wl,-z,ignore")
|
||||||
|
elseif (EMSCRIPTEN)
|
||||||
|
set(TD_LINKER_FLAGS "-Wl,--gc-sections")
|
||||||
|
elseif (ANDROID)
|
||||||
|
set(TD_LINKER_FLAGS "-Wl,--gc-sections -Wl,--exclude-libs,ALL -Wl,--icf=safe")
|
||||||
|
else()
|
||||||
|
set(TD_LINKER_FLAGS "-Wl,--gc-sections -Wl,--exclude-libs,ALL")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${TD_LINKER_FLAGS}")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${TD_LINKER_FLAGS}")
|
||||||
|
|
||||||
|
if (WIN32 OR CYGWIN)
|
||||||
|
if (GCC)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
elseif (INTEL)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${STD17_FLAG}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
add_definitions(-DNTDDI_VERSION=0x06020000 -DWINVER=0x0602 -D_WIN32_WINNT=0x0602 -DPSAPI_VERSION=1 -DNOMINMAX -DUNICODE -D_UNICODE -DWIN32_LEAN_AND_MEAN)
|
||||||
|
endif()
|
||||||
|
if (CYGWIN)
|
||||||
|
add_definitions(-D_DEFAULT_SOURCE=1 -DFD_SETSIZE=4096)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# _FILE_OFFSET_BITS is broken in Android NDK r15, r15b and r17 and doesn't work prior to Android 7.0
|
||||||
|
add_definitions(-D_FILE_OFFSET_BITS=64)
|
||||||
|
|
||||||
|
# _GNU_SOURCE might not be defined by g++
|
||||||
|
add_definitions(-D_GNU_SOURCE)
|
||||||
|
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL "SunOS")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lsocket -lnsl")
|
||||||
|
if (ILLUMOS)
|
||||||
|
add_definitions(-DTD_ILLUMOS=1)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(AddCXXCompilerFlag)
|
||||||
|
if (NOT MSVC)
|
||||||
|
add_cxx_compiler_flag("-Wall")
|
||||||
|
add_cxx_compiler_flag("-Wextra")
|
||||||
|
add_cxx_compiler_flag("-Wimplicit-fallthrough=2")
|
||||||
|
add_cxx_compiler_flag("-Wpointer-arith")
|
||||||
|
add_cxx_compiler_flag("-Wcast-qual")
|
||||||
|
add_cxx_compiler_flag("-Wsign-compare")
|
||||||
|
add_cxx_compiler_flag("-Wduplicated-branches")
|
||||||
|
add_cxx_compiler_flag("-Wduplicated-cond")
|
||||||
|
add_cxx_compiler_flag("-Walloc-zero")
|
||||||
|
add_cxx_compiler_flag("-Wlogical-op")
|
||||||
|
add_cxx_compiler_flag("-Wno-tautological-compare")
|
||||||
|
add_cxx_compiler_flag("-Wpointer-arith")
|
||||||
|
add_cxx_compiler_flag("-Wvla")
|
||||||
|
add_cxx_compiler_flag("-Wnon-virtual-dtor")
|
||||||
|
add_cxx_compiler_flag("-Wno-unused-parameter")
|
||||||
|
add_cxx_compiler_flag("-Wconversion")
|
||||||
|
add_cxx_compiler_flag("-Wno-sign-conversion")
|
||||||
|
add_cxx_compiler_flag("-Wc++17-compat-pedantic")
|
||||||
|
add_cxx_compiler_flag("-Wdeprecated")
|
||||||
|
add_cxx_compiler_flag("-Wno-unused-command-line-argument")
|
||||||
|
add_cxx_compiler_flag("-Qunused-arguments")
|
||||||
|
add_cxx_compiler_flag("-Wno-unknown-warning-option")
|
||||||
|
add_cxx_compiler_flag("-Wodr")
|
||||||
|
add_cxx_compiler_flag("-flto-odr-type-merging")
|
||||||
|
add_cxx_compiler_flag("-Wno-psabi")
|
||||||
|
add_cxx_compiler_flag("-Wunused-member-function")
|
||||||
|
add_cxx_compiler_flag("-Wunused-private-field")
|
||||||
|
|
||||||
|
# add_cxx_compiler_flag("-Werror")
|
||||||
|
|
||||||
|
# add_cxx_compiler_flag("-Wcast-align")
|
||||||
|
|
||||||
|
#std::int32_t <-> int and off_t <-> std::size_t/std::int64_t
|
||||||
|
# add_cxx_compiler_flag("-Wuseless-cast")
|
||||||
|
|
||||||
|
#external headers like openssl
|
||||||
|
# add_cxx_compiler_flag("-Wzero-as-null-pointer-constant")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (GCC)
|
||||||
|
add_cxx_compiler_flag("-Wno-maybe-uninitialized") # too many false positives
|
||||||
|
endif()
|
||||||
|
if (WIN32 AND GCC AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0))
|
||||||
|
# warns about casts of function pointers returned by GetProcAddress
|
||||||
|
add_cxx_compiler_flag("-Wno-cast-function-type")
|
||||||
|
endif()
|
||||||
|
if (GCC AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0))
|
||||||
|
# warns about a lot of "return std::move", which are not redundant for compilers without fix for DR 1579, i.e. GCC 4.9 or clang 3.8
|
||||||
|
# see http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1579
|
||||||
|
add_cxx_compiler_flag("-Wno-redundant-move")
|
||||||
|
endif()
|
||||||
|
if (GCC AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0))
|
||||||
|
add_cxx_compiler_flag("-Wno-stringop-overflow") # some false positives
|
||||||
|
endif()
|
||||||
|
if (CLANG AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5))
|
||||||
|
# https://stackoverflow.com/questions/26744556/warning-returning-a-captured-reference-from-a-lambda
|
||||||
|
add_cxx_compiler_flag("-Wno-return-stack-address")
|
||||||
|
endif()
|
||||||
|
if (GCC AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.0))
|
||||||
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104030
|
||||||
|
add_cxx_compiler_flag("-Wbidi-chars=none")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (MINGW)
|
||||||
|
add_cxx_compiler_flag("-ftrack-macro-expansion=0")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem /usr/include/c++/v1")
|
||||||
|
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||||
|
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
|
||||||
|
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
|
||||||
|
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
|
||||||
|
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=leak")
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" PARENT_SCOPE)
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}" PARENT_SCOPE)
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}" PARENT_SCOPE)
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}" PARENT_SCOPE)
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
278
third-party/td/td/CMake/iOS.cmake
vendored
Normal file
278
third-party/td/td/CMake/iOS.cmake
vendored
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake
|
||||||
|
# files which are included with CMake 2.8.4
|
||||||
|
# It has been altered for iOS development
|
||||||
|
|
||||||
|
# Options:
|
||||||
|
#
|
||||||
|
# IOS_PLATFORM = OS (default) or SIMULATOR
|
||||||
|
# This decides if SDKS will be selected from the iPhoneOS.platform or iPhoneSimulator.platform folders
|
||||||
|
# OS - the default, used to build for iPhone and iPad physical devices, which have an arm arch.
|
||||||
|
# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch.
|
||||||
|
#
|
||||||
|
# IOS_ARCH = automatic(default) or "arch1;arch2" (e.q. "x86_64;arm64")
|
||||||
|
# By default this value will be automatically chosen based on the IOS_PLATFORM value above.
|
||||||
|
# If set manually, it will override the default and force to build those architectures only.
|
||||||
|
#
|
||||||
|
# CMAKE_IOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder
|
||||||
|
# By default this location is automatically chosen based on the IOS_PLATFORM value above.
|
||||||
|
# If set manually, it will override the default location and force the user of a particular Developer Platform
|
||||||
|
#
|
||||||
|
# CMAKE_IOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder
|
||||||
|
# By default this location is automatically chosen based on the CMAKE_IOS_DEVELOPER_ROOT value.
|
||||||
|
# In this case it will always be the most up-to-date SDK found in the CMAKE_IOS_DEVELOPER_ROOT path.
|
||||||
|
# If set manually, this will force the use of a specific SDK version
|
||||||
|
|
||||||
|
# Macros:
|
||||||
|
#
|
||||||
|
# set_xcode_property (TARGET XCODE_PROPERTY XCODE_VALUE)
|
||||||
|
# A convenience macro for setting xcode specific properties on targets
|
||||||
|
# example: set_xcode_property (myioslib IPHONEOS_DEPLOYMENT_TARGET "3.1")
|
||||||
|
#
|
||||||
|
# find_host_package (PROGRAM ARGS)
|
||||||
|
# A macro used to find executable programs on the host system, not within the iOS environment.
|
||||||
|
# Thanks to the android-cmake project for providing the command
|
||||||
|
|
||||||
|
# Standard settings
|
||||||
|
set (CMAKE_SYSTEM_NAME Darwin)
|
||||||
|
set (CMAKE_SYSTEM_VERSION 1)
|
||||||
|
set (UNIX True)
|
||||||
|
set (APPLE True)
|
||||||
|
set (IOS True)
|
||||||
|
|
||||||
|
# Required as of cmake 2.8.10
|
||||||
|
set (CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for iOS" FORCE)
|
||||||
|
|
||||||
|
# Determine the cmake host system version so we know where to find the iOS SDKs
|
||||||
|
find_program (CMAKE_UNAME uname /bin /usr/bin /usr/local/bin)
|
||||||
|
if (CMAKE_UNAME)
|
||||||
|
execute_process(COMMAND uname -r OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}")
|
||||||
|
endif (CMAKE_UNAME)
|
||||||
|
|
||||||
|
# Force the compilers to gcc for iOS
|
||||||
|
set (CMAKE_C_COMPILER /usr/bin/gcc)
|
||||||
|
set (CMAKE_CXX_COMPILER /usr/bin/g++)
|
||||||
|
set (CMAKE_AR ar CACHE FILEPATH "" FORCE)
|
||||||
|
set (CMAKE_RANLIB ranlib CACHE FILEPATH "" FORCE)
|
||||||
|
set (PKG_CONFIG_EXECUTABLE pkg-config CACHE FILEPATH "" FORCE)
|
||||||
|
|
||||||
|
# Setup iOS platform unless specified manually with IOS_PLATFORM
|
||||||
|
if (NOT DEFINED IOS_PLATFORM)
|
||||||
|
set (IOS_PLATFORM "OS")
|
||||||
|
endif (NOT DEFINED IOS_PLATFORM)
|
||||||
|
set (IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS Platform")
|
||||||
|
|
||||||
|
# Check the platform selection and setup for developer root
|
||||||
|
if (IOS_PLATFORM STREQUAL "OS")
|
||||||
|
set (IOS_PLATFORM_LOCATION "iPhoneOS.platform")
|
||||||
|
set (XCODE_IOS_PLATFORM ios)
|
||||||
|
|
||||||
|
# This causes the installers to properly locate the output libraries
|
||||||
|
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphoneos")
|
||||||
|
|
||||||
|
set (APPLE_IOS True)
|
||||||
|
elseif (IOS_PLATFORM STREQUAL "SIMULATOR")
|
||||||
|
set (SIMULATOR_FLAG true)
|
||||||
|
set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform")
|
||||||
|
set (XCODE_IOS_PLATFORM ios-simulator)
|
||||||
|
|
||||||
|
# This causes the installers to properly locate the output libraries
|
||||||
|
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator")
|
||||||
|
|
||||||
|
set (APPLE_IOS True)
|
||||||
|
elseif (IOS_PLATFORM STREQUAL "WATCHOS")
|
||||||
|
set (IOS_PLATFORM_LOCATION "WatchOS.platform")
|
||||||
|
set (XCODE_IOS_PLATFORM watchos)
|
||||||
|
|
||||||
|
# This causes the installers to properly locate the output libraries
|
||||||
|
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-watchos")
|
||||||
|
|
||||||
|
set (APPLE_WATCH True)
|
||||||
|
elseif (IOS_PLATFORM STREQUAL "WATCHSIMULATOR")
|
||||||
|
set (SIMULATOR_FLAG true)
|
||||||
|
set (IOS_PLATFORM_LOCATION "WatchSimulator.platform")
|
||||||
|
set (XCODE_IOS_PLATFORM watchos-simulator)
|
||||||
|
|
||||||
|
# This causes the installers to properly locate the output libraries
|
||||||
|
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-watchsimulator")
|
||||||
|
|
||||||
|
set (APPLE_WATCH True)
|
||||||
|
elseif (IOS_PLATFORM STREQUAL "TVOS")
|
||||||
|
set (IOS_PLATFORM_LOCATION "AppleTvOS.platform")
|
||||||
|
set (XCODE_IOS_PLATFORM tvos)
|
||||||
|
|
||||||
|
# This causes the installers to properly locate the output libraries
|
||||||
|
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-appletvos")
|
||||||
|
|
||||||
|
set (APPLE_TV True)
|
||||||
|
elseif (IOS_PLATFORM STREQUAL "TVSIMULATOR")
|
||||||
|
set (SIMULATOR_FLAG true)
|
||||||
|
set (IOS_PLATFORM_LOCATION "AppleTvSimulator.platform")
|
||||||
|
set (XCODE_IOS_PLATFORM tvos-simulator)
|
||||||
|
|
||||||
|
# This causes the installers to properly locate the output libraries
|
||||||
|
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-tvsimulator")
|
||||||
|
|
||||||
|
set (APPLE_TV True)
|
||||||
|
elseif (IOS_PLATFORM STREQUAL "VISIONOS")
|
||||||
|
set (IOS_PLATFORM_LOCATION "XROS.platform")
|
||||||
|
set (XCODE_IOS_PLATFORM xros)
|
||||||
|
|
||||||
|
# This causes the installers to properly locate the output libraries
|
||||||
|
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-xros")
|
||||||
|
|
||||||
|
set (APPLE_VISION True)
|
||||||
|
elseif (IOS_PLATFORM STREQUAL "VISIONSIMULATOR")
|
||||||
|
set (SIMULATOR_FLAG true)
|
||||||
|
set (IOS_PLATFORM_LOCATION "XRSimulator.platform")
|
||||||
|
set (XCODE_IOS_PLATFORM xros-simulator)
|
||||||
|
|
||||||
|
# This causes the installers to properly locate the output libraries
|
||||||
|
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-xrsimulator")
|
||||||
|
|
||||||
|
set (APPLE_VISION True)
|
||||||
|
else (IOS_PLATFORM STREQUAL "OS")
|
||||||
|
message (FATAL_ERROR "Unsupported IOS_PLATFORM value selected. Please choose OS, SIMULATOR, or WATCHOS.")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# All iOS/Darwin specific settings - some may be redundant
|
||||||
|
set (CMAKE_SHARED_LIBRARY_PREFIX "lib")
|
||||||
|
set (CMAKE_SHARED_LIBRARY_SUFFIX ".dylib")
|
||||||
|
set (CMAKE_SHARED_MODULE_PREFIX "lib")
|
||||||
|
set (CMAKE_SHARED_MODULE_SUFFIX ".so")
|
||||||
|
set (CMAKE_MODULE_EXISTS 1)
|
||||||
|
set (CMAKE_DL_LIBS "")
|
||||||
|
|
||||||
|
set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ")
|
||||||
|
set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ")
|
||||||
|
set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}")
|
||||||
|
set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}")
|
||||||
|
|
||||||
|
if (IOS_DEPLOYMENT_TARGET)
|
||||||
|
set (XCODE_IOS_PLATFORM_VERSION_FLAGS "-m${XCODE_IOS_PLATFORM}-version-min=${IOS_DEPLOYMENT_TARGET}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set (CMAKE_SHARED_LINKER_FLAGS_INIT "-fapplication-extension")
|
||||||
|
set (CMAKE_C_FLAGS_INIT "${XCODE_IOS_PLATFORM_VERSION_FLAGS}")
|
||||||
|
# Hidden visibility is required for cxx on iOS
|
||||||
|
set (CMAKE_CXX_FLAGS_INIT "${XCODE_IOS_PLATFORM_VERSION_FLAGS} -fvisibility-inlines-hidden")
|
||||||
|
|
||||||
|
set (CMAKE_C_LINK_FLAGS "${XCODE_IOS_PLATFORM_VERSION_FLAGS} -fapplication-extension -Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}")
|
||||||
|
set (CMAKE_CXX_LINK_FLAGS "${XCODE_IOS_PLATFORM_VERSION_FLAGS} -fapplication-extension -Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}")
|
||||||
|
|
||||||
|
set (CMAKE_PLATFORM_HAS_INSTALLNAME 1)
|
||||||
|
set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -headerpad_max_install_names")
|
||||||
|
set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -headerpad_max_install_names")
|
||||||
|
set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,")
|
||||||
|
set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,")
|
||||||
|
set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a")
|
||||||
|
|
||||||
|
# hack: if a new cmake (which uses CMAKE_INSTALL_NAME_TOOL) runs on an old build tree
|
||||||
|
# (where install_name_tool was hardcoded) and where CMAKE_INSTALL_NAME_TOOL isn't in the cache
|
||||||
|
# and still cmake didn't fail in CMakeFindBinUtils.cmake (because it isn't rerun)
|
||||||
|
# hardcode CMAKE_INSTALL_NAME_TOOL here to install_name_tool, so it behaves as it did before, Alex
|
||||||
|
if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL)
|
||||||
|
find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool)
|
||||||
|
endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL)
|
||||||
|
|
||||||
|
# Setup iOS deployment target
|
||||||
|
set (IOS_DEPLOYMENT_TARGET ${IOS_DEPLOYMENT_TARGET} CACHE STRING "Minimum iOS version")
|
||||||
|
|
||||||
|
# Setup iOS developer location unless specified manually with CMAKE_IOS_DEVELOPER_ROOT
|
||||||
|
# Note Xcode 4.3 changed the installation location, choose the most recent one available
|
||||||
|
execute_process(COMMAND /usr/bin/xcode-select -print-path OUTPUT_VARIABLE CMAKE_XCODE_DEVELOPER_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
set (XCODE_POST_43_ROOT "${CMAKE_XCODE_DEVELOPER_DIR}/Platforms/${IOS_PLATFORM_LOCATION}/Developer")
|
||||||
|
set (XCODE_PRE_43_ROOT "/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer")
|
||||||
|
if (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT)
|
||||||
|
if (EXISTS ${XCODE_POST_43_ROOT})
|
||||||
|
set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_POST_43_ROOT})
|
||||||
|
elseif (EXISTS ${XCODE_PRE_43_ROOT})
|
||||||
|
set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_PRE_43_ROOT})
|
||||||
|
endif (EXISTS ${XCODE_POST_43_ROOT})
|
||||||
|
endif (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT)
|
||||||
|
set (CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT} CACHE PATH "Location of iOS Platform")
|
||||||
|
|
||||||
|
# Find and use the most recent iOS sdk unless specified manually with CMAKE_IOS_SDK_ROOT
|
||||||
|
if (NOT DEFINED CMAKE_IOS_SDK_ROOT)
|
||||||
|
file (GLOB _CMAKE_IOS_SDKS "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*")
|
||||||
|
if (_CMAKE_IOS_SDKS)
|
||||||
|
list (SORT _CMAKE_IOS_SDKS)
|
||||||
|
list (REVERSE _CMAKE_IOS_SDKS)
|
||||||
|
list (GET _CMAKE_IOS_SDKS 0 CMAKE_IOS_SDK_ROOT)
|
||||||
|
else (_CMAKE_IOS_SDKS)
|
||||||
|
message (FATAL_ERROR "No iOS SDK's found in default search path ${CMAKE_IOS_DEVELOPER_ROOT}. Manually set CMAKE_IOS_SDK_ROOT or install the iOS SDK.")
|
||||||
|
endif (_CMAKE_IOS_SDKS)
|
||||||
|
message (STATUS "Toolchain using default iOS SDK: ${CMAKE_IOS_SDK_ROOT}")
|
||||||
|
endif (NOT DEFINED CMAKE_IOS_SDK_ROOT)
|
||||||
|
set (CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Location of the selected iOS SDK")
|
||||||
|
|
||||||
|
# Set the sysroot default to the most recent SDK
|
||||||
|
set (CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support")
|
||||||
|
|
||||||
|
# Set the architectures unless specified manually with IOS_ARCH
|
||||||
|
if (NOT DEFINED IOS_ARCH)
|
||||||
|
if (IOS_PLATFORM STREQUAL "OS")
|
||||||
|
set (IOS_ARCH "arm64")
|
||||||
|
elseif (IOS_PLATFORM STREQUAL "SIMULATOR")
|
||||||
|
set (IOS_ARCH "x86_64;arm64")
|
||||||
|
elseif (IOS_PLATFORM STREQUAL "WATCHOS")
|
||||||
|
set (IOS_ARCH "armv7k;arm64_32;arm64")
|
||||||
|
|
||||||
|
# Include C++ Standard Library for Xcode 15 builds.
|
||||||
|
include_directories(SYSTEM "${CMAKE_IOS_SDK_ROOT}/usr/include/c++/v1")
|
||||||
|
elseif (IOS_PLATFORM STREQUAL "WATCHSIMULATOR")
|
||||||
|
set (IOS_ARCH "x86_64;arm64")
|
||||||
|
|
||||||
|
# Include C++ Standard Library for Xcode 15 builds.
|
||||||
|
include_directories(SYSTEM "${CMAKE_IOS_SDK_ROOT}/usr/include/c++/v1")
|
||||||
|
elseif (IOS_PLATFORM STREQUAL "TVOS")
|
||||||
|
set (IOS_ARCH "arm64")
|
||||||
|
elseif (IOS_PLATFORM STREQUAL "TVSIMULATOR")
|
||||||
|
set (IOS_ARCH "x86_64;arm64")
|
||||||
|
elseif (IOS_PLATFORM STREQUAL "VISIONOS")
|
||||||
|
set (IOS_ARCH "arm64")
|
||||||
|
elseif (IOS_PLATFORM STREQUAL "VISIONSIMULATOR")
|
||||||
|
set (IOS_ARCH "x86_64;arm64")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
message (STATUS "The iOS architectures: ${IOS_ARCH}")
|
||||||
|
|
||||||
|
set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE STRING "Build architecture for iOS")
|
||||||
|
|
||||||
|
# Set the find root to the iOS developer roots and to user defined paths
|
||||||
|
set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE STRING "iOS find search path root")
|
||||||
|
|
||||||
|
# default to searching for frameworks first
|
||||||
|
set (CMAKE_FIND_FRAMEWORK FIRST)
|
||||||
|
|
||||||
|
# set up the default search directories for frameworks
|
||||||
|
set (CMAKE_SYSTEM_FRAMEWORK_PATH
|
||||||
|
${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks
|
||||||
|
${CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks
|
||||||
|
${CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks
|
||||||
|
)
|
||||||
|
|
||||||
|
# only search the iOS sdks, not the remainder of the host filesystem
|
||||||
|
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
||||||
|
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||||
|
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||||
|
|
||||||
|
# This little macro lets you set any Xcode specific property
|
||||||
|
macro (set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE)
|
||||||
|
set_property (TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} ${XCODE_VALUE})
|
||||||
|
endmacro (set_xcode_property)
|
||||||
|
|
||||||
|
# This macro lets you find executable programs on the host system
|
||||||
|
macro (find_host_package)
|
||||||
|
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
|
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER)
|
||||||
|
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER)
|
||||||
|
set (IOS FALSE)
|
||||||
|
|
||||||
|
find_package(${ARGN})
|
||||||
|
|
||||||
|
set (IOS TRUE)
|
||||||
|
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
||||||
|
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||||
|
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||||
|
endmacro (find_host_package)
|
10
third-party/td/td/CMake/illumos.cmake
vendored
Normal file
10
third-party/td/td/CMake/illumos.cmake
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL "SunOS")
|
||||||
|
#
|
||||||
|
# Determine if the host is running an illumos distribution:
|
||||||
|
#
|
||||||
|
execute_process(COMMAND /usr/bin/uname -o OUTPUT_VARIABLE UNAME_O OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
|
||||||
|
if (UNAME_O STREQUAL "illumos")
|
||||||
|
set(ILLUMOS 1)
|
||||||
|
endif()
|
||||||
|
endif()
|
1440
third-party/td/td/CMakeLists.txt
vendored
Normal file
1440
third-party/td/td/CMakeLists.txt
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2473
third-party/td/td/Doxyfile
vendored
Normal file
2473
third-party/td/td/Doxyfile
vendored
Normal file
File diff suppressed because it is too large
Load Diff
23
third-party/td/td/LICENSE_1_0.txt
vendored
Normal file
23
third-party/td/td/LICENSE_1_0.txt
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Boost Software License - Version 1.0 - August 17th, 2003
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
execute, and transmit the Software, and to prepare derivative works of the
|
||||||
|
Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
do so, all subject to the following:
|
||||||
|
|
||||||
|
The copyright notices in the Software and this entire statement, including
|
||||||
|
the above license grant, this restriction and the following disclaimer,
|
||||||
|
must be included in all copies of the Software, in whole or in part, and
|
||||||
|
all derivative works of the Software, unless such copies or derivative
|
||||||
|
works are solely in the form of machine-executable object code generated by
|
||||||
|
a source language processor.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
145
third-party/td/td/README.md
vendored
Normal file
145
third-party/td/td/README.md
vendored
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
# TDLib
|
||||||
|
|
||||||
|
TDLib (Telegram Database library) is a cross-platform library for building [Telegram](https://telegram.org) clients. It can be easily used from almost any programming language.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
- [Features](#features)
|
||||||
|
- [Examples and documentation](#usage)
|
||||||
|
- [Dependencies](#dependencies)
|
||||||
|
- [Building](#building)
|
||||||
|
- [Using in CMake C++ projects](#using-cxx)
|
||||||
|
- [Using in Java projects](#using-java)
|
||||||
|
- [Using in .NET projects](#using-dotnet)
|
||||||
|
- [Using with other programming languages](#using-json)
|
||||||
|
- [License](#license)
|
||||||
|
|
||||||
|
<a name="features"></a>
|
||||||
|
## Features
|
||||||
|
|
||||||
|
`TDLib` has many advantages. Notably `TDLib` is:
|
||||||
|
|
||||||
|
* **Cross-platform**: `TDLib` can be used on Android, iOS, Windows, macOS, Linux, FreeBSD, OpenBSD, NetBSD, illumos, Windows Phone, WebAssembly, watchOS, tvOS, visionOS, Tizen, Cygwin. It should also work on other *nix systems with or without minimal effort.
|
||||||
|
* **Multilanguage**: `TDLib` can be easily used with any programming language that is able to execute C functions. Additionally, it already has native Java (using `JNI`) bindings and .NET (using `C++/CLI` and `C++/CX`) bindings.
|
||||||
|
* **Easy to use**: `TDLib` takes care of all network implementation details, encryption and local data storage.
|
||||||
|
* **High-performance**: in the [Telegram Bot API](https://core.telegram.org/bots/api), each `TDLib` instance handles more than 25000 active bots simultaneously.
|
||||||
|
* **Well-documented**: all `TDLib` API methods and public interfaces are fully documented.
|
||||||
|
* **Consistent**: `TDLib` guarantees that all updates are delivered in the right order.
|
||||||
|
* **Reliable**: `TDLib` remains stable on slow and unreliable Internet connections.
|
||||||
|
* **Secure**: all local data is encrypted using a user-provided encryption key.
|
||||||
|
* **Fully-asynchronous**: requests to `TDLib` don't block each other or anything else, responses are sent when they are available.
|
||||||
|
|
||||||
|
<a name="usage"></a>
|
||||||
|
## Examples and documentation
|
||||||
|
See our [Getting Started](https://core.telegram.org/tdlib/getting-started) tutorial for a description of basic TDLib concepts.
|
||||||
|
|
||||||
|
Take a look at our [examples](https://github.com/tdlib/td/blob/master/example/README.md#tdlib-usage-and-build-examples).
|
||||||
|
|
||||||
|
See a [TDLib build instructions generator](https://tdlib.github.io/td/build.html) for detailed instructions on how to build TDLib.
|
||||||
|
|
||||||
|
See description of our [JSON](#using-json), [C++](#using-cxx), [Java](#using-java) and [.NET](#using-dotnet) interfaces.
|
||||||
|
|
||||||
|
See the [td_api.tl](https://github.com/tdlib/td/blob/master/td/generate/scheme/td_api.tl) scheme or the automatically generated [HTML documentation](https://core.telegram.org/tdlib/docs/td__api_8h.html)
|
||||||
|
for a list of all available `TDLib` [methods](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_function.html) and [classes](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_object.html).
|
||||||
|
|
||||||
|
<a name="dependencies"></a>
|
||||||
|
## Dependencies
|
||||||
|
`TDLib` depends on:
|
||||||
|
|
||||||
|
* C++17 compatible compiler (Clang 5.0+, GCC 7.0+, MSVC 19.1+ (Visual Studio 2017.7+), Intel C++ Compiler 19+)
|
||||||
|
* OpenSSL
|
||||||
|
* zlib
|
||||||
|
* gperf (build only)
|
||||||
|
* CMake (3.10+, build only)
|
||||||
|
* PHP (optional, for documentation generation)
|
||||||
|
|
||||||
|
<a name="building"></a>
|
||||||
|
## Building
|
||||||
|
|
||||||
|
The simplest way to build `TDLib` is to use our [TDLib build instructions generator](https://tdlib.github.io/td/build.html).
|
||||||
|
You need only to choose your programming language and target operating system to receive complete build instructions.
|
||||||
|
|
||||||
|
In general, you need to install all `TDLib` [dependencies](#dependencies), enter directory containing `TDLib` sources and compile them using CMake:
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||||
|
cmake --build .
|
||||||
|
```
|
||||||
|
|
||||||
|
To build `TDLib` on low memory devices you can run [SplitSource.php](https://github.com/tdlib/td/blob/master/SplitSource.php) script
|
||||||
|
before compiling main `TDLib` source code and compile only needed targets:
|
||||||
|
```
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||||
|
cmake --build . --target prepare_cross_compiling
|
||||||
|
cd ..
|
||||||
|
php SplitSource.php
|
||||||
|
cd build
|
||||||
|
cmake --build . --target tdjson
|
||||||
|
cmake --build . --target tdjson_static
|
||||||
|
cd ..
|
||||||
|
php SplitSource.php --undo
|
||||||
|
```
|
||||||
|
In our tests clang 6.0 with libc++ required less than 500 MB of RAM per file and GCC 4.9/6.3 used less than 1 GB of RAM per file.
|
||||||
|
|
||||||
|
<a name="using-cxx"></a>
|
||||||
|
## Using in CMake C++ projects
|
||||||
|
For C++ projects that use CMake, the best approach is to build `TDLib` as part of your project or to install it system-wide.
|
||||||
|
|
||||||
|
There are several libraries that you could use in your CMake project:
|
||||||
|
|
||||||
|
* Td::TdJson, Td::TdJsonStatic — dynamic and static version of a JSON interface. This has a simple C interface, so it can be easily used with any programming language that is able to execute C functions.
|
||||||
|
See [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html) documentation for more information.
|
||||||
|
* Td::TdStatic — static library with C++ interface for general usage.
|
||||||
|
See [ClientManager](https://core.telegram.org/tdlib/docs/classtd_1_1_client_manager.html) and [Client](https://core.telegram.org/tdlib/docs/classtd_1_1_client.html) documentation for more information.
|
||||||
|
|
||||||
|
For example, part of your CMakeLists.txt may look like this:
|
||||||
|
```
|
||||||
|
add_subdirectory(td)
|
||||||
|
target_link_libraries(YourTarget PRIVATE Td::TdStatic)
|
||||||
|
```
|
||||||
|
|
||||||
|
Or you could install `TDLib` and then reference it in your CMakeLists.txt like this:
|
||||||
|
```
|
||||||
|
find_package(Td 1.8.46 REQUIRED)
|
||||||
|
target_link_libraries(YourTarget PRIVATE Td::TdStatic)
|
||||||
|
```
|
||||||
|
See [example/cpp/CMakeLists.txt](https://github.com/tdlib/td/blob/master/example/cpp/CMakeLists.txt).
|
||||||
|
|
||||||
|
<a name="using-java"></a>
|
||||||
|
## Using in Java projects
|
||||||
|
`TDLib` provides native Java interface through JNI. To enable it, specify option `-DTD_ENABLE_JNI=ON` to CMake.
|
||||||
|
|
||||||
|
See [example/java](https://github.com/tdlib/td/tree/master/example/java) for example of using `TDLib` from Java and detailed build and usage instructions.
|
||||||
|
|
||||||
|
<a name="using-dotnet"></a>
|
||||||
|
## Using in .NET projects
|
||||||
|
`TDLib` provides native .NET interface through `C++/CLI` and `C++/CX`. To enable it, specify option `-DTD_ENABLE_DOTNET=ON` to CMake.
|
||||||
|
.NET Core supports `C++/CLI` only since version 3.1 and only on Windows, so if older .NET Core is used or portability is needed, then `TDLib` JSON interface should be used through P/Invoke instead.
|
||||||
|
|
||||||
|
See [example/csharp](https://github.com/tdlib/td/tree/master/example/csharp) for example of using `TDLib` from C# and detailed build and usage instructions.
|
||||||
|
See [example/uwp](https://github.com/tdlib/td/tree/master/example/uwp) for example of using `TDLib` from C# UWP application and detailed build and usage instructions for Visual Studio Extension "TDLib for Universal Windows Platform".
|
||||||
|
|
||||||
|
When `TDLib` is built with `TD_ENABLE_DOTNET` option enabled, `C++` documentation is removed from some files. You need to checkout these files to return `C++` documentation back:
|
||||||
|
```
|
||||||
|
git checkout td/telegram/Client.h td/telegram/Log.h td/tl/TlObject.h
|
||||||
|
```
|
||||||
|
|
||||||
|
<a name="using-json"></a>
|
||||||
|
## Using from other programming languages
|
||||||
|
`TDLib` provides efficient native C++, Java, and .NET interfaces.
|
||||||
|
But for most use cases we suggest to use the JSON interface, which can be easily used with any programming language that is able to execute C functions.
|
||||||
|
See [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html) documentation for detailed JSON interface description,
|
||||||
|
the [td_api.tl](https://github.com/tdlib/td/blob/master/td/generate/scheme/td_api.tl) scheme or the automatically generated [HTML documentation](https://core.telegram.org/tdlib/docs/td__api_8h.html) for a list of
|
||||||
|
all available `TDLib` [methods](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_function.html) and [classes](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_object.html).
|
||||||
|
|
||||||
|
`TDLib` JSON interface adheres to semantic versioning and versions with the same major version number are binary and backward compatible, but the underlying `TDLib` API can be different for different minor and even patch versions.
|
||||||
|
If you need to support different `TDLib` versions, then you can use a value of the `version` option to find exact `TDLib` version to use appropriate API methods.
|
||||||
|
|
||||||
|
See [example/python/tdjson_example.py](https://github.com/tdlib/td/blob/master/example/python/tdjson_example.py) for an example of such usage.
|
||||||
|
|
||||||
|
<a name="license"></a>
|
||||||
|
## License
|
||||||
|
`TDLib` is licensed under the terms of the Boost Software License. See [LICENSE_1_0.txt](http://www.boost.org/LICENSE_1_0.txt) for more information.
|
508
third-party/td/td/SplitSource.php
vendored
Normal file
508
third-party/td/td/SplitSource.php
vendored
Normal file
@ -0,0 +1,508 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function disjoint_set_find(&$parents, $x) {
|
||||||
|
if ($parents[$x] !== $x) {
|
||||||
|
return $parents[$x] = disjoint_set_find($parents, $parents[$x]);
|
||||||
|
}
|
||||||
|
return $x;
|
||||||
|
}
|
||||||
|
|
||||||
|
function disjoint_set_union(&$parents, $x, $y) {
|
||||||
|
$x = disjoint_set_find($parents, $x);
|
||||||
|
$y = disjoint_set_find($parents, $y);
|
||||||
|
|
||||||
|
if ($x !== $y) {
|
||||||
|
if (rand(0, 1) == 0) {
|
||||||
|
$parents[$x] = $y;
|
||||||
|
} else {
|
||||||
|
$parents[$y] = $x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function split_file($file, $chunks, $undo) {
|
||||||
|
$cpp_name = "$file.cpp";
|
||||||
|
|
||||||
|
echo "Processing file $cpp_name".PHP_EOL;
|
||||||
|
|
||||||
|
$new_files = array();
|
||||||
|
foreach (range(0, $chunks - 1) as $n) {
|
||||||
|
$new_files[] = "$file$n.cpp";
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_generated = (strpos($file, 'td/generate/') === 0);
|
||||||
|
|
||||||
|
$cmake_file = $is_generated ? 'td/generate/CMakeLists.txt' : 'CMakeLists.txt';
|
||||||
|
$cmake = file_get_contents($cmake_file);
|
||||||
|
|
||||||
|
$cmake_cpp_name = $cpp_name;
|
||||||
|
$cmake_new_files = $new_files;
|
||||||
|
if ($is_generated) {
|
||||||
|
foreach ($cmake_new_files as &$file_ref) {
|
||||||
|
$file_ref = str_replace('td/generate/auto/td', '${TD_AUTO_INCLUDE_DIR}', $file_ref);
|
||||||
|
}
|
||||||
|
$cmake_cpp_name = str_replace('td/generate/auto/td', '${TD_AUTO_INCLUDE_DIR}', $cmake_cpp_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($undo) {
|
||||||
|
foreach ($new_files as $file) {
|
||||||
|
if (file_exists($file)) {
|
||||||
|
echo "Unlinking ".$file.PHP_EOL;
|
||||||
|
unlink($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($cmake, $cmake_cpp_name) === false) {
|
||||||
|
$cmake = str_replace(implode(PHP_EOL.' ', $cmake_new_files), $cmake_cpp_name, $cmake);
|
||||||
|
file_put_contents($cmake_file, $cmake);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($cmake, $cmake_cpp_name) !== false) {
|
||||||
|
$cmake = str_replace($cmake_cpp_name, implode(PHP_EOL.' ', $cmake_new_files), $cmake);
|
||||||
|
file_put_contents($cmake_file, $cmake);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file_exists($cpp_name)) {
|
||||||
|
echo "ERROR: skip nonexistent file $cpp_name".PHP_EOL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$lines = file($cpp_name);
|
||||||
|
$depth = 0;
|
||||||
|
$target_depth = 1 + $is_generated;
|
||||||
|
$is_static = false;
|
||||||
|
$in_define = false;
|
||||||
|
$in_comment = false;
|
||||||
|
$current = '';
|
||||||
|
$common = '';
|
||||||
|
$functions = array();
|
||||||
|
$namespace_begin = '';
|
||||||
|
$namespace_end = '';
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$add_depth = strpos($line, 'namespace ') === 0 ? 1 : (strpos($line, '} // namespace') === 0 ? -1 : 0);
|
||||||
|
if ($add_depth) {
|
||||||
|
# namespace begin/end
|
||||||
|
if ($add_depth > 0) {
|
||||||
|
$depth += $add_depth;
|
||||||
|
}
|
||||||
|
if ($depth <= $target_depth) {
|
||||||
|
if ($add_depth > 0) {
|
||||||
|
$namespace_begin .= $line;
|
||||||
|
} else {
|
||||||
|
$namespace_end .= $line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($add_depth < 0) {
|
||||||
|
$depth += $add_depth;
|
||||||
|
}
|
||||||
|
if ($is_static) {
|
||||||
|
$common .= $current;
|
||||||
|
} else {
|
||||||
|
$functions[] = $current;
|
||||||
|
}
|
||||||
|
$common .= $line;
|
||||||
|
$current = '';
|
||||||
|
$is_static = false;
|
||||||
|
$in_define = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($line, '#undef') === 0 && !trim($current)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($in_comment && strpos($line, '*/') === 0) {
|
||||||
|
$in_comment = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strpos($line, '/*') === 0) {
|
||||||
|
$in_comment = true;
|
||||||
|
}
|
||||||
|
if ($in_comment) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($depth !== $target_depth) {
|
||||||
|
$common .= $line;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($line, 'static ') === 0 && $depth === $target_depth) {
|
||||||
|
$is_static = true;
|
||||||
|
}
|
||||||
|
if (!trim($current) && strpos($line, '#define ') === 0) {
|
||||||
|
$is_static = true;
|
||||||
|
$in_define = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current .= $line;
|
||||||
|
if ((strpos($line, '}') === 0 || ($in_define && !trim($line)) || preg_match('/^[a-z].*;\s*$/i', $line)) && $depth === $target_depth) {
|
||||||
|
# block end
|
||||||
|
if ($is_static) {
|
||||||
|
$common .= $current;
|
||||||
|
} else {
|
||||||
|
$functions[] = $current;
|
||||||
|
}
|
||||||
|
$current = '';
|
||||||
|
$is_static = false;
|
||||||
|
$in_define = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$current = trim($current);
|
||||||
|
if (!empty($current)) {
|
||||||
|
fwrite(STDERR, "ERROR: $current".PHP_EOL);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($functions) < $chunks) {
|
||||||
|
fwrite(STDERR, "ERROR: file is too small to be split more".PHP_EOL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$deps = array(); // all functions from the same subarray must be in the same file
|
||||||
|
$parents = array();
|
||||||
|
foreach ($functions as $i => $f) {
|
||||||
|
if (preg_match_all('/(?J)create_handler<(?<name>[A-Z][A-Za-z]*)>|'.
|
||||||
|
'(?<name>[A-Z][A-Za-z]*) (final )?: public (Td::ResultHandler|Request)|'.
|
||||||
|
'(CREATE_REQUEST|CREATE_NO_ARGS_REQUEST)[(](?<name>[A-Z][A-Za-z]*)|'.
|
||||||
|
'(?<name>complete_pending_preauthentication_requests)|'.
|
||||||
|
'(?<name>get_message_history_slice)|'.
|
||||||
|
'(Up|Down)load(?!ManagerCallback)[a-zA-Z]+C(?<name>allback)|(up|down)load_[a-z_]*_c(?<name>allback)_|'.
|
||||||
|
'(?<name>lazy_to_json)|'.
|
||||||
|
'(?<name>LogEvent)[^sA]|'.
|
||||||
|
'(?<name>parse)[(]|'.
|
||||||
|
'(?<name>store)[(]/', $f, $matches, PREG_SET_ORDER)) {
|
||||||
|
foreach ($matches as $match) {
|
||||||
|
$name = $match['name'];
|
||||||
|
if ($name === 'parse' || $name === 'store') {
|
||||||
|
if ($is_generated) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = 'LogEvent';
|
||||||
|
}
|
||||||
|
$deps[$name][] = $i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$parents[$i] = $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($deps as $func_ids) {
|
||||||
|
foreach ($func_ids as $func_id) {
|
||||||
|
disjoint_set_union($parents, $func_ids[0], $func_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$sets = array();
|
||||||
|
$set_sizes = array();
|
||||||
|
foreach ($functions as $i => $f) {
|
||||||
|
$parent = disjoint_set_find($parents, $i);
|
||||||
|
if (!isset($sets[$parent])) {
|
||||||
|
$sets[$parent] = '';
|
||||||
|
$set_sizes[$parent] = 0;
|
||||||
|
}
|
||||||
|
$sets[$parent] .= $f;
|
||||||
|
$set_sizes[$parent] += strlen($f);
|
||||||
|
}
|
||||||
|
arsort($set_sizes);
|
||||||
|
|
||||||
|
$files = array_fill(0, $chunks, '');
|
||||||
|
$file_sizes = array_fill(0, $chunks, 0);
|
||||||
|
foreach ($set_sizes as $parent => $size) {
|
||||||
|
$file_id = array_search(min($file_sizes), $file_sizes);
|
||||||
|
$files[$file_id] .= $sets[$parent];
|
||||||
|
$file_sizes[$file_id] += $size;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($files as $n => $f) {
|
||||||
|
$new_content = $common.$namespace_begin.$f.$namespace_end;
|
||||||
|
|
||||||
|
$std_methods = array();
|
||||||
|
preg_match_all('/std::[a-z_0-9]*|td::unique(?!_)/', $new_content, $std_methods);
|
||||||
|
$std_methods = array_unique($std_methods[0]);
|
||||||
|
|
||||||
|
$needed_std_headers = array();
|
||||||
|
$type_headers = array(
|
||||||
|
'std::move' => '',
|
||||||
|
'std::vector' => '',
|
||||||
|
'std::string' => '',
|
||||||
|
'std::uint32_t' => '',
|
||||||
|
'std::int32_t' => '',
|
||||||
|
'std::int64_t' => '',
|
||||||
|
'td::unique' => 'algorithm',
|
||||||
|
'std::count_if' => 'algorithm',
|
||||||
|
'std::fill' => 'algorithm',
|
||||||
|
'std::find' => 'algorithm',
|
||||||
|
'std::is_sorted' => 'algorithm',
|
||||||
|
'std::lower_bound' => 'algorithm',
|
||||||
|
'std::max' => 'algorithm',
|
||||||
|
'std::merge' => 'algorithm',
|
||||||
|
'std::min' => 'algorithm',
|
||||||
|
'std::partial_sort' => 'algorithm',
|
||||||
|
'std::partition' => 'algorithm',
|
||||||
|
'std::remove' => 'algorithm',
|
||||||
|
'std::reverse' => 'algorithm',
|
||||||
|
'std::rotate' => 'algorithm',
|
||||||
|
'std::sort' => 'algorithm',
|
||||||
|
'std::stable_sort' => 'algorithm',
|
||||||
|
'std::upper_bound' => 'algorithm',
|
||||||
|
'std::abs' => 'cmath',
|
||||||
|
'std::isfinite' => 'cmath',
|
||||||
|
'std::function' => 'functional',
|
||||||
|
'std::greater' => 'functional',
|
||||||
|
'std::reference_wrapper' => 'functional',
|
||||||
|
'std::make_move_iterator' => 'iterator',
|
||||||
|
'std::numeric_limits' => 'limits',
|
||||||
|
'std::map' => 'map',
|
||||||
|
'std::multimap' => 'map',
|
||||||
|
'std::make_shared' => 'memory',
|
||||||
|
'std::shared_ptr' => 'memory',
|
||||||
|
'std::multiset' => 'set',
|
||||||
|
'std::set' => 'set',
|
||||||
|
'std::get' => 'tuple',
|
||||||
|
'std::make_tuple' => 'tuple',
|
||||||
|
'std::tie' => 'tuple',
|
||||||
|
'std::tuple' => 'tuple',
|
||||||
|
'std::decay_t' => 'type_traits',
|
||||||
|
'std::is_same' => 'type_traits',
|
||||||
|
'std::unordered_map' => 'unordered_map',
|
||||||
|
'std::unordered_set' => 'unordered_set',
|
||||||
|
'std::make_pair' => 'utility',
|
||||||
|
'std::pair' => 'utility',
|
||||||
|
'std::swap' => 'utility');
|
||||||
|
foreach ($type_headers as $type => $header) {
|
||||||
|
if (in_array($type, $std_methods)) {
|
||||||
|
$std_methods = array_diff($std_methods, array($type));
|
||||||
|
if ($header && !in_array($header, $needed_std_headers)) {
|
||||||
|
$needed_std_headers[] = $header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$std_methods) { // know all needed std headers
|
||||||
|
$new_content = preg_replace_callback(
|
||||||
|
'/#include <([a-z_]*)>/',
|
||||||
|
function ($matches) use ($needed_std_headers) {
|
||||||
|
if (in_array($matches[1], $needed_std_headers)) {
|
||||||
|
return $matches[0];
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
$new_content
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$td_methods = array(
|
||||||
|
'AccentColorId' => 'AccentColorId',
|
||||||
|
'account_manager[_(-](?![.]get[(][)])|AccountManager[^;>]' => 'AccountManager',
|
||||||
|
'AffiliateType' => 'AffiliateType',
|
||||||
|
'alarm_manager[_(-](?![.]get[(][)])|AlarmManager' => 'AlarmManager',
|
||||||
|
'animations_manager[_(-](?![.]get[(][)])|AnimationsManager[^;>]' => 'AnimationsManager',
|
||||||
|
'attach_menu_manager[_(-](?![.]get[(][)])|AttachMenuManager[^;>]' => 'AttachMenuManager',
|
||||||
|
'audios_manager[_(-](?![.]get[(][)])|AudiosManager' => 'AudiosManager',
|
||||||
|
'auth_manager[_(-](?![.]get[(][)])|AuthManager' => 'AuthManager',
|
||||||
|
'AutoDownloadSettings|[a-z_]*auto_download_settings' => 'AutoDownloadSettings',
|
||||||
|
'autosave_manager[_(-](?![.]get[(][)])|AutosaveManager' => 'AutosaveManager',
|
||||||
|
'BackgroundId' => 'BackgroundId',
|
||||||
|
'background_manager[_(-](?![.]get[(][)])|BackgroundManager' => 'BackgroundManager',
|
||||||
|
'BackgroundType' => 'BackgroundType',
|
||||||
|
'Birthdate' => 'Birthdate',
|
||||||
|
'boost_manager[_(-](?![.]get[(][)])|BoostManager' => 'BoostManager',
|
||||||
|
'bot_info_manager[_(-](?![.]get[(][)])|BotInfoManager' => 'BotInfoManager',
|
||||||
|
'BotMenuButton|[a-z_]*_menu_button' => 'BotMenuButton',
|
||||||
|
'send_bot_custom_query|answer_bot_custom_query|set_bot_updates_status' => 'BotQueries',
|
||||||
|
'bot_recommendation_manager[_(-](?![.]get[(][)])|BotRecommendationManager' => 'BotRecommendationManager',
|
||||||
|
'BotVerification' => 'BotVerification',
|
||||||
|
'BotVerifierSettings' => 'BotVerifierSettings',
|
||||||
|
'BusinessAwayMessage' => 'BusinessAwayMessage',
|
||||||
|
'BusinessChatLink' => 'BusinessChatLink',
|
||||||
|
'BusinessConnectedBot' => 'BusinessConnectedBot',
|
||||||
|
'BusinessConnectionId' => 'BusinessConnectionId',
|
||||||
|
'business_connection_manager[_(-](?![.]get[(][)])|BusinessConnectionManager' => 'BusinessConnectionManager',
|
||||||
|
'BusinessGreetingMessage' => 'BusinessGreetingMessage',
|
||||||
|
'BusinessInfo|business_info' => 'BusinessInfo',
|
||||||
|
'BusinessIntro' => 'BusinessIntro',
|
||||||
|
'business_manager[_(-](?![.]get[(][)])|BusinessManager' => 'BusinessManager',
|
||||||
|
'BusinessRecipients' => 'BusinessRecipients',
|
||||||
|
'BusinessWorkHours' => 'BusinessWorkHours',
|
||||||
|
'callback_queries_manager[_(-](?![.]get[(][)])|CallbackQueriesManager' => 'CallbackQueriesManager',
|
||||||
|
'CallId' => 'CallId',
|
||||||
|
'call_manager[_(-](?![.]get[(][)])|CallManager' => 'CallManager',
|
||||||
|
'ChannelId' => 'ChannelId',
|
||||||
|
'channel_recommendation_manager[_(-](?![.]get[(][)])|ChannelRecommendationManager' => 'ChannelRecommendationManager',
|
||||||
|
'ChatId' => 'ChatId',
|
||||||
|
'chat_manager[_(-](?![.]get[(][)])|ChatManager([^ ;.]| [^*])' => 'ChatManager',
|
||||||
|
'common_dialog_manager[_(-](?![.]get[(][)])|CommonDialogManager' => 'CommonDialogManager',
|
||||||
|
'connection_state_manager[_(-](?![.]get[(][)])|ConnectionStateManager' => 'ConnectionStateManager',
|
||||||
|
'country_info_manager[_(-](?![.]get[(][)])|CountryInfoManager' => 'CountryInfoManager',
|
||||||
|
'CustomEmojiId' => 'CustomEmojiId',
|
||||||
|
'device_token_manager[_(-](?![.]get[(][)])|DeviceTokenManager' => 'DeviceTokenManager',
|
||||||
|
'DialogAction[^M]' => 'DialogAction',
|
||||||
|
'dialog_action_manager[_(-](?![.]get[(][)])|DialogActionManager' => 'DialogActionManager',
|
||||||
|
'DialogFilter[^A-Z]' => 'DialogFilter',
|
||||||
|
'DialogFilterId' => 'DialogFilterId',
|
||||||
|
'dialog_filter_manager[_(-](?![.]get[(][)])|DialogFilterManager' => 'DialogFilterManager',
|
||||||
|
'DialogId' => 'DialogId',
|
||||||
|
'dialog_invite_link_manager[_(-](?![.]get[(][)])|DialogInviteLinkManager' => 'DialogInviteLinkManager',
|
||||||
|
'DialogListId' => 'DialogListId',
|
||||||
|
'DialogLocation' => 'DialogLocation',
|
||||||
|
'dialog_manager[_(-](?![.]get[(][)])|DialogManager' => 'DialogManager',
|
||||||
|
'DialogParticipantFilter' => 'DialogParticipantFilter',
|
||||||
|
'dialog_participant_manager[_(-](?![.]get[(][)])|DialogParticipantManager' => 'DialogParticipantManager',
|
||||||
|
'DialogSource' => 'DialogSource',
|
||||||
|
'documents_manager[_(-](?![.]get[(][)])|DocumentsManager' => 'DocumentsManager',
|
||||||
|
'download_manager[_(-](?![.]get[(][)])|DownloadManager[^C]' => 'DownloadManager',
|
||||||
|
'DownloadManagerCallback' => 'DownloadManagerCallback',
|
||||||
|
'EmailVerification' => 'EmailVerification',
|
||||||
|
'EmojiGroup' => 'EmojiGroup',
|
||||||
|
'FactCheck' => 'FactCheck',
|
||||||
|
'file_reference_manager[_(-](?![.]get[(][)])|FileReferenceManager|file_references[)]' => 'FileReferenceManager',
|
||||||
|
'file_manager[_(-](?![.]get[(][)])|FileManager([^ ;.]| [^*])|update_file[)]' => 'files/FileManager',
|
||||||
|
'FolderId' => 'FolderId',
|
||||||
|
'forum_topic_manager[_(-](?![.]get[(][)])|ForumTopicManager' => 'ForumTopicManager',
|
||||||
|
'game_manager[_(-](?![.]get[(][)])|GameManager' => 'GameManager',
|
||||||
|
'G[(][)]|Global[^A-Za-z]' => 'Global',
|
||||||
|
'GlobalPrivacySettings' => 'GlobalPrivacySettings',
|
||||||
|
'GroupCallId' => 'GroupCallId',
|
||||||
|
'group_call_manager[_(-](?![.]get[(][)])|GroupCallManager' => 'GroupCallManager',
|
||||||
|
'hashtag_hints[_(-](?![.]get[(][)])|HashtagHints' => 'HashtagHints',
|
||||||
|
'inline_message_manager[_(-](?![.]get[(][)])|InlineMessageManager' => 'InlineMessageManager',
|
||||||
|
'inline_queries_manager[_(-](?![.]get[(][)])|InlineQueriesManager' => 'InlineQueriesManager',
|
||||||
|
'InputBusinessChatLink' => 'InputBusinessChatLink',
|
||||||
|
'language_pack_manager[_(-]|LanguagePackManager' => 'LanguagePackManager',
|
||||||
|
'link_manager[_(-](?![.]get[(][)])|LinkManager' => 'LinkManager',
|
||||||
|
'LogeventIdWithGeneration|add_log_event|delete_log_event|get_erase_log_event_promise|parse_time|store_time' => 'logevent/LogEventHelper',
|
||||||
|
'MessageCopyOptions' => 'MessageCopyOptions',
|
||||||
|
'MessageEffectId' => 'MessageEffectId',
|
||||||
|
'MessageForwardInfo|LastForwardedMessageInfo|forward_info' => 'MessageForwardInfo',
|
||||||
|
'MessageFullId' => 'MessageFullId',
|
||||||
|
'MessageId' => 'MessageId',
|
||||||
|
'message_import_manager[_(-](?![.]get[(][)])|MessageImportManager' => 'MessageImportManager',
|
||||||
|
'message_query_manager[_(-](?![.]get[(][)])|MessageQueryManager' => 'MessageQueryManager',
|
||||||
|
'MessageLinkInfo' => 'MessageLinkInfo',
|
||||||
|
'MessageQuote' => 'MessageQuote',
|
||||||
|
'MessageReaction|UnreadMessageReaction|[a-z_]*message[a-z_]*reaction|reload_paid_reaction_privacy|get_chosen_tags' => 'MessageReaction',
|
||||||
|
'MessageReactor' => 'MessageReactor',
|
||||||
|
'MessageSearchOffset' => 'MessageSearchOffset',
|
||||||
|
'[a-z_]*_message_sender' => 'MessageSender',
|
||||||
|
'messages_manager[_(-](?![.]get[(][)])|MessagesManager' => 'MessagesManager',
|
||||||
|
'MessageThreadInfo' => 'MessageThreadInfo',
|
||||||
|
'MessageTtl' => 'MessageTtl',
|
||||||
|
'MissingInvitee' => 'MissingInvitee',
|
||||||
|
'notification_manager[_(-](?![.]get[(][)])|NotificationManager|notifications[)]' => 'NotificationManager',
|
||||||
|
'notification_settings_manager[_(-](?![.]get[(][)])|NotificationSettingsManager' => 'NotificationSettingsManager',
|
||||||
|
'online_manager[_(-](?![.]get[(][)])|OnlineManager' => 'OnlineManager',
|
||||||
|
'option_manager[_(-](?![.]get[(][)])|OptionManager' => 'OptionManager',
|
||||||
|
'PaidReactionType' => 'PaidReactionType',
|
||||||
|
'password_manager[_(-](?![.]get[(][)])|PasswordManager' => 'PasswordManager',
|
||||||
|
'people_nearby_manager[_(-](?![.]get[(][)])|PeopleNearbyManager' => 'PeopleNearbyManager',
|
||||||
|
'phone_number_manager[_(-](?![.]get[(][)])|PhoneNumberManager' => 'PhoneNumberManager',
|
||||||
|
'PhotoSizeSource' => 'PhotoSizeSource',
|
||||||
|
'poll_manager[_(-](?![.]get[(][)])|PollManager' => 'PollManager',
|
||||||
|
'privacy_manager[_(-](?![.]get[(][)])|PrivacyManager' => 'PrivacyManager',
|
||||||
|
'promo_data_manager[_(-](?![.]get[(][)])|PromoDataManager' => 'PromoDataManager',
|
||||||
|
'PublicDialogType|get_public_dialog_type' => 'PublicDialogType',
|
||||||
|
'quick_reply_manager[_(-](?![.]get[(][)])|QuickReplyManager' => 'QuickReplyManager',
|
||||||
|
'ReactionListType|[a-z_]*_reaction_list_type' => 'ReactionListType',
|
||||||
|
'reaction_manager[_(-](?![.]get[(][)])|ReactionManager' => 'ReactionManager',
|
||||||
|
'ReactionNotificationSettings' => 'ReactionNotificationSettings',
|
||||||
|
'ReactionNotificationsFrom' => 'ReactionNotificationsFrom',
|
||||||
|
'ReactionType|[a-z_]*_reaction_type' => 'ReactionType',
|
||||||
|
'ReferralProgramInfo' => 'ReferralProgramInfo',
|
||||||
|
'referral_program_manager[_(-](?![.]get[(][)])|ReferralProgramManager' => 'ReferralProgramManager',
|
||||||
|
'ReferralProgramParameters' => 'ReferralProgramParameters',
|
||||||
|
'RequestActor|RequestOnceActor' => 'RequestActor',
|
||||||
|
'saved_messages_manager[_(-](?![.]get[(][)])|SavedMessagesManager' => 'SavedMessagesManager',
|
||||||
|
'ScopeNotificationSettings|[a-z_]*_scope_notification_settings' => 'ScopeNotificationSettings',
|
||||||
|
'SecretChatActor' => 'SecretChatActor',
|
||||||
|
'secret_chats_manager[_(-]|SecretChatsManager' => 'SecretChatsManager',
|
||||||
|
'secure_manager[_(-](?![.]get[(][)])|SecureManager' => 'SecureManager',
|
||||||
|
'SentEmailCode' => 'SentEmailCode',
|
||||||
|
'SharedDialog' => 'SharedDialog',
|
||||||
|
'sponsored_message_manager[_(-](?![.]get[(][)])|SponsoredMessageManager' => 'SponsoredMessageManager',
|
||||||
|
'StarAmount' => 'StarAmount',
|
||||||
|
'StarGift[^A-Z]' => 'StarGift',
|
||||||
|
'StarGiftAttribute' => 'StarGiftAttribute',
|
||||||
|
'StarGiftId' => 'StarGiftId',
|
||||||
|
'star_gift_manager[_(-](?![.]get[(][)])|StarGiftManager' => 'StarGiftManager',
|
||||||
|
'star_manager[_(-](?![.]get[(][)])|StarManager' => 'StarManager',
|
||||||
|
'StarSubscription[^P]' => 'StarSubscription',
|
||||||
|
'StarSubscriptionPricing' => 'StarSubscriptionPricing',
|
||||||
|
'state_manager[_(-](?![.]get[(][)])|StateManager' => 'StateManager',
|
||||||
|
'statistics_manager[_(-](?![.]get[(][)])|StatisticsManager' => 'StatisticsManager',
|
||||||
|
'StickerSetId' => 'StickerSetId',
|
||||||
|
'stickers_manager[_(-](?![.]get[(][)])|StickersManager' => 'StickersManager',
|
||||||
|
'storage_manager[_(-](?![.]get[(][)])|StorageManager' => 'StorageManager',
|
||||||
|
'StoryId' => 'StoryId',
|
||||||
|
'StoryListId' => 'StoryListId',
|
||||||
|
'story_manager[_(-](?![.]get[(][)])|StoryManager' => 'StoryManager',
|
||||||
|
'SuggestedAction|[a-z_]*_suggested_action' => 'SuggestedAction',
|
||||||
|
'suggested_action_manager[_(-](?![.]get[(][)])|SuggestedActionManager' => 'SuggestedActionManager',
|
||||||
|
'SynchronousRequests' => 'SynchronousRequests',
|
||||||
|
'TargetDialogTypes' => 'TargetDialogTypes',
|
||||||
|
'td_api' => 'td_api',
|
||||||
|
'td_db[(][)]|TdDb[^A-Za-z]' => 'TdDb',
|
||||||
|
'telegram_api' => 'telegram_api',
|
||||||
|
'terms_of_service_manager[_(-](?![.]get[(][)])|TermsOfServiceManager' => 'TermsOfServiceManager',
|
||||||
|
'theme_manager[_(-](?![.]get[(][)])|ThemeManager' => 'ThemeManager',
|
||||||
|
'ThemeSettings' => 'ThemeSettings',
|
||||||
|
'time_zone_manager[_(-](?![.]get[(][)])|TimeZoneManager' => 'TimeZoneManager',
|
||||||
|
'TopDialogCategory|get_top_dialog_category' => 'TopDialogCategory',
|
||||||
|
'top_dialog_manager[_(-](?![.]get[(][)])|TopDialogManager' => 'TopDialogManager',
|
||||||
|
'translation_manager[_(-](?![.]get[(][)])|TranslationManager' => 'TranslationManager',
|
||||||
|
'transcription_manager[_(-](?![.]get[(][)])|TranscriptionManager' => 'TranscriptionManager',
|
||||||
|
'updates_manager[_(-](?![.]get[(][)])|UpdatesManager|get_difference[)]|updateSentMessage|dummyUpdate' => 'UpdatesManager',
|
||||||
|
'UserId' => 'UserId',
|
||||||
|
'user_manager[_(-](?![.]get[(][)])|UserManager([^ ;.]| [^*])' => 'UserManager',
|
||||||
|
'UserStarGift' => 'UserStarGift',
|
||||||
|
'video_notes_manager[_(-](?![.]get[(][)])|VideoNotesManager' => 'VideoNotesManager',
|
||||||
|
'videos_manager[_(-](?![.]get[(][)])|VideosManager' => 'VideosManager',
|
||||||
|
'voice_notes_manager[_(-](?![.]get[(][)])|VoiceNotesManager' => 'VoiceNotesManager',
|
||||||
|
'web_app_manager[_(-](?![.]get[(][)])|WebAppManager' => 'WebAppManager',
|
||||||
|
'WebAppOpenParameters' => 'WebAppOpenParameters',
|
||||||
|
'WebPageId(Hash)?' => 'WebPageId',
|
||||||
|
'web_pages_manager[_(-](?![.]get[(][)])|WebPagesManager' => 'WebPagesManager');
|
||||||
|
|
||||||
|
foreach ($td_methods as $pattern => $header) {
|
||||||
|
if (strpos($cpp_name, $header) !== false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$include_name = '#include "td/telegram/'.$header.'.h"';
|
||||||
|
if (strpos($new_content, $include_name) !== false && preg_match('/[^a-zA-Z0-9_]('.$pattern.')/', str_replace($include_name, '', $new_content)) === 0) {
|
||||||
|
$new_content = str_replace($include_name, '', $new_content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file_exists($new_files[$n]) || file_get_contents($new_files[$n]) !== $new_content) {
|
||||||
|
echo "Writing file ".$new_files[$n].PHP_EOL;
|
||||||
|
file_put_contents($new_files[$n], $new_content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array('--help', $argv) || in_array('-h', $argv)) {
|
||||||
|
echo "Usage: php SplitSource.php [OPTION]...\n".
|
||||||
|
"Splits some source files to reduce a maximum amount of RAM needed for compiling a single file.\n".
|
||||||
|
" -u, --undo Undo all source code changes.\n".
|
||||||
|
" -h, --help Show this help.\n";
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
$undo = in_array('--undo', $argv) || in_array('-u', $argv);
|
||||||
|
$files = array('td/telegram/ChatManager' => 10,
|
||||||
|
'td/telegram/MessagesManager' => 50,
|
||||||
|
'td/telegram/NotificationManager' => 10,
|
||||||
|
'td/telegram/Requests' => 50,
|
||||||
|
'td/telegram/StickersManager' => 10,
|
||||||
|
'td/telegram/StoryManager' => 10,
|
||||||
|
'td/telegram/UpdatesManager' => 10,
|
||||||
|
'td/telegram/UserManager' => 10,
|
||||||
|
'td/generate/auto/td/telegram/td_api' => 10,
|
||||||
|
'td/generate/auto/td/telegram/td_api_json' => 10,
|
||||||
|
'td/generate/auto/td/telegram/telegram_api' => 10);
|
||||||
|
|
||||||
|
foreach ($files as $file => $chunks) {
|
||||||
|
split_file($file, $chunks, $undo);
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user