Merge branch 'master' into experimental-2

This commit is contained in:
Ali 2021-07-23 18:11:21 +02:00
commit cdc2ce8447
42 changed files with 577 additions and 295 deletions

View File

@ -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)
}
}
})
}

View File

@ -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";

View File

@ -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 }

View File

@ -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 }

View File

@ -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)
}
}

View File

@ -170,6 +170,14 @@ public final class ChatListSearchItemHeader: ListViewItemHeader {
self.actionTitle = actionTitle
self.action = action
}
public func combinesWith(other: ListViewItemHeader) -> Bool {
if let other = other as? ChatListSearchItemHeader, other.id == self.id {
return true
} else {
return false
}
}
public func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
return ChatListSearchItemHeaderNode(type: self.type, theme: self.theme, strings: self.strings, actionTitle: self.actionTitle, action: self.action)

View File

@ -18,6 +18,14 @@ final class ContactListNameIndexHeader: Equatable, ListViewItemHeader {
self.letter = letter
self.id = ListViewItemNode.HeaderId(space: 0, id: Int64(letter))
}
func combinesWith(other: ListViewItemHeader) -> Bool {
if let other = other as? ContactListNameIndexHeader, self.id == other.id {
return true
} else {
return false
}
}
func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
return ContactListNameIndexHeaderNode(theme: self.theme, letter: self.letter)

View File

@ -3297,7 +3297,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
var containsSameHeader = false
if let nextHeaders = nextItemNode.headers() {
nextHeaderSearch: for nextHeader in nextHeaders {
if nextHeader.id == currentId {
if nextHeader.id == currentId && nextHeader.combinesWith(other: currentHeader) {
containsSameHeader = true
break nextHeaderSearch
}
@ -3341,7 +3341,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
let flashing = self.headerItemsAreFlashing()
func addHeader(id: VisibleHeaderNodeId, upperBound: CGFloat, upperBoundEdge: CGFloat, lowerBound: CGFloat, item: ListViewItemHeader, hasValidNodes: Bool) {
func addHeader(id: VisibleHeaderNodeId, upperBound: CGFloat, upperIndex: Int, upperBoundEdge: CGFloat, lowerBound: CGFloat, lowerIndex: Int, item: ListViewItemHeader, hasValidNodes: Bool) {
let itemHeaderHeight: CGFloat = item.height
let headerFrame: CGRect
@ -3364,7 +3364,9 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
visibleHeaderNodes.insert(id)
let initialHeaderNodeAlpha = self.itemHeaderNodesAlpha
if let headerNode = self.itemHeaderNodes[id] {
let headerNode: ListViewItemHeaderNode
if let current = self.itemHeaderNodes[id] {
headerNode = current
switch transition.0 {
case .immediate:
headerNode.frame = headerFrame
@ -3410,7 +3412,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
}
headerNode.updateStickDistanceFactor(stickLocationDistanceFactor, transition: transition.0)
} else {
let headerNode = item.node(synchronousLoad: synchronousLoad)
headerNode = item.node(synchronousLoad: synchronousLoad)
headerNode.alpha = initialHeaderNodeAlpha
if headerNode.item !== item {
item.updateNode(headerNode, previous: nil, next: nil)
@ -3432,11 +3434,52 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
}
headerNode.updateStickDistanceFactor(stickLocationDistanceFactor, transition: .immediate)
}
var maxIntersectionHeight: (CGFloat, Int)?
for i in upperIndex ... lowerIndex {
let itemNode = self.itemNodes[i]
let itemNodeFrame = itemNode.apparentFrame
let intersectionHeight: CGFloat = itemNodeFrame.intersection(headerFrame).height
if let (currentMaxIntersectionHeight, _) = maxIntersectionHeight {
if currentMaxIntersectionHeight < intersectionHeight {
maxIntersectionHeight = (intersectionHeight, i)
}
} else {
maxIntersectionHeight = (intersectionHeight, i)
}
}
if let (_, i) = maxIntersectionHeight {
let itemNode = self.itemNodes[i]
let itemNodeFrame = itemNode.apparentFrame
if itemNodeFrame.intersects(headerFrame) {
var updated = false
if let previousItemNode = headerNode.attachedToItemNode {
if previousItemNode !== itemNode {
previousItemNode.attachedHeaderNodes.removeAll(where: { $0 === headerNode })
updated = true
}
} else {
updated = true
}
if updated {
headerNode.attachedToItemNode = itemNode
itemNode.attachedHeaderNodes.append(headerNode)
itemNode.attachedHeaderNodesUpdated()
}
}
} else {
if let previousItemNode = headerNode.attachedToItemNode {
previousItemNode.attachedHeaderNodes.removeAll(where: { $0 === headerNode })
headerNode.attachedToItemNode = nil
}
}
}
var previousHeaderBySpace: [AnyHashable: (id: VisibleHeaderNodeId, upperBound: CGFloat, upperBoundEdge: CGFloat, lowerBound: CGFloat, item: ListViewItemHeader, hasValidNodes: Bool)] = [:]
var previousHeaderBySpace: [AnyHashable: (id: VisibleHeaderNodeId, upperBound: CGFloat, upperBoundIndex: Int, upperBoundEdge: CGFloat, lowerBound: CGFloat, lowerBoundIndex: Int, item: ListViewItemHeader, hasValidNodes: Bool)] = [:]
for itemNode in self.itemNodes {
for i in 0 ..< self.itemNodes.count {
let itemNode = self.itemNodes[i]
let itemFrame = itemNode.apparentFrame
let itemTopInset = itemNode.insets.top
var validItemHeaderSpaces: [AnyHashable] = []
@ -3458,16 +3501,16 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
itemMaxY = itemFrame.maxY - (self.rotated ? itemNode.insets.top : itemNode.insets.bottom)
}
if let (previousHeaderId, previousUpperBound, previousUpperBoundEdge, previousLowerBound, previousHeaderItem, hasValidNodes) = previousHeaderBySpace[itemHeader.id.space] {
if let (previousHeaderId, previousUpperBound, previousUpperIndex, previousUpperBoundEdge, previousLowerBound, previousLowerIndex, previousHeaderItem, hasValidNodes) = previousHeaderBySpace[itemHeader.id.space] {
if previousHeaderId == headerId {
previousHeaderBySpace[itemHeader.id.space] = (previousHeaderId, previousUpperBound, previousUpperBoundEdge, itemMaxY, previousHeaderItem, hasValidNodes || itemNode.index != nil)
previousHeaderBySpace[itemHeader.id.space] = (previousHeaderId, previousUpperBound, previousUpperIndex, previousUpperBoundEdge, itemMaxY, i, previousHeaderItem, hasValidNodes || itemNode.index != nil)
} else {
addHeader(id: previousHeaderId, upperBound: previousUpperBound, upperBoundEdge: previousUpperBoundEdge, lowerBound: previousLowerBound, item: previousHeaderItem, hasValidNodes: hasValidNodes)
addHeader(id: previousHeaderId, upperBound: previousUpperBound, upperIndex: previousUpperIndex, upperBoundEdge: previousUpperBoundEdge, lowerBound: previousLowerBound, lowerIndex: previousLowerIndex, item: previousHeaderItem, hasValidNodes: hasValidNodes)
previousHeaderBySpace[itemHeader.id.space] = (headerId, itemFrame.minY, itemFrame.minY + itemTopInset, itemMaxY, itemHeader, itemNode.index != nil)
previousHeaderBySpace[itemHeader.id.space] = (headerId, itemFrame.minY, i, itemFrame.minY + itemTopInset, itemMaxY, i, itemHeader, itemNode.index != nil)
}
} else {
previousHeaderBySpace[itemHeader.id.space] = (headerId, itemFrame.minY, itemFrame.minY + itemTopInset, itemMaxY, itemHeader, itemNode.index != nil)
previousHeaderBySpace[itemHeader.id.space] = (headerId, itemFrame.minY, i, itemFrame.minY + itemTopInset, itemMaxY, i, itemHeader, itemNode.index != nil)
}
}
}
@ -3477,18 +3520,18 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
continue
}
let (previousHeaderId, previousUpperBound, previousUpperBoundEdge, previousLowerBound, previousHeaderItem, hasValidNodes) = previousHeader
let (previousHeaderId, previousUpperBound, previousUpperIndex, previousUpperBoundEdge, previousLowerBound, previousLowerIndex, previousHeaderItem, hasValidNodes) = previousHeader
addHeader(id: previousHeaderId, upperBound: previousUpperBound, upperBoundEdge: previousUpperBoundEdge, lowerBound: previousLowerBound, item: previousHeaderItem, hasValidNodes: hasValidNodes)
addHeader(id: previousHeaderId, upperBound: previousUpperBound, upperIndex: previousUpperIndex, upperBoundEdge: previousUpperBoundEdge, lowerBound: previousLowerBound, lowerIndex: previousLowerIndex, item: previousHeaderItem, hasValidNodes: hasValidNodes)
previousHeaderBySpace.removeValue(forKey: space)
}
}
for (space, previousHeader) in previousHeaderBySpace {
let (previousHeaderId, previousUpperBound, previousUpperBoundEdge, previousLowerBound, previousHeaderItem, hasValidNodes) = previousHeader
let (previousHeaderId, previousUpperBound, previousUpperIndex, previousUpperBoundEdge, previousLowerBound, previousLowerIndex, previousHeaderItem, hasValidNodes) = previousHeader
addHeader(id: previousHeaderId, upperBound: previousUpperBound, upperBoundEdge: previousUpperBoundEdge, lowerBound: previousLowerBound, item: previousHeaderItem, hasValidNodes: hasValidNodes)
addHeader(id: previousHeaderId, upperBound: previousUpperBound, upperIndex: previousUpperIndex, upperBoundEdge: previousUpperBoundEdge, lowerBound: previousLowerBound, lowerIndex: previousLowerIndex, item: previousHeaderItem, hasValidNodes: hasValidNodes)
previousHeaderBySpace.removeValue(forKey: space)
}

View File

@ -13,6 +13,8 @@ public protocol ListViewItemHeader: AnyObject {
var stickDirection: ListViewItemHeaderStickDirection { get }
var height: CGFloat { get }
var stickOverInsets: Bool { get }
func combinesWith(other: ListViewItemHeader) -> Bool
func node(synchronousLoad: Bool) -> ListViewItemHeaderNode
func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?)
@ -25,6 +27,7 @@ open class ListViewItemHeaderNode: ASDisplayNode {
final private(set) var internalStickLocationDistanceFactor: CGFloat = 0.0
final var internalStickLocationDistance: CGFloat = 0.0
private var isFlashingOnScrolling = false
weak var attachedToItemNode: ListViewItemNode?
var item: ListViewItemHeader?

View File

@ -129,6 +129,11 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode {
final var tempHeaderSpaceAffinities: [ListViewItemNode.HeaderId: Int] = [:]
final var headerSpaceAffinities: [ListViewItemNode.HeaderId: Int] = [:]
public internal(set) var attachedHeaderNodes: [ListViewItemHeaderNode] = []
open func attachedHeaderNodesUpdated() {
}
final let wantsScrollDynamics: Bool

View File

@ -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<Bool>?) -> Void, baseNavigationController: NavigationController?, actionInteraction: GalleryControllerActionInteraction? = nil) {
public init(context: AccountContext, source: GalleryControllerItemSource, invertItemOrder: Bool = false, streamSingleVideo: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double? = nil, synchronousLoad: Bool = false, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, baseNavigationController: NavigationController?, actionInteraction: GalleryControllerActionInteraction? = nil) {
self.context = context
self.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)
}

View File

@ -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))
}
}
})))
}

View File

@ -1374,6 +1374,14 @@ public final class ItemListPeerItemHeader: ListViewItemHeader {
self.actionTitle = actionTitle
self.action = action
}
public func combinesWith(other: ListViewItemHeader) -> Bool {
if let other = other as? ItemListPeerItemHeader, other.id == self.id {
return true
} else {
return false
}
}
public func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
return ItemListPeerItemHeaderNode(theme: self.theme, strings: self.strings, text: self.text, additionalText: self.additionalText, actionTitle: self.actionTitle, action: self.action)

View File

@ -67,6 +67,14 @@ final class ListMessageDateHeader: ListViewItemHeader {
let stickOverInsets: Bool = true
let height: CGFloat = 28.0
public func combinesWith(other: ListViewItemHeader) -> Bool {
if let other = other as? ListMessageDateHeader, other.id == self.id {
return true
} else {
return false
}
}
func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
return ListMessageDateHeaderNode(theme: self.theme, strings: self.strings, fontSize: self.fontSize, roundedTimestamp: self.roundedTimestamp, month: self.month, year: self.year)

View File

@ -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))
}

View File

@ -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

View File

@ -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 {

View File

@ -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
)

View File

@ -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 {

View File

@ -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<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
init?(startCall: @escaping (AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
if !CallKitIntegration.isAvailable {
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<Bool, NoError>)?
private var startCall: ((AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>)?
private var answerCall: ((UUID) -> Void)?
private var endCall: ((UUID) -> Signal<Bool, NoError>)?
private var setCallMuted: ((UUID, Bool) -> Void)?
@ -119,7 +120,7 @@ class CallKitProviderDelegate: NSObject, CXProviderDelegate {
self.provider.setDelegate(self, queue: nil)
}
func setup(audioSessionActivePromise: ValuePromise<Bool>, startCall: @escaping (Account, UUID, String, Bool) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
func setup(audioSessionActivePromise: ValuePromise<Bool>, startCall: @escaping (AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>, answerCall: @escaping (UUID) -> Void, endCall: @escaping (UUID) -> Signal<Bool, NoError>, setCallMuted: @escaping (UUID, Bool) -> Void, audioSessionActivationChanged: @escaping (Bool) -> Void) {
self.audioSessionActivePromise = audioSessionActivePromise
self.startCall = startCall
self.answerCall = answerCall
@ -165,10 +166,10 @@ 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)
let handle = CXHandle(type: .generic, value: "\(peerId.id._internalGetInt64Value())")
self.currentStartCallAccount = (uuid, context)
let handle = CXHandle(type: .generic, value: "\(peerId.id. _internalGetInt64Value())")
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 {

View File

@ -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)

View File

@ -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<Bool, NoError>)?
var startCallImpl: ((AccountContext, UUID, String, Bool) -> Signal<Bool, NoError>)?
var answerCallImpl: ((UUID) -> Void)?
var endCallImpl: ((UUID) -> Signal<Bool, NoError>)?
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 = Int64(handle) {
return strongSelf.startCall(account: account, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)), isVideo: isVideo, internalId: uuid)
return strongSelf.startCall(context: context, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(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<Peer?, NoError> 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<Bool, NoError> 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),

View File

@ -2496,8 +2496,19 @@ public final class VoiceChatController: ViewController {
}
let isScheduled = strongSelf.isScheduled
let canSpeak: Bool
if let callState = strongSelf.callState {
if let muteState = callState.muteState {
canSpeak = muteState.canUnmute
} else {
canSpeak = true
}
} else {
canSpeak = false
}
if !isScheduled {
if !isScheduled && canSpeak {
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_NoiseSuppression, textColor: .primary, textLayout: .secondLineWithValue(strongSelf.isNoiseSuppressionEnabled ? strongSelf.presentationData.strings.VoiceChat_NoiseSuppressionEnabled : strongSelf.presentationData.strings.VoiceChat_NoiseSuppressionDisabled), icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Call/Context Menu/Noise"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in

View File

@ -423,10 +423,17 @@ public extension TelegramEngine {
public func peerExportedInvitationsCreators(peerId: PeerId) -> Signal<[ExportedInvitationCreator], NoError> {
return _internal_peerExportedInvitationsCreators(account: self.account, peerId: peerId)
}
public func direct_peerExportedInvitations(peerId: PeerId, revoked: Bool, adminId: PeerId? = nil, offsetLink: ExportedInvitation? = nil) -> Signal<ExportedInvitations?, NoError> {
return _internal_peerExportedInvitations(account: self.account, peerId: peerId, revoked: revoked, adminId: adminId, offsetLink: offsetLink)
}
public func peerExportedInvitations(peerId: PeerId, adminId: PeerId?, revoked: Bool, forceUpdate: Bool) -> PeerExportedInvitationsContext {
return PeerExportedInvitationsContext(account: self.account, peerId: peerId, adminId: adminId, revoked: revoked, forceUpdate: forceUpdate)
}
public func revokePersistentPeerExportedInvitation(peerId: PeerId) -> Signal<ExportedInvitation?, NoError> {
return _internal_revokePersistentPeerExportedInvitation(account: self.account, peerId: peerId)
}
public func peerInvitationImporters(peerId: PeerId, invite: ExportedInvitation) -> PeerInvitationImportersContext {
return PeerInvitationImportersContext(account: self.account, peerId: peerId, invite: invite)

View File

@ -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<AuthorizedApplicationContext?, NoError> 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<UnauthorizedApplicationContext?, NoError> 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<PeerId>), NoError> = self.sharedContextPromise.get()
|> take(1)
|> mapToSignal { sharedContext -> Signal<(AccountManager, Set<PeerId>), NoError> in
return sharedContext.sharedContext.activeAccounts
return sharedContext.sharedContext.activeAccountContexts
|> map { _, accounts, _ -> Set<PeerId> in
return Set(accounts.map { $0.1.peerId })
return Set(accounts.map { $0.1.account.peerId })
}
|> reduceLeft(value: Set<PeerId>()) { current, updated, emit in
if !current.isEmpty {
@ -1211,7 +1206,9 @@ final class SharedApplicationContext {
|> map { loggedOutAccountPeerIds -> (AccountManager, Set<PeerId>) 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<Account?, NoError> in
return account.postbox.transaction { transaction -> Account? in
|> mapToSignal { primary, contexts, _ -> Signal<(AccountRecordId?, [AccountContext?]), NoError> in
return combineLatest(contexts.map { _, context, _ -> Signal<AccountContext?, NoError> 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<Void, NoError> in
sharedContext.wakeupManager.allowBackgroundTimeExtension(timeout: 2.0, extendNow: true)
return sharedContext.sharedContext.activeAccounts
|> mapToSignal { _, accounts, _ -> Signal<Account, NoError> in
for account in accounts {
if account.1.id == accountId {
return .single(account.1)
return sharedContext.sharedContext.activeAccountContexts
|> mapToSignal { _, contexts, _ -> Signal<Account, NoError> 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<AccountRecordId?, NoError> in
return sharedContext.sharedContext.activeAccounts
return sharedContext.sharedContext.activeAccountContexts
|> take(1)
|> mapToSignal { _, accounts, _ -> Signal<AccountRecordId?, NoError> in
let keys = accounts.map { _, account, _ -> Signal<(AccountRecordId, MasterNotificationKey)?, NoError> in
return masterNotificationsKey(account: account, ignoreDisabled: true)
|> mapToSignal { _, contexts, _ -> Signal<AccountRecordId?, NoError> 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<AccountRecordId?, NoError> in
return sharedContext.sharedContext.activeAccounts
return sharedContext.sharedContext.activeAccountContexts
|> take(1)
|> map { _, accounts, _ -> AccountRecordId? in
for (_, account, _) in accounts {
if Int(account.peerId.id._internalGetInt64Value()) == userId {
return account.id
|> map { _, contexts, _ -> AccountRecordId? in
for (_, context, _) in contexts {
if Int(context.account.peerId.id. _internalGetInt64Value()) == userId {
return context.account.id
}
}
return nil

View File

@ -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)
})
}

View File

@ -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<ExportAuthTransferTokenResult, ExportAuthTransferTokenError> 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

View File

@ -1497,6 +1497,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
var bounds = self.bounds
bounds.origin.x = -translation.x
self.bounds = bounds
self.updateAttachedAvatarNodeOffset(offset: translation.x, transition: .immediate)
if let swipeToReplyNode = self.swipeToReplyNode {
swipeToReplyNode.frame = CGRect(origin: CGPoint(x: bounds.size.width, y: floor((self.contentSize.height - 33.0) / 2.0)), size: CGSize(width: 33.0, height: 33.0))
@ -1532,6 +1534,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
bounds.origin.x = 0.0
self.bounds = bounds
self.layer.animateBounds(from: previousBounds, to: bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
self.updateAttachedAvatarNodeOffset(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring))
if let swipeToReplyNode = self.swipeToReplyNode {
self.swipeToReplyNode = nil
swipeToReplyNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak swipeToReplyNode] _ in

View File

@ -3657,6 +3657,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
var shadowBounds = self.shadowNode.bounds
shadowBounds.origin.x = -translation.x
self.shadowNode.bounds = shadowBounds
self.updateAttachedAvatarNodeOffset(offset: translation.x, transition: .immediate)
if let swipeToReplyNode = self.swipeToReplyNode {
swipeToReplyNode.frame = CGRect(origin: CGPoint(x: bounds.size.width, y: floor((self.contentSize.height - 33.0) / 2.0)), size: CGSize(width: 33.0, height: 33.0))
@ -3696,6 +3698,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
shadowBounds.origin.x = 0.0
self.shadowNode.bounds = shadowBounds
self.layer.animateBounds(from: previousBounds, to: bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
self.updateAttachedAvatarNodeOffset(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring))
self.shadowNode.layer.animateBounds(from: previousShadowBounds, to: shadowBounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
if let swipeToReplyNode = self.swipeToReplyNode {
self.swipeToReplyNode = nil

View File

@ -42,6 +42,14 @@ final class ChatMessageDateHeader: ListViewItemHeader {
let stickOverInsets: Bool = true
let height: CGFloat = 34.0
public func combinesWith(other: ListViewItemHeader) -> Bool {
if let other = other as? ChatMessageDateHeader, other.id == self.id {
return true
} else {
return false
}
}
func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
return ChatMessageDateHeaderNode(localTimestamp: self.roundedTimestamp, scheduled: self.scheduled, presentationData: self.presentationData, context: self.context, action: self.action)
@ -316,14 +324,22 @@ final class ChatMessageAvatarHeader: ListViewItemHeader {
let peerId: PeerId
let peer: Peer?
let messageReference: MessageReference?
let effectiveTimestamp: Int32
let presentationData: ChatPresentationData
let context: AccountContext
let controllerInteraction: ChatControllerInteraction
init(timestamp: Int32, peerId: PeerId, peer: Peer?, messageReference: MessageReference?, presentationData: ChatPresentationData, context: AccountContext, controllerInteraction: ChatControllerInteraction) {
init(timestamp: Int32, peerId: PeerId, peer: Peer?, messageReference: MessageReference?, message: Message, presentationData: ChatPresentationData, context: AccountContext, controllerInteraction: ChatControllerInteraction) {
self.peerId = peerId
self.peer = peer
self.messageReference = messageReference
var effectiveTimestamp = message.timestamp
if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported) {
effectiveTimestamp = forwardInfo.date
}
self.effectiveTimestamp = effectiveTimestamp
self.presentationData = presentationData
self.context = context
self.controllerInteraction = controllerInteraction
@ -335,6 +351,17 @@ final class ChatMessageAvatarHeader: ListViewItemHeader {
let height: CGFloat = 38.0
public func combinesWith(other: ListViewItemHeader) -> Bool {
if let other = other as? ChatMessageAvatarHeader, other.id == self.id {
if abs(self.effectiveTimestamp - other.effectiveTimestamp) >= 10 * 60 {
return false
}
return true
} else {
return false
}
}
func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
return ChatMessageAvatarHeaderNode(peerId: self.peerId, peer: self.peer, messageReference: self.messageReference, presentationData: self.presentationData, context: self.context, controllerInteraction: self.controllerInteraction, synchronousLoad: synchronousLoad)
}

View File

@ -913,6 +913,8 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
var bounds = self.bounds
bounds.origin.x = -translation.x
self.bounds = bounds
self.updateAttachedAvatarNodeOffset(offset: translation.x, transition: .immediate)
if let swipeToReplyNode = self.swipeToReplyNode {
swipeToReplyNode.frame = CGRect(origin: CGPoint(x: bounds.size.width, y: floor((self.contentSize.height - 33.0) / 2.0)), size: CGSize(width: 33.0, height: 33.0))
@ -948,6 +950,9 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
bounds.origin.x = 0.0
self.bounds = bounds
self.layer.animateBounds(from: previousBounds, to: bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
self.updateAttachedAvatarNodeOffset(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring))
if let swipeToReplyNode = self.swipeToReplyNode {
self.swipeToReplyNode = nil
swipeToReplyNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak swipeToReplyNode] _ in

View File

@ -357,7 +357,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
if !hasActionMedia && !isBroadcastChannel {
if let effectiveAuthor = effectiveAuthor {
//accessoryItem = ChatMessageAvatarAccessoryItem(context: context, peerId: effectiveAuthor.id, peer: effectiveAuthor, messageReference: MessageReference(message), messageTimestamp: content.index.timestamp, forwardInfo: message.forwardInfo, emptyColor: presentationData.theme.theme.chat.message.incoming.bubble.withoutWallpaper.fill, controllerInteraction: controllerInteraction)
avatarHeader = ChatMessageAvatarHeader(timestamp: content.index.timestamp, peerId: effectiveAuthor.id, peer: effectiveAuthor, messageReference: MessageReference(message), presentationData: presentationData, context: context, controllerInteraction: controllerInteraction)
avatarHeader = ChatMessageAvatarHeader(timestamp: content.index.timestamp, peerId: effectiveAuthor.id, peer: effectiveAuthor, messageReference: MessageReference(message), message: message, presentationData: presentationData, context: context, controllerInteraction: controllerInteraction)
}
}
}

View File

@ -874,4 +874,18 @@ public class ChatMessageItemView: ListViewItemNode {
func getStatusNode() -> ASDisplayNode? {
return nil
}
private var attachedAvatarNodeOffset: CGFloat = 0.0
override public func attachedHeaderNodesUpdated() {
self.updateAttachedAvatarNodeOffset(offset: self.attachedAvatarNodeOffset, transition: .immediate)
}
func updateAttachedAvatarNodeOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {
for headerNode in self.attachedHeaderNodes {
if let headerNode = headerNode as? ChatMessageAvatarHeaderNode {
transition.updateSublayerTransformOffset(layer: headerNode.layer, offset: CGPoint(x: offset, y: 0.0))
}
}
}
}

View File

@ -1017,6 +1017,8 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
var bounds = self.bounds
bounds.origin.x = -translation.x
self.bounds = bounds
self.updateAttachedAvatarNodeOffset(offset: translation.x, transition: .immediate)
if let swipeToReplyNode = self.swipeToReplyNode {
swipeToReplyNode.frame = CGRect(origin: CGPoint(x: bounds.size.width, y: floor((self.contentSize.height - 33.0) / 2.0)), size: CGSize(width: 33.0, height: 33.0))
@ -1052,6 +1054,9 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
bounds.origin.x = 0.0
self.bounds = bounds
self.layer.animateBounds(from: previousBounds, to: bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
self.updateAttachedAvatarNodeOffset(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring))
if let swipeToReplyNode = self.swipeToReplyNode {
self.swipeToReplyNode = nil
swipeToReplyNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak swipeToReplyNode] _ in

View File

@ -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)

View File

@ -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<NotificationExceptionsList?, NoError>, privacySettings: Signal<AccountPrivacySettings?, NoError>, archivedStickerPacks: Signal<[ArchivedStickerPackItem]?, NoError>, hasPassport: Signal<Bool, NoError>) -> Signal<PeerInfoScreenData, NoError> {
func peerInfoScreenSettingsData(context: AccountContext, peerId: PeerId, accountsAndPeers: Signal<[(AccountContext, Peer, Int32)], NoError>, activeSessionsContextAndCount: Signal<(ActiveSessionsContext, Int, WebSessionsContext)?, NoError>, notificationExceptions: Signal<NotificationExceptionsList?, NoError>, privacySettings: Signal<AccountPrivacySettings?, NoError>, archivedStickerPacks: Signal<[ArchivedStickerPackItem]?, NoError>, hasPassport: Signal<Bool, NoError>) -> Signal<PeerInfoScreenData, NoError> {
let preferences = context.sharedContext.accountManager.sharedData(keys: [
SharedDataKeys.proxySettings,
ApplicationSpecificSharedDataKeys.inAppNotificationSettings,

View File

@ -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<NotificationExceptionsList?>()
private let privacySettings = Promise<AccountPrivacySettings?>()
@ -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)
}
}

View File

@ -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)
})
}

View File

@ -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<Data?, NoError>
private let voipNotificationToken: Signal<Data?, NoError>
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<AccountRecordId>()
@ -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<AddedAccountResult, NoError> 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 {
@ -560,16 +569,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<AccountWithInfo?, NoError> in
let peerViewKey: PostboxViewKey = .peer(peerId: account.peerId, components: [])
return account.postbox.combinedView(keys: [peerViewKey])
return combineLatest(accounts.map { _, context, _ -> Signal<AccountWithInfo?, NoError> 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
})
@ -580,7 +589,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
accountsWithInfoResult.append(info)
}
}
return (primary?.id, accountsWithInfoResult)
return (primary?.account.id, accountsWithInfoResult)
}
})
@ -609,7 +618,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
@ -624,7 +633,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
@ -691,7 +700,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 {
@ -753,9 +762,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]))
@ -764,10 +773,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 {
@ -835,24 +844,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<Never, NoError> in
let (primary, activeAccounts, _) = activeAccountsAndInfo
var applied: [Signal<Never, NoError>] = []
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 = []
@ -864,14 +873,14 @@ public final class SharedAccountContextImpl: SharedAccountContext {
let appliedAps: Signal<Never, NoError>
let appliedVoip: Signal<Never, NoError>
if !activeProductionUserIds.contains(account.peerId.id) && !activeTestingUserIds.contains(account.peerId.id) {
if !activeProductionUserIds.contains(account.account.peerId.id) && !activeTestingUserIds.contains(account.account.peerId.id) {
appliedAps = self.apsNotificationToken
|> distinctUntilChanged(isEqual: { $0 == $1 })
|> mapToSignal { token -> Signal<Never, NoError> 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 })
@ -879,7 +888,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
@ -894,7 +903,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 })
@ -902,7 +911,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)
}
}
@ -921,7 +930,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
}

View File

@ -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

View File

@ -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))
}
}

@ -1 +1 @@
Subproject commit ef796349808b187b80b75ea1876b940f2882fcbb
Subproject commit 9c00e5d67f39cbfc8cc764b3c87c0f526eb0040f