mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-09-05 04:10:16 +00:00
Support updated shared context
This commit is contained in:
parent
cb86c69d55
commit
6764936d2c
@ -88,6 +88,14 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
|
||||
initializeAccountManagement()
|
||||
let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata")
|
||||
|
||||
var initialPresentationDataAndSettings: InitialPresentationDataAndSettings?
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
let _ = currentPresentationDataAndSettings(accountManager: accountManager).start(next: { value in
|
||||
initialPresentationDataAndSettings = value
|
||||
semaphore.signal()
|
||||
})
|
||||
semaphore.wait()
|
||||
|
||||
let applicationBindings = TelegramApplicationBindings(isMainApp: false, containerPath: appGroupUrl.path, appSpecificScheme: BuildConfig.shared().appSpecificUrlScheme, openUrl: { _ in
|
||||
}, openUniversalUrl: { _, completion in
|
||||
completion.completion(false)
|
||||
@ -109,7 +117,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
|
||||
|
||||
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
|
||||
|
||||
sharedAccountContext = SharedAccountContext(accountManager: accountManager, applicationBindings: applicationBindings, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), rootPath: rootPath, apsNotificationToken: .never(), voipNotificationToken: .never())
|
||||
sharedAccountContext = SharedAccountContext(mainWindow: nil, accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), rootPath: rootPath, apsNotificationToken: .never(), voipNotificationToken: .never())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,7 +144,15 @@ class ShareRootController: UIViewController {
|
||||
|
||||
initializeAccountManagement()
|
||||
let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata")
|
||||
let sharedContext = SharedAccountContext(accountManager: accountManager, applicationBindings: applicationBindings, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), rootPath: rootPath, apsNotificationToken: .never(), voipNotificationToken: .never())
|
||||
var initialPresentationDataAndSettings: InitialPresentationDataAndSettings?
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
let _ = currentPresentationDataAndSettings(accountManager: accountManager).start(next: { value in
|
||||
initialPresentationDataAndSettings = value
|
||||
semaphore.signal()
|
||||
})
|
||||
semaphore.wait()
|
||||
|
||||
let sharedContext = SharedAccountContext(mainWindow: nil, accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), rootPath: rootPath, apsNotificationToken: .never(), voipNotificationToken: .never())
|
||||
|
||||
account = accountManager.transaction { transaction -> (SharedAccountContext, LoggingSettings) in
|
||||
return (sharedContext, transaction.getSharedData(SharedDataKeys.loggingSettings) as? LoggingSettings ?? LoggingSettings.defaultSettings)
|
||||
@ -181,14 +189,17 @@ class ShareRootController: UIViewController {
|
||||
let shouldBeMaster = self.shouldBeMaster
|
||||
let applicationInterface = account
|
||||
|> mapToSignal { sharedContext, account -> Signal<(AccountContext, PostboxAccessChallengeData), ShareAuthorizationError> in
|
||||
return combineLatest(sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]), currentPresentationDataAndSettings(accountManager: sharedContext.accountManager, postbox: account.postbox), sharedContext.accountManager.accessChallengeData())
|
||||
let limitsConfiguration = account.postbox.transaction { transaction -> LimitsConfiguration in
|
||||
return transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration) as? LimitsConfiguration ?? LimitsConfiguration.defaultValue
|
||||
}
|
||||
return combineLatest(sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]), limitsConfiguration, sharedContext.accountManager.accessChallengeData())
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue
|
||||
|> introduceError(ShareAuthorizationError.self)
|
||||
|> map { sharedData, dataAndSettings, data -> (AccountContext, PostboxAccessChallengeData) in
|
||||
|> map { sharedData, limitsConfiguration, data -> (AccountContext, PostboxAccessChallengeData) in
|
||||
accountCache = (sharedContext, account)
|
||||
updateLegacyLocalization(strings: dataAndSettings.presentationData.strings)
|
||||
let context = AccountContext(sharedContext: sharedContext, account: account, initialPresentationDataAndSettings: dataAndSettings)
|
||||
updateLegacyLocalization(strings: sharedContext.currentPresentationData.with({ $0 }).strings)
|
||||
let context = AccountContext(sharedContext: sharedContext, account: account, limitsConfiguration: limitsConfiguration)
|
||||
return (context, data.data)
|
||||
}
|
||||
}
|
||||
|
@ -361,6 +361,8 @@
|
||||
D0AF32291FACA1920097362B /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D17E891CAAD66600C4750B /* Accelerate.framework */; };
|
||||
D0AF322C1FACA1B00097362B /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D0B8445F1DACF561005F29E1 /* libc++.tbd */; };
|
||||
D0AF322F1FACBA280097362B /* TGShareLocationSignals.m in Sources */ = {isa = PBXBuildFile; fileRef = D0AF322D1FACBA270097362B /* TGShareLocationSignals.m */; };
|
||||
D0B21B0D2203A9A1003F741D /* SharedWakeupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B21B0C2203A9A1003F741D /* SharedWakeupManager.swift */; };
|
||||
D0B21B0F220438E9003F741D /* SharedNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B21B0E220438E9003F741D /* SharedNotificationManager.swift */; };
|
||||
D0B2F738204F4C9900D3BFB9 /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E41A381D65A69C00FBFC00 /* NotificationCenter.framework */; };
|
||||
D0B2F742204F4C9900D3BFB9 /* Widget.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D0B2F737204F4C9900D3BFB9 /* Widget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
D0B2F74A204F4D6100D3BFB9 /* Postbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0B2F74F204F4D6100D3BFB9 /* Postbox.framework */; };
|
||||
@ -1092,6 +1094,8 @@
|
||||
D0AF322A1FACA1A80097362B /* libstdc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libstdc++.tbd"; path = "usr/lib/libstdc++.tbd"; sourceTree = SDKROOT; };
|
||||
D0AF322D1FACBA270097362B /* TGShareLocationSignals.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGShareLocationSignals.m; sourceTree = "<group>"; };
|
||||
D0AF322E1FACBA270097362B /* TGShareLocationSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGShareLocationSignals.h; sourceTree = "<group>"; };
|
||||
D0B21B0C2203A9A1003F741D /* SharedWakeupManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedWakeupManager.swift; sourceTree = "<group>"; };
|
||||
D0B21B0E220438E9003F741D /* SharedNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedNotificationManager.swift; sourceTree = "<group>"; };
|
||||
D0B2F737204F4C9900D3BFB9 /* Widget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Widget.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D0B2F74E204F4D6100D3BFB9 /* Display.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Display.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D0B2F74F204F4D6100D3BFB9 /* Postbox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Postbox.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -1975,6 +1979,8 @@
|
||||
D0ADF956212B56C200310BBC /* Legacy Data Import */,
|
||||
D0ECCB8B1FE9CE2B00609802 /* Snapshots */,
|
||||
D008599F1B28189D00EAF753 /* Supporting Files */,
|
||||
D0B21B0C2203A9A1003F741D /* SharedWakeupManager.swift */,
|
||||
D0B21B0E220438E9003F741D /* SharedNotificationManager.swift */,
|
||||
);
|
||||
path = "Telegram-iOS";
|
||||
sourceTree = "<group>";
|
||||
@ -3212,6 +3218,7 @@
|
||||
D039FB172170F06A00BD1BAD /* PreFetchedLegacyResource.swift in Sources */,
|
||||
09D304292174343300C00567 /* TGBridgeBotInfo.m in Sources */,
|
||||
09D304272174341E00C00567 /* TGBridgeChatMessages.m in Sources */,
|
||||
D0B21B0F220438E9003F741D /* SharedNotificationManager.swift in Sources */,
|
||||
D02E31231BD803E800CD3F01 /* main.m in Sources */,
|
||||
D05B37FD1FEA8D870041D2A5 /* SnapshotResources.swift in Sources */,
|
||||
D0EB243B201B77C400F6CC13 /* ClearNotificationsManager.swift in Sources */,
|
||||
@ -3221,6 +3228,7 @@
|
||||
D06E4C2F21347D9200088087 /* UIImage+ImageEffects.m in Sources */,
|
||||
D0B3B53B21666C0000FC60A0 /* LegacyFileImport.swift in Sources */,
|
||||
D0ADF95E212C818F00310BBC /* LegacyPreferencesImport.swift in Sources */,
|
||||
D0B21B0D2203A9A1003F741D /* SharedWakeupManager.swift in Sources */,
|
||||
09D304282174342E00C00567 /* TGBridgeChat.m in Sources */,
|
||||
09C50E8A2173AEDB009E676F /* WatchRequestHandlers.swift in Sources */,
|
||||
09D304302174344900C00567 /* TGBridgeForwardedMessageMediaAttachment.m in Sources */,
|
||||
|
@ -120,6 +120,18 @@ private enum QueuedWakeup: Int32 {
|
||||
case backgroundLocation
|
||||
}
|
||||
|
||||
private final class SharedApplicationContext {
|
||||
let sharedContext: SharedAccountContext
|
||||
let notificationManager: SharedNotificationManager
|
||||
let wakeupManager: SharedWakeupManager
|
||||
|
||||
init(sharedContext: SharedAccountContext, notificationManager: SharedNotificationManager, wakeupManager: SharedWakeupManager) {
|
||||
self.sharedContext = sharedContext
|
||||
self.notificationManager = notificationManager
|
||||
self.wakeupManager = wakeupManager
|
||||
}
|
||||
}
|
||||
|
||||
@objc(AppDelegate) class AppDelegate: UIResponder, UIApplicationDelegate, PKPushRegistryDelegate, BITHockeyManagerDelegate, UNUserNotificationCenterDelegate, UIAlertViewDelegate {
|
||||
@objc var window: UIWindow?
|
||||
var nativeWindow: (UIWindow & WindowHost)?
|
||||
@ -134,13 +146,17 @@ private enum QueuedWakeup: Int32 {
|
||||
private var isActiveValue = false
|
||||
let hasActiveAudioSession = Promise<Bool>(false)
|
||||
|
||||
private let sharedContextPromise = Promise<SharedAccountContext>()
|
||||
private let sharedContextPromise = Promise<SharedApplicationContext>()
|
||||
private let watchCommunicationManagerPromise = Promise<WatchCommunicationManager?>()
|
||||
|
||||
private var contextValue: ApplicationContext?
|
||||
private let context = Promise<ApplicationContext?>()
|
||||
private var contextValue: AuthorizedApplicationContext?
|
||||
private let context = Promise<AuthorizedApplicationContext?>()
|
||||
private let contextDisposable = MetaDisposable()
|
||||
|
||||
private var authContextValue: UnauthorizedApplicationContext?
|
||||
private let authContext = Promise<UnauthorizedApplicationContext?>()
|
||||
private let authContextDisposable = MetaDisposable()
|
||||
|
||||
private let openChatWhenReadyDisposable = MetaDisposable()
|
||||
private let openUrlWhenReadyDisposable = MetaDisposable()
|
||||
|
||||
@ -193,8 +209,8 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
}
|
||||
|
||||
private var queuedNotifications: [PKPushPayload] = []
|
||||
private var queuedNotificationRequests: [(String, String, String?, NotificationManagedNotificationRequestId)] = []
|
||||
//private var queuedNotifications: [[AnyHashable: Any]] = []
|
||||
//private var queuedNotificationRequests: [(String, String, String?, NotificationManagedNotificationRequestId)] = []
|
||||
private var queuedMutePolling = false
|
||||
private var queuedAnnouncements: [String] = []
|
||||
private var queuedWakeups = Set<QueuedWakeup>()
|
||||
@ -220,7 +236,7 @@ private enum QueuedWakeup: Int32 {
|
||||
self.window = window
|
||||
self.nativeWindow = window
|
||||
|
||||
self.clearNotificationsManager = ClearNotificationsManager(getNotificationIds: { completion in
|
||||
let clearNotificationsManager = ClearNotificationsManager(getNotificationIds: { completion in
|
||||
if #available(iOS 10.0, *) {
|
||||
UNUserNotificationCenter.current().getDeliveredNotifications(completionHandler: { notifications in
|
||||
var result: [(String, NotificationManagedNotificationRequestId)] = []
|
||||
@ -323,6 +339,7 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
}
|
||||
})
|
||||
self.clearNotificationsManager = clearNotificationsManager
|
||||
|
||||
#if DEBUG
|
||||
for argument in ProcessInfo.processInfo.arguments {
|
||||
@ -543,7 +560,7 @@ private enum QueuedWakeup: Int32 {
|
||||
UIApplication.shared.openURL(url)
|
||||
}
|
||||
}, registerForNotifications: { completion in
|
||||
let _ = (self.currentAuthorizedContext()
|
||||
let _ = (self.context.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { context in
|
||||
if let context = context {
|
||||
@ -584,18 +601,51 @@ private enum QueuedWakeup: Int32 {
|
||||
})
|
||||
|
||||
let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata")
|
||||
let sharedContext = SharedAccountContext(accountManager: accountManager, applicationBindings: applicationBindings, networkArguments: networkArguments, rootPath: rootPath, apsNotificationToken: self.notificationTokenPromise.get() |> map(Optional.init), voipNotificationToken: self.voipTokenPromise.get() |> map(Optional.init))
|
||||
var initialPresentationDataAndSettings: InitialPresentationDataAndSettings?
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
let _ = currentPresentationDataAndSettings(accountManager: accountManager).start(next: { value in
|
||||
initialPresentationDataAndSettings = value
|
||||
semaphore.signal()
|
||||
})
|
||||
semaphore.wait()
|
||||
|
||||
self.sharedContextPromise.set(
|
||||
accountManager.transaction { transaction -> (SharedAccountContext, LoggingSettings) in
|
||||
return (sharedContext, transaction.getSharedData(SharedDataKeys.loggingSettings) as? LoggingSettings ?? LoggingSettings.defaultSettings)
|
||||
let sharedContext = SharedAccountContext(mainWindow: self.mainWindow, accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: networkArguments, rootPath: rootPath, apsNotificationToken: self.notificationTokenPromise.get() |> map(Optional.init), voipNotificationToken: self.voipTokenPromise.get() |> map(Optional.init))
|
||||
sharedContext.presentGlobalController = { [weak self] c, a in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.mainWindow.present(c, on: .root)
|
||||
}
|
||||
|> mapToSignal { sharedContext, loggingSettings -> Signal<SharedAccountContext, NoError> in
|
||||
sharedContext.presentCrossfadeController = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
var exists = false
|
||||
strongSelf.mainWindow.forEachViewController { controller in
|
||||
if controller is ThemeSettingsCrossfadeController {
|
||||
exists = true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if !exists {
|
||||
strongSelf.mainWindow.present(ThemeSettingsCrossfadeController(), on: .root)
|
||||
}
|
||||
}
|
||||
|
||||
let notificationManager = SharedNotificationManager(episodeId: self.episodeId, clearNotificationsManager: clearNotificationsManager, inForeground: applicationBindings.applicationInForeground, accounts: sharedContext.activeAccounts |> map { primary, accounts, _ in Array(accounts.values.map({ ($0, $0.id == primary?.id) })) })
|
||||
let wakeupManager = SharedWakeupManager(activeAccounts: sharedContext.activeAccounts |> map { ($0.0, $0.1) }, inForeground: applicationBindings.applicationInForeground, hasActiveAudioSession: hasActiveAudioSession.get(), notificationManager: notificationManager)
|
||||
let sharedApplicationContext = SharedApplicationContext(sharedContext: sharedContext, notificationManager: notificationManager, wakeupManager: wakeupManager)
|
||||
self.sharedContextPromise.set(
|
||||
accountManager.transaction { transaction -> (SharedApplicationContext, LoggingSettings) in
|
||||
return (sharedApplicationContext, transaction.getSharedData(SharedDataKeys.loggingSettings) as? LoggingSettings ?? LoggingSettings.defaultSettings)
|
||||
}
|
||||
|> mapToSignal { sharedApplicationContext, loggingSettings -> Signal<SharedApplicationContext, NoError> in
|
||||
Logger.shared.logToFile = loggingSettings.logToFile
|
||||
Logger.shared.logToConsole = loggingSettings.logToConsole
|
||||
Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData
|
||||
|
||||
return importedLegacyAccount(basePath: appGroupUrl.path, accountManager: sharedContext.accountManager, present: { controller in
|
||||
return importedLegacyAccount(basePath: appGroupUrl.path, accountManager: sharedApplicationContext.sharedContext.accountManager, present: { controller in
|
||||
self.window?.rootViewController?.present(controller, animated: true, completion: nil)
|
||||
})
|
||||
|> `catch` { _ -> Signal<ImportedLegacyAccountEvent, NoError> in
|
||||
@ -615,7 +665,7 @@ private enum QueuedWakeup: Int32 {
|
||||
return EmptyDisposable
|
||||
} |> runOn(Queue.mainQueue())
|
||||
}
|
||||
|> mapToSignal { event -> Signal<SharedAccountContext, NoError> in
|
||||
|> mapToSignal { event -> Signal<SharedApplicationContext, NoError> in
|
||||
switch event {
|
||||
case let .progress(type, value):
|
||||
Queue.mainQueue().async {
|
||||
@ -642,7 +692,7 @@ private enum QueuedWakeup: Int32 {
|
||||
let _ = try? FileManager.default.createDirectory(atPath: appGroupUrl.path + "/Documents", withIntermediateDirectories: true, attributes: nil)
|
||||
let _ = try? Data().write(to: URL(fileURLWithPath: statusPath))
|
||||
})
|
||||
return sharedContext.accountManager.transaction { transaction -> SharedAccountContext in
|
||||
return sharedApplicationContext.sharedContext.accountManager.transaction { transaction -> SharedApplicationContext in
|
||||
transaction.setCurrentId(temporaryId)
|
||||
transaction.updateRecord(temporaryId, { record in
|
||||
if let record = record {
|
||||
@ -650,10 +700,10 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
return record
|
||||
})
|
||||
return sharedContext
|
||||
return sharedApplicationContext
|
||||
}
|
||||
} else {
|
||||
return .single(sharedContext)
|
||||
return .single(sharedApplicationContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -672,10 +722,55 @@ private enum QueuedWakeup: Int32 {
|
||||
|
||||
self.context.set(self.sharedContextPromise.get()
|
||||
|> deliverOnMainQueue
|
||||
|> mapToSignal { sharedContext -> Signal<ApplicationContext?, NoError> in
|
||||
return sharedContext.activeAccounts
|
||||
|> map { primary, _, auth -> (AnyObject?, Bool) in
|
||||
return (auth ?? primary, primary != nil)
|
||||
|> mapToSignal { sharedApplicationContext -> Signal<AuthorizedApplicationContext?, NoError> in
|
||||
return sharedApplicationContext.sharedContext.activeAccounts
|
||||
|> map { primary, _, _ -> Account? in
|
||||
return primary
|
||||
}
|
||||
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||
if lhs !== rhs {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|> mapToSignal { account -> Signal<(Account, LimitsConfiguration, CallListSettings)?, NoError> in
|
||||
return sharedApplicationContext.sharedContext.accountManager.transaction { transaction -> CallListSettings in
|
||||
return transaction.getSharedData(ApplicationSpecificSharedDataKeys.callListSettings) as? CallListSettings ?? CallListSettings.defaultSettings
|
||||
}
|
||||
|> mapToSignal { callListSettings -> Signal<(Account, LimitsConfiguration, CallListSettings)?, NoError> in
|
||||
if let account = account {
|
||||
return account.postbox.transaction { transaction -> (Account, LimitsConfiguration, CallListSettings)? in
|
||||
let limitsConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration) as? LimitsConfiguration ?? LimitsConfiguration.defaultValue
|
||||
return (account, limitsConfiguration, callListSettings)
|
||||
}
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue
|
||||
|> map { accountAndSettings -> AuthorizedApplicationContext? in
|
||||
return accountAndSettings.flatMap { account, limitsConfiguration, callListSettings in
|
||||
let context = AccountContext(sharedContext: sharedApplicationContext.sharedContext, account: account, limitsConfiguration: limitsConfiguration)
|
||||
return AuthorizedApplicationContext(mainWindow: self.mainWindow, replyFromNotificationsActive: replyFromNotificationsActive, backgroundAudioActive: backgroundAudioActive, watchManagerArguments: watchManagerArgumentsPromise.get(), context: context, accountManager: sharedApplicationContext.sharedContext.accountManager, legacyBasePath: appGroupUrl.path, showCallsTab: callListSettings.showTab, reinitializedNotificationSettings: {
|
||||
let _ = (self.context.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { context in
|
||||
if let context = context {
|
||||
self.registerForNotifications(context: context.context, authorize: false)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.authContext.set(self.sharedContextPromise.get()
|
||||
|> deliverOnMainQueue
|
||||
|> mapToSignal { sharedApplicationContext -> Signal<UnauthorizedApplicationContext?, NoError> in
|
||||
return sharedApplicationContext.sharedContext.activeAccounts
|
||||
|> map { _, accounts, auth -> (UnauthorizedAccount?, Bool) in
|
||||
return (auth, !accounts.isEmpty)
|
||||
}
|
||||
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||
if lhs.0 !== rhs.0 {
|
||||
@ -683,102 +778,64 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
return true
|
||||
})
|
||||
|> mapToSignal { account, hasOther -> Signal<(AnyObject, InitialPresentationDataAndSettings, Bool)?, NoError> in
|
||||
if let account = account as? Account {
|
||||
return currentPresentationDataAndSettings(accountManager: sharedContext.accountManager, postbox: account.postbox)
|
||||
|> map { initialSettings in
|
||||
return (account, initialSettings, hasOther)
|
||||
|> mapToSignal { account, hasOther -> Signal<(UnauthorizedAccount, LimitsConfiguration, CallListSettings, Bool)?, NoError> in
|
||||
return sharedApplicationContext.sharedContext.accountManager.transaction { transaction -> CallListSettings in
|
||||
return transaction.getSharedData(ApplicationSpecificSharedDataKeys.callListSettings) as? CallListSettings ?? CallListSettings.defaultSettings
|
||||
}
|
||||
} else if let account = account as? UnauthorizedAccount {
|
||||
return currentPresentationDataAndSettings(accountManager: sharedContext.accountManager, postbox: account.postbox)
|
||||
|> map { initialSettings in
|
||||
return (account, initialSettings, hasOther)
|
||||
|> mapToSignal { callListSettings -> Signal<(UnauthorizedAccount, LimitsConfiguration, CallListSettings, Bool)?, NoError> in
|
||||
if let account = account {
|
||||
return account.postbox.transaction { transaction -> (UnauthorizedAccount, LimitsConfiguration, CallListSettings, Bool)? in
|
||||
let limitsConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration) as? LimitsConfiguration ?? LimitsConfiguration.defaultValue
|
||||
return (account, limitsConfiguration, callListSettings, hasOther)
|
||||
}
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue
|
||||
|> map { accountAndSettings -> ApplicationContext? in
|
||||
return accountAndSettings.flatMap { account, initialSettings, hasOther in
|
||||
if let account = account as? Account {
|
||||
let context = AccountContext(sharedContext: sharedContext, account: account, initialPresentationDataAndSettings: initialSettings)
|
||||
return ApplicationContext.authorized(AuthorizedApplicationContext(mainWindow: self.mainWindow, replyFromNotificationsActive: replyFromNotificationsActive, backgroundAudioActive: backgroundAudioActive, watchManagerArguments: watchManagerArgumentsPromise.get(), context: context, accountManager: sharedContext.accountManager, legacyBasePath: appGroupUrl.path, showCallsTab: initialSettings.callListSettings.showTab, reinitializedNotificationSettings: {
|
||||
let _ = (self.context.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { value in
|
||||
if let value = value, case let .authorized(context) = value {
|
||||
self.registerForNotifications(context: context.context, authorize: false)
|
||||
}
|
||||
})
|
||||
}))
|
||||
} else if let account = account as? UnauthorizedAccount {
|
||||
return ApplicationContext.unauthorized(UnauthorizedApplicationContext(sharedContext: sharedContext, account: account, hasOtherAccounts: hasOther))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|> map { accountAndSettings -> UnauthorizedApplicationContext? in
|
||||
return accountAndSettings.flatMap { account, limitsConfiguration, callListSettings, hasOther in
|
||||
return UnauthorizedApplicationContext(sharedContext: sharedApplicationContext.sharedContext, account: account, hasOtherAccounts: hasOther)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let contextReadyDisposable = MetaDisposable()
|
||||
|
||||
self.contextDisposable.set(self.context.get().start(next: { context in
|
||||
assert(Queue.mainQueue().isCurrent())
|
||||
self.contextDisposable.set((self.context.get()
|
||||
|> deliverOnMainQueue).start(next: { context in
|
||||
var network: Network?
|
||||
if let context = context {
|
||||
switch context {
|
||||
case let .unauthorized(unauthorized):
|
||||
network = unauthorized.account.network
|
||||
case let .authorized(authorized):
|
||||
network = authorized.context.account.network
|
||||
}
|
||||
network = context.context.account.network
|
||||
}
|
||||
|
||||
Logger.shared.log("App \(self.episodeId)", "received context \(String(describing: context)) account \(String(describing: context?.accountId)) network \(String(describing: network))")
|
||||
Logger.shared.log("App \(self.episodeId)", "received context \(String(describing: context)) account \(String(describing: context?.context.account.id)) network \(String(describing: network))")
|
||||
|
||||
if let contextValue = self.contextValue {
|
||||
switch contextValue {
|
||||
case let .unauthorized(unauthorized):
|
||||
unauthorized.account.shouldBeServiceTaskMaster.set(.single(.never))
|
||||
case let .authorized(authorized):
|
||||
authorized.context.isCurrent = false
|
||||
authorized.context.account.shouldBeServiceTaskMaster.set(.single(.never))
|
||||
authorized.context.account.shouldKeepOnlinePresence.set(.single(false))
|
||||
authorized.context.account.shouldExplicitelyKeepWorkerConnections.set(.single(false))
|
||||
authorized.context.account.shouldKeepBackgroundDownloadConnections.set(.single(false))
|
||||
}
|
||||
contextValue.context.isCurrent = false
|
||||
contextValue.context.account.shouldExplicitelyKeepWorkerConnections.set(.single(false))
|
||||
contextValue.context.account.shouldKeepBackgroundDownloadConnections.set(.single(false))
|
||||
}
|
||||
self.contextValue = context
|
||||
if let context = context {
|
||||
let isReady: Signal<Bool, NoError>
|
||||
switch context {
|
||||
case let .authorized(authorized):
|
||||
setupLegacyComponents(context: authorized.context)
|
||||
authorized.context.isCurrent = true
|
||||
isReady = authorized.isReady.get()
|
||||
default:
|
||||
isReady = .single(true)
|
||||
}
|
||||
setupLegacyComponents(context: context.context)
|
||||
context.context.isCurrent = true
|
||||
let isReady = context.isReady.get()
|
||||
contextReadyDisposable.set((isReady
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { _ in
|
||||
self.mainWindow.viewController = context.rootController
|
||||
self.mainWindow.topLevelOverlayControllers = context.overlayControllers
|
||||
self.maybeDequeueNotificationPayloads()
|
||||
self.mainWindow.topLevelOverlayControllers = [context.overlayMediaController, context.notificationController]
|
||||
/*self.maybeDequeueNotificationPayloads()
|
||||
self.maybeDequeueNotificationRequests()
|
||||
self.maybeDequeueWakeups()
|
||||
switch context {
|
||||
case let .authorized(context):
|
||||
var authorizeNotifications = true
|
||||
if #available(iOS 10.0, *) {
|
||||
authorizeNotifications = false
|
||||
}
|
||||
self.registerForNotifications(context: context.context, authorize: authorizeNotifications)
|
||||
case .unauthorized:
|
||||
break
|
||||
self.maybeDequeueWakeups()*/
|
||||
var authorizeNotifications = true
|
||||
if #available(iOS 10.0, *) {
|
||||
authorizeNotifications = false
|
||||
}
|
||||
self.registerForNotifications(context: context.context, authorize: authorizeNotifications)
|
||||
}))
|
||||
} else {
|
||||
self.mainWindow.viewController = nil
|
||||
@ -787,6 +844,38 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
}))
|
||||
|
||||
let authContextReadyDisposable = MetaDisposable()
|
||||
|
||||
self.authContextDisposable.set((self.authContext.get()
|
||||
|> deliverOnMainQueue).start(next: { context in
|
||||
var network: Network?
|
||||
if let context = context {
|
||||
network = context.account.network
|
||||
}
|
||||
|
||||
Logger.shared.log("App \(self.episodeId)", "received auth context \(String(describing: context)) account \(String(describing: context?.account.id)) network \(String(describing: network))")
|
||||
|
||||
if let authContextValue = self.authContextValue {
|
||||
authContextValue.account.shouldBeServiceTaskMaster.set(.single(.never))
|
||||
authContextValue.rootController.view.endEditing(true)
|
||||
authContextValue.rootController.dismiss()
|
||||
}
|
||||
self.authContextValue = context
|
||||
if let context = context {
|
||||
let isReady: Signal<Bool, NoError> = .single(true)
|
||||
authContextReadyDisposable.set((isReady
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { _ in
|
||||
self.mainWindow.present(context.rootController, on: .root)
|
||||
//self.mainWindow.viewController = context.rootController
|
||||
//self.mainWindow.topLevelOverlayControllers = context.overlayControllers
|
||||
}))
|
||||
} else {
|
||||
authContextReadyDisposable.set(nil)
|
||||
}
|
||||
}))
|
||||
|
||||
self.watchCommunicationManagerPromise.set(watchCommunicationManager(context: self.context))
|
||||
let _ = self.watchCommunicationManagerPromise.get().start(next: { manager in
|
||||
if let manager = manager {
|
||||
@ -804,14 +893,9 @@ private enum QueuedWakeup: Int32 {
|
||||
self.badgeDisposable.set((self.context.get()
|
||||
|> mapToSignal { context -> Signal<Int32, NoError> in
|
||||
if let context = context {
|
||||
switch context {
|
||||
case let .authorized(context):
|
||||
return context.applicationBadge
|
||||
case .unauthorized:
|
||||
return .single(0)
|
||||
}
|
||||
return context.applicationBadge
|
||||
} else {
|
||||
return .never()
|
||||
return .single(0)
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { count in
|
||||
@ -822,15 +906,10 @@ private enum QueuedWakeup: Int32 {
|
||||
self.quickActionsDisposable.set((self.context.get()
|
||||
|> mapToSignal { context -> Signal<[ApplicationShortcutItem], NoError> in
|
||||
if let context = context {
|
||||
switch context {
|
||||
case let .authorized(context):
|
||||
let presentationData = context.context.currentPresentationData.with { $0 }
|
||||
return .single(applicationShortcutItems(strings: presentationData.strings))
|
||||
case .unauthorized:
|
||||
return .single([])
|
||||
}
|
||||
let presentationData = context.context.sharedContext.currentPresentationData.with { $0 }
|
||||
return .single(applicationShortcutItems(strings: presentationData.strings))
|
||||
} else {
|
||||
return .never()
|
||||
return .single([])
|
||||
}
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
@ -876,14 +955,8 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
}))
|
||||
}
|
||||
if let contextValue = strongSelf.contextValue {
|
||||
if case let .authorized(context) = contextValue {
|
||||
let presentationData = context.context.currentPresentationData.with { $0 }
|
||||
strongSelf.mainWindow.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: alert.title, text: alert.message ?? "", actions: actions), on: .root)
|
||||
} else if case let .unauthorized(context) = contextValue {
|
||||
strongSelf.mainWindow.present(standardTextAlertController(theme: AlertControllerTheme(authTheme: context.rootController.theme), title: alert.title, text: alert.message ?? "", actions: actions), on: .root)
|
||||
}
|
||||
}
|
||||
let presentationData = sharedContext.currentPresentationData.with { $0 }
|
||||
strongSelf.mainWindow.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: alert.title, text: alert.message ?? "", actions: actions), on: .root)
|
||||
}
|
||||
})
|
||||
|
||||
@ -916,6 +989,12 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
|
||||
func applicationDidEnterBackground(_ application: UIApplication) {
|
||||
let _ = (self.sharedContextPromise.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { sharedApplicationContext in
|
||||
sharedApplicationContext.wakeupManager.allowBackgroundTimeExtension(timeout: 4.0)
|
||||
})
|
||||
|
||||
self.isInForegroundValue = false
|
||||
self.isInForegroundPromise.set(false)
|
||||
self.isActiveValue = false
|
||||
@ -964,6 +1043,12 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
|
||||
let _ = (self.sharedContextPromise.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { sharedApplicationContext in
|
||||
sharedApplicationContext.wakeupManager.allowBackgroundTimeExtension(timeout: 4.0)
|
||||
})
|
||||
|
||||
var redactedPayload = userInfo
|
||||
if var aps = redactedPayload["aps"] as? [AnyHashable: Any] {
|
||||
if Logger.shared.redactSensitiveData {
|
||||
@ -977,7 +1062,6 @@ private enum QueuedWakeup: Int32 {
|
||||
redactedPayload["aps"] = aps
|
||||
}
|
||||
|
||||
|
||||
Logger.shared.log("App \(self.episodeId)", "remoteNotification: \(redactedPayload)")
|
||||
completionHandler(UIBackgroundFetchResult.noData)
|
||||
}
|
||||
@ -996,28 +1080,24 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
}
|
||||
|
||||
private var pushCnt = 0
|
||||
public func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
|
||||
if case PKPushType.voIP = type {
|
||||
Logger.shared.log("App \(self.episodeId)", "pushRegistry payload: \(payload.dictionaryPayload)")
|
||||
/*#if DEBUG
|
||||
self.pushCnt += 1
|
||||
if self.pushCnt % 2 != 0 {
|
||||
Logger.shared.log("App \(self.episodeId)", "pushRegistry payload drop")
|
||||
return
|
||||
}
|
||||
#endif*/
|
||||
let _ = (self.sharedContextPromise.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { sharedApplicationContext in
|
||||
sharedApplicationContext.wakeupManager.allowBackgroundTimeExtension(timeout: 4.0)
|
||||
|
||||
self.queuedNotifications.append(payload)
|
||||
self.maybeDequeueNotificationPayloads()
|
||||
}
|
||||
if case PKPushType.voIP = type {
|
||||
Logger.shared.log("App \(self.episodeId)", "pushRegistry payload: \(payload.dictionaryPayload)")
|
||||
sharedApplicationContext.notificationManager.addEncryptedNotification(payload.dictionaryPayload)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private func processPushPayload(_ payload: PKPushPayload, account: Account) {
|
||||
/*private func processPushPayload(_ payload: [AnyHashable: Any], account: Account) {
|
||||
let decryptedPayload: Signal<[AnyHashable: Any]?, NoError>
|
||||
if let _ = payload.dictionaryPayload["aps"] as? [AnyHashable: Any] {
|
||||
decryptedPayload = .single(payload.dictionaryPayload as [AnyHashable: Any])
|
||||
} else if var encryptedPayload = payload.dictionaryPayload["p"] as? String {
|
||||
if let _ = payload["aps"] as? [AnyHashable: Any] {
|
||||
decryptedPayload = .single(payload)
|
||||
} else if var encryptedPayload = payload["p"] as? String {
|
||||
encryptedPayload = encryptedPayload.replacingOccurrences(of: "-", with: "+")
|
||||
encryptedPayload = encryptedPayload.replacingOccurrences(of: "_", with: "/")
|
||||
while encryptedPayload.count % 4 != 0 {
|
||||
@ -1234,7 +1314,7 @@ private enum QueuedWakeup: Int32 {
|
||||
self.clearNotificationsManager?.append(readMessageId)
|
||||
self.clearNotificationsManager?.commitNow()
|
||||
|
||||
let signal = self.currentAuthorizedContext()
|
||||
let signal = self.context.get()
|
||||
|> take(1)
|
||||
|> mapToSignal { context -> Signal<Void, NoError> in
|
||||
if let context = context {
|
||||
@ -1249,7 +1329,7 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
|
||||
if let (datacenterId, host, port, secret) = configurationUpdate {
|
||||
let signal = self.currentAuthorizedContext()
|
||||
let signal = self.context.get()
|
||||
|> take(1)
|
||||
|> mapToSignal { context -> Signal<Void, NoError> in
|
||||
if let context = context {
|
||||
@ -1261,28 +1341,16 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}*/
|
||||
|
||||
public func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
|
||||
Logger.shared.log("App \(self.episodeId)", "invalidated token for \(type)")
|
||||
}
|
||||
|
||||
private func currentAuthorizedContext() -> Signal<AuthorizedApplicationContext?, NoError> {
|
||||
return self.context.get()
|
||||
|> take(1)
|
||||
|> mapToSignal { contextValue -> Signal<AuthorizedApplicationContext?, NoError> in
|
||||
if let contextValue = contextValue, case let .authorized(context) = contextValue {
|
||||
return .single(context)
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func authorizedContext() -> Signal<AuthorizedApplicationContext, NoError> {
|
||||
return self.context.get()
|
||||
|> mapToSignal { contextValue -> Signal<AuthorizedApplicationContext, NoError> in
|
||||
if let contextValue = contextValue, case let .authorized(context) = contextValue {
|
||||
|> mapToSignal { context -> Signal<AuthorizedApplicationContext, NoError> in
|
||||
if let context = context {
|
||||
return .single(context)
|
||||
} else {
|
||||
return .complete()
|
||||
@ -1311,30 +1379,35 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
|
||||
private func openUrl(url: URL) {
|
||||
let _ = (self.context.get()
|
||||
|> flatMap { $0 }
|
||||
let _ = (self.sharedContextPromise.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { contextValue in
|
||||
switch contextValue {
|
||||
case let .authorized(context):
|
||||
context.openUrl(url)
|
||||
case let .unauthorized(context):
|
||||
if let proxyData = parseProxyUrl(url) {
|
||||
context.rootController.view.endEditing(true)
|
||||
let strings = context.strings
|
||||
let controller = ProxyServerActionSheetController(theme: defaultPresentationTheme, strings: strings, accountManager: context.sharedContext.accountManager, postbox: context.account.postbox, network: context.account.network, server: proxyData, presentationData: nil)
|
||||
context.rootController.currentWindow?.present(controller, on: PresentationSurfaceLevel.root, blockInteraction: false, completion: {})
|
||||
} else if let secureIdData = parseSecureIdUrl(url) {
|
||||
let strings = context.strings
|
||||
let theme = context.rootController.theme
|
||||
context.rootController.currentWindow?.present(standardTextAlertController(theme: AlertControllerTheme(authTheme: theme), title: nil, text: strings.Passport_NotLoggedInMessage, actions: [TextAlertAction(type: .genericAction, title: strings.Calls_NotNow, action: {
|
||||
if let callbackUrl = URL(string: secureIdCallbackUrl(with: secureIdData.callbackUrl, peerId: secureIdData.peerId, result: .cancel, parameters: [:])) {
|
||||
UIApplication.shared.openURL(callbackUrl)
|
||||
}
|
||||
}), TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})]), on: .root, blockInteraction: false, completion: {})
|
||||
} else if let confirmationCode = parseConfirmationCodeUrl(url) {
|
||||
context.rootController.applyConfirmationCode(confirmationCode)
|
||||
}
|
||||
|> mapToSignal { sharedApplicationContext -> Signal<(SharedAccountContext, AuthorizedApplicationContext?, UnauthorizedApplicationContext?), NoError> in
|
||||
combineLatest(self.context.get(), self.authContext.get())
|
||||
|> filter { $0 != nil || $1 != nil }
|
||||
|> take(1)
|
||||
|> map { context, authContext -> (SharedAccountContext, AuthorizedApplicationContext?, UnauthorizedApplicationContext?) in
|
||||
return (sharedApplicationContext.sharedContext, context, authContext)
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { _, context, authContext in
|
||||
if let context = context {
|
||||
context.openUrl(url)
|
||||
} else if let authContext = authContext {
|
||||
if let proxyData = parseProxyUrl(url) {
|
||||
authContext.rootController.view.endEditing(true)
|
||||
let presentationData = authContext.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = ProxyServerActionSheetController(theme: presentationData.theme, strings: presentationData.strings, accountManager: authContext.sharedContext.accountManager, postbox: authContext.account.postbox, network: authContext.account.network, server: proxyData, presentationData: nil)
|
||||
authContext.rootController.currentWindow?.present(controller, on: PresentationSurfaceLevel.root, blockInteraction: false, completion: {})
|
||||
} else if let secureIdData = parseSecureIdUrl(url) {
|
||||
let presentationData = authContext.sharedContext.currentPresentationData.with { $0 }
|
||||
authContext.rootController.currentWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Passport_NotLoggedInMessage, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Calls_NotNow, action: {
|
||||
if let callbackUrl = URL(string: secureIdCallbackUrl(with: secureIdData.callbackUrl, peerId: secureIdData.peerId, result: .cancel, parameters: [:])) {
|
||||
UIApplication.shared.openURL(callbackUrl)
|
||||
}
|
||||
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), on: .root, blockInteraction: false, completion: {})
|
||||
} else if let confirmationCode = parseConfirmationCodeUrl(url) {
|
||||
authContext.rootController.applyConfirmationCode(confirmationCode)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -1345,8 +1418,8 @@ private enum QueuedWakeup: Int32 {
|
||||
if let contact = startCallIntent.contacts?.first {
|
||||
if let handle = contact.personHandle?.value {
|
||||
if let userId = Int32(handle) {
|
||||
if let contextValue = self.contextValue, case let .authorized(context) = contextValue {
|
||||
let _ = context.context.callManager?.requestCall(account: context.context.account, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), endCurrentIfAny: false)
|
||||
if let context = self.contextValue {
|
||||
let _ = context.context.sharedContext.callManager?.requestCall(account: context.context.account, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), endCurrentIfAny: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1364,25 +1437,21 @@ private enum QueuedWakeup: Int32 {
|
||||
@available(iOS 9.0, *)
|
||||
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
|
||||
let _ = (self.context.get()
|
||||
|> mapToSignal { value -> Signal<ApplicationContext?, NoError> in
|
||||
if let value = value {
|
||||
if case let .authorized(context) = value {
|
||||
return context.unlockedState
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
|> map { _ -> ApplicationContext? in
|
||||
return value
|
||||
}
|
||||
} else {
|
||||
return .single(nil)
|
||||
|> mapToSignal { context -> Signal<AuthorizedApplicationContext?, NoError> in
|
||||
if let context = context {
|
||||
return context.unlockedState
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
|> map { _ -> AuthorizedApplicationContext? in
|
||||
return context
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { contextValue in
|
||||
if let contextValue = contextValue, case let .authorized(context) = contextValue {
|
||||
|> deliverOnMainQueue).start(next: { context in
|
||||
if let context = context {
|
||||
if let type = ApplicationShortcutItemType(rawValue: shortcutItem.type) {
|
||||
switch type {
|
||||
case .search:
|
||||
@ -1402,9 +1471,9 @@ private enum QueuedWakeup: Int32 {
|
||||
private func openChatWhenReady(accountId: AccountRecordId?, peerId: PeerId, messageId: MessageId? = nil) {
|
||||
let signal = self.sharedContextPromise.get()
|
||||
|> take(1)
|
||||
|> mapToSignal { sharedContext -> Signal<AuthorizedApplicationContext, NoError> in
|
||||
|> mapToSignal { sharedApplicationContext -> Signal<AuthorizedApplicationContext, NoError> in
|
||||
if let accountId = accountId {
|
||||
sharedContext.switchToAccount(id: accountId)
|
||||
sharedApplicationContext.sharedContext.switchToAccount(id: accountId)
|
||||
return self.authorizedContext()
|
||||
|> filter { context in
|
||||
context.context.account.id == accountId
|
||||
@ -1425,7 +1494,7 @@ private enum QueuedWakeup: Int32 {
|
||||
self.openUrlWhenReadyDisposable.set((self.authorizedContext()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { context in
|
||||
let presentationData = context.context.currentPresentationData.with { $0 }
|
||||
let presentationData = context.context.sharedContext.currentPresentationData.with { $0 }
|
||||
openExternalUrl(context: context.context, url: url, presentationData: presentationData, navigationController: context.rootController, dismissInput: {
|
||||
})
|
||||
}))
|
||||
@ -1511,7 +1580,7 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
|
||||
private func registerForNotifications(context: AccountContext, authorize: Bool = true, completion: @escaping (Bool) -> Void = { _ in }) {
|
||||
let presentationData = context.currentPresentationData.with { $0 }
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let _ = (context.sharedContext.accountManager.transaction { transaction -> Bool in
|
||||
let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.inAppNotificationSettings) as? InAppNotificationSettings ?? InAppNotificationSettings.defaultSettings
|
||||
return settings.displayNameOnLockscreen
|
||||
@ -1582,8 +1651,8 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
}
|
||||
|
||||
private func maybeDequeueNotificationPayloads() {
|
||||
if let contextValue = self.contextValue, case let .authorized(context) = contextValue, !self.queuedNotifications.isEmpty {
|
||||
/*private func maybeDequeueNotificationPayloads() {
|
||||
if let context = self.contextValue, !self.queuedNotifications.isEmpty {
|
||||
let queuedNotifications = self.queuedNotifications
|
||||
self.queuedNotifications = []
|
||||
for payload in queuedNotifications {
|
||||
@ -1593,7 +1662,7 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
|
||||
private func maybeDequeueNotificationRequests() {
|
||||
if let contextValue = self.contextValue, case let .authorized(context) = contextValue {
|
||||
if let context = self.contextValue {
|
||||
let requests = self.queuedNotificationRequests
|
||||
self.queuedNotificationRequests = []
|
||||
let queuedMutePolling = self.queuedMutePolling
|
||||
@ -1603,20 +1672,20 @@ private enum QueuedWakeup: Int32 {
|
||||
return transaction.getAccessChallengeData()
|
||||
})
|
||||
|> deliverOnMainQueue).start(next: { accessChallengeData in
|
||||
guard let contextValue = self.contextValue, case let .authorized(context) = contextValue else {
|
||||
guard let context = self.contextValue else {
|
||||
Logger.shared.log("App \(self.episodeId)", "Couldn't process remote notification request")
|
||||
return
|
||||
}
|
||||
|
||||
let strings = context.context.currentPresentationData.with({ $0 }).strings
|
||||
let strings = context.context.sharedContext.currentPresentationData.with({ $0 }).strings
|
||||
|
||||
for (title, body, apnsSound, requestId) in requests {
|
||||
if handleVoipNotifications {
|
||||
context.notificationManager.enqueueRemoteNotification(title: title, text: body, apnsSound: apnsSound, requestId: requestId, strings: strings, accessChallengeData: accessChallengeData)
|
||||
//context.notificationManager.enqueueRemoteNotification(title: title, text: body, apnsSound: apnsSound, requestId: requestId, strings: strings, accessChallengeData: accessChallengeData)
|
||||
}
|
||||
|
||||
context.wakeupManager.wakeupForIncomingMessages(account: context.context.account, completion: { messageIds -> Signal<Void, NoError> in
|
||||
if let contextValue = self.contextValue, case let .authorized(context) = contextValue {
|
||||
/*context.wakeupManager.wakeupForIncomingMessages(account: context.context.account, completion: { messageIds -> Signal<Void, NoError> in
|
||||
if let context = self.contextValue {
|
||||
if handleVoipNotifications {
|
||||
return context.notificationManager.commitRemoteNotification(context: context.context, originalRequestId: requestId, messageIds: messageIds)
|
||||
} else {
|
||||
@ -1626,17 +1695,17 @@ private enum QueuedWakeup: Int32 {
|
||||
Logger.shared.log("App \(self.episodeId)", "Couldn't process remote notifications wakeup result")
|
||||
return .complete()
|
||||
}
|
||||
})
|
||||
})*/
|
||||
}
|
||||
if queuedMutePolling {
|
||||
context.wakeupManager.wakeupForIncomingMessages(account: context.context.account, completion: { messageIds -> Signal<Void, NoError> in
|
||||
if let contextValue = self.contextValue, case .authorized = contextValue {
|
||||
/*context.wakeupManager.wakeupForIncomingMessages(account: context.context.account, completion: { messageIds -> Signal<Void, NoError> in
|
||||
if let context = self.contextValue {
|
||||
return .single(Void())
|
||||
} else {
|
||||
Logger.shared.log("App \(self.episodeId)", "Couldn't process remote notifications wakeup result")
|
||||
return .single(Void())
|
||||
}
|
||||
})
|
||||
})*/
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@ -1645,7 +1714,7 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
|
||||
private func maybeDequeueAnnouncements() {
|
||||
if let contextValue = self.contextValue, case let .authorized(context) = contextValue, !self.queuedAnnouncements.isEmpty {
|
||||
if let context = self.contextValue, !self.queuedAnnouncements.isEmpty {
|
||||
let queuedAnnouncements = self.queuedAnnouncements
|
||||
self.queuedAnnouncements = []
|
||||
let _ = (context.context.account.postbox.transaction(ignoreDisabled: true, { transaction -> [MessageId: String] in
|
||||
@ -1672,9 +1741,9 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
return result
|
||||
}) |> deliverOnMainQueue).start(next: { result in
|
||||
if let contextValue = self.contextValue, case let .authorized(context) = contextValue {
|
||||
if let context = self.contextValue {
|
||||
for (id, text) in result {
|
||||
context.notificationManager.enqueueRemoteNotification(title: "", text: text, apnsSound: nil, requestId: .messageId(id), strings: context.context.currentPresentationData.with({ $0 }).strings, accessChallengeData: .none)
|
||||
//context.notificationManager.enqueueRemoteNotification(title: "", text: text, apnsSound: nil, requestId: .messageId(id), strings: context.context.sharedContext.currentPresentationData.with({ $0 }).strings, accessChallengeData: .none)
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -1685,12 +1754,12 @@ private enum QueuedWakeup: Int32 {
|
||||
for wakeup in self.queuedWakeups {
|
||||
switch wakeup {
|
||||
case .call:
|
||||
if let contextValue = self.contextValue, case let .authorized(context) = contextValue {
|
||||
context.wakeupManager.wakeupForIncomingMessages(account: context.context.account)
|
||||
if let context = self.contextValue {
|
||||
//context.wakeupManager.wakeupForIncomingMessages(account: context.context.account)
|
||||
}
|
||||
case .backgroundLocation:
|
||||
if UIApplication.shared.applicationState == .background {
|
||||
if let contextValue = self.contextValue, case let .authorized(context) = contextValue {
|
||||
if let context = self.contextValue {
|
||||
context.context.liveLocationManager?.pollOnce()
|
||||
}
|
||||
}
|
||||
@ -1698,19 +1767,19 @@ private enum QueuedWakeup: Int32 {
|
||||
}
|
||||
|
||||
self.queuedWakeups.removeAll()
|
||||
}
|
||||
}*/
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
||||
#if DEBUG
|
||||
completionHandler([.alert])
|
||||
#else
|
||||
completionHandler([])
|
||||
#endif
|
||||
if let context = self.contextValue {
|
||||
if let accountId = accountIdFromNotification(notification), context.context.account.id != accountId {
|
||||
completionHandler([.alert])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override var next: UIResponder? {
|
||||
if let contextValue = self.contextValue, case let .authorized(context) = contextValue, let controller = context.context.keyShortcutsController {
|
||||
if let context = self.contextValue, let controller = context.context.keyShortcutsController {
|
||||
return controller
|
||||
}
|
||||
return super.next
|
||||
|
@ -87,29 +87,6 @@ private struct PasscodeState: Equatable {
|
||||
let enableBiometrics: Bool
|
||||
}
|
||||
|
||||
private enum CallStatusText: Equatable {
|
||||
case none
|
||||
case inProgress(Double?)
|
||||
|
||||
static func ==(lhs: CallStatusText, rhs: CallStatusText) -> Bool {
|
||||
switch lhs {
|
||||
case .none:
|
||||
if case .none = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .inProgress(lhsReferenceTime):
|
||||
if case let .inProgress(rhsReferenceTime) = rhs, lhsReferenceTime == rhsReferenceTime {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class AuthorizedApplicationContext {
|
||||
let mainWindow: Window1
|
||||
let lockedCoveringView: LockedWindowCoveringView
|
||||
@ -125,8 +102,7 @@ final class AuthorizedApplicationContext {
|
||||
private var scheduledOperChatWithPeerId: PeerId?
|
||||
private var scheduledOpenExternalUrl: URL?
|
||||
|
||||
let wakeupManager: WakeupManager
|
||||
let notificationManager: NotificationManager
|
||||
//let notificationManager: NotificationManager
|
||||
|
||||
private let passcodeStatusDisposable = MetaDisposable()
|
||||
private let passcodeLockDisposable = MetaDisposable()
|
||||
@ -142,9 +118,6 @@ final class AuthorizedApplicationContext {
|
||||
|
||||
private var isLocked: Bool = true
|
||||
private var passcodeController: ViewController?
|
||||
private var callController: CallController?
|
||||
private let hasOngoingCall = ValuePromise<Bool>(false)
|
||||
private let callState = Promise<PresentationCallState?>(nil)
|
||||
|
||||
private var currentTermsOfServiceUpdate: TermsOfServiceUpdate?
|
||||
private var currentPermissionsController: PermissionController?
|
||||
@ -166,10 +139,6 @@ final class AuthorizedApplicationContext {
|
||||
private var presentationDataDisposable: Disposable?
|
||||
private var displayAlertsDisposable: Disposable?
|
||||
private var removeNotificationsDisposable: Disposable?
|
||||
private var callDisposable: Disposable?
|
||||
private var callStateDisposable: Disposable?
|
||||
private var currentCallStatusText: CallStatusText = .none
|
||||
private var currentCallStatusTextTimer: SwiftSignalKit.Timer?
|
||||
|
||||
private var applicationInForegroundDisposable: Disposable?
|
||||
|
||||
@ -179,7 +148,7 @@ final class AuthorizedApplicationContext {
|
||||
|
||||
init(mainWindow: Window1, replyFromNotificationsActive: Signal<Bool, NoError>, backgroundAudioActive: Signal<Bool, NoError>, watchManagerArguments: Signal<WatchManagerArguments?, NoError>, context: AccountContext, accountManager: AccountManager, legacyBasePath: String, showCallsTab: Bool, reinitializedNotificationSettings: @escaping () -> Void) {
|
||||
setupLegacyComponents(context: context)
|
||||
let presentationData = context.currentPresentationData.with { $0 }
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
self.mainWindow = mainWindow
|
||||
self.lockedCoveringView = LockedWindowCoveringView(theme: presentationData.theme)
|
||||
@ -207,29 +176,18 @@ final class AuthorizedApplicationContext {
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|
||||
self.wakeupManager = WakeupManager(accountManager: context.sharedContext.accountManager, inForeground: context.sharedContext.applicationBindings.applicationInForeground, runningServiceTasks: context.account.importantTasksRunning, runningBackgroundLocationTasks: runningBackgroundLocationTasks, runningWatchTasks: runningWatchTasksPromise.get(), runningDownloadTasks: runningDownloadTasks)
|
||||
//self.wakeupManager = WakeupManager(accountManager: context.sharedContext.accountManager, inForeground: context.sharedContext.applicationBindings.applicationInForeground, runningServiceTasks: context.account.importantTasksRunning, runningBackgroundLocationTasks: runningBackgroundLocationTasks, runningWatchTasks: runningWatchTasksPromise.get(), runningDownloadTasks: runningDownloadTasks)
|
||||
|
||||
self.showCallsTab = showCallsTab
|
||||
|
||||
self.notificationManager = NotificationManager()
|
||||
self.notificationManager.isApplicationInForeground = false
|
||||
//self.notificationManager = NotificationManager()
|
||||
//self.notificationManager.isApplicationInForeground = false
|
||||
|
||||
self.overlayMediaController = OverlayMediaController()
|
||||
|
||||
context.attachOverlayMediaController(self.overlayMediaController)
|
||||
var presentImpl: ((ViewController, Any?) -> Void)?
|
||||
var openSettingsImpl: (() -> Void)?
|
||||
let callManager = PresentationCallManager(accountManager: context.sharedContext.accountManager, account: context.account, getDeviceAccessData: {
|
||||
return (context.currentPresentationData.with { $0 }, { c, a in
|
||||
presentImpl?(c, a)
|
||||
}, {
|
||||
openSettingsImpl?()
|
||||
})
|
||||
}, networkType: context.account.networkType, audioSession: context.sharedContext.mediaManager.audioSession, activeAccounts: .single([context.account]))
|
||||
context.callManager = callManager
|
||||
context.hasOngoingCall = self.hasOngoingCall.get()
|
||||
|
||||
let shouldBeServiceTaskMaster = combineLatest(context.sharedContext.applicationBindings.applicationInForeground, self.wakeupManager.isWokenUp, replyFromNotificationsActive, backgroundAudioActive, callManager.hasActiveCalls)
|
||||
/*let shouldBeServiceTaskMaster = combineLatest(context.sharedContext.applicationBindings.applicationInForeground, self.wakeupManager.isWokenUp, replyFromNotificationsActive, backgroundAudioActive, callManager.hasActiveCalls)
|
||||
|> map { foreground, wokenUp, replyFromNotificationsActive, backgroundAudioActive, hasActiveCalls -> AccountServiceTaskMasterMode in
|
||||
if foreground || wokenUp || replyFromNotificationsActive || hasActiveCalls {
|
||||
return .always
|
||||
@ -256,10 +214,8 @@ final class AuthorizedApplicationContext {
|
||||
Logger.shared.log("ApplicationContext", "setting canBeginTransactions to \(next)")
|
||||
context.account.postbox.setCanBeginTransactions(next)
|
||||
}
|
||||
})
|
||||
})*/
|
||||
context.account.shouldExplicitelyKeepWorkerConnections.set(backgroundAudioActive)
|
||||
context.account.shouldKeepBackgroundDownloadConnections.set(context.fetchManager.hasUserInitiatedEntries)
|
||||
context.account.shouldKeepOnlinePresence.set(context.sharedContext.applicationBindings.applicationInForeground)
|
||||
|
||||
let cache = TGCache(cachesPath: legacyBasePath + "/Caches")!
|
||||
|
||||
@ -295,56 +251,11 @@ final class AuthorizedApplicationContext {
|
||||
context.keyShortcutsController = keyShortcutsController
|
||||
}
|
||||
|
||||
self.applicationInForegroundDisposable = context.sharedContext.applicationBindings.applicationInForeground.start(next: { [weak self] value in
|
||||
/*self.applicationInForegroundDisposable = context.sharedContext.applicationBindings.applicationInForeground.start(next: { [weak self] value in
|
||||
Queue.mainQueue().async {
|
||||
self?.notificationManager.isApplicationInForeground = value
|
||||
}
|
||||
})
|
||||
|
||||
self.mainWindow.inCallNavigate = { [weak self] in
|
||||
if let strongSelf = self, let callController = strongSelf.callController {
|
||||
if callController.isNodeLoaded && callController.view.superview == nil {
|
||||
strongSelf.rootController.view.endEditing(true)
|
||||
strongSelf.mainWindow.present(callController, on: .calls)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.presentGlobalController = { [weak self] c, a in
|
||||
self?.mainWindow.present(c, on: .root)
|
||||
}
|
||||
context.presentCrossfadeController = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
var exists = false
|
||||
strongSelf.mainWindow.forEachViewController { controller in
|
||||
if controller is ThemeSettingsCrossfadeController {
|
||||
exists = true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if !exists {
|
||||
mainWindow.present(ThemeSettingsCrossfadeController(), on: .root)
|
||||
}
|
||||
}
|
||||
|
||||
context.navigateToCurrentCall = { [weak self] in
|
||||
if let strongSelf = self, let callController = strongSelf.callController {
|
||||
if callController.isNodeLoaded && callController.view.superview == nil {
|
||||
strongSelf.rootController.view.endEditing(true)
|
||||
strongSelf.mainWindow.present(callController, on: .calls)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
presentImpl = { [weak self] c, _ in
|
||||
self?.mainWindow.present(c, on: .root)
|
||||
}
|
||||
openSettingsImpl = { [weak context] in
|
||||
context?.sharedContext.applicationBindings.openSettings()
|
||||
}
|
||||
})*/
|
||||
|
||||
let previousPasscodeState = Atomic<PasscodeState?>(value: nil)
|
||||
|
||||
@ -400,7 +311,7 @@ final class AuthorizedApplicationContext {
|
||||
}
|
||||
|
||||
strongSelf.isLocked = isLocked
|
||||
strongSelf.notificationManager.isApplicationLocked = isLocked
|
||||
//strongSelf.notificationManager.isApplicationLocked = isLocked
|
||||
|
||||
if isLocked {
|
||||
if updatedState.isActive {
|
||||
@ -418,7 +329,7 @@ final class AuthorizedApplicationContext {
|
||||
case .plaintextPassword:
|
||||
mode = TGPasscodeEntryControllerModeVerifyComplex
|
||||
}
|
||||
let presentationData = strongSelf.context.currentPresentationData.with { $0 }
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let presentAnimated = previousState != nil && previousState!.isActive
|
||||
let legacyController = LegacyController(presentation: LegacyControllerPresentation.modal(animateIn: presentAnimated), theme: presentationData.theme)
|
||||
let controller = TGPasscodeEntryController(context: legacyController.context, style: TGPasscodeEntryControllerStyleDefault, mode: mode, cancelEnabled: false, allowTouchId: updatedState.enableBiometrics, attemptData: attemptData, completion: { value in
|
||||
@ -543,7 +454,7 @@ final class AuthorizedApplicationContext {
|
||||
|
||||
if #available(iOS 10.0, *) {
|
||||
} else {
|
||||
DeviceAccess.authorizeAccess(to: .contacts, presentationData: strongSelf.context.currentPresentationData.with { $0 }, present: { c, a in
|
||||
DeviceAccess.authorizeAccess(to: .contacts, presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, present: { c, a in
|
||||
}, openSettings: {}, { _ in })
|
||||
}
|
||||
|
||||
@ -657,7 +568,7 @@ final class AuthorizedApplicationContext {
|
||||
}
|
||||
|
||||
if inAppNotificationSettings.displayPreviews {
|
||||
let presentationData = strongSelf.context.currentPresentationData.with { $0 }
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
strongSelf.notificationController.enqueue(ChatMessageNotificationItem(context: strongSelf.context, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, messages: messages, tapAction: {
|
||||
if let strongSelf = self {
|
||||
var foundOverlay = false
|
||||
@ -713,7 +624,7 @@ final class AuthorizedApplicationContext {
|
||||
|
||||
strongSelf.currentTermsOfServiceUpdate = termsOfServiceUpdate
|
||||
if let termsOfServiceUpdate = termsOfServiceUpdate {
|
||||
let presentationData = strongSelf.context.currentPresentationData.with { $0 }
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
var acceptImpl: ((String?) -> Void)?
|
||||
var declineImpl: (() -> Void)?
|
||||
let controller = TermsOfServiceController(theme: TermsOfServiceControllerTheme(presentationTheme: presentationData.theme), strings: presentationData.strings, text: termsOfServiceUpdate.text, entities: termsOfServiceUpdate.entities, ageConfirmation: termsOfServiceUpdate.ageConfirmation, signingUp: false, accept: { proccedBot in
|
||||
@ -892,7 +803,7 @@ final class AuthorizedApplicationContext {
|
||||
|> deliverOnMainQueue).start(next: { [weak self] alerts in
|
||||
if let strongSelf = self{
|
||||
for text in alerts {
|
||||
let presentationData = strongSelf.context.currentPresentationData.with { $0 }
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
|
||||
}
|
||||
@ -906,84 +817,12 @@ final class AuthorizedApplicationContext {
|
||||
}
|
||||
})
|
||||
|
||||
self.callDisposable = (callManager.currentCallSignal
|
||||
|> deliverOnMainQueue).start(next: { [weak self] call in
|
||||
if let strongSelf = self {
|
||||
if call !== strongSelf.callController?.call {
|
||||
strongSelf.callController?.dismiss()
|
||||
strongSelf.callController = nil
|
||||
strongSelf.hasOngoingCall.set(false)
|
||||
|
||||
if let call = call {
|
||||
let callController = CallController(context: strongSelf.context, call: call)
|
||||
strongSelf.callController = callController
|
||||
strongSelf.rootController.view?.endEditing(true)
|
||||
strongSelf.mainWindow.present(callController, on: .calls)
|
||||
strongSelf.callState.set(call.state
|
||||
|> map(Optional.init))
|
||||
strongSelf.hasOngoingCall.set(true)
|
||||
strongSelf.notificationManager.setNotificationCall(call, strings: strongSelf.context.currentPresentationData.with({ $0 }).strings)
|
||||
} else {
|
||||
strongSelf.callState.set(.single(nil))
|
||||
strongSelf.hasOngoingCall.set(false)
|
||||
strongSelf.notificationManager.setNotificationCall(nil, strings: strongSelf.context.currentPresentationData.with({ $0 }).strings)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.callStateDisposable = (self.callState.get()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||
if let strongSelf = self {
|
||||
let resolvedText: CallStatusText
|
||||
if let state = state {
|
||||
switch state {
|
||||
case .connecting, .requesting, .terminating, .ringing, .waiting:
|
||||
resolvedText = .inProgress(nil)
|
||||
case .terminated:
|
||||
resolvedText = .none
|
||||
case let .active(timestamp, _, _):
|
||||
resolvedText = .inProgress(timestamp)
|
||||
}
|
||||
} else {
|
||||
resolvedText = .none
|
||||
}
|
||||
|
||||
if strongSelf.currentCallStatusText != resolvedText {
|
||||
strongSelf.currentCallStatusText = resolvedText
|
||||
|
||||
var referenceTimestamp: Double?
|
||||
if case let .inProgress(timestamp) = resolvedText, let concreteTimestamp = timestamp {
|
||||
referenceTimestamp = concreteTimestamp
|
||||
}
|
||||
|
||||
if let _ = referenceTimestamp {
|
||||
if strongSelf.currentCallStatusTextTimer == nil {
|
||||
let timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: {
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateStatusBarText()
|
||||
}
|
||||
}, queue: Queue.mainQueue())
|
||||
strongSelf.currentCallStatusTextTimer = timer
|
||||
timer.start()
|
||||
}
|
||||
} else {
|
||||
strongSelf.currentCallStatusTextTimer?.invalidate()
|
||||
strongSelf.currentCallStatusTextTimer = nil
|
||||
}
|
||||
|
||||
strongSelf.updateStatusBarText()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.context.account.resetStateManagement()
|
||||
let contactSynchronizationPreferencesKey = PostboxViewKey.preferences(keys: Set([ApplicationSpecificPreferencesKeys.contactSynchronizationSettings]))
|
||||
|
||||
let importableContacts = self.context.sharedContext.contactDataManager?.importable() ?? .single([:])
|
||||
self.context.account.importableContacts.set(self.context.account.postbox.combinedView(keys: [contactSynchronizationPreferencesKey])
|
||||
|> mapToSignal { preferences -> Signal<[DeviceContactNormalizedPhoneNumber: ImportableDeviceContactData], NoError> in
|
||||
let settings: ContactSynchronizationSettings = ((preferences.views[contactSynchronizationPreferencesKey] as? PreferencesView)?.values[ApplicationSpecificPreferencesKeys.contactSynchronizationSettings] as? ContactSynchronizationSettings) ?? .defaultSettings
|
||||
self.context.account.importableContacts.set(self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.contactSynchronizationSettings])
|
||||
|> mapToSignal { sharedData -> Signal<[DeviceContactNormalizedPhoneNumber: ImportableDeviceContactData], NoError> in
|
||||
let settings: ContactSynchronizationSettings = (sharedData.entries[ApplicationSpecificSharedDataKeys.contactSynchronizationSettings] as? ContactSynchronizationSettings) ?? .defaultSettings
|
||||
if settings.synchronizeDeviceContacts {
|
||||
return importableContacts
|
||||
} else {
|
||||
@ -992,7 +831,7 @@ final class AuthorizedApplicationContext {
|
||||
})
|
||||
|
||||
let previousTheme = Atomic<PresentationTheme?>(value: nil)
|
||||
self.presentationDataDisposable = (context.presentationData
|
||||
self.presentationDataDisposable = (context.sharedContext.presentationData
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||
if let strongSelf = self {
|
||||
if previousTheme.swap(presentationData.theme) !== presentationData.theme {
|
||||
@ -1051,42 +890,18 @@ final class AuthorizedApplicationContext {
|
||||
if chatIsVisible {
|
||||
navigateToMessage()
|
||||
} else {
|
||||
let presentationData = strongSelf.context.currentPresentationData.with { $0 }
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.WatchRemote_AlertTitle, text: presentationData.strings.WatchRemote_AlertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.WatchRemote_AlertOpen, action:navigateToMessage)])
|
||||
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
|
||||
}
|
||||
} else {
|
||||
strongSelf.notificationManager.presentWatchContinuityNotification(context: strongSelf.context, messageId: messageId)
|
||||
//strongSelf.notificationManager.presentWatchContinuityNotification(context: strongSelf.context, messageId: messageId)
|
||||
}
|
||||
}
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
private func updateStatusBarText() {
|
||||
if case let .inProgress(timestamp) = self.currentCallStatusText {
|
||||
let text: String
|
||||
let presentationData = self.context.currentPresentationData.with { $0 }
|
||||
if let timestamp = timestamp {
|
||||
let duration = Int32(CFAbsoluteTimeGetCurrent() - timestamp)
|
||||
let durationString: String
|
||||
if duration > 60 * 60 {
|
||||
durationString = String(format: "%02d:%02d:%02d", arguments: [duration / 3600, (duration / 60) % 60, duration % 60])
|
||||
} else {
|
||||
durationString = String(format: "%02d:%02d", arguments: [(duration / 60) % 60, duration % 60])
|
||||
}
|
||||
|
||||
text = presentationData.strings.Call_StatusBar(durationString).0
|
||||
} else {
|
||||
text = presentationData.strings.Call_StatusBar("").0
|
||||
}
|
||||
|
||||
self.mainWindow.setForceInCallStatusBar(text)
|
||||
} else {
|
||||
self.mainWindow.setForceInCallStatusBar(nil)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.context.account.postbox.clearCaches()
|
||||
self.context.account.shouldKeepOnlinePresence.set(.single(false))
|
||||
@ -1099,9 +914,6 @@ final class AuthorizedApplicationContext {
|
||||
self.passcodeStatusDisposable.dispose()
|
||||
self.displayAlertsDisposable?.dispose()
|
||||
self.removeNotificationsDisposable?.dispose()
|
||||
self.callDisposable?.dispose()
|
||||
self.callStateDisposable?.dispose()
|
||||
self.currentCallStatusTextTimer?.invalidate()
|
||||
self.presentationDataDisposable?.dispose()
|
||||
self.enablePostboxTransactionsDiposable?.dispose()
|
||||
self.termsOfServiceProceedToBotDisposable.dispose()
|
||||
@ -1126,7 +938,7 @@ final class AuthorizedApplicationContext {
|
||||
|
||||
func openUrl(_ url: URL) {
|
||||
if self.rootController.rootTabController != nil {
|
||||
let presentationData = self.context.currentPresentationData.with { $0 }
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
openExternalUrl(context: self.context, url: url.absoluteString, presentationData: presentationData, navigationController: self.rootController, dismissInput: { [weak self] in
|
||||
self?.rootController.view.endEditing(true)
|
||||
})
|
||||
|
@ -476,15 +476,15 @@ func importLegacyPreferences(accountManager: AccountManager, account: TemporaryA
|
||||
return settings
|
||||
})
|
||||
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.contactSynchronizationSettings, { current in
|
||||
var settings: ContactSynchronizationSettings = current as? ContactSynchronizationSettings ?? .defaultSettings
|
||||
if let contactsInhibitSync = contactsInhibitSync, contactsInhibitSync {
|
||||
settings.synchronizeDeviceContacts = false
|
||||
}
|
||||
return settings
|
||||
})
|
||||
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.contactSynchronizationSettings, { current in
|
||||
var settings: ContactSynchronizationSettings = current as? ContactSynchronizationSettings ?? .defaultSettings
|
||||
if let contactsInhibitSync = contactsInhibitSync, contactsInhibitSync {
|
||||
settings.synchronizeDeviceContacts = false
|
||||
}
|
||||
return settings
|
||||
})
|
||||
|
||||
if let secretInlineBotsInitialized = secretInlineBotsInitialized, secretInlineBotsInitialized {
|
||||
ApplicationSpecificNotice.setSecretChatInlineBotUsage(transaction: transaction)
|
||||
}
|
||||
|
@ -323,7 +323,7 @@ final class NotificationManager {
|
||||
}
|
||||
|
||||
private func processNotificationMessages(context: AccountContext, messageList: [([Message], PeerMessageSound, Bool, Bool)], isLocked: Bool) {
|
||||
let presentationData = (context.currentPresentationData.with { $0 })
|
||||
let presentationData = (context.sharedContext.currentPresentationData.with { $0 })
|
||||
let strings = presentationData.strings
|
||||
let nameDisplayOrder = presentationData.nameDisplayOrder
|
||||
|
||||
@ -801,7 +801,7 @@ final class NotificationManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
let presentationData = context.currentPresentationData.with { $0 }
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
var userInfo: [AnyHashable : Any] = [:]
|
||||
userInfo["peerId"] = messageId.peerId.toInt64()
|
||||
|
361
Telegram-iOS/SharedNotificationManager.swift
Normal file
361
Telegram-iOS/SharedNotificationManager.swift
Normal file
@ -0,0 +1,361 @@
|
||||
import Foundation
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
|
||||
private final class PollStateContext {
|
||||
let subscribers = Bag<(Bool) -> Void>()
|
||||
var disposable: Disposable?
|
||||
|
||||
deinit {
|
||||
self.disposable?.dispose()
|
||||
}
|
||||
|
||||
var isEmpty: Bool {
|
||||
return self.disposable == nil && self.subscribers.isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
final class SharedNotificationManager {
|
||||
private let episodeId: UInt32
|
||||
|
||||
private let clearNotificationsManager: ClearNotificationsManager
|
||||
|
||||
private var inForeground: Bool = false
|
||||
private var inForegroundDisposable: Disposable?
|
||||
|
||||
private var accountsAndKeys: [(Account, Bool, MasterNotificationKey)]?
|
||||
private var accountsAndKeysDisposable: Disposable?
|
||||
|
||||
private var encryptedNotifications: [[AnyHashable: Any]] = []
|
||||
|
||||
private var pollStateContexts: [AccountRecordId: PollStateContext] = [:]
|
||||
|
||||
init(episodeId: UInt32, clearNotificationsManager: ClearNotificationsManager, inForeground: Signal<Bool, NoError>, accounts: Signal<[(Account, Bool)], NoError>) {
|
||||
assert(Queue.mainQueue().isCurrent())
|
||||
|
||||
self.episodeId = episodeId
|
||||
self.clearNotificationsManager = clearNotificationsManager
|
||||
|
||||
self.inForegroundDisposable = (inForeground
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.inForeground = value
|
||||
})
|
||||
|
||||
self.accountsAndKeysDisposable = (accounts
|
||||
|> mapToSignal { accounts -> Signal<[(Account, Bool, MasterNotificationKey)], NoError> in
|
||||
let signals = accounts.map { account, isCurrent -> Signal<(Account, Bool, MasterNotificationKey), NoError> in
|
||||
return masterNotificationsKey(account: account, ignoreDisabled: true)
|
||||
|> map { key -> (Account, Bool, MasterNotificationKey) in
|
||||
return (account, isCurrent, key)
|
||||
}
|
||||
}
|
||||
return combineLatest(signals)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] accountsAndKeys in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let shouldProcess = strongSelf.accountsAndKeys == nil
|
||||
strongSelf.accountsAndKeys = accountsAndKeys
|
||||
if shouldProcess {
|
||||
strongSelf.process()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.inForegroundDisposable?.dispose()
|
||||
self.accountsAndKeysDisposable?.dispose()
|
||||
}
|
||||
|
||||
func isPollingState(accountId: AccountRecordId) -> Signal<Bool, NoError> {
|
||||
return Signal { subscriber in
|
||||
let context: PollStateContext
|
||||
if let current = self.pollStateContexts[accountId] {
|
||||
context = current
|
||||
} else {
|
||||
context = PollStateContext()
|
||||
self.pollStateContexts[accountId] = context
|
||||
}
|
||||
subscriber.putNext(context.disposable != nil)
|
||||
let index = context.subscribers.add({ value in
|
||||
subscriber.putNext(value)
|
||||
})
|
||||
|
||||
return ActionDisposable { [weak context] in
|
||||
Queue.mainQueue().async {
|
||||
if let current = self.pollStateContexts[accountId], current === context {
|
||||
current.subscribers.remove(index)
|
||||
if current.isEmpty {
|
||||
self.pollStateContexts.removeValue(forKey: accountId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func beginPollingState(account: Account) {
|
||||
let accountId = account.id
|
||||
let context: PollStateContext
|
||||
if let current = self.pollStateContexts[accountId] {
|
||||
context = current
|
||||
} else {
|
||||
context = PollStateContext()
|
||||
self.pollStateContexts[accountId] = context
|
||||
}
|
||||
let previousDisposable = context.disposable
|
||||
context.disposable = (account.stateManager.pollStateUpdateCompletion()
|
||||
|> deliverOnMainQueue).start(next: { [weak self, weak context] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let current = strongSelf.pollStateContexts[accountId], current === context {
|
||||
if let disposable = current.disposable {
|
||||
disposable.dispose()
|
||||
current.disposable = nil
|
||||
for f in current.subscribers.copyItems() {
|
||||
f(false)
|
||||
}
|
||||
}
|
||||
if current.isEmpty {
|
||||
strongSelf.pollStateContexts.removeValue(forKey: accountId)
|
||||
}
|
||||
}
|
||||
})
|
||||
previousDisposable?.dispose()
|
||||
if previousDisposable == nil {
|
||||
for f in context.subscribers.copyItems() {
|
||||
f(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addEncryptedNotification(_ dict: [AnyHashable: Any]) {
|
||||
self.encryptedNotifications.append(dict)
|
||||
|
||||
if self.accountsAndKeys != nil {
|
||||
self.process()
|
||||
}
|
||||
}
|
||||
|
||||
private func process() {
|
||||
guard let accountsAndKeys = self.accountsAndKeys else {
|
||||
return
|
||||
}
|
||||
var decryptedNotifications: [(Account, Bool, [AnyHashable: Any])] = []
|
||||
for dict in self.encryptedNotifications {
|
||||
if var encryptedPayload = dict["p"] as? String {
|
||||
encryptedPayload = encryptedPayload.replacingOccurrences(of: "-", with: "+")
|
||||
encryptedPayload = encryptedPayload.replacingOccurrences(of: "_", with: "/")
|
||||
while encryptedPayload.count % 4 != 0 {
|
||||
encryptedPayload.append("=")
|
||||
}
|
||||
if let data = Data(base64Encoded: encryptedPayload) {
|
||||
inner: for (account, isCurrent, key) in accountsAndKeys {
|
||||
if let decryptedData = decryptedNotificationPayload(key: key, data: data) {
|
||||
if let decryptedDict = (try? JSONSerialization.jsonObject(with: decryptedData, options: [])) as? [AnyHashable: Any] {
|
||||
decryptedNotifications.append((account, isCurrent, decryptedDict))
|
||||
}
|
||||
break inner
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.encryptedNotifications.removeAll()
|
||||
|
||||
for (account, isCurrent, payload) in decryptedNotifications {
|
||||
var redactedPayload = payload
|
||||
if var aps = redactedPayload["aps"] as? [AnyHashable: Any] {
|
||||
if Logger.shared.redactSensitiveData {
|
||||
if aps["alert"] != nil {
|
||||
aps["alert"] = "[[redacted]]"
|
||||
}
|
||||
if aps["body"] != nil {
|
||||
aps["body"] = "[[redacted]]"
|
||||
}
|
||||
}
|
||||
redactedPayload["aps"] = aps
|
||||
}
|
||||
Logger.shared.log("Apns \(self.episodeId)", "\(redactedPayload)")
|
||||
|
||||
let aps = payload["aps"] as? [AnyHashable: Any]
|
||||
|
||||
var readMessageId: MessageId?
|
||||
var isCall = false
|
||||
var isAnnouncement = false
|
||||
var isLocationPolling = false
|
||||
var notificationRequestId: NotificationManagedNotificationRequestId?
|
||||
var isMutePolling = false
|
||||
var title: String = ""
|
||||
var body: String?
|
||||
var apnsSound: String?
|
||||
var configurationUpdate: (Int32, String, Int32, Data?)?
|
||||
if let aps = aps, let alert = aps["alert"] as? String {
|
||||
if let range = alert.range(of: ": ") {
|
||||
title = String(alert[..<range.lowerBound])
|
||||
body = String(alert[range.upperBound...])
|
||||
} else {
|
||||
body = alert
|
||||
}
|
||||
} else if let aps = aps, let alert = aps["alert"] as? [AnyHashable: AnyObject] {
|
||||
if let alertBody = alert["body"] as? String {
|
||||
body = alertBody
|
||||
if let alertTitle = alert["title"] as? String {
|
||||
title = alertTitle
|
||||
}
|
||||
}
|
||||
if let locKey = alert["loc-key"] as? String {
|
||||
if locKey == "PHONE_CALL_REQUEST" {
|
||||
isCall = true
|
||||
} else if locKey == "GEO_LIVE_PENDING" {
|
||||
isLocationPolling = true
|
||||
} else if locKey == "MESSAGE_MUTED" {
|
||||
isMutePolling = true
|
||||
}
|
||||
let string = NSLocalizedString(locKey, comment: "")
|
||||
if !string.isEmpty {
|
||||
if let locArgs = alert["loc-args"] as? [AnyObject] {
|
||||
var args: [CVarArg] = []
|
||||
var failed = false
|
||||
for arg in locArgs {
|
||||
if let arg = arg as? CVarArg {
|
||||
args.append(arg)
|
||||
} else {
|
||||
failed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if failed {
|
||||
body = "\(string)"
|
||||
} else {
|
||||
body = String(format: string, arguments: args)
|
||||
}
|
||||
} else {
|
||||
body = "\(string)"
|
||||
}
|
||||
} else {
|
||||
body = nil
|
||||
}
|
||||
} else {
|
||||
body = nil
|
||||
}
|
||||
}
|
||||
|
||||
if let aps = aps, let address = aps["addr"] as? String, let datacenterId = aps["dc"] as? Int {
|
||||
var host = address
|
||||
var port: Int32 = 443
|
||||
if let range = address.range(of: ":") {
|
||||
host = String(address[address.startIndex ..< range.lowerBound])
|
||||
if let portValue = Int(String(address[range.upperBound...])) {
|
||||
port = Int32(portValue)
|
||||
}
|
||||
}
|
||||
var secret: Data?
|
||||
if let secretString = aps["sec"] as? String {
|
||||
let data = dataWithHexString(secretString)
|
||||
if data.count == 16 || data.count == 32 {
|
||||
secret = data
|
||||
}
|
||||
}
|
||||
configurationUpdate = (Int32(datacenterId), host, port, secret)
|
||||
}
|
||||
|
||||
if let aps = aps, let sound = aps["sound"] as? String {
|
||||
apnsSound = sound
|
||||
}
|
||||
|
||||
if payload["call_id"] != nil {
|
||||
isCall = true
|
||||
}
|
||||
|
||||
if payload["announcement"] != nil {
|
||||
isAnnouncement = true
|
||||
}
|
||||
|
||||
if let body = body {
|
||||
if isAnnouncement {
|
||||
//presentAnnouncement
|
||||
} else {
|
||||
var peerId: PeerId?
|
||||
|
||||
if let fromId = payload["from_id"] {
|
||||
let fromIdValue = fromId as! NSString
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: Int32(fromIdValue.intValue))
|
||||
} else if let fromId = payload["chat_id"] {
|
||||
let fromIdValue = fromId as! NSString
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: Int32(fromIdValue.intValue))
|
||||
} else if let fromId = payload["channel_id"] {
|
||||
let fromIdValue = fromId as! NSString
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: Int32(fromIdValue.intValue))
|
||||
}
|
||||
|
||||
if let msgId = payload["msg_id"] {
|
||||
let msgIdValue = msgId as! NSString
|
||||
if let peerId = peerId {
|
||||
notificationRequestId = .messageId(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(msgIdValue.intValue)))
|
||||
}
|
||||
} else if let randomId = payload["random_id"] {
|
||||
let randomIdValue = randomId as! NSString
|
||||
var peerId: PeerId?
|
||||
if let encryptionIdString = payload["encryption_id"] as? String, let encryptionId = Int32(encryptionIdString) {
|
||||
peerId = PeerId(namespace: Namespaces.Peer.SecretChat, id: encryptionId)
|
||||
}
|
||||
notificationRequestId = .globallyUniqueId(randomIdValue.longLongValue, peerId)
|
||||
} else {
|
||||
isMutePolling = true
|
||||
}
|
||||
}
|
||||
} else if let _ = payload["max_id"] {
|
||||
var peerId: PeerId?
|
||||
|
||||
if let fromId = payload["from_id"] {
|
||||
let fromIdValue = fromId as! NSString
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: Int32(fromIdValue.intValue))
|
||||
} else if let fromId = payload["chat_id"] {
|
||||
let fromIdValue = fromId as! NSString
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: Int32(fromIdValue.intValue))
|
||||
} else if let fromId = payload["channel_id"] {
|
||||
let fromIdValue = fromId as! NSString
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: Int32(fromIdValue.intValue))
|
||||
}
|
||||
|
||||
if let peerId = peerId {
|
||||
if let msgId = payload["max_id"] {
|
||||
let msgIdValue = msgId as! NSString
|
||||
if msgIdValue.intValue != 0 {
|
||||
readMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(msgIdValue.intValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if notificationRequestId != nil || isMutePolling || isCall {
|
||||
if !self.inForeground || !isCurrent {
|
||||
self.beginPollingState(account: account)
|
||||
}
|
||||
}
|
||||
if isLocationPolling {
|
||||
//addedWakeups.insert(.backgroundLocation)
|
||||
}
|
||||
|
||||
if let readMessageId = readMessageId {
|
||||
self.clearNotificationsManager.append(readMessageId)
|
||||
self.clearNotificationsManager.commitNow()
|
||||
|
||||
let _ = account.postbox.transaction(ignoreDisabled: true, { transaction -> Void in
|
||||
transaction.applyIncomingReadMaxId(readMessageId)
|
||||
}).start()
|
||||
}
|
||||
|
||||
if let (datacenterId, host, port, secret) = configurationUpdate {
|
||||
account.network.mergeBackupDatacenterAddress(datacenterId: datacenterId, host: host, port: port, secret: secret)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
184
Telegram-iOS/SharedWakeupManager.swift
Normal file
184
Telegram-iOS/SharedWakeupManager.swift
Normal file
@ -0,0 +1,184 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
|
||||
private struct AccountTasks {
|
||||
let stateSynchronization: Bool
|
||||
let importantTasks: AccountRunningImportantTasks
|
||||
let backgroundLocation: Bool
|
||||
let backgroundDownloads: Bool
|
||||
|
||||
var isEmpty: Bool {
|
||||
if self.stateSynchronization {
|
||||
return false
|
||||
}
|
||||
if !self.importantTasks.isEmpty {
|
||||
return false
|
||||
}
|
||||
if self.backgroundLocation {
|
||||
return false
|
||||
}
|
||||
if self.backgroundDownloads {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
final class SharedWakeupManager {
|
||||
private var inForeground: Bool = false
|
||||
private var hasActiveAudioSession: Bool = false
|
||||
private var allowBackgroundTimeExtensionDeadline: Double?
|
||||
private var isInBackgroundExtension: Bool = false
|
||||
|
||||
private var inForegroundDisposable: Disposable?
|
||||
private var hasActiveAudioSessionDisposable: Disposable?
|
||||
private var tasksDisposable: Disposable?
|
||||
private var currentTask: (UIBackgroundTaskIdentifier, Double, SwiftSignalKit.Timer)?
|
||||
|
||||
private var accountsAndTasks: [(Account, Bool, AccountTasks)] = []
|
||||
|
||||
init(activeAccounts: Signal<(primary: Account?, accounts: [AccountRecordId: Account]), NoError>, inForeground: Signal<Bool, NoError>, hasActiveAudioSession: Signal<Bool, NoError>, notificationManager: SharedNotificationManager) {
|
||||
assert(Queue.mainQueue().isCurrent())
|
||||
|
||||
self.inForegroundDisposable = (inForeground
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.inForeground = value
|
||||
strongSelf.checkTasks()
|
||||
})
|
||||
|
||||
self.hasActiveAudioSessionDisposable = (hasActiveAudioSession
|
||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.hasActiveAudioSession = value
|
||||
strongSelf.checkTasks()
|
||||
})
|
||||
|
||||
self.tasksDisposable = (activeAccounts
|
||||
|> deliverOnMainQueue
|
||||
|> mapToSignal { primary, accounts -> Signal<[(Account, Bool, AccountTasks)], NoError> in
|
||||
let signals: [Signal<(Account, Bool, AccountTasks), NoError>] = accounts.values.map { account in
|
||||
return combineLatest(queue: .mainQueue(), account.importantTasksRunning, notificationManager.isPollingState(accountId: account.id))
|
||||
|> map { importantTasksRunning, isPollingState -> (Account, Bool, AccountTasks) in
|
||||
return (account, primary?.id == account.id, AccountTasks(stateSynchronization: isPollingState, importantTasks: importantTasksRunning, backgroundLocation: false, backgroundDownloads: false))
|
||||
}
|
||||
}
|
||||
return combineLatest(signals)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] accountsAndTasks in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.accountsAndTasks = accountsAndTasks
|
||||
strongSelf.checkTasks()
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.inForegroundDisposable?.dispose()
|
||||
self.hasActiveAudioSessionDisposable?.dispose()
|
||||
self.tasksDisposable?.dispose()
|
||||
if let (taskId, _, timer) = self.currentTask {
|
||||
timer.invalidate()
|
||||
UIApplication.shared.endBackgroundTask(taskId)
|
||||
}
|
||||
}
|
||||
|
||||
func allowBackgroundTimeExtension(timeout: Double) {
|
||||
let shouldCheckTasks = self.allowBackgroundTimeExtensionDeadline == nil
|
||||
self.allowBackgroundTimeExtensionDeadline = CACurrentMediaTime() + timeout
|
||||
if shouldCheckTasks {
|
||||
self.checkTasks()
|
||||
}
|
||||
}
|
||||
|
||||
func checkTasks() {
|
||||
if self.inForeground || self.hasActiveAudioSession {
|
||||
if let (taskId, _, timer) = self.currentTask {
|
||||
self.currentTask = nil
|
||||
timer.invalidate()
|
||||
UIApplication.shared.endBackgroundTask(taskId)
|
||||
self.isInBackgroundExtension = false
|
||||
}
|
||||
} else {
|
||||
var hasTasksForBackgroundExtension = false
|
||||
for (_, _, tasks) in self.accountsAndTasks {
|
||||
if !tasks.isEmpty {
|
||||
hasTasksForBackgroundExtension = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let canBeginBackgroundExtensionTasks = self.allowBackgroundTimeExtensionDeadline.flatMap({ CACurrentMediaTime() < $0 }) ?? false
|
||||
if hasTasksForBackgroundExtension {
|
||||
if canBeginBackgroundExtensionTasks {
|
||||
var endTaskId: UIBackgroundTaskIdentifier?
|
||||
|
||||
let currentTime = CACurrentMediaTime()
|
||||
if let (taskId, startTime, timer) = self.currentTask {
|
||||
if startTime < currentTime + 1.0 {
|
||||
self.currentTask = nil
|
||||
timer.invalidate()
|
||||
endTaskId = taskId
|
||||
}
|
||||
}
|
||||
|
||||
if self.currentTask == nil {
|
||||
let handleExpiration:() -> Void = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.isInBackgroundExtension = false
|
||||
strongSelf.checkTasks()
|
||||
}
|
||||
let taskId = UIApplication.shared.beginBackgroundTask(withName: "background-wakeup", expirationHandler: {
|
||||
handleExpiration()
|
||||
})
|
||||
let timer = SwiftSignalKit.Timer(timeout: min(30.0, UIApplication.shared.backgroundTimeRemaining), repeat: false, completion: {
|
||||
handleExpiration()
|
||||
}, queue: Queue.mainQueue())
|
||||
self.currentTask = (taskId, currentTime, timer)
|
||||
timer.start()
|
||||
|
||||
endTaskId.flatMap(UIApplication.shared.endBackgroundTask)
|
||||
|
||||
self.isInBackgroundExtension = true
|
||||
}
|
||||
}
|
||||
} else if let (taskId, _, timer) = self.currentTask {
|
||||
self.currentTask = nil
|
||||
timer.invalidate()
|
||||
UIApplication.shared.endBackgroundTask(taskId)
|
||||
self.isInBackgroundExtension = false
|
||||
}
|
||||
}
|
||||
self.updateAccounts()
|
||||
}
|
||||
|
||||
private func updateAccounts() {
|
||||
if self.inForeground || self.hasActiveAudioSession || self.isInBackgroundExtension {
|
||||
for (account, primary, tasks) in self.accountsAndTasks {
|
||||
if primary || !tasks.isEmpty {
|
||||
account.shouldBeServiceTaskMaster.set(.single(.always))
|
||||
} else {
|
||||
account.shouldBeServiceTaskMaster.set(.single(.never))
|
||||
}
|
||||
account.shouldKeepOnlinePresence.set(.single(primary && self.inForeground))
|
||||
account.shouldKeepBackgroundDownloadConnections.set(.single(tasks.backgroundDownloads))
|
||||
}
|
||||
} else {
|
||||
for (account, _, _) in self.accountsAndTasks {
|
||||
account.shouldBeServiceTaskMaster.set(.single(.never))
|
||||
account.shouldKeepOnlinePresence.set(.single(false))
|
||||
account.shouldKeepBackgroundDownloadConnections.set(.single(false))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ final class WatchCommunicationManager {
|
||||
private let presets = Promise<WatchPresetSettings?>(nil)
|
||||
private let navigateToMessagePipe = ValuePipe<MessageId>()
|
||||
|
||||
init(queue: Queue, context: Promise<ApplicationContext?>) {
|
||||
init(queue: Queue, context: Promise<AuthorizedApplicationContext?>) {
|
||||
self.queue = queue
|
||||
|
||||
let handlers = allWatchRequestHandlers.reduce([String : AnyClass]()) { (map, handler) -> [String : AnyClass] in
|
||||
@ -57,7 +57,7 @@ final class WatchCommunicationManager {
|
||||
guard let strongSelf = self, appInstalled else {
|
||||
return
|
||||
}
|
||||
if let appContext = appContext, case let .authorized(context) = appContext {
|
||||
if let context = appContext {
|
||||
strongSelf.accountContext.set(.single(context.context))
|
||||
strongSelf.server.setAuthorized(true, userId: context.context.account.peerId.id)
|
||||
strongSelf.server.setMicAccessAllowed(false)
|
||||
@ -79,10 +79,10 @@ final class WatchCommunicationManager {
|
||||
}))
|
||||
|
||||
self.presetsDisposable.set((combineLatest(self.watchAppInstalled, self.presets.get() |> distinctUntilChanged |> deliverOn(self.queue), context.get() |> deliverOn(self.queue))).start(next: { [weak self] appInstalled, presets, appContext in
|
||||
guard let strongSelf = self, let presets = presets, let appContext = appContext, case let .authorized(context) = appContext, appInstalled, let tempPath = strongSelf.watchTemporaryStorePath else {
|
||||
guard let strongSelf = self, let presets = presets, let context = appContext, appInstalled, let tempPath = strongSelf.watchTemporaryStorePath else {
|
||||
return
|
||||
}
|
||||
let presentationData = context.context.currentPresentationData.with { $0 }
|
||||
let presentationData = context.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let defaultSuggestions: [String : String] = [
|
||||
"OK": presentationData.strings.Watch_Suggestion_OK,
|
||||
"Thanks": presentationData.strings.Watch_Suggestion_Thanks,
|
||||
@ -173,7 +173,7 @@ final class WatchCommunicationManager {
|
||||
}
|
||||
}
|
||||
|
||||
func watchCommunicationManager(context: Promise<ApplicationContext?>) -> Signal<WatchCommunicationManager?, NoError> {
|
||||
func watchCommunicationManager(context: Promise<AuthorizedApplicationContext?>) -> Signal<WatchCommunicationManager?, NoError> {
|
||||
return Signal { subscriber in
|
||||
let queue = Queue()
|
||||
queue.async {
|
||||
|
@ -39,7 +39,7 @@ final class WatchChatListHandler: WatchRequestHandler {
|
||||
if let context = context {
|
||||
return context.account.viewTracker.tailChatListView(groupId: nil, count: limit)
|
||||
|> map { chatListView, _ -> (ChatListView, PresentationData) in
|
||||
return (chatListView, context.currentPresentationData.with { $0 })
|
||||
return (chatListView, context.sharedContext.currentPresentationData.with { $0 })
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
@ -87,7 +87,7 @@ final class WatchChatMessagesHandler: WatchRequestHandler {
|
||||
if let context = context {
|
||||
return context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId), index: .upperBound, anchorIndex: .upperBound, count: limit, fixedCombinedReadStates: nil)
|
||||
|> map { messageHistoryView, _, _ -> (MessageHistoryView, Bool, PresentationData) in
|
||||
return (messageHistoryView, peerId == context.account.peerId, context.currentPresentationData.with { $0 })
|
||||
return (messageHistoryView, peerId == context.account.peerId, context.sharedContext.currentPresentationData.with { $0 })
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
@ -140,7 +140,7 @@ final class WatchChatMessagesHandler: WatchRequestHandler {
|
||||
let messageSignal = downloadMessage(postbox: context.account.postbox, network: context.account.network, messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.messageId))
|
||||
|> map { message -> (Message, PresentationData)? in
|
||||
if let message = message {
|
||||
return (message, context.currentPresentationData.with { $0 })
|
||||
return (message, context.sharedContext.currentPresentationData.with { $0 })
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 709df1faeffdd6fefc72bb8a1853b65a5e803bdc
|
||||
Subproject commit 076af3926c4a5d55bcad4ffece41464059c891d9
|
@ -1 +1 @@
|
||||
Subproject commit 88bc22e1192569ae4c0111b6d371119a87a91f08
|
||||
Subproject commit 57821c9c362f56e8ea652b51086fda516e791999
|
@ -1 +1 @@
|
||||
Subproject commit 84231fb6fcbb39f72b50b2812c728cab8c659b52
|
||||
Subproject commit 3e4cc1a26fb677682de98117cf4c918331f5160d
|
@ -1 +1 @@
|
||||
Subproject commit 0d8163db0b4d763090b44e3df750ae6b4632ff31
|
||||
Subproject commit 867d73ba99f1caac56d997762415948de633ca8d
|
Loading…
x
Reference in New Issue
Block a user