Added new call P2P settings

This commit is contained in:
Ilya Laktyushin 2018-11-03 23:32:29 +04:00
parent 3cb29347d1
commit ef533b02a6
28 changed files with 426 additions and 207 deletions

View File

@ -682,6 +682,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
if let continueActionButtonLayout = continueActionButtonLayout {
let (size, apply) = continueActionButtonLayout(boundingWidth - 13.0 - insets.right)
actionButtonSizeAndApply = (size, apply)
adjustedBoundingSize.width = max(adjustedBoundingSize.width, insets.left + size.width + insets.right)
adjustedBoundingSize.height += 7.0 + size.height
}

View File

@ -22,7 +22,7 @@ final class InstantPageAnchorItem: InstantPageItem {
func drawInTile(context: CGContext) {
}
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (Int, Int) -> Void, updateDetailsOpened: @escaping (Int, Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
return nil
}

View File

@ -25,7 +25,7 @@ final class InstantPageArticleItem: InstantPageItem {
self.webpageId = webpageId
}
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (Int, Int) -> Void, updateDetailsOpened: @escaping (Int, Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
return InstantPageArticleNode(account: account, webPage: self.webPage, strings: strings, theme: theme, title: self.title, description: self.description, cover: self.cover, url: self.url, webpageId: self.webpageId, openUrl: openUrl)
}

View File

@ -18,7 +18,7 @@ final class InstantPageAudioItem: InstantPageItem {
self.medias = [media]
}
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (Int, Int) -> Void, updateDetailsOpened: @escaping (Int, Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
return InstantPageAudioNode(account: account, strings: strings, theme: theme, webPage: self.webpage, media: self.media, openMedia: openMedia)
}

View File

@ -43,8 +43,9 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
var visibleTiles: [Int: InstantPageTileNode] = [:]
var visibleItemsWithNodes: [Int: InstantPageNode] = [:]
var currentWebEmbedHeights: [Int : Int] = [:]
var currentOpenedDetails: [Int : Bool]? = [:]
var currentWebEmbedHeights: [Int : CGFloat] = [:]
var currentExpandedDetails: [Int : Bool]?
var currentDetailsItems: [InstantPageDetailsItem] = []
var previousContentOffset: CGPoint?
var isDeceleratingBecauseOfDragging = false
@ -196,7 +197,8 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
if let strongSelf = self {
if let currentLayout = strongSelf.currentLayout {
for item in currentLayout.items {
if item.frame.contains(point) {
let frame = strongSelf.effectiveFrameForItem(item)
if frame.contains(point) {
if item is InstantPagePeerReferenceItem {
return .fail
} else if item is InstantPageAudioItem {
@ -319,10 +321,11 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
let currentLayoutTiles = instantPageTilesFromLayout(currentLayout, boundingWidth: containerLayout.size.width)
var currentDetailsItems: [InstantPageDetailsItem] = []
var currentLayoutItemsWithNodes: [InstantPageItem] = []
var distanceThresholdGroupCount: [Int : Int] = [:]
var openedDetails: [Int : Bool] = [:]
var expandedDetails: [Int : Bool] = [:]
var itemIndex = -1
for item in currentLayout.items {
@ -340,26 +343,29 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
if let detailsItem = item as? InstantPageDetailsItem {
openedDetails[itemIndex] = detailsItem.open
expandedDetails[itemIndex] = detailsItem.initiallyExpanded
}
}
if let item = item as? InstantPageDetailsItem {
currentDetailsItems.append(item)
}
}
if self.currentOpenedDetails == nil {
self.currentOpenedDetails = openedDetails
if self.currentExpandedDetails == nil {
self.currentExpandedDetails = expandedDetails
}
self.currentLayout = currentLayout
self.currentLayoutTiles = currentLayoutTiles
self.currentLayoutItemsWithNodes = currentLayoutItemsWithNodes
self.currentDetailsItems = currentDetailsItems
self.distanceThresholdGroupCount = distanceThresholdGroupCount
self.scrollNode.view.contentSize = currentLayout.contentSize
self.scrollNodeFooter.frame = CGRect(origin: CGPoint(x: 0.0, y: currentLayout.contentSize.height), size: CGSize(width: containerLayout.size.width, height: 2000.0))
}
func updateVisibleItems() {
func updateVisibleItems(animated: Bool = false) {
guard let theme = self.theme else {
return
}
@ -370,6 +376,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
let visibleBounds = self.scrollNode.view.bounds
var topNode: ASDisplayNode?
let topTileNode = topNode
if let scrollSubnodes = self.scrollNode.subnodes {
for node in scrollSubnodes.reversed() {
if let node = node as? InstantPageTileNode {
@ -379,34 +386,27 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
}
var tileIndex = -1
for tile in self.currentLayoutTiles {
tileIndex += 1
var tileVisibleFrame = tile.frame
tileVisibleFrame.origin.y -= 400.0
tileVisibleFrame.size.height += 400.0 * 2.0
if tileVisibleFrame.intersects(visibleBounds) {
visibleTileIndices.insert(tileIndex)
if visibleTiles[tileIndex] == nil {
let tileNode = InstantPageTileNode(tile: tile, backgroundColor: theme.pageBackgroundColor)
tileNode.frame = tile.frame
if let topNode = topNode {
self.scrollNode.insertSubnode(tileNode, aboveSubnode: topNode)
} else {
self.scrollNode.insertSubnode(tileNode, at: 0)
}
topNode = tileNode
self.visibleTiles[tileIndex] = tileNode
}
}
}
var collapseOffset: CGFloat = 0.0
let transition: ContainedViewLayoutTransition
if animated {
transition = .animated(duration: 0.3, curve: .spring)
} else {
transition = .immediate
}
var itemIndex = -1
var embedIndex = -1
var detailsIndex = -1
for item in self.currentLayoutItemsWithNodes {
itemIndex += 1
if item is InstantPageWebEmbedItem {
embedIndex += 1
}
if item is InstantPageDetailsItem {
detailsIndex += 1
}
var itemThreshold: CGFloat = 0.0
if let group = item.distanceThresholdGroup() {
var count: Int = 0
@ -421,8 +421,8 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
thresholdedItemFrame.origin.y -= itemThreshold
thresholdedItemFrame.size.height += itemThreshold * 2.0
if let opened = self.currentOpenedDetails?[itemIndex], !opened {
collapseOffset = itemFrame.height - 44.0
if let expanded = self.currentExpandedDetails?[detailsIndex], !expanded {
collapseOffset += itemFrame.height - 44.0
itemFrame = CGRect(origin: itemFrame.origin, size: CGSize(width: itemFrame.width, height: 44.0))
}
@ -439,16 +439,19 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
if itemNode == nil {
let itemIndex = itemIndex
let embedIndex = embedIndex
let detailsIndex = detailsIndex
if let itemNode = item.node(account: self.account, strings: self.strings, theme: theme, openMedia: { [weak self] media in
self?.openMedia(media)
}, openPeer: { [weak self] peerId in
self?.openPeer(peerId)
}, openUrl: { [weak self] url in
self?.openUrl(url)
}, updateWebEmbedHeight: { [weak self] key, height in
self?.updateWebEmbedHeight(key, height)
}, updateDetailsOpened: { [weak self] key, opened in
self?.updateDetailsOpened(key, opened)
}, updateWebEmbedHeight: { [weak self] height in
self?.updateWebEmbedHeight(embedIndex, height)
}, updateDetailsExpanded: { [weak self] expanded in
self?.updateDetailsExpanded(detailsIndex, expanded)
}) {
itemNode.frame = itemFrame
if let topNode = topNode {
@ -461,12 +464,62 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
} else {
if (itemNode as! ASDisplayNode).frame != itemFrame {
let previousFrame = (itemNode as! ASDisplayNode).frame
(itemNode as! ASDisplayNode).frame = itemFrame
transition.animateFrame(node: (itemNode as! ASDisplayNode), from: previousFrame)
}
}
}
}
topNode = topTileNode
var tileIndex = -1
for tile in self.currentLayoutTiles {
tileIndex += 1
var tileFrame = tile.frame
if tileIndex > 0 {
tileFrame = tileFrame.offsetBy(dx: 0.0, dy: -collapseOffset)
}
var tileVisibleFrame = tileFrame
tileVisibleFrame.origin.y -= 400.0
tileVisibleFrame.size.height += 400.0 * 2.0
if tileVisibleFrame.intersects(visibleBounds) || animated {
visibleTileIndices.insert(tileIndex)
if visibleTiles[tileIndex] == nil {
let tileNode = InstantPageTileNode(tile: tile, backgroundColor: theme.pageBackgroundColor)
tileNode.frame = tile.frame
if let topNode = topNode {
self.scrollNode.insertSubnode(tileNode, aboveSubnode: topNode)
} else {
self.scrollNode.insertSubnode(tileNode, at: 0)
}
topNode = tileNode
self.visibleTiles[tileIndex] = tileNode
} else {
if visibleTiles[tileIndex]!.frame != tileFrame {
let previousFrame = visibleTiles[tileIndex]!.frame
visibleTiles[tileIndex]!.frame = tileFrame
transition.animateFrame(node: visibleTiles[tileIndex]!, from: previousFrame)
}
}
}
}
if let currentLayout = self.currentLayout, collapseOffset > 0.0 {
let effectiveContentHeight = currentLayout.contentSize.height - collapseOffset
if effectiveContentHeight != self.scrollNode.view.contentSize.height {
transition.animateView {
self.scrollNode.view.contentSize = CGSize(width: currentLayout.contentSize.width, height: effectiveContentHeight)
}
let previousFrame = self.scrollNodeFooter.frame
self.scrollNodeFooter.frame = CGRect(origin: CGPoint(x: 0.0, y: effectiveContentHeight), size: CGSize(width: previousFrame.width, height: 2000.0))
transition.animateFrame(node: self.scrollNodeFooter, from: previousFrame)
}
}
var removeTileIndices: [Int] = []
for (index, tileNode) in self.visibleTiles {
if !visibleTileIndices.contains(index) {
@ -644,16 +697,41 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
return contentOffset
}
private func effectiveFrameForItem(_ item: InstantPageItem) -> CGRect {
let layoutOrigin = item.frame.origin
var origin = layoutOrigin
for item in self.currentDetailsItems {
let expanded = self.currentExpandedDetails?[item.index] ?? item.initiallyExpanded
if !expanded && layoutOrigin.y >= item.frame.maxY {
let offset = 44.0 - item.frame.height
origin.y += offset
}
}
if let item = item as? InstantPageDetailsItem {
let expanded = self.currentExpandedDetails?[item.index] ?? item.initiallyExpanded
return CGRect(origin: origin, size: CGSize(width: item.frame.width, height: expanded ? item.frame.height : 44.0))
} else {
return CGRect(origin: origin, size: item.frame.size)
}
}
private func textItemAtLocation(_ location: CGPoint) -> (InstantPageTextItem, CGPoint)? {
if let currentLayout = self.currentLayout {
for item in currentLayout.items {
if let item = item as? InstantPageTextItem, item.selectable, item.frame.contains(location) {
let frame = self.effectiveFrameForItem(item)
if frame.contains(location) {
if let item = item as? InstantPageTextItem, item.selectable {
return (item, CGPoint())
} else if let item = item as? InstantPageTableItem, item.frame.contains(location) {
} else if let item = item as? InstantPageTableItem {
let contentOffset = tableContentOffset(item: item)
if let (textItem, parentOffset) = item.textItemAtLocation(location.offsetBy(dx: -item.frame.minX + contentOffset.x, dy: -item.frame.minY)) {
return (textItem, item.frame.origin.offsetBy(dx: parentOffset.x - contentOffset.x, dy: parentOffset.y))
}
} else if let item = item as? InstantPageDetailsItem {
}
}
}
}
@ -706,7 +784,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
])])
self.present(actionSheet, nil)
} else if let (item, parentOffset) = self.textItemAtLocation(location) {
let textNodeFrame = item.frame
let textNodeFrame = effectiveFrameForItem(item)
var itemRects = item.lineRects()
for i in 0 ..< itemRects.count {
itemRects[i] = itemRects[i].offsetBy(dx: parentOffset.x + textNodeFrame.minX, dy: parentOffset.y + textNodeFrame.minY).insetBy(dx: -2.0, dy: -2.0)
@ -812,7 +890,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
case let .withBotStartPayload(botStart):
if let navigationController = strongSelf.getNavigationController() {
navigateToChatController(navigationController: navigationController, account: strongSelf.account, chatLocation: .peer(peerId), botStart: botStart)
navigateToChatController(navigationController: navigationController, account: strongSelf.account, chatLocation: .peer(peerId), botStart: botStart, keepStack: .always)
}
case .info:
let _ = (strongSelf.account.postbox.loadedPeerWithId(peerId)
@ -910,13 +988,13 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
}
private func updateWebEmbedHeight(_ key: Int, _ height: Int) {
let currentHeight = self.currentWebEmbedHeights[key]
private func updateWebEmbedHeight(_ index: Int, _ height: CGFloat) {
let currentHeight = self.currentWebEmbedHeights[index]
if height != currentHeight {
if let currentHeight = currentHeight, currentHeight > height {
return
}
self.currentWebEmbedHeights[key] = height
self.currentWebEmbedHeights[index] = height
let signal: Signal<Void, NoError> = (.complete() |> delay(0.08, queue: Queue.mainQueue()))
self.updateLayoutDisposable.set(signal.start(completed: { [weak self] in
@ -928,13 +1006,12 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
}
private func updateDetailsOpened(_ index: Int, _ opened: Bool) {
if var currentOpenedDetails = self.currentOpenedDetails {
currentOpenedDetails[index] = opened
self.currentOpenedDetails = currentOpenedDetails
private func updateDetailsExpanded(_ index: Int, _ expanded: Bool) {
if var currentExpandedDetails = self.currentExpandedDetails {
currentExpandedDetails[index] = expanded
self.currentExpandedDetails = currentExpandedDetails
}
//self.updateLayout()
self.updateVisibleItems()
self.updateVisibleItems(animated: true)
}
private func presentSettings() {

View File

@ -10,20 +10,23 @@ final class InstantPageDetailsItem: InstantPageItem {
let title: NSAttributedString
let items: [InstantPageItem]
let safeInset: CGFloat
let rtl: Bool
var initiallyExpanded: Bool
let index: Int
var open: Bool
init(frame: CGRect, title: NSAttributedString, items: [InstantPageItem], rtl: Bool, open: Bool) {
init(frame: CGRect, title: NSAttributedString, items: [InstantPageItem], safeInset: CGFloat, rtl: Bool, initiallyExpanded: Bool, index: Int) {
self.frame = frame
self.title = title
self.items = items
self.safeInset = safeInset
self.rtl = rtl
self.open = open
self.initiallyExpanded = initiallyExpanded
self.index = index
}
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (Int, Int) -> Void, updateDetailsOpened: @escaping (Int, Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
return InstantPageDetailsNode(account: account, strings: strings, theme: theme, item: self, updateDetailsOpened: updateDetailsOpened)
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
return InstantPageDetailsNode(account: account, strings: strings, theme: theme, item: self, updateDetailsExpanded: updateDetailsExpanded)
}
func matchesAnchor(_ anchor: String) -> Bool {
@ -58,6 +61,6 @@ final class InstantPageDetailsItem: InstantPageItem {
}
}
func layoutDetailsItem(theme: InstantPageTheme, title: NSAttributedString, boundingWidth: CGFloat, items: [InstantPageItem], contentSize: CGSize, rtl: Bool, open: Bool) -> InstantPageDetailsItem {
return InstantPageDetailsItem(frame: CGRect(x: 0.0, y: 0.0, width: boundingWidth, height: contentSize.height + 44.0), title: title, items: items, rtl: rtl, open: open)
func layoutDetailsItem(theme: InstantPageTheme, title: NSAttributedString, boundingWidth: CGFloat, items: [InstantPageItem], contentSize: CGSize, safeInset: CGFloat, rtl: Bool, initiallyExpanded: Bool, index: Int) -> InstantPageDetailsItem {
return InstantPageDetailsItem(frame: CGRect(x: 0.0, y: 0.0, width: boundingWidth, height: contentSize.height + 44.0), title: title, items: items, safeInset: safeInset, rtl: rtl, initiallyExpanded: initiallyExpanded, index: index)
}

View File

@ -140,9 +140,9 @@ final class InstantPageDetailsContentNode : ASDisplayNode {
//self?.openPeer(peerId)
}, openUrl: { [weak self] url in
//self?.openUrl(url)
}, updateWebEmbedHeight: { [weak self] key, height in
}, updateWebEmbedHeight: { [weak self] height in
//self?.updateWebEmbedHeight(key, height)
}, updateDetailsOpened: { _, _ in
}, updateDetailsExpanded: { _ in
}) {
itemNode.frame = item.frame
if let topNode = topNode {
@ -206,16 +206,16 @@ final class InstantPageDetailsNode: ASDisplayNode, InstantPageNode {
private let separatorNode: ASDisplayNode
private let contentNode: InstantPageDetailsContentNode
let updateOpened: (Int, Bool) -> Void
var opened: Bool
private let updateExpanded: (Bool) -> Void
var expanded: Bool
init(account: Account, strings: PresentationStrings, theme: InstantPageTheme, item: InstantPageDetailsItem, updateDetailsOpened: @escaping (Int, Bool) -> Void) {
init(account: Account, strings: PresentationStrings, theme: InstantPageTheme, item: InstantPageDetailsItem, updateDetailsExpanded: @escaping (Bool) -> Void) {
self.account = account
self.strings = strings
self.theme = theme
self.item = item
self.updateOpened = updateDetailsOpened
self.updateExpanded = updateDetailsExpanded
let frame = item.frame
@ -241,12 +241,12 @@ final class InstantPageDetailsNode: ASDisplayNode, InstantPageNode {
}
self.titleTile.items.append(contentsOf: titleItems)
self.arrowNode = InstantPageDetailsArrowNode(color: theme.controlColor, open: item.open)
self.arrowNode = InstantPageDetailsArrowNode(color: theme.controlColor, open: item.initiallyExpanded)
self.separatorNode = ASDisplayNode()
self.contentNode = InstantPageDetailsContentNode(account: account, strings: strings, theme: theme, items: item.items, contentSize: CGSize(width: item.frame.width, height: item.frame.height))
self.opened = item.open
self.expanded = item.initiallyExpanded
super.init()
@ -259,9 +259,6 @@ final class InstantPageDetailsNode: ASDisplayNode, InstantPageNode {
self.addSubnode(self.separatorNode)
self.addSubnode(self.contentNode)
let lineSize = CGSize(width: frame.width - detailsInset, height: UIScreenPixel)
self.separatorNode.frame = CGRect(origin: CGPoint(x: item.rtl ? 0.0 : detailsInset, y: detailsHeaderHeight - lineSize.height), size: lineSize)
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
self.buttonNode.highligthedChanged = { [weak self] highlighted in
@ -287,26 +284,38 @@ final class InstantPageDetailsNode: ASDisplayNode, InstantPageNode {
}
@objc func buttonPressed() {
self.setOpened(!self.opened, animated: true)
self.setExpanded(!self.expanded, animated: true)
}
func setOpened(_ opened: Bool, animated: Bool) {
self.opened = opened
self.arrowNode.setOpen(opened, animated: animated)
self.updateOpened(0, opened)
func setExpanded(_ expanded: Bool, animated: Bool) {
self.expanded = expanded
self.arrowNode.setOpen(expanded, animated: animated)
self.updateExpanded(expanded)
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
let size = layout.size
let inset = detailsInset + self.item.safeInset
let lineSize = CGSize(width: frame.width - inset, height: UIScreenPixel)
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: item.rtl ? 0.0 : inset, y: size.height - lineSize.height), size: lineSize))
}
override func layout() {
super.layout()
let size = self.bounds.size
let inset = detailsInset + self.item.safeInset
self.titleTileNode.frame = self.titleTile.frame
self.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: detailsHeaderHeight + UIScreenPixel))
self.buttonNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: detailsHeaderHeight))
self.arrowNode.frame = CGRect(x: detailsInset, y: floorToScreenPixels((detailsHeaderHeight - 8.0) / 2.0) + 1.0, width: 13.0, height: 8.0)
self.arrowNode.frame = CGRect(x: inset, y: floorToScreenPixels((detailsHeaderHeight - 8.0) / 2.0) + 1.0, width: 13.0, height: 8.0)
self.contentNode.frame = CGRect(x: 0.0, y: detailsHeaderHeight, width: size.width, height: self.item.frame.height - detailsHeaderHeight)
let lineSize = CGSize(width: frame.width - inset, height: UIScreenPixel)
self.separatorNode.frame = CGRect(origin: CGPoint(x: item.rtl ? 0.0 : inset, y: size.height - lineSize.height), size: lineSize)
self.contentNode.updateVisibleItems()
}
@ -437,9 +446,9 @@ final class InstantPageDetailsArrowNode : ASDisplayNode {
context.setLineCap(.round)
context.setLineWidth(2.0)
context.move(to: CGPoint(x: 1.0, y: 6.0 - 5.0 * parameters.progress))
context.addLine(to: CGPoint(x: 6.0, y: 1.0 + 5.0 * parameters.progress))
context.addLine(to: CGPoint(x: 11.0, y: 6.0 - 5.0 * parameters.progress))
context.move(to: CGPoint(x: 1.0, y: 1.0 + 5.0 * parameters.progress))
context.addLine(to: CGPoint(x: 6.0, y: 6.0 - 5.0 * parameters.progress))
context.addLine(to: CGPoint(x: 11.0, y: 1.0 + 5.0 * parameters.progress))
context.strokePath()
}
}

View File

@ -15,7 +15,7 @@ final class InstantPageFeedbackItem: InstantPageItem {
self.webPage = webPage
}
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (Int, Int) -> Void, updateDetailsOpened: @escaping (Int, Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
return InstantPageFeedbackNode(account: account, strings: strings, theme: theme, webPage: self.webPage, openUrl: openUrl)
}

View File

@ -41,7 +41,7 @@ final class InstantPageImageItem: InstantPageItem {
self.fit = fit
}
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (Int, Int) -> Void, updateDetailsOpened: @escaping (Int, Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
return InstantPageImageNode(account: account, theme: theme, webPage: self.webPage, media: self.media, attributes: self.attributes, url: self.url, interactive: self.interactive, roundCorners: self.roundCorners, fit: self.fit, openMedia: openMedia, openUrl: openUrl)
}

View File

@ -7,7 +7,8 @@ import SwiftSignalKit
final class InstantPageImageNode: ASDisplayNode, InstantPageNode {
private let account: Account
private let theme: InstantPageTheme
private let webPage: TelegramMediaWebpage
private var theme: InstantPageTheme
let media: InstantPageMedia
let attributes: [InstantPageImageAttribute]
let url: InstantPageUrlItem?
@ -24,9 +25,12 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode {
private var fetchedDisposable = MetaDisposable()
private var themeUpdated: Bool = false
init(account: Account, theme: InstantPageTheme, webPage: TelegramMediaWebpage, media: InstantPageMedia, attributes: [InstantPageImageAttribute], url: InstantPageUrlItem? = nil, interactive: Bool, roundCorners: Bool, fit: Bool, openMedia: @escaping (InstantPageMedia) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void = { _ in }) {
self.account = account
self.theme = theme
self.webPage = webPage
self.media = media
self.attributes = attributes
self.url = url
@ -88,6 +92,19 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode {
}
func update(strings: PresentationStrings, theme: InstantPageTheme) {
if self.theme.imageEmptyColor != theme.imageEmptyColor {
self.theme = theme
self.themeUpdated = true
self.setNeedsLayout()
if let file = self.media.media as? TelegramMediaFile {
let fileReference = FileMediaReference.webPage(webPage: WebpageReference(webPage), media: file)
if file.mimeType.hasPrefix("image/") {
_ = freeMediaFileInteractiveFetched(account: self.account, fileReference: fileReference).start()
self.imageNode.setSignal(chatMessageImageFile(account: self.account, fileReference: fileReference, thumbnail: false, fetched: true))
}
}
}
}
override func layout() {
@ -95,8 +112,9 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode {
let size = self.bounds.size
if self.currentSize != size {
if self.currentSize != size || self.themeUpdated {
self.currentSize = size
self.themeUpdated = false
self.imageNode.frame = CGRect(origin: CGPoint(), size: size)

View File

@ -10,7 +10,7 @@ protocol InstantPageItem {
func matchesAnchor(_ anchor: String) -> Bool
func drawInTile(context: CGContext)
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (Int, Int) -> Void, updateDetailsOpened: @escaping (Int, Bool) -> Void) -> (InstantPageNode & ASDisplayNode)?
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void) -> (InstantPageNode & ASDisplayNode)?
func matchesNode(_ node: InstantPageNode) -> Bool
func linkSelectionRects(at point: CGPoint) -> [CGRect]

View File

@ -41,7 +41,7 @@ private func setupStyleStack(_ stack: InstantPageTextStyleStack, theme: InstantP
}
}
func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: InstantPageBlock, boundingWidth: CGFloat, horizontalInset: CGFloat, safeInset: CGFloat, isCover: Bool, previousItems: [InstantPageItem], fillToWidthAndHeight: Bool, media: [MediaId: Media], mediaIndexCounter: inout Int, embedIndexCounter: inout Int, theme: InstantPageTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, webEmbedHeights: [Int : Int] = [:]) -> InstantPageLayout {
func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: InstantPageBlock, boundingWidth: CGFloat, horizontalInset: CGFloat, safeInset: CGFloat, isCover: Bool, previousItems: [InstantPageItem], fillToWidthAndHeight: Bool, media: [MediaId: Media], mediaIndexCounter: inout Int, embedIndexCounter: inout Int, detailsIndexCounter: inout Int, theme: InstantPageTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, webEmbedHeights: [Int : CGFloat] = [:]) -> InstantPageLayout {
let layoutCaption: (InstantPageCaption, CGSize) -> ([InstantPageItem], CGSize) = { caption, contentSize in
var items: [InstantPageItem] = []
@ -80,7 +80,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins
switch block {
case let .cover(block):
return layoutInstantPageBlock(webpage: webpage, rtl: rtl, block: block, boundingWidth: boundingWidth, horizontalInset: horizontalInset, safeInset: safeInset, isCover: true, previousItems:previousItems, fillToWidthAndHeight: fillToWidthAndHeight, media: media, mediaIndexCounter: &mediaIndexCounter, embedIndexCounter: &embedIndexCounter, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, webEmbedHeights: webEmbedHeights)
return layoutInstantPageBlock(webpage: webpage, rtl: rtl, block: block, boundingWidth: boundingWidth, horizontalInset: horizontalInset, safeInset: safeInset, isCover: true, previousItems:previousItems, fillToWidthAndHeight: fillToWidthAndHeight, media: media, mediaIndexCounter: &mediaIndexCounter, embedIndexCounter: &embedIndexCounter, detailsIndexCounter: &detailsIndexCounter, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, webEmbedHeights: webEmbedHeights)
case let .title(text):
let styleStack = InstantPageTextStyleStack()
setupStyleStack(styleStack, theme: theme, category: .header, link: false)
@ -265,7 +265,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins
var previousBlock: InstantPageBlock?
var originY: CGFloat = 0.0
for subBlock in blocks {
let subLayout = layoutInstantPageBlock(webpage: webpage, rtl: rtl, block: subBlock, boundingWidth: boundingWidth - horizontalInset * 2.0 - indexSpacing - maxIndexWidth, horizontalInset: 0.0, safeInset: 0.0, isCover: false, previousItems: listItems, fillToWidthAndHeight: false, media: media, mediaIndexCounter: &mediaIndexCounter, embedIndexCounter: &embedIndexCounter, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, webEmbedHeights: webEmbedHeights)
let subLayout = layoutInstantPageBlock(webpage: webpage, rtl: rtl, block: subBlock, boundingWidth: boundingWidth - horizontalInset * 2.0 - indexSpacing - maxIndexWidth, horizontalInset: 0.0, safeInset: 0.0, isCover: false, previousItems: listItems, fillToWidthAndHeight: false, media: media, mediaIndexCounter: &mediaIndexCounter, embedIndexCounter: &embedIndexCounter, detailsIndexCounter: &detailsIndexCounter, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, webEmbedHeights: webEmbedHeights)
let spacing: CGFloat = previousBlock != nil ? spacingBetweenBlocks(upper: previousBlock, lower: subBlock) : 0.0
let blockItems = subLayout.flattenedItemsWithOrigin(CGPoint(x: horizontalInset + indexSpacing + maxIndexWidth, y: contentSize.height + spacing))
@ -454,7 +454,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins
nextItemOrigin.x = 0.0
nextItemOrigin.y += itemSize + spacing
}
let subLayout = layoutInstantPageBlock(webpage: webpage, rtl: rtl, block: subItem, boundingWidth: itemSize, horizontalInset: 0.0, safeInset: 0.0, isCover: false, previousItems: items, fillToWidthAndHeight: true, media: media, mediaIndexCounter: &mediaIndexCounter, embedIndexCounter: &embedIndexCounter, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, webEmbedHeights: webEmbedHeights)
let subLayout = layoutInstantPageBlock(webpage: webpage, rtl: rtl, block: subItem, boundingWidth: itemSize, horizontalInset: 0.0, safeInset: 0.0, isCover: false, previousItems: items, fillToWidthAndHeight: true, media: media, mediaIndexCounter: &mediaIndexCounter, embedIndexCounter: &embedIndexCounter, detailsIndexCounter: &detailsIndexCounter, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, webEmbedHeights: webEmbedHeights)
items.append(contentsOf: subLayout.flattenedItemsWithOrigin(nextItemOrigin))
nextItemOrigin.x += itemSize + spacing
}
@ -522,7 +522,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins
var previousBlock: InstantPageBlock?
for subBlock in blocks {
let subLayout = layoutInstantPageBlock(webpage: webpage, rtl: rtl, block: subBlock, boundingWidth: boundingWidth - horizontalInset * 2.0 - lineInset, horizontalInset: 0.0, safeInset: 0.0, isCover: false, previousItems: items, fillToWidthAndHeight: false, media: media, mediaIndexCounter: &mediaIndexCounter, embedIndexCounter: &embedIndexCounter, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, webEmbedHeights: webEmbedHeights)
let subLayout = layoutInstantPageBlock(webpage: webpage, rtl: rtl, block: subBlock, boundingWidth: boundingWidth - horizontalInset * 2.0 - lineInset, horizontalInset: 0.0, safeInset: 0.0, isCover: false, previousItems: items, fillToWidthAndHeight: false, media: media, mediaIndexCounter: &mediaIndexCounter, embedIndexCounter: &embedIndexCounter, detailsIndexCounter: &detailsIndexCounter, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, webEmbedHeights: webEmbedHeights)
let spacing = spacingBetweenBlocks(upper: previousBlock, lower: subBlock)
let blockItems = subLayout.flattenedItemsWithOrigin(CGPoint(x: horizontalInset + lineInset, y: contentSize.height + spacing))
@ -576,6 +576,10 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins
if stretchToWidth {
embedBoundingWidth = boundingWidth
}
let embedIndex = embedIndexCounter
embedIndexCounter += 1
let size: CGSize
if let dimensions = dimensions {
if dimensions.width.isLessThanOrEqualTo(0.0) {
@ -584,14 +588,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins
size = dimensions.aspectFitted(CGSize(width: embedBoundingWidth, height: embedBoundingWidth))
}
} else {
var hash: Int?
if let url = url {
hash = url.hashValue
} else if let html = html {
hash = html.hashValue
}
if let hash = hash, let height = webEmbedHeights[hash] {
if let height = webEmbedHeights[embedIndex] {
size = CGSize(width: embedBoundingWidth, height: CGFloat(height))
} else {
size = CGSize(width: embedBoundingWidth, height: 44.0)
@ -619,7 +616,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins
}
if let peer = peer {
let item = InstantPagePeerReferenceItem(frame: CGRect(origin: CGPoint(), size: CGSize(width: boundingWidth, height: 40.0)), initialPeer: peer, rtl: rtl || previousItemHasRTL)
let item = InstantPagePeerReferenceItem(frame: CGRect(origin: CGPoint(), size: CGSize(width: boundingWidth, height: 40.0)), initialPeer: peer, safeInset: safeInset, rtl: rtl || previousItemHasRTL)
items.append(item)
contentSize.height += 40.0
}
@ -670,13 +667,16 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins
items.append(tableItem)
return InstantPageLayout(origin: CGPoint(), contentSize: contentSize, items: items)
case let .details(title, blocks, open):
case let .details(title, blocks, expanded):
var contentSize = CGSize(width: boundingWidth, height: 0.0)
var subitems: [InstantPageItem] = []
let detailsIndex = detailsIndexCounter
detailsIndexCounter += 1
var previousBlock: InstantPageBlock?
for subBlock in blocks {
let subLayout = layoutInstantPageBlock(webpage: webpage, rtl: rtl, block: subBlock, boundingWidth: boundingWidth - horizontalInset * 2.0, horizontalInset: 0.0, safeInset: 0.0, isCover: false, previousItems: subitems, fillToWidthAndHeight: false, media: media, mediaIndexCounter: &mediaIndexCounter, embedIndexCounter: &embedIndexCounter, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, webEmbedHeights: webEmbedHeights)
let subLayout = layoutInstantPageBlock(webpage: webpage, rtl: rtl, block: subBlock, boundingWidth: boundingWidth - horizontalInset * 2.0, horizontalInset: 0.0, safeInset: 0.0, isCover: false, previousItems: subitems, fillToWidthAndHeight: false, media: media, mediaIndexCounter: &mediaIndexCounter, embedIndexCounter: &embedIndexCounter, detailsIndexCounter: &detailsIndexCounter, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, webEmbedHeights: webEmbedHeights)
let spacing = spacingBetweenBlocks(upper: previousBlock, lower: subBlock)
let blockItems = subLayout.flattenedItemsWithOrigin(CGPoint(x: horizontalInset, y: contentSize.height + spacing))
@ -690,7 +690,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins
let styleStack = InstantPageTextStyleStack()
setupStyleStack(styleStack, theme: theme, category: .paragraph, link: false)
let detailsItem = layoutDetailsItem(theme: theme, title: attributedStringForRichText(title, styleStack: styleStack), boundingWidth: boundingWidth, items: subitems, contentSize: contentSize, rtl: rtl, open: open)
let detailsItem = layoutDetailsItem(theme: theme, title: attributedStringForRichText(title, styleStack: styleStack), boundingWidth: boundingWidth, items: subitems, contentSize: contentSize, safeInset: safeInset, rtl: rtl, initiallyExpanded: expanded, index: detailsIndex)
return InstantPageLayout(origin: CGPoint(), contentSize: detailsItem.frame.size, items: [detailsItem])
case let .relatedArticles(title, articles):
@ -756,7 +756,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins
}
}
func instantPageLayoutForWebPage(_ webPage: TelegramMediaWebpage, boundingWidth: CGFloat, safeInset: CGFloat, strings: PresentationStrings, theme: InstantPageTheme, dateTimeFormat: PresentationDateTimeFormat, webEmbedHeights: [Int : Int] = [:]) -> InstantPageLayout {
func instantPageLayoutForWebPage(_ webPage: TelegramMediaWebpage, boundingWidth: CGFloat, safeInset: CGFloat, strings: PresentationStrings, theme: InstantPageTheme, dateTimeFormat: PresentationDateTimeFormat, webEmbedHeights: [Int : CGFloat] = [:]) -> InstantPageLayout {
var maybeLoadedContent: TelegramMediaWebpageLoadedContent?
if case let .Loaded(content) = webPage.content {
maybeLoadedContent = content
@ -778,10 +778,11 @@ func instantPageLayoutForWebPage(_ webPage: TelegramMediaWebpage, boundingWidth:
var mediaIndexCounter: Int = 0
var embedIndexCounter: Int = 0
var detailsIndexCounter: Int = 0
var previousBlock: InstantPageBlock?
for block in pageBlocks {
let blockLayout = layoutInstantPageBlock(webpage: webPage, rtl: rtl, block: block, boundingWidth: boundingWidth, horizontalInset: 17.0 + safeInset, safeInset: safeInset, isCover: false, previousItems: items, fillToWidthAndHeight: false, media: media, mediaIndexCounter: &mediaIndexCounter, embedIndexCounter: &embedIndexCounter, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, webEmbedHeights: webEmbedHeights)
let blockLayout = layoutInstantPageBlock(webpage: webPage, rtl: rtl, block: block, boundingWidth: boundingWidth, horizontalInset: 17.0 + safeInset, safeInset: safeInset, isCover: false, previousItems: items, fillToWidthAndHeight: false, media: media, mediaIndexCounter: &mediaIndexCounter, embedIndexCounter: &embedIndexCounter, detailsIndexCounter: &detailsIndexCounter, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, webEmbedHeights: webEmbedHeights)
let spacing = spacingBetweenBlocks(upper: previousBlock, lower: block)
let blockItems = blockLayout.flattenedItemsWithOrigin(CGPoint(x: 0.0, y: contentSize.height + spacing))
items.append(contentsOf: blockItems)

View File

@ -9,16 +9,18 @@ final class InstantPagePeerReferenceItem: InstantPageItem {
let medias: [InstantPageMedia] = []
let initialPeer: Peer
let safeInset: CGFloat
let rtl: Bool
init(frame: CGRect, initialPeer: Peer, rtl: Bool) {
init(frame: CGRect, initialPeer: Peer, safeInset: CGFloat, rtl: Bool) {
self.frame = frame
self.initialPeer = initialPeer
self.safeInset = safeInset
self.rtl = rtl
}
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (Int, Int) -> Void, updateDetailsOpened: @escaping (Int, Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
return InstantPagePeerReferenceNode(account: account, strings: strings, theme: theme, initialPeer: self.initialPeer, rtl: self.rtl, openPeer: openPeer)
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
return InstantPagePeerReferenceNode(account: account, strings: strings, theme: theme, initialPeer: self.initialPeer, safeInset: self.safeInset, rtl: self.rtl, openPeer: openPeer)
}
func matchesAnchor(_ anchor: String) -> Bool {
@ -27,7 +29,7 @@ final class InstantPagePeerReferenceItem: InstantPageItem {
func matchesNode(_ node: InstantPageNode) -> Bool {
if let node = node as? InstantPagePeerReferenceNode {
return self.initialPeer.id == node.initialPeer.id
return self.initialPeer.id == node.initialPeer.id && self.safeInset == node.safeInset
} else {
return false
}

View File

@ -44,6 +44,7 @@ private enum JoinState: Equatable {
final class InstantPagePeerReferenceNode: ASDisplayNode, InstantPageNode {
private let account: Account
let initialPeer: Peer
let safeInset: CGFloat
private let rtl: Bool
private var strings: PresentationStrings
private var theme: InstantPageTheme
@ -63,11 +64,12 @@ final class InstantPagePeerReferenceNode: ASDisplayNode, InstantPageNode {
private var joinState: JoinState = .none
init(account: Account, strings: PresentationStrings, theme: InstantPageTheme, initialPeer: Peer, rtl: Bool, openPeer: @escaping (PeerId) -> Void) {
init(account: Account, strings: PresentationStrings, theme: InstantPageTheme, initialPeer: Peer, safeInset: CGFloat, rtl: Bool, openPeer: @escaping (PeerId) -> Void) {
self.account = account
self.strings = strings
self.theme = theme
self.initialPeer = initialPeer
self.safeInset = safeInset
self.rtl = rtl
self.openPeer = openPeer
@ -219,7 +221,7 @@ final class InstantPagePeerReferenceNode: ASDisplayNode, InstantPageNode {
super.layout()
let size = self.bounds.size
let inset: CGFloat = 17.0
let inset: CGFloat = 17.0 + safeInset
self.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(), size: size)
self.buttonNode.frame = CGRect(origin: CGPoint(), size: size)
@ -230,15 +232,15 @@ final class InstantPagePeerReferenceNode: ASDisplayNode, InstantPageNode {
let indicatorSize = self.activityIndicator.measure(size)
if self.rtl {
self.nameNode.frame = CGRect(origin: CGPoint(x: size.width - 17.0 - nameSize.width, y: floor((size.height - nameSize.height) / 2.0)), size: nameSize)
self.joinNode.frame = CGRect(origin: CGPoint(x: 17.0, y: floor((size.height - joinSize.height) / 2.0)), size: joinSize)
self.checkNode.frame = CGRect(origin: CGPoint(x: 17.0, y: floor((size.height - checkSize.height) / 2.0)), size: checkSize)
self.activityIndicator.frame = CGRect(origin: CGPoint(x: 17.0, y: floor((size.height - indicatorSize.height) / 2.0)), size: indicatorSize)
self.nameNode.frame = CGRect(origin: CGPoint(x: size.width - inset - nameSize.width, y: floor((size.height - nameSize.height) / 2.0)), size: nameSize)
self.joinNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((size.height - joinSize.height) / 2.0)), size: joinSize)
self.checkNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((size.height - checkSize.height) / 2.0)), size: checkSize)
self.activityIndicator.frame = CGRect(origin: CGPoint(x: inset, y: floor((size.height - indicatorSize.height) / 2.0)), size: indicatorSize)
} else {
self.nameNode.frame = CGRect(origin: CGPoint(x: 17.0, y: floor((size.height - nameSize.height) / 2.0)), size: nameSize)
self.joinNode.frame = CGRect(origin: CGPoint(x: size.width - 17.0 - joinSize.width, y: floor((size.height - joinSize.height) / 2.0)), size: joinSize)
self.checkNode.frame = CGRect(origin: CGPoint(x: size.width - 17.0 - checkSize.width, y: floor((size.height - checkSize.height) / 2.0)), size: checkSize)
self.activityIndicator.frame = CGRect(origin: CGPoint(x: size.width - 17.0 - indicatorSize.width, y: floor((size.height - indicatorSize.height) / 2.0)), size: indicatorSize)
self.nameNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((size.height - nameSize.height) / 2.0)), size: nameSize)
self.joinNode.frame = CGRect(origin: CGPoint(x: size.width - inset - joinSize.width, y: floor((size.height - joinSize.height) / 2.0)), size: joinSize)
self.checkNode.frame = CGRect(origin: CGPoint(x: size.width - inset - checkSize.width, y: floor((size.height - checkSize.height) / 2.0)), size: checkSize)
self.activityIndicator.frame = CGRect(origin: CGPoint(x: size.width - inset - indicatorSize.width, y: floor((size.height - indicatorSize.height) / 2.0)), size: indicatorSize)
}
}

View File

@ -23,7 +23,7 @@ final class InstantPagePlayableVideoItem: InstantPageItem {
self.interactive = interactive
}
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (Int, Int) -> Void, updateDetailsOpened: @escaping (Int, Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
return InstantPagePlayableVideoNode(account: account, webPage: self.webPage, media: self.media, interactive: self.interactive, openMedia: openMedia)
}

View File

@ -56,7 +56,7 @@ final class InstantPageShapeItem: InstantPageItem {
return false
}
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (Int, Int) -> Void, updateDetailsOpened: @escaping (Int, Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
return nil
}

View File

@ -15,7 +15,7 @@ final class InstantPageSlideshowItem: InstantPageItem {
self.medias = medias
}
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (Int, Int) -> Void, updateDetailsOpened: @escaping (Int, Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
return InstantPageSlideshowNode(account: account, theme: theme, webPage: webPage, medias: self.medias, openMedia: openMedia)
}

View File

@ -166,7 +166,7 @@ final class InstantPageTableItem: InstantPageItem {
return false
}
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (Int, Int) -> Void, updateDetailsOpened: @escaping (Int, Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
return InstantPageTableNode(item: self, account: account, strings: strings, theme: theme)
}
@ -227,7 +227,7 @@ final class InstantPageTableContentNode: ASDisplayNode {
for cell in self.item.cells {
for item in cell.additionalItems {
if item.wantsNode {
if let node = item.node(account: account, strings: strings, theme: theme, openMedia: { _ in }, openPeer: { _ in }, openUrl: { _ in}, updateWebEmbedHeight: { _, _ in }, updateDetailsOpened: { _, _ in }) {
if let node = item.node(account: account, strings: strings, theme: theme, openMedia: { _ in }, openPeer: { _ in }, openUrl: { _ in}, updateWebEmbedHeight: { _ in }, updateDetailsExpanded: { _ in }) {
node.frame = item.frame.offsetBy(dx: cell.frame.minX, dy: cell.frame.minY)
self.addSubnode(node)
}

View File

@ -309,7 +309,7 @@ final class InstantPageTextItem: InstantPageItem {
return false
}
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (Int, Int) -> Void, updateDetailsOpened: @escaping (Int, Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
return nil
}

View File

@ -19,7 +19,7 @@ final class InstantPageWebEmbedItem: InstantPageItem {
self.enableScrolling = enableScrolling
}
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (Int, Int) -> Void, updateDetailsOpened: @escaping (Int, Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
func node(account: Account, strings: PresentationStrings, theme: InstantPageTheme, openMedia: @escaping (InstantPageMedia) -> Void, openPeer: @escaping (PeerId) -> Void, openUrl: @escaping (InstantPageUrlItem) -> Void, updateWebEmbedHeight: @escaping (CGFloat) -> Void, updateDetailsExpanded: @escaping (Bool) -> Void) -> (InstantPageNode & ASDisplayNode)? {
return InstantPageWebEmbedNode(frame: self.frame, url: self.url, html: self.html, enableScrolling: self.enableScrolling, updateWebEmbedHeight: updateWebEmbedHeight)
}

View File

@ -20,11 +20,11 @@ private class WeakInstantPageWebEmbedNodeMessageHandler: NSObject, WKScriptMessa
final class InstantPageWebEmbedNode: ASDisplayNode, InstantPageNode {
let url: String?
let html: String?
let updateWebEmbedHeight: (Int, Int) -> Void
let updateWebEmbedHeight: (CGFloat) -> Void
private var webView: WKWebView?
init(frame: CGRect, url: String?, html: String?, enableScrolling: Bool, updateWebEmbedHeight: @escaping (Int, Int) -> Void) {
init(frame: CGRect, url: String?, html: String?, enableScrolling: Bool, updateWebEmbedHeight: @escaping (CGFloat) -> Void) {
self.url = url
self.html = html
self.updateWebEmbedHeight = updateWebEmbedHeight
@ -91,15 +91,7 @@ final class InstantPageWebEmbedNode: ASDisplayNode, InstantPageNode {
}
if eventName == "resize_frame", let height = dict["height"] as? Int {
var hash: Int?
if let url = self.url {
hash = url.hashValue
} else if let html = self.html {
hash = html.hashValue
}
if let hash = hash {
self.updateWebEmbedHeight(hash, height)
}
self.updateWebEmbedHeight(CGFloat(height))
}
}

View File

@ -125,7 +125,7 @@ final class OngoingCallContext {
private let audioSessionDisposable = MetaDisposable()
private var networkTypeDisposable: Disposable?
init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, allowP2P: Bool, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal<NetworkType, NoError>) {
init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal<NetworkType, NoError>) {
let _ = setupLogs
self.internalId = internalId
@ -142,7 +142,7 @@ final class OngoingCallContext {
break
}
}
let context = OngoingCallThreadLocalContext(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), allowP2P: allowP2P, proxy: voipProxyServer, networkType: ongoingNetworkTypeForType(initialNetworkType))
let context = OngoingCallThreadLocalContext(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForType(initialNetworkType))
self.contextRef = Unmanaged.passRetained(context)
context.stateChanged = { [weak self] state in
self?.contextState.set(.single(state))
@ -186,13 +186,13 @@ final class OngoingCallContext {
}
}
func start(key: Data, isOutgoing: Bool, connections: CallSessionConnectionSet, maxLayer: Int32, audioSessionActive: Signal<Bool, NoError>) {
func start(key: Data, isOutgoing: Bool, connections: CallSessionConnectionSet, maxLayer: Int32, allowP2P: Bool, audioSessionActive: Signal<Bool, NoError>) {
self.audioSessionDisposable.set((audioSessionActive
|> filter { $0 }
|> take(1)).start(next: { [weak self] _ in
if let strongSelf = self {
strongSelf.withContext { context in
context.start(withKey: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescription(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescription), maxLayer: maxLayer)
context.start(withKey: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescription(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescription), maxLayer: maxLayer, allowP2P: allowP2P)
}
}
}))

View File

@ -54,8 +54,8 @@ typedef NS_ENUM(int32_t, OngoingCallNetworkType) {
@property (nonatomic, copy) void (^ _Nullable stateChanged)(OngoingCallState);
@property (nonatomic, copy) void (^ _Nullable callEnded)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile);
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueue> _Nonnull)queue allowP2P:(BOOL)allowP2P proxy:(VoipProxyServer * _Nullable)proxy networkType:(OngoingCallNetworkType)networkType;
- (void)startWithKey:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescription * _Nonnull)primaryConnection alternativeConnections:(NSArray<OngoingCallConnectionDescription *> * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer;
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueue> _Nonnull)queue proxy:(VoipProxyServer * _Nullable)proxy networkType:(OngoingCallNetworkType)networkType;
- (void)startWithKey:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescription * _Nonnull)primaryConnection alternativeConnections:(NSArray<OngoingCallConnectionDescription *> * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P;
- (void)stop;
- (NSString * _Nullable)debugInfo;

View File

@ -127,7 +127,6 @@ static void withContext(int32_t contextId, void (^f)(OngoingCallThreadLocalConte
NSTimeInterval _callConnectTimeout;
NSTimeInterval _callPacketTimeout;
int32_t _dataSavingMode;
bool _allowP2P;
tgvoip::VoIPController *_controller;
@ -181,7 +180,7 @@ static int callControllerNetworkTypeForType(OngoingCallNetworkType type) {
TGVoipLoggingFunction = loggingFunction;
}
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueue> _Nonnull)queue allowP2P:(BOOL)allowP2P proxy:(VoipProxyServer * _Nullable)proxy networkType:(OngoingCallNetworkType)networkType {
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueue> _Nonnull)queue proxy:(VoipProxyServer * _Nullable)proxy networkType:(OngoingCallNetworkType)networkType {
self = [super init];
if (self != nil) {
_queue = queue;
@ -193,7 +192,6 @@ static int callControllerNetworkTypeForType(OngoingCallNetworkType type) {
_callConnectTimeout = 30.0;
_callPacketTimeout = 10.0;
_dataSavingMode = 0;
_allowP2P = allowP2P;
_networkType = networkType;
_controller = new tgvoip::VoIPController();
@ -232,7 +230,7 @@ static int callControllerNetworkTypeForType(OngoingCallNetworkType type) {
}
}
- (void)startWithKey:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescription * _Nonnull)primaryConnection alternativeConnections:(NSArray<OngoingCallConnectionDescription *> * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer {
- (void)startWithKey:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescription * _Nonnull)primaryConnection alternativeConnections:(NSArray<OngoingCallConnectionDescription *> * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P {
std::vector<tgvoip::Endpoint> endpoints;
NSArray<OngoingCallConnectionDescription *> *connections = [@[primaryConnection] arrayByAddingObjectsFromArray:alternativeConnections];
for (OngoingCallConnectionDescription *connection in connections) {
@ -261,7 +259,7 @@ static int callControllerNetworkTypeForType(OngoingCallNetworkType type) {
_controller->SetConfig(config);
_controller->SetEncryptionKey((char *)key.bytes, isOutgoing);
_controller->SetRemoteEndpoints(endpoints, _allowP2P, maxLayer);
_controller->SetRemoteEndpoints(endpoints, allowP2P, maxLayer);
_controller->Start();
_controller->Connect();

View File

@ -188,6 +188,7 @@ public class PeerMediaCollectionController: TelegramController {
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _ in
}, longTap: { [weak self] content in
if let strongSelf = self {
strongSelf.view.endEditing(true)
switch content {
case let .url(url):
let canOpenIn = availableOpenInOptions(applicationContext: strongSelf.account.telegramApplicationContext, item: .url(url: url)).count > 1

View File

@ -230,7 +230,7 @@ public final class PresentationCall {
self.isOutgoing = isOutgoing
self.peer = peer
self.ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, allowP2P: allowP2P, proxyServer: proxyServer, initialNetworkType: currentNetworkType, updatedNetworkType: updatedNetworkType)
self.ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: currentNetworkType, updatedNetworkType: updatedNetworkType)
var didReceiveAudioOutputs = false
self.sessionStateDisposable = (callSessionManager.callState(internalId: internalId)
@ -413,7 +413,7 @@ public final class PresentationCall {
presentationState = .terminated(reason)
case let .requesting(ringing):
presentationState = .requesting(ringing)
case let .active(_, keyVisualHash, _, _):
case let .active(_, keyVisualHash, _, _, _):
if let callContextState = callContextState {
switch callContextState {
case .initializing:
@ -441,10 +441,10 @@ public final class PresentationCall {
if let _ = audioSessionControl {
self.audioSessionShouldBeActive.set(true)
}
case let .active(key, _, connections, maxLayer):
case let .active(key, _, connections, maxLayer, allowsP2P):
self.audioSessionShouldBeActive.set(true)
if let _ = audioSessionControl, !wasActive || previousControl == nil {
self.ongoingContext.start(key: key, isOutgoing: sessionState.isOutgoing, connections: connections, maxLayer: maxLayer, audioSessionActive: self.audioSessionActive.get())
self.ongoingContext.start(key: key, isOutgoing: sessionState.isOutgoing, connections: connections, maxLayer: maxLayer, allowP2P: allowsP2P, audioSessionActive: self.audioSessionActive.get())
if sessionState.isOutgoing {
self.callKitIntegration?.reportOutgoingCallConnected(uuid: sessionState.id, at: Date())
}

View File

@ -376,7 +376,7 @@ public func privacyAndSecurityController(account: Account, initialSettings: Sign
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: updated, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: updated, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, accountRemovalTimeout: value.accountRemovalTimeout)))
}
return .complete()
}
@ -399,7 +399,7 @@ public func privacyAndSecurityController(account: Account, initialSettings: Sign
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: updated, voiceCalls: value.voiceCalls, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: updated, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, accountRemovalTimeout: value.accountRemovalTimeout)))
}
return .complete()
}
@ -424,8 +424,8 @@ public func privacyAndSecurityController(account: Account, initialSettings: Sign
currentInfoDisposable.set((combineLatest(privacySignal, callsSignal)
|> deliverOnMainQueue).start(next: { [weak currentInfoDisposable] info, callSettings in
if let info = info {
pushControllerImpl?(selectivePrivacySettingsController(account: account, kind: .voiceCalls, current: info.voiceCalls, callSettings: callSettings.0, voipConfiguration: callSettings.1, callIntegrationAvailable: CallKitIntegration.isAvailable, updated: { updated, updatedCallSettings in
if let currentInfoDisposable = currentInfoDisposable, let updatedCallSettings = updatedCallSettings {
pushControllerImpl?(selectivePrivacySettingsController(account: account, kind: .voiceCalls, current: info.voiceCalls, callSettings: (info.voiceCallsP2P, callSettings.0), voipConfiguration: callSettings.1, callIntegrationAvailable: CallKitIntegration.isAvailable, updated: { updated, updatedCallSettings in
if let currentInfoDisposable = currentInfoDisposable, let (updatedCallsPrivacy, updatedCallSettings) = updatedCallSettings {
let _ = updateVoiceCallSettingsSettingsInteractively(postbox: account.postbox, { _ in
return updatedCallSettings
}).start()
@ -436,7 +436,7 @@ public func privacyAndSecurityController(account: Account, initialSettings: Sign
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: updated, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: updated, voiceCallsP2P: updatedCallsPrivacy, accountRemovalTimeout: value.accountRemovalTimeout)))
}
return .complete()
}
@ -485,7 +485,7 @@ public func privacyAndSecurityController(account: Account, initialSettings: Sign
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, accountRemovalTimeout: timeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, accountRemovalTimeout: timeout)))
}
return .complete()
}

View File

@ -27,24 +27,29 @@ private enum SelectivePrivacySettingType {
}
}
enum SelectivePrivacySettingsPeerTarget {
case main
case callP2P
}
private final class SelectivePrivacySettingsControllerArguments {
let account: Account
let updateType: (SelectivePrivacySettingType) -> Void
let openEnableFor: () -> Void
let openDisableFor: () -> Void
let openEnableFor: (SelectivePrivacySettingsPeerTarget) -> Void
let openDisableFor: (SelectivePrivacySettingsPeerTarget) -> Void
let updateCallsP2PMode: ((VoiceCallP2PMode) -> Void)?
let updateCallsIntegrationEnabled: ((Bool) -> Void)?
let updateCallP2PMode: ((SelectivePrivacySettingType) -> Void)?
let updateCallIntegrationEnabled: ((Bool) -> Void)?
init(account: Account, updateType: @escaping (SelectivePrivacySettingType) -> Void, openEnableFor: @escaping () -> Void, openDisableFor: @escaping () -> Void, updateCallsP2PMode: ((VoiceCallP2PMode) -> Void)?, updateCallsIntegrationEnabled: ((Bool) -> Void)?) {
init(account: Account, updateType: @escaping (SelectivePrivacySettingType) -> Void, openEnableFor: @escaping (SelectivePrivacySettingsPeerTarget) -> Void, openDisableFor: @escaping (SelectivePrivacySettingsPeerTarget) -> Void, updateCallP2PMode: ((SelectivePrivacySettingType) -> Void)?, updateCallIntegrationEnabled: ((Bool) -> Void)?) {
self.account = account
self.updateType = updateType
self.openEnableFor = openEnableFor
self.openDisableFor = openDisableFor
self.updateCallsP2PMode = updateCallsP2PMode
self.updateCallsIntegrationEnabled = updateCallsIntegrationEnabled
self.updateCallP2PMode = updateCallP2PMode
self.updateCallIntegrationEnabled = updateCallIntegrationEnabled
}
}
@ -52,6 +57,7 @@ private enum SelectivePrivacySettingsSection: Int32 {
case setting
case peers
case callsP2P
case callsP2PPeers
case callsIntegrationEnabled
}
@ -77,6 +83,9 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
case callsP2PContacts(PresentationTheme, String, Bool)
case callsP2PNever(PresentationTheme, String, Bool)
case callsP2PInfo(PresentationTheme, String)
case callsP2PDisableFor(PresentationTheme, String, String)
case callsP2PEnableFor(PresentationTheme, String, String)
case callsP2PPeersInfo(PresentationTheme, String)
case callsIntegrationEnabled(PresentationTheme, String, Bool)
case callsIntegrationInfo(PresentationTheme, String)
@ -88,6 +97,8 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
return SelectivePrivacySettingsSection.peers.rawValue
case .callsP2PHeader, .callsP2PAlways, .callsP2PContacts, .callsP2PNever, .callsP2PInfo:
return SelectivePrivacySettingsSection.callsP2P.rawValue
case .callsP2PDisableFor, .callsP2PEnableFor, .callsP2PPeersInfo:
return SelectivePrivacySettingsSection.callsP2PPeers.rawValue
case .callsIntegrationEnabled, .callsIntegrationInfo:
return SelectivePrivacySettingsSection.callsIntegrationEnabled.rawValue
}
@ -121,10 +132,16 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
return 11
case .callsP2PInfo:
return 12
case .callsIntegrationEnabled:
case .callsP2PDisableFor:
return 13
case .callsIntegrationInfo:
case .callsP2PEnableFor:
return 14
case .callsP2PPeersInfo:
return 15
case .callsIntegrationEnabled:
return 16
case .callsIntegrationInfo:
return 17
}
}
@ -208,6 +225,24 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
} else {
return false
}
case let .callsP2PDisableFor(lhsTheme, lhsText, lhsValue):
if case let .callsP2PDisableFor(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
} else {
return false
}
case let .callsP2PEnableFor(lhsTheme, lhsText, lhsValue):
if case let .callsP2PEnableFor(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
} else {
return false
}
case let .callsP2PPeersInfo(lhsTheme, lhsText):
if case let .callsP2PPeersInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .callsIntegrationEnabled(lhsTheme, lhsText, lhsValue):
if case let .callsIntegrationEnabled(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
@ -247,11 +282,11 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
case let .disableFor(theme, title, value):
return ItemListDisclosureItem(theme: theme, title: title, label: value, sectionId: self.section, style: .blocks, action: {
arguments.openDisableFor()
arguments.openDisableFor(.main)
})
case let .enableFor(theme, title, value):
return ItemListDisclosureItem(theme: theme, title: title, label: value, sectionId: self.section, style: .blocks, action: {
arguments.openEnableFor()
arguments.openEnableFor(.main)
})
case let .peersInfo(theme, text):
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
@ -259,21 +294,31 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .callsP2PAlways(theme, text, value):
return ItemListCheckboxItem(theme: theme, title: text, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: {
arguments.updateCallsP2PMode?(.always)
arguments.updateCallP2PMode?(.everybody)
})
case let .callsP2PContacts(theme, text, value):
return ItemListCheckboxItem(theme: theme, title: text, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: {
arguments.updateCallsP2PMode?(.contacts)
arguments.updateCallP2PMode?(.contacts)
})
case let .callsP2PNever(theme, text, value):
return ItemListCheckboxItem(theme: theme, title: text, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: {
arguments.updateCallsP2PMode?(.never)
arguments.updateCallP2PMode?(.nobody)
})
case let .callsP2PInfo(theme, text):
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
case let .callsP2PDisableFor(theme, title, value):
return ItemListDisclosureItem(theme: theme, title: title, label: value, sectionId: self.section, style: .blocks, action: {
arguments.openDisableFor(.callP2P)
})
case let .callsP2PEnableFor(theme, title, value):
return ItemListDisclosureItem(theme: theme, title: title, label: value, sectionId: self.section, style: .blocks, action: {
arguments.openEnableFor(.callP2P)
})
case let .callsP2PPeersInfo(theme, text):
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
case let .callsIntegrationEnabled(theme, text, value):
return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateCallsIntegrationEnabled?(value)
arguments.updateCallIntegrationEnabled?(value)
})
case let .callsIntegrationInfo(theme, text):
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
@ -289,17 +334,21 @@ private struct SelectivePrivacySettingsControllerState: Equatable {
let saving: Bool
let callDataSaving: VoiceCallDataSaving?
let callP2PMode: VoiceCallP2PMode?
let callP2PMode: SelectivePrivacySettingType?
let callP2PEnableFor: Set<PeerId>?
let callP2PDisableFor: Set<PeerId>?
let callIntegrationAvailable: Bool?
let callIntegrationEnabled: Bool?
init(setting: SelectivePrivacySettingType, enableFor: Set<PeerId>, disableFor: Set<PeerId>, saving: Bool, callDataSaving: VoiceCallDataSaving?, callP2PMode: VoiceCallP2PMode?, callIntegrationAvailable: Bool?, callIntegrationEnabled: Bool?) {
init(setting: SelectivePrivacySettingType, enableFor: Set<PeerId>, disableFor: Set<PeerId>, saving: Bool, callDataSaving: VoiceCallDataSaving?, callP2PMode: SelectivePrivacySettingType?, callP2PEnableFor: Set<PeerId>?, callP2PDisableFor: Set<PeerId>?, callIntegrationAvailable: Bool?, callIntegrationEnabled: Bool?) {
self.setting = setting
self.enableFor = enableFor
self.disableFor = disableFor
self.saving = saving
self.callDataSaving = callDataSaving
self.callP2PMode = callP2PMode
self.callP2PEnableFor = callP2PEnableFor
self.callP2PDisableFor = callP2PDisableFor
self.callIntegrationAvailable = callIntegrationAvailable
self.callIntegrationEnabled = callIntegrationEnabled
}
@ -323,6 +372,12 @@ private struct SelectivePrivacySettingsControllerState: Equatable {
if lhs.callP2PMode != rhs.callP2PMode {
return false
}
if lhs.callP2PEnableFor != rhs.callP2PEnableFor {
return false
}
if lhs.callP2PDisableFor != rhs.callP2PDisableFor {
return false
}
if lhs.callIntegrationAvailable != rhs.callIntegrationAvailable {
return false
}
@ -334,27 +389,35 @@ private struct SelectivePrivacySettingsControllerState: Equatable {
}
func withUpdatedSetting(_ setting: SelectivePrivacySettingType) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled)
return SelectivePrivacySettingsControllerState(setting: setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled)
}
func withUpdatedEnableFor(_ enableFor: Set<PeerId>) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled)
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled)
}
func withUpdatedDisableFor(_ disableFor: Set<PeerId>) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled)
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled)
}
func withUpdatedSaving(_ saving: Bool) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled)
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled)
}
func withUpdatedCallsP2PMode(_ mode: VoiceCallP2PMode) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: mode, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled)
func withUpdatedCallP2PMode(_ mode: SelectivePrivacySettingType) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: mode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled)
}
func withUpdatedCallP2PEnableFor(_ enableFor: Set<PeerId>) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: enableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled)
}
func withUpdatedCallP2PDisableFor(_ disableFor: Set<PeerId>) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: disableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: self.callIntegrationEnabled)
}
func withUpdatedCallsIntegrationEnabled(_ enabled: Bool) -> SelectivePrivacySettingsControllerState {
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: enabled)
return SelectivePrivacySettingsControllerState(setting: self.setting, enableFor: self.enableFor, disableFor: self.disableFor, saving: self.saving, callDataSaving: self.callDataSaving, callP2PMode: self.callP2PMode, callP2PEnableFor: self.callP2PEnableFor, callP2PDisableFor: self.callP2PDisableFor, callIntegrationAvailable: self.callIntegrationAvailable, callIntegrationEnabled: enabled)
}
}
@ -409,12 +472,24 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
if case .voiceCalls = kind, let p2pMode = state.callP2PMode, let integrationAvailable = state.callIntegrationAvailable, let integrationEnabled = state.callIntegrationEnabled {
entries.append(.callsP2PHeader(presentationData.theme, presentationData.strings.Privacy_Calls_P2P.uppercased()))
entries.append(.callsP2PAlways(presentationData.theme, presentationData.strings.Privacy_Calls_P2PAlways, p2pMode == .always))
entries.append(.callsP2PAlways(presentationData.theme, presentationData.strings.Privacy_Calls_P2PAlways, p2pMode == .everybody))
entries.append(.callsP2PContacts(presentationData.theme, presentationData.strings.Privacy_Calls_P2PContacts, p2pMode == .contacts))
entries.append(.callsP2PNever(presentationData.theme, presentationData.strings.Privacy_Calls_P2PNever, p2pMode == .never))
entries.append(.callsP2PNever(presentationData.theme, presentationData.strings.Privacy_Calls_P2PNever, p2pMode == .nobody))
entries.append(.callsP2PInfo(presentationData.theme, presentationData.strings.Privacy_Calls_P2PHelp))
if let callP2PMode = state.callP2PMode, let disableFor = state.callP2PDisableFor, let enableFor = state.callP2PEnableFor {
switch callP2PMode {
case .everybody:
entries.append(.callsP2PDisableFor(presentationData.theme, disableForText, stringForUserCount(disableFor.count, strings: presentationData.strings)))
case .contacts:
entries.append(.callsP2PDisableFor(presentationData.theme, disableForText, stringForUserCount(disableFor.count, strings: presentationData.strings)))
entries.append(.callsP2PEnableFor(presentationData.theme, enableForText, stringForUserCount(enableFor.count, strings: presentationData.strings)))
case .nobody:
entries.append(.callsP2PEnableFor(presentationData.theme, enableForText, stringForUserCount(enableFor.count, strings: presentationData.strings)))
}
}
entries.append(.callsP2PPeersInfo(presentationData.theme, presentationData.strings.PrivacyLastSeenSettings_CustomShareSettingsHelp))
if integrationAvailable {
entries.append(.callsIntegrationEnabled(presentationData.theme, presentationData.strings.Privacy_Calls_Integration, integrationEnabled))
entries.append(.callsIntegrationInfo(presentationData.theme, presentationData.strings.Privacy_Calls_IntegrationHelp))
@ -424,7 +499,7 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
return entries
}
func selectivePrivacySettingsController(account: Account, kind: SelectivePrivacySettingsKind, current: SelectivePrivacySettings, callSettings: VoiceCallSettings? = nil, voipConfiguration: VoipConfiguration? = nil, callIntegrationAvailable: Bool? = nil, updated: @escaping (SelectivePrivacySettings, VoiceCallSettings?) -> Void) -> ViewController {
func selectivePrivacySettingsController(account: Account, kind: SelectivePrivacySettingsKind, current: SelectivePrivacySettings, callSettings: (SelectivePrivacySettings, VoiceCallSettings)? = nil, voipConfiguration: VoipConfiguration? = nil, callIntegrationAvailable: Bool? = nil, updated: @escaping (SelectivePrivacySettings, (SelectivePrivacySettings, VoiceCallSettings)?) -> Void) -> ViewController {
let strings = account.telegramApplicationContext.currentPresentationData.with { $0 }.strings
var initialEnableFor = Set<PeerId>()
@ -438,7 +513,23 @@ func selectivePrivacySettingsController(account: Account, kind: SelectivePrivacy
case let .enableEveryone(disableFor):
initialDisableFor = disableFor
}
let initialState = SelectivePrivacySettingsControllerState(setting: SelectivePrivacySettingType(current), enableFor: initialEnableFor, disableFor: initialDisableFor, saving: false, callDataSaving: callSettings?.dataSaving, callP2PMode: callSettings?.p2pMode ?? voipConfiguration?.defaultP2PMode, callIntegrationAvailable: callIntegrationAvailable, callIntegrationEnabled: callSettings?.enableSystemIntegration)
var initialCallP2PEnableFor: Set<PeerId>?
var initialCallP2PDisableFor: Set<PeerId>?
if let callCurrent = callSettings?.0 {
switch callCurrent {
case let .disableEveryone(enableFor):
initialCallP2PEnableFor = enableFor
initialCallP2PDisableFor = Set<PeerId>()
case let .enableContacts(enableFor, disableFor):
initialCallP2PEnableFor = enableFor
initialCallP2PDisableFor = disableFor
case let .enableEveryone(disableFor):
initialCallP2PEnableFor = Set<PeerId>()
initialCallP2PDisableFor = disableFor
}
}
let initialState = SelectivePrivacySettingsControllerState(setting: SelectivePrivacySettingType(current), enableFor: initialEnableFor, disableFor: initialDisableFor, saving: false, callDataSaving: callSettings?.1.dataSaving, callP2PMode: callSettings != nil ? SelectivePrivacySettingType(callSettings!.0) : nil, callP2PEnableFor: initialCallP2PEnableFor, callP2PDisableFor: initialCallP2PDisableFor, callIntegrationAvailable: callIntegrationAvailable, callIntegrationEnabled: callSettings?.1.enableSystemIntegration)
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
let stateValue = Atomic(value: initialState)
@ -458,7 +549,7 @@ func selectivePrivacySettingsController(account: Account, kind: SelectivePrivacy
updateState {
$0.withUpdatedSetting(type)
}
}, openEnableFor: {
}, openEnableFor: { target in
let title: String
switch kind {
case .presence:
@ -475,10 +566,15 @@ func selectivePrivacySettingsController(account: Account, kind: SelectivePrivacy
}
pushControllerImpl?(selectivePrivacyPeersController(account: account, title: title, initialPeerIds: Array(peerIds), updated: { updatedPeerIds in
updateState { state in
switch target {
case .main:
return state.withUpdatedEnableFor(Set(updatedPeerIds)).withUpdatedDisableFor(state.disableFor.subtracting(Set(updatedPeerIds)))
case .callP2P:
return state.withUpdatedCallP2PEnableFor(Set(updatedPeerIds)).withUpdatedCallP2PDisableFor(state.disableFor.subtracting(Set(updatedPeerIds)))
}
}
}))
}, openDisableFor: {
}, openDisableFor: { target in
let title: String
switch kind {
case .presence:
@ -495,19 +591,19 @@ func selectivePrivacySettingsController(account: Account, kind: SelectivePrivacy
}
pushControllerImpl?(selectivePrivacyPeersController(account: account, title: title, initialPeerIds: Array(peerIds), updated: { updatedPeerIds in
updateState { state in
switch target {
case .main:
return state.withUpdatedDisableFor(Set(updatedPeerIds)).withUpdatedEnableFor(state.enableFor.subtracting(Set(updatedPeerIds)))
case .callP2P:
return state.withUpdatedCallP2PDisableFor(Set(updatedPeerIds)).withUpdatedCallP2PEnableFor(state.enableFor.subtracting(Set(updatedPeerIds)))
}
}
}))
}, updateCallsP2PMode: { mode in
}, updateCallP2PMode: { mode in
updateState { state in
return state.withUpdatedCallsP2PMode(mode)
return state.withUpdatedCallP2PMode(mode)
}
let _ = updateVoiceCallSettingsSettingsInteractively(postbox: account.postbox, { settings in
var settings = settings
settings.p2pMode = mode
return settings
}).start()
}, updateCallsIntegrationEnabled: { enabled in
}, updateCallIntegrationEnabled: { enabled in
updateState { state in
return state.withUpdatedCallsIntegrationEnabled(enabled)
}
@ -532,6 +628,7 @@ func selectivePrivacySettingsController(account: Account, kind: SelectivePrivacy
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
var wasSaving = false
var settings: SelectivePrivacySettings?
var callP2PSettings: SelectivePrivacySettings?
updateState { state in
wasSaving = state.saving
switch state.setting {
@ -542,6 +639,18 @@ func selectivePrivacySettingsController(account: Account, kind: SelectivePrivacy
case .nobody:
settings = SelectivePrivacySettings.disableEveryone(enableFor: state.enableFor)
}
if case .voiceCalls = kind, let callP2PMode = state.callP2PMode, let disableFor = state.callP2PDisableFor, let enableFor = state.callP2PEnableFor {
switch callP2PMode {
case .everybody:
callP2PSettings = SelectivePrivacySettings.enableEveryone(disableFor: disableFor)
case .contacts:
callP2PSettings = SelectivePrivacySettings.enableContacts(enableFor: enableFor, disableFor: disableFor)
case .nobody:
callP2PSettings = SelectivePrivacySettings.disableEveryone(enableFor: enableFor)
}
}
return state.withUpdatedSaving(true)
}
@ -556,12 +665,18 @@ func selectivePrivacySettingsController(account: Account, kind: SelectivePrivacy
type = .voiceCalls
}
updateSettingsDisposable.set((updateSelectiveAccountPrivacySettings(account: account, type: type, settings: settings) |> deliverOnMainQueue).start(completed: {
let updateSettingsSignal = updateSelectiveAccountPrivacySettings(account: account, type: type, settings: settings)
var updateCallP2PSettingsSignal: Signal<Void, NoError> = Signal.complete()
if let callP2PSettings = callP2PSettings {
updateCallP2PSettingsSignal = updateSelectiveAccountPrivacySettings(account: account, type: .voiceCallsP2P, settings: callP2PSettings)
}
updateSettingsDisposable.set((combineLatest(updateSettingsSignal, updateCallP2PSettingsSignal) |> deliverOnMainQueue).start(completed: {
updateState { state in
return state.withUpdatedSaving(false)
}
if case .voiceCalls = kind, let dataSaving = state.callDataSaving, let p2pMode = state.callP2PMode, let systemIntegrationEnabled = state.callIntegrationEnabled {
updated(settings, VoiceCallSettings(dataSaving: dataSaving, p2pMode: p2pMode, enableSystemIntegration: systemIntegrationEnabled))
if case .voiceCalls = kind, let dataSaving = state.callDataSaving, let callP2PSettings = callP2PSettings, let systemIntegrationEnabled = state.callIntegrationEnabled {
updated(settings, (callP2PSettings, VoiceCallSettings(dataSaving: dataSaving, p2pMode: nil, enableSystemIntegration: systemIntegrationEnabled)))
} else {
updated(settings, nil)
}