mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Merge branch 'master' into experimental-2
This commit is contained in:
commit
cdc2ce8447
@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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 }
|
||||
|
@ -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 }
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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?
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user