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