From 598756ee449c87367d7a50e953110b5f5bc20d69 Mon Sep 17 00:00:00 2001 From: Peter Iakovlev Date: Fri, 25 Jan 2019 17:49:49 +0400 Subject: [PATCH] Improved multiaccount support --- TelegramUI.xcodeproj/project.pbxproj | 14 +- TelegramUI/AccountContext.swift | 41 ++- TelegramUI/AccountStore.swift | 26 -- TelegramUI/AccountUserInterface.swift | 7 + .../AuthorizationSequenceController.swift | 79 ++++-- ...rizationSequencePhoneEntryController.swift | 12 +- TelegramUI/CallKitIntergation.swift | 21 +- TelegramUI/CallListController.swift | 18 +- TelegramUI/ChatController.swift | 52 ++-- .../ChatMessageInteractiveFileNode.swift | 6 +- ...atMessageInteractiveInstantVideoNode.swift | 6 +- .../ChatMessageInteractiveMediaNode.swift | 2 +- .../ChatRecentActionsControllerNode.swift | 6 +- .../ChatRecordingPreviewInputPanelNode.swift | 2 +- TelegramUI/CheckDeviceAccess.swift | 8 +- TelegramUI/ContactListNode.swift | 4 +- TelegramUI/ContactsController.swift | 4 +- TelegramUI/ContactsControllerNode.swift | 2 +- TelegramUI/ContactsSearchContainerNode.swift | 4 +- TelegramUI/DeviceContactInfoController.swift | 82 +++--- TelegramUI/FileMediaResourceStatus.swift | 3 +- TelegramUI/GalleryController.swift | 10 +- TelegramUI/GalleryHiddenMediaManager.swift | 8 +- TelegramUI/GroupInfoController.swift | 2 +- TelegramUI/InstantPageAudioNode.swift | 8 +- TelegramUI/InstantPageControllerNode.swift | 4 +- TelegramUI/InstantPagePlayableVideoNode.swift | 2 +- TelegramUI/InviteContactsControllerNode.swift | 2 +- TelegramUI/ItemListPeerItem.swift | 20 +- TelegramUI/LegacyAttachmentMenu.swift | 2 +- TelegramUI/LegacyMediaPickers.swift | 2 +- TelegramUI/LegacyWallpaperEditor.swift | 2 +- TelegramUI/ListMessageFileItemNode.swift | 2 +- TelegramUI/MediaManager.swift | 129 ++++------ TelegramUI/NotificationSoundSelection.swift | 2 +- TelegramUI/NotificationsAndSounds.swift | 8 +- TelegramUI/OpenAddContact.swift | 2 +- TelegramUI/OpenChatMessage.swift | 4 +- TelegramUI/OpenInOptions.swift | 2 +- TelegramUI/OpenResolvedUrl.swift | 2 +- TelegramUI/OpenUrl.swift | 38 +-- TelegramUI/OverlayPlayerControllerNode.swift | 4 +- TelegramUI/OverlayPlayerControlsNode.swift | 24 +- .../PeerMediaCollectionController.swift | 6 +- TelegramUI/PermissionController.swift | 2 +- TelegramUI/PhotoResources.swift | 2 +- TelegramUI/PresentationCallManager.swift | 79 +++--- TelegramUI/RecentSessionsController.swift | 2 + TelegramUI/SaveToCameraRoll.swift | 2 +- TelegramUI/SecretMediaPreviewController.swift | 8 +- TelegramUI/SecureIdAuthController.swift | 2 +- TelegramUI/SettingsController.swift | 175 +++++++------ TelegramUI/SharedAccountContext.swift | 240 ++++++++++++++++++ TelegramUI/SharedMediaPlayer.swift | 14 +- TelegramUI/StorageUsageController.swift | 4 +- TelegramUI/SuppressContactsWarning.swift | 2 +- TelegramUI/TelegramController.swift | 37 ++- .../TelegramInitializeLegacyComponents.swift | 4 +- TelegramUI/TelegramRootController.swift | 6 +- .../ThemeAutoNightSettingsController.swift | 2 +- TelegramUI/UniversalVideoGalleryItem.swift | 12 +- TelegramUI/UserInfoController.swift | 18 +- TelegramUI/WebSearchVideoGalleryItem.swift | 4 +- 63 files changed, 793 insertions(+), 506 deletions(-) delete mode 100644 TelegramUI/AccountStore.swift create mode 100644 TelegramUI/AccountUserInterface.swift create mode 100644 TelegramUI/SharedAccountContext.swift diff --git a/TelegramUI.xcodeproj/project.pbxproj b/TelegramUI.xcodeproj/project.pbxproj index b0d99f7677..b2f24cbc4d 100644 --- a/TelegramUI.xcodeproj/project.pbxproj +++ b/TelegramUI.xcodeproj/project.pbxproj @@ -207,7 +207,8 @@ D025A4231F79344500563950 /* FetchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D025A4221F79344500563950 /* FetchManager.swift */; }; D025A4261F79428E00563950 /* FetchManagerLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D025A4251F79428E00563950 /* FetchManagerLocation.swift */; }; D02660941F34CE5C000E2DC5 /* LegacyLocationVenueIconDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02660931F34CE5C000E2DC5 /* LegacyLocationVenueIconDataSource.swift */; }; - D02B198A21F1DA9E0094A764 /* AccountStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02B198921F1DA9E0094A764 /* AccountStore.swift */; }; + D02B198A21F1DA9E0094A764 /* SharedAccountContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02B198921F1DA9E0094A764 /* SharedAccountContext.swift */; }; + D02B198E21FA453F0094A764 /* AccountUserInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02B198D21FA453F0094A764 /* AccountUserInterface.swift */; }; D02B2B9820810DA00062476B /* StickerPaneSearchStickerItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02B2B9720810DA00062476B /* StickerPaneSearchStickerItem.swift */; }; D02B676320800A00001A864A /* StickerPaneSearchBarPlaceholderItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02B676220800A00001A864A /* StickerPaneSearchBarPlaceholderItem.swift */; }; D02C81712177729000CD1006 /* NotificationExceptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02C81702177729000CD1006 /* NotificationExceptions.swift */; }; @@ -1428,7 +1429,8 @@ D025A4251F79428E00563950 /* FetchManagerLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchManagerLocation.swift; sourceTree = ""; }; D02660931F34CE5C000E2DC5 /* LegacyLocationVenueIconDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LegacyLocationVenueIconDataSource.swift; path = "../third-party/RMIntro/LegacyLocationVenueIconDataSource.swift"; sourceTree = ""; }; D02958011D6F0D5F00360E5E /* TapLongTapOrDoubleTapGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TapLongTapOrDoubleTapGestureRecognizer.swift; sourceTree = ""; }; - D02B198921F1DA9E0094A764 /* AccountStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountStore.swift; sourceTree = ""; }; + D02B198921F1DA9E0094A764 /* SharedAccountContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedAccountContext.swift; sourceTree = ""; }; + D02B198D21FA453F0094A764 /* AccountUserInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountUserInterface.swift; sourceTree = ""; }; D02B2B9720810DA00062476B /* StickerPaneSearchStickerItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerPaneSearchStickerItem.swift; sourceTree = ""; }; D02B676220800A00001A864A /* StickerPaneSearchBarPlaceholderItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerPaneSearchBarPlaceholderItem.swift; sourceTree = ""; }; D02BE0701D91814C000889C2 /* ChatHistoryGridNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatHistoryGridNode.swift; sourceTree = ""; }; @@ -2733,7 +2735,9 @@ D02B198821F1DA8A0094A764 /* Account Management */ = { isa = PBXGroup; children = ( - D02B198921F1DA9E0094A764 /* AccountStore.swift */, + D02B198921F1DA9E0094A764 /* SharedAccountContext.swift */, + D05811931DD5F9380057C769 /* AccountContext.swift */, + D02B198D21FA453F0094A764 /* AccountUserInterface.swift */, ); name = "Account Management"; sourceTree = ""; @@ -4678,7 +4682,6 @@ D0B844571DAC44E8005F29E1 /* PeerPresenceStatusManager.swift */, D073CE641DCBC26B007511FD /* ServiceSoundManager.swift */, D073CE701DCBF23F007511FD /* DeclareEncodables.swift */, - D05811931DD5F9380057C769 /* AccountContext.swift */, D023836F1DDF0462004018B6 /* UrlHandling.swift */, D0F917B41E0DA396003687E6 /* GenerateTextEntities.swift */, D01749541E1082770057C89A /* StoredMessageFromSearchPeer.swift */, @@ -5202,7 +5205,7 @@ D0EC6CE31EB9F58800EBF1C3 /* PresentationResourcesChat.swift in Sources */, D0AA840C1FEB2BA3005C6E91 /* OverlayPlayerControlsNode.swift in Sources */, 09DD5D5021ECC3C400D7007A /* SuppressContactsWarning.swift in Sources */, - D02B198A21F1DA9E0094A764 /* AccountStore.swift in Sources */, + D02B198A21F1DA9E0094A764 /* SharedAccountContext.swift in Sources */, D0F67FF21EE6B915000E5906 /* ChannelMembersSearchControllerNode.swift in Sources */, D0EC6CE41EB9F58800EBF1C3 /* PresentationData.swift in Sources */, D0EC6CE51EB9F58800EBF1C3 /* PresentationStrings.swift in Sources */, @@ -5520,6 +5523,7 @@ D0EC6D801EB9F58800EBF1C3 /* HashtagSearchControllerNode.swift in Sources */, D0EC6D811EB9F58800EBF1C3 /* ChatController.swift in Sources */, D0FFF7F81F55B83600BEBC01 /* InstantPageAudioNode.swift in Sources */, + D02B198E21FA453F0094A764 /* AccountUserInterface.swift in Sources */, D0B37C5E1F8D26A8004252DF /* ThemeSettingsChatPreviewItem.swift in Sources */, D093D7DB2062CFF500BC3599 /* SecureIdAuthFormContentNode.swift in Sources */, D0EC6D821EB9F58800EBF1C3 /* ChatControllerInteraction.swift in Sources */, diff --git a/TelegramUI/AccountContext.swift b/TelegramUI/AccountContext.swift index 19c5177576..d534a3080e 100644 --- a/TelegramUI/AccountContext.swift +++ b/TelegramUI/AccountContext.swift @@ -60,22 +60,18 @@ public final class TelegramApplicationBindings { } public final class AccountContext { + public let sharedContext: SharedAccountContext public let account: Account - public let applicationBindings: TelegramApplicationBindings - public let accountManager: AccountManager public let fetchManager: FetchManager public var callManager: PresentationCallManager? public var keyShortcutsController: KeyShortcutsController? - public let mediaManager: MediaManager + let downloadedMediaStoreManager: DownloadedMediaStoreManager - let locationManager: DeviceLocationManager? public let liveLocationManager: LiveLocationManager? - public let contactDataManager = DeviceContactDataManager() - let peerChannelMemberCategoriesContextsManager = PeerChannelMemberCategoriesContextsManager() public let currentPresentationData: Atomic @@ -123,43 +119,36 @@ public final class AccountContext { public var isCurrent: Bool = false { didSet { - self.mediaManager.isCurrent = self.isCurrent if !self.isCurrent { self.callManager = nil } } } - public init(account: Account, applicationBindings: TelegramApplicationBindings, accountManager: AccountManager, initialPresentationDataAndSettings: InitialPresentationDataAndSettings, postbox: Postbox) { + public init(sharedContext: SharedAccountContext, account: Account, initialPresentationDataAndSettings: InitialPresentationDataAndSettings) { + self.sharedContext = sharedContext self.account = account - self.mediaManager = MediaManager(postbox: postbox, inForeground: applicationBindings.applicationInForeground) + self.downloadedMediaStoreManager = DownloadedMediaStoreManager(postbox: account.postbox) - if applicationBindings.isMainApp { - self.locationManager = DeviceLocationManager(queue: Queue.mainQueue()) - } else { - self.locationManager = nil - } - if let locationManager = self.locationManager { - self.liveLocationManager = LiveLocationManager(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, viewTracker: account.viewTracker, stateManager: account.stateManager, locationManager: locationManager, inForeground: applicationBindings.applicationInForeground) + if let locationManager = self.sharedContext.locationManager { + self.liveLocationManager = LiveLocationManager(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, viewTracker: account.viewTracker, stateManager: account.stateManager, locationManager: locationManager, inForeground: self.sharedContext.applicationBindings.applicationInForeground) } else { self.liveLocationManager = nil } - self.applicationBindings = applicationBindings - self.accountManager = accountManager - self.fetchManager = FetchManager(postbox: postbox, storeManager: self.mediaManager.downloadedMediaStoreManager) + self.fetchManager = FetchManager(postbox: account.postbox, storeManager: self.downloadedMediaStoreManager) self.currentPresentationData = Atomic(value: initialPresentationDataAndSettings.presentationData) self.currentAutomaticMediaDownloadSettings = Atomic(value: initialPresentationDataAndSettings.automaticMediaDownloadSettings) self.currentMediaInputSettings = Atomic(value: initialPresentationDataAndSettings.mediaInputSettings) self._presentationData.set(.single(initialPresentationDataAndSettings.presentationData) - |> then(updatedPresentationData(postbox: account.postbox, applicationBindings: applicationBindings))) + |> then(updatedPresentationData(postbox: account.postbox, applicationBindings: self.sharedContext.applicationBindings))) self._automaticMediaDownloadSettings.set(.single(initialPresentationDataAndSettings.automaticMediaDownloadSettings) |> then(updatedAutomaticMediaDownloadSettings(postbox: account.postbox))) self.currentInAppNotificationSettings = Atomic(value: initialPresentationDataAndSettings.inAppNotificationSettings) let inAppPreferencesKey = PostboxViewKey.preferences(keys: Set([ApplicationSpecificPreferencesKeys.inAppNotificationSettings])) - inAppNotificationSettingsDisposable = (postbox.combinedView(keys: [inAppPreferencesKey]) |> deliverOnMainQueue).start(next: { [weak self] views in + inAppNotificationSettingsDisposable = (account.postbox.combinedView(keys: [inAppPreferencesKey]) |> deliverOnMainQueue).start(next: { [weak self] views in if let strongSelf = self { if let view = views.views[inAppPreferencesKey] as? PreferencesView { if let settings = view.values[ApplicationSpecificPreferencesKeys.inAppNotificationSettings] as? InAppNotificationSettings { @@ -170,7 +159,7 @@ public final class AccountContext { }) let mediaInputSettingsPreferencesKey = PostboxViewKey.preferences(keys: Set([ApplicationSpecificPreferencesKeys.mediaInputSettings])) - self.mediaInputSettingsDisposable = (postbox.combinedView(keys: [mediaInputSettingsPreferencesKey]) |> deliverOnMainQueue).start(next: { [weak self] views in + self.mediaInputSettingsDisposable = (account.postbox.combinedView(keys: [mediaInputSettingsPreferencesKey]) |> deliverOnMainQueue).start(next: { [weak self] views in if let strongSelf = self { if let view = views.views[mediaInputSettingsPreferencesKey] as? PreferencesView { if let settings = view.values[ApplicationSpecificPreferencesKeys.mediaInputSettings] as? MediaInputSettings { @@ -223,15 +212,15 @@ public final class AccountContext { let immediateExperimentalUISettingsValue = self.immediateExperimentalUISettingsValue let _ = immediateExperimentalUISettingsValue.swap(initialPresentationDataAndSettings.experimentalUISettings) - self.experimentalUISettingsDisposable = (postbox.preferencesView(keys: [ApplicationSpecificPreferencesKeys.experimentalUISettings]) + self.experimentalUISettingsDisposable = (account.postbox.preferencesView(keys: [ApplicationSpecificPreferencesKeys.experimentalUISettings]) |> deliverOnMainQueue).start(next: { view in if let settings = view.values[ApplicationSpecificPreferencesKeys.experimentalUISettings] as? ExperimentalUISettings { let _ = immediateExperimentalUISettingsValue.swap(settings) } }) - let _ = self.contactDataManager.personNameDisplayOrder().start(next: { order in - let _ = updateContactSettingsInteractively(postbox: postbox, { settings in + let _ = self.sharedContext.contactDataManager?.personNameDisplayOrder().start(next: { order in + let _ = updateContactSettingsInteractively(postbox: account.postbox, { settings in var settings = settings settings.nameDisplayOrder = order return settings @@ -247,7 +236,7 @@ public final class AccountContext { } public func attachOverlayMediaController(_ controller: OverlayMediaController) { - self.mediaManager.overlayMediaManager.attachOverlayMediaController(controller) + self.sharedContext.mediaManager.overlayMediaManager.attachOverlayMediaController(controller) } public func storeSecureIdPassword(password: String) { diff --git a/TelegramUI/AccountStore.swift b/TelegramUI/AccountStore.swift deleted file mode 100644 index c51cf52b28..0000000000 --- a/TelegramUI/AccountStore.swift +++ /dev/null @@ -1,26 +0,0 @@ -import Foundation -import Postbox -import TelegramCore -import SwiftSignalKit -import Display - -public final class AccountStore { - private static var accountManager: AccountManager? - static var switchingSettingsController: (SettingsController & ViewController)? - - public static func initialize(accountManager: AccountManager) { - assert(self.accountManager == nil) - self.accountManager = accountManager - } - - static func switchToAccount(id: AccountRecordId, fromSettingsController settingsController: (SettingsController & ViewController)? = nil) { - assert(Queue.mainQueue().isCurrent()) - guard let accountManager = self.accountManager else { - preconditionFailure() - } - self.switchingSettingsController = settingsController - let _ = accountManager.transaction({ transaction in - transaction.setCurrentId(id) - }).start() - } -} diff --git a/TelegramUI/AccountUserInterface.swift b/TelegramUI/AccountUserInterface.swift new file mode 100644 index 0000000000..67ef21b664 --- /dev/null +++ b/TelegramUI/AccountUserInterface.swift @@ -0,0 +1,7 @@ +import Foundation +import Display +import Postbox +import TelegramCore +import SwiftSignalKit + + diff --git a/TelegramUI/AuthorizationSequenceController.swift b/TelegramUI/AuthorizationSequenceController.swift index 3318f9e46d..2f9f995af2 100644 --- a/TelegramUI/AuthorizationSequenceController.swift +++ b/TelegramUI/AuthorizationSequenceController.swift @@ -18,7 +18,9 @@ public final class AuthorizationSequenceController: NavigationController { return NavigationBarTheme(buttonColor: theme.accentColor, disabledButtonColor: UIColor(rgb: 0xd0d0d0), primaryTextColor: .black, backgroundColor: .clear, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) } + private let sharedContext: SharedAccountContext private var account: UnauthorizedAccount + private let hasOtherAccounts: Bool private let apiId: Int32 private let apiHash: String private var strings: PresentationStrings @@ -28,8 +30,10 @@ public final class AuthorizationSequenceController: NavigationController { private var stateDisposable: Disposable? private let actionDisposable = MetaDisposable() - public init(account: UnauthorizedAccount, strings: PresentationStrings, openUrl: @escaping (String) -> Void, apiId: Int32, apiHash: String) { + public init(sharedContext: SharedAccountContext, account: UnauthorizedAccount, hasOtherAccounts: Bool, strings: PresentationStrings, openUrl: @escaping (String) -> Void, apiId: Int32, apiHash: String) { + self.sharedContext = sharedContext self.account = account + self.hasOtherAccounts = hasOtherAccounts self.apiId = apiId self.apiHash = apiHash self.strings = strings @@ -112,15 +116,21 @@ public final class AuthorizationSequenceController: NavigationController { if let currentController = currentController { controller = currentController } else { - controller = AuthorizationSequencePhoneEntryController(network: self.account.network, strings: self.strings, theme: self.theme, openUrl: { [weak self] url in + controller = AuthorizationSequencePhoneEntryController(hasOtherAccounts: self.hasOtherAccounts, network: self.account.network, strings: self.strings, theme: self.theme, openUrl: { [weak self] url in self?.openUrl(url) }, back: { [weak self] in guard let strongSelf = self else { return } - let _ = strongSelf.account.postbox.transaction({ transaction -> Void in - transaction.setState(UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .empty)) - }).start() + if strongSelf.hasOtherAccounts { + let _ = (strongSelf.sharedContext.accountManager.transaction { transaction -> Void in + transaction.removeAuth() + }).start() + } else { + let _ = strongSelf.account.postbox.transaction({ transaction -> Void in + transaction.setState(UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .empty)) + }).start() + } }) controller.loginWithNumber = { [weak self, weak controller] number in if let strongSelf = self { @@ -243,7 +253,7 @@ public final class AuthorizationSequenceController: NavigationController { if let strongSelf = self { controller?.inProgress = true - strongSelf.actionDisposable.set((authorizeWithCode(account: strongSelf.account, code: code, termsOfService: termsOfService?.0) + strongSelf.actionDisposable.set((authorizeWithCode(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, code: code, termsOfService: termsOfService?.0) |> deliverOnMainQueue).start(next: { result in guard let strongSelf = self else { return @@ -420,7 +430,7 @@ public final class AuthorizationSequenceController: NavigationController { if let strongSelf = self { controller?.inProgress = true - strongSelf.actionDisposable.set((authorizeWithPassword(account: strongSelf.account, password: password) |> deliverOnMainQueue).start(error: { error in + strongSelf.actionDisposable.set((authorizeWithPassword(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, password: password) |> deliverOnMainQueue).start(error: { error in Queue.mainQueue().async { if let strongSelf = self, let controller = controller { controller.inProgress = false @@ -528,7 +538,7 @@ public final class AuthorizationSequenceController: NavigationController { if let strongSelf = self { controller?.inProgress = true - strongSelf.actionDisposable.set((performPasswordRecovery(account: strongSelf.account, code: code) |> deliverOnMainQueue).start(error: { error in + strongSelf.actionDisposable.set((performPasswordRecovery(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, code: code) |> deliverOnMainQueue).start(error: { error in Queue.mainQueue().async { if let strongSelf = self, let controller = controller { controller.inProgress = false @@ -655,7 +665,7 @@ public final class AuthorizationSequenceController: NavigationController { if let strongSelf = self { controller?.inProgress = true - strongSelf.actionDisposable.set((signUpWithName(account: strongSelf.account, firstName: firstName, lastName: lastName, avatarData: avatarData) + strongSelf.actionDisposable.set((signUpWithName(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, firstName: firstName, lastName: lastName, avatarData: avatarData) |> deliverOnMainQueue).start(error: { error in Queue.mainQueue().async { if let strongSelf = self, let controller = controller { @@ -695,20 +705,57 @@ public final class AuthorizationSequenceController: NavigationController { case .empty: if let _ = self.viewControllers.last as? AuthorizationSequenceSplashController { } else { - self.setViewControllers([self.splashController()], animated: !self.viewControllers.isEmpty) + var controllers: [ViewController] = [] + if !self.hasOtherAccounts { + controllers.append(self.splashController()) + } else { + controllers.append(self.phoneEntryController(countryCode: defaultCountryCode(), number: "")) + } + self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty) } case let .phoneEntry(countryCode, number): - self.setViewControllers([self.splashController(), self.phoneEntryController(countryCode: countryCode, number: number)], animated: !self.viewControllers.isEmpty) + var controllers: [ViewController] = [] + if !self.hasOtherAccounts { + controllers.append(self.splashController()) + } + controllers.append(self.phoneEntryController(countryCode: countryCode, number: number)) + self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty) case let .confirmationCodeEntry(number, type, _, timeout, nextType, termsOfService): - self.setViewControllers([self.splashController(), self.phoneEntryController(countryCode: defaultCountryCode(), number: ""), self.codeEntryController(number: number, type: type, nextType: nextType, timeout: timeout, termsOfService: termsOfService)], animated: !self.viewControllers.isEmpty) + var controllers: [ViewController] = [] + if !self.hasOtherAccounts { + controllers.append(self.splashController()) + } + controllers.append(self.phoneEntryController(countryCode: defaultCountryCode(), number: "")) + controllers.append(self.codeEntryController(number: number, type: type, nextType: nextType, timeout: timeout, termsOfService: termsOfService)) + self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty) case let .passwordEntry(hint, _, _, suggestReset): - self.setViewControllers([self.splashController(), self.passwordEntryController(hint: hint, suggestReset: suggestReset)], animated: !self.viewControllers.isEmpty) + var controllers: [ViewController] = [] + if !self.hasOtherAccounts { + controllers.append(self.splashController()) + } + controllers.append(self.passwordEntryController(hint: hint, suggestReset: suggestReset)) + self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty) case let .passwordRecovery(_, _, _, emailPattern): - self.setViewControllers([self.splashController(), self.passwordRecoveryController(emailPattern: emailPattern)], animated: !self.viewControllers.isEmpty) + var controllers: [ViewController] = [] + if !self.hasOtherAccounts { + controllers.append(self.splashController()) + } + controllers.append(self.passwordRecoveryController(emailPattern: emailPattern)) + self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty) case let .awaitingAccountReset(protectedUntil, number): - self.setViewControllers([self.splashController(), self.awaitingAccountResetController(protectedUntil: protectedUntil, number: number)], animated: !self.viewControllers.isEmpty) + var controllers: [ViewController] = [] + if !self.hasOtherAccounts { + controllers.append(self.splashController()) + } + controllers.append(self.awaitingAccountResetController(protectedUntil: protectedUntil, number: number)) + self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty) case let .signUp(_, _, _, firstName, lastName, termsOfService): - self.setViewControllers([self.splashController(), self.signUpController(firstName: firstName, lastName: lastName, termsOfService: termsOfService)], animated: !self.viewControllers.isEmpty) + var controllers: [ViewController] = [] + if !self.hasOtherAccounts { + controllers.append(self.splashController()) + } + controllers.append(self.signUpController(firstName: firstName, lastName: lastName, termsOfService: termsOfService)) + self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty) } } } diff --git a/TelegramUI/AuthorizationSequencePhoneEntryController.swift b/TelegramUI/AuthorizationSequencePhoneEntryController.swift index c3f5d30444..fa66b15387 100644 --- a/TelegramUI/AuthorizationSequencePhoneEntryController.swift +++ b/TelegramUI/AuthorizationSequencePhoneEntryController.swift @@ -14,6 +14,8 @@ final class AuthorizationSequencePhoneEntryController: ViewController { private let theme: AuthorizationTheme private let openUrl: (String) -> Void + private let back: () -> Void + private var currentData: (Int32, String?, String)? var inProgress: Bool = false { @@ -33,11 +35,12 @@ final class AuthorizationSequencePhoneEntryController: ViewController { private let hapticFeedback = HapticFeedback() - init(network: Network, strings: PresentationStrings, theme: AuthorizationTheme, openUrl: @escaping (String) -> Void, back: @escaping () -> Void) { + init(hasOtherAccounts: Bool, network: Network, strings: PresentationStrings, theme: AuthorizationTheme, openUrl: @escaping (String) -> Void, back: @escaping () -> Void) { self.network = network self.strings = strings self.theme = theme self.openUrl = openUrl + self.back = back super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: AuthorizationSequenceController.navigationBarTheme(theme), strings: NavigationBarStrings(presentationStrings: strings))) @@ -53,6 +56,9 @@ final class AuthorizationSequencePhoneEntryController: ViewController { back() } + if hasOtherAccounts { + self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)) + } self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed)) } @@ -64,6 +70,10 @@ final class AuthorizationSequencePhoneEntryController: ViewController { self.termsDisposable.dispose() } + @objc private func cancelPressed() { + self.back() + } + func updateData(countryCode: Int32, countryName: String?, number: String) { self.currentData = (countryCode, countryName, number) if self.isNodeLoaded { diff --git a/TelegramUI/CallKitIntergation.swift b/TelegramUI/CallKitIntergation.swift index d11abfb86f..abb5838a4f 100644 --- a/TelegramUI/CallKitIntergation.swift +++ b/TelegramUI/CallKitIntergation.swift @@ -2,6 +2,7 @@ import Foundation import CallKit import AVFoundation import Postbox +import TelegramCore import SwiftSignalKit private var sharedProviderDelegate: AnyObject? @@ -24,7 +25,7 @@ public final class CallKitIntegration { return self.audioSessionActivePromise.get() } - init?(startCall: @escaping (UUID, String) -> Signal, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) { + init?(startCall: @escaping (Account, UUID, String) -> Signal, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) { if !CallKitIntegration.isAvailable { return nil } @@ -44,9 +45,9 @@ public final class CallKitIntegration { #endif } - func startCall(peerId: PeerId, displayTitle: String) { + func startCall(account: Account, peerId: PeerId, displayTitle: String) { if #available(iOSApplicationExtension 10.0, *) { - (sharedProviderDelegate as? CallKitProviderDelegate)?.startCall(peerId: peerId, displayTitle: displayTitle) + (sharedProviderDelegate as? CallKitProviderDelegate)?.startCall(account: account, peerId: peerId, displayTitle: displayTitle) } } @@ -80,7 +81,9 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate { private let provider: CXProvider private let callController = CXCallController() - private var startCall: ((UUID, String) -> Signal)? + private var currentStartCallAccount: (UUID, Account)? + + private var startCall: ((Account, UUID, String) -> Signal)? private var answerCall: ((UUID) -> Void)? private var endCall: ((UUID) -> Signal)? private var setCallMuted: ((UUID, Bool) -> Void)? @@ -98,7 +101,7 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate { self.provider.setDelegate(self, queue: nil) } - func setup(audioSessionActivePromise: ValuePromise, startCall: @escaping (UUID, String) -> Signal, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) { + func setup(audioSessionActivePromise: ValuePromise, startCall: @escaping (Account, UUID, String) -> Signal, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) { self.audioSessionActivePromise = audioSessionActivePromise self.startCall = startCall self.answerCall = answerCall @@ -144,8 +147,9 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate { } - func startCall(peerId: PeerId, displayTitle: String) { + func startCall(account: Account, peerId: PeerId, displayTitle: String) { let uuid = UUID() + self.currentStartCallAccount = (uuid, account) let handle = CXHandle(type: .generic, value: "\(peerId.id)") let startCallAction = CXStartCallAction(call: uuid, handle: handle) startCallAction.contactIdentifier = displayTitle @@ -192,13 +196,14 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate { } func provider(_ provider: CXProvider, perform action: CXStartCallAction) { - guard let startCall = self.startCall else { + guard let startCall = self.startCall, let (uuid, account) = self.currentStartCallAccount, uuid == action.callUUID else { action.fail() return } + self.currentStartCallAccount = nil let disposable = MetaDisposable() self.disposableSet.add(disposable) - disposable.set((startCall(action.callUUID, action.handle.value) + disposable.set((startCall(account, action.callUUID, action.handle.value) |> deliverOnMainQueue |> afterDisposed { [weak self, weak disposable] in if let strongSelf = self, let disposable = disposable { diff --git a/TelegramUI/CallListController.swift b/TelegramUI/CallListController.swift index a8e065c611..14d16cb92e 100644 --- a/TelegramUI/CallListController.swift +++ b/TelegramUI/CallListController.swift @@ -143,13 +143,13 @@ public final class CallListController: ViewController { }, openInfo: { [weak self] peerId, messages in if let strongSelf = self { let _ = (strongSelf.context.account.postbox.loadedPeerWithId(peerId) - |> take(1) - |> deliverOnMainQueue).start(next: { peer in - if let strongSelf = self { - let infoController = userInfoController(context: strongSelf.context, peerId: peer.id, mode: .calls(messages: messages)) - (strongSelf.navigationController as? NavigationController)?.pushViewController(infoController) - } - }) + |> take(1) + |> deliverOnMainQueue).start(next: { peer in + if let strongSelf = self { + let infoController = userInfoController(context: strongSelf.context, peerId: peer.id, mode: .calls(messages: messages)) + (strongSelf.navigationController as? NavigationController)?.pushViewController(infoController) + } + }) } }, emptyStateUpdated: { [weak self] empty in if let strongSelf = self { @@ -265,7 +265,7 @@ public final class CallListController: ViewController { return } - let callResult = strongSelf.context.callManager?.requestCall(peerId: peerId, endCurrentIfAny: false) + let callResult = strongSelf.context.callManager?.requestCall(account: strongSelf.context.account, peerId: peerId, endCurrentIfAny: false) if let callResult = callResult { if case let .alreadyInProgress(currentPeerId) = callResult { if currentPeerId == peerId { @@ -279,7 +279,7 @@ public final class CallListController: ViewController { if let strongSelf = self, let peer = peer, let current = current { strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { if let strongSelf = self { - let _ = strongSelf.context.callManager?.requestCall(peerId: peerId, endCurrentIfAny: true) + let _ = strongSelf.context.callManager?.requestCall(account: strongSelf.context.account, peerId: peerId, endCurrentIfAny: true) began?() } })]), in: .window(.root)) diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index 2003e622b7..9170021a14 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -640,7 +640,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal }, shareCurrentLocation: { [weak self] in if let strongSelf = self { strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Conversation_ShareBotLocationConfirmationTitle, text: strongSelf.presentationData.strings.Conversation_ShareBotLocationConfirmation, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { - if let strongSelf = self, let locationManager = strongSelf.context.locationManager { + if let strongSelf = self, let locationManager = strongSelf.context.sharedContext.locationManager { let _ = (currentLocationManagerCoordinate(manager: locationManager, timeout: 5.0) |> deliverOnMainQueue).start(next: { coordinate in if let strongSelf = self { @@ -820,7 +820,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal return } - let callResult = context.callManager?.requestCall(peerId: peer.id, endCurrentIfAny: false) + let callResult = context.callManager?.requestCall(account: context.account, peerId: peer.id, endCurrentIfAny: false) if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult { if currentPeerId == peer.id { context.navigateToCurrentCall?() @@ -832,7 +832,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal |> deliverOnMainQueue).start(next: { peer, current in if let peer = peer, let current = current { strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { - let _ = context.callManager?.requestCall(peerId: peer.id, endCurrentIfAny: true) + let _ = context.callManager?.requestCall(account: context.account, peerId: peer.id, endCurrentIfAny: true) })]), in: .window(.root)) } }) @@ -1143,7 +1143,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } }, openAppStorePage: { [weak self] in if let strongSelf = self { - strongSelf.context.applicationBindings.openAppStorePage() + strongSelf.context.sharedContext.applicationBindings.openAppStorePage() } }, requestMessageUpdate: { [weak self] id in if let strongSelf = self { @@ -1545,7 +1545,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } }) - self.applicationInForegroundDisposable = (context.applicationBindings.applicationInForeground + self.applicationInForegroundDisposable = (context.sharedContext.applicationBindings.applicationInForeground |> distinctUntilChanged |> deliverOn(Queue.mainQueue())).start(next: { [weak self] value in if let strongSelf = self, strongSelf.isNodeLoaded { @@ -1558,7 +1558,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } }) - self.canReadHistoryDisposable = (combineLatest(context.applicationBindings.applicationInForeground, self.canReadHistory.get()) |> map { a, b in + self.canReadHistoryDisposable = (combineLatest(context.sharedContext.applicationBindings.applicationInForeground, self.canReadHistory.get()) |> map { a, b in return a && b } |> deliverOnMainQueue).start(next: { [weak self] value in if let strongSelf = self, strongSelf.canReadHistoryValue != value { @@ -1572,14 +1572,6 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal strongSelf.chatTitleView?.networkState = state } }) - - if case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat { - self.screenCaptureEventsDisposable = screenCaptureEvents().start(next: { [weak self] _ in - if let strongSelf = self, strongSelf.canReadHistoryValue, strongSelf.traceVisibility() { - let _ = addSecretChatMessageScreenshot(account: context.account, peerId: peerId).start() - } - }) - } } required public init(coder aDecoder: NSCoder) { @@ -1633,7 +1625,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal self.screenCaptureEventsDisposable?.dispose() self.chatAdditionalDataDisposable.dispose() self.shareStatusDisposable?.dispose() - self.context.mediaManager.galleryHiddenMediaManager.removeTarget(self) + self.context.sharedContext.mediaManager.galleryHiddenMediaManager.removeTarget(self) } public func updatePresentationMode(_ mode: ChatControllerPresentationMode) { @@ -2530,7 +2522,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal DeviceAccess.authorizeAccess(to: .microphone(isVideo ? .video : .audio), presentationData: strongSelf.presentationData, present: { c, a in self?.present(c, in: .window(.root), with: a) }, openSettings: { - self?.context.applicationBindings.openSettings() + self?.context.sharedContext.applicationBindings.openSettings() }, { granted in guard let strongSelf = self, granted else { return @@ -2539,7 +2531,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal DeviceAccess.authorizeAccess(to: .camera, presentationData: strongSelf.presentationData, present: { c, a in self?.present(c, in: .window(.root), with: a) }, openSettings: { - self?.context.applicationBindings.openSettings() + self?.context.sharedContext.applicationBindings.openSettings() }, { granted in if granted { begin() @@ -3139,14 +3131,13 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal self.interfaceInteraction = interfaceInteraction self.chatDisplayNode.interfaceInteraction = interfaceInteraction - let mediaManager = self.context.mediaManager - mediaManager.galleryHiddenMediaManager.addTarget(self) - self.galleryHiddenMesageAndMediaDisposable.set(mediaManager.galleryHiddenMediaManager.hiddenIds().start(next: { [weak self] ids in + self.context.sharedContext.mediaManager.galleryHiddenMediaManager.addTarget(self) + self.galleryHiddenMesageAndMediaDisposable.set(self.context.sharedContext.mediaManager.galleryHiddenMediaManager.hiddenIds().start(next: { [weak self] ids in if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction { var messageIdAndMedia: [MessageId: [Media]] = [:] for id in ids { - if case let .chat(messageId, media) = id { + if case let .chat(accountId, messageId, media) = id, accountId == strongSelf.context.account.id { messageIdAndMedia[messageId] = [media] } } @@ -3182,7 +3173,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal super.viewDidAppear(animated) self.chatDisplayNode.historyNode.preloadPages = true - self.chatDisplayNode.historyNode.canReadHistory.set(combineLatest(context.applicationBindings.applicationInForeground, self.canReadHistory.get()) |> map { a, b in + self.chatDisplayNode.historyNode.canReadHistory.set(combineLatest(context.sharedContext.applicationBindings.applicationInForeground, self.canReadHistory.get()) |> map { a, b in return a && b }) @@ -3272,6 +3263,15 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal if !self.checkedPeerChatServiceActions { self.checkedPeerChatServiceActions = true + + if case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat { + self.screenCaptureEventsDisposable = screenCaptureEvents().start(next: { [weak self] _ in + if let strongSelf = self, strongSelf.canReadHistoryValue, strongSelf.traceVisibility() { + let _ = addSecretChatMessageScreenshot(account: strongSelf.context.account, peerId: peerId).start() + } + }) + } + if case let .peer(peerId) = self.chatLocation { let _ = checkPeerChatServiceActions(postbox: self.context.account.postbox, peerId: peerId).start() } @@ -4170,7 +4170,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: contact.firstName ?? "", lastName: contact.lastName ?? "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!!$_", value: phoneNumber)]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: []) let context = strongSelf.context - dataSignal = strongSelf.context.contactDataManager.basicData() + dataSignal = (strongSelf.context.sharedContext.contactDataManager?.basicData() ?? .single([:])) |> take(1) |> mapToSignal { basicData -> Signal<(Peer?, DeviceContactExtendedData?), NoError> in var stableId: String? @@ -4185,7 +4185,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } if let stableId = stableId { - return context.contactDataManager.extendedData(stableId: stableId) + return (context.sharedContext.contactDataManager?.extendedData(stableId: stableId) ?? .single(nil)) |> take(1) |> map { extendedData -> (Peer?, DeviceContactExtendedData?) in return (contact, extendedData) @@ -4195,7 +4195,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal } } case let .deviceContact(id, _): - dataSignal = strongSelf.context.contactDataManager.extendedData(stableId: id) + dataSignal = (strongSelf.context.sharedContext.contactDataManager?.extendedData(stableId: id) ?? .single(nil)) |> take(1) |> map { extendedData -> (Peer?, DeviceContactExtendedData?) in return (nil, extendedData) @@ -4439,7 +4439,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal self.recorderFeedback?.prepareImpact(.light) } - self.audioRecorder.set(self.context.mediaManager.audioRecorder(beginWithTone: beginWithTone, applicationBindings: self.context.applicationBindings, beganWithTone: { _ in + self.audioRecorder.set(self.context.sharedContext.mediaManager.audioRecorder(beginWithTone: beginWithTone, applicationBindings: self.context.sharedContext.applicationBindings, beganWithTone: { _ in })) } } diff --git a/TelegramUI/ChatMessageInteractiveFileNode.swift b/TelegramUI/ChatMessageInteractiveFileNode.swift index 7c61ef1736..6e77b4058f 100644 --- a/TelegramUI/ChatMessageInteractiveFileNode.swift +++ b/TelegramUI/ChatMessageInteractiveFileNode.swift @@ -148,8 +148,8 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { } } case .playbackStatus: - if let context = context, let message = self.message, let type = peerMessageMediaPlayerType(message) { - context.mediaManager.playlistControl(.playback(.togglePlayPause), type: type) + if let context = self.context, let message = self.message, let type = peerMessageMediaPlayerType(message) { + context.sharedContext.mediaManager.playlistControl(.playback(.togglePlayPause), type: type) } } } @@ -511,7 +511,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { waveformScrubbingNode.hitTestSlop = UIEdgeInsetsMake(-10.0, 0.0, -10.0, 0.0) waveformScrubbingNode.seek = { timestamp in if let strongSelf = self, let context = strongSelf.context, let message = strongSelf.message, let type = peerMessageMediaPlayerType(message) { - context.mediaManager.playlistControl(.seek(timestamp), type: type) + context.sharedContext.mediaManager.playlistControl(.seek(timestamp), type: type) } } waveformScrubbingNode.status = strongSelf.playbackStatus.get() diff --git a/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift b/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift index 2d442a2923..f198b69a07 100644 --- a/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift +++ b/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift @@ -359,12 +359,12 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { videoNode?.removeFromSupernode() }) } - let mediaManager = item.context.mediaManager + let mediaManager = item.context.sharedContext.mediaManager let videoNode = UniversalVideoNode(postbox: item.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: ChatBubbleInstantVideoDecoration(diameter: displaySize.width + 2.0, backgroundImage: instantVideoBackgroundImage, tapped: { if let strongSelf = self { if let item = strongSelf.item { if strongSelf.infoBackgroundNode.alpha.isZero { - item.context.mediaManager.playlistControl(.playback(.togglePlayPause), type: .voice) + item.context.sharedContext.mediaManager.playlistControl(.playback(.togglePlayPause), type: .voice) } else { //let _ = item.controllerInteraction.openMessage(item.message) } @@ -603,7 +603,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { return } if self.infoBackgroundNode.alpha.isZero { - item.context.mediaManager.playlistControl(.playback(.togglePlayPause), type: .voice) + item.context.sharedContext.mediaManager.playlistControl(.playback(.togglePlayPause), type: .voice) } else { let _ = item.controllerInteraction.openMessage(item.message, .default) } diff --git a/TelegramUI/ChatMessageInteractiveMediaNode.swift b/TelegramUI/ChatMessageInteractiveMediaNode.swift index 1352caa588..1edc890053 100644 --- a/TelegramUI/ChatMessageInteractiveMediaNode.swift +++ b/TelegramUI/ChatMessageInteractiveMediaNode.swift @@ -453,7 +453,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { } if replaceVideoNode, let updatedVideoFile = updateVideoFile { - let mediaManager = context.mediaManager + let mediaManager = context.sharedContext.mediaManager let cornerRadius: CGFloat = arguments.corners.topLeft.radius let videoNode = UniversalVideoNode(postbox: context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: ChatBubbleVideoDecoration(cornerRadius: cornerRadius, nativeSize: nativeSize, backgroudColor: arguments.emptyColor ?? .black), content: NativeVideoContent(id: .message(message.id, message.stableId, updatedVideoFile.fileId), fileReference: .message(message: MessageReference(message), media: updatedVideoFile), enableSound: false, fetchAutomatically: false), priority: .embedded) videoNode.isUserInteractionEnabled = false diff --git a/TelegramUI/ChatRecentActionsControllerNode.swift b/TelegramUI/ChatRecentActionsControllerNode.swift index 71a89849bb..266042fda6 100644 --- a/TelegramUI/ChatRecentActionsControllerNode.swift +++ b/TelegramUI/ChatRecentActionsControllerNode.swift @@ -360,7 +360,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }, requestSelectMessagePollOption: { _, _ in }, openAppStorePage: { [weak self] in if let strongSelf = self { - strongSelf.context.applicationBindings.openAppStorePage() + strongSelf.context.sharedContext.applicationBindings.openAppStorePage() } }, requestMessageUpdate: { _ in }, cancelInteractiveKeyboardGestures: { @@ -413,13 +413,13 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { self.historyDisposable = appliedTransition.start() - let mediaManager = self.context.mediaManager + let mediaManager = self.context.sharedContext.mediaManager self.galleryHiddenMesageAndMediaDisposable.set(mediaManager.galleryHiddenMediaManager.hiddenIds().start(next: { [weak self] ids in if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction { var messageIdAndMedia: [MessageId: [Media]] = [:] for id in ids { - if case let .chat(messageId, media) = id { + if case let .chat(accountId, messageId, media) = id, accountId == strongSelf.context.account.id { messageIdAndMedia[messageId] = [media] } } diff --git a/TelegramUI/ChatRecordingPreviewInputPanelNode.swift b/TelegramUI/ChatRecordingPreviewInputPanelNode.swift index 3a85a878a3..dce082ba39 100644 --- a/TelegramUI/ChatRecordingPreviewInputPanelNode.swift +++ b/TelegramUI/ChatRecordingPreviewInputPanelNode.swift @@ -110,7 +110,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode { self.mediaPlayer?.pause() } if let context = self.context { - let mediaManager = context.mediaManager + let mediaManager = context.sharedContext.mediaManager let mediaPlayer = MediaPlayer(audioSessionManager: mediaManager.audioSession, postbox: context.account.postbox, resourceReference: .standalone(resource: recordedMediaPreview.resource), streamable: false, video: false, preferSoftwareDecoding: false, enableSound: true, fetchAutomatically: true) self.mediaPlayer = mediaPlayer self.durationLabel.defaultDuration = Double(recordedMediaPreview.duration) diff --git a/TelegramUI/CheckDeviceAccess.swift b/TelegramUI/CheckDeviceAccess.swift index 533394fb1e..4c50c44b20 100644 --- a/TelegramUI/CheckDeviceAccess.swift +++ b/TelegramUI/CheckDeviceAccess.swift @@ -125,7 +125,7 @@ public final class DeviceAccess { } }) if let context = context { - return context.applicationBindings.applicationInForeground + return context.sharedContext.applicationBindings.applicationInForeground |> distinctUntilChanged |> mapToSignal { inForeground -> Signal in return status @@ -198,7 +198,7 @@ public final class DeviceAccess { case .siri: if let context = context { return Signal { subscriber in - let status = context.applicationBindings.siriAuthorization() + let status = context.sharedContext.applicationBindings.siriAuthorization() subscriber.putNext(status) subscriber.putCompletion() return EmptyDisposable @@ -405,14 +405,14 @@ public final class DeviceAccess { }) case .notifications: if let context = context { - context.applicationBindings.registerForNotifications { result in + context.sharedContext.applicationBindings.registerForNotifications { result in self.notificationsPromise.set(.single(result)) completion(result) } } case .siri: if let context = context { - context.applicationBindings.requestSiriAuthorization { result in + context.sharedContext.applicationBindings.requestSiriAuthorization { result in self.siriPromise.set(.single(result)) completion(result) } diff --git a/TelegramUI/ContactListNode.swift b/TelegramUI/ContactListNode.swift index 085a918bcd..075752e75d 100644 --- a/TelegramUI/ContactListNode.swift +++ b/TelegramUI/ContactListNode.swift @@ -966,7 +966,7 @@ final class ContactListNode: ASDisplayNode { ) let foundDeviceContacts: Signal<[DeviceContactStableId: DeviceContactBasicData], NoError> if searchDeviceContacts { - foundDeviceContacts = context.contactDataManager.search(query: query) + foundDeviceContacts = context.sharedContext.contactDataManager?.search(query: query) ?? .single([:]) } else { foundDeviceContacts = .single([:]) } @@ -1188,7 +1188,7 @@ final class ContactListNode: ASDisplayNode { case .notDetermined: DeviceAccess.authorizeAccess(to: .contacts) case .denied, .restricted: - context.applicationBindings.openSettings() + context.sharedContext.applicationBindings.openSettings() default: break } diff --git a/TelegramUI/ContactsController.swift b/TelegramUI/ContactsController.swift index a2bad08e61..7618a0a443 100644 --- a/TelegramUI/ContactsController.swift +++ b/TelegramUI/ContactsController.swift @@ -219,7 +219,7 @@ public class ContactsController: ViewController { case let .peer(peer, _): (strongSelf.navigationController as? NavigationController)?.pushViewController(ChatController(context: strongSelf.context, chatLocation: .peer(peer.id))) case let .deviceContact(id, _): - let _ = (strongSelf.context.contactDataManager.extendedData(stableId: id) + let _ = ((strongSelf.context.sharedContext.contactDataManager?.extendedData(stableId: id) ?? .single(nil)) |> take(1) |> deliverOnMainQueue).start(next: { value in guard let strongSelf = self, let value = value else { @@ -362,7 +362,7 @@ public class ContactsController: ViewController { default: let presentationData = strongSelf.presentationData strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.AccessDenied_Title, text: presentationData.strings.Contacts_AccessDeniedError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { - self?.context.applicationBindings.openSettings() + self?.context.sharedContext.applicationBindings.openSettings() })]), in: .window(.root)) } }) diff --git a/TelegramUI/ContactsControllerNode.swift b/TelegramUI/ContactsControllerNode.swift index 22a5759d24..6f988df89c 100644 --- a/TelegramUI/ContactsControllerNode.swift +++ b/TelegramUI/ContactsControllerNode.swift @@ -84,7 +84,7 @@ final class ContactsControllerNode: ASDisplayNode { default: let presentationData = strongSelf.presentationData present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.AccessDenied_Title, text: presentationData.strings.Contacts_AccessDeniedError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { - self?.context.applicationBindings.openSettings() + self?.context.sharedContext.applicationBindings.openSettings() })]), nil) } }) diff --git a/TelegramUI/ContactsSearchContainerNode.swift b/TelegramUI/ContactsSearchContainerNode.swift index cf1f85c319..c04e2b7618 100644 --- a/TelegramUI/ContactsSearchContainerNode.swift +++ b/TelegramUI/ContactsSearchContainerNode.swift @@ -186,8 +186,8 @@ final class ContactsSearchContainerNode: SearchDisplayControllerContentNode { } let searchDeviceContacts = categories.contains(.deviceContacts) let foundDeviceContacts: Signal<[DeviceContactStableId: DeviceContactBasicData]?, NoError> - if searchDeviceContacts { - foundDeviceContacts = context.contactDataManager.search(query: query) + if searchDeviceContacts, let contactDataManager = context.sharedContext.contactDataManager { + foundDeviceContacts = contactDataManager.search(query: query) |> map(Optional.init) } else { foundDeviceContacts = .single([:]) diff --git a/TelegramUI/DeviceContactInfoController.swift b/TelegramUI/DeviceContactInfoController.swift index 9b94a8b947..ae4486cd2f 100644 --- a/TelegramUI/DeviceContactInfoController.swift +++ b/TelegramUI/DeviceContactInfoController.swift @@ -772,7 +772,7 @@ public func deviceContactInfoController(context: AccountContext, subject: Device ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: presentationData.strings.UserInfo_TelegramCall, action: { dismissAction() - let callResult = context.callManager?.requestCall(peerId: user.id, endCurrentIfAny: false) + let callResult = context.callManager?.requestCall(account: context.account, peerId: user.id, endCurrentIfAny: false) if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult { if currentPeerId == user.id { context.navigateToCurrentCall?() @@ -783,7 +783,7 @@ public func deviceContactInfoController(context: AccountContext, subject: Device } |> deliverOnMainQueue).start(next: { peer, current in if let peer = peer, let current = current { presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { - let _ = context.callManager?.requestCall(peerId: peer.id, endCurrentIfAny: true) + let _ = context.callManager?.requestCall(account: context.account, peerId: peer.id, endCurrentIfAny: true) })]), nil) } }) @@ -792,14 +792,14 @@ public func deviceContactInfoController(context: AccountContext, subject: Device }), ActionSheetButtonItem(title: presentationData.strings.UserInfo_PhoneCall, action: { dismissAction() - context.applicationBindings.openUrl("tel:\(formatPhoneNumber(number).replacingOccurrences(of: " ", with: ""))") + context.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(number).replacingOccurrences(of: " ", with: ""))") }), ]), ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) ]) presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) } else { - context.applicationBindings.openUrl("tel:\(formatPhoneNumber(number).replacingOccurrences(of: " ", with: ""))") + context.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(number).replacingOccurrences(of: " ", with: ""))") } }) } @@ -968,19 +968,21 @@ public func deviceContactInfoController(context: AccountContext, subject: Device state.savingData = true return state } - let _ = (context.contactDataManager.createContactWithData(composedContactData) - |> deliverOnMainQueue).start(next: { contactIdAndData in - updateState { state in - var state = state - state.savingData = false - return state - } - if let contactIdAndData = contactIdAndData { - //completion(nil, contactIdAndData.0, contactIdAndData.1) - } - completed?() - dismissImpl?(true) - }) + if let contactDataManager = context.sharedContext.contactDataManager { + let _ = (contactDataManager.createContactWithData(composedContactData) + |> deliverOnMainQueue).start(next: { contactIdAndData in + updateState { state in + var state = state + state.savingData = false + return state + } + if let contactIdAndData = contactIdAndData { + //completion(nil, contactIdAndData.0, contactIdAndData.1) + } + completed?() + dismissImpl?(true) + }) + } } }) } @@ -1121,7 +1123,7 @@ private func addContactToExisting(context: AccountContext, parentController: Vie guard let contact = contact as? TelegramUser, let phoneNumber = contact.phone else { return } - dataSignal = context.contactDataManager.basicData() + dataSignal = (context.sharedContext.contactDataManager?.basicData() ?? .single([:])) |> take(1) |> mapToSignal { basicData -> Signal<(Peer?, DeviceContactStableId?), NoError> in var stableId: String? @@ -1147,30 +1149,32 @@ private func addContactToExisting(context: AccountContext, parentController: Vie })), in: .window(.root)) return } - let _ = (context.contactDataManager.appendContactData(contactData, to: stableId) - |> deliverOnMainQueue).start(next: { contactData in - guard let contactData = contactData else { - return - } - let _ = (context.account.postbox.contactPeersView(accountPeerId: nil, includePresences: false) - |> take(1) - |> deliverOnMainQueue).start(next: { view in - let phones = Set(contactData.basicData.phoneNumbers.map { - return formatPhoneNumber($0.value) - }) - var foundPeer: Peer? - for peer in view.peers { - if let user = peer as? TelegramUser, let phone = user.phone { - let phone = formatPhoneNumber(phone) - if phones.contains(phone) { - foundPeer = peer - break + if let contactDataManager = context.sharedContext.contactDataManager { + let _ = (contactDataManager.appendContactData(contactData, to: stableId) + |> deliverOnMainQueue).start(next: { contactData in + guard let contactData = contactData else { + return + } + let _ = (context.account.postbox.contactPeersView(accountPeerId: nil, includePresences: false) + |> take(1) + |> deliverOnMainQueue).start(next: { view in + let phones = Set(contactData.basicData.phoneNumbers.map { + return formatPhoneNumber($0.value) + }) + var foundPeer: Peer? + for peer in view.peers { + if let user = peer as? TelegramUser, let phone = user.phone { + let phone = formatPhoneNumber(phone) + if phones.contains(phone) { + foundPeer = peer + break + } } } - } - completion(foundPeer, stableId, contactData) + completion(foundPeer, stableId, contactData) + }) }) - }) + } }) } }) diff --git a/TelegramUI/FileMediaResourceStatus.swift b/TelegramUI/FileMediaResourceStatus.swift index 41f01f4e03..c8d920b5bf 100644 --- a/TelegramUI/FileMediaResourceStatus.swift +++ b/TelegramUI/FileMediaResourceStatus.swift @@ -24,8 +24,7 @@ private func internalMessageFileMediaPlaybackStatus(context: AccountContext, fil } if let (playlistId, itemId) = peerMessagesMediaPlaylistAndItemId(message, isRecentActions: isRecentActions) { - let mediaManager = context.mediaManager - return mediaManager.filteredPlaylistState(playlistId: playlistId, itemId: itemId, type: playerType) + return context.sharedContext.mediaManager.filteredPlaylistState(accountId: context.account.id, playlistId: playlistId, itemId: itemId, type: playerType) |> mapToSignal { state -> Signal in return .single(state?.status) } diff --git a/TelegramUI/GalleryController.swift b/TelegramUI/GalleryController.swift index 2e15d6de36..425244ccc4 100644 --- a/TelegramUI/GalleryController.swift +++ b/TelegramUI/GalleryController.swift @@ -502,10 +502,11 @@ class GalleryController: ViewController { } })) - let mediaManager = context.mediaManager - self.hiddenMediaManagerIndex = mediaManager.galleryHiddenMediaManager.addSource(self._hiddenMedia.get() |> map { messageIdAndMedia in + let mediaManager = context.sharedContext.mediaManager + self.hiddenMediaManagerIndex = mediaManager.galleryHiddenMediaManager.addSource(self._hiddenMedia.get() + |> map { messageIdAndMedia in if let (messageId, media) = messageIdAndMedia { - return .chat(messageId, media) + return .chat(context.account.id, messageId, media) } else { return nil } @@ -688,8 +689,7 @@ class GalleryController: ViewController { self.disposable.dispose() self.centralItemAttributesDisposable.dispose() if let hiddenMediaManagerIndex = self.hiddenMediaManagerIndex { - let mediaManager = self.context.mediaManager - mediaManager.galleryHiddenMediaManager.removeSource(hiddenMediaManagerIndex) + self.context.sharedContext.mediaManager.galleryHiddenMediaManager.removeSource(hiddenMediaManagerIndex) } } diff --git a/TelegramUI/GalleryHiddenMediaManager.swift b/TelegramUI/GalleryHiddenMediaManager.swift index 2f7c8f8c9c..5210699052 100644 --- a/TelegramUI/GalleryHiddenMediaManager.swift +++ b/TelegramUI/GalleryHiddenMediaManager.swift @@ -4,12 +4,12 @@ import SwiftSignalKit import AsyncDisplayKit enum GalleryHiddenMediaId: Hashable { - case chat(MessageId, Media) + case chat(AccountRecordId, MessageId, Media) static func ==(lhs: GalleryHiddenMediaId, rhs: GalleryHiddenMediaId) -> Bool { switch lhs { - case let .chat(lhsMessageId, lhsMedia): - if case let .chat(rhsMessageId, rhsMedia) = rhs, lhsMessageId == rhsMessageId, lhsMedia.isEqual(to: rhsMedia) { + case let .chat(lhsAccountId ,lhsMessageId, lhsMedia): + if case let .chat(rhsAccountId, rhsMessageId, rhsMedia) = rhs, lhsAccountId == rhsAccountId, lhsMessageId == rhsMessageId, lhsMedia.isEqual(to: rhsMedia) { return true } else { return false @@ -19,7 +19,7 @@ enum GalleryHiddenMediaId: Hashable { var hashValue: Int { switch self { - case let .chat(messageId, _): + case let .chat(_, messageId, _): return messageId.hashValue } } diff --git a/TelegramUI/GroupInfoController.swift b/TelegramUI/GroupInfoController.swift index 7163b4ff35..dcfce201c3 100644 --- a/TelegramUI/GroupInfoController.swift +++ b/TelegramUI/GroupInfoController.swift @@ -2223,7 +2223,7 @@ func handlePeerInfoAboutTextAction(context: AccountContext, peerId: PeerId, navi if let controller = controller { switch result { case let .externalUrl(url): - context.applicationBindings.openUrl(url) + context.sharedContext.applicationBindings.openUrl(url) case let .peer(peerId, _): openResolvedPeerImpl(peerId, .default) case let .channelMessage(peerId, messageId): diff --git a/TelegramUI/InstantPageAudioNode.swift b/TelegramUI/InstantPageAudioNode.swift index df5c1e4d53..3b332737a6 100644 --- a/TelegramUI/InstantPageAudioNode.swift +++ b/TelegramUI/InstantPageAudioNode.swift @@ -130,7 +130,7 @@ final class InstantPageAudioNode: ASDisplayNode, InstantPageNode { self.scrubbingNode.seek = { [weak self] timestamp in if let strongSelf = self { if let _ = strongSelf.playbackState { - strongSelf.context.mediaManager.playlistControl(.seek(timestamp), type: strongSelf.playlistType) + strongSelf.context.sharedContext.mediaManager.playlistControl(.seek(timestamp), type: strongSelf.playlistType) } } } @@ -174,12 +174,12 @@ final class InstantPageAudioNode: ASDisplayNode, InstantPageNode { } })*/ - self.scrubbingNode.status = context.mediaManager.filteredPlaylistState(playlistId: InstantPageMediaPlaylistId(webpageId: webPage.webpageId), itemId: InstantPageMediaPlaylistItemId(index: self.media.index), type: self.playlistType) + self.scrubbingNode.status = context.sharedContext.mediaManager.filteredPlaylistState(accountId: context.account.id, playlistId: InstantPageMediaPlaylistId(webpageId: webPage.webpageId), itemId: InstantPageMediaPlaylistItemId(index: self.media.index), type: self.playlistType) |> map { playbackState -> MediaPlayerStatus in return playbackState?.status ?? MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused) } - self.playerStatusDisposable = (context.mediaManager.filteredPlaylistState(playlistId: InstantPageMediaPlaylistId(webpageId: webPage.webpageId), itemId: InstantPageMediaPlaylistItemId(index: self.media.index), type: playlistType) + self.playerStatusDisposable = (context.sharedContext.mediaManager.filteredPlaylistState(accountId: context.account.id, playlistId: InstantPageMediaPlaylistId(webpageId: webPage.webpageId), itemId: InstantPageMediaPlaylistItemId(index: self.media.index), type: playlistType) |> deliverOnMainQueue).start(next: { [weak self] playbackState in guard let strongSelf = self else { return @@ -249,7 +249,7 @@ final class InstantPageAudioNode: ASDisplayNode, InstantPageNode { @objc func buttonPressed() { if let _ = self.playbackState { - self.context.mediaManager.playlistControl(.playback(.togglePlayPause), type: self.playlistType) + self.context.sharedContext.mediaManager.playlistControl(.playback(.togglePlayPause), type: self.playlistType) } else { self.openMedia(self.media) } diff --git a/TelegramUI/InstantPageControllerNode.swift b/TelegramUI/InstantPageControllerNode.swift index 066fd46e24..8bac3d2498 100644 --- a/TelegramUI/InstantPageControllerNode.swift +++ b/TelegramUI/InstantPageControllerNode.swift @@ -1215,7 +1215,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { } } } - self.context.mediaManager.setPlaylist(InstantPageMediaPlaylist(webPage: webPage, items: medias, initialItemIndex: initialIndex), type: file.isVoice ? .voice : .music) + self.context.sharedContext.mediaManager.setPlaylist((self.context.account, InstantPageMediaPlaylist(webPage: webPage, items: medias, initialItemIndex: initialIndex)), type: file.isVoice ? .voice : .music) return } @@ -1313,7 +1313,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { } }, openInSafari: { [weak self] in if let strongSelf = self, let webPage = strongSelf.webPage, case let .Loaded(content) = webPage.content { - strongSelf.context.applicationBindings.openUrl(content.url) + strongSelf.context.sharedContext.applicationBindings.openUrl(content.url) } }) self.addSubnode(settingsNode) diff --git a/TelegramUI/InstantPagePlayableVideoNode.swift b/TelegramUI/InstantPagePlayableVideoNode.swift index e0017a1007..f18d49d4b8 100644 --- a/TelegramUI/InstantPagePlayableVideoNode.swift +++ b/TelegramUI/InstantPagePlayableVideoNode.swift @@ -31,7 +31,7 @@ final class InstantPagePlayableVideoNode: ASDisplayNode, InstantPageNode { imageReference = ImageMediaReference.webPage(webPage: WebpageReference(webPage), media: image) } - self.videoNode = UniversalVideoNode(postbox: context.account.postbox, audioSession: context.mediaManager.audioSession, manager: context.mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: NativeVideoContent(id: .instantPage(webPage.webpageId, media.media.id!), fileReference: .webPage(webPage: WebpageReference(webPage), media: media.media as! TelegramMediaFile), imageReference: imageReference, loopVideo: true, enableSound: false, fetchAutomatically: true, placeholderColor: theme.pageBackgroundColor), priority: .embedded, autoplay: true) + self.videoNode = UniversalVideoNode(postbox: context.account.postbox, audioSession: context.sharedContext.mediaManager.audioSession, manager: context.sharedContext.mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: NativeVideoContent(id: .instantPage(webPage.webpageId, media.media.id!), fileReference: .webPage(webPage: WebpageReference(webPage), media: media.media as! TelegramMediaFile), imageReference: imageReference, loopVideo: true, enableSound: false, fetchAutomatically: true, placeholderColor: theme.pageBackgroundColor), priority: .embedded, autoplay: true) self.videoNode.isUserInteractionEnabled = false super.init() diff --git a/TelegramUI/InviteContactsControllerNode.swift b/TelegramUI/InviteContactsControllerNode.swift index 0f2a6e7330..82ed427632 100644 --- a/TelegramUI/InviteContactsControllerNode.swift +++ b/TelegramUI/InviteContactsControllerNode.swift @@ -350,7 +350,7 @@ final class InviteContactsControllerNode: ASDisplayNode { } let currentSortedContacts = self.currentSortedContacts - let sortedContacts: Signal<[(DeviceContactStableId, DeviceContactBasicData, Int32)], NoError> = combineLatest(existingNumbers, context.contactDataManager.basicData() |> take(1)) + let sortedContacts: Signal<[(DeviceContactStableId, DeviceContactBasicData, Int32)], NoError> = combineLatest(existingNumbers, (context.sharedContext.contactDataManager?.basicData() ?? .single([:])) |> take(1)) |> mapToSignal { existingNumbers, contacts -> Signal<[(DeviceContactStableId, DeviceContactBasicData, Int32)], NoError> in var mappedContacts: [(String, [DeviceContactNormalizedPhoneNumber])] = [] for (id, basicData) in contacts { diff --git a/TelegramUI/ItemListPeerItem.swift b/TelegramUI/ItemListPeerItem.swift index a9f38e393e..7397423988 100644 --- a/TelegramUI/ItemListPeerItem.swift +++ b/TelegramUI/ItemListPeerItem.swift @@ -641,7 +641,7 @@ class ItemListPeerItemNode: ItemListRevealOptionsItemNode { let badgeWidth = max(badgeDiameter, labelLayout.size.width + 10.0) let labelFrame: CGRect if case .badge = item.label { - labelFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - rightLabelInset - badgeWidth + (badgeWidth - labelLayout.size.width) / 2.0, y: 13.0), size: labelLayout.size) + labelFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - rightLabelInset - badgeWidth + (badgeWidth - labelLayout.size.width) / 2.0, y: floor((contentSize.height - labelLayout.size.height) / 2.0)), size: labelLayout.size) strongSelf.labelNode.frame = labelFrame } else { labelFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - labelLayout.size.width - rightLabelInset - rightInset, y: floor((contentSize.height - labelLayout.size.height) / 2.0)), size: labelLayout.size) @@ -659,7 +659,7 @@ class ItemListPeerItemNode: ItemListRevealOptionsItemNode { strongSelf.labelBadgeNode.removeFromSupernode() } - strongSelf.labelBadgeNode.frame = CGRect(origin: CGPoint(x: params.width - rightLabelInset - badgeWidth, y: 12.0), size: CGSize(width: badgeWidth, height: badgeDiameter)) + strongSelf.labelBadgeNode.frame = CGRect(origin: CGPoint(x: revealOffset + params.width - rightLabelInset - badgeWidth, y: labelFrame.minY - 1.0), size: CGSize(width: badgeWidth, height: badgeDiameter)) transition.updateFrame(node: strongSelf.avatarNode, frame: CGRect(origin: CGPoint(x: params.leftInset + revealOffset + editingOffset + 15.0, y: 5.0), size: CGSize(width: 40.0, height: 40.0))) @@ -733,7 +733,7 @@ class ItemListPeerItemNode: ItemListRevealOptionsItemNode { override func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) { super.updateRevealOffset(offset: offset, transition: transition) - guard let params = self.layoutParams?.1 else { + guard let item = self.layoutParams?.0, let params = self.layoutParams?.1 else { return } @@ -762,7 +762,19 @@ class ItemListPeerItemNode: ItemListRevealOptionsItemNode { } } - transition.updateFrame(node: self.labelNode, frame: CGRect(origin: CGPoint(x: revealOffset + params.width - self.labelNode.bounds.size.width - rightLabelInset, y: self.labelNode.frame.minY), size: self.labelNode.bounds.size)) + let badgeDiameter: CGFloat = 20.0 + let labelSize = self.labelNode.frame.size + + let badgeWidth = max(badgeDiameter, labelSize.width + 10.0) + let labelFrame: CGRect + if case .badge = item.label { + labelFrame = CGRect(origin: CGPoint(x: offset + params.width - rightLabelInset - badgeWidth + (badgeWidth - labelSize.width) / 2.0, y: self.labelNode.frame.minY), size: labelSize) + } else { + labelFrame = CGRect(origin: CGPoint(x: offset + params.width - self.labelNode.bounds.size.width - rightLabelInset, y: self.labelNode.frame.minY), size: self.labelNode.bounds.size) + } + transition.updateFrame(node: self.labelNode, frame: labelFrame) + + transition.updateFrame(node: self.labelBadgeNode, frame: CGRect(origin: CGPoint(x: offset + params.width - rightLabelInset - badgeWidth, y: self.labelBadgeNode.frame.minY), size: CGSize(width: badgeWidth, height: badgeDiameter))) transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: revealOffset + editingOffset + params.leftInset + 15.0, y: self.avatarNode.frame.minY), size: CGSize(width: 40.0, height: 40.0))) } diff --git a/TelegramUI/LegacyAttachmentMenu.swift b/TelegramUI/LegacyAttachmentMenu.swift index 6673329a10..59c4baa3e1 100644 --- a/TelegramUI/LegacyAttachmentMenu.swift +++ b/TelegramUI/LegacyAttachmentMenu.swift @@ -39,7 +39,7 @@ func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaOptions: carouselItem.recipientName = peer.displayTitle carouselItem.cameraPressed = { [weak controller] cameraView in if let controller = controller { - DeviceAccess.authorizeAccess(to: .camera, presentationData: context.currentPresentationData.with { $0 }, present: context.presentGlobalController, openSettings: context.applicationBindings.openSettings, { value in + DeviceAccess.authorizeAccess(to: .camera, presentationData: context.currentPresentationData.with { $0 }, present: context.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in if value { openCamera(cameraView, controller) } diff --git a/TelegramUI/LegacyMediaPickers.swift b/TelegramUI/LegacyMediaPickers.swift index 7344dd0c8a..7e013f0721 100644 --- a/TelegramUI/LegacyMediaPickers.swift +++ b/TelegramUI/LegacyMediaPickers.swift @@ -34,7 +34,7 @@ func legacyAssetPicker(context: AccountContext, presentationData: PresentationDa return Signal { subscriber in let intent = fileMode ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent - DeviceAccess.authorizeAccess(to: .mediaLibrary(.send), presentationData: presentationData, present: context.presentGlobalController, openSettings: context.applicationBindings.openSettings, { value in + DeviceAccess.authorizeAccess(to: .mediaLibrary(.send), presentationData: presentationData, present: context.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in if !value { subscriber.putError(Void()) return diff --git a/TelegramUI/LegacyWallpaperEditor.swift b/TelegramUI/LegacyWallpaperEditor.swift index 97926f931d..ef03eea8c4 100644 --- a/TelegramUI/LegacyWallpaperEditor.swift +++ b/TelegramUI/LegacyWallpaperEditor.swift @@ -52,7 +52,7 @@ func legacyWallpaperPicker(context: AccountContext, presentationData: Presentati return Signal { subscriber in let intent = TGMediaAssetsControllerSetCustomWallpaperIntent - DeviceAccess.authorizeAccess(to: .mediaLibrary(.wallpaper), presentationData: presentationData, present: context.presentGlobalController, openSettings: context.applicationBindings.openSettings, { value in + DeviceAccess.authorizeAccess(to: .mediaLibrary(.wallpaper), presentationData: presentationData, present: context.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in if !value { subscriber.putError(Void()) return diff --git a/TelegramUI/ListMessageFileItemNode.swift b/TelegramUI/ListMessageFileItemNode.swift index f4b225b6d0..206dc9f5eb 100644 --- a/TelegramUI/ListMessageFileItemNode.swift +++ b/TelegramUI/ListMessageFileItemNode.swift @@ -859,7 +859,7 @@ final class ListMessageFileItemNode: ListMessageNode { } case .playbackStatus: if let context = self.context { - context.mediaManager.playlistControl(.playback(.togglePlayPause)) + context.sharedContext.mediaManager.playlistControl(.playback(.togglePlayPause)) } } } diff --git a/TelegramUI/MediaManager.swift b/TelegramUI/MediaManager.swift index c7a1adc635..f1ab7b8a56 100644 --- a/TelegramUI/MediaManager.swift +++ b/TelegramUI/MediaManager.swift @@ -1,8 +1,8 @@ import Foundation import SwiftSignalKit -import Postbox import AVFoundation import MobileCoreServices +import Postbox import TelegramCore import MediaPlayer @@ -62,7 +62,6 @@ public final class MediaManager: NSObject { private let queue = Queue.mainQueue() - private let postbox: Postbox private let inForeground: Signal public let audioSession: ManagedAudioSession @@ -75,15 +74,16 @@ public final class MediaManager: NSObject { didSet { if self.voiceMediaPlayer !== oldValue { if let voiceMediaPlayer = self.voiceMediaPlayer { + let account = voiceMediaPlayer.account self.voiceMediaPlayerStateValue.set(voiceMediaPlayer.playbackState - |> map { state -> SharedMediaPlayerItemPlaybackStateOrLoading? in + |> map { state -> (Account, SharedMediaPlayerItemPlaybackStateOrLoading)? in guard let state = state else { return nil } if case let .item(item) = state { - return .state(item) + return (account, .state(item)) } else { - return .loading + return (account, .loading) } } |> deliverOnMainQueue) } else { @@ -92,8 +92,8 @@ public final class MediaManager: NSObject { } } } - private let voiceMediaPlayerStateValue = Promise(nil) - var voiceMediaPlayerState: Signal { + private let voiceMediaPlayerStateValue = Promise<(Account, SharedMediaPlayerItemPlaybackStateOrLoading)?>(nil) + var voiceMediaPlayerState: Signal<(Account, SharedMediaPlayerItemPlaybackStateOrLoading)?, NoError> { return self.voiceMediaPlayerStateValue.get() } @@ -101,15 +101,16 @@ public final class MediaManager: NSObject { didSet { if self.musicMediaPlayer !== oldValue { if let musicMediaPlayer = self.musicMediaPlayer { + let account = musicMediaPlayer.account self.musicMediaPlayerStateValue.set(musicMediaPlayer.playbackState - |> map { state -> SharedMediaPlayerItemPlaybackStateOrLoading? in + |> map { state -> (Account, SharedMediaPlayerItemPlaybackStateOrLoading)? in guard let state = state else { return nil } if case let .item(item) = state { - return .state(item) + return (account, .state(item)) } else { - return .loading + return (account, .loading) } } |> deliverOnMainQueue) } else { @@ -118,13 +119,13 @@ public final class MediaManager: NSObject { } } } - private let musicMediaPlayerStateValue = Promise(nil) - var musicMediaPlayerState: Signal { + private let musicMediaPlayerStateValue = Promise<(Account, SharedMediaPlayerItemPlaybackStateOrLoading)?>(nil) + var musicMediaPlayerState: Signal<(Account, SharedMediaPlayerItemPlaybackStateOrLoading)?, NoError> { return self.musicMediaPlayerStateValue.get() } - private let globalMediaPlayerStateValue = Promise<(SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?>() - var globalMediaPlayerState: Signal<(SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?, NoError> { + private let globalMediaPlayerStateValue = Promise<(Account, SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?>() + var globalMediaPlayerState: Signal<(Account, SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?, NoError> { return self.globalMediaPlayerStateValue.get() } @@ -137,7 +138,7 @@ public final class MediaManager: NSObject { private let globalControlsDisposable = MetaDisposable() private let globalControlsArtworkDisposable = MetaDisposable() - private let globalControlsArtwork = Promise(nil) + private let globalControlsArtwork = Promise<(Account, SharedMediaPlaybackAlbumArt)?>(nil) private let globalControlsStatusDisposable = MetaDisposable() private let globalAudioSessionForegroundDisposable = MetaDisposable() @@ -145,65 +146,30 @@ public final class MediaManager: NSObject { let galleryHiddenMediaManager = GalleryHiddenMediaManager() - let downloadedMediaStoreManager: DownloadedMediaStoreManager - - init(postbox: Postbox, inForeground: Signal) { - self.postbox = postbox + init(inForeground: Signal) { self.inForeground = inForeground self.audioSession = sharedAudioSession - self.downloadedMediaStoreManager = DownloadedMediaStoreManager(postbox: postbox) - super.init() - let combinedPlayersSignal: Signal<(SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?, NoError> = combineLatest(queue: Queue.mainQueue(), self.voiceMediaPlayerState, self.musicMediaPlayerState) - |> map { voice, music -> (SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)? in + let combinedPlayersSignal: Signal<(Account, SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?, NoError> = combineLatest(queue: Queue.mainQueue(), self.voiceMediaPlayerState, self.musicMediaPlayerState) + |> map { voice, music -> (Account, SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)? in if let voice = voice { - return (voice, .voice) + return (voice.0, voice.1, .voice) } else if let music = music { - return (music, .music) + return (music.0, music.1, .music) } else { return nil } } self.globalMediaPlayerStateValue.set(combinedPlayersSignal |> distinctUntilChanged(isEqual: { lhs, rhs in - return lhs?.0 == rhs?.0 && lhs?.1 == rhs?.1 + return lhs?.0 === rhs?.0 && lhs?.1 == rhs?.1 && lhs?.2 == rhs?.2 })) - /*let commandCenter = MPRemoteCommandCenter.shared() - - commandCenter.playCommand.isEnabled = false*/ - - /*commandCenter.pauseCommand.isEnabled = false - commandCenter.pauseCommand.addTarget(self, action: #selector(pauseCommandEvent(_:))) - - commandCenter.previousTrackCommand.isEnabled = false - commandCenter.previousTrackCommand.addTarget(self, action: #selector(previousTrackCommandEvent(_:))) - - commandCenter.nextTrackCommand.isEnabled = false - commandCenter.nextTrackCommand.addTarget(self, action: #selector(nextTrackCommandEvent(_:))) - - commandCenter.togglePlayPauseCommand.isEnabled = false - commandCenter.togglePlayPauseCommand.addTarget(self, action: #selector(togglePlayPauseCommandEvent(_:)))*/ - var baseNowPlayingInfo: [String: Any]? - /*if #available(iOSApplicationExtension 9.1, *) { - commandCenter.changePlaybackPositionCommand.isEnabled = false - commandCenter.changePlaybackPositionCommand.addTarget(handler: { [weak self] event in - if let strongSelf = self, let event = event as? MPChangePlaybackPositionCommandEvent { - strongSelf.playlistControl(.seek(event.positionTime)) - } - if baseNowPlayingInfo != nil { - return .success - } else { - return .noActionableNowPlayingItem - } - }) - }*/ - var previousState: SharedMediaPlayerItemPlaybackState? var previousDisplayData: SharedMediaPlaybackDisplayData? let globalControlsArtwork = self.globalControlsArtwork @@ -214,7 +180,7 @@ public final class MediaManager: NSObject { self.globalControlsDisposable.set((self.globalMediaPlayerState |> deliverOnMainQueue).start(next: { stateAndType in var updatedGlobalControlOptions = GlobalControlOptions() - if let (stateOrLoading, type) = stateAndType, case let .state(state) = stateOrLoading { + if let (_, stateOrLoading, type) = stateAndType, case let .state(state) = stateOrLoading { if type == .music { updatedGlobalControlOptions.insert(.previous) updatedGlobalControlOptions.insert(.next) @@ -228,7 +194,7 @@ public final class MediaManager: NSObject { } } - if let (stateOrLoading, type) = stateAndType, type == .music, case let .state(state) = stateOrLoading, let displayData = state.item.displayData { + if let (account, stateOrLoading, type) = stateAndType, type == .music, case let .state(state) = stateOrLoading, let displayData = state.item.displayData { if previousDisplayData != displayData { previousDisplayData = displayData @@ -255,7 +221,7 @@ public final class MediaManager: NSObject { nowPlayingInfo[MPMediaItemPropertyTitle] = titleText } - globalControlsArtwork.set(.single(artwork)) + globalControlsArtwork.set(.single(artwork.flatMap({ (account, $0) }))) baseNowPlayingInfo = nowPlayingInfo @@ -311,12 +277,12 @@ public final class MediaManager: NSObject { })) self.globalControlsArtworkDisposable.set((self.globalControlsArtwork.get() - |> distinctUntilChanged(isEqual: { $0 == $1 }) + |> distinctUntilChanged(isEqual: { $0?.0 === $1?.0 && $0?.1 == $1?.1 }) |> mapToSignal { value -> Signal in - if let value = value { + if let (account, value) = value { return Signal { subscriber in - let fetched = postbox.mediaBox.fetchedResource(value.fullSizeResource, parameters: nil).start() - let data = postbox.mediaBox.resourceData(value.fullSizeResource, pathExtension: nil, option: .complete(waitUntilFetchStatus: false)).start(next: { data in + let fetched = account.postbox.mediaBox.fetchedResource(value.fullSizeResource, parameters: nil).start() + let data = account.postbox.mediaBox.resourceData(value.fullSizeResource, pathExtension: nil, option: .complete(waitUntilFetchStatus: false)).start(next: { data in if data.complete, let value = try? Data(contentsOf: URL(fileURLWithPath: data.path)) { subscriber.putNext(UIImage(data: value)) subscriber.putCompletion() @@ -370,7 +336,7 @@ public final class MediaManager: NSObject { let shouldKeepAudioSession: Signal = combineLatest(queue: Queue.mainQueue(), self.globalMediaPlayerState, inForeground) |> map { stateAndType, inForeground -> Bool in var isPlaying = false - if let (stateOrLoading, _) = stateAndType, case let .state(state) = stateOrLoading { + if let (_, stateOrLoading, _) = stateAndType, case let .state(state) = stateOrLoading { switch state.status.status { case .playing: isPlaying = true @@ -432,21 +398,30 @@ public final class MediaManager: NSObject { } } - func setPlaylist(_ playlist: SharedMediaPlaylist?, type: MediaManagerPlayerType) { + func setPlaylist(_ playlist: (Account, SharedMediaPlaylist)?, type: MediaManagerPlayerType) { assert(Queue.mainQueue().isCurrent()) - self.setPlaylistByTypeDisposables.set((self.postbox.preferencesView(keys: [ApplicationSpecificPreferencesKeys.musicPlaybackSettings]) - |> take(1) - |> deliverOnMainQueue).start(next: { [weak self] view in - if let strongSelf = self { + let inputData: Signal<(Account, SharedMediaPlaylist, MusicPlaybackSettings)?, NoError> + if let (account, playlist) = playlist { + inputData = account.postbox.preferencesView(keys: [ApplicationSpecificPreferencesKeys.musicPlaybackSettings]) + |> take(1) + |> map { view in let settings = (view.values[ApplicationSpecificPreferencesKeys.musicPlaybackSettings] as? MusicPlaybackSettings) ?? MusicPlaybackSettings.defaultSettings + return (account, playlist, settings) + } + } else { + inputData = .single(nil) + } + self.setPlaylistByTypeDisposables.set((inputData + |> deliverOnMainQueue).start(next: { [weak self] inputData in + if let strongSelf = self { let nextPlayerIndex = strongSelf.nextPlayerIndex strongSelf.nextPlayerIndex += 1 switch type { case .voice: strongSelf.musicMediaPlayer?.control(.playback(.pause)) strongSelf.voiceMediaPlayer?.stop() - if let playlist = playlist { - let voiceMediaPlayer = SharedMediaPlayer(mediaManager: strongSelf, inForeground: strongSelf.inForeground, postbox: strongSelf.postbox, audioSession: strongSelf.audioSession, overlayMediaManager: strongSelf.overlayMediaManager, playlist: playlist, initialOrder: .reversed, initialLooping: .none, initialPlaybackRate: settings.voicePlaybackRate, playerIndex: nextPlayerIndex, controlPlaybackWithProximity: true) + if let (account, playlist, settings) = inputData { + let voiceMediaPlayer = SharedMediaPlayer(mediaManager: strongSelf, inForeground: strongSelf.inForeground, account: account, audioSession: strongSelf.audioSession, overlayMediaManager: strongSelf.overlayMediaManager, playlist: playlist, initialOrder: .reversed, initialLooping: .none, initialPlaybackRate: settings.voicePlaybackRate, playerIndex: nextPlayerIndex, controlPlaybackWithProximity: true) strongSelf.voiceMediaPlayer = voiceMediaPlayer voiceMediaPlayer.playedToEnd = { [weak voiceMediaPlayer] in if let strongSelf = self, let voiceMediaPlayer = voiceMediaPlayer, voiceMediaPlayer === strongSelf.voiceMediaPlayer { @@ -467,8 +442,8 @@ public final class MediaManager: NSObject { case .music: strongSelf.musicMediaPlayer?.stop() strongSelf.voiceMediaPlayer?.control(.playback(.pause)) - if let playlist = playlist { - let musicMediaPlayer = SharedMediaPlayer(mediaManager: strongSelf, inForeground: strongSelf.inForeground, postbox: strongSelf.postbox, audioSession: strongSelf.audioSession, overlayMediaManager: strongSelf.overlayMediaManager, playlist: playlist, initialOrder: settings.order, initialLooping: settings.looping, initialPlaybackRate: .x1, playerIndex: nextPlayerIndex, controlPlaybackWithProximity: false) + if let (account, playlist, settings) = inputData { + let musicMediaPlayer = SharedMediaPlayer(mediaManager: strongSelf, inForeground: strongSelf.inForeground, account: account, audioSession: strongSelf.audioSession, overlayMediaManager: strongSelf.overlayMediaManager, playlist: playlist, initialOrder: settings.order, initialLooping: settings.looping, initialPlaybackRate: .x1, playerIndex: nextPlayerIndex, controlPlaybackWithProximity: false) strongSelf.musicMediaPlayer = musicMediaPlayer musicMediaPlayer.cancelled = { [weak musicMediaPlayer] in if let strongSelf = self, let musicMediaPlayer = musicMediaPlayer, musicMediaPlayer === strongSelf.musicMediaPlayer { @@ -511,8 +486,8 @@ public final class MediaManager: NSObject { } } - func filteredPlaylistState(playlistId: SharedMediaPlaylistId, itemId: SharedMediaPlaylistItemId, type: MediaManagerPlayerType) -> Signal { - let signal: Signal + func filteredPlaylistState(accountId: AccountRecordId, playlistId: SharedMediaPlaylistId, itemId: SharedMediaPlaylistItemId, type: MediaManagerPlayerType) -> Signal { + let signal: Signal<(Account, SharedMediaPlayerItemPlaybackStateOrLoading)?, NoError> switch type { case .voice: signal = self.voiceMediaPlayerState @@ -520,8 +495,8 @@ public final class MediaManager: NSObject { signal = self.musicMediaPlayerState } return signal - |> map { stateOrLoading in - if let stateOrLoading = stateOrLoading, case let .state(state) = stateOrLoading { + |> map { stateOrLoading -> SharedMediaPlayerItemPlaybackState? in + if let (account, stateOrLoading) = stateOrLoading, account.id == accountId, case let .state(state) = stateOrLoading { if state.playlistId.isEqual(to: playlistId) && state.item.id.isEqual(to: itemId) { return state } diff --git a/TelegramUI/NotificationSoundSelection.swift b/TelegramUI/NotificationSoundSelection.swift index ed1cf73b1b..8f7f065547 100644 --- a/TelegramUI/NotificationSoundSelection.swift +++ b/TelegramUI/NotificationSoundSelection.swift @@ -216,7 +216,7 @@ func playSound(context: AccountContext, sound: PeerMessageSound, defaultSound: P return Signal { subscriber in var currentPlayer: AudioPlayerWrapper? var deactivateImpl: (() -> Void)? - let session = context.mediaManager.audioSession.push(audioSessionType: .play, activate: { _ in + let session = context.sharedContext.mediaManager.audioSession.push(audioSessionType: .play, activate: { _ in if let url = Bundle.main.url(forResource: fileNameForNotificationSound(sound, defaultSound: defaultSound), withExtension: "m4a") { currentPlayer = AudioPlayerWrapper(url: url, completed: { deactivateImpl?() diff --git a/TelegramUI/NotificationsAndSounds.swift b/TelegramUI/NotificationsAndSounds.swift index a6ca02b568..47ebff9882 100644 --- a/TelegramUI/NotificationsAndSounds.swift +++ b/TelegramUI/NotificationsAndSounds.swift @@ -701,10 +701,10 @@ public func notificationsAndSoundsController(context: AccountContext, exceptions case .notDetermined: DeviceAccess.authorizeAccess(to: .notifications, context: context) case .denied, .restricted: - context.applicationBindings.openSettings() + context.sharedContext.applicationBindings.openSettings() case .unreachable: ApplicationSpecificNotice.setNotificationsPermissionWarning(postbox: context.account.postbox, value: Int32(Date().timeIntervalSince1970)) - context.applicationBindings.openSettings() + context.sharedContext.applicationBindings.openSettings() default: break } @@ -714,7 +714,7 @@ public func notificationsAndSoundsController(context: AccountContext, exceptions presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Notifications_PermissionsSuppressWarningTitle, text: presentationData.strings.Notifications_PermissionsSuppressWarningText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Notifications_PermissionsKeepDisabled, action: { ApplicationSpecificNotice.setNotificationsPermissionWarning(postbox: context.account.postbox, value: Int32(Date().timeIntervalSince1970)) }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Notifications_PermissionsEnable, action: { - context.applicationBindings.openSettings() + context.sharedContext.applicationBindings.openSettings() })]), nil) }, updateMessageAlerts: { value in let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in @@ -851,7 +851,7 @@ public func notificationsAndSoundsController(context: AccountContext, exceptions } }) }, openAppSettings: { - context.applicationBindings.openSettings() + context.sharedContext.applicationBindings.openSettings() }, updateJoinedNotifications: { value in let _ = updateGlobalNotificationSettingsInteractively(postbox: context.account.postbox, { settings in var settings = settings diff --git a/TelegramUI/OpenAddContact.swift b/TelegramUI/OpenAddContact.swift index 428c3c6f0c..32267e865c 100644 --- a/TelegramUI/OpenAddContact.swift +++ b/TelegramUI/OpenAddContact.swift @@ -16,7 +16,7 @@ func openAddContact(context: AccountContext, firstName: String = "", lastName: S default: let presentationData = context.currentPresentationData.with { $0 } present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.AccessDenied_Title, text: presentationData.strings.Contacts_AccessDeniedError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { - context.applicationBindings.openSettings() + context.sharedContext.applicationBindings.openSettings() })]), nil) } }) diff --git a/TelegramUI/OpenChatMessage.swift b/TelegramUI/OpenChatMessage.swift index fa2b16d845..0171319f4d 100644 --- a/TelegramUI/OpenChatMessage.swift +++ b/TelegramUI/OpenChatMessage.swift @@ -249,7 +249,7 @@ func openChatMessage(context: AccountContext, message: Message, standalone: Bool } playerType = (file.isVoice || file.isInstantVideo) ? .voice : .music } - context.mediaManager.setPlaylist(PeerMessagesMediaPlaylist(postbox: context.account.postbox, network: context.account.network, location: location), type: playerType) + context.sharedContext.mediaManager.setPlaylist((context.account, PeerMessagesMediaPlaylist(postbox: context.account.postbox, network: context.account.network, location: location)), type: playerType) return true case let .gallery(gallery): dismissInput() @@ -319,7 +319,7 @@ func openChatMessage(context: AccountContext, message: Message, standalone: Bool } items.append(ActionSheetButtonItem(title: presentationData.strings.UserInfo_PhoneCall, action: { dismissAction() - account.telegramApplicationContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(contact.phoneNumber).replacingOccurrences(of: " ", with: ""))") + account.telegramApplicationcontext.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(contact.phoneNumber).replacingOccurrences(of: " ", with: ""))") })) controller.setItemGroups([ ActionSheetItemGroup(items: items), diff --git a/TelegramUI/OpenInOptions.swift b/TelegramUI/OpenInOptions.swift index 0bdaa2a504..a7a7973708 100644 --- a/TelegramUI/OpenInOptions.swift +++ b/TelegramUI/OpenInOptions.swift @@ -46,7 +46,7 @@ final class OpenInOption { func availableOpenInOptions(context: AccountContext, item: OpenInItem) -> [OpenInOption] { return allOpenInOptions(context: context, item: item).filter { option in if case let .other(_, _, scheme) = option.application { - return context.applicationBindings.canOpenUrl("\(scheme)://") + return context.sharedContext.applicationBindings.canOpenUrl("\(scheme)://") } else { return true } diff --git a/TelegramUI/OpenResolvedUrl.swift b/TelegramUI/OpenResolvedUrl.swift index 614bdb85ee..85315564f0 100644 --- a/TelegramUI/OpenResolvedUrl.swift +++ b/TelegramUI/OpenResolvedUrl.swift @@ -189,7 +189,7 @@ func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlCon } } if let navigationController = navigationController { - context.applicationBindings.dismissNativeController() + context.sharedContext.applicationBindings.dismissNativeController() (navigationController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet)) } } diff --git a/TelegramUI/OpenUrl.swift b/TelegramUI/OpenUrl.swift index a69d556a55..0298fc4f8b 100644 --- a/TelegramUI/OpenUrl.swift +++ b/TelegramUI/OpenUrl.swift @@ -173,7 +173,7 @@ public enum OpenURLContext { public func openExternalUrl(context: AccountContext, urlContext: OpenURLContext = .generic, url: String, forceExternal: Bool = false, presentationData: PresentationData, navigationController: NavigationController?, dismissInput: @escaping () -> Void) { if forceExternal || url.lowercased().hasPrefix("tel:") || url.lowercased().hasPrefix("calshow:") { - context.applicationBindings.openUrl(url) + context.sharedContext.applicationBindings.openUrl(url) return } @@ -185,7 +185,7 @@ public func openExternalUrl(context: AccountContext, urlContext: OpenURLContext } if let parsedUrlValue = parsedUrlValue, parsedUrlValue.scheme == "mailto" { - context.applicationBindings.openUrl(url) + context.sharedContext.applicationBindings.openUrl(url) return } @@ -199,19 +199,19 @@ public func openExternalUrl(context: AccountContext, urlContext: OpenURLContext if let host = parsedUrl.host?.lowercased() { if host == "itunes.apple.com" { - if context.applicationBindings.canOpenUrl(parsedUrl.absoluteString) { - context.applicationBindings.openUrl(url) + if context.sharedContext.applicationBindings.canOpenUrl(parsedUrl.absoluteString) { + context.sharedContext.applicationBindings.openUrl(url) return } } if host == "twitter.com" || host == "mobile.twitter.com" { - if context.applicationBindings.canOpenUrl("twitter://status") { - context.applicationBindings.openUrl(url) + if context.sharedContext.applicationBindings.canOpenUrl("twitter://status") { + context.sharedContext.applicationBindings.openUrl(url) return } } else if host == "instagram.com" { - if context.applicationBindings.canOpenUrl("instagram://photo") { - context.applicationBindings.openUrl(url) + if context.sharedContext.applicationBindings.canOpenUrl("instagram://photo") { + context.sharedContext.applicationBindings.openUrl(url) return } } @@ -220,7 +220,7 @@ public func openExternalUrl(context: AccountContext, urlContext: OpenURLContext let continueHandling: () -> Void = { let handleRevolvedUrl: (ResolvedUrl) -> Void = { resolved in if case let .externalUrl(value) = resolved { - context.applicationBindings.openUrl(value) + context.sharedContext.applicationBindings.openUrl(value) } else { openResolvedUrl(resolved, context: context, navigationController: navigationController, openPeer: { peerId, navigation in switch navigation { @@ -248,11 +248,11 @@ public func openExternalUrl(context: AccountContext, urlContext: OpenURLContext break } }, present: { c, a in - context.applicationBindings.dismissNativeController() + context.sharedContext.applicationBindings.dismissNativeController() c.presentationArguments = a - context.applicationBindings.getWindowHost()?.present(c, on: .root, blockInteraction: false, completion: {}) + context.sharedContext.applicationBindings.getWindowHost()?.present(c, on: .root, blockInteraction: false, completion: {}) }, dismissInput: { dismissInput() }) @@ -264,7 +264,7 @@ public func openExternalUrl(context: AccountContext, urlContext: OpenURLContext |> deliverOnMainQueue).start(next: handleRevolvedUrl) } - if let scheme = parsedUrl.scheme, (scheme == "tg" || scheme == context.applicationBindings.appSpecificScheme), let query = parsedUrl.query { + if let scheme = parsedUrl.scheme, (scheme == "tg" || scheme == context.sharedContext.applicationBindings.appSpecificScheme), let query = parsedUrl.query { var convertedUrl: String? if parsedUrl.host == "localpeer" { if let components = URLComponents(string: "/?" + query) { @@ -279,7 +279,7 @@ public func openExternalUrl(context: AccountContext, urlContext: OpenURLContext } } if let peerId = peerId, let navigationController = navigationController { - context.applicationBindings.dismissNativeController() + context.sharedContext.applicationBindings.dismissNativeController() navigateToChatController(navigationController: navigationController, context: context, chatLocation: .peer(peerId)) } } @@ -475,10 +475,10 @@ public func openExternalUrl(context: AccountContext, urlContext: OpenURLContext let controller = SecureIdAuthController(context: context, mode: .form(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: botId), scope: scope, publicKey: publicKey, callbackUrl: callbackUrl, opaquePayload: opaquePayload, opaqueNonce: opaqueNonce)) if let navigationController = navigationController { - context.applicationBindings.dismissNativeController() + context.sharedContext.applicationBindings.dismissNativeController() navigationController.view.window?.endEditing(true) - context.applicationBindings.getWindowHost()?.present(controller, on: .root, blockInteraction: false, completion: {}) + context.sharedContext.applicationBindings.getWindowHost()?.present(controller, on: .root, blockInteraction: false, completion: {}) } } return @@ -628,14 +628,14 @@ public func openExternalUrl(context: AccountContext, urlContext: OpenURLContext } window.rootViewController?.present(controller, animated: true) } else { - context.applicationBindings.openUrl(parsedUrl.absoluteString) + context.sharedContext.applicationBindings.openUrl(parsedUrl.absoluteString) } } else { - context.applicationBindings.openUrl(url) + context.sharedContext.applicationBindings.openUrl(url) } } } else { - context.applicationBindings.openUrl(url) + context.sharedContext.applicationBindings.openUrl(url) } } @@ -644,7 +644,7 @@ public func openExternalUrl(context: AccountContext, urlContext: OpenURLContext if let host = parsedUrl.host, nativeHosts.contains(host) { continueHandling() } else { - context.applicationBindings.openUniversalUrl(url, TelegramApplicationOpenUrlCompletion(completion: { success in + context.sharedContext.applicationBindings.openUniversalUrl(url, TelegramApplicationOpenUrlCompletion(completion: { success in if !success { continueHandling() } diff --git a/TelegramUI/OverlayPlayerControllerNode.swift b/TelegramUI/OverlayPlayerControllerNode.swift index 37e640672b..a679c498a6 100644 --- a/TelegramUI/OverlayPlayerControllerNode.swift +++ b/TelegramUI/OverlayPlayerControllerNode.swift @@ -81,7 +81,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec self.contentNode = ASDisplayNode() - self.controlsNode = OverlayPlayerControlsNode(postbox: context.account.postbox, theme: self.presentationData.theme, status: context.mediaManager.musicMediaPlayerState) + self.controlsNode = OverlayPlayerControlsNode(account: context.account, theme: self.presentationData.theme, status: context.sharedContext.mediaManager.musicMediaPlayerState) self.historyBackgroundNode = ASDisplayNode() self.historyBackgroundNode.isLayerBacked = true @@ -148,7 +148,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec self.controlsNode.control = { [weak self] action in if let strongSelf = self { - strongSelf.context.mediaManager.playlistControl(action, type: strongSelf.type) + strongSelf.context.sharedContext.mediaManager.playlistControl(action, type: strongSelf.type) } } diff --git a/TelegramUI/OverlayPlayerControlsNode.swift b/TelegramUI/OverlayPlayerControlsNode.swift index 5597f6d542..fde0121edc 100644 --- a/TelegramUI/OverlayPlayerControlsNode.swift +++ b/TelegramUI/OverlayPlayerControlsNode.swift @@ -100,8 +100,8 @@ final class OverlayPlayerControlsNode: ASDisplayNode { private var validLayout: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat)? - init(postbox: Postbox, theme: PresentationTheme, status: Signal) { - self.postbox = postbox + init(account: Account, theme: PresentationTheme, status: Signal<(Account, SharedMediaPlayerItemPlaybackStateOrLoading)?, NoError>) { + self.postbox = account.postbox self.theme = theme self.backgroundNode = ASImageNode() @@ -180,21 +180,23 @@ final class OverlayPlayerControlsNode: ASDisplayNode { self.addSubnode(self.separatorNode) + let accountId = account.id let delayedStatus = status - |> mapToSignal { value -> Signal in - guard let value = value else { + |> mapToSignal { value -> Signal<(Account, SharedMediaPlayerItemPlaybackStateOrLoading)?, NoError> in + guard let value = value, value.0.id == accountId else { return .single(nil) } - switch value { + switch value.1 { case .state: return .single(value) case .loading: - return .single(value) |> delay(0.1, queue: .mainQueue()) + return .single(value) + |> delay(0.1, queue: .mainQueue()) } } let mappedStatus = combineLatest(delayedStatus, self.scrubberNode.scrubbingTimestamp) |> map { value, scrubbingTimestamp -> MediaPlayerStatus in - if let valueOrLoading = value, case let .state(value) = valueOrLoading { + if let (_, valueOrLoading) = value, case let .state(value) = valueOrLoading { return MediaPlayerStatus(generationTimestamp: value.status.generationTimestamp, duration: value.status.duration, dimensions: value.status.dimensions, timestamp: scrubbingTimestamp ?? value.status.timestamp, baseRate: value.status.baseRate, seekId: value.status.seekId, status: value.status.status) } else { return MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused) @@ -208,7 +210,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { |> deliverOnMainQueue).start(next: { [weak self] value in if let strongSelf = self { var valueItemId: SharedMediaPlaylistItemId? - if let value = value, case let .state(state) = value { + if let (_, value) = value, case let .state(state) = value { valueItemId = state.item.id } if !areSharedMediaPlaylistItemIdsEqual(valueItemId, strongSelf.currentItemId) { @@ -217,7 +219,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode { } strongSelf.shareNode.isHidden = false var displayData: SharedMediaPlaybackDisplayData? - if let valueOrLoading = value, case let .state(value) = valueOrLoading { + if let (_, valueOrLoading) = value, case let .state(value) = valueOrLoading { let isPaused: Bool switch value.status.status { case .playing: @@ -278,12 +280,12 @@ final class OverlayPlayerControlsNode: ASDisplayNode { if strongSelf.displayData != displayData { strongSelf.displayData = displayData - if let valueOrLoading = value, case let .state(value) = valueOrLoading, let source = value.item.playbackData?.source { + if let (_, valueOrLoading) = value, case let .state(value) = valueOrLoading, let source = value.item.playbackData?.source { switch source { case let .telegramFile(fileReference): strongSelf.currentFileReference = fileReference if let size = fileReference.media.size { - strongSelf.scrubberNode.bufferingStatus = postbox.mediaBox.resourceRangesStatus(fileReference.media.resource) + strongSelf.scrubberNode.bufferingStatus = strongSelf.postbox.mediaBox.resourceRangesStatus(fileReference.media.resource) |> map { ranges -> (IndexSet, Int) in return (ranges, size) } diff --git a/TelegramUI/PeerMediaCollectionController.swift b/TelegramUI/PeerMediaCollectionController.swift index d3fc974308..a321caaff3 100644 --- a/TelegramUI/PeerMediaCollectionController.swift +++ b/TelegramUI/PeerMediaCollectionController.swift @@ -218,7 +218,7 @@ public class PeerMediaCollectionController: TelegramController { ActionSheetButtonItem(title: openText, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self { - strongSelf.context.applicationBindings.openUrl(url) + strongSelf.context.sharedContext.applicationBindings.openUrl(url) } }), ActionSheetButtonItem(title: strongSelf.presentationData.strings.ShareMenu_CopyShareLink, color: .accent, action: { [weak actionSheet] in @@ -405,13 +405,13 @@ public class PeerMediaCollectionController: TelegramController { self?.deactivateSearch() }) - let mediaManager = self.context.mediaManager + let mediaManager = self.context.sharedContext.mediaManager self.galleryHiddenMesageAndMediaDisposable.set(mediaManager.galleryHiddenMediaManager.hiddenIds().start(next: { [weak self] ids in if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction { var messageIdAndMedia: [MessageId: [Media]] = [:] for id in ids { - if case let .chat(messageId, media) = id { + if case let .chat(accountId, messageId, media) = id, accountId == strongSelf.context.account.id { messageIdAndMedia[messageId] = [media] } } diff --git a/TelegramUI/PermissionController.swift b/TelegramUI/PermissionController.swift index 2873fb9294..6458a4a264 100644 --- a/TelegramUI/PermissionController.swift +++ b/TelegramUI/PermissionController.swift @@ -81,7 +81,7 @@ public final class PermissionController : ViewController { } private func openAppSettings() { - self.context.applicationBindings.openSettings() + self.context.sharedContext.applicationBindings.openSettings() } public func setState(_ state: PermissionState, animated: Bool) { diff --git a/TelegramUI/PhotoResources.swift b/TelegramUI/PhotoResources.swift index 203d9ac41e..4f2f8b50b9 100644 --- a/TelegramUI/PhotoResources.swift +++ b/TelegramUI/PhotoResources.swift @@ -1705,7 +1705,7 @@ public func chatMessagePhotoInteractiveFetched(context: AccountContext, photoRef return fetchedMediaResource(postbox: context.account.postbox, reference: photoReference.resourceReference(largestRepresentation.resource), statsCategory: .image, reportResultStatus: true) |> mapToSignal { type -> Signal in if case .remote = type, let peerType = storeToDownloadsPeerType { - return storeDownloadedMedia(storeManager: context.mediaManager.downloadedMediaStoreManager, media: photoReference.abstract, peerType: peerType) + return storeDownloadedMedia(storeManager: context.downloadedMediaStoreManager, media: photoReference.abstract, peerType: peerType) |> introduceError(FetchResourceError.self) |> mapToSignal { _ -> Signal in return .complete() diff --git a/TelegramUI/PresentationCallManager.swift b/TelegramUI/PresentationCallManager.swift index db2ac63739..b9d8f6706b 100644 --- a/TelegramUI/PresentationCallManager.swift +++ b/TelegramUI/PresentationCallManager.swift @@ -32,11 +32,10 @@ public enum RequestCallResult { } public final class PresentationCallManager { - private let account: Account + private let tempAccount: Account private let getDeviceAccessData: () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void) private let networkType: Signal private let audioSession: ManagedAudioSession - private let callSessionManager: CallSessionManager private let callKitIntegration: CallKitIntegration? private var currentCall: PresentationCall? @@ -66,22 +65,21 @@ public final class PresentationCallManager { return OngoingCallContext.maxLayer } - public init(account: Account, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), networkType: Signal, audioSession: ManagedAudioSession, callSessionManager: CallSessionManager) { - self.account = account + public init(account: Account, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), networkType: Signal, audioSession: ManagedAudioSession, activeAccounts: Signal<[Account], NoError>) { + self.tempAccount = account self.getDeviceAccessData = getDeviceAccessData self.networkType = networkType self.audioSession = audioSession - self.callSessionManager = callSessionManager - var startCallImpl: ((UUID, String) -> Signal)? + var startCallImpl: ((Account, UUID, String) -> Signal)? var answerCallImpl: ((UUID) -> Void)? var endCallImpl: ((UUID) -> Signal)? var setCallMutedImpl: ((UUID, Bool) -> Void)? var audioSessionActivationChangedImpl: ((Bool) -> Void)? - self.callKitIntegration = CallKitIntegration(startCall: { uuid, handle in + self.callKitIntegration = CallKitIntegration(startCall: { account, uuid, handle in if let startCallImpl = startCallImpl { - return startCallImpl(uuid, handle) + return startCallImpl(account, uuid, handle) } else { return .single(false) } @@ -99,7 +97,7 @@ public final class PresentationCallManager { audioSessionActivationChangedImpl?(value) }) - let postbox = account.postbox + let postbox = self.tempAccount.postbox let enableCallKit = postbox.preferencesView(keys: [ApplicationSpecificPreferencesKeys.voiceCallSettings]) |> map { preferences -> Bool in let settings = preferences.values[ApplicationSpecificPreferencesKeys.voiceCallSettings] as? VoiceCallSettings ?? .defaultSettings @@ -114,26 +112,39 @@ public final class PresentationCallManager { } |> runOn(Queue.mainQueue()) - self.ringingStatesDisposable = (combineLatest(callSessionManager.ringingStates(), enableCallKit, enabledMicrophoneAccess) - |> mapToSignal { ringingStates, enableCallKit, enabledMicrophoneAccess -> Signal<([(Peer, CallSessionRingingState, Bool)], Bool), NoError> in - if ringingStates.isEmpty { + let queue = Queue() + let ringingStatesByAccount: Signal<[(Account, CallSessionRingingState)], NoError> = Signal { subscriber in + let disposable = MetaDisposable() + queue.async { + var currentAccounts: [(Account, [CallSessionRingingState])] = [] + disposable.set((activeAccounts + |> deliverOn(queue)).start(next: { accounts in + + })) + } + return disposable + } + + self.ringingStatesDisposable = (combineLatest(ringingStatesByAccount, enableCallKit, enabledMicrophoneAccess) + |> mapToSignal { ringingStatesByAccount, enableCallKit, enabledMicrophoneAccess -> Signal<([(Account, Peer, CallSessionRingingState, Bool)], Bool), NoError> in + if ringingStatesByAccount.isEmpty { return .single(([], enableCallKit && enabledMicrophoneAccess)) } else { - return postbox.transaction { transaction -> ([(Peer, CallSessionRingingState, Bool)], Bool) in - var result: [(Peer, CallSessionRingingState, Bool)] = [] - for state in ringingStates { + return postbox.transaction { transaction -> ([(Account, Peer, CallSessionRingingState, Bool)], Bool) in + var result: [(Account, Peer, CallSessionRingingState, Bool)] = [] + for (account, state) in ringingStatesByAccount { if let peer = transaction.getPeer(state.peerId) { - result.append((peer, state, transaction.isPeerContact(peerId: state.peerId))) + result.append((account, peer, state, transaction.isPeerContact(peerId: state.peerId))) } } return (result, enableCallKit && enabledMicrophoneAccess) } } } - |> mapToSignal { states, enableCallKit -> Signal<([(Peer, CallSessionRingingState, Bool)], NetworkType, Bool), NoError> in + |> mapToSignal { states, enableCallKit -> Signal<([(Account, Peer, CallSessionRingingState, Bool)], NetworkType, Bool), NoError> in return networkType |> take(1) - |> map { currentNetworkType -> ([(Peer, CallSessionRingingState, Bool)], NetworkType, Bool) in + |> map { currentNetworkType -> ([(Account, Peer, CallSessionRingingState, Bool)], NetworkType, Bool) in return (states, currentNetworkType, enableCallKit) } } @@ -141,9 +152,9 @@ public final class PresentationCallManager { self?.ringingStatesUpdated(ringingStates, currentNetworkType: currentNetworkType, enableCallKit: enableCallKit) }) - startCallImpl = { [weak self] uuid, handle in + startCallImpl = { [weak self] account, uuid, handle in if let strongSelf = self, let userId = Int32(handle) { - return strongSelf.startCall(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), internalId: uuid) + return strongSelf.startCall(account: account, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), internalId: uuid) |> take(1) |> map { result -> Bool in return result @@ -216,7 +227,7 @@ public final class PresentationCallManager { case .never: settings = .disableEveryone(enableFor: Set()) } - _ = updateSelectiveAccountPrivacySettings(account: account, type: .voiceCallsP2P, settings: settings).start() + _ = updateSelectiveAccountPrivacySettings(account: strongSelf.tempAccount, type: .voiceCallsP2P, settings: settings).start() } } }) @@ -230,10 +241,10 @@ public final class PresentationCallManager { self.callSettingsDisposable?.dispose() } - private func ringingStatesUpdated(_ ringingStates: [(Peer, CallSessionRingingState, Bool)], currentNetworkType: NetworkType, enableCallKit: Bool) { + private func ringingStatesUpdated(_ ringingStates: [(Account, Peer, CallSessionRingingState, Bool)], currentNetworkType: NetworkType, enableCallKit: Bool) { if let firstState = ringingStates.first { if self.currentCall == nil { - let call = PresentationCall(account: self.account, audioSession: self.audioSession, callSessionManager: self.callSessionManager, callKitIntegration: enableCallKit ? callKitIntegrationIfEnabled(self.callKitIntegration, settings: self.callSettings?.0) : nil, serializedData: self.callSettings?.1.serializedData, dataSaving: self.callSettings?.0.dataSaving ?? .never, derivedState: self.callSettings?.2 ?? VoipDerivedState.default, getDeviceAccessData: self.getDeviceAccessData, internalId: firstState.1.id, peerId: firstState.1.peerId, isOutgoing: false, peer: firstState.0, proxyServer: self.proxyServer, currentNetworkType: currentNetworkType, updatedNetworkType: self.networkType) + let call = PresentationCall(account: firstState.0, audioSession: self.audioSession, callSessionManager: firstState.0.callSessionManager, callKitIntegration: enableCallKit ? callKitIntegrationIfEnabled(self.callKitIntegration, settings: self.callSettings?.0) : nil, serializedData: self.callSettings?.1.serializedData, dataSaving: self.callSettings?.0.dataSaving ?? .never, derivedState: self.callSettings?.2 ?? VoipDerivedState.default, getDeviceAccessData: self.getDeviceAccessData, internalId: firstState.2.id, peerId: firstState.2.peerId, isOutgoing: false, peer: firstState.1, proxyServer: self.proxyServer, currentNetworkType: currentNetworkType, updatedNetworkType: self.networkType) self.currentCall = call self.currentCallPromise.set(.single(call)) self.hasActiveCallsPromise.set(true) @@ -248,16 +259,16 @@ public final class PresentationCallManager { } })) } else { - for (_, state, _) in ringingStates { + for (account, _, state, _) in ringingStates { if state.id != self.currentCall?.internalId { - self.callSessionManager.drop(internalId: state.id, reason: .missed) + account.callSessionManager.drop(internalId: state.id, reason: .missed) } } } } } - public func requestCall(peerId: PeerId, endCurrentIfAny: Bool) -> RequestCallResult { + public func requestCall(account: Account, peerId: PeerId, endCurrentIfAny: Bool) -> RequestCallResult { if let call = self.currentCall, !endCurrentIfAny { return .alreadyInProgress(call.peerId) } @@ -280,7 +291,7 @@ public final class PresentationCallManager { return EmptyDisposable } |> runOn(Queue.mainQueue()) - let postbox = strongSelf.account.postbox + let postbox = account.postbox strongSelf.startCallDisposable.set((accessEnabledSignal |> mapToSignal { accessEnabled -> Signal in if !accessEnabled { @@ -294,7 +305,7 @@ public final class PresentationCallManager { guard let strongSelf = self, let peer = peer else { return } - strongSelf.callKitIntegration?.startCall(peerId: peerId, displayTitle: peer.displayTitle) + strongSelf.callKitIntegration?.startCall(account: account, peerId: peerId, displayTitle: peer.displayTitle) })) } if let currentCall = self.currentCall { @@ -311,7 +322,7 @@ public final class PresentationCallManager { guard let strongSelf = self else { return } - let _ = strongSelf.startCall(peerId: peerId).start() + let _ = strongSelf.startCall(account: account, peerId: peerId).start() } if let currentCall = self.currentCall { self.startCallDisposable.set((currentCall.hangUp() @@ -325,7 +336,7 @@ public final class PresentationCallManager { return .requested } - private func startCall(peerId: PeerId, internalId: CallSessionInternalId = CallSessionInternalId()) -> Signal { + private func startCall(account: Account, peerId: PeerId, internalId: CallSessionInternalId = CallSessionInternalId()) -> Signal { let (presentationData, present, openSettings) = self.getDeviceAccessData() let accessEnabledSignal: Signal = Signal { subscriber in @@ -341,15 +352,13 @@ public final class PresentationCallManager { } |> runOn(Queue.mainQueue()) - let postbox = self.account.postbox - let callSessionManager = self.callSessionManager let networkType = self.networkType return accessEnabledSignal |> mapToSignal { [weak self] accessEnabled -> Signal in if !accessEnabled { return .single(false) } - return (combineLatest(queue: .mainQueue(), callSessionManager.request(peerId: peerId, internalId: internalId), networkType |> take(1), postbox.peerView(id: peerId) |> take(1) |> map({ peerView -> Bool in + return (combineLatest(queue: .mainQueue(), account.callSessionManager.request(peerId: peerId, internalId: internalId), networkType |> take(1), account.postbox.peerView(id: peerId) |> take(1) |> map({ peerView -> Bool in return peerView.peerIsContact }) |> take(1)) |> deliverOnMainQueue @@ -358,8 +367,8 @@ public final class PresentationCallManager { if let currentCall = strongSelf.currentCall { currentCall.rejectBusy() } - - let call = PresentationCall(account: strongSelf.account, audioSession: strongSelf.audioSession, callSessionManager: strongSelf.callSessionManager, callKitIntegration: callKitIntegrationIfEnabled(strongSelf.callKitIntegration, settings: strongSelf.callSettings?.0), serializedData: strongSelf.callSettings?.1.serializedData, dataSaving: strongSelf.callSettings?.0.dataSaving ?? .never, derivedState: strongSelf.callSettings?.2 ?? VoipDerivedState.default, getDeviceAccessData: strongSelf.getDeviceAccessData, internalId: internalId, peerId: peerId, isOutgoing: true, peer: nil, proxyServer: strongSelf.proxyServer, currentNetworkType: currentNetworkType, updatedNetworkType: strongSelf.networkType) + + let call = PresentationCall(account: account, audioSession: strongSelf.audioSession, callSessionManager: account.callSessionManager, callKitIntegration: callKitIntegrationIfEnabled(strongSelf.callKitIntegration, settings: strongSelf.callSettings?.0), serializedData: strongSelf.callSettings?.1.serializedData, dataSaving: strongSelf.callSettings?.0.dataSaving ?? .never, derivedState: strongSelf.callSettings?.2 ?? VoipDerivedState.default, getDeviceAccessData: strongSelf.getDeviceAccessData, internalId: internalId, peerId: peerId, isOutgoing: true, peer: nil, proxyServer: strongSelf.proxyServer, currentNetworkType: currentNetworkType, updatedNetworkType: strongSelf.networkType) strongSelf.currentCall = call strongSelf.currentCallPromise.set(.single(call)) strongSelf.hasActiveCallsPromise.set(true) diff --git a/TelegramUI/RecentSessionsController.swift b/TelegramUI/RecentSessionsController.swift index b41c57151e..fd892381fe 100644 --- a/TelegramUI/RecentSessionsController.swift +++ b/TelegramUI/RecentSessionsController.swift @@ -477,6 +477,7 @@ public func recentSessionsController(context: AccountContext) -> ViewController updateState { return $0.withUpdatedRemovingSessionId(nil) } + context.sharedContext.updateNotificationTokensRegistration() })) }) ]), @@ -519,6 +520,7 @@ public func recentSessionsController(context: AccountContext) -> ViewController updateState { return $0.withUpdatedTerminatingOtherSessions(false) } + context.sharedContext.updateNotificationTokensRegistration() })) }) ]), diff --git a/TelegramUI/SaveToCameraRoll.swift b/TelegramUI/SaveToCameraRoll.swift index fe55dfee0c..24e54439fd 100644 --- a/TelegramUI/SaveToCameraRoll.swift +++ b/TelegramUI/SaveToCameraRoll.swift @@ -86,7 +86,7 @@ func saveToCameraRoll(context: AccountContext, postbox: Postbox, mediaReference: return Signal { subscriber in DeviceAccess.authorizeAccess(to: .mediaLibrary(.save), presentationData: context.currentPresentationData.with { $0 }, present: { c, a in context.presentGlobalController(c, a) - }, openSettings: context.applicationBindings.openSettings, { authorized in + }, openSettings: context.sharedContext.applicationBindings.openSettings, { authorized in if !authorized { subscriber.putCompletion() return diff --git a/TelegramUI/SecretMediaPreviewController.swift b/TelegramUI/SecretMediaPreviewController.swift index 7016820152..a4eeb5fdce 100644 --- a/TelegramUI/SecretMediaPreviewController.swift +++ b/TelegramUI/SecretMediaPreviewController.swift @@ -162,11 +162,10 @@ public final class SecretMediaPreviewController: ViewController { } })) - let mediaManager = context.mediaManager - self.hiddenMediaManagerIndex = mediaManager.galleryHiddenMediaManager.addSource(self._hiddenMedia.get() + self.hiddenMediaManagerIndex = self.context.sharedContext.mediaManager.galleryHiddenMediaManager.addSource(self._hiddenMedia.get() |> map { messageIdAndMedia in if let (messageId, media) = messageIdAndMedia { - return .chat(messageId, media) + return .chat(context.account.id, messageId, media) } else { return nil } @@ -192,8 +191,7 @@ public final class SecretMediaPreviewController: ViewController { self.disposable.dispose() self.markMessageAsConsumedDisposable.dispose() if let hiddenMediaManagerIndex = self.hiddenMediaManagerIndex { - let mediaManager = self.context.mediaManager - mediaManager.galleryHiddenMediaManager.removeSource(hiddenMediaManagerIndex) + self.context.sharedContext.mediaManager.galleryHiddenMediaManager.removeSource(hiddenMediaManagerIndex) } self.screenCaptureEventsDisposable?.dispose() if let tempFile = self.tempFile { diff --git a/TelegramUI/SecureIdAuthController.swift b/TelegramUI/SecureIdAuthController.swift index a9f8a344c7..5dae5f622a 100644 --- a/TelegramUI/SecureIdAuthController.swift +++ b/TelegramUI/SecureIdAuthController.swift @@ -184,7 +184,7 @@ final class SecureIdAuthController: ViewController { if appUpdateRequired { let errorText = strongSelf.presentationData.strings.Passport_UpdateRequiredError strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Application_Update, action: { - context.applicationBindings.openAppStorePage() + context.sharedContext.applicationBindings.openAppStorePage() })]), in: .window(.root)) } else if let callbackUrl = callbackUrl, let peerId = peerId { let errorText = strongSelf.presentationData.strings.Login_UnknownError diff --git a/TelegramUI/SettingsController.swift b/TelegramUI/SettingsController.swift index a1bebd45ab..d3b4303e91 100644 --- a/TelegramUI/SettingsController.swift +++ b/TelegramUI/SettingsController.swift @@ -51,6 +51,8 @@ private struct SettingsItemArguments { let displayCopyContextMenu: () -> Void let switchToAccount: (AccountRecordId) -> Void let addAccount: () -> Void + let setAccountIdWithRevealedOptions: (AccountRecordId?, AccountRecordId?) -> Void + let removeAccount: (AccountRecordId) -> Void } private enum SettingsSection: Int32 { @@ -68,7 +70,7 @@ private enum SettingsEntry: ItemListNodeEntry { case setProfilePhoto(PresentationTheme, String) case setUsername(PresentationTheme, String) - case account(Int, Account, PresentationTheme, PresentationStrings, Peer, Int32) + case account(Int, Account, PresentationTheme, PresentationStrings, Peer, Int32, Bool) case addAccount(PresentationTheme, String) case proxy(PresentationTheme, UIImage?, String, String) @@ -188,8 +190,8 @@ private enum SettingsEntry: ItemListNodeEntry { } else { return false } - case let .account(lhsIndex, lhsAccount, lhsTheme, lhsStrings, lhsPeer, lhsBadgeCount): - if case let .account(rhsIndex, rhsAccount, rhsTheme, rhsStrings, rhsPeer, rhsBadgeCount) = rhs, lhsIndex == rhsIndex, lhsAccount === rhsAccount, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsPeer.isEqual(rhsPeer), lhsBadgeCount == rhsBadgeCount { + case let .account(lhsIndex, lhsAccount, lhsTheme, lhsStrings, lhsPeer, lhsBadgeCount, lhsRevealed): + if case let .account(rhsIndex, rhsAccount, rhsTheme, rhsStrings, rhsPeer, rhsBadgeCount, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsAccount === rhsAccount, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsPeer.isEqual(rhsPeer), lhsBadgeCount == rhsBadgeCount, lhsRevealed == rhsRevealed { return true } else { return false @@ -316,11 +318,21 @@ private enum SettingsEntry: ItemListNodeEntry { return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: { arguments.openUsername() }) - case let .account(_, account, theme, strings, peer, badgeCount): - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: PresentationDateTimeFormat(timeFormat: .regular, dateFormat: .dayFirst, dateSeparator: "."), nameDisplayOrder: .firstLast, account: account, peer: peer, aliasHandling: .standard, presence: nil, text: .none, label: .badge("\(badgeCount)"), editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, sectionId: self.section, action: { + case let .account(_, account, theme, strings, peer, badgeCount, revealed): + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: PresentationDateTimeFormat(timeFormat: .regular, dateFormat: .dayFirst, dateSeparator: "."), nameDisplayOrder: .firstLast, account: account, peer: peer, aliasHandling: .standard, presence: nil, text: .none, label: .badge("\(badgeCount)"), editing: ItemListPeerItemEditing(editable: true, editing: false, revealed: revealed), revealOptions: nil, switchValue: nil, enabled: true, sectionId: self.section, action: { arguments.switchToAccount(account.id) }, setPeerIdWithRevealedOptions: { lhs, rhs in + var lhsAccountId: AccountRecordId? + if lhs == peer.id { + lhsAccountId = account.id + } + var rhsAccountId: AccountRecordId? + if rhs == peer.id { + rhsAccountId = account.id + } + arguments.setAccountIdWithRevealedOptions(lhsAccountId, rhsAccountId) }, removePeer: { _ in + arguments.removeAccount(account.id) }) case let .addAccount(theme, text): return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.plusIconImage(theme), title: text, alwaysPlain: false, sectionId: self.section, editing: false, action: { @@ -383,22 +395,8 @@ private enum SettingsEntry: ItemListNodeEntry { } private struct SettingsState: Equatable { - let updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar? - - init(updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar? = nil) { - self.updatingAvatar = updatingAvatar - } - - func withUpdatedUpdatingAvatar(_ updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar?) -> SettingsState { - return SettingsState(updatingAvatar: updatingAvatar) - } - - static func ==(lhs: SettingsState, rhs: SettingsState) -> Bool { - if lhs.updatingAvatar != rhs.updatingAvatar { - return false - } - return true - } + var updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar? + var accountIdWithRevealedOptions: AccountRecordId? } private func settingsEntries(account: Account, presentationData: PresentationData, state: SettingsState, view: PeerView, proxySettings: ProxySettings, notifyExceptions: NotificationExceptionsList?, notificationsAuthorizationStatus: AccessType, notificationsWarningSuppressed: Bool, unreadTrendingStickerPacks: Int, archivedPacks: [ArchivedStickerPackItem]?, hasPassport: Bool, hasWatchApp: Bool, accountsAndPeers: [(Account, Peer, Int32)]) -> [SettingsEntry] { @@ -417,7 +415,7 @@ private func settingsEntries(account: Account, presentationData: PresentationDat if !accountsAndPeers.isEmpty { var index = 0 for (peerAccount, peer, badgeCount) in accountsAndPeers { - entries.append(.account(index, peerAccount, presentationData.theme, presentationData.strings, peer, badgeCount)) + entries.append(.account(index, peerAccount, presentationData.theme, presentationData.strings, peer, badgeCount, state.accountIdWithRevealedOptions == peerAccount.id)) index += 1 } entries.append(.addAccount(presentationData.theme, presentationData.strings.Settings_AddAccount)) @@ -538,63 +536,37 @@ public func settingsController(context: AccountContext, accountManager: AccountM let networkArguments = context.account.networkArguments let auxiliaryMethods = context.account.auxiliaryMethods - let rootPath = rootPathForBasePath(context.applicationBindings.containerPath) + let rootPath = rootPathForBasePath(context.sharedContext.applicationBindings.containerPath) - let accountsAndPeers: Signal<[(Account, Peer, Int32)], NoError> = accountManager.accountRecords() - |> map { view -> [AccountRecordId] in - return view.records.compactMap { record -> AccountRecordId? in - if record.attributes.contains(where: { $0 is LoggedOutAccountAttribute }) { - return nil + let accountsAndPeers: Signal<[(Account, Peer, Int32)], NoError> = context.sharedContext.activeAccounts + |> mapToSignal { primary, activeAccounts, _ -> Signal<[(Account, Peer, Int32)], NoError> in + var accounts: [Signal<(Account, Peer, Int32)?, NoError>] = [] + func accountWithPeer(_ account: Account) -> Signal<(Account, Peer, Int32)?, NoError> { + return combineLatest(account.postbox.peerView(id: account.peerId), renderedTotalUnreadCount(postbox: account.postbox)) + |> map { view, totalUnreadCount -> (Peer?, Int32) in + return (view.peers[view.peerId], totalUnreadCount.0) } - return record.id - } - } - |> distinctUntilChanged - |> mapToSignal { recordIds -> Signal<[(Account, Peer, Int32)], NoError> in - return contextValue.get() - |> mapToSignal { currentContext -> Signal<[(Account, Peer, Int32)], NoError> in - var accounts: [Signal<(Account, Peer, Int32)?, NoError>] = [] - func accountWithPeer(_ account: Signal) -> Signal<(Account, Peer, Int32)?, NoError> { - return account - |> mapToSignal { account -> Signal<(Account, Peer, Int32)?, NoError> in - guard let account = account else { - return .single(nil) - } - return combineLatest(account.postbox.peerView(id: account.peerId), renderedTotalUnreadCount(postbox: account.postbox)) - |> map { view, totalUnreadCount -> (Peer?, Int32) in - return (view.peers[view.peerId], totalUnreadCount.0) - } - |> distinctUntilChanged { lhs, rhs in - return arePeersEqual(lhs.0, rhs.0) && lhs.1 == rhs.1 - } - |> map { peer, totalUnreadCount -> (Account, Peer, Int32)? in - if let peer = peer { - return (account, peer, totalUnreadCount) - } else { - return nil - } - } - } + |> distinctUntilChanged { lhs, rhs in + return arePeersEqual(lhs.0, rhs.0) && lhs.1 == rhs.1 } - for id in recordIds { - if id == currentContext.account.id { - continue + |> map { peer, totalUnreadCount -> (Account, Peer, Int32)? in + if let peer = peer { + return (account, peer, totalUnreadCount) } else { - accounts.append(accountWithPeer(accountWithId(networkArguments: networkArguments, id: id, supplementary: true, rootPath: rootPath, beginWithTestingEnvironment: false, auxiliaryMethods: auxiliaryMethods) - |> map { result -> Account? in - if case let .authorized(account) = result { - return account - } else { - return nil - } - })) + return nil } } - return combineLatest(accounts) - |> map { accounts -> [(Account, Peer, Int32)] in - return accounts.compactMap({ $0 }) + } + for (_, account) in activeAccounts { + if account.id != primary?.id { + accounts.append(accountWithPeer(account)) } } + + return combineLatest(accounts) + |> map { accounts -> [(Account, Peer, Int32)] in + return accounts.compactMap({ $0 }) + } } let openFaq: (Promise) -> Void = { resolvedUrl in @@ -835,10 +807,39 @@ public func settingsController(context: AccountContext, accountManager: AccountM |> take(1)).start(next: { context in let isTestingEnvironment = context.account.testingEnvironment let _ = accountManager.transaction({ transaction -> Void in - let id = transaction.createRecord([AccountEnvironmentAttribute(environment: isTestingEnvironment ? .test : .production)]) - transaction.setCurrentId(id) + let _ = transaction.createAuth([AccountEnvironmentAttribute(environment: isTestingEnvironment ? .test : .production)]) }).start() }) + }, setAccountIdWithRevealedOptions: { accountId, fromAccountId in + updateState { state in + var state = state + if (accountId == nil && fromAccountId == state.accountIdWithRevealedOptions) || (accountId != nil && fromAccountId == nil) { + state.accountIdWithRevealedOptions = accountId + } + return state + } + }, removeAccount: { id in + let _ = (contextValue.get() + |> deliverOnMainQueue + |> take(1)).start(next: { context in + let presentationData = context.currentPresentationData.with { $0 } + let controller = ActionSheetController(presentationTheme: presentationData.theme) + let dismissAction: () -> Void = { [weak controller] in + controller?.dismissAnimated() + } + + var items: [ActionSheetItem] = [] + items.append(ActionSheetTextItem(title: presentationData.strings.Settings_LogoutConfirmationText.trimmingCharacters(in: .whitespacesAndNewlines))) + items.append(ActionSheetButtonItem(title: presentationData.strings.Settings_Logout, color: .destructive, action: { + dismissAction() + let _ = logoutFromAccount(id: id, accountManager: context.sharedContext.accountManager).start() + })) + controller.setItemGroups([ + ActionSheetItemGroup(items: items), + ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) + ]) + presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + }) }) changeProfilePhotoImpl = { @@ -873,16 +874,20 @@ public func settingsController(context: AccountContext, accountManager: AccountM let resource = LocalFileMediaResource(fileId: arc4random64()) context.account.postbox.mediaBox.storeResourceData(resource.id, data: data) let representation = TelegramMediaImageRepresentation(dimensions: CGSize(width: 640.0, height: 640.0), resource: resource) - updateState { - $0.withUpdatedUpdatingAvatar(.image(representation, true)) + updateState { state in + var state = state + state.updatingAvatar = .image(representation, true) + return state } updateAvatarDisposable.set((updateAccountPhoto(account: context.account, resource: resource, mapResourceToAvatarSizes: { resource, representations in return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations) }) |> deliverOnMainQueue).start(next: { result in switch result { case .complete: - updateState { - $0.withUpdatedUpdatingAvatar(nil) + updateState { state in + var state = state + state.updatingAvatar = nil + return state } case .progress: break @@ -907,20 +912,24 @@ public func settingsController(context: AccountContext, accountManager: AccountM } mixin.didFinishWithDelete = { let _ = currentAvatarMixin.swap(nil) - updateState { + updateState { state in + var state = state if let profileImage = peer?.smallProfileImage { - return $0.withUpdatedUpdatingAvatar(.image(profileImage, false)) + state.updatingAvatar = .image(profileImage, false) } else { - return $0.withUpdatedUpdatingAvatar(.none) + state.updatingAvatar = .none } + return state } updateAvatarDisposable.set((updateAccountPhoto(account: context.account, resource: nil, mapResourceToAvatarSizes: { resource, representations in return mapResourceToAvatarSizes(postbox: context.account.postbox, resource: resource, representations: representations) }) |> deliverOnMainQueue).start(next: { result in switch result { case .complete: - updateState { - $0.withUpdatedUpdatingAvatar(nil) + updateState { state in + var state = state + state.updatingAvatar = nil + return state } case .progress: break @@ -1180,7 +1189,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM }) } switchToAccountImpl = { [weak controller] id in - AccountStore.switchToAccount(id: id, fromSettingsController: controller) + context.sharedContext.switchToAccount(id: id, fromSettingsController: controller) } controller.didAppear = { _ in updatePassport() diff --git a/TelegramUI/SharedAccountContext.swift b/TelegramUI/SharedAccountContext.swift new file mode 100644 index 0000000000..24d342d240 --- /dev/null +++ b/TelegramUI/SharedAccountContext.swift @@ -0,0 +1,240 @@ +import Foundation +import Postbox +import TelegramCore +import SwiftSignalKit +import Display + +public final class SharedAccountContext { + public let applicationBindings: TelegramApplicationBindings + public let accountManager: AccountManager + + private let apsNotificationToken: Signal + private let voipNotificationToken: Signal + + private var activeAccountsValue: (primary: Account?, accounts: [AccountRecordId: Account], currentAuth: UnauthorizedAccount?)? + private let activeAccountsPromise = Promise<(primary: Account?, accounts: [AccountRecordId: Account], currentAuth: UnauthorizedAccount?)>() + public var activeAccounts: Signal<(primary: Account?, accounts: [AccountRecordId: Account], currentAuth: UnauthorizedAccount?), NoError> { + return self.activeAccountsPromise.get() + } + + private var activeUnauthorizedAccountValue: UnauthorizedAccount? + private let activeUnauthorizedAccountPromise = Promise() + public var activeUnauthorizedAccount: Signal { + return self.activeUnauthorizedAccountPromise.get() + } + + private let registeredNotificationTokensDisposable = MetaDisposable() + + public let mediaManager: MediaManager + public let contactDataManager: DeviceContactDataManager? + let locationManager: DeviceLocationManager? + + var switchingSettingsController: (SettingsController & ViewController)? + + public init(accountManager: AccountManager, applicationBindings: TelegramApplicationBindings, networkArguments: NetworkInitializationArguments, rootPath: String, apsNotificationToken: Signal, voipNotificationToken: Signal) { + assert(Queue.mainQueue().isCurrent()) + self.applicationBindings = applicationBindings + self.accountManager = accountManager + + self.apsNotificationToken = apsNotificationToken + self.voipNotificationToken = voipNotificationToken + + self.mediaManager = MediaManager(inForeground: applicationBindings.applicationInForeground) + + if applicationBindings.isMainApp { + self.locationManager = DeviceLocationManager(queue: Queue.mainQueue()) + self.contactDataManager = DeviceContactDataManager() + } else { + self.locationManager = nil + self.contactDataManager = nil + } + + let differenceDisposable = MetaDisposable() + let _ = (accountManager.accountRecords() + |> map { view -> (AccountRecordId?, [AccountRecordId: Bool], (AccountRecordId, Bool)?) in + var result: [AccountRecordId: Bool] = [:] + for record in view.records { + let isLoggedOut = record.attributes.contains(where: { attribute in + return attribute is LoggedOutAccountAttribute + }) + if isLoggedOut { + continue + } + let isTestingEnvironment = record.attributes.contains(where: { attribute in + if let attribute = attribute as? AccountEnvironmentAttribute, case .test = attribute.environment { + return true + } else { + return false + } + }) + result[record.id] = isTestingEnvironment + } + let authRecord: (AccountRecordId, Bool)? = view.currentAuthAccount.flatMap({ authAccount in + let isTestingEnvironment = authAccount.attributes.contains(where: { attribute in + if let attribute = attribute as? AccountEnvironmentAttribute, case .test = attribute.environment { + return true + } else { + return false + } + }) + return (authAccount.id, isTestingEnvironment) + }) + return (view.currentRecord?.id, result, authRecord) + } + |> distinctUntilChanged(isEqual: { lhs, rhs in + if lhs.0 != rhs.0 { + return false + } + if lhs.1 != rhs.1 { + return false + } + if lhs.2?.0 != rhs.2?.0 { + return false + } + if lhs.2?.1 != rhs.2?.1 { + return false + } + return true + }) + |> deliverOnMainQueue).start(next: { primaryId, records, authRecord in + var addedSignals: [Signal] = [] + var addedAuthSignal: Signal = .single(nil) + for (id, isTestingEnvironment) in records { + if self.activeAccountsValue?.accounts[id] == nil { + addedSignals.append(accountWithId(networkArguments: networkArguments, id: id, supplementary: false, rootPath: rootPath, beginWithTestingEnvironment: isTestingEnvironment, auxiliaryMethods: telegramAccountAuxiliaryMethods) + |> map { result -> Account? in + switch result { + case let .authorized(account): + return account + default: + return nil + } + }) + } + } + if let authRecord = authRecord, authRecord.0 != self.activeAccountsValue?.currentAuth?.id { + addedAuthSignal = accountWithId(networkArguments: networkArguments, id: authRecord.0, supplementary: false, rootPath: rootPath, beginWithTestingEnvironment: authRecord.1, auxiliaryMethods: telegramAccountAuxiliaryMethods) + |> map { result -> UnauthorizedAccount? in + switch result { + case let .unauthorized(account): + return account + default: + return nil + } + } + } + differenceDisposable.set((combineLatest(combineLatest(addedSignals), addedAuthSignal) + |> deliverOnMainQueue).start(next: { accounts, authAccount in + var hadUpdates = false + if self.activeAccountsValue == nil { + self.activeAccountsValue = (nil, [:], nil) + hadUpdates = true + } + for account in accounts { + if let account = account { + self.activeAccountsValue!.accounts[account.id] = account + hadUpdates = true + } + } + var removedIds: [AccountRecordId] = [] + for id in self.activeAccountsValue!.accounts.keys { + if records[id] == nil { + removedIds.append(id) + } + } + for id in removedIds { + hadUpdates = true + self.activeAccountsValue!.accounts.removeValue(forKey: id) + } + var primary: Account? + if let primaryId = primaryId { + primary = self.activeAccountsValue!.accounts[primaryId] + } else if !self.activeAccountsValue!.accounts.isEmpty { + primary = self.activeAccountsValue!.accounts.sorted(by: { lhs, rhs in lhs.key < rhs.key }).first?.1 + } + if primary !== self.activeAccountsValue!.primary { + hadUpdates = true + self.activeAccountsValue!.primary?.postbox.clearCaches() + self.activeAccountsValue!.primary = primary + } + if self.activeAccountsValue!.currentAuth?.id != authRecord?.0 { + hadUpdates = true + self.activeAccountsValue!.currentAuth?.postbox.clearCaches() + self.activeAccountsValue!.currentAuth = nil + } + if let authAccount = authAccount { + hadUpdates = true + self.activeAccountsValue!.currentAuth = authAccount + } + if hadUpdates { + self.activeAccountsPromise.set(.single(self.activeAccountsValue!)) + } + + if self.activeAccountsValue!.primary == nil && self.activeAccountsValue!.currentAuth == nil { + self.beginNewAuth(testingEnvironment: false) + } + })) + }) + + let _ = managedCleanupAccounts(networkArguments: networkArguments, accountManager: self.accountManager, rootPath: rootPath, auxiliaryMethods: telegramAccountAuxiliaryMethods).start() + + self.updateNotificationTokensRegistration() + } + + deinit { + assertionFailure("SharedAccountContext is not supposed to be deallocated") + self.registeredNotificationTokensDisposable.dispose() + } + + public func updateNotificationTokensRegistration() { + let sandbox: Bool + #if DEBUG + sandbox = true + #else + sandbox = false + #endif + + self.registeredNotificationTokensDisposable.set((self.activeAccounts + |> mapToSignal { _, activeAccounts, _ -> Signal in + var applied: [Signal] = [] + let activeUserIds = activeAccounts.values.map({ $0.peerId.id }) + for (_, account) in activeAccounts { + let appliedAps = self.apsNotificationToken + |> distinctUntilChanged(isEqual: { $0 == $1 }) + |> mapToSignal { token -> Signal in + guard let token = token else { + return .complete() + } + return registerNotificationToken(account: account, token: token, type: .aps, sandbox: sandbox, otherAccountUserIds: activeUserIds.filter({ $0 != account.peerId.id })) + } + let appliedVoip = self.voipNotificationToken + |> distinctUntilChanged(isEqual: { $0 == $1 }) + |> mapToSignal { token -> Signal in + guard let token = token else { + return .complete() + } + return registerNotificationToken(account: account, token: token, type: .voip, sandbox: sandbox, otherAccountUserIds: activeUserIds.filter({ $0 != account.peerId.id })) + } + + applied.append(appliedAps) + applied.append(appliedVoip) + } + return combineLatest(applied) + |> ignoreValues + }).start()) + } + + public func beginNewAuth(testingEnvironment: Bool) { + let _ = self.accountManager.transaction({ transaction -> Void in + let _ = transaction.createAuth([AccountEnvironmentAttribute(environment: testingEnvironment ? .test : .production)]) + }).start() + } + + func switchToAccount(id: AccountRecordId, fromSettingsController settingsController: (SettingsController & ViewController)? = nil) { + assert(Queue.mainQueue().isCurrent()) + self.switchingSettingsController = settingsController + let _ = self.accountManager.transaction({ transaction in + transaction.setCurrentId(id) + }).start() + } +} diff --git a/TelegramUI/SharedMediaPlayer.swift b/TelegramUI/SharedMediaPlayer.swift index 0331f313ee..0c27fcb247 100644 --- a/TelegramUI/SharedMediaPlayer.swift +++ b/TelegramUI/SharedMediaPlayer.swift @@ -369,7 +369,7 @@ private enum SharedMediaPlaybackItem: Equatable { final class SharedMediaPlayer { private weak var mediaManager: MediaManager? - private let postbox: Postbox + let account: Account private let audioSession: ManagedAudioSession private let overlayMediaManager: OverlayMediaManager private let playerIndex: Int32 @@ -413,9 +413,9 @@ final class SharedMediaPlayer { private var currentPrefetchItems: (SharedMediaPlaybackDataSource, SharedMediaPlaybackDataSource)? private let prefetchDisposable = MetaDisposable() - init(mediaManager: MediaManager, inForeground: Signal, postbox: Postbox, audioSession: ManagedAudioSession, overlayMediaManager: OverlayMediaManager, playlist: SharedMediaPlaylist, initialOrder: MusicPlaybackSettingsOrder, initialLooping: MusicPlaybackSettingsLooping, initialPlaybackRate: AudioPlaybackRate, playerIndex: Int32, controlPlaybackWithProximity: Bool) { + init(mediaManager: MediaManager, inForeground: Signal, account: Account, audioSession: ManagedAudioSession, overlayMediaManager: OverlayMediaManager, playlist: SharedMediaPlaylist, initialOrder: MusicPlaybackSettingsOrder, initialLooping: MusicPlaybackSettingsLooping, initialPlaybackRate: AudioPlaybackRate, playerIndex: Int32, controlPlaybackWithProximity: Bool) { self.mediaManager = mediaManager - self.postbox = postbox + self.account = account self.audioSession = audioSession self.overlayMediaManager = overlayMediaManager playlist.setOrder(initialOrder) @@ -466,13 +466,13 @@ final class SharedMediaPlayer { case .voice, .music: switch playbackData.source { case let .telegramFile(fileReference): - strongSelf.playbackItem = .audio(MediaPlayer(audioSessionManager: strongSelf.audioSession, postbox: strongSelf.postbox, resourceReference: fileReference.resourceReference(fileReference.media.resource), streamable: playbackData.type == .music, video: false, preferSoftwareDecoding: false, enableSound: true, baseRate: rateValue, fetchAutomatically: true, playAndRecord: controlPlaybackWithProximity)) + strongSelf.playbackItem = .audio(MediaPlayer(audioSessionManager: strongSelf.audioSession, postbox: strongSelf.account.postbox, resourceReference: fileReference.resourceReference(fileReference.media.resource), streamable: playbackData.type == .music, video: false, preferSoftwareDecoding: false, enableSound: true, baseRate: rateValue, fetchAutomatically: true, playAndRecord: controlPlaybackWithProximity)) } case .instantVideo: if let mediaManager = strongSelf.mediaManager, let item = item as? MessageMediaPlaylistItem { switch playbackData.source { case let .telegramFile(fileReference): - let videoNode = OverlayInstantVideoNode(postbox: strongSelf.postbox, audioSession: strongSelf.audioSession, manager: mediaManager.universalVideoManager, content: NativeVideoContent(id: .message(item.message.id, item.message.stableId, fileReference.media.fileId), fileReference: fileReference, streamVideo: false, enableSound: false, baseRate: rateValue), close: { [weak mediaManager] in + let videoNode = OverlayInstantVideoNode(postbox: strongSelf.account.postbox, audioSession: strongSelf.audioSession, manager: mediaManager.universalVideoManager, content: NativeVideoContent(id: .message(item.message.id, item.message.stableId, fileReference.media.fileId), fileReference: fileReference, streamVideo: false, enableSound: false, baseRate: rateValue), close: { [weak mediaManager] in mediaManager?.setPlaylist(nil, type: .voice) }) strongSelf.playbackItem = .instantVideo(videoNode) @@ -714,7 +714,7 @@ final class SharedMediaPlayer { let fetchedNextSignal: Signal switch current { case let .telegramFile(file): - fetchedCurrentSignal = self.postbox.mediaBox.resourceData(file.media.resource) + fetchedCurrentSignal = self.account.postbox.mediaBox.resourceData(file.media.resource) |> mapToSignal { data -> Signal in if data.complete { return .single(Void()) @@ -727,7 +727,7 @@ final class SharedMediaPlayer { } switch next { case let .telegramFile(file): - fetchedNextSignal = fetchedMediaResource(postbox: self.postbox, reference: file.resourceReference(file.media.resource)) + fetchedNextSignal = fetchedMediaResource(postbox: self.account.postbox, reference: file.resourceReference(file.media.resource)) |> ignoreValues |> `catch` { _ -> Signal in return .complete() diff --git a/TelegramUI/StorageUsageController.swift b/TelegramUI/StorageUsageController.swift index 6ae8339bdf..d8749d5540 100644 --- a/TelegramUI/StorageUsageController.swift +++ b/TelegramUI/StorageUsageController.swift @@ -279,7 +279,7 @@ func storageUsageController(context: AccountContext, isModal: Bool = false) -> V let statsPromise = Promise() let resetStats: () -> Void = { - let containerPath = context.applicationBindings.containerPath + let containerPath = context.sharedContext.applicationBindings.containerPath let additionalPaths: [String] = [ NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)[0], containerPath + "/Documents/files", @@ -289,7 +289,7 @@ func storageUsageController(context: AccountContext, isModal: Bool = false) -> V containerPath + "/Documents/tempcache_v1/store", ] statsPromise.set(.single(nil) - |> then(collectCacheUsageStats(account: context.account, additionalCachePaths: additionalPaths, logFilesPath: context.applicationBindings.containerPath + "/telegram-data/logs") + |> then(collectCacheUsageStats(account: context.account, additionalCachePaths: additionalPaths, logFilesPath: context.sharedContext.applicationBindings.containerPath + "/telegram-data/logs") |> map(Optional.init))) } resetStats() diff --git a/TelegramUI/SuppressContactsWarning.swift b/TelegramUI/SuppressContactsWarning.swift index a0a123f911..6529417274 100644 --- a/TelegramUI/SuppressContactsWarning.swift +++ b/TelegramUI/SuppressContactsWarning.swift @@ -16,7 +16,7 @@ func presentContactsWarningSuppression(context: AccountContext, present: (ViewCo case .notDetermined: DeviceAccess.authorizeAccess(to: .contacts, context: context) case .denied, .restricted: - context.applicationBindings.openSettings() + context.sharedContext.applicationBindings.openSettings() default: break } diff --git a/TelegramUI/TelegramController.swift b/TelegramUI/TelegramController.swift index 3543033ec1..becec2f2de 100644 --- a/TelegramUI/TelegramController.swift +++ b/TelegramUI/TelegramController.swift @@ -106,12 +106,12 @@ public class TelegramController: ViewController { if case .none = mediaAccessoryPanelVisibility { } else { - self.mediaStatusDisposable = (context.mediaManager.globalMediaPlayerState - |> mapToSignal { playlistStateAndType -> Signal<(SharedMediaPlayerItemPlaybackState, MediaManagerPlayerType)?, NoError> in - if let (state, type) = playlistStateAndType { + self.mediaStatusDisposable = (context.sharedContext.mediaManager.globalMediaPlayerState + |> mapToSignal { playlistStateAndType -> Signal<(Account, SharedMediaPlayerItemPlaybackState, MediaManagerPlayerType)?, NoError> in + if let (account, state, type) = playlistStateAndType { switch state { case let .state(state): - return .single((state, type)) + return .single((account, state, type)) case .loading: return .single(nil) |> delay(0.2, queue: .mainQueue()) } @@ -123,21 +123,21 @@ public class TelegramController: ViewController { guard let strongSelf = self else { return } - if !arePlaylistItemsEqual(strongSelf.playlistStateAndType?.0, playlistStateAndType?.0.item) || - strongSelf.playlistStateAndType?.1 != playlistStateAndType?.0.order || strongSelf.playlistStateAndType?.2 != playlistStateAndType?.1 { + if !arePlaylistItemsEqual(strongSelf.playlistStateAndType?.0, playlistStateAndType?.1.item) || + strongSelf.playlistStateAndType?.1 != playlistStateAndType?.1.order || strongSelf.playlistStateAndType?.2 != playlistStateAndType?.2 { var previousVoiceItem: SharedMediaPlaylistItem? if let playlistStateAndType = strongSelf.playlistStateAndType, playlistStateAndType.2 == .voice { previousVoiceItem = playlistStateAndType.0 } var updatedVoiceItem: SharedMediaPlaylistItem? - if let playlistStateAndType = playlistStateAndType, playlistStateAndType.1 == .voice { - updatedVoiceItem = playlistStateAndType.0.item + if let playlistStateAndType = playlistStateAndType, playlistStateAndType.2 == .voice { + updatedVoiceItem = playlistStateAndType.1.item } strongSelf.tempVoicePlaylistItemChanged?(previousVoiceItem, updatedVoiceItem) if let playlistStateAndType = playlistStateAndType { - strongSelf.playlistStateAndType = (playlistStateAndType.0.item, playlistStateAndType.0.order, playlistStateAndType.1) + strongSelf.playlistStateAndType = (playlistStateAndType.1.item, playlistStateAndType.1.order, playlistStateAndType.2) } else { var voiceEnded = false if strongSelf.playlistStateAndType?.2 == .voice { @@ -425,12 +425,12 @@ public class TelegramController: ViewController { mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition) mediaAccessoryPanel.containerNode.headerNode.playbackItem = item - let delayedStatus = self.context.mediaManager.globalMediaPlayerState - |> mapToSignal { value -> Signal<(SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?, NoError> in + let delayedStatus = self.context.sharedContext.mediaManager.globalMediaPlayerState + |> mapToSignal { value -> Signal<(Account, SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?, NoError> in guard let value = value else { return .single(nil) } - switch value.0 { + switch value.1 { case .state: return .single(value) case .loading: @@ -440,7 +440,7 @@ public class TelegramController: ViewController { mediaAccessoryPanel.containerNode.headerNode.playbackStatus = delayedStatus |> map { state -> MediaPlayerStatus in - if let stateOrLoading = state?.0, case let .state(state) = stateOrLoading { + if let stateOrLoading = state?.1, case let .state(state) = stateOrLoading { return state.status } else { return MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused) @@ -462,7 +462,7 @@ public class TelegramController: ViewController { mediaAccessoryPanel.containerNode.headerNode.displayScrubber = type != .voice mediaAccessoryPanel.close = { [weak self] in if let strongSelf = self, let (_, _, type) = strongSelf.playlistStateAndType { - strongSelf.context.mediaManager.setPlaylist(nil, type: type) + strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type) } } mediaAccessoryPanel.toggleRate = { @@ -488,12 +488,12 @@ public class TelegramController: ViewController { return } - strongSelf.context.mediaManager.playlistControl(.setBaseRate(baseRate), type: type) + strongSelf.context.sharedContext.mediaManager.playlistControl(.setBaseRate(baseRate), type: type) }) } mediaAccessoryPanel.togglePlayPause = { [weak self] in if let strongSelf = self, let (_, _, type) = strongSelf.playlistStateAndType { - strongSelf.context.mediaManager.playlistControl(.playback(.togglePlayPause), type: type) + strongSelf.context.sharedContext.mediaManager.playlistControl(.playback(.togglePlayPause), type: type) } } mediaAccessoryPanel.tapAction = { [weak self] in @@ -582,10 +582,9 @@ public class TelegramController: ViewController { self.mediaAccessoryPanel = (mediaAccessoryPanel, type) mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: .immediate) mediaAccessoryPanel.containerNode.headerNode.playbackItem = item - let mediaManager = self.context.mediaManager - mediaAccessoryPanel.containerNode.headerNode.playbackStatus = mediaManager.globalMediaPlayerState + mediaAccessoryPanel.containerNode.headerNode.playbackStatus = self.context.sharedContext.mediaManager.globalMediaPlayerState |> map { state -> MediaPlayerStatus in - if let stateOrLoading = state?.0, case let .state(state) = stateOrLoading { + if let stateOrLoading = state?.1, case let .state(state) = stateOrLoading { return state.status } else { return MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused) diff --git a/TelegramUI/TelegramInitializeLegacyComponents.swift b/TelegramUI/TelegramInitializeLegacyComponents.swift index 86a5c2e7e4..33e0dde406 100644 --- a/TelegramUI/TelegramInitializeLegacyComponents.swift +++ b/TelegramUI/TelegramInitializeLegacyComponents.swift @@ -64,7 +64,7 @@ private final class LegacyComponentsAccessCheckerImpl: NSObject, LegacyComponent subject = .send } if let context = self.context { - DeviceAccess.authorizeAccess(to: .location(subject), presentationData: context.currentPresentationData.with { $0 }, present: context.presentGlobalController, openSettings: context.applicationBindings.openSettings, { value in + DeviceAccess.authorizeAccess(to: .location(subject), presentationData: context.currentPresentationData.with { $0 }, present: context.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in if !value { alertDismissCompletion?() } @@ -218,7 +218,7 @@ private final class LegacyComponentsGlobalsProviderImpl: NSObject, LegacyCompone default: convertedType = .play } - let disposable = legacyContext.mediaManager.audioSession.push(audioSessionType: convertedType, once: true, activate: { _ in + let disposable = legacyContext.sharedContext.mediaManager.audioSession.push(audioSessionType: convertedType, once: true, activate: { _ in }, deactivate: { interrupted?() return .complete() diff --git a/TelegramUI/TelegramRootController.swift b/TelegramUI/TelegramRootController.swift index 0e28f72433..25c911a90c 100644 --- a/TelegramUI/TelegramRootController.swift +++ b/TelegramUI/TelegramRootController.swift @@ -62,11 +62,11 @@ public final class TelegramRootController: NavigationController { } controllers.append(chatListController) - let restoreSettignsController = AccountStore.switchingSettingsController + let restoreSettignsController = self.context.sharedContext.switchingSettingsController restoreSettignsController?.updateContext(context: self.context) - AccountStore.switchingSettingsController = nil + self.context.sharedContext.switchingSettingsController = nil - let accountSettingsController = restoreSettignsController ?? settingsController(context: self.context, accountManager: context.accountManager) + let accountSettingsController = restoreSettignsController ?? settingsController(context: self.context, accountManager: context.sharedContext.accountManager) controllers.append(accountSettingsController) tabBarController.setControllers(controllers, selectedIndex: restoreSettignsController != nil ? (controllers.count - 1) : (controllers.count - 2)) diff --git a/TelegramUI/ThemeAutoNightSettingsController.swift b/TelegramUI/ThemeAutoNightSettingsController.swift index cde8b04ff0..c0b9052e22 100644 --- a/TelegramUI/ThemeAutoNightSettingsController.swift +++ b/TelegramUI/ThemeAutoNightSettingsController.swift @@ -355,7 +355,7 @@ public func themeAutoNightSettingsController(context: AccountContext) -> ViewCon let forceUpdateLocation: () -> Void = { let locationCoordinates = Signal<(Double, Double), NoError> { subscriber in - return context.locationManager!.push(mode: DeviceLocationMode.precise, updated: { coordinate in + return context.sharedContext.locationManager!.push(mode: DeviceLocationMode.precise, updated: { coordinate in subscriber.putNext((coordinate.latitude, coordinate.longitude)) subscriber.putCompletion() }) diff --git a/TelegramUI/UniversalVideoGalleryItem.swift b/TelegramUI/UniversalVideoGalleryItem.swift index 7e5941a8ec..2ae05d5411 100644 --- a/TelegramUI/UniversalVideoGalleryItem.swift +++ b/TelegramUI/UniversalVideoGalleryItem.swift @@ -289,7 +289,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { self.footerContentNode.scrubberView = nil } - let mediaManager = item.context.mediaManager + let mediaManager = item.context.sharedContext.mediaManager let videoNode = UniversalVideoNode(postbox: item.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: item.content, priority: .gallery) let videoSize = CGSize(width: item.content.dimensions.width * 2.0, height: item.content.dimensions.height * 2.0) @@ -537,7 +537,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { let transform = CATransform3DScale(videoNode.layer.transform, transformedFrame.size.width / videoNode.layer.bounds.size.width, transformedFrame.size.height / videoNode.layer.bounds.size.height, 1.0) videoNode.layer.animate(from: NSValue(caTransform3D: transform), to: NSValue(caTransform3D: videoNode.layer.transform), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25) - self.context.mediaManager.setOverlayVideoNode(nil) + self.context.sharedContext.mediaManager.setOverlayVideoNode(nil) } else { var transformedFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view) let transformedSuperFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view.superview) @@ -813,9 +813,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { if let item = self.item, let _ = self.videoNode { let context = self.context let baseNavigationController = self.baseNavigationController() - let mediaManager = self.context.mediaManager + let mediaManager = self.context.sharedContext.mediaManager var expandImpl: (() -> Void)? - let overlayNode = OverlayUniversalVideoNode(postbox: self.context.account.postbox, audioSession: context.mediaManager.audioSession, manager: context.mediaManager.universalVideoManager, content: item.content, expand: { + let overlayNode = OverlayUniversalVideoNode(postbox: self.context.account.postbox, audioSession: context.sharedContext.mediaManager.audioSession, manager: context.sharedContext.mediaManager.universalVideoManager, content: item.content, expand: { expandImpl?() }, close: { [weak mediaManager] in mediaManager?.setOverlayVideoNode(nil) @@ -844,7 +844,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { overlaySupernode?.view.addSubview(view) overlayNode?.canAttachContent = false }) - } else if let info = context.mediaManager.galleryHiddenMediaManager.findTarget(messageId: id, media: media) { + } else if let info = context.sharedContext.mediaManager.galleryHiddenMediaManager.findTarget(messageId: id, media: media) { return GalleryTransitionArguments(transitionNode: (info.1, info.2), addToTransitionSurface: info.0) } return nil @@ -853,7 +853,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { break } } - context.mediaManager.setOverlayVideoNode(overlayNode) + context.sharedContext.mediaManager.setOverlayVideoNode(overlayNode) if overlayNode.supernode != nil { self.beginCustomDismiss() self.animateOut(toOverlay: overlayNode, completion: { [weak self] in diff --git a/TelegramUI/UserInfoController.swift b/TelegramUI/UserInfoController.swift index 272c25526b..395d769bad 100644 --- a/TelegramUI/UserInfoController.swift +++ b/TelegramUI/UserInfoController.swift @@ -792,7 +792,7 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Us return } - let callResult = context.callManager?.requestCall(peerId: peer.id, endCurrentIfAny: false) + let callResult = context.callManager?.requestCall(account: context.account, peerId: peer.id, endCurrentIfAny: false) if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult { if currentPeerId == peer.id { context.navigateToCurrentCall?() @@ -803,7 +803,7 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Us } |> deliverOnMainQueue).start(next: { peer, current in if let peer = peer, let current = current { presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { - let _ = context.callManager?.requestCall(peerId: peer.id, endCurrentIfAny: true) + let _ = context.callManager?.requestCall(account: context.account, peerId: peer.id, endCurrentIfAny: true) })]), nil) } }) @@ -968,14 +968,14 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Us }), ActionSheetButtonItem(title: presentationData.strings.UserInfo_PhoneCall, action: { dismissAction() - context.applicationBindings.openUrl("tel:\(formatPhoneNumber(number).replacingOccurrences(of: " ", with: ""))") + context.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(number).replacingOccurrences(of: " ", with: ""))") }), ]), ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) ]) presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) } else { - context.applicationBindings.openUrl("tel:\(formatPhoneNumber(number).replacingOccurrences(of: " ", with: ""))") + context.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(number).replacingOccurrences(of: " ", with: ""))") } }) }, aboutLinkAction: { action, itemLink in @@ -1035,7 +1035,7 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Us if number.isEmpty { return .single([]) } else { - return context.contactDataManager.basicDataForNormalizedPhoneNumber(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(number))) + return context.sharedContext.contactDataManager?.basicDataForNormalizedPhoneNumber(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(number))) ?? .single([]) } } @@ -1112,12 +1112,14 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Us guard let peer = peer as? TelegramUser, let phone = peer.phone, !phone.isEmpty else { return .complete() } - return context.contactDataManager.basicDataForNormalizedPhoneNumber(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(phone))) + return (context.sharedContext.contactDataManager?.basicDataForNormalizedPhoneNumber(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(phone))) ?? .single([])) |> take(1) |> mapToSignal { records -> Signal in var signals: [Signal] = [] - for (id, basicData) in records { - signals.append(context.contactDataManager.appendContactData(DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: firstName, lastName: lastName, phoneNumbers: basicData.phoneNumbers), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: []), to: id)) + if let contactDataManager = context.sharedContext.contactDataManager { + for (id, basicData) in records { + signals.append(contactDataManager.appendContactData(DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: firstName, lastName: lastName, phoneNumbers: basicData.phoneNumbers), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: []), to: id)) + } } return combineLatest(signals) |> mapToSignal { _ -> Signal in diff --git a/TelegramUI/WebSearchVideoGalleryItem.swift b/TelegramUI/WebSearchVideoGalleryItem.swift index 0b5654a83a..129a8328f4 100644 --- a/TelegramUI/WebSearchVideoGalleryItem.swift +++ b/TelegramUI/WebSearchVideoGalleryItem.swift @@ -151,7 +151,7 @@ final class WebSearchVideoGalleryItemNode: ZoomableContentGalleryItemNode { videoNode.removeFromSupernode() } - let mediaManager = item.context.mediaManager + let mediaManager = item.context.sharedContext.mediaManager let videoNode = UniversalVideoNode(postbox: item.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: item.content, priority: .gallery) let videoSize = CGSize(width: item.content.dimensions.width * 2.0, height: item.content.dimensions.height * 2.0) @@ -293,7 +293,7 @@ final class WebSearchVideoGalleryItemNode: ZoomableContentGalleryItemNode { let transform = CATransform3DScale(videoNode.layer.transform, transformedFrame.size.width / videoNode.layer.bounds.size.width, transformedFrame.size.height / videoNode.layer.bounds.size.height, 1.0) videoNode.layer.animate(from: NSValue(caTransform3D: transform), to: NSValue(caTransform3D: videoNode.layer.transform), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25) - self.context.mediaManager.setOverlayVideoNode(nil) + self.context.sharedContext.mediaManager.setOverlayVideoNode(nil) } else { var transformedFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view) let transformedSuperFrame = node.0.view.convert(node.0.view.bounds, to: videoNode.view.superview)