mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
e9eac3eac0
@ -145,6 +145,7 @@ objc_library(
|
|||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
"//submodules/NumberPluralizationForm:NumberPluralizationForm",
|
"//submodules/NumberPluralizationForm:NumberPluralizationForm",
|
||||||
|
"//submodules/AppBundle:AppBundle",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -70,14 +70,21 @@ private func rootPathForBasePath(_ appGroupPath: String) -> String {
|
|||||||
let screencastBufferClientContext = IpcGroupCallBufferBroadcastContext(basePath: rootPath + "/broadcast-coordination")
|
let screencastBufferClientContext = IpcGroupCallBufferBroadcastContext(basePath: rootPath + "/broadcast-coordination")
|
||||||
self.screencastBufferClientContext = screencastBufferClientContext
|
self.screencastBufferClientContext = screencastBufferClientContext
|
||||||
|
|
||||||
|
var wasRunning = false
|
||||||
self.statusDisposable = (screencastBufferClientContext.status
|
self.statusDisposable = (screencastBufferClientContext.status
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch status {
|
switch status {
|
||||||
case let .finished(reason):
|
case .active:
|
||||||
|
wasRunning = true
|
||||||
|
case let .finished(reason):
|
||||||
|
if wasRunning {
|
||||||
|
strongSelf.finish(with: .screencastEnded)
|
||||||
|
} else {
|
||||||
strongSelf.finish(with: reason)
|
strongSelf.finish(with: reason)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5509,6 +5509,7 @@ Sorry for the inconvenience.";
|
|||||||
"Stats.MessagePublicForwardsTitle" = "Public Shares";
|
"Stats.MessagePublicForwardsTitle" = "Public Shares";
|
||||||
|
|
||||||
"Call.CameraTooltip" = "Tap here to turn on your camera";
|
"Call.CameraTooltip" = "Tap here to turn on your camera";
|
||||||
|
"Call.CameraOrScreenTooltip" = "Turn on your camera or screensharing";
|
||||||
"Call.CameraConfirmationText" = "Switch to video call?";
|
"Call.CameraConfirmationText" = "Switch to video call?";
|
||||||
"Call.CameraConfirmationConfirm" = "Switch";
|
"Call.CameraConfirmationConfirm" = "Switch";
|
||||||
|
|
||||||
|
|||||||
@ -308,6 +308,7 @@ static _FormattedString * _Nonnull getFormatted{num_arguments}(_PresentationStri
|
|||||||
|
|
||||||
#import <PresentationStrings/PresentationStrings.h>
|
#import <PresentationStrings/PresentationStrings.h>
|
||||||
#import <NumberPluralizationForm/NumberPluralizationForm.h>
|
#import <NumberPluralizationForm/NumberPluralizationForm.h>
|
||||||
|
#import <AppBundle/AppBundle.h>
|
||||||
|
|
||||||
@implementation _FormattedStringRange
|
@implementation _FormattedStringRange
|
||||||
|
|
||||||
@ -447,7 +448,7 @@ static NSString * _Nonnull getSingle(_PresentationStrings * _Nonnull strings, NS
|
|||||||
static NSDictionary<NSString *, NSString *> *fallbackDict = nil;
|
static NSDictionary<NSString *, NSString *> *fallbackDict = nil;
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
NSString *lprojPath = [[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"];
|
NSString *lprojPath = [getAppBundle() pathForResource:@"en" ofType:@"lproj"];
|
||||||
if (!lprojPath) {
|
if (!lprojPath) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -496,7 +497,7 @@ static NSString * _Nonnull getPluralizedIndirect(_PresentationStrings * _Nonnull
|
|||||||
static NSDictionary<NSNumber *, NSString *> *idToKey = nil;
|
static NSDictionary<NSNumber *, NSString *> *idToKey = nil;
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
NSString *dataPath = [[NSBundle mainBundle] pathForResource:@"PresentationStrings" ofType:@"data"];
|
NSString *dataPath = [getAppBundle() pathForResource:@"PresentationStrings" ofType:@"data"];
|
||||||
if (!dataPath) {
|
if (!dataPath) {
|
||||||
assert(false);
|
assert(false);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -568,7 +568,7 @@ public protocol SharedAccountContext: class {
|
|||||||
var callManager: PresentationCallManager? { get }
|
var callManager: PresentationCallManager? { get }
|
||||||
var contactDataManager: DeviceContactDataManager? { get }
|
var contactDataManager: DeviceContactDataManager? { get }
|
||||||
|
|
||||||
var activeAccounts: Signal<(primary: Account?, accounts: [(AccountRecordId, Account, Int32)], currentAuth: UnauthorizedAccount?), NoError> { get }
|
var activeAccountContexts: Signal<(primary: AccountContext?, accounts: [(AccountRecordId, AccountContext, Int32)], currentAuth: UnauthorizedAccount?), NoError> { get }
|
||||||
var activeAccountsWithInfo: Signal<(primary: AccountRecordId?, accounts: [AccountWithInfo]), NoError> { get }
|
var activeAccountsWithInfo: Signal<(primary: AccountRecordId?, accounts: [AccountWithInfo]), NoError> { get }
|
||||||
|
|
||||||
var presentGlobalController: (ViewController, Any?) -> Void { get }
|
var presentGlobalController: (ViewController, Any?) -> Void { get }
|
||||||
|
|||||||
@ -57,8 +57,8 @@ public struct PresentationCallState: Equatable {
|
|||||||
public enum VideoState: Equatable {
|
public enum VideoState: Equatable {
|
||||||
case notAvailable
|
case notAvailable
|
||||||
case inactive
|
case inactive
|
||||||
case active
|
case active(isScreencast: Bool)
|
||||||
case paused
|
case paused(isScreencast: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum RemoteVideoState: Equatable {
|
public enum RemoteVideoState: Equatable {
|
||||||
@ -132,7 +132,7 @@ public final class PresentationCallVideoView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public protocol PresentationCall: class {
|
public protocol PresentationCall: class {
|
||||||
var account: Account { get }
|
var context: AccountContext { get }
|
||||||
var isIntegratedWithCallKit: Bool { get }
|
var isIntegratedWithCallKit: Bool { get }
|
||||||
var internalId: CallSessionInternalId { get }
|
var internalId: CallSessionInternalId { get }
|
||||||
var peerId: PeerId { get }
|
var peerId: PeerId { get }
|
||||||
|
|||||||
@ -7,38 +7,38 @@ import AccountContext
|
|||||||
|
|
||||||
public let maximumNumberOfAccounts = 3
|
public let maximumNumberOfAccounts = 3
|
||||||
|
|
||||||
public func activeAccountsAndPeers(context: AccountContext, includePrimary: Bool = false) -> Signal<((Account, Peer)?, [(Account, Peer, Int32)]), NoError> {
|
public func activeAccountsAndPeers(context: AccountContext, includePrimary: Bool = false) -> Signal<((AccountContext, Peer)?, [(AccountContext, Peer, Int32)]), NoError> {
|
||||||
let sharedContext = context.sharedContext
|
let sharedContext = context.sharedContext
|
||||||
return context.sharedContext.activeAccounts
|
return context.sharedContext.activeAccountContexts
|
||||||
|> mapToSignal { primary, activeAccounts, _ -> Signal<((Account, Peer)?, [(Account, Peer, Int32)]), NoError> in
|
|> mapToSignal { primary, activeAccounts, _ -> Signal<((AccountContext, Peer)?, [(AccountContext, Peer, Int32)]), NoError> in
|
||||||
var accounts: [Signal<(Account, Peer, Int32)?, NoError>] = []
|
var accounts: [Signal<(AccountContext, Peer, Int32)?, NoError>] = []
|
||||||
func accountWithPeer(_ account: Account) -> Signal<(Account, Peer, Int32)?, NoError> {
|
func accountWithPeer(_ context: AccountContext) -> Signal<(AccountContext, Peer, Int32)?, NoError> {
|
||||||
return combineLatest(account.postbox.peerView(id: account.peerId), renderedTotalUnreadCount(accountManager: sharedContext.accountManager, postbox: account.postbox))
|
return combineLatest(context.account.postbox.peerView(id: context.account.peerId), renderedTotalUnreadCount(accountManager: sharedContext.accountManager, postbox: context.account.postbox))
|
||||||
|> map { view, totalUnreadCount -> (Peer?, Int32) in
|
|> map { view, totalUnreadCount -> (Peer?, Int32) in
|
||||||
return (view.peers[view.peerId], totalUnreadCount.0)
|
return (view.peers[view.peerId], totalUnreadCount.0)
|
||||||
}
|
}
|
||||||
|> distinctUntilChanged { lhs, rhs in
|
|> distinctUntilChanged { lhs, rhs in
|
||||||
return arePeersEqual(lhs.0, rhs.0) && lhs.1 == rhs.1
|
return arePeersEqual(lhs.0, rhs.0) && lhs.1 == rhs.1
|
||||||
}
|
}
|
||||||
|> map { peer, totalUnreadCount -> (Account, Peer, Int32)? in
|
|> map { peer, totalUnreadCount -> (AccountContext, Peer, Int32)? in
|
||||||
if let peer = peer {
|
if let peer = peer {
|
||||||
return (account, peer, totalUnreadCount)
|
return (context, peer, totalUnreadCount)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (_, account, _) in activeAccounts {
|
for (_, context, _) in activeAccounts {
|
||||||
accounts.append(accountWithPeer(account))
|
accounts.append(accountWithPeer(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
return combineLatest(accounts)
|
return combineLatest(accounts)
|
||||||
|> map { accounts -> ((Account, Peer)?, [(Account, Peer, Int32)]) in
|
|> map { accounts -> ((AccountContext, Peer)?, [(AccountContext, Peer, Int32)]) in
|
||||||
var primaryRecord: (Account, Peer)?
|
var primaryRecord: (AccountContext, Peer)?
|
||||||
if let first = accounts.filter({ $0?.0.id == primary?.id }).first, let (account, peer, _) = first {
|
if let first = accounts.filter({ $0?.0.account.id == primary?.account.id }).first, let (account, peer, _) = first {
|
||||||
primaryRecord = (account, peer)
|
primaryRecord = (account, peer)
|
||||||
}
|
}
|
||||||
let accountRecords: [(Account, Peer, Int32)] = (includePrimary ? accounts : accounts.filter({ $0?.0.id != primary?.id })).compactMap({ $0 })
|
let accountRecords: [(AccountContext, Peer, Int32)] = (includePrimary ? accounts : accounts.filter({ $0?.0.account.id != primary?.account.id })).compactMap({ $0 })
|
||||||
return (primaryRecord, accountRecords)
|
return (primaryRecord, accountRecords)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,7 +144,7 @@ private func galleryMessageCaptionText(_ message: Message) -> String {
|
|||||||
return message.text
|
return message.text
|
||||||
}
|
}
|
||||||
|
|
||||||
public func galleryItemForEntry(context: AccountContext, presentationData: PresentationData, entry: MessageHistoryEntry, isCentral: Bool = false, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, isSecret: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double = 1.0, displayInfoOnTop: Bool = false, configuration: GalleryConfiguration? = nil, tempFilePath: String? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void = { _, _ in }, storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void = { _, _, _ in }, present: @escaping (ViewController, Any?) -> Void) -> GalleryItem? {
|
public func galleryItemForEntry(context: AccountContext, presentationData: PresentationData, entry: MessageHistoryEntry, isCentral: Bool = false, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, isSecret: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double? = nil, displayInfoOnTop: Bool = false, configuration: GalleryConfiguration? = nil, tempFilePath: String? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void = { _, _ in }, storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void = { _, _, _ in }, present: @escaping (ViewController, Any?) -> Void) -> GalleryItem? {
|
||||||
let message = entry.message
|
let message = entry.message
|
||||||
let location = entry.location
|
let location = entry.location
|
||||||
if let (media, mediaImage) = mediaForMessage(message: message) {
|
if let (media, mediaImage) = mediaForMessage(message: message) {
|
||||||
@ -348,7 +348,7 @@ public class GalleryController: ViewController, StandalonePresentableController
|
|||||||
private let fromPlayingVideo: Bool
|
private let fromPlayingVideo: Bool
|
||||||
private let landscape: Bool
|
private let landscape: Bool
|
||||||
private let timecode: Double?
|
private let timecode: Double?
|
||||||
private let playbackRate: Double
|
private let playbackRate: Double?
|
||||||
|
|
||||||
private let accountInUseDisposable = MetaDisposable()
|
private let accountInUseDisposable = MetaDisposable()
|
||||||
private let disposable = MetaDisposable()
|
private let disposable = MetaDisposable()
|
||||||
@ -388,7 +388,7 @@ public class GalleryController: ViewController, StandalonePresentableController
|
|||||||
|
|
||||||
private var initialOrientation: UIInterfaceOrientation?
|
private var initialOrientation: UIInterfaceOrientation?
|
||||||
|
|
||||||
public init(context: AccountContext, source: GalleryControllerItemSource, invertItemOrder: Bool = false, streamSingleVideo: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double = 1.0, synchronousLoad: Bool = false, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, baseNavigationController: NavigationController?, actionInteraction: GalleryControllerActionInteraction? = nil) {
|
public init(context: AccountContext, source: GalleryControllerItemSource, invertItemOrder: Bool = false, streamSingleVideo: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double? = nil, synchronousLoad: Bool = false, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, baseNavigationController: NavigationController?, actionInteraction: GalleryControllerActionInteraction? = nil) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.source = source
|
self.source = source
|
||||||
self.invertItemOrder = invertItemOrder
|
self.invertItemOrder = invertItemOrder
|
||||||
@ -538,7 +538,7 @@ public class GalleryController: ViewController, StandalonePresentableController
|
|||||||
if entry.message.stableId == strongSelf.centralEntryStableId {
|
if entry.message.stableId == strongSelf.centralEntryStableId {
|
||||||
isCentral = true
|
isCentral = true
|
||||||
}
|
}
|
||||||
if let item = galleryItemForEntry(context: context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: streamSingleVideo, fromPlayingVideo: isCentral && fromPlayingVideo, landscape: isCentral && landscape, timecode: isCentral ? timecode : nil, playbackRate: isCentral ? playbackRate : 1.0, displayInfoOnTop: displayInfoOnTop, configuration: configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _, _ in }, present: { [weak self] c, a in
|
if let item = galleryItemForEntry(context: context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: streamSingleVideo, fromPlayingVideo: isCentral && fromPlayingVideo, landscape: isCentral && landscape, timecode: isCentral ? timecode : nil, playbackRate: isCentral ? playbackRate : nil, displayInfoOnTop: displayInfoOnTop, configuration: configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _, _ in }, present: { [weak self] c, a in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.presentInGlobalOverlay(c, with: a)
|
strongSelf.presentInGlobalOverlay(c, with: a)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,7 +44,7 @@ public class UniversalVideoGalleryItem: GalleryItem {
|
|||||||
let isSecret: Bool
|
let isSecret: Bool
|
||||||
let landscape: Bool
|
let landscape: Bool
|
||||||
let timecode: Double?
|
let timecode: Double?
|
||||||
let playbackRate: Double
|
let playbackRate: Double?
|
||||||
let configuration: GalleryConfiguration?
|
let configuration: GalleryConfiguration?
|
||||||
let playbackCompleted: () -> Void
|
let playbackCompleted: () -> Void
|
||||||
let performAction: (GalleryControllerInteractionTapAction) -> Void
|
let performAction: (GalleryControllerInteractionTapAction) -> Void
|
||||||
@ -52,7 +52,7 @@ public class UniversalVideoGalleryItem: GalleryItem {
|
|||||||
let storeMediaPlaybackState: (MessageId, Double?, Double) -> Void
|
let storeMediaPlaybackState: (MessageId, Double?, Double) -> Void
|
||||||
let present: (ViewController, Any?) -> Void
|
let present: (ViewController, Any?) -> Void
|
||||||
|
|
||||||
public init(context: AccountContext, presentationData: PresentationData, content: UniversalVideoContent, originData: GalleryItemOriginData?, indexData: GalleryItemIndexData?, contentInfo: UniversalVideoGalleryItemContentInfo?, caption: NSAttributedString, credit: NSAttributedString? = nil, displayInfoOnTop: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, isSecret: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double = 1.0, configuration: GalleryConfiguration? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void, storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
public init(context: AccountContext, presentationData: PresentationData, content: UniversalVideoContent, originData: GalleryItemOriginData?, indexData: GalleryItemIndexData?, contentInfo: UniversalVideoGalleryItemContentInfo?, caption: NSAttributedString, credit: NSAttributedString? = nil, displayInfoOnTop: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, isSecret: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double? = nil, configuration: GalleryConfiguration? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void, storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.content = content
|
self.content = content
|
||||||
@ -1314,7 +1314,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
|
|
||||||
var isAnimated = false
|
var isAnimated = false
|
||||||
var seek = MediaPlayerSeek.start
|
var seek = MediaPlayerSeek.start
|
||||||
var playbackRate: Double = 1.0
|
var playbackRate: Double? = nil
|
||||||
if let item = self.item {
|
if let item = self.item {
|
||||||
if let content = item.content as? NativeVideoContent {
|
if let content = item.content as? NativeVideoContent {
|
||||||
isAnimated = content.fileReference.media.isAnimated
|
isAnimated = content.fileReference.media.isAnimated
|
||||||
@ -1329,7 +1329,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
playbackRate = item.playbackRate
|
playbackRate = item.playbackRate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
videoNode.setBaseRate(playbackRate)
|
if let playbackRate = playbackRate {
|
||||||
|
videoNode.setBaseRate(playbackRate)
|
||||||
|
}
|
||||||
if isAnimated {
|
if isAnimated {
|
||||||
videoNode.seek(0.0)
|
videoNode.seek(0.0)
|
||||||
videoNode.play()
|
videoNode.play()
|
||||||
@ -2056,17 +2058,27 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
f(.default)
|
f(.default)
|
||||||
|
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let _ = (SaveToCameraRoll.saveToCameraRoll(context: strongSelf.context, postbox: strongSelf.context.account.postbox, mediaReference: .message(message: MessageReference(message), media: file))
|
switch strongSelf.fetchStatus {
|
||||||
|> deliverOnMainQueue).start(completed: {
|
case .Local:
|
||||||
guard let strongSelf = self else {
|
let _ = (SaveToCameraRoll.saveToCameraRoll(context: strongSelf.context, postbox: strongSelf.context.account.postbox, mediaReference: .message(message: MessageReference(message), media: file))
|
||||||
return
|
|> deliverOnMainQueue).start(completed: {
|
||||||
}
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let controller = strongSelf.galleryController() else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//TODO:localize
|
||||||
|
controller.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .mediaSaved(text: "Video Saved"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
|
||||||
|
})
|
||||||
|
default:
|
||||||
guard let controller = strongSelf.galleryController() else {
|
guard let controller = strongSelf.galleryController() else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
controller.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .mediaSaved(text: "Video Saved"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
|
controller.present(textAlertController(context: strongSelf.context, title: nil, text: "Please wait for the video to be fully downloaded.", actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||||
})
|
})]), in: .window(.root))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -312,7 +312,7 @@ public func intentsSettingsController(context: AccountContext) -> ViewController
|
|||||||
let settings = (sharedData.entries[ApplicationSpecificSharedDataKeys.intentsSettings] as? IntentsSettings) ?? IntentsSettings.defaultSettings
|
let settings = (sharedData.entries[ApplicationSpecificSharedDataKeys.intentsSettings] as? IntentsSettings) ?? IntentsSettings.defaultSettings
|
||||||
|
|
||||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.IntentsSettings_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.IntentsSettings_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: intentsSettingsControllerEntries(context: context, presentationData: presentationData, settings: settings, accounts: accounts.1.map { ($0.0, $0.1) }), style: .blocks, animateChanges: false)
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: intentsSettingsControllerEntries(context: context, presentationData: presentationData, settings: settings, accounts: accounts.1.map { ($0.0.account, $0.1) }), style: .blocks, animateChanges: false)
|
||||||
|
|
||||||
return (controllerState, (listState, arguments))
|
return (controllerState, (listState, arguments))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1067,9 +1067,9 @@ public func notificationsAndSoundsController(context: AccountContext, exceptions
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
let hasMoreThanOneAccount = context.sharedContext.activeAccounts
|
let hasMoreThanOneAccount = context.sharedContext.activeAccountContexts
|
||||||
|> map { _, accounts, _ -> Bool in
|
|> map { _, contexts, _ -> Bool in
|
||||||
return accounts.count > 1
|
return contexts.count > 1
|
||||||
}
|
}
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,7 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
|
|||||||
case end
|
case end
|
||||||
case cancel
|
case cancel
|
||||||
case share
|
case share
|
||||||
|
case screencast
|
||||||
}
|
}
|
||||||
|
|
||||||
var appearance: Appearance
|
var appearance: Appearance
|
||||||
@ -299,6 +300,10 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode {
|
|||||||
})
|
})
|
||||||
case .share:
|
case .share:
|
||||||
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallShareButton"), color: imageColor)
|
image = generateTintedImage(image: UIImage(bundleImageName: "Call/CallShareButton"), color: imageColor)
|
||||||
|
case .screencast:
|
||||||
|
if let iconImage = generateTintedImage(image: UIImage(bundleImageName: "Call/ScreenSharePhone"), color: imageColor) {
|
||||||
|
image = generateScaledImage(image: iconImage, size: iconImage.size.aspectFitted(CGSize(width: 38.0, height: 38.0)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let image = image {
|
if let image = image {
|
||||||
|
|||||||
@ -25,6 +25,7 @@ enum CallControllerButtonsMode: Equatable {
|
|||||||
struct VideoState: Equatable {
|
struct VideoState: Equatable {
|
||||||
var isAvailable: Bool
|
var isAvailable: Bool
|
||||||
var isCameraActive: Bool
|
var isCameraActive: Bool
|
||||||
|
var isScreencastActive: Bool
|
||||||
var canChangeStatus: Bool
|
var canChangeStatus: Bool
|
||||||
var hasVideo: Bool
|
var hasVideo: Bool
|
||||||
var isInitializingCamera: Bool
|
var isInitializingCamera: Bool
|
||||||
@ -64,7 +65,7 @@ private enum ButtonDescription: Equatable {
|
|||||||
|
|
||||||
case accept
|
case accept
|
||||||
case end(EndType)
|
case end(EndType)
|
||||||
case enableCamera(Bool, Bool, Bool)
|
case enableCamera(isActive: Bool, isEnabled: Bool, isLoading: Bool, isScreencast: Bool)
|
||||||
case switchCamera(Bool)
|
case switchCamera(Bool)
|
||||||
case soundOutput(SoundOutput)
|
case soundOutput(SoundOutput)
|
||||||
case mute(Bool)
|
case mute(Bool)
|
||||||
@ -224,15 +225,18 @@ final class CallControllerButtonsNode: ASDisplayNode {
|
|||||||
|
|
||||||
if videoState.isAvailable {
|
if videoState.isAvailable {
|
||||||
let isCameraActive: Bool
|
let isCameraActive: Bool
|
||||||
|
let isScreencastActive: Bool
|
||||||
let isCameraInitializing: Bool
|
let isCameraInitializing: Bool
|
||||||
if videoState.hasVideo {
|
if videoState.hasVideo {
|
||||||
isCameraActive = videoState.isCameraActive
|
isCameraActive = videoState.isCameraActive
|
||||||
|
isScreencastActive = videoState.isScreencastActive
|
||||||
isCameraInitializing = videoState.isInitializingCamera
|
isCameraInitializing = videoState.isInitializingCamera
|
||||||
} else {
|
} else {
|
||||||
isCameraActive = false
|
isCameraActive = false
|
||||||
|
isScreencastActive = false
|
||||||
isCameraInitializing = videoState.isInitializingCamera
|
isCameraInitializing = videoState.isInitializingCamera
|
||||||
}
|
}
|
||||||
topButtons.append(.enableCamera(isCameraActive, false, isCameraInitializing))
|
topButtons.append(.enableCamera(isActive: isCameraActive || isScreencastActive, isEnabled: false, isLoading: isCameraInitializing, isScreencast: isScreencastActive))
|
||||||
if !videoState.hasVideo {
|
if !videoState.hasVideo {
|
||||||
topButtons.append(.mute(self.isMuted))
|
topButtons.append(.mute(self.isMuted))
|
||||||
topButtons.append(.soundOutput(soundOutput))
|
topButtons.append(.soundOutput(soundOutput))
|
||||||
@ -242,7 +246,9 @@ final class CallControllerButtonsNode: ASDisplayNode {
|
|||||||
} else {
|
} else {
|
||||||
topButtons.append(.mute(self.isMuted))
|
topButtons.append(.mute(self.isMuted))
|
||||||
}
|
}
|
||||||
topButtons.append(.switchCamera(isCameraActive && !isCameraInitializing))
|
if !isScreencastActive {
|
||||||
|
topButtons.append(.switchCamera(isCameraActive && !isCameraInitializing))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
topButtons.append(.mute(self.isMuted))
|
topButtons.append(.mute(self.isMuted))
|
||||||
@ -280,14 +286,17 @@ final class CallControllerButtonsNode: ASDisplayNode {
|
|||||||
case .active:
|
case .active:
|
||||||
if videoState.hasVideo {
|
if videoState.hasVideo {
|
||||||
let isCameraActive: Bool
|
let isCameraActive: Bool
|
||||||
|
let isScreencastActive: Bool
|
||||||
let isCameraEnabled: Bool
|
let isCameraEnabled: Bool
|
||||||
let isCameraInitializing: Bool
|
let isCameraInitializing: Bool
|
||||||
if videoState.hasVideo {
|
if videoState.hasVideo {
|
||||||
isCameraActive = videoState.isCameraActive
|
isCameraActive = videoState.isCameraActive
|
||||||
|
isScreencastActive = videoState.isScreencastActive
|
||||||
isCameraEnabled = videoState.canChangeStatus
|
isCameraEnabled = videoState.canChangeStatus
|
||||||
isCameraInitializing = videoState.isInitializingCamera
|
isCameraInitializing = videoState.isInitializingCamera
|
||||||
} else {
|
} else {
|
||||||
isCameraActive = false
|
isCameraActive = false
|
||||||
|
isScreencastActive = false
|
||||||
isCameraEnabled = videoState.canChangeStatus
|
isCameraEnabled = videoState.canChangeStatus
|
||||||
isCameraInitializing = videoState.isInitializingCamera
|
isCameraInitializing = videoState.isInitializingCamera
|
||||||
}
|
}
|
||||||
@ -315,13 +324,15 @@ final class CallControllerButtonsNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
topButtons.append(.enableCamera(isCameraActive, isCameraEnabled, isCameraInitializing))
|
topButtons.append(.enableCamera(isActive: isCameraActive || isScreencastActive, isEnabled: isCameraEnabled, isLoading: isCameraInitializing, isScreencast: isScreencastActive))
|
||||||
if hasAudioRouteMenu {
|
if hasAudioRouteMenu {
|
||||||
topButtons.append(.soundOutput(soundOutput))
|
topButtons.append(.soundOutput(soundOutput))
|
||||||
} else {
|
} else {
|
||||||
topButtons.append(.mute(isMuted))
|
topButtons.append(.mute(isMuted))
|
||||||
}
|
}
|
||||||
topButtons.append(.switchCamera(isCameraActive && !isCameraInitializing))
|
if !isScreencastActive {
|
||||||
|
topButtons.append(.switchCamera(isCameraActive && !isCameraInitializing))
|
||||||
|
}
|
||||||
topButtons.append(.end(.end))
|
topButtons.append(.end(.end))
|
||||||
|
|
||||||
let topButtonsContentWidth = CGFloat(topButtons.count) * smallButtonSize
|
let topButtonsContentWidth = CGFloat(topButtons.count) * smallButtonSize
|
||||||
@ -340,14 +351,17 @@ final class CallControllerButtonsNode: ASDisplayNode {
|
|||||||
var bottomButtons: [ButtonDescription] = []
|
var bottomButtons: [ButtonDescription] = []
|
||||||
|
|
||||||
let isCameraActive: Bool
|
let isCameraActive: Bool
|
||||||
|
let isScreencastActive: Bool
|
||||||
let isCameraEnabled: Bool
|
let isCameraEnabled: Bool
|
||||||
let isCameraInitializing: Bool
|
let isCameraInitializing: Bool
|
||||||
if videoState.hasVideo {
|
if videoState.hasVideo {
|
||||||
isCameraActive = videoState.isCameraActive
|
isCameraActive = videoState.isCameraActive
|
||||||
|
isScreencastActive = videoState.isScreencastActive
|
||||||
isCameraEnabled = videoState.canChangeStatus
|
isCameraEnabled = videoState.canChangeStatus
|
||||||
isCameraInitializing = videoState.isInitializingCamera
|
isCameraInitializing = videoState.isInitializingCamera
|
||||||
} else {
|
} else {
|
||||||
isCameraActive = false
|
isCameraActive = false
|
||||||
|
isScreencastActive = false
|
||||||
isCameraEnabled = videoState.canChangeStatus
|
isCameraEnabled = videoState.canChangeStatus
|
||||||
isCameraInitializing = videoState.isInitializingCamera
|
isCameraInitializing = videoState.isInitializingCamera
|
||||||
}
|
}
|
||||||
@ -373,7 +387,7 @@ final class CallControllerButtonsNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
topButtons.append(.enableCamera(isCameraActive, isCameraEnabled, isCameraInitializing))
|
topButtons.append(.enableCamera(isActive: isCameraActive || isScreencastActive, isEnabled: isCameraEnabled, isLoading: isCameraInitializing, isScreencast: isScreencastActive))
|
||||||
topButtons.append(.mute(self.isMuted))
|
topButtons.append(.mute(self.isMuted))
|
||||||
topButtons.append(.soundOutput(soundOutput))
|
topButtons.append(.soundOutput(soundOutput))
|
||||||
|
|
||||||
@ -442,10 +456,10 @@ final class CallControllerButtonsNode: ASDisplayNode {
|
|||||||
case .end:
|
case .end:
|
||||||
buttonText = strings.Call_End
|
buttonText = strings.Call_End
|
||||||
}
|
}
|
||||||
case let .enableCamera(isActivated, isEnabled, isInitializing):
|
case let .enableCamera(isActivated, isEnabled, isInitializing, isScreencastActive):
|
||||||
buttonContent = CallControllerButtonItemNode.Content(
|
buttonContent = CallControllerButtonItemNode.Content(
|
||||||
appearance: .blurred(isFilled: isActivated),
|
appearance: .blurred(isFilled: isActivated),
|
||||||
image: .camera,
|
image: isScreencastActive ? .screencast : .camera,
|
||||||
isEnabled: isEnabled,
|
isEnabled: isEnabled,
|
||||||
hasProgress: isInitializing
|
hasProgress: isInitializing
|
||||||
)
|
)
|
||||||
|
|||||||
@ -569,7 +569,17 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
}
|
}
|
||||||
switch callState.state {
|
switch callState.state {
|
||||||
case .active:
|
case .active:
|
||||||
if strongSelf.outgoingVideoNodeValue == nil {
|
var isScreencastActive = false
|
||||||
|
switch callState.videoState {
|
||||||
|
case .active(true), .paused(true):
|
||||||
|
isScreencastActive = true
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if isScreencastActive {
|
||||||
|
(strongSelf.call as! PresentationCallImpl).disableScreencast()
|
||||||
|
} else if strongSelf.outgoingVideoNodeValue == nil {
|
||||||
DeviceAccess.authorizeAccess(to: .camera(.videoCall), onlyCheck: true, presentationData: strongSelf.presentationData, present: { [weak self] c, a in
|
DeviceAccess.authorizeAccess(to: .camera(.videoCall), onlyCheck: true, presentationData: strongSelf.presentationData, present: { [weak self] c, a in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.present?(c)
|
strongSelf.present?(c)
|
||||||
@ -620,10 +630,8 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
updateLayoutImpl?(layout, navigationBarHeight)
|
updateLayoutImpl?(layout, navigationBarHeight)
|
||||||
})
|
})
|
||||||
|
|
||||||
let controller = VoiceChatCameraPreviewController(sharedContext: strongSelf.sharedContext, cameraNode: outgoingVideoNode, shareCamera: { [weak self] _, _ in
|
let controller = VoiceChatCameraPreviewController(sharedContext: strongSelf.sharedContext, cameraNode: outgoingVideoNode, shareCamera: { _, _ in
|
||||||
if let strongSelf = self {
|
proceed()
|
||||||
proceed()
|
|
||||||
}
|
|
||||||
}, switchCamera: { [weak self] in
|
}, switchCamera: { [weak self] in
|
||||||
Queue.mainQueue().after(0.1) {
|
Queue.mainQueue().after(0.1) {
|
||||||
self?.call.switchVideoCamera()
|
self?.call.switchVideoCamera()
|
||||||
@ -636,14 +644,6 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// if strongSelf.displayedCameraConfirmation {
|
|
||||||
// proceed()
|
|
||||||
// } else {
|
|
||||||
// strongSelf.present?(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: strongSelf.presentationData.strings.Call_CameraConfirmationText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Call_CameraConfirmationConfirm, action: {
|
|
||||||
// proceed()
|
|
||||||
// })]))
|
|
||||||
// }
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
strongSelf.call.disableVideo()
|
strongSelf.call.disableVideo()
|
||||||
@ -716,7 +716,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.present?(TooltipScreen(text: self.presentationData.strings.Call_CameraTooltip, style: .light, icon: nil, location: .point(location.offsetBy(dx: 0.0, dy: -14.0), .bottom), displayDuration: .custom(5.0), shouldDismissOnTouch: { _ in
|
self.present?(TooltipScreen(text: self.presentationData.strings.Call_CameraOrScreenTooltip, style: .light, icon: nil, location: .point(location.offsetBy(dx: 0.0, dy: -14.0), .bottom), displayDuration: .custom(5.0), shouldDismissOnTouch: { _ in
|
||||||
return .dismiss(consume: false)
|
return .dismiss(consume: false)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -877,7 +877,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch callState.videoState {
|
switch callState.videoState {
|
||||||
case .active, .paused:
|
case .active(false), .paused(false):
|
||||||
if !self.outgoingVideoViewRequested {
|
if !self.outgoingVideoViewRequested {
|
||||||
self.outgoingVideoViewRequested = true
|
self.outgoingVideoViewRequested = true
|
||||||
let delayUntilInitialized = self.isRequestingVideo
|
let delayUntilInitialized = self.isRequestingVideo
|
||||||
@ -959,7 +959,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
case .notAvailable, .inactive:
|
default:
|
||||||
self.candidateOutgoingVideoNodeValue = nil
|
self.candidateOutgoingVideoNodeValue = nil
|
||||||
if let outgoingVideoNodeValue = self.outgoingVideoNodeValue {
|
if let outgoingVideoNodeValue = self.outgoingVideoNodeValue {
|
||||||
if self.minimizedVideoNode == outgoingVideoNodeValue {
|
if self.minimizedVideoNode == outgoingVideoNodeValue {
|
||||||
@ -1256,16 +1256,20 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
mode = .none
|
mode = .none
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var mappedVideoState = CallControllerButtonsMode.VideoState(isAvailable: false, isCameraActive: self.outgoingVideoNodeValue != nil, canChangeStatus: false, hasVideo: self.outgoingVideoNodeValue != nil || self.incomingVideoNodeValue != nil, isInitializingCamera: self.isRequestingVideo)
|
var mappedVideoState = CallControllerButtonsMode.VideoState(isAvailable: false, isCameraActive: self.outgoingVideoNodeValue != nil, isScreencastActive: false, canChangeStatus: false, hasVideo: self.outgoingVideoNodeValue != nil || self.incomingVideoNodeValue != nil, isInitializingCamera: self.isRequestingVideo)
|
||||||
switch callState.videoState {
|
switch callState.videoState {
|
||||||
case .notAvailable:
|
case .notAvailable:
|
||||||
break
|
break
|
||||||
case .inactive:
|
case .inactive:
|
||||||
mappedVideoState.isAvailable = true
|
mappedVideoState.isAvailable = true
|
||||||
mappedVideoState.canChangeStatus = true
|
mappedVideoState.canChangeStatus = true
|
||||||
case .active, .paused:
|
case .active(let isScreencast), .paused(let isScreencast):
|
||||||
mappedVideoState.isAvailable = true
|
mappedVideoState.isAvailable = true
|
||||||
mappedVideoState.canChangeStatus = true
|
mappedVideoState.canChangeStatus = true
|
||||||
|
if isScreencast {
|
||||||
|
mappedVideoState.isScreencastActive = true
|
||||||
|
mappedVideoState.hasVideo = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch callState.state {
|
switch callState.state {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import Postbox
|
|||||||
import TelegramCore
|
import TelegramCore
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import AppBundle
|
import AppBundle
|
||||||
|
import AccountContext
|
||||||
|
|
||||||
private var sharedProviderDelegate: AnyObject?
|
private var sharedProviderDelegate: AnyObject?
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ public final class CallKitIntegration {
|
|||||||
return self.audioSessionActivePromise.get()
|
return self.audioSessionActivePromise.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
init?(startCall: @escaping (Account, UUID, String, Bool) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
|
init?(startCall: @escaping (AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
|
||||||
if !CallKitIntegration.isAvailable {
|
if !CallKitIntegration.isAvailable {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -48,9 +49,9 @@ public final class CallKitIntegration {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func startCall(account: Account, peerId: PeerId, isVideo: Bool, displayTitle: String) {
|
func startCall(context: AccountContext, peerId: PeerId, isVideo: Bool, displayTitle: String) {
|
||||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||||
(sharedProviderDelegate as? CallKitProviderDelegate)?.startCall(account: account, peerId: peerId, isVideo: isVideo, displayTitle: displayTitle)
|
(sharedProviderDelegate as? CallKitProviderDelegate)?.startCall(context: context, peerId: peerId, isVideo: isVideo, displayTitle: displayTitle)
|
||||||
self.donateIntent(peerId: peerId, displayTitle: displayTitle)
|
self.donateIntent(peerId: peerId, displayTitle: displayTitle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,9 +100,9 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
|||||||
private let provider: CXProvider
|
private let provider: CXProvider
|
||||||
private let callController = CXCallController()
|
private let callController = CXCallController()
|
||||||
|
|
||||||
private var currentStartCallAccount: (UUID, Account)?
|
private var currentStartCallAccount: (UUID, AccountContext)?
|
||||||
|
|
||||||
private var startCall: ((Account, UUID, String, Bool) -> Signal<Bool, NoError>)?
|
private var startCall: ((AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>)?
|
||||||
private var answerCall: ((UUID) -> Void)?
|
private var answerCall: ((UUID) -> Void)?
|
||||||
private var endCall: ((UUID) -> Signal<Bool, NoError>)?
|
private var endCall: ((UUID) -> Signal<Bool, NoError>)?
|
||||||
private var setCallMuted: ((UUID, Bool) -> Void)?
|
private var setCallMuted: ((UUID, Bool) -> Void)?
|
||||||
@ -119,7 +120,7 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
|||||||
self.provider.setDelegate(self, queue: nil)
|
self.provider.setDelegate(self, queue: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(audioSessionActivePromise: ValuePromise<Bool>, startCall: @escaping (Account, UUID, String, Bool) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
|
func setup(audioSessionActivePromise: ValuePromise<Bool>, startCall: @escaping (AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
|
||||||
self.audioSessionActivePromise = audioSessionActivePromise
|
self.audioSessionActivePromise = audioSessionActivePromise
|
||||||
self.startCall = startCall
|
self.startCall = startCall
|
||||||
self.answerCall = answerCall
|
self.answerCall = answerCall
|
||||||
@ -165,9 +166,9 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func startCall(account: Account, peerId: PeerId, isVideo: Bool, displayTitle: String) {
|
func startCall(context: AccountContext, peerId: PeerId, isVideo: Bool, displayTitle: String) {
|
||||||
let uuid = UUID()
|
let uuid = UUID()
|
||||||
self.currentStartCallAccount = (uuid, account)
|
self.currentStartCallAccount = (uuid, context)
|
||||||
let handle = CXHandle(type: .generic, value: "\(peerId.id._internalGetInt32Value())")
|
let handle = CXHandle(type: .generic, value: "\(peerId.id._internalGetInt32Value())")
|
||||||
let startCallAction = CXStartCallAction(call: uuid, handle: handle)
|
let startCallAction = CXStartCallAction(call: uuid, handle: handle)
|
||||||
startCallAction.contactIdentifier = displayTitle
|
startCallAction.contactIdentifier = displayTitle
|
||||||
@ -215,14 +216,14 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
|
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
|
||||||
guard let startCall = self.startCall, let (uuid, account) = self.currentStartCallAccount, uuid == action.callUUID else {
|
guard let startCall = self.startCall, let (uuid, context) = self.currentStartCallAccount, uuid == action.callUUID else {
|
||||||
action.fail()
|
action.fail()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.currentStartCallAccount = nil
|
self.currentStartCallAccount = nil
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
self.disposableSet.add(disposable)
|
self.disposableSet.add(disposable)
|
||||||
disposable.set((startCall(account, action.callUUID, action.handle.value, action.isVideo)
|
disposable.set((startCall(context, action.callUUID, action.handle.value, action.isVideo)
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> afterDisposed { [weak self, weak disposable] in
|
|> afterDisposed { [weak self, weak disposable] in
|
||||||
if let strongSelf = self, let disposable = disposable {
|
if let strongSelf = self, let disposable = disposable {
|
||||||
|
|||||||
@ -186,7 +186,7 @@ final class PresentationCallToneRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class PresentationCallImpl: PresentationCall {
|
public final class PresentationCallImpl: PresentationCall {
|
||||||
public let account: Account
|
public let context: AccountContext
|
||||||
private let audioSession: ManagedAudioSession
|
private let audioSession: ManagedAudioSession
|
||||||
private let callSessionManager: CallSessionManager
|
private let callSessionManager: CallSessionManager
|
||||||
private let callKitIntegration: CallKitIntegration?
|
private let callKitIntegration: CallKitIntegration?
|
||||||
@ -286,11 +286,19 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
|
|
||||||
private var useFrontCamera: Bool = true
|
private var useFrontCamera: Bool = true
|
||||||
private var videoCapturer: OngoingCallVideoCapturer?
|
private var videoCapturer: OngoingCallVideoCapturer?
|
||||||
|
|
||||||
|
private var screencastBufferServerContext: IpcGroupCallBufferAppContext?
|
||||||
|
private var screencastCapturer: OngoingCallVideoCapturer?
|
||||||
|
private var isScreencastActive: Bool = false
|
||||||
|
|
||||||
private var proximityManagerIndex: Int?
|
private var proximityManagerIndex: Int?
|
||||||
|
|
||||||
|
private let screencastFramesDisposable = MetaDisposable()
|
||||||
|
private let screencastAudioDataDisposable = MetaDisposable()
|
||||||
|
private let screencastStateDisposable = MetaDisposable()
|
||||||
|
|
||||||
init(
|
init(
|
||||||
account: Account,
|
context: AccountContext,
|
||||||
audioSession: ManagedAudioSession,
|
audioSession: ManagedAudioSession,
|
||||||
callSessionManager: CallSessionManager,
|
callSessionManager: CallSessionManager,
|
||||||
callKitIntegration: CallKitIntegration?,
|
callKitIntegration: CallKitIntegration?,
|
||||||
@ -313,7 +321,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
enableTCP: Bool,
|
enableTCP: Bool,
|
||||||
preferredVideoCodec: String?
|
preferredVideoCodec: String?
|
||||||
) {
|
) {
|
||||||
self.account = account
|
self.context = context
|
||||||
self.audioSession = audioSession
|
self.audioSession = audioSession
|
||||||
self.callSessionManager = callSessionManager
|
self.callSessionManager = callSessionManager
|
||||||
self.callKitIntegration = callKitIntegration
|
self.callKitIntegration = callKitIntegration
|
||||||
@ -345,7 +353,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
self.isVideo = startWithVideo
|
self.isVideo = startWithVideo
|
||||||
if self.isVideo {
|
if self.isVideo {
|
||||||
self.videoCapturer = OngoingCallVideoCapturer()
|
self.videoCapturer = OngoingCallVideoCapturer()
|
||||||
self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: .active, remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal))
|
self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: .active(isScreencast: self.isScreencastActive), remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal))
|
||||||
} else {
|
} else {
|
||||||
self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: self.isVideoPossible ? .inactive : .notAvailable, remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal))
|
self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: self.isVideoPossible ? .inactive : .notAvailable, remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal))
|
||||||
}
|
}
|
||||||
@ -457,6 +465,11 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
strongSelf.updateIsAudioSessionActive(value)
|
strongSelf.updateIsAudioSessionActive(value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let screencastCapturer = OngoingCallVideoCapturer(isCustom: true)
|
||||||
|
self.screencastCapturer = screencastCapturer
|
||||||
|
|
||||||
|
self.resetScreencastContext()
|
||||||
|
|
||||||
if callKitIntegration == nil {
|
if callKitIntegration == nil {
|
||||||
self.proximityManagerIndex = DeviceProximityManager.shared().add { _ in
|
self.proximityManagerIndex = DeviceProximityManager.shared().add { _ in
|
||||||
@ -473,6 +486,9 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
self.audioLevelDisposable?.dispose()
|
self.audioLevelDisposable?.dispose()
|
||||||
self.batteryLevelDisposable?.dispose()
|
self.batteryLevelDisposable?.dispose()
|
||||||
self.audioSessionDisposable?.dispose()
|
self.audioSessionDisposable?.dispose()
|
||||||
|
self.screencastFramesDisposable.dispose()
|
||||||
|
self.screencastAudioDataDisposable.dispose()
|
||||||
|
self.screencastStateDisposable.dispose()
|
||||||
|
|
||||||
if let dropCallKitCallTimer = self.dropCallKitCallTimer {
|
if let dropCallKitCallTimer = self.dropCallKitCallTimer {
|
||||||
dropCallKitCallTimer.invalidate()
|
dropCallKitCallTimer.invalidate()
|
||||||
@ -530,11 +546,11 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
case .notAvailable:
|
case .notAvailable:
|
||||||
mappedVideoState = .notAvailable
|
mappedVideoState = .notAvailable
|
||||||
case .active:
|
case .active:
|
||||||
mappedVideoState = .active
|
mappedVideoState = .active(isScreencast: self.isScreencastActive)
|
||||||
case .inactive:
|
case .inactive:
|
||||||
mappedVideoState = .inactive
|
mappedVideoState = .inactive
|
||||||
case .paused:
|
case .paused:
|
||||||
mappedVideoState = .paused
|
mappedVideoState = .paused(isScreencast: self.isScreencastActive)
|
||||||
}
|
}
|
||||||
switch callContextState.remoteVideoState {
|
switch callContextState.remoteVideoState {
|
||||||
case .inactive:
|
case .inactive:
|
||||||
@ -565,7 +581,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
mappedVideoState = previousVideoState
|
mappedVideoState = previousVideoState
|
||||||
} else {
|
} else {
|
||||||
if self.isVideo {
|
if self.isVideo {
|
||||||
mappedVideoState = .active
|
mappedVideoState = .active(isScreencast: self.isScreencastActive)
|
||||||
} else if self.isVideoPossible && sessionState.isVideoPossible {
|
} else if self.isVideoPossible && sessionState.isVideoPossible {
|
||||||
mappedVideoState = .inactive
|
mappedVideoState = .inactive
|
||||||
} else {
|
} else {
|
||||||
@ -664,7 +680,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
if let _ = audioSessionControl, !wasActive || previousControl == nil {
|
if let _ = audioSessionControl, !wasActive || previousControl == nil {
|
||||||
let logName = "\(id.id)_\(id.accessHash)"
|
let logName = "\(id.id)_\(id.accessHash)"
|
||||||
|
|
||||||
let ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: connections, maxLayer: maxLayer, version: version, allowP2P: allowsP2P, enableTCP: self.enableTCP, enableStunMarking: self.enableStunMarking, audioSessionActive: self.audioSessionActive.get(), logName: logName, preferredVideoCodec: self.preferredVideoCodec)
|
let ongoingContext = OngoingCallContext(account: self.context.account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: connections, maxLayer: maxLayer, version: version, allowP2P: allowsP2P, enableTCP: self.enableTCP, enableStunMarking: self.enableStunMarking, audioSessionActive: self.audioSessionActive.get(), logName: logName, preferredVideoCodec: self.preferredVideoCodec)
|
||||||
self.ongoingContext = ongoingContext
|
self.ongoingContext = ongoingContext
|
||||||
ongoingContext.setIsMuted(self.isMutedValue)
|
ongoingContext.setIsMuted(self.isMutedValue)
|
||||||
if let requestedVideoAspect = self.requestedVideoAspect {
|
if let requestedVideoAspect = self.requestedVideoAspect {
|
||||||
@ -927,6 +943,61 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
self.ongoingContext?.disableVideo()
|
self.ongoingContext?.disableVideo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func resetScreencastContext() {
|
||||||
|
let basePath = self.context.sharedContext.basePath + "/broadcast-coordination"
|
||||||
|
let screencastBufferServerContext = IpcGroupCallBufferAppContext(basePath: basePath)
|
||||||
|
self.screencastBufferServerContext = screencastBufferServerContext
|
||||||
|
|
||||||
|
self.screencastFramesDisposable.set((screencastBufferServerContext.frames
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak screencastCapturer] screencastFrame in
|
||||||
|
guard let screencastCapturer = screencastCapturer else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
screencastCapturer.injectPixelBuffer(screencastFrame.0, rotation: screencastFrame.1)
|
||||||
|
}))
|
||||||
|
self.screencastAudioDataDisposable.set((screencastBufferServerContext.audioData
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] data in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.ongoingContext?.addExternalAudioData(data: data)
|
||||||
|
}))
|
||||||
|
self.screencastStateDisposable.set((screencastBufferServerContext.isActive
|
||||||
|
|> distinctUntilChanged
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] isActive in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if isActive {
|
||||||
|
strongSelf.requestScreencast()
|
||||||
|
} else {
|
||||||
|
strongSelf.disableScreencast(reset: false)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func requestScreencast() {
|
||||||
|
self.disableVideo()
|
||||||
|
|
||||||
|
if let screencastCapturer = self.screencastCapturer {
|
||||||
|
self.isScreencastActive = true
|
||||||
|
self.ongoingContext?.requestVideo(screencastCapturer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func disableScreencast(reset: Bool = true) {
|
||||||
|
if self.isScreencastActive {
|
||||||
|
if let _ = self.videoCapturer {
|
||||||
|
self.videoCapturer = nil
|
||||||
|
}
|
||||||
|
self.isScreencastActive = false
|
||||||
|
self.ongoingContext?.disableVideo()
|
||||||
|
if reset {
|
||||||
|
self.resetScreencastContext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func setOutgoingVideoIsPaused(_ isPaused: Bool) {
|
public func setOutgoingVideoIsPaused(_ isPaused: Bool) {
|
||||||
self.videoCapturer?.setIsVideoEnabled(!isPaused)
|
self.videoCapturer?.setIsVideoEnabled(!isPaused)
|
||||||
|
|||||||
@ -113,7 +113,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
return OngoingCallContext.versions(includeExperimental: includeExperimental, includeReference: includeReference)
|
return OngoingCallContext.versions(includeExperimental: includeExperimental, includeReference: includeReference)
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(accountManager: AccountManager, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), isMediaPlaying: @escaping () -> Bool, resumeMediaPlayback: @escaping () -> Void, audioSession: ManagedAudioSession, activeAccounts: Signal<[Account], NoError>) {
|
public init(accountManager: AccountManager, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), isMediaPlaying: @escaping () -> Bool, resumeMediaPlayback: @escaping () -> Void, audioSession: ManagedAudioSession, activeAccounts: Signal<[AccountContext], NoError>) {
|
||||||
self.getDeviceAccessData = getDeviceAccessData
|
self.getDeviceAccessData = getDeviceAccessData
|
||||||
self.accountManager = accountManager
|
self.accountManager = accountManager
|
||||||
self.audioSession = audioSession
|
self.audioSession = audioSession
|
||||||
@ -121,15 +121,15 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
self.isMediaPlaying = isMediaPlaying
|
self.isMediaPlaying = isMediaPlaying
|
||||||
self.resumeMediaPlayback = resumeMediaPlayback
|
self.resumeMediaPlayback = resumeMediaPlayback
|
||||||
|
|
||||||
var startCallImpl: ((Account, UUID, String, Bool) -> Signal<Bool, NoError>)?
|
var startCallImpl: ((AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>)?
|
||||||
var answerCallImpl: ((UUID) -> Void)?
|
var answerCallImpl: ((UUID) -> Void)?
|
||||||
var endCallImpl: ((UUID) -> Signal<Bool, NoError>)?
|
var endCallImpl: ((UUID) -> Signal<Bool, NoError>)?
|
||||||
var setCallMutedImpl: ((UUID, Bool) -> Void)?
|
var setCallMutedImpl: ((UUID, Bool) -> Void)?
|
||||||
var audioSessionActivationChangedImpl: ((Bool) -> Void)?
|
var audioSessionActivationChangedImpl: ((Bool) -> Void)?
|
||||||
|
|
||||||
self.callKitIntegration = CallKitIntegration(startCall: { account, uuid, handle, isVideo in
|
self.callKitIntegration = CallKitIntegration(startCall: { context, uuid, handle, isVideo in
|
||||||
if let startCallImpl = startCallImpl {
|
if let startCallImpl = startCallImpl {
|
||||||
return startCallImpl(account, uuid, handle, isVideo)
|
return startCallImpl(context, uuid, handle, isVideo)
|
||||||
} else {
|
} else {
|
||||||
return .single(false)
|
return .single(false)
|
||||||
}
|
}
|
||||||
@ -161,19 +161,19 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
}
|
}
|
||||||
|> runOn(Queue.mainQueue())
|
|> runOn(Queue.mainQueue())
|
||||||
|
|
||||||
let ringingStatesByAccount: Signal<[(Account, CallSessionRingingState, NetworkType)], NoError> = activeAccounts
|
let ringingStatesByAccount: Signal<[(AccountContext, CallSessionRingingState, NetworkType)], NoError> = activeAccounts
|
||||||
|> mapToSignal { accounts -> Signal<[(Account, CallSessionRingingState, NetworkType)], NoError> in
|
|> mapToSignal { accounts -> Signal<[(AccountContext, CallSessionRingingState, NetworkType)], NoError> in
|
||||||
return combineLatest(accounts.map { account -> Signal<(Account, [CallSessionRingingState], NetworkType), NoError> in
|
return combineLatest(accounts.map { context -> Signal<(AccountContext, [CallSessionRingingState], NetworkType), NoError> in
|
||||||
return combineLatest(account.callSessionManager.ringingStates(), account.networkType)
|
return combineLatest(context.account.callSessionManager.ringingStates(), context.account.networkType)
|
||||||
|> map { ringingStates, networkType -> (Account, [CallSessionRingingState], NetworkType) in
|
|> map { ringingStates, networkType -> (AccountContext, [CallSessionRingingState], NetworkType) in
|
||||||
return (account, ringingStates, networkType)
|
return (context, ringingStates, networkType)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|> map { ringingStatesByAccount -> [(Account, CallSessionRingingState, NetworkType)] in
|
|> map { ringingStatesByAccount -> [(AccountContext, CallSessionRingingState, NetworkType)] in
|
||||||
var result: [(Account, CallSessionRingingState, NetworkType)] = []
|
var result: [(AccountContext, CallSessionRingingState, NetworkType)] = []
|
||||||
for (account, states, networkType) in ringingStatesByAccount {
|
for (context, states, networkType) in ringingStatesByAccount {
|
||||||
for state in states {
|
for state in states {
|
||||||
result.append((account, state, networkType))
|
result.append((context, state, networkType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@ -181,20 +181,20 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.ringingStatesDisposable = (combineLatest(ringingStatesByAccount, enableCallKit, enabledMicrophoneAccess)
|
self.ringingStatesDisposable = (combineLatest(ringingStatesByAccount, enableCallKit, enabledMicrophoneAccess)
|
||||||
|> mapToSignal { ringingStatesByAccount, enableCallKit, enabledMicrophoneAccess -> Signal<([(Account, Peer, CallSessionRingingState, Bool, NetworkType)], Bool), NoError> in
|
|> mapToSignal { ringingStatesByAccount, enableCallKit, enabledMicrophoneAccess -> Signal<([(AccountContext, Peer, CallSessionRingingState, Bool, NetworkType)], Bool), NoError> in
|
||||||
if ringingStatesByAccount.isEmpty {
|
if ringingStatesByAccount.isEmpty {
|
||||||
return .single(([], enableCallKit && enabledMicrophoneAccess))
|
return .single(([], enableCallKit && enabledMicrophoneAccess))
|
||||||
} else {
|
} else {
|
||||||
return combineLatest(ringingStatesByAccount.map { account, state, networkType -> Signal<(Account, Peer, CallSessionRingingState, Bool, NetworkType)?, NoError> in
|
return combineLatest(ringingStatesByAccount.map { context, state, networkType -> Signal<(AccountContext, Peer, CallSessionRingingState, Bool, NetworkType)?, NoError> in
|
||||||
return account.postbox.transaction { transaction -> (Account, Peer, CallSessionRingingState, Bool, NetworkType)? in
|
return context.account.postbox.transaction { transaction -> (AccountContext, Peer, CallSessionRingingState, Bool, NetworkType)? in
|
||||||
if let peer = transaction.getPeer(state.peerId) {
|
if let peer = transaction.getPeer(state.peerId) {
|
||||||
return (account, peer, state, transaction.isPeerContact(peerId: state.peerId), networkType)
|
return (context, peer, state, transaction.isPeerContact(peerId: state.peerId), networkType)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|> map { ringingStatesByAccount -> ([(Account, Peer, CallSessionRingingState, Bool, NetworkType)], Bool) in
|
|> map { ringingStatesByAccount -> ([(AccountContext, Peer, CallSessionRingingState, Bool, NetworkType)], Bool) in
|
||||||
return (ringingStatesByAccount.compactMap({ $0 }), enableCallKit && enabledMicrophoneAccess)
|
return (ringingStatesByAccount.compactMap({ $0 }), enableCallKit && enabledMicrophoneAccess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,9 +203,9 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
self?.ringingStatesUpdated(ringingStates, enableCallKit: enableCallKit)
|
self?.ringingStatesUpdated(ringingStates, enableCallKit: enableCallKit)
|
||||||
})
|
})
|
||||||
|
|
||||||
startCallImpl = { [weak self] account, uuid, handle, isVideo in
|
startCallImpl = { [weak self] context, uuid, handle, isVideo in
|
||||||
if let strongSelf = self, let userId = Int32(handle) {
|
if let strongSelf = self, let userId = Int32(handle) {
|
||||||
return strongSelf.startCall(account: account, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt32Value(userId)), isVideo: isVideo, internalId: uuid)
|
return strongSelf.startCall(context: context, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt32Value(userId)), isVideo: isVideo, internalId: uuid)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> map { result -> Bool in
|
|> map { result -> Bool in
|
||||||
return result
|
return result
|
||||||
@ -272,10 +272,10 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
self.callSettingsDisposable?.dispose()
|
self.callSettingsDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func ringingStatesUpdated(_ ringingStates: [(Account, Peer, CallSessionRingingState, Bool, NetworkType)], enableCallKit: Bool) {
|
private func ringingStatesUpdated(_ ringingStates: [(AccountContext, Peer, CallSessionRingingState, Bool, NetworkType)], enableCallKit: Bool) {
|
||||||
if let firstState = ringingStates.first {
|
if let firstState = ringingStates.first {
|
||||||
if self.currentCall == nil && self.currentGroupCall == nil {
|
if self.currentCall == nil && self.currentGroupCall == nil {
|
||||||
self.currentCallDisposable.set((combineLatest(firstState.0.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, ApplicationSpecificPreferencesKeys.voipDerivedState, PreferencesKeys.appConfiguration]) |> take(1), accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]) |> take(1))
|
self.currentCallDisposable.set((combineLatest(firstState.0.account.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, ApplicationSpecificPreferencesKeys.voipDerivedState, PreferencesKeys.appConfiguration]) |> take(1), accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]) |> take(1))
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] preferences, sharedData in
|
|> deliverOnMainQueue).start(next: { [weak self] preferences, sharedData in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -288,9 +288,9 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
let appConfiguration = preferences.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? AppConfiguration.defaultValue
|
let appConfiguration = preferences.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? AppConfiguration.defaultValue
|
||||||
|
|
||||||
let call = PresentationCallImpl(
|
let call = PresentationCallImpl(
|
||||||
account: firstState.0,
|
context: firstState.0,
|
||||||
audioSession: strongSelf.audioSession,
|
audioSession: strongSelf.audioSession,
|
||||||
callSessionManager: firstState.0.callSessionManager,
|
callSessionManager: firstState.0.account.callSessionManager,
|
||||||
callKitIntegration: enableCallKit ? callKitIntegrationIfEnabled(strongSelf.callKitIntegration, settings: strongSelf.callSettings) : nil,
|
callKitIntegration: enableCallKit ? callKitIntegrationIfEnabled(strongSelf.callKitIntegration, settings: strongSelf.callSettings) : nil,
|
||||||
serializedData: configuration.serializedData,
|
serializedData: configuration.serializedData,
|
||||||
dataSaving: effectiveDataSaving(for: strongSelf.callSettings, autodownloadSettings: autodownloadSettings),
|
dataSaving: effectiveDataSaving(for: strongSelf.callSettings, autodownloadSettings: autodownloadSettings),
|
||||||
@ -304,7 +304,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
proxyServer: strongSelf.proxyServer,
|
proxyServer: strongSelf.proxyServer,
|
||||||
auxiliaryServers: [],
|
auxiliaryServers: [],
|
||||||
currentNetworkType: firstState.4,
|
currentNetworkType: firstState.4,
|
||||||
updatedNetworkType: firstState.0.networkType,
|
updatedNetworkType: firstState.0.account.networkType,
|
||||||
startWithVideo: firstState.2.isVideo,
|
startWithVideo: firstState.2.isVideo,
|
||||||
isVideoPossible: firstState.2.isVideoPossible,
|
isVideoPossible: firstState.2.isVideoPossible,
|
||||||
enableStunMarking: shouldEnableStunMarking(appConfiguration: appConfiguration),
|
enableStunMarking: shouldEnableStunMarking(appConfiguration: appConfiguration),
|
||||||
@ -326,9 +326,9 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
}))
|
}))
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
for (account, _, state, _, _) in ringingStates {
|
for (context, _, state, _, _) in ringingStates {
|
||||||
if state.id != self.currentCall?.internalId {
|
if state.id != self.currentCall?.internalId {
|
||||||
account.callSessionManager.drop(internalId: state.id, reason: .busy, debugLog: .single(nil))
|
context.account.callSessionManager.drop(internalId: state.id, reason: .busy, debugLog: .single(nil))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,8 +336,6 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func requestCall(context: AccountContext, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult {
|
public func requestCall(context: AccountContext, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult {
|
||||||
let account = context.account
|
|
||||||
|
|
||||||
var alreadyInCall: Bool = false
|
var alreadyInCall: Bool = false
|
||||||
var alreadyInCallWithPeerId: PeerId?
|
var alreadyInCallWithPeerId: PeerId?
|
||||||
|
|
||||||
@ -388,7 +386,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
return EmptyDisposable
|
return EmptyDisposable
|
||||||
}
|
}
|
||||||
|> runOn(Queue.mainQueue())
|
|> runOn(Queue.mainQueue())
|
||||||
let postbox = account.postbox
|
let postbox = context.account.postbox
|
||||||
strongSelf.startCallDisposable.set((accessEnabledSignal
|
strongSelf.startCallDisposable.set((accessEnabledSignal
|
||||||
|> mapToSignal { accessEnabled -> Signal<Peer?, NoError> in
|
|> mapToSignal { accessEnabled -> Signal<Peer?, NoError> in
|
||||||
if !accessEnabled {
|
if !accessEnabled {
|
||||||
@ -402,7 +400,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
guard let strongSelf = self, let peer = peer else {
|
guard let strongSelf = self, let peer = peer else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.callKitIntegration?.startCall(account: account, peerId: peerId, isVideo: isVideo, displayTitle: peer.debugDisplayTitle)
|
strongSelf.callKitIntegration?.startCall(context: context, peerId: peerId, isVideo: isVideo, displayTitle: peer.debugDisplayTitle)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
if let currentCall = self.currentCall {
|
if let currentCall = self.currentCall {
|
||||||
@ -426,7 +424,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = strongSelf.startCall(account: account, peerId: peerId, isVideo: isVideo).start()
|
let _ = strongSelf.startCall(context: context, peerId: peerId, isVideo: isVideo).start()
|
||||||
}
|
}
|
||||||
if let currentCall = self.currentCall {
|
if let currentCall = self.currentCall {
|
||||||
self.startCallDisposable.set((currentCall.hangUp()
|
self.startCallDisposable.set((currentCall.hangUp()
|
||||||
@ -448,7 +446,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func startCall(
|
private func startCall(
|
||||||
account: Account,
|
context: AccountContext,
|
||||||
peerId: PeerId,
|
peerId: PeerId,
|
||||||
isVideo: Bool,
|
isVideo: Bool,
|
||||||
internalId: CallSessionInternalId = CallSessionInternalId()
|
internalId: CallSessionInternalId = CallSessionInternalId()
|
||||||
@ -479,7 +477,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
}
|
}
|
||||||
|> runOn(Queue.mainQueue())
|
|> runOn(Queue.mainQueue())
|
||||||
|
|
||||||
let networkType = account.networkType
|
let networkType = context.account.networkType
|
||||||
let accountManager = self.accountManager
|
let accountManager = self.accountManager
|
||||||
return accessEnabledSignal
|
return accessEnabledSignal
|
||||||
|> mapToSignal { [weak self] accessEnabled -> Signal<Bool, NoError> in
|
|> mapToSignal { [weak self] accessEnabled -> Signal<Bool, NoError> in
|
||||||
@ -487,7 +485,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
return .single(false)
|
return .single(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
let request = account.postbox.transaction { transaction -> (VideoCallsConfiguration, CachedUserData?) in
|
let request = context.account.postbox.transaction { transaction -> (VideoCallsConfiguration, CachedUserData?) in
|
||||||
let appConfiguration: AppConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration) as? AppConfiguration ?? AppConfiguration.defaultValue
|
let appConfiguration: AppConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration) as? AppConfiguration ?? AppConfiguration.defaultValue
|
||||||
return (VideoCallsConfiguration(appConfiguration: appConfiguration), transaction.getPeerCachedData(peerId: peerId) as? CachedUserData)
|
return (VideoCallsConfiguration(appConfiguration: appConfiguration), transaction.getPeerCachedData(peerId: peerId) as? CachedUserData)
|
||||||
}
|
}
|
||||||
@ -506,16 +504,16 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
isVideoPossible = false
|
isVideoPossible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
return account.callSessionManager.request(peerId: peerId, isVideo: isVideo, enableVideo: isVideoPossible, internalId: internalId)
|
return context.account.callSessionManager.request(peerId: peerId, isVideo: isVideo, enableVideo: isVideoPossible, internalId: internalId)
|
||||||
}
|
}
|
||||||
|
|
||||||
let cachedUserData = account.postbox.transaction { transaction -> CachedUserData? in
|
let cachedUserData = context.account.postbox.transaction { transaction -> CachedUserData? in
|
||||||
return transaction.getPeerCachedData(peerId: peerId) as? CachedUserData
|
return transaction.getPeerCachedData(peerId: peerId) as? CachedUserData
|
||||||
}
|
}
|
||||||
|
|
||||||
return (combineLatest(queue: .mainQueue(), request, networkType |> take(1), account.postbox.peerView(id: peerId) |> map { peerView -> Bool in
|
return (combineLatest(queue: .mainQueue(), request, networkType |> take(1), context.account.postbox.peerView(id: peerId) |> map { peerView -> Bool in
|
||||||
return peerView.peerIsContact
|
return peerView.peerIsContact
|
||||||
} |> take(1), account.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, ApplicationSpecificPreferencesKeys.voipDerivedState, PreferencesKeys.appConfiguration]) |> take(1), accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]) |> take(1), cachedUserData)
|
} |> take(1), context.account.postbox.preferencesView(keys: [PreferencesKeys.voipConfiguration, ApplicationSpecificPreferencesKeys.voipDerivedState, PreferencesKeys.appConfiguration]) |> take(1), accountManager.sharedData(keys: [SharedDataKeys.autodownloadSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings]) |> take(1), cachedUserData)
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> beforeNext { internalId, currentNetworkType, isContact, preferences, sharedData, cachedUserData in
|
|> beforeNext { internalId, currentNetworkType, isContact, preferences, sharedData, cachedUserData in
|
||||||
if let strongSelf = self, accessEnabled {
|
if let strongSelf = self, accessEnabled {
|
||||||
@ -546,9 +544,9 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
let experimentalSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.experimentalUISettings] as? ExperimentalUISettings ?? .defaultSettings
|
let experimentalSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.experimentalUISettings] as? ExperimentalUISettings ?? .defaultSettings
|
||||||
|
|
||||||
let call = PresentationCallImpl(
|
let call = PresentationCallImpl(
|
||||||
account: account,
|
context: context,
|
||||||
audioSession: strongSelf.audioSession,
|
audioSession: strongSelf.audioSession,
|
||||||
callSessionManager: account.callSessionManager,
|
callSessionManager: context.account.callSessionManager,
|
||||||
callKitIntegration: callKitIntegrationIfEnabled(
|
callKitIntegration: callKitIntegrationIfEnabled(
|
||||||
strongSelf.callKitIntegration,
|
strongSelf.callKitIntegration,
|
||||||
settings: strongSelf.callSettings
|
settings: strongSelf.callSettings
|
||||||
@ -565,7 +563,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
proxyServer: strongSelf.proxyServer,
|
proxyServer: strongSelf.proxyServer,
|
||||||
auxiliaryServers: [],
|
auxiliaryServers: [],
|
||||||
currentNetworkType: currentNetworkType,
|
currentNetworkType: currentNetworkType,
|
||||||
updatedNetworkType: account.networkType,
|
updatedNetworkType: context.account.networkType,
|
||||||
startWithVideo: isVideo,
|
startWithVideo: isVideo,
|
||||||
isVideoPossible: isVideoPossible,
|
isVideoPossible: isVideoPossible,
|
||||||
enableStunMarking: shouldEnableStunMarking(appConfiguration: appConfiguration),
|
enableStunMarking: shouldEnableStunMarking(appConfiguration: appConfiguration),
|
||||||
|
|||||||
@ -423,10 +423,17 @@ public extension TelegramEngine {
|
|||||||
public func peerExportedInvitationsCreators(peerId: PeerId) -> Signal<[ExportedInvitationCreator], NoError> {
|
public func peerExportedInvitationsCreators(peerId: PeerId) -> Signal<[ExportedInvitationCreator], NoError> {
|
||||||
return _internal_peerExportedInvitationsCreators(account: self.account, peerId: peerId)
|
return _internal_peerExportedInvitationsCreators(account: self.account, peerId: peerId)
|
||||||
}
|
}
|
||||||
|
public func direct_peerExportedInvitations(peerId: PeerId, revoked: Bool, adminId: PeerId? = nil, offsetLink: ExportedInvitation? = nil) -> Signal<ExportedInvitations?, NoError> {
|
||||||
|
return _internal_peerExportedInvitations(account: self.account, peerId: peerId, revoked: revoked, adminId: adminId, offsetLink: offsetLink)
|
||||||
|
}
|
||||||
|
|
||||||
public func peerExportedInvitations(peerId: PeerId, adminId: PeerId?, revoked: Bool, forceUpdate: Bool) -> PeerExportedInvitationsContext {
|
public func peerExportedInvitations(peerId: PeerId, adminId: PeerId?, revoked: Bool, forceUpdate: Bool) -> PeerExportedInvitationsContext {
|
||||||
return PeerExportedInvitationsContext(account: self.account, peerId: peerId, adminId: adminId, revoked: revoked, forceUpdate: forceUpdate)
|
return PeerExportedInvitationsContext(account: self.account, peerId: peerId, adminId: adminId, revoked: revoked, forceUpdate: forceUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func revokePersistentPeerExportedInvitation(peerId: PeerId) -> Signal<ExportedInvitation?, NoError> {
|
||||||
|
return _internal_revokePersistentPeerExportedInvitation(account: self.account, peerId: peerId)
|
||||||
|
}
|
||||||
|
|
||||||
public func peerInvitationImporters(peerId: PeerId, invite: ExportedInvitation) -> PeerInvitationImportersContext {
|
public func peerInvitationImporters(peerId: PeerId, invite: ExportedInvitation) -> PeerInvitationImportersContext {
|
||||||
return PeerInvitationImportersContext(account: self.account, peerId: peerId, invite: invite)
|
return PeerInvitationImportersContext(account: self.account, peerId: peerId, invite: invite)
|
||||||
|
|||||||
@ -833,9 +833,9 @@ final class SharedApplicationContext {
|
|||||||
|
|
||||||
presentationDataPromise.set(sharedContext.presentationData)
|
presentationDataPromise.set(sharedContext.presentationData)
|
||||||
|
|
||||||
let rawAccounts = sharedContext.activeAccounts
|
let rawAccounts = sharedContext.activeAccountContexts
|
||||||
|> map { _, accounts, _ -> [Account] in
|
|> map { _, contexts, _ -> [Account] in
|
||||||
return accounts.map({ $0.1 })
|
return contexts.map({ $0.1.account })
|
||||||
}
|
}
|
||||||
let storeQueue = Queue()
|
let storeQueue = Queue()
|
||||||
let _ = (
|
let _ = (
|
||||||
@ -870,7 +870,7 @@ final class SharedApplicationContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let notificationManager = SharedNotificationManager(episodeId: self.episodeId, application: application, clearNotificationsManager: clearNotificationsManager, inForeground: applicationBindings.applicationInForeground, accounts: sharedContext.activeAccounts |> map { primary, accounts, _ in accounts.map({ ($0.1, $0.1.id == primary?.id) }) }, pollLiveLocationOnce: { accountId in
|
let notificationManager = SharedNotificationManager(episodeId: self.episodeId, application: application, clearNotificationsManager: clearNotificationsManager, inForeground: applicationBindings.applicationInForeground, accounts: sharedContext.activeAccountContexts |> map { primary, accounts, _ in accounts.map({ ($0.1.account, $0.1.account.id == primary?.account.id) }) }, pollLiveLocationOnce: { accountId in
|
||||||
let _ = (self.context.get()
|
let _ = (self.context.get()
|
||||||
|> filter {
|
|> filter {
|
||||||
return $0 != nil
|
return $0 != nil
|
||||||
@ -922,7 +922,7 @@ final class SharedApplicationContext {
|
|||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let wakeupManager = SharedWakeupManager(beginBackgroundTask: { name, expiration in application.beginBackgroundTask(withName: name, expirationHandler: expiration) }, endBackgroundTask: { id in application.endBackgroundTask(id) }, backgroundTimeRemaining: { application.backgroundTimeRemaining }, activeAccounts: sharedContext.activeAccounts |> map { ($0.0, $0.1.map { ($0.0, $0.1) }) }, liveLocationPolling: liveLocationPolling, watchTasks: watchTasks, inForeground: applicationBindings.applicationInForeground, hasActiveAudioSession: self.hasActiveAudioSession.get(), notificationManager: notificationManager, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in
|
let wakeupManager = SharedWakeupManager(beginBackgroundTask: { name, expiration in application.beginBackgroundTask(withName: name, expirationHandler: expiration) }, endBackgroundTask: { id in application.endBackgroundTask(id) }, backgroundTimeRemaining: { application.backgroundTimeRemaining }, activeAccounts: sharedContext.activeAccountContexts |> map { ($0.0?.account, $0.1.map { ($0.0, $0.1.account) }) }, liveLocationPolling: liveLocationPolling, watchTasks: watchTasks, inForeground: applicationBindings.applicationInForeground, hasActiveAudioSession: self.hasActiveAudioSession.get(), notificationManager: notificationManager, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in
|
||||||
return sharedContext.accountUserInterfaceInUse(id)
|
return sharedContext.accountUserInterfaceInUse(id)
|
||||||
})
|
})
|
||||||
let sharedApplicationContext = SharedApplicationContext(sharedContext: sharedContext, notificationManager: notificationManager, wakeupManager: wakeupManager)
|
let sharedApplicationContext = SharedApplicationContext(sharedContext: sharedContext, notificationManager: notificationManager, wakeupManager: wakeupManager)
|
||||||
@ -946,8 +946,8 @@ final class SharedApplicationContext {
|
|||||||
self.context.set(self.sharedContextPromise.get()
|
self.context.set(self.sharedContextPromise.get()
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> mapToSignal { sharedApplicationContext -> Signal<AuthorizedApplicationContext?, NoError> in
|
|> mapToSignal { sharedApplicationContext -> Signal<AuthorizedApplicationContext?, NoError> in
|
||||||
return sharedApplicationContext.sharedContext.activeAccounts
|
return sharedApplicationContext.sharedContext.activeAccountContexts
|
||||||
|> map { primary, _, _ -> Account? in
|
|> map { primary, _, _ -> AccountContext? in
|
||||||
return primary
|
return primary
|
||||||
}
|
}
|
||||||
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||||
@ -956,7 +956,7 @@ final class SharedApplicationContext {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|> mapToSignal { account -> Signal<(Account, LimitsConfiguration, CallListSettings, ContentSettings, AppConfiguration)?, NoError> in
|
|> mapToSignal { context -> Signal<(AccountContext, CallListSettings)?, NoError> in
|
||||||
return sharedApplicationContext.sharedContext.accountManager.transaction { transaction -> CallListSettings? in
|
return sharedApplicationContext.sharedContext.accountManager.transaction { transaction -> CallListSettings? in
|
||||||
return transaction.getSharedData(ApplicationSpecificSharedDataKeys.callListSettings) as? CallListSettings
|
return transaction.getSharedData(ApplicationSpecificSharedDataKeys.callListSettings) as? CallListSettings
|
||||||
}
|
}
|
||||||
@ -969,24 +969,18 @@ final class SharedApplicationContext {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|> mapToSignal { callListSettings -> Signal<(Account, LimitsConfiguration, CallListSettings, ContentSettings, AppConfiguration)?, NoError> in
|
|> map { callListSettings -> (AccountContext, CallListSettings)? in
|
||||||
if let account = account {
|
if let context = context {
|
||||||
return account.postbox.transaction { transaction -> (Account, LimitsConfiguration, CallListSettings, ContentSettings, AppConfiguration)? in
|
return (context, callListSettings ?? .defaultSettings)
|
||||||
let limitsConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration) as? LimitsConfiguration ?? LimitsConfiguration.defaultValue
|
|
||||||
let contentSettings = getContentSettings(transaction: transaction)
|
|
||||||
let appConfiguration = getAppConfiguration(transaction: transaction)
|
|
||||||
return (account, limitsConfiguration, callListSettings ?? CallListSettings.defaultSettings, contentSettings, appConfiguration)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return .single(nil)
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> map { accountAndSettings -> AuthorizedApplicationContext? in
|
|> map { accountAndSettings -> AuthorizedApplicationContext? in
|
||||||
return accountAndSettings.flatMap { account, limitsConfiguration, callListSettings, contentSettings, appConfiguration in
|
return accountAndSettings.flatMap { context, callListSettings in
|
||||||
let context = AccountContextImpl(sharedContext: sharedApplicationContext.sharedContext, account: account, limitsConfiguration: limitsConfiguration, contentSettings: contentSettings, appConfiguration: appConfiguration)
|
return AuthorizedApplicationContext(sharedApplicationContext: sharedApplicationContext, mainWindow: self.mainWindow, watchManagerArguments: watchManagerArgumentsPromise.get(), context: context as! AccountContextImpl, accountManager: sharedApplicationContext.sharedContext.accountManager, showCallsTab: callListSettings.showTab, reinitializedNotificationSettings: {
|
||||||
return AuthorizedApplicationContext(sharedApplicationContext: sharedApplicationContext, mainWindow: self.mainWindow, watchManagerArguments: watchManagerArgumentsPromise.get(), context: context, accountManager: sharedApplicationContext.sharedContext.accountManager, showCallsTab: callListSettings.showTab, reinitializedNotificationSettings: {
|
|
||||||
let _ = (self.context.get()
|
let _ = (self.context.get()
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { context in
|
|> deliverOnMainQueue).start(next: { context in
|
||||||
@ -1002,8 +996,8 @@ final class SharedApplicationContext {
|
|||||||
self.authContext.set(self.sharedContextPromise.get()
|
self.authContext.set(self.sharedContextPromise.get()
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> mapToSignal { sharedApplicationContext -> Signal<UnauthorizedApplicationContext?, NoError> in
|
|> mapToSignal { sharedApplicationContext -> Signal<UnauthorizedApplicationContext?, NoError> in
|
||||||
return sharedApplicationContext.sharedContext.activeAccounts
|
return sharedApplicationContext.sharedContext.activeAccountContexts
|
||||||
|> map { primary, accounts, auth -> (Account?, UnauthorizedAccount, [Account])? in
|
|> map { primary, accounts, auth -> (AccountContext?, UnauthorizedAccount, [AccountContext])? in
|
||||||
if let auth = auth {
|
if let auth = auth {
|
||||||
return (primary, auth, Array(accounts.map({ $0.1 })))
|
return (primary, auth, Array(accounts.map({ $0.1 })))
|
||||||
} else {
|
} else {
|
||||||
@ -1018,10 +1012,10 @@ final class SharedApplicationContext {
|
|||||||
})
|
})
|
||||||
|> mapToSignal { authAndAccounts -> Signal<(UnauthorizedAccount, ((String, AccountRecordId, Bool)?, [(String, AccountRecordId, Bool)]))?, NoError> in
|
|> mapToSignal { authAndAccounts -> Signal<(UnauthorizedAccount, ((String, AccountRecordId, Bool)?, [(String, AccountRecordId, Bool)]))?, NoError> in
|
||||||
if let (primary, auth, accounts) = authAndAccounts {
|
if let (primary, auth, accounts) = authAndAccounts {
|
||||||
let phoneNumbers = combineLatest(accounts.map { account -> Signal<(AccountRecordId, String, Bool)?, NoError> in
|
let phoneNumbers = combineLatest(accounts.map { context -> Signal<(AccountRecordId, String, Bool)?, NoError> in
|
||||||
return account.postbox.transaction { transaction -> (AccountRecordId, String, Bool)? in
|
return context.account.postbox.transaction { transaction -> (AccountRecordId, String, Bool)? in
|
||||||
if let phone = (transaction.getPeer(account.peerId) as? TelegramUser)?.phone {
|
if let phone = (transaction.getPeer(context.account.peerId) as? TelegramUser)?.phone {
|
||||||
return (account.id, phone, account.testingEnvironment)
|
return (context.account.id, phone, context.account.testingEnvironment)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1032,7 +1026,7 @@ final class SharedApplicationContext {
|
|||||||
var primaryNumber: (String, AccountRecordId, Bool)?
|
var primaryNumber: (String, AccountRecordId, Bool)?
|
||||||
if let primary = primary {
|
if let primary = primary {
|
||||||
for idAndNumber in phoneNumbers {
|
for idAndNumber in phoneNumbers {
|
||||||
if let (id, number, testingEnvironment) = idAndNumber, id == primary.id {
|
if let (id, number, testingEnvironment) = idAndNumber, id == primary.account.id {
|
||||||
primaryNumber = (number, id, testingEnvironment)
|
primaryNumber = (number, id, testingEnvironment)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1194,13 +1188,14 @@ final class SharedApplicationContext {
|
|||||||
authContextReadyDisposable.set(nil)
|
authContextReadyDisposable.set(nil)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
self.logoutDisposable.set((self.sharedContextPromise.get()
|
|
||||||
|
let logoutDataSignal: Signal<(AccountManager, Set<PeerId>), NoError> = self.sharedContextPromise.get()
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> mapToSignal { sharedContext -> Signal<(AccountManager, Set<PeerId>), NoError> in
|
|> mapToSignal { sharedContext -> Signal<(AccountManager, Set<PeerId>), NoError> in
|
||||||
return sharedContext.sharedContext.activeAccounts
|
return sharedContext.sharedContext.activeAccountContexts
|
||||||
|> map { _, accounts, _ -> Set<PeerId> in
|
|> map { _, accounts, _ -> Set<PeerId> in
|
||||||
return Set(accounts.map { $0.1.peerId })
|
return Set(accounts.map { $0.1.account.peerId })
|
||||||
}
|
}
|
||||||
|> reduceLeft(value: Set<PeerId>()) { current, updated, emit in
|
|> reduceLeft(value: Set<PeerId>()) { current, updated, emit in
|
||||||
if !current.isEmpty {
|
if !current.isEmpty {
|
||||||
@ -1211,7 +1206,9 @@ final class SharedApplicationContext {
|
|||||||
|> map { loggedOutAccountPeerIds -> (AccountManager, Set<PeerId>) in
|
|> map { loggedOutAccountPeerIds -> (AccountManager, Set<PeerId>) in
|
||||||
return (sharedContext.sharedContext.accountManager, loggedOutAccountPeerIds)
|
return (sharedContext.sharedContext.accountManager, loggedOutAccountPeerIds)
|
||||||
}
|
}
|
||||||
}).start(next: { accountManager, loggedOutAccountPeerIds in
|
}
|
||||||
|
|
||||||
|
self.logoutDisposable.set(logoutDataSignal.start(next: { accountManager, loggedOutAccountPeerIds in
|
||||||
let _ = (updateIntentsSettingsInteractively(accountManager: accountManager) { current in
|
let _ = (updateIntentsSettingsInteractively(accountManager: accountManager) { current in
|
||||||
var updated = current
|
var updated = current
|
||||||
for peerId in loggedOutAccountPeerIds {
|
for peerId in loggedOutAccountPeerIds {
|
||||||
@ -1266,7 +1263,7 @@ final class SharedApplicationContext {
|
|||||||
|
|
||||||
return activeAccountsAndPeers(context: context.context)
|
return activeAccountsAndPeers(context: context.context)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> map { primaryAndAccounts -> (Account, Peer, Int32)? in
|
|> map { primaryAndAccounts -> (AccountContext, Peer, Int32)? in
|
||||||
return primaryAndAccounts.1.first
|
return primaryAndAccounts.1.first
|
||||||
}
|
}
|
||||||
|> map { accountAndPeer -> String? in
|
|> map { accountAndPeer -> String? in
|
||||||
@ -1579,13 +1576,13 @@ final class SharedApplicationContext {
|
|||||||
encryptedPayload.append("=")
|
encryptedPayload.append("=")
|
||||||
}
|
}
|
||||||
if let data = Data(base64Encoded: encryptedPayload) {
|
if let data = Data(base64Encoded: encryptedPayload) {
|
||||||
let _ = (sharedApplicationContext.sharedContext.activeAccounts
|
let _ = (sharedApplicationContext.sharedContext.activeAccountContexts
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> mapToSignal { activeAccounts -> Signal<[(Account, MasterNotificationKey)], NoError> in
|
|> mapToSignal { activeAccounts -> Signal<[(Account, MasterNotificationKey)], NoError> in
|
||||||
return combineLatest(activeAccounts.accounts.map { account -> Signal<(Account, MasterNotificationKey), NoError> in
|
return combineLatest(activeAccounts.accounts.map { context -> Signal<(Account, MasterNotificationKey), NoError> in
|
||||||
return masterNotificationsKey(account: account.1, ignoreDisabled: true)
|
return masterNotificationsKey(account: context.1.account, ignoreDisabled: true)
|
||||||
|> map { key -> (Account, MasterNotificationKey) in
|
|> map { key -> (Account, MasterNotificationKey) in
|
||||||
return (account.1, key)
|
return (context.1.account, key)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1803,38 +1800,38 @@ final class SharedApplicationContext {
|
|||||||
|
|
||||||
let signal = self.sharedContextPromise.get()
|
let signal = self.sharedContextPromise.get()
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> mapToSignal { sharedApplicationContext -> Signal<(AccountRecordId?, [Account?]), NoError> in
|
|> mapToSignal { sharedApplicationContext -> Signal<(AccountRecordId?, [AccountContext?]), NoError> in
|
||||||
return sharedApplicationContext.sharedContext.activeAccounts
|
return sharedApplicationContext.sharedContext.activeAccountContexts
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> mapToSignal { primary, accounts, _ -> Signal<(AccountRecordId?, [Account?]), NoError> in
|
|> mapToSignal { primary, contexts, _ -> Signal<(AccountRecordId?, [AccountContext?]), NoError> in
|
||||||
return combineLatest(accounts.map { _, account, _ -> Signal<Account?, NoError> in
|
return combineLatest(contexts.map { _, context, _ -> Signal<AccountContext?, NoError> in
|
||||||
return account.postbox.transaction { transaction -> Account? in
|
return context.account.postbox.transaction { transaction -> AccountContext? in
|
||||||
if transaction.getPeer(peerId) != nil {
|
if transaction.getPeer(peerId) != nil {
|
||||||
return account
|
return context
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|> map { accounts -> (AccountRecordId?, [Account?]) in
|
|> map { contexts -> (AccountRecordId?, [AccountContext?]) in
|
||||||
return (primary?.id, accounts)
|
return (primary?.account.id, contexts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let _ = (signal
|
let _ = (signal
|
||||||
|> deliverOnMainQueue).start(next: { primary, accounts in
|
|> deliverOnMainQueue).start(next: { primary, contexts in
|
||||||
if let primary = primary {
|
if let primary = primary {
|
||||||
for account in accounts {
|
for context in contexts {
|
||||||
if let account = account, account.id == primary {
|
if let context = context, context.account.id == primary {
|
||||||
self.openChatWhenReady(accountId: nil, peerId: peerId)
|
self.openChatWhenReady(accountId: nil, peerId: peerId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for account in accounts {
|
for context in contexts {
|
||||||
if let account = account {
|
if let context = context {
|
||||||
self.openChatWhenReady(accountId: account.id, peerId: peerId)
|
self.openChatWhenReady(accountId: context.account.id, peerId: peerId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1974,11 +1971,11 @@ final class SharedApplicationContext {
|
|||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> mapToSignal { sharedContext -> Signal<Void, NoError> in
|
|> mapToSignal { sharedContext -> Signal<Void, NoError> in
|
||||||
sharedContext.wakeupManager.allowBackgroundTimeExtension(timeout: 2.0, extendNow: true)
|
sharedContext.wakeupManager.allowBackgroundTimeExtension(timeout: 2.0, extendNow: true)
|
||||||
return sharedContext.sharedContext.activeAccounts
|
return sharedContext.sharedContext.activeAccountContexts
|
||||||
|> mapToSignal { _, accounts, _ -> Signal<Account, NoError> in
|
|> mapToSignal { _, contexts, _ -> Signal<Account, NoError> in
|
||||||
for account in accounts {
|
for context in contexts {
|
||||||
if account.1.id == accountId {
|
if context.1.account.id == accountId {
|
||||||
return .single(account.1)
|
return .single(context.1.account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return .complete()
|
return .complete()
|
||||||
@ -2300,13 +2297,13 @@ private func accountIdFromNotification(_ notification: UNNotification, sharedCon
|
|||||||
return sharedContext
|
return sharedContext
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> mapToSignal { sharedContext -> Signal<AccountRecordId?, NoError> in
|
|> mapToSignal { sharedContext -> Signal<AccountRecordId?, NoError> in
|
||||||
return sharedContext.sharedContext.activeAccounts
|
return sharedContext.sharedContext.activeAccountContexts
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> mapToSignal { _, accounts, _ -> Signal<AccountRecordId?, NoError> in
|
|> mapToSignal { _, contexts, _ -> Signal<AccountRecordId?, NoError> in
|
||||||
let keys = accounts.map { _, account, _ -> Signal<(AccountRecordId, MasterNotificationKey)?, NoError> in
|
let keys = contexts.map { _, context, _ -> Signal<(AccountRecordId, MasterNotificationKey)?, NoError> in
|
||||||
return masterNotificationsKey(account: account, ignoreDisabled: true)
|
return masterNotificationsKey(account: context.account, ignoreDisabled: true)
|
||||||
|> map { key in
|
|> map { key in
|
||||||
return (account.id, key)
|
return (context.account.id, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return combineLatest(keys)
|
return combineLatest(keys)
|
||||||
@ -2324,12 +2321,12 @@ private func accountIdFromNotification(_ notification: UNNotification, sharedCon
|
|||||||
return sharedContext
|
return sharedContext
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> mapToSignal { sharedContext -> Signal<AccountRecordId?, NoError> in
|
|> mapToSignal { sharedContext -> Signal<AccountRecordId?, NoError> in
|
||||||
return sharedContext.sharedContext.activeAccounts
|
return sharedContext.sharedContext.activeAccountContexts
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> map { _, accounts, _ -> AccountRecordId? in
|
|> map { _, contexts, _ -> AccountRecordId? in
|
||||||
for (_, account, _) in accounts {
|
for (_, context, _) in contexts {
|
||||||
if Int(account.peerId.id._internalGetInt32Value()) == userId {
|
if Int(context.account.peerId.id._internalGetInt32Value()) == userId {
|
||||||
return account.id
|
return context.account.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -870,21 +870,21 @@ final class AuthorizedApplicationContext {
|
|||||||
func switchAccount() {
|
func switchAccount() {
|
||||||
let _ = (activeAccountsAndPeers(context: self.context)
|
let _ = (activeAccountsAndPeers(context: self.context)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> map { primaryAndAccounts -> (Account, Peer, Int32)? in
|
|> map { primaryAndAccounts -> (AccountContext, Peer, Int32)? in
|
||||||
return primaryAndAccounts.1.first
|
return primaryAndAccounts.1.first
|
||||||
}
|
}
|
||||||
|> map { accountAndPeer -> Account? in
|
|> map { accountAndPeer -> AccountContext? in
|
||||||
if let (account, _, _) = accountAndPeer {
|
if let (context, _, _) = accountAndPeer {
|
||||||
return account
|
return context
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] account in
|
|> deliverOnMainQueue).start(next: { [weak self] context in
|
||||||
guard let strongSelf = self, let account = account else {
|
guard let strongSelf = self, let context = context else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.context.sharedContext.switchToAccount(id: account.id, fromSettingsController: nil, withChatListController: nil)
|
strongSelf.context.sharedContext.switchToAccount(id: context.account.id, fromSettingsController: nil, withChatListController: nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -433,13 +433,13 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
|||||||
private func refreshQrToken() {
|
private func refreshQrToken() {
|
||||||
let sharedContext = self.sharedContext
|
let sharedContext = self.sharedContext
|
||||||
let account = self.account
|
let account = self.account
|
||||||
let tokenSignal = sharedContext.activeAccounts
|
let tokenSignal = sharedContext.activeAccountContexts
|
||||||
|> castError(ExportAuthTransferTokenError.self)
|
|> castError(ExportAuthTransferTokenError.self)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> mapToSignal { activeAccountsAndInfo -> Signal<ExportAuthTransferTokenResult, ExportAuthTransferTokenError> in
|
|> mapToSignal { activeAccountsAndInfo -> Signal<ExportAuthTransferTokenResult, ExportAuthTransferTokenError> in
|
||||||
let (_, activeAccounts, _) = activeAccountsAndInfo
|
let (_, activeAccounts, _) = activeAccountsAndInfo
|
||||||
let activeProductionUserIds = activeAccounts.map({ $0.1 }).filter({ !$0.testingEnvironment }).map({ $0.peerId.id })
|
let activeProductionUserIds = activeAccounts.map({ $0.1.account }).filter({ !$0.testingEnvironment }).map({ $0.peerId.id })
|
||||||
let activeTestingUserIds = activeAccounts.map({ $0.1 }).filter({ $0.testingEnvironment }).map({ $0.peerId.id })
|
let activeTestingUserIds = activeAccounts.map({ $0.1.account }).filter({ $0.testingEnvironment }).map({ $0.peerId.id })
|
||||||
|
|
||||||
let allProductionUserIds = activeProductionUserIds
|
let allProductionUserIds = activeProductionUserIds
|
||||||
let allTestingUserIds = activeTestingUserIds
|
let allTestingUserIds = activeTestingUserIds
|
||||||
|
|||||||
@ -203,9 +203,9 @@ public final class NotificationViewControllerImpl {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.applyDisposable.set((sharedAccountContext.activeAccounts
|
self.applyDisposable.set((sharedAccountContext.activeAccountContexts
|
||||||
|> map { _, accounts, _ -> Account? in
|
|> map { _, accounts, _ -> Account? in
|
||||||
return accounts.first(where: { $0.0 == AccountRecordId(rawValue: accountIdValue) })?.1
|
return accounts.first(where: { $0.0 == AccountRecordId(rawValue: accountIdValue) })?.1.account
|
||||||
}
|
}
|
||||||
|> filter { account in
|
|> filter { account in
|
||||||
return account != nil
|
return account != nil
|
||||||
@ -254,16 +254,16 @@ public final class NotificationViewControllerImpl {
|
|||||||
self.imageInfo = (true, dimensions.cgSize)
|
self.imageInfo = (true, dimensions.cgSize)
|
||||||
self.updateImageLayout(boundingSize: view.bounds.size)
|
self.updateImageLayout(boundingSize: view.bounds.size)
|
||||||
|
|
||||||
self.applyDisposable.set((sharedAccountContext.activeAccounts
|
self.applyDisposable.set((sharedAccountContext.activeAccountContexts
|
||||||
|> map { _, accounts, _ -> Account? in
|
|> map { _, contexts, _ -> AccountContext? in
|
||||||
return accounts.first(where: { $0.0 == AccountRecordId(rawValue: accountIdValue) })?.1
|
return contexts.first(where: { $0.0 == AccountRecordId(rawValue: accountIdValue) })?.1
|
||||||
}
|
}
|
||||||
|> filter { account in
|
|> filter { context in
|
||||||
return account != nil
|
return context != nil
|
||||||
}
|
}
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> mapToSignal { account -> Signal<(Account, FileMediaReference?), NoError> in
|
|> mapToSignal { context -> Signal<(Account, FileMediaReference?), NoError> in
|
||||||
guard let account = account else {
|
guard let account = context?.account else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
return account.postbox.messageAtId(messageId)
|
return account.postbox.messageAtId(messageId)
|
||||||
|
|||||||
@ -112,7 +112,7 @@ final class PeerInfoState {
|
|||||||
final class TelegramGlobalSettings {
|
final class TelegramGlobalSettings {
|
||||||
let suggestPhoneNumberConfirmation: Bool
|
let suggestPhoneNumberConfirmation: Bool
|
||||||
let suggestPasswordConfirmation: Bool
|
let suggestPasswordConfirmation: Bool
|
||||||
let accountsAndPeers: [(Account, Peer, Int32)]
|
let accountsAndPeers: [(AccountContext, Peer, Int32)]
|
||||||
let activeSessionsContext: ActiveSessionsContext?
|
let activeSessionsContext: ActiveSessionsContext?
|
||||||
let webSessionsContext: WebSessionsContext?
|
let webSessionsContext: WebSessionsContext?
|
||||||
let otherSessionsCount: Int?
|
let otherSessionsCount: Int?
|
||||||
@ -131,7 +131,7 @@ final class TelegramGlobalSettings {
|
|||||||
init(
|
init(
|
||||||
suggestPhoneNumberConfirmation: Bool,
|
suggestPhoneNumberConfirmation: Bool,
|
||||||
suggestPasswordConfirmation: Bool,
|
suggestPasswordConfirmation: Bool,
|
||||||
accountsAndPeers: [(Account, Peer, Int32)],
|
accountsAndPeers: [(AccountContext, Peer, Int32)],
|
||||||
activeSessionsContext: ActiveSessionsContext?,
|
activeSessionsContext: ActiveSessionsContext?,
|
||||||
webSessionsContext: WebSessionsContext?,
|
webSessionsContext: WebSessionsContext?,
|
||||||
otherSessionsCount: Int?,
|
otherSessionsCount: Int?,
|
||||||
@ -356,7 +356,7 @@ func keepPeerInfoScreenDataHot(context: AccountContext, peerId: PeerId) -> Signa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func peerInfoScreenSettingsData(context: AccountContext, peerId: PeerId, accountsAndPeers: Signal<[(Account, Peer, Int32)], NoError>, activeSessionsContextAndCount: Signal<(ActiveSessionsContext, Int, WebSessionsContext)?, NoError>, notificationExceptions: Signal<NotificationExceptionsList?, NoError>, privacySettings: Signal<AccountPrivacySettings?, NoError>, archivedStickerPacks: Signal<[ArchivedStickerPackItem]?, NoError>, hasPassport: Signal<Bool, NoError>) -> Signal<PeerInfoScreenData, NoError> {
|
func peerInfoScreenSettingsData(context: AccountContext, peerId: PeerId, accountsAndPeers: Signal<[(AccountContext, Peer, Int32)], NoError>, activeSessionsContextAndCount: Signal<(ActiveSessionsContext, Int, WebSessionsContext)?, NoError>, notificationExceptions: Signal<NotificationExceptionsList?, NoError>, privacySettings: Signal<AccountPrivacySettings?, NoError>, archivedStickerPacks: Signal<[ArchivedStickerPackItem]?, NoError>, hasPassport: Signal<Bool, NoError>) -> Signal<PeerInfoScreenData, NoError> {
|
||||||
let preferences = context.sharedContext.accountManager.sharedData(keys: [
|
let preferences = context.sharedContext.accountManager.sharedData(keys: [
|
||||||
SharedDataKeys.proxySettings,
|
SharedDataKeys.proxySettings,
|
||||||
ApplicationSpecificSharedDataKeys.inAppNotificationSettings,
|
ApplicationSpecificSharedDataKeys.inAppNotificationSettings,
|
||||||
|
|||||||
@ -720,19 +720,19 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !settings.accountsAndPeers.isEmpty {
|
if !settings.accountsAndPeers.isEmpty {
|
||||||
for (peerAccount, peer, badgeCount) in settings.accountsAndPeers {
|
for (peerAccountContext, peer, badgeCount) in settings.accountsAndPeers {
|
||||||
let member: PeerInfoMember = .account(peer: RenderedPeer(peer: peer))
|
let member: PeerInfoMember = .account(peer: RenderedPeer(peer: peer))
|
||||||
items[.accounts]!.append(PeerInfoScreenMemberItem(id: member.id, context: context.sharedContext.makeTempAccountContext(account: peerAccount), enclosingPeer: nil, member: member, badge: badgeCount > 0 ? "\(compactNumericCountString(Int(badgeCount), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))" : nil, action: { action in
|
items[.accounts]!.append(PeerInfoScreenMemberItem(id: member.id, context: context.sharedContext.makeTempAccountContext(account: peerAccountContext.account), enclosingPeer: nil, member: member, badge: badgeCount > 0 ? "\(compactNumericCountString(Int(badgeCount), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))" : nil, action: { action in
|
||||||
switch action {
|
switch action {
|
||||||
case .open:
|
case .open:
|
||||||
interaction.switchToAccount(peerAccount.id)
|
interaction.switchToAccount(peerAccountContext.account.id)
|
||||||
case .remove:
|
case .remove:
|
||||||
interaction.logoutAccount(peerAccount.id)
|
interaction.logoutAccount(peerAccountContext.account.id)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}, contextAction: { node, gesture in
|
}, contextAction: { node, gesture in
|
||||||
interaction.accountContextMenu(peerAccount.id, node, gesture)
|
interaction.accountContextMenu(peerAccountContext.account.id, node, gesture)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
if settings.accountsAndPeers.count + 1 < maximumNumberOfAccounts {
|
if settings.accountsAndPeers.count + 1 < maximumNumberOfAccounts {
|
||||||
@ -1549,7 +1549,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
|
|
||||||
private let displayAsPeersPromise = Promise<[FoundPeer]>([])
|
private let displayAsPeersPromise = Promise<[FoundPeer]>([])
|
||||||
|
|
||||||
fileprivate let accountsAndPeers = Promise<[(Account, Peer, Int32)]>()
|
fileprivate let accountsAndPeers = Promise<[(AccountContext, Peer, Int32)]>()
|
||||||
fileprivate let activeSessionsContextAndCount = Promise<(ActiveSessionsContext, Int, WebSessionsContext)?>()
|
fileprivate let activeSessionsContextAndCount = Promise<(ActiveSessionsContext, Int, WebSessionsContext)?>()
|
||||||
private let notificationExceptions = Promise<NotificationExceptionsList?>()
|
private let notificationExceptions = Promise<NotificationExceptionsList?>()
|
||||||
private let privacySettings = Promise<AccountPrivacySettings?>()
|
private let privacySettings = Promise<AccountPrivacySettings?>()
|
||||||
@ -5559,8 +5559,8 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { accountsAndPeers in
|
|> deliverOnMainQueue).start(next: { accountsAndPeers in
|
||||||
for (account, _, _) in accountsAndPeers {
|
for (account, _, _) in accountsAndPeers {
|
||||||
if account.id == id {
|
if account.account.id == id {
|
||||||
selectedAccount = account
|
selectedAccount = account.account
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6450,8 +6450,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
|
|||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private var presentationDataDisposable: Disposable?
|
private var presentationDataDisposable: Disposable?
|
||||||
|
|
||||||
private let accountsAndPeers = Promise<((Account, Peer)?, [(Account, Peer, Int32)])>()
|
private let accountsAndPeers = Promise<((AccountContext, Peer)?, [(AccountContext, Peer, Int32)])>()
|
||||||
private var accountsAndPeersValue: ((Account, Peer)?, [(Account, Peer, Int32)])?
|
private var accountsAndPeersValue: ((AccountContext, Peer)?, [(AccountContext, Peer, Int32)])?
|
||||||
private var accountsAndPeersDisposable: Disposable?
|
private var accountsAndPeersDisposable: Disposable?
|
||||||
|
|
||||||
private let activeSessionsContextAndCount = Promise<(ActiveSessionsContext, Int, WebSessionsContext)?>(nil)
|
private let activeSessionsContextAndCount = Promise<(ActiveSessionsContext, Int, WebSessionsContext)?>(nil)
|
||||||
@ -6545,7 +6545,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
|
|||||||
let accountTabBarAvatar: Signal<(UIImage, UIImage)?, NoError> = combineLatest(self.accountsAndPeers.get(), context.sharedContext.presentationData)
|
let accountTabBarAvatar: Signal<(UIImage, UIImage)?, NoError> = combineLatest(self.accountsAndPeers.get(), context.sharedContext.presentationData)
|
||||||
|> map { primaryAndOther, presentationData -> (Account, Peer, PresentationTheme)? in
|
|> map { primaryAndOther, presentationData -> (Account, Peer, PresentationTheme)? in
|
||||||
if let primary = primaryAndOther.0, !primaryAndOther.1.isEmpty {
|
if let primary = primaryAndOther.0, !primaryAndOther.1.isEmpty {
|
||||||
return (primary.0, primary.1, presentationData.theme)
|
return (primary.0.account, primary.1, presentationData.theme)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -6811,7 +6811,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
|
|||||||
|
|
||||||
let avatarSize = CGSize(width: 28.0, height: 28.0)
|
let avatarSize = CGSize(width: 28.0, height: 28.0)
|
||||||
|
|
||||||
items.append(.action(ContextMenuActionItem(text: primary.1.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: primary.0, peer: primary.1, size: avatarSize)), action: { _, f in
|
items.append(.action(ContextMenuActionItem(text: primary.1.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: primary.0.account, peer: primary.1, size: avatarSize)), action: { _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
})))
|
})))
|
||||||
|
|
||||||
@ -6820,8 +6820,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for account in other {
|
for account in other {
|
||||||
let id = account.0.id
|
let id = account.0.account.id
|
||||||
items.append(.action(ContextMenuActionItem(text: account.1.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), badge: account.2 != 0 ? ContextMenuActionBadge(value: "\(account.2)", color: .accent) : nil, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: account.0, peer: account.1, size: avatarSize)), action: { [weak self] _, f in
|
items.append(.action(ContextMenuActionItem(text: account.1.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), badge: account.2 != 0 ? ContextMenuActionBadge(value: "\(account.2)", color: .accent) : nil, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: account.0.account, peer: account.1, size: avatarSize)), action: { [weak self] _, f in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -6830,7 +6830,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = ContextController(account: primary.0, presentationData: self.presentationData, source: .extracted(SettingsTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(items), reactionItems: [], recognizer: nil, gesture: gesture)
|
let controller = ContextController(account: primary.0.account, presentationData: self.presentationData, source: .extracted(SettingsTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(items), reactionItems: [], recognizer: nil, gesture: gesture)
|
||||||
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
|
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,7 +32,7 @@ private final class InternalContext {
|
|||||||
|
|
||||||
init(sharedContext: SharedAccountContextImpl) {
|
init(sharedContext: SharedAccountContextImpl) {
|
||||||
self.sharedContext = sharedContext
|
self.sharedContext = sharedContext
|
||||||
self.wakeupManager = SharedWakeupManager(beginBackgroundTask: { _, _ in nil }, endBackgroundTask: { _ in }, backgroundTimeRemaining: { 0.0 }, activeAccounts: sharedContext.activeAccounts |> map { ($0.0, $0.1.map { ($0.0, $0.1) }) }, liveLocationPolling: .single(nil), watchTasks: .single(nil), inForeground: inForeground.get(), hasActiveAudioSession: .single(false), notificationManager: nil, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in
|
self.wakeupManager = SharedWakeupManager(beginBackgroundTask: { _, _ in nil }, endBackgroundTask: { _ in }, backgroundTimeRemaining: { 0.0 }, activeAccounts: sharedContext.activeAccountContexts |> map { ($0.0?.account, $0.1.map { ($0.0, $0.1.account) }) }, liveLocationPolling: .single(nil), watchTasks: .single(nil), inForeground: inForeground.get(), hasActiveAudioSession: .single(false), notificationManager: nil, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in
|
||||||
return sharedContext.accountUserInterfaceInUse(id)
|
return sharedContext.accountUserInterfaceInUse(id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,12 +42,12 @@ private struct AccountAttributes: Equatable {
|
|||||||
|
|
||||||
private enum AddedAccountResult {
|
private enum AddedAccountResult {
|
||||||
case upgrading(Float)
|
case upgrading(Float)
|
||||||
case ready(AccountRecordId, Account?, Int32)
|
case ready(AccountRecordId, Account?, Int32, LimitsConfiguration?, ContentSettings?, AppConfiguration?)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum AddedAccountsResult {
|
private enum AddedAccountsResult {
|
||||||
case upgrading(Float)
|
case upgrading(Float)
|
||||||
case ready([(AccountRecordId, Account?, Int32)])
|
case ready([(AccountRecordId, Account?, Int32, LimitsConfiguration?, ContentSettings?, AppConfiguration?)])
|
||||||
}
|
}
|
||||||
|
|
||||||
private var testHasInstance = false
|
private var testHasInstance = false
|
||||||
@ -65,9 +65,9 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
private let apsNotificationToken: Signal<Data?, NoError>
|
private let apsNotificationToken: Signal<Data?, NoError>
|
||||||
private let voipNotificationToken: Signal<Data?, NoError>
|
private let voipNotificationToken: Signal<Data?, NoError>
|
||||||
|
|
||||||
private var activeAccountsValue: (primary: Account?, accounts: [(AccountRecordId, Account, Int32)], currentAuth: UnauthorizedAccount?)?
|
private var activeAccountsValue: (primary: AccountContext?, accounts: [(AccountRecordId, AccountContext, Int32)], currentAuth: UnauthorizedAccount?)?
|
||||||
private let activeAccountsPromise = Promise<(primary: Account?, accounts: [(AccountRecordId, Account, Int32)], currentAuth: UnauthorizedAccount?)>()
|
private let activeAccountsPromise = Promise<(primary: AccountContext?, accounts: [(AccountRecordId, AccountContext, Int32)], currentAuth: UnauthorizedAccount?)>()
|
||||||
public var activeAccounts: Signal<(primary: Account?, accounts: [(AccountRecordId, Account, Int32)], currentAuth: UnauthorizedAccount?), NoError> {
|
public var activeAccountContexts: Signal<(primary: AccountContext?, accounts: [(AccountRecordId, AccountContext, Int32)], currentAuth: UnauthorizedAccount?), NoError> {
|
||||||
return self.activeAccountsPromise.get()
|
return self.activeAccountsPromise.get()
|
||||||
}
|
}
|
||||||
private let managedAccountDisposables = DisposableDict<AccountRecordId>()
|
private let managedAccountDisposables = DisposableDict<AccountRecordId>()
|
||||||
@ -401,17 +401,22 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
for (id, attributes) in records {
|
for (id, attributes) in records {
|
||||||
if self.activeAccountsValue?.accounts.firstIndex(where: { $0.0 == id}) == nil {
|
if self.activeAccountsValue?.accounts.firstIndex(where: { $0.0 == id}) == nil {
|
||||||
addedSignals.append(accountWithId(accountManager: accountManager, networkArguments: networkArguments, id: id, encryptionParameters: encryptionParameters, supplementary: !applicationBindings.isMainApp, rootPath: rootPath, beginWithTestingEnvironment: attributes.isTestingEnvironment, backupData: attributes.backupData, auxiliaryMethods: telegramAccountAuxiliaryMethods)
|
addedSignals.append(accountWithId(accountManager: accountManager, networkArguments: networkArguments, id: id, encryptionParameters: encryptionParameters, supplementary: !applicationBindings.isMainApp, rootPath: rootPath, beginWithTestingEnvironment: attributes.isTestingEnvironment, backupData: attributes.backupData, auxiliaryMethods: telegramAccountAuxiliaryMethods)
|
||||||
|> map { result -> AddedAccountResult in
|
|> mapToSignal { result -> Signal<AddedAccountResult, NoError> in
|
||||||
switch result {
|
switch result {
|
||||||
case let .authorized(account):
|
case let .authorized(account):
|
||||||
setupAccount(account, fetchCachedResourceRepresentation: fetchCachedResourceRepresentation, transformOutgoingMessageMedia: transformOutgoingMessageMedia, preFetchedResourcePath: { resource in
|
setupAccount(account, fetchCachedResourceRepresentation: fetchCachedResourceRepresentation, transformOutgoingMessageMedia: transformOutgoingMessageMedia, preFetchedResourcePath: { resource in
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return .ready(id, account, attributes.sortIndex)
|
return account.postbox.transaction { transaction -> AddedAccountResult in
|
||||||
|
let limitsConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration) as? LimitsConfiguration ?? LimitsConfiguration.defaultValue
|
||||||
|
let contentSettings = getContentSettings(transaction: transaction)
|
||||||
|
let appConfiguration = getAppConfiguration(transaction: transaction)
|
||||||
|
return .ready(id, account, attributes.sortIndex, limitsConfiguration, contentSettings, appConfiguration)
|
||||||
|
}
|
||||||
case let .upgrading(progress):
|
case let .upgrading(progress):
|
||||||
return .upgrading(progress)
|
return .single(.upgrading(progress))
|
||||||
default:
|
default:
|
||||||
return .ready(id, nil, attributes.sortIndex)
|
return .single(.ready(id, nil, attributes.sortIndex, nil, nil, nil))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -432,13 +437,13 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
|
|
||||||
let mappedAddedAccounts = combineLatest(queue: .mainQueue(), addedSignals)
|
let mappedAddedAccounts = combineLatest(queue: .mainQueue(), addedSignals)
|
||||||
|> map { results -> AddedAccountsResult in
|
|> map { results -> AddedAccountsResult in
|
||||||
var readyAccounts: [(AccountRecordId, Account?, Int32)] = []
|
var readyAccounts: [(AccountRecordId, Account?, Int32, LimitsConfiguration?, ContentSettings?, AppConfiguration?)] = []
|
||||||
var totalProgress: Float = 0.0
|
var totalProgress: Float = 0.0
|
||||||
var hasItemsWithProgress = false
|
var hasItemsWithProgress = false
|
||||||
for result in results {
|
for result in results {
|
||||||
switch result {
|
switch result {
|
||||||
case let .ready(id, account, sortIndex):
|
case let .ready(id, account, sortIndex, limitsConfiguration, contentSettings, appConfiguration):
|
||||||
readyAccounts.append((id, account, sortIndex))
|
readyAccounts.append((id, account, sortIndex, limitsConfiguration, contentSettings, appConfiguration))
|
||||||
totalProgress += 1.0
|
totalProgress += 1.0
|
||||||
case let .upgrading(progress):
|
case let .upgrading(progress):
|
||||||
hasItemsWithProgress = true
|
hasItemsWithProgress = true
|
||||||
@ -456,7 +461,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
|> deliverOnMainQueue).start(next: { mappedAddedAccounts, authAccount in
|
|> deliverOnMainQueue).start(next: { mappedAddedAccounts, authAccount in
|
||||||
print("SharedAccountContextImpl: accounts processed in \(CFAbsoluteTimeGetCurrent() - startTime)")
|
print("SharedAccountContextImpl: accounts processed in \(CFAbsoluteTimeGetCurrent() - startTime)")
|
||||||
|
|
||||||
var addedAccounts: [(AccountRecordId, Account?, Int32)] = []
|
var addedAccounts: [(AccountRecordId, Account?, Int32, LimitsConfiguration?, ContentSettings?, AppConfiguration?)] = []
|
||||||
switch mappedAddedAccounts {
|
switch mappedAddedAccounts {
|
||||||
case let .upgrading(progress):
|
case let .upgrading(progress):
|
||||||
self.displayUpgradeProgress(progress)
|
self.displayUpgradeProgress(progress)
|
||||||
@ -494,7 +499,11 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
self.managedAccountDisposables.set(nil, forKey: account.id)
|
self.managedAccountDisposables.set(nil, forKey: account.id)
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
}
|
}
|
||||||
self.activeAccountsValue!.accounts.append((account.id, account, accountRecord.2))
|
|
||||||
|
let context = AccountContextImpl(sharedContext: self, account: account, limitsConfiguration: accountRecord.3 ?? .defaultValue, contentSettings: accountRecord.4 ?? .default, appConfiguration: accountRecord.5 ?? .defaultValue)
|
||||||
|
|
||||||
|
self.activeAccountsValue!.accounts.append((account.id, context, accountRecord.2))
|
||||||
|
|
||||||
self.managedAccountDisposables.set(self.updateAccountBackupData(account: account).start(), forKey: account.id)
|
self.managedAccountDisposables.set(self.updateAccountBackupData(account: account).start(), forKey: account.id)
|
||||||
account.resetStateManagement()
|
account.resetStateManagement()
|
||||||
hadUpdates = true
|
hadUpdates = true
|
||||||
@ -520,7 +529,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
self.managedAccountDisposables.set(nil, forKey: id)
|
self.managedAccountDisposables.set(nil, forKey: id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var primary: Account?
|
var primary: AccountContext?
|
||||||
if let primaryId = primaryId {
|
if let primaryId = primaryId {
|
||||||
if let index = self.activeAccountsValue?.accounts.firstIndex(where: { $0.0 == primaryId }) {
|
if let index = self.activeAccountsValue?.accounts.firstIndex(where: { $0.0 == primaryId }) {
|
||||||
primary = self.activeAccountsValue?.accounts[index].1
|
primary = self.activeAccountsValue?.accounts[index].1
|
||||||
@ -531,8 +540,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}
|
}
|
||||||
if primary !== self.activeAccountsValue!.primary {
|
if primary !== self.activeAccountsValue!.primary {
|
||||||
hadUpdates = true
|
hadUpdates = true
|
||||||
self.activeAccountsValue!.primary?.postbox.clearCaches()
|
self.activeAccountsValue!.primary?.account.postbox.clearCaches()
|
||||||
self.activeAccountsValue!.primary?.resetCachedData()
|
self.activeAccountsValue!.primary?.account.resetCachedData()
|
||||||
self.activeAccountsValue!.primary = primary
|
self.activeAccountsValue!.primary = primary
|
||||||
}
|
}
|
||||||
if self.activeAccountsValue!.currentAuth?.id != authRecord?.0 {
|
if self.activeAccountsValue!.currentAuth?.id != authRecord?.0 {
|
||||||
@ -555,16 +564,16 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
self.activeAccountsWithInfoPromise.set(self.activeAccounts
|
self.activeAccountsWithInfoPromise.set(self.activeAccountContexts
|
||||||
|> mapToSignal { primary, accounts, _ -> Signal<(primary: AccountRecordId?, accounts: [AccountWithInfo]), NoError> in
|
|> mapToSignal { primary, accounts, _ -> Signal<(primary: AccountRecordId?, accounts: [AccountWithInfo]), NoError> in
|
||||||
return combineLatest(accounts.map { _, account, _ -> Signal<AccountWithInfo?, NoError> in
|
return combineLatest(accounts.map { _, context, _ -> Signal<AccountWithInfo?, NoError> in
|
||||||
let peerViewKey: PostboxViewKey = .peer(peerId: account.peerId, components: [])
|
let peerViewKey: PostboxViewKey = .peer(peerId: context.account.peerId, components: [])
|
||||||
return account.postbox.combinedView(keys: [peerViewKey])
|
return context.account.postbox.combinedView(keys: [peerViewKey])
|
||||||
|> map { view -> AccountWithInfo? in
|
|> map { view -> AccountWithInfo? in
|
||||||
guard let peerView = view.views[peerViewKey] as? PeerView, let peer = peerView.peers[peerView.peerId] else {
|
guard let peerView = view.views[peerViewKey] as? PeerView, let peer = peerView.peers[peerView.peerId] else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return AccountWithInfo(account: account, peer: peer)
|
return AccountWithInfo(account: context.account, peer: peer)
|
||||||
}
|
}
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
})
|
})
|
||||||
@ -575,7 +584,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
accountsWithInfoResult.append(info)
|
accountsWithInfoResult.append(info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (primary?.id, accountsWithInfoResult)
|
return (primary?.account.id, accountsWithInfoResult)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -604,7 +613,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.mediaManager.playlistControl(.playback(.play), type: nil)
|
strongSelf.mediaManager.playlistControl(.playback(.play), type: nil)
|
||||||
}, audioSession: self.mediaManager.audioSession, activeAccounts: self.activeAccounts |> map { _, accounts, _ in
|
}, audioSession: self.mediaManager.audioSession, activeAccounts: self.activeAccountContexts |> map { _, accounts, _ in
|
||||||
return Array(accounts.map({ $0.1 }))
|
return Array(accounts.map({ $0.1 }))
|
||||||
})
|
})
|
||||||
self.callManager = callManager
|
self.callManager = callManager
|
||||||
@ -619,7 +628,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
|
|
||||||
if let call = call {
|
if let call = call {
|
||||||
mainWindow.hostView.containerView.endEditing(true)
|
mainWindow.hostView.containerView.endEditing(true)
|
||||||
let callController = CallController(sharedContext: strongSelf, account: call.account, call: call, easyDebugAccess: !GlobalExperimentalSettings.isAppStoreBuild)
|
let callController = CallController(sharedContext: strongSelf, account: call.context.account, call: call, easyDebugAccess: !GlobalExperimentalSettings.isAppStoreBuild)
|
||||||
strongSelf.callController = callController
|
strongSelf.callController = callController
|
||||||
strongSelf.mainWindow?.present(callController, on: .calls)
|
strongSelf.mainWindow?.present(callController, on: .calls)
|
||||||
strongSelf.callState.set(call.state
|
strongSelf.callState.set(call.state
|
||||||
@ -686,7 +695,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let statusBarContent: CallStatusBarNodeImpl.Content?
|
let statusBarContent: CallStatusBarNodeImpl.Content?
|
||||||
if let call = call {
|
if let call = call {
|
||||||
statusBarContent = .call(strongSelf, call.account, call)
|
statusBarContent = .call(strongSelf, call.context.account, call)
|
||||||
} else if let groupCall = groupCall, !hasGroupCallOnScreen {
|
} else if let groupCall = groupCall, !hasGroupCallOnScreen {
|
||||||
statusBarContent = .groupCall(strongSelf, groupCall.account, groupCall)
|
statusBarContent = .groupCall(strongSelf, groupCall.account, groupCall)
|
||||||
} else {
|
} else {
|
||||||
@ -748,9 +757,9 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
self.updateNotificationTokensRegistration()
|
self.updateNotificationTokensRegistration()
|
||||||
|
|
||||||
if applicationBindings.isMainApp {
|
if applicationBindings.isMainApp {
|
||||||
self.widgetDataContext = WidgetDataContext(basePath: self.basePath, inForeground: self.applicationBindings.applicationInForeground, activeAccounts: self.activeAccounts
|
self.widgetDataContext = WidgetDataContext(basePath: self.basePath, inForeground: self.applicationBindings.applicationInForeground, activeAccounts: self.activeAccountContexts
|
||||||
|> map { _, accounts, _ in
|
|> map { _, accounts, _ in
|
||||||
return accounts.map { $0.1 }
|
return accounts.map { $0.1.account }
|
||||||
}, presentationData: self.presentationData, appLockContext: self.appLockContext as! AppLockContextImpl)
|
}, presentationData: self.presentationData, appLockContext: self.appLockContext as! AppLockContextImpl)
|
||||||
|
|
||||||
let enableSpotlight = accountManager.sharedData(keys: Set([ApplicationSpecificSharedDataKeys.intentsSettings]))
|
let enableSpotlight = accountManager.sharedData(keys: Set([ApplicationSpecificSharedDataKeys.intentsSettings]))
|
||||||
@ -759,10 +768,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
return intentsSettings.contacts
|
return intentsSettings.contacts
|
||||||
}
|
}
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
self.spotlightDataContext = SpotlightDataContext(appBasePath: applicationBindings.containerPath, accountManager: accountManager, accounts: combineLatest(enableSpotlight, self.activeAccounts
|
self.spotlightDataContext = SpotlightDataContext(appBasePath: applicationBindings.containerPath, accountManager: accountManager, accounts: combineLatest(enableSpotlight, self.activeAccountContexts
|
||||||
|> map { _, accounts, _ in
|
|> map { _, accounts, _ in
|
||||||
return accounts.map { _, account, _ in
|
return accounts.map { _, account, _ in
|
||||||
return account
|
return account.account
|
||||||
}
|
}
|
||||||
}) |> map { enableSpotlight, accounts in
|
}) |> map { enableSpotlight, accounts in
|
||||||
if enableSpotlight {
|
if enableSpotlight {
|
||||||
@ -830,24 +839,24 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
self.registeredNotificationTokensDisposable.set((combineLatest(queue: .mainQueue(), settings, self.activeAccounts)
|
self.registeredNotificationTokensDisposable.set((combineLatest(queue: .mainQueue(), settings, self.activeAccountContexts)
|
||||||
|> mapToSignal { settings, activeAccountsAndInfo -> Signal<Never, NoError> in
|
|> mapToSignal { settings, activeAccountsAndInfo -> Signal<Never, NoError> in
|
||||||
let (primary, activeAccounts, _) = activeAccountsAndInfo
|
let (primary, activeAccounts, _) = activeAccountsAndInfo
|
||||||
var applied: [Signal<Never, NoError>] = []
|
var applied: [Signal<Never, NoError>] = []
|
||||||
var activeProductionUserIds = activeAccounts.map({ $0.1 }).filter({ !$0.testingEnvironment }).map({ $0.peerId.id })
|
var activeProductionUserIds = activeAccounts.map({ $0.1 }).filter({ !$0.account.testingEnvironment }).map({ $0.account.peerId.id })
|
||||||
var activeTestingUserIds = activeAccounts.map({ $0.1 }).filter({ $0.testingEnvironment }).map({ $0.peerId.id })
|
var activeTestingUserIds = activeAccounts.map({ $0.1 }).filter({ $0.account.testingEnvironment }).map({ $0.account.peerId.id })
|
||||||
|
|
||||||
let allProductionUserIds = activeProductionUserIds
|
let allProductionUserIds = activeProductionUserIds
|
||||||
let allTestingUserIds = activeTestingUserIds
|
let allTestingUserIds = activeTestingUserIds
|
||||||
|
|
||||||
if !settings.allAccounts {
|
if !settings.allAccounts {
|
||||||
if let primary = primary {
|
if let primary = primary {
|
||||||
if !primary.testingEnvironment {
|
if !primary.account.testingEnvironment {
|
||||||
activeProductionUserIds = [primary.peerId.id]
|
activeProductionUserIds = [primary.account.peerId.id]
|
||||||
activeTestingUserIds = []
|
activeTestingUserIds = []
|
||||||
} else {
|
} else {
|
||||||
activeProductionUserIds = []
|
activeProductionUserIds = []
|
||||||
activeTestingUserIds = [primary.peerId.id]
|
activeTestingUserIds = [primary.account.peerId.id]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
activeProductionUserIds = []
|
activeProductionUserIds = []
|
||||||
@ -859,14 +868,14 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
let appliedAps: Signal<Never, NoError>
|
let appliedAps: Signal<Never, NoError>
|
||||||
let appliedVoip: Signal<Never, NoError>
|
let appliedVoip: Signal<Never, NoError>
|
||||||
|
|
||||||
if !activeProductionUserIds.contains(account.peerId.id) && !activeTestingUserIds.contains(account.peerId.id) {
|
if !activeProductionUserIds.contains(account.account.peerId.id) && !activeTestingUserIds.contains(account.account.peerId.id) {
|
||||||
appliedAps = self.apsNotificationToken
|
appliedAps = self.apsNotificationToken
|
||||||
|> distinctUntilChanged(isEqual: { $0 == $1 })
|
|> distinctUntilChanged(isEqual: { $0 == $1 })
|
||||||
|> mapToSignal { token -> Signal<Never, NoError> in
|
|> mapToSignal { token -> Signal<Never, NoError> in
|
||||||
guard let token = token else {
|
guard let token = token else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
return TelegramEngine(account: account).accountData.unregisterNotificationToken(token: token, type: .aps(encrypt: false), otherAccountUserIds: (account.testingEnvironment ? allTestingUserIds : allProductionUserIds).filter({ $0 != account.peerId.id }))
|
return account.engine.accountData.unregisterNotificationToken(token: token, type: .aps(encrypt: false), otherAccountUserIds: (account.account.testingEnvironment ? allTestingUserIds : allProductionUserIds).filter({ $0 != account.account.peerId.id }))
|
||||||
}
|
}
|
||||||
appliedVoip = self.voipNotificationToken
|
appliedVoip = self.voipNotificationToken
|
||||||
|> distinctUntilChanged(isEqual: { $0 == $1 })
|
|> distinctUntilChanged(isEqual: { $0 == $1 })
|
||||||
@ -874,7 +883,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
guard let token = token else {
|
guard let token = token else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
return TelegramEngine(account: account).accountData.unregisterNotificationToken(token: token, type: .voip, otherAccountUserIds: (account.testingEnvironment ? allTestingUserIds : allProductionUserIds).filter({ $0 != account.peerId.id }))
|
return account.engine.accountData.unregisterNotificationToken(token: token, type: .voip, otherAccountUserIds: (account.account.testingEnvironment ? allTestingUserIds : allProductionUserIds).filter({ $0 != account.account.peerId.id }))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
appliedAps = self.apsNotificationToken
|
appliedAps = self.apsNotificationToken
|
||||||
@ -889,7 +898,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
} else {
|
} else {
|
||||||
encrypt = false
|
encrypt = false
|
||||||
}
|
}
|
||||||
return TelegramEngine(account: account).accountData.registerNotificationToken(token: token, type: .aps(encrypt: encrypt), sandbox: sandbox, otherAccountUserIds: (account.testingEnvironment ? activeTestingUserIds : activeProductionUserIds).filter({ $0 != account.peerId.id }), excludeMutedChats: !settings.includeMuted)
|
return account.engine.accountData.registerNotificationToken(token: token, type: .aps(encrypt: encrypt), sandbox: sandbox, otherAccountUserIds: (account.account.testingEnvironment ? activeTestingUserIds : activeProductionUserIds).filter({ $0 != account.account.peerId.id }), excludeMutedChats: !settings.includeMuted)
|
||||||
}
|
}
|
||||||
appliedVoip = self.voipNotificationToken
|
appliedVoip = self.voipNotificationToken
|
||||||
|> distinctUntilChanged(isEqual: { $0 == $1 })
|
|> distinctUntilChanged(isEqual: { $0 == $1 })
|
||||||
@ -897,7 +906,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
guard let token = token else {
|
guard let token = token else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
return TelegramEngine(account: account).accountData.registerNotificationToken(token: token, type: .voip, sandbox: sandbox, otherAccountUserIds: (account.testingEnvironment ? activeTestingUserIds : activeProductionUserIds).filter({ $0 != account.peerId.id }), excludeMutedChats: !settings.includeMuted)
|
return account.engine.accountData.registerNotificationToken(token: token, type: .voip, sandbox: sandbox, otherAccountUserIds: (account.account.testingEnvironment ? activeTestingUserIds : activeProductionUserIds).filter({ $0 != account.account.peerId.id }), excludeMutedChats: !settings.includeMuted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -916,7 +925,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func switchToAccount(id: AccountRecordId, fromSettingsController settingsController: ViewController? = nil, withChatListController chatListController: ViewController? = nil) {
|
public func switchToAccount(id: AccountRecordId, fromSettingsController settingsController: ViewController? = nil, withChatListController chatListController: ViewController? = nil) {
|
||||||
if self.activeAccountsValue?.primary?.id == id {
|
if self.activeAccountsValue?.primary?.account.id == id {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -129,7 +129,7 @@ public final class SharedWakeupManager {
|
|||||||
|
|
||||||
let hasActiveCalls = (callManager?.currentCallSignal ?? .single(nil))
|
let hasActiveCalls = (callManager?.currentCallSignal ?? .single(nil))
|
||||||
|> map { call in
|
|> map { call in
|
||||||
return call?.account.id == account.id
|
return call?.context.account.id == account.id
|
||||||
}
|
}
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
|
|
||||||
|
|||||||
@ -107,8 +107,10 @@ private final class FdReadConnection {
|
|||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
assert(bytesRead == 4)
|
assert(bytesRead == 4)
|
||||||
assert(length > 0 && length <= 30 * 1024 * 1024)
|
if length > 0 {
|
||||||
strongSelf.currendData = PendingData(count: Int(length))
|
assert(length > 0 && length <= 30 * 1024 * 1024)
|
||||||
|
strongSelf.currendData = PendingData(count: Int(length))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -513,6 +515,7 @@ public final class IpcGroupCallBufferBroadcastContext {
|
|||||||
case callEnded
|
case callEnded
|
||||||
case error
|
case error
|
||||||
}
|
}
|
||||||
|
case active
|
||||||
case finished(FinishReason)
|
case finished(FinishReason)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -620,6 +623,8 @@ public final class IpcGroupCallBufferBroadcastContext {
|
|||||||
}, queue: .mainQueue())
|
}, queue: .mainQueue())
|
||||||
self.keepaliveInfoTimer = keepaliveInfoTimer
|
self.keepaliveInfoTimer = keepaliveInfoTimer
|
||||||
keepaliveInfoTimer.start()
|
keepaliveInfoTimer.start()
|
||||||
|
|
||||||
|
self.statusPromise.set(.single(.active))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -252,20 +252,7 @@ private func ongoingDataSavingForTypeWebrtc(_ type: VoiceCallDataSaving) -> Ongo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*private func ongoingDataSavingForTypeWebrtcCustom(_ type: VoiceCallDataSaving) -> OngoingCallDataSavingWebrtcCustom {
|
private protocol OngoingCallThreadLocalContextProtocol: AnyObject {
|
||||||
switch type {
|
|
||||||
case .never:
|
|
||||||
return .never
|
|
||||||
case .cellular:
|
|
||||||
return .cellular
|
|
||||||
case .always:
|
|
||||||
return .always
|
|
||||||
default:
|
|
||||||
return .never
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
private protocol OngoingCallThreadLocalContextProtocol: class {
|
|
||||||
func nativeSetNetworkType(_ type: NetworkType)
|
func nativeSetNetworkType(_ type: NetworkType)
|
||||||
func nativeSetIsMuted(_ value: Bool)
|
func nativeSetIsMuted(_ value: Bool)
|
||||||
func nativeSetIsLowBatteryLevel(_ value: Bool)
|
func nativeSetIsLowBatteryLevel(_ value: Bool)
|
||||||
@ -277,6 +264,7 @@ private protocol OngoingCallThreadLocalContextProtocol: class {
|
|||||||
func nativeDebugInfo() -> String
|
func nativeDebugInfo() -> String
|
||||||
func nativeVersion() -> String
|
func nativeVersion() -> String
|
||||||
func nativeGetDerivedState() -> Data
|
func nativeGetDerivedState() -> Data
|
||||||
|
func addExternalAudioData(data: Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class OngoingCallThreadLocalContextHolder {
|
private final class OngoingCallThreadLocalContextHolder {
|
||||||
@ -329,6 +317,9 @@ extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol {
|
|||||||
func nativeGetDerivedState() -> Data {
|
func nativeGetDerivedState() -> Data {
|
||||||
return self.getDerivedState()
|
return self.getDerivedState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addExternalAudioData(data: Data) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class OngoingCallVideoCapturer {
|
public final class OngoingCallVideoCapturer {
|
||||||
@ -517,6 +508,10 @@ extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProt
|
|||||||
func nativeGetDerivedState() -> Data {
|
func nativeGetDerivedState() -> Data {
|
||||||
return self.getDerivedState()
|
return self.getDerivedState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addExternalAudioData(data: Data) {
|
||||||
|
self.addExternalAudioData(data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension OngoingCallContextState.State {
|
private extension OngoingCallContextState.State {
|
||||||
@ -586,8 +581,6 @@ extension OngoingCallVideoOrientation {
|
|||||||
return .orientation180
|
return .orientation180
|
||||||
case .rotation270:
|
case .rotation270:
|
||||||
return .orientation270
|
return .orientation270
|
||||||
@unknown default:
|
|
||||||
return .orientation0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1026,4 +1019,10 @@ public final class OngoingCallContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func addExternalAudioData(data: Data) {
|
||||||
|
self.withContext { context in
|
||||||
|
context.addExternalAudioData(data: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -221,6 +221,8 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
|
|||||||
- (void)addSignalingData:(NSData * _Nonnull)data;
|
- (void)addSignalingData:(NSData * _Nonnull)data;
|
||||||
- (void)switchAudioOutput:(NSString * _Nonnull)deviceId;
|
- (void)switchAudioOutput:(NSString * _Nonnull)deviceId;
|
||||||
- (void)switchAudioInput:(NSString * _Nonnull)deviceId;
|
- (void)switchAudioInput:(NSString * _Nonnull)deviceId;
|
||||||
|
- (void)addExternalAudioData:(NSData * _Nonnull)data;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@ -1264,6 +1264,15 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||||||
_tgVoip->setAudioInputDevice(deviceId.UTF8String);
|
_tgVoip->setAudioInputDevice(deviceId.UTF8String);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)addExternalAudioData:(NSData * _Nonnull)data {
|
||||||
|
if (_tgVoip) {
|
||||||
|
std::vector<uint8_t> samples;
|
||||||
|
samples.resize(data.length);
|
||||||
|
[data getBytes:samples.data() length:data.length];
|
||||||
|
_tgVoip->addExternalAudioSamples(std::move(samples));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit ef796349808b187b80b75ea1876b940f2882fcbb
|
Subproject commit b2bc0201b3ae33982777c2d1ddbbc492c11b0625
|
||||||
Loading…
x
Reference in New Issue
Block a user