Update Privacy and Security

This commit is contained in:
Peter 2019-05-18 12:25:04 +02:00
parent 87c91d57b2
commit 775e2cb852
38 changed files with 3690 additions and 3189 deletions

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "blocked.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "faceid.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "sessions.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "touchid.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "2step.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -308,6 +308,7 @@
D0428200200E6A00009DDE36 /* ChatRecentActionsHistoryTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04281FF200E6A00009DDE36 /* ChatRecentActionsHistoryTransition.swift */; };
D0430B001FF4570500A35ADD /* WebController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0430AFF1FF4570500A35ADD /* WebController.swift */; };
D0430B021FF4584100A35ADD /* WebControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0430B011FF4584100A35ADD /* WebControllerNode.swift */; };
D0439B5B228EC4A00067E026 /* ChatMessagePhoneNumberRequestContentNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0439B5A228EC4A00067E026 /* ChatMessagePhoneNumberRequestContentNode.swift */; };
D044A0F320BDA05800326FAC /* ThrottledValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D044A0F220BDA05800326FAC /* ThrottledValue.swift */; };
D044A0FB20BDC40C00326FAC /* CachedChannelAdmins.swift in Sources */ = {isa = PBXBuildFile; fileRef = D044A0FA20BDC40C00326FAC /* CachedChannelAdmins.swift */; };
D045549A21B2F173007A6DD9 /* libturbojpeg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D045549921B2F173007A6DD9 /* libturbojpeg.a */; };
@ -1612,6 +1613,7 @@
D042C6891E8DAAB000C863B0 /* ChatItemGalleryFooterContentNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatItemGalleryFooterContentNode.swift; sourceTree = "<group>"; };
D0430AFF1FF4570500A35ADD /* WebController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebController.swift; sourceTree = "<group>"; };
D0430B011FF4584100A35ADD /* WebControllerNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebControllerNode.swift; sourceTree = "<group>"; };
D0439B5A228EC4A00067E026 /* ChatMessagePhoneNumberRequestContentNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessagePhoneNumberRequestContentNode.swift; sourceTree = "<group>"; };
D044A0F220BDA05800326FAC /* ThrottledValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThrottledValue.swift; sourceTree = "<group>"; };
D044A0FA20BDC40C00326FAC /* CachedChannelAdmins.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedChannelAdmins.swift; sourceTree = "<group>"; };
D045549921B2F173007A6DD9 /* libturbojpeg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libturbojpeg.a; path = "third-party/libjpeg-turbo/libturbojpeg.a"; sourceTree = "<group>"; };
@ -4774,6 +4776,7 @@
D018BE57218C7BD800C02DDC /* ChatMessageDeliveryFailedNode.swift */,
D0AB262821C307D7008F6685 /* ChatMessagePollBubbleContentNode.swift */,
099529AF21D2123E00805E13 /* ChatMessageUnsupportedBubbleContentNode.swift */,
D0439B5A228EC4A00067E026 /* ChatMessagePhoneNumberRequestContentNode.swift */,
);
name = Items;
sourceTree = "<group>";
@ -5873,6 +5876,7 @@
D0E9BA2B1F0557A600F079A4 /* STPFormEncoder.m in Sources */,
D01BAA1C1ECC92F700295217 /* CallListViewTransition.swift in Sources */,
D0FBE84F2273395C00B33B52 /* ChatListArchiveInfoItem.swift in Sources */,
D0439B5B228EC4A00067E026 /* ChatMessagePhoneNumberRequestContentNode.swift in Sources */,
09F664D021EBCFB900AB7E26 /* WallpaperCropNode.swift in Sources */,
D097C26C20DD1EA5007BB4B8 /* OverlayStatusController.swift in Sources */,
D0EC6D9E1EB9F58900EBF1C3 /* ChatMessageWebpageBubbleContentNode.swift in Sources */,

View File

@ -198,7 +198,7 @@ public final class CallListController: ViewController {
|> take(1)
|> deliverOnMainQueue).start(next: { [weak controller, weak self] peer in
controller?.dismissSearch()
if let strongSelf = self, let contactPeer = peer, case let .peer(peer, _) = contactPeer {
if let strongSelf = self, let contactPeer = peer, case let .peer(peer, _, _) = contactPeer {
strongSelf.call(peer.id, began: {
if let strongSelf = self {
let _ = (strongSelf.context.sharedContext.hasOngoingCall.get()

View File

@ -338,7 +338,7 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
|> take(1)
|> deliverOnMainQueue).start(next: { members in
let disabledIds = members?.compactMap({$0.peer.id}) ?? []
let contactsController = ContactMultiselectionController(context: context, mode: .peerSelection(searchChatList: false), options: [], filters: [.excludeSelf, .disable(disabledIds)])
let contactsController = ContactMultiselectionController(context: context, mode: .peerSelection(searchChatList: false, searchGroups: false), options: [], filters: [.excludeSelf, .disable(disabledIds)])
addMembersDisposable.set((contactsController.result
|> deliverOnMainQueue

View File

@ -200,6 +200,8 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
}
case .payment:
break
case .urlAuth:
break
}
}
}

View File

@ -4545,7 +4545,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
if let strongSelf = self, let peer = peer {
let dataSignal: Signal<(Peer?, DeviceContactExtendedData?), NoError>
switch peer {
case let .peer(contact, _):
case let .peer(contact, _, _):
guard let contact = contact as? TelegramUser, let phoneNumber = contact.phone else {
return
}

View File

@ -752,6 +752,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
}
}
}
var contentRequiredValidation = false
for attribute in message.attributes {
if attribute is ViewCountMessageAttribute {
if message.id.namespace == Namespaces.Message.Cloud {
@ -759,13 +760,18 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
}
} else if let attribute = attribute as? ConsumableContentMessageAttribute, !attribute.consumed {
hasUnconsumedContent = true
} else if let _ = attribute as? ContentRequiresValidationMessageAttribute {
contentRequiredValidation = true
}
}
for media in message.media {
if let _ = media as? TelegramMediaUnsupported {
messageIdsWithUnsupportedMedia.append(message.id)
contentRequiredValidation = true
}
}
if contentRequiredValidation {
messageIdsWithUnsupportedMedia.append(message.id)
}
if hasUnconsumedMention && !hasUnconsumedContent {
messageIdsWithUnseenPersonalMention.append(message.id)
}

View File

@ -171,7 +171,7 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, account: A
let directionHint: ListViewScrollToItemDirectionHint = sourceIndex > index ? .Down : .Up
let chatScrollPosition = ChatHistoryViewScrollPosition.index(index: index, position: scrollPosition, directionHint: directionHint, animated: animated)
var first = true
return account.viewTracker.aroundMessageHistoryViewForLocation(chatLocation, index: index, anchorIndex: anchorIndex, count: 200, fixedCombinedReadStates: fixedCombinedReadStates, tagMask: tagMask, orderStatistics: orderStatistics, additionalData: additionalData)
return account.viewTracker.aroundMessageHistoryViewForLocation(chatLocation, index: index, anchorIndex: anchorIndex, count: 100, fixedCombinedReadStates: fixedCombinedReadStates, tagMask: tagMask, orderStatistics: orderStatistics, additionalData: additionalData)
|> map { view, updateType, initialData -> ChatHistoryViewUpdate in
let (cachedData, cachedDataMessages, readStateData) = extractAdditionalData(view: view, chatLocation: chatLocation)

View File

@ -417,6 +417,8 @@ private func universalServiceMessageString(theme: ChatPresentationThemeData?, st
attributedString = NSAttributedString(string: strings.Notification_PassportValuesSentMessage(message.peers[message.id.peerId]?.compactDisplayTitle ?? "", typesString).0, font: titleFont, textColor: primaryTextColor)
case .peerJoined:
attributedString = addAttributesToStringWithRanges(strings.Notification_Joined(authorName), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]))
case .phoneNumberRequest:
attributedString = nil
case .unknown:
attributedString = nil
}

View File

@ -29,6 +29,8 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(
} else if let action = media as? TelegramMediaAction {
if case .phoneCall = action.action {
result.append((message, ChatMessageCallBubbleContentNode.self))
} else if case .phoneNumberRequest = action.action {
result.append((message, ChatMessagePhoneNumberRequestContentNode.self))
} else {
result.append((message, ChatMessageActionBubbleContentNode.self))
}

View File

@ -749,6 +749,8 @@ public class ChatMessageItemView: ListViewItemNode {
}
case .payment:
item.controllerInteraction.openCheckoutOrReceipt(item.message.id)
case .urlAuth:
break
}
}
}

View File

@ -0,0 +1,200 @@
import Foundation
import AsyncDisplayKit
import Display
import SwiftSignalKit
import Postbox
import TelegramCore
private let avatarFont: UIFont = UIFont(name: ".SFCompactRounded-Semibold", size: 16.0)!
private let titleFont = Font.medium(14.0)
private let textFont = Font.regular(14.0)
class ChatMessagePhoneNumberRequestContentNode: ChatMessageBubbleContentNode {
private let dateAndStatusNode: ChatMessageDateAndStatusNode
private let textNode: TextNode
private let buttonNode: ChatMessageAttachedContentButtonNode
required init() {
self.dateAndStatusNode = ChatMessageDateAndStatusNode()
self.textNode = TextNode()
self.buttonNode = ChatMessageAttachedContentButtonNode()
super.init()
self.addSubnode(self.textNode)
self.addSubnode(self.buttonNode)
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didLoad() {
super.didLoad()
}
override func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize) -> (ChatMessageBubbleContentProperties, CGSize?, CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool) -> Void))) {
let statusLayout = self.dateAndStatusNode.asyncLayout()
let makeTextLayout = TextNode.asyncLayout(self.textNode)
let makeButtonLayout = ChatMessageAttachedContentButtonNode.asyncLayout(self.buttonNode)
return { item, layoutConstants, _, _, constrainedSize in
let text: String
if item.message.effectivelyIncoming(item.context.account.peerId) {
text = "\(item.message.author?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) ?? "") requests your phone number"
} else {
text = "You have requested phone number"
}
let textString = NSAttributedString(string: text, font: textFont, textColor: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.bubble.incomingPrimaryTextColor : item.presentationData.theme.theme.chat.bubble.outgoingPrimaryTextColor)
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)
return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in
let maxTextWidth = max(1.0, constrainedSize.width - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right)
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: textString, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
var edited = false
var sentViaBot = false
var viewCount: Int?
for attribute in item.message.attributes {
if let _ = attribute as? EditedMessageAttribute {
edited = true
} else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count
} else if let _ = attribute as? InlineBotMessageAttribute {
sentViaBot = true
}
}
if let author = item.message.author as? TelegramUser, author.botInfo != nil || author.flags.contains(.isSupport) {
sentViaBot = true
}
let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings)
let statusType: ChatMessageDateAndStatusType?
switch position {
case .linear(_, .None):
if item.message.effectivelyIncoming(item.context.account.peerId) {
statusType = .BubbleIncoming
} else {
if item.message.flags.contains(.Failed) {
statusType = .BubbleOutgoing(.Failed)
} else if item.message.flags.isSending && !item.message.isSentOrAcknowledged {
statusType = .BubbleOutgoing(.Sending)
} else {
statusType = .BubbleOutgoing(.Sent(read: item.read))
}
}
default:
statusType = nil
}
var statusSize = CGSize()
var statusApply: ((Bool) -> Void)?
if let statusType = statusType {
let (size, apply) = statusLayout(item.presentationData, edited && !sentViaBot, viewCount, dateText, statusType, CGSize(width: constrainedSize.width, height: CGFloat.greatestFiniteMagnitude))
statusSize = size
statusApply = apply
}
let buttonImage: UIImage
let buttonHighlightedImage: UIImage
let titleColor: UIColor
let titleHighlightedColor: UIColor
if item.message.effectivelyIncoming(item.context.account.peerId) {
buttonImage = PresentationResourcesChat.chatMessageAttachedContentButtonIncoming(item.presentationData.theme.theme)!
buttonHighlightedImage = PresentationResourcesChat.chatMessageAttachedContentHighlightedButtonIncoming(item.presentationData.theme.theme)!
titleColor = item.presentationData.theme.theme.chat.bubble.incomingAccentTextColor
let bubbleColors = bubbleColorComponents(theme: item.presentationData.theme.theme, incoming: true, wallpaper: !item.presentationData.theme.wallpaper.isEmpty)
titleHighlightedColor = bubbleColors.fill
} else {
buttonImage = PresentationResourcesChat.chatMessageAttachedContentButtonOutgoing(item.presentationData.theme.theme)!
buttonHighlightedImage = PresentationResourcesChat.chatMessageAttachedContentHighlightedButtonOutgoing(item.presentationData.theme.theme)!
titleColor = item.presentationData.theme.theme.chat.bubble.outgoingAccentTextColor
let bubbleColors = bubbleColorComponents(theme: item.presentationData.theme.theme, incoming: false, wallpaper: !item.presentationData.theme.wallpaper.isEmpty)
titleHighlightedColor = bubbleColors.fill
}
let (buttonWidth, continueLayout) = makeButtonLayout(constrainedSize.width, buttonImage, buttonHighlightedImage, nil, nil, "SHARE MY PHONE NUMBER", titleColor, titleHighlightedColor)
var maxContentWidth: CGFloat = 0.0
maxContentWidth = max(maxContentWidth, statusSize.width)
maxContentWidth = max(maxContentWidth, textLayout.size.width)
maxContentWidth = max(maxContentWidth, buttonWidth)
let contentWidth = maxContentWidth + layoutConstants.text.bubbleInsets.right + 8.0
return (contentWidth, { boundingWidth in
let layoutSize: CGSize
let statusFrame: CGRect
let (buttonSize, buttonApply) = continueLayout(boundingWidth - layoutConstants.text.bubbleInsets.right * 2.0)
let buttonSpacing: CGFloat = 4.0
layoutSize = CGSize(width: contentWidth, height: layoutConstants.text.bubbleInsets.top + textLayout.size.height + 9.0 + statusSize.height + buttonSize.height + buttonSpacing)
statusFrame = CGRect(origin: CGPoint(x: boundingWidth - statusSize.width - layoutConstants.text.bubbleInsets.right, y: layoutSize.height - statusSize.height - 9.0 - buttonSpacing - buttonSize.height), size: statusSize)
let buttonFrame = CGRect(origin: CGPoint(x: layoutConstants.text.bubbleInsets.right, y: layoutSize.height - 9.0 - buttonSize.height), size: buttonSize)
return (layoutSize, { [weak self] animation, _ in
if let strongSelf = self {
strongSelf.item = item
let _ = textApply()
let _ = buttonApply()
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: layoutConstants.text.bubbleInsets.left, y: layoutConstants.text.bubbleInsets.top), size: textLayout.size)
strongSelf.buttonNode.frame = buttonFrame
if let statusApply = statusApply {
if strongSelf.dateAndStatusNode.supernode == nil {
strongSelf.addSubnode(strongSelf.dateAndStatusNode)
}
var hasAnimation = true
if case .None = animation {
hasAnimation = false
}
statusApply(hasAnimation)
strongSelf.dateAndStatusNode.frame = statusFrame
} else if strongSelf.dateAndStatusNode.supernode != nil {
strongSelf.dateAndStatusNode.removeFromSupernode()
}
}
})
})
})
}
}
override func animateInsertion(_ currentTimestamp: Double, duration: Double) {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
override func animateAdded(_ currentTimestamp: Double, duration: Double) {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
}
override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture) -> ChatMessageBubbleContentTapAction {
if self.buttonNode.frame.contains(point) {
//return .openMessage
}
return .none
}
@objc private func buttonPressed() {
if let item = self.item {
item.controllerInteraction.shareAccountContact()
}
}
}

View File

@ -104,7 +104,7 @@ public class ComposeController: ViewController {
}
self.contactsNode.contactListNode.openPeer = { [weak self] peer in
if case let .peer(peer, _) = peer {
if case let .peer(peer, _, _) = peer {
self?.openPeer(peerId: peer.id)
}
}
@ -135,7 +135,7 @@ public class ComposeController: ViewController {
strongSelf.createActionDisposable.set((controller.result
|> take(1)
|> deliverOnMainQueue).start(next: { [weak controller] peer in
if let strongSelf = self, let contactPeer = peer, case let .peer(peer, _) = contactPeer {
if let strongSelf = self, let contactPeer = peer, case let .peer(peer, _, _) = contactPeer {
controller?.dismissSearch()
controller?.displayNavigationActivity = true
strongSelf.createActionDisposable.set((createSecretChat(account: strongSelf.context.account, peerId: peer.id) |> deliverOnMainQueue).start(next: { peerId in

View File

@ -112,7 +112,7 @@ final class ComposeControllerNode: ASDisplayNode {
}
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, onlyWriteable: false, categories: [.cloudContacts, .global], openPeer: { [weak self] peer in
if let requestOpenPeerFromSearch = self?.requestOpenPeerFromSearch, case let .peer(peer, _) = peer {
if let requestOpenPeerFromSearch = self?.requestOpenPeerFromSearch, case let .peer(peer, _, _) = peer {
requestOpenPeerFromSearch(peer.id)
}
}), cancel: { [weak self] in

View File

@ -121,12 +121,12 @@ enum ContactListPeerId: Hashable {
}
enum ContactListPeer: Equatable {
case peer(peer: Peer, isGlobal: Bool)
case peer(peer: Peer, isGlobal: Bool, participantCount: Int32?)
case deviceContact(DeviceContactStableId, DeviceContactBasicData)
var id: ContactListPeerId {
switch self {
case let .peer(peer, _):
case let .peer(peer, _, _):
return .peer(peer.id)
case let .deviceContact(id, _):
return .deviceContact(id)
@ -135,7 +135,7 @@ enum ContactListPeer: Equatable {
var indexName: PeerIndexNameRepresentation {
switch self {
case let .peer(peer, _):
case let .peer(peer, _, _):
return peer.indexName
case let .deviceContact(_, contact):
return .personName(first: contact.firstName, last: contact.lastName, addressName: "", phoneNumber: "")
@ -144,8 +144,8 @@ enum ContactListPeer: Equatable {
static func ==(lhs: ContactListPeer, rhs: ContactListPeer) -> Bool {
switch lhs {
case let .peer(lhsPeer, lhsIsGlobal):
if case let .peer(rhsPeer, rhsIsGlobal) = rhs, lhsPeer.isEqual(rhsPeer), lhsIsGlobal == rhsIsGlobal {
case let .peer(lhsPeer, lhsIsGlobal, lhsParticipantCount):
if case let .peer(rhsPeer, rhsIsGlobal, rhsParticipantCount) = rhs, lhsPeer.isEqual(rhsPeer), lhsIsGlobal == rhsIsGlobal, lhsParticipantCount == rhsParticipantCount {
return true
} else {
return false
@ -182,7 +182,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
return .option(index: index)
case let .peer(_, peer, _, _, _, _, _, _, _, _, _):
switch peer {
case let .peer(peer, _):
case let .peer(peer, _, _):
return .peerId(peer.id.toInt64())
case let .deviceContact(id, _):
return .deviceContact(id)
@ -218,12 +218,24 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
let status: ContactsPeerItemStatus
let itemPeer: ContactsPeerItemPeer
switch peer {
case let .peer(peer, isGlobal):
case let .peer(peer, isGlobal, participantCount):
if isGlobal, let _ = peer.addressName {
status = .addressName("")
} else {
let presence = presence ?? TelegramUserPresence(status: .none, lastActivity: 0)
status = .presence(presence, dateTimeFormat)
if let _ = peer as? TelegramUser {
let presence = presence ?? TelegramUserPresence(status: .none, lastActivity: 0)
status = .presence(presence, dateTimeFormat)
} else if let group = peer as? TelegramGroup {
status = .custom(strings.Conversation_StatusMembers(Int32(group.participantCount)))
} else if let _ = peer as? TelegramChannel {
if let participantCount = participantCount {
status = .custom(strings.Conversation_StatusMembers(participantCount))
} else {
status = .custom(strings.Group_Status)
}
} else {
status = .none
}
}
itemPeer = .peer(peer: peer, chatPeer: peer)
case let .deviceContact(id, contact):
@ -465,7 +477,7 @@ private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer]
switch presentation {
case let .orderedByPresence(options):
orderedPeers = peers.sorted(by: { lhs, rhs in
if case let .peer(lhsPeer, _) = lhs, case let .peer(rhsPeer, _) = rhs {
if case let .peer(lhsPeer, _, _) = lhs, case let .peer(rhsPeer, _, _) = rhs {
let lhsPresence = presences[lhsPeer.id]
let rhsPresence = presences[rhsPeer.id]
if let lhsPresence = lhsPresence as? TelegramUserPresence, let rhsPresence = rhsPresence as? TelegramUserPresence {
@ -493,7 +505,7 @@ private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer]
let sortedPeers = peers.sorted(by: { lhs, rhs in
let result = lhs.indexName.isLessThan(other: rhs.indexName, ordering: sortOrder)
if result == .orderedSame {
if case let .peer(lhsPeer, _) = lhs, case let .peer(rhsPeer, _) = rhs {
if case let .peer(lhsPeer, _, _) = lhs, case let .peer(rhsPeer, _, _) = rhs {
return lhsPeer.id < rhsPeer.id
} else if case let .deviceContact(lhsId, _) = lhs, case let .deviceContact(rhsId, _) = rhs {
return lhsId < rhsId
@ -596,12 +608,12 @@ private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer]
header = headers[orderedPeers[i].id]
}
var presence: PeerPresence?
if case let .peer(peer, _) = orderedPeers[i] {
if case let .peer(peer, _, _) = orderedPeers[i] {
presence = presences[peer.id]
}
let enabled: Bool
switch orderedPeers[i] {
case let .peer(peer, _):
case let .peer(peer, _, _):
enabled = !disabledPeerIds.contains(peer.id)
default:
enabled = true
@ -687,7 +699,7 @@ public struct ContactListAdditionalOption: Equatable {
enum ContactListPresentation {
case orderedByPresence(options: [ContactListAdditionalOption])
case natural(options: [ContactListAdditionalOption])
case search(signal: Signal<String, NoError>, searchChatList: Bool, searchDeviceContacts: Bool)
case search(signal: Signal<String, NoError>, searchChatList: Bool, searchDeviceContacts: Bool, searchGroups: Bool)
var sortOrder: ContactsSortOrder? {
switch self {
@ -929,35 +941,61 @@ final class ContactListNode: ASDisplayNode {
generateSections = true
}
if case let .search(query, searchChatList, searchDeviceContacts) = presentation {
if case let .search(query, searchChatList, searchDeviceContacts, searchGroups) = presentation {
return query
|> mapToSignal { query in
let foundLocalContacts: Signal<([Peer], [PeerId : PeerPresence]), NoError>
let foundLocalContacts: Signal<([FoundPeer], [PeerId: PeerPresence]), NoError>
if searchChatList {
let foundChatListPeers = context.account.postbox.searchPeers(query: query.lowercased())
foundLocalContacts = foundChatListPeers
|> mapToSignal { peers -> Signal<([Peer], [PeerId : PeerPresence]), NoError> in
var resultPeers: [Peer] = []
|> mapToSignal { peers -> Signal<([FoundPeer], [PeerId: PeerPresence]), NoError> in
var resultPeers: [FoundPeer] = []
for peer in peers {
if peer.peerId.namespace != Namespaces.Peer.CloudUser {
continue
}
if let mainPeer = peer.chatMainPeer {
resultPeers.append(mainPeer)
}
}
return context.account.postbox.transaction { transaction -> ([Peer], [PeerId : PeerPresence]) in
var resultPresences: [PeerId: PeerPresence] = [:]
for peer in resultPeers {
if let presence = transaction.getPeerPresence(peerId: peer.id) {
resultPresences[peer.id] = presence
if searchGroups {
let mainPeer = peer.chatMainPeer
if let _ = mainPeer as? TelegramUser {
} else if let _ = mainPeer as? TelegramGroup {
} else if let channel = mainPeer as? TelegramChannel {
if case .broadcast = channel.info {
continue
}
} else {
continue
}
} else {
if peer.peerId.namespace != Namespaces.Peer.CloudUser {
continue
}
}
return (resultPeers, resultPresences)
if let mainPeer = peer.chatMainPeer {
resultPeers.append(FoundPeer(peer: mainPeer, subscribers: nil))
}
}
return context.account.postbox.transaction { transaction -> ([FoundPeer], [PeerId: PeerPresence]) in
var resultPresences: [PeerId: PeerPresence] = [:]
var mappedPeers: [FoundPeer] = []
for peer in resultPeers {
if let presence = transaction.getPeerPresence(peerId: peer.peer.id) {
resultPresences[peer.peer.id] = presence
}
if let _ = peer.peer as? TelegramChannel {
var subscribers: Int32?
if let cachedData = transaction.getPeerCachedData(peerId: peer.peer.id) as? CachedChannelData {
subscribers = cachedData.participantsSummary.memberCount
}
mappedPeers.append(FoundPeer(peer: peer.peer, subscribers: subscribers))
} else {
mappedPeers.append(peer)
}
}
return (mappedPeers, resultPresences)
}
}
} else {
foundLocalContacts = context.account.postbox.searchContacts(query: query.lowercased())
|> map { peers, presences -> ([FoundPeer], [PeerId: PeerPresence]) in
return (peers.map({ FoundPeer(peer: $0, subscribers: nil) }), presences)
}
}
let foundRemoteContacts: Signal<([FoundPeer], [FoundPeer]), NoError> = .single(([], []))
|> then(
@ -992,19 +1030,38 @@ final class ContactListNode: ASDisplayNode {
var peers: [ContactListPeer] = []
for peer in localPeersAndStatuses.0 {
if !existingPeerIds.contains(peer.id) {
existingPeerIds.insert(peer.id)
peers.append(.peer(peer: peer, isGlobal: false))
if searchDeviceContacts, let user = peer as? TelegramUser, let phone = user.phone {
if !existingPeerIds.contains(peer.peer.id) {
existingPeerIds.insert(peer.peer.id)
peers.append(.peer(peer: peer.peer, isGlobal: false, participantCount: peer.subscribers))
if searchDeviceContacts, let user = peer.peer as? TelegramUser, let phone = user.phone {
existingNormalizedPhoneNumbers.insert(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(phone)))
}
}
}
for peer in remotePeers.0 {
let matches: Bool
if peer.peer is TelegramUser {
matches = true
} else if searchGroups {
if peer.peer is TelegramGroup {
matches = true
} else if let channel = peer.peer as? TelegramChannel {
if case .group = channel.info {
matches = true
} else {
matches = false
}
} else {
matches = false
}
} else {
matches = false
}
if matches {
if !existingPeerIds.contains(peer.peer.id) {
existingPeerIds.insert(peer.peer.id)
peers.append(.peer(peer: peer.peer, isGlobal: true))
peers.append(.peer(peer: peer.peer, isGlobal: true, participantCount: peer.subscribers))
if searchDeviceContacts, let user = peer.peer as? TelegramUser, let phone = user.phone {
existingNormalizedPhoneNumbers.insert(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(phone)))
}
@ -1012,10 +1069,29 @@ final class ContactListNode: ASDisplayNode {
}
}
for peer in remotePeers.1 {
let matches: Bool
if peer.peer is TelegramUser {
matches = true
} else if searchGroups {
if peer.peer is TelegramGroup {
matches = true
} else if let channel = peer.peer as? TelegramChannel {
if case .group = channel.info {
matches = true
} else {
matches = false
}
} else {
matches = false
}
} else {
matches = false
}
if matches {
if !existingPeerIds.contains(peer.peer.id) {
existingPeerIds.insert(peer.peer.id)
peers.append(.peer(peer: peer.peer, isGlobal: true))
peers.append(.peer(peer: peer.peer, isGlobal: true, participantCount: peer.subscribers))
if searchDeviceContacts, let user = peer.peer as? TelegramUser, let phone = user.phone {
existingNormalizedPhoneNumbers.insert(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(phone)))
}
@ -1049,7 +1125,7 @@ final class ContactListNode: ASDisplayNode {
return (combineLatest(self.contactPeersViewPromise.get(), selectionStateSignal, themeAndStringsPromise.get(), contactsAuthorization.get(), contactsWarningSuppressed.get())
|> mapToQueue { view, selectionState, themeAndStrings, authorizationStatus, warningSuppressed -> Signal<ContactsListNodeTransition, NoError> in
let signal = deferred { () -> Signal<ContactsListNodeTransition, NoError> in
var peers = view.peers.map({ ContactListPeer.peer(peer: $0, isGlobal: false) })
var peers = view.peers.map({ ContactListPeer.peer(peer: $0, isGlobal: false, participantCount: nil) })
var existingPeerIds = Set<PeerId>()
var disabledPeerIds = Set<PeerId>()
for filter in filters {
@ -1065,7 +1141,7 @@ final class ContactListNode: ASDisplayNode {
peers = peers.filter { contact in
switch contact {
case let .peer(peer, _):
case let .peer(peer, _, _):
return !existingPeerIds.contains(peer.id)
default:
return true

View File

@ -7,7 +7,7 @@ import TelegramCore
enum ContactMultiselectionControllerMode {
case groupCreation
case peerSelection(searchChatList: Bool)
case peerSelection(searchChatList: Bool, searchGroups: Bool)
case channelCreation
}
@ -162,7 +162,7 @@ class ContactMultiselectionController: ViewController {
}
self.contactsNode.openPeer = { [weak self] peer in
if let strongSelf = self, case let .peer(peer, _) = peer {
if let strongSelf = self, case let .peer(peer, _, _) = peer {
var updatedCount: Int?
var addedToken: EditableTokenListToken?
var removedTokenId: AnyHashable?

View File

@ -50,8 +50,12 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
let placeholder: String
switch mode {
case .peerSelection:
placeholder = self.presentationData.strings.Contacts_SearchLabel
case let .peerSelection(_, searchGroups):
if searchGroups {
placeholder = self.presentationData.strings.Contacts_SearchUsersAndGroupsLabel
} else {
placeholder = self.presentationData.strings.Contacts_SearchLabel
}
default:
placeholder = self.presentationData.strings.Compose_TokenListPlaceholder
}
@ -96,10 +100,12 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
return state
}
var searchChatList = false
if case let .peerSelection(value) = mode {
searchChatList = value
var searchGroups = false
if case let .peerSelection(peerSelection) = mode {
searchChatList = peerSelection.searchChatList
searchGroups = peerSelection.searchGroups
}
let searchResultsNode = ContactListNode(context: context, presentation: .single(.search(signal: searchText.get(), searchChatList: searchChatList, searchDeviceContacts: false)), filters: filters, selectionState: selectionState)
let searchResultsNode = ContactListNode(context: context, presentation: .single(.search(signal: searchText.get(), searchChatList: searchChatList, searchDeviceContacts: false, searchGroups: searchGroups)), filters: filters, selectionState: selectionState)
searchResultsNode.openPeer = { peer in
self?.tokenListNode.setText("")
self?.openPeer?(peer)

View File

@ -193,7 +193,7 @@ public class ContactsController: ViewController {
if let strongSelf = self {
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
switch peer {
case let .peer(peer, _):
case let .peer(peer, _, _):
if let navigationController = strongSelf.navigationController as? NavigationController {
navigateToChatController(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), purposefulAction: { [weak self] in
if fromSearch {

View File

@ -70,7 +70,7 @@ private struct ContactListSearchEntry: Identifiable, Comparable {
}
case .global:
header = ChatListSearchItemHeader(type: .globalPeers, theme: self.theme, strings: self.strings, actionTitle: nil, action: nil)
if case let .peer(peer, _) = self.peer, let _ = peer.addressName {
if case let .peer(peer, _, _) = self.peer, let _ = peer.addressName {
status = .addressName("")
} else {
status = .none
@ -82,7 +82,7 @@ private struct ContactListSearchEntry: Identifiable, Comparable {
let peer = self.peer
let peerItem: ContactsPeerItemPeer
switch peer {
case let .peer(peer, _):
case let .peer(peer, _, _):
peerItem = .peer(peer: peer, chatPeer: peer)
case let .deviceContact(stableId, contact):
peerItem = .deviceContact(stableId: stableId, contact: contact)
@ -220,7 +220,7 @@ final class ContactsSearchContainerNode: SearchDisplayControllerContentNode {
if onlyWriteable {
enabled = canSendMessagesToPeer(peer)
}
entries.append(ContactListSearchEntry(index: index, theme: themeAndStrings.0, strings: themeAndStrings.1, peer: .peer(peer: peer, isGlobal: false), presence: localPeersAndPresences.1[peer.id], group: .contacts, enabled: enabled))
entries.append(ContactListSearchEntry(index: index, theme: themeAndStrings.0, strings: themeAndStrings.1, peer: .peer(peer: peer, isGlobal: false, participantCount: nil), presence: localPeersAndPresences.1[peer.id], group: .contacts, enabled: enabled))
if searchDeviceContacts, let user = peer as? TelegramUser, let phone = user.phone {
existingNormalizedPhoneNumbers.insert(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(phone)))
}
@ -239,7 +239,7 @@ final class ContactsSearchContainerNode: SearchDisplayControllerContentNode {
enabled = canSendMessagesToPeer(peer.peer)
}
entries.append(ContactListSearchEntry(index: index, theme: themeAndStrings.0, strings: themeAndStrings.1, peer: .peer(peer: peer.peer, isGlobal: true), presence: nil, group: .global, enabled: enabled))
entries.append(ContactListSearchEntry(index: index, theme: themeAndStrings.0, strings: themeAndStrings.1, peer: .peer(peer: peer.peer, isGlobal: true, participantCount: peer.subscribers), presence: nil, group: .global, enabled: enabled))
if searchDeviceContacts, let user = peer.peer as? TelegramUser, let phone = user.phone {
existingNormalizedPhoneNumbers.insert(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(phone)))
}
@ -258,7 +258,7 @@ final class ContactsSearchContainerNode: SearchDisplayControllerContentNode {
enabled = canSendMessagesToPeer(peer.peer)
}
entries.append(ContactListSearchEntry(index: index, theme: themeAndStrings.0, strings: themeAndStrings.1, peer: .peer(peer: peer.peer, isGlobal: true), presence: nil, group: .global, enabled: enabled))
entries.append(ContactListSearchEntry(index: index, theme: themeAndStrings.0, strings: themeAndStrings.1, peer: .peer(peer: peer.peer, isGlobal: true, participantCount: peer.subscribers), presence: nil, group: .global, enabled: enabled))
if searchDeviceContacts, let user = peer.peer as? TelegramUser, let phone = user.phone {
existingNormalizedPhoneNumbers.insert(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(phone)))
}

View File

@ -1139,7 +1139,7 @@ private func addContactToExisting(context: AccountContext, parentController: Vie
if let peer = peer {
let dataSignal: Signal<(Peer?, DeviceContactStableId?), NoError>
switch peer {
case let .peer(contact, _):
case let .peer(contact, _, _):
guard let contact = contact as? TelegramUser, let phoneNumber = contact.phone else {
return
}

View File

@ -1492,14 +1492,14 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
let contactsController: ViewController
if peerView.peerId.namespace == Namespaces.Peer.CloudGroup {
contactsController = ContactSelectionController(context: context, autoDismiss: false, title: { $0.GroupInfo_AddParticipantTitle }, options: options, confirmation: { peer in
if let confirmationImpl = confirmationImpl, case let .peer(peer, _) = peer {
if let confirmationImpl = confirmationImpl, case let .peer(peer, _, _) = peer {
return confirmationImpl(peer.id)
} else {
return .single(false)
}
})
} else {
contactsController = ContactMultiselectionController(context: context, mode: .peerSelection(searchChatList: false), options: options, filters: [.excludeSelf, .disable(recentIds)])
contactsController = ContactMultiselectionController(context: context, mode: .peerSelection(searchChatList: false, searchGroups: false), options: options, filters: [.excludeSelf, .disable(recentIds)])
}
confirmationImpl = { [weak contactsController] peerId in
@ -1525,7 +1525,7 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
}
let addMember: (ContactListPeer) -> Signal<Void, NoError> = { memberPeer -> Signal<Void, NoError> in
if case let .peer(selectedPeer, _) = memberPeer {
if case let .peer(selectedPeer, _, _) = memberPeer {
let memberId = selectedPeer.id
if peerView.peerId.namespace == Namespaces.Peer.CloudChannel {
return context.peerChannelMemberCategoriesContextsManager.addMember(account: context.account, peerId: peerView.peerId, memberId: memberId)

View File

@ -246,7 +246,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, onlyWriteable: true, categories: [.cloudContacts, .global], openPeer: { [weak self] peer in
if let strongSelf = self {
switch peer {
case let .peer(peer, _):
case let .peer(peer, _, _):
let _ = (strongSelf.context.account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(peer.id)
} |> deliverOnMainQueue).start(next: { peer in
@ -333,7 +333,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
self?.requestActivateSearch?()
}
contactListNode.openPeer = { [weak self] peer in
if case let .peer(peer, _) = peer {
if case let .peer(peer, _, _) = peer {
self?.requestOpenPeer?(peer.id)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -12,13 +12,14 @@ private final class PrivacyAndSecurityControllerArguments {
let openVoiceCallPrivacy: () -> Void
let openProfilePhotoPrivacy: () -> Void
let openForwardPrivacy: () -> Void
let openPhoneNumberPrivacy: () -> Void
let openPasscode: () -> Void
let openTwoStepVerification: () -> Void
let openActiveSessions: () -> Void
let setupAccountAutoremove: () -> Void
let openDataSettings: () -> Void
init(account: Account, openBlockedUsers: @escaping () -> Void, openLastSeenPrivacy: @escaping () -> Void, openGroupsPrivacy: @escaping () -> Void, openVoiceCallPrivacy: @escaping () -> Void, openProfilePhotoPrivacy: @escaping () -> Void, openForwardPrivacy: @escaping () -> Void, openPasscode: @escaping () -> Void, openTwoStepVerification: @escaping () -> Void, openActiveSessions: @escaping () -> Void, setupAccountAutoremove: @escaping () -> Void, openDataSettings: @escaping () -> Void) {
init(account: Account, openBlockedUsers: @escaping () -> Void, openLastSeenPrivacy: @escaping () -> Void, openGroupsPrivacy: @escaping () -> Void, openVoiceCallPrivacy: @escaping () -> Void, openProfilePhotoPrivacy: @escaping () -> Void, openForwardPrivacy: @escaping () -> Void, openPhoneNumberPrivacy: @escaping () -> Void, openPasscode: @escaping () -> Void, openTwoStepVerification: @escaping () -> Void, openActiveSessions: @escaping () -> Void, setupAccountAutoremove: @escaping () -> Void, openDataSettings: @escaping () -> Void) {
self.account = account
self.openBlockedUsers = openBlockedUsers
self.openLastSeenPrivacy = openLastSeenPrivacy
@ -26,6 +27,7 @@ private final class PrivacyAndSecurityControllerArguments {
self.openVoiceCallPrivacy = openVoiceCallPrivacy
self.openProfilePhotoPrivacy = openProfilePhotoPrivacy
self.openForwardPrivacy = openForwardPrivacy
self.openPhoneNumberPrivacy = openPhoneNumberPrivacy
self.openPasscode = openPasscode
self.openTwoStepVerification = openTwoStepVerification
self.openActiveSessions = openActiveSessions
@ -35,8 +37,8 @@ private final class PrivacyAndSecurityControllerArguments {
}
private enum PrivacyAndSecuritySection: Int32 {
case general
case privacy
case security
case account
case dataSettings
}
@ -56,14 +58,14 @@ public enum PrivacyAndSecurityEntryTag: ItemListItemTag {
private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
case privacyHeader(PresentationTheme, String)
case blockedPeers(PresentationTheme, String)
case phoneNumberPrivacy(PresentationTheme, String, String)
case lastSeenPrivacy(PresentationTheme, String, String)
case profilePhotoPrivacy(PresentationTheme, String, String)
case voiceCallPrivacy(PresentationTheme, String, String)
case forwardPrivacy(PresentationTheme, String, String)
case groupPrivacy(PresentationTheme, String, String)
case selectivePrivacyInfo(PresentationTheme, String)
case securityHeader(PresentationTheme, String)
case passcode(PresentationTheme, String)
case passcode(PresentationTheme, String, Bool)
case twoStepVerification(PresentationTheme, String)
case activeSessions(PresentationTheme, String)
case accountHeader(PresentationTheme, String)
@ -74,10 +76,10 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
var section: ItemListSectionId {
switch self {
case .privacyHeader, .blockedPeers, .lastSeenPrivacy, .profilePhotoPrivacy, .forwardPrivacy, .groupPrivacy, .selectivePrivacyInfo, .voiceCallPrivacy:
case .blockedPeers, .activeSessions, .passcode, .twoStepVerification:
return PrivacyAndSecuritySection.general.rawValue
case .privacyHeader, .phoneNumberPrivacy, .lastSeenPrivacy, .profilePhotoPrivacy, .forwardPrivacy, .groupPrivacy, .selectivePrivacyInfo, .voiceCallPrivacy:
return PrivacyAndSecuritySection.privacy.rawValue
case .securityHeader, .passcode, .twoStepVerification, .activeSessions:
return PrivacyAndSecuritySection.security.rawValue
case .accountHeader, .accountTimeout, .accountInfo:
return PrivacyAndSecuritySection.account.rawValue
case .dataSettings, .dataSettingsInfo:
@ -87,40 +89,40 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
var stableId: Int32 {
switch self {
case .privacyHeader:
return 0
case .blockedPeers:
return 1
case .lastSeenPrivacy:
return 2
case .profilePhotoPrivacy:
return 3
case .voiceCallPrivacy:
return 4
case .forwardPrivacy:
return 5
case .groupPrivacy:
return 6
case .selectivePrivacyInfo:
return 7
case .securityHeader:
return 8
case .passcode:
return 9
case .twoStepVerification:
return 10
case .activeSessions:
return 2
case .passcode:
return 3
case .twoStepVerification:
return 4
case .privacyHeader:
return 5
case .phoneNumberPrivacy:
return 6
case .lastSeenPrivacy:
return 7
case .profilePhotoPrivacy:
return 8
case .voiceCallPrivacy:
return 9
case .forwardPrivacy:
return 10
case .groupPrivacy:
return 11
case .accountHeader:
case .selectivePrivacyInfo:
return 12
case .accountTimeout:
case .accountHeader:
return 13
case .accountInfo:
case .accountTimeout:
return 14
case .dataSettings:
case .accountInfo:
return 15
case .dataSettingsInfo:
case .dataSettings:
return 16
case .dataSettingsInfo:
return 17
}
}
@ -138,6 +140,12 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
} else {
return false
}
case let .phoneNumberPrivacy(lhsTheme, lhsText, lhsValue):
if case let .phoneNumberPrivacy(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
} else {
return false
}
case let .lastSeenPrivacy(lhsTheme, lhsText, lhsValue):
if case let .lastSeenPrivacy(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
@ -174,14 +182,8 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
} else {
return false
}
case let .securityHeader(lhsTheme, lhsText):
if case let .securityHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .passcode(lhsTheme, lhsText):
if case let .passcode(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
case let .passcode(lhsTheme, lhsText, lhsHasFaceId):
if case let .passcode(rhsTheme, rhsText, rhsHasFaceId) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsHasFaceId == rhsHasFaceId {
return true
} else {
return false
@ -240,9 +242,13 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
case let .privacyHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .blockedPeers(theme, text):
return ItemListDisclosureItem(theme: theme, title: text, label: "", sectionId: self.section, style: .blocks, action: {
return ItemListDisclosureItem(theme: theme, icon: UIImage(bundleImageName: "Settings/MenuIcons/Blocked")?.precomposed(), title: text, label: "", sectionId: self.section, style: .blocks, action: {
arguments.openBlockedUsers()
})
case let .phoneNumberPrivacy(theme, text, value):
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .blocks, action: {
arguments.openPhoneNumberPrivacy()
})
case let .lastSeenPrivacy(theme, text, value):
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .blocks, action: {
arguments.openLastSeenPrivacy()
@ -265,18 +271,16 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
return ItemListDisclosureItem(theme: theme, title: text, label: value, sectionId: self.section, style: .blocks, action: {
arguments.openVoiceCallPrivacy()
})
case let .securityHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .passcode(theme, text):
return ItemListDisclosureItem(theme: theme, title: text, label: "", sectionId: self.section, style: .blocks, action: {
case let .passcode(theme, text, hasFaceId):
return ItemListDisclosureItem(theme: theme, icon: UIImage(bundleImageName: hasFaceId ? "Settings/MenuIcons/FaceId" : "Settings/MenuIcons/TouchId")?.precomposed(), title: text, label: "", sectionId: self.section, style: .blocks, action: {
arguments.openPasscode()
})
case let .twoStepVerification(theme, text):
return ItemListDisclosureItem(theme: theme, title: text, label: "", sectionId: self.section, style: .blocks, action: {
return ItemListDisclosureItem(theme: theme, icon: UIImage(bundleImageName: "Settings/MenuIcons/TwoStepAuth")?.precomposed(), title: text, label: "", sectionId: self.section, style: .blocks, action: {
arguments.openTwoStepVerification()
})
case let .activeSessions(theme, text):
return ItemListDisclosureItem(theme: theme, title: text, label: "", sectionId: self.section, style: .blocks, action: {
return ItemListDisclosureItem(theme: theme, icon: UIImage(bundleImageName: "Settings/MenuIcons/Sessions")?.precomposed(), title: text, label: "", sectionId: self.section, style: .blocks, action: {
arguments.openActiveSessions()
})
case let .accountHeader(theme, text):
@ -301,27 +305,35 @@ private struct PrivacyAndSecurityControllerState: Equatable {
var updatingAccountTimeoutValue: Int32? = nil
}
private func countForSelectivePeers(_ peers: [PeerId: SelectivePrivacyPeer]) -> Int {
var result = 0
for (_, peer) in peers {
result += peer.userCount
}
return result
}
private func stringForSelectiveSettings(strings: PresentationStrings, settings: SelectivePrivacySettings) -> String {
switch settings {
case let .disableEveryone(enableFor):
if enableFor.isEmpty {
return strings.PrivacySettings_LastSeenNobody
} else {
return strings.PrivacySettings_LastSeenNobodyPlus("\(enableFor.count)").0
return strings.PrivacySettings_LastSeenNobodyPlus("\(countForSelectivePeers(enableFor))").0
}
case let .enableEveryone(disableFor):
if disableFor.isEmpty {
return strings.PrivacySettings_LastSeenEverybody
} else {
return strings.PrivacySettings_LastSeenEverybodyMinus("\(disableFor.count)").0
return strings.PrivacySettings_LastSeenEverybodyMinus("\(countForSelectivePeers(disableFor))").0
}
case let .enableContacts(enableFor, disableFor):
if !enableFor.isEmpty && !disableFor.isEmpty {
return strings.PrivacySettings_LastSeenContactsMinusPlus("\(enableFor.count)", "\(disableFor.count)").0
return strings.PrivacySettings_LastSeenContactsMinusPlus("\(countForSelectivePeers(enableFor))", "\(countForSelectivePeers(disableFor))").0
} else if !enableFor.isEmpty {
return strings.PrivacySettings_LastSeenContactsPlus("\(enableFor.count)").0
return strings.PrivacySettings_LastSeenContactsPlus("\(countForSelectivePeers(enableFor))").0
} else if !disableFor.isEmpty {
return strings.PrivacySettings_LastSeenContactsMinus("\(disableFor.count)").0
return strings.PrivacySettings_LastSeenContactsMinus("\(countForSelectivePeers(disableFor))").0
} else {
return strings.PrivacySettings_LastSeenContacts
}
@ -331,9 +343,23 @@ private func stringForSelectiveSettings(strings: PresentationStrings, settings:
private func privacyAndSecurityControllerEntries(presentationData: PresentationData, state: PrivacyAndSecurityControllerState, privacySettings: AccountPrivacySettings?) -> [PrivacyAndSecurityEntry] {
var entries: [PrivacyAndSecurityEntry] = []
entries.append(.privacyHeader(presentationData.theme, presentationData.strings.PrivacySettings_PrivacyTitle))
entries.append(.blockedPeers(presentationData.theme, presentationData.strings.Settings_BlockedUsers))
entries.append(.activeSessions(presentationData.theme, presentationData.strings.PrivacySettings_AuthSessions))
if let biometricAuthentication = LocalAuth.biometricAuthentication {
switch biometricAuthentication {
case .touchId:
entries.append(.passcode(presentationData.theme, presentationData.strings.PrivacySettings_PasscodeAndTouchId, true))
case .faceId:
entries.append(.passcode(presentationData.theme, presentationData.strings.PrivacySettings_PasscodeAndFaceId, false))
}
} else {
entries.append(.passcode(presentationData.theme, presentationData.strings.PrivacySettings_Passcode, false))
}
entries.append(.twoStepVerification(presentationData.theme, presentationData.strings.PrivacySettings_TwoStepAuth))
entries.append(.privacyHeader(presentationData.theme, presentationData.strings.PrivacySettings_PrivacyTitle))
if let privacySettings = privacySettings {
entries.append(.phoneNumberPrivacy(presentationData.theme, presentationData.strings.PrivacySettings_PhoneNumber, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.phoneNumber)))
entries.append(.lastSeenPrivacy(presentationData.theme, presentationData.strings.PrivacySettings_LastSeen, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.presence)))
entries.append(.profilePhotoPrivacy(presentationData.theme, presentationData.strings.Privacy_ProfilePhoto, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.profilePhoto)))
entries.append(.voiceCallPrivacy(presentationData.theme, presentationData.strings.Privacy_Calls, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.voiceCalls)))
@ -350,19 +376,6 @@ private func privacyAndSecurityControllerEntries(presentationData: PresentationD
entries.append(.selectivePrivacyInfo(presentationData.theme, presentationData.strings.PrivacyLastSeenSettings_GroupsAndChannelsHelp))
}
entries.append(.securityHeader(presentationData.theme, presentationData.strings.PrivacySettings_SecurityTitle))
if let biometricAuthentication = LocalAuth.biometricAuthentication {
switch biometricAuthentication {
case .touchId:
entries.append(.passcode(presentationData.theme, presentationData.strings.PrivacySettings_PasscodeAndTouchId))
case .faceId:
entries.append(.passcode(presentationData.theme, presentationData.strings.PrivacySettings_PasscodeAndFaceId))
}
} else {
entries.append(.passcode(presentationData.theme, presentationData.strings.PrivacySettings_Passcode))
}
entries.append(.twoStepVerification(presentationData.theme, presentationData.strings.PrivacySettings_TwoStepAuth))
entries.append(.activeSessions(presentationData.theme, presentationData.strings.PrivacySettings_AuthSessions))
entries.append(.accountHeader(presentationData.theme, presentationData.strings.PrivacySettings_DeleteAccountTitle.uppercased()))
if let privacySettings = privacySettings {
let value: Int32
@ -422,7 +435,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: updated, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: updated, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, accountRemovalTimeout: value.accountRemovalTimeout)))
}
return .complete()
}
@ -445,7 +458,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: updated, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: updated, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, accountRemovalTimeout: value.accountRemovalTimeout)))
}
return .complete()
}
@ -482,7 +495,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: updated, voiceCallsP2P: updatedCallsPrivacy, profilePhoto: value.profilePhoto, forwards: value.forwards, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: updated, voiceCallsP2P: updatedCallsPrivacy, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, accountRemovalTimeout: value.accountRemovalTimeout)))
}
return .complete()
}
@ -505,7 +518,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: updated, forwards: value.forwards, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: updated, forwards: value.forwards, phoneNumber: value.phoneNumber, accountRemovalTimeout: value.accountRemovalTimeout)))
}
return .complete()
}
@ -528,7 +541,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: updated, accountRemovalTimeout: value.accountRemovalTimeout)))
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: updated, phoneNumber: value.phoneNumber, accountRemovalTimeout: value.accountRemovalTimeout)))
}
return .complete()
}
@ -537,6 +550,29 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
}))
}
}))
}, openPhoneNumberPrivacy: {
let signal = privacySettingsPromise.get()
|> take(1)
|> deliverOnMainQueue
currentInfoDisposable.set(signal.start(next: { [weak currentInfoDisposable] info in
if let info = info {
pushControllerImpl?(selectivePrivacySettingsController(context: context, kind: .phoneNumber, current: info.phoneNumber, updated: { updated, _ in
if let currentInfoDisposable = currentInfoDisposable {
let applySetting: Signal<Void, NoError> = privacySettingsPromise.get()
|> filter { $0 != nil }
|> take(1)
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: updated, accountRemovalTimeout: value.accountRemovalTimeout)))
}
return .complete()
}
currentInfoDisposable.set(applySetting.start())
}
}))
}
}))
}, openPasscode: {
let _ = passcodeOptionsAccessController(context: context, pushController: { controller in
replaceTopControllerImpl?(controller)
@ -570,24 +606,24 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
return state
}
let applyTimeout: Signal<Void, NoError> = privacySettingsPromise.get()
|> filter { $0 != nil }
|> take(1)
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, accountRemovalTimeout: timeout)))
}
return .complete()
|> filter { $0 != nil }
|> take(1)
|> deliverOnMainQueue
|> mapToSignal { value -> Signal<Void, NoError> in
if let value = value {
privacySettingsPromise.set(.single(AccountPrivacySettings(presence: value.presence, groupInvitations: value.groupInvitations, voiceCalls: value.voiceCalls, voiceCallsP2P: value.voiceCallsP2P, profilePhoto: value.profilePhoto, forwards: value.forwards, phoneNumber: value.phoneNumber, accountRemovalTimeout: timeout)))
}
updateAccountTimeoutDisposable.set((updateAccountRemovalTimeout(account: context.account, timeout: timeout)
|> then(applyTimeout)
|> deliverOnMainQueue).start(completed: {
updateState { state in
var state = state
state.updatingAccountTimeoutValue = nil
return state
}
}))
return .complete()
}
updateAccountTimeoutDisposable.set((updateAccountRemovalTimeout(account: context.account, timeout: timeout)
|> then(applyTimeout)
|> deliverOnMainQueue).start(completed: {
updateState { state in
var state = state
state.updatingAccountTimeoutValue = nil
return state
}
}))
}
}
let timeoutValues: [Int32] = [

View File

@ -10,6 +10,7 @@ enum SelectivePrivacySettingsKind {
case voiceCalls
case profilePhoto
case forwards
case phoneNumber
}
private enum SelectivePrivacySettingType {
@ -64,11 +65,15 @@ private enum SelectivePrivacySettingsSection: Int32 {
case callsIntegrationEnabled
}
private func stringForUserCount(_ count: Int, strings: PresentationStrings) -> String {
if count == 0 {
private func stringForUserCount(_ peers: [PeerId: SelectivePrivacyPeer], strings: PresentationStrings) -> String {
if peers.isEmpty {
return strings.PrivacyLastSeenSettings_EmpryUsersPlaceholder
} else {
return strings.UserCount(Int32(count))
var result = 0
for (_, peer) in peers {
result += peer.userCount
}
return strings.UserCount(Int32(result))
}
}
@ -80,6 +85,7 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
case contacts(PresentationTheme, String, Bool)
case nobody(PresentationTheme, String, Bool)
case settingInfo(PresentationTheme, String)
case exceptionsHeader(PresentationTheme, String)
case disableFor(PresentationTheme, String, String)
case enableFor(PresentationTheme, String, String)
case peersInfo(PresentationTheme, String)
@ -100,7 +106,7 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
return SelectivePrivacySettingsSection.forwards.rawValue
case .settingHeader, .everybody, .contacts, .nobody, .settingInfo:
return SelectivePrivacySettingsSection.setting.rawValue
case .disableFor, .enableFor, .peersInfo:
case .exceptionsHeader, .disableFor, .enableFor, .peersInfo:
return SelectivePrivacySettingsSection.peers.rawValue
case .callsP2PHeader, .callsP2PAlways, .callsP2PContacts, .callsP2PNever, .callsP2PInfo:
return SelectivePrivacySettingsSection.callsP2P.rawValue
@ -127,32 +133,34 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
return 5
case .settingInfo:
return 6
case .disableFor:
case .exceptionsHeader:
return 7
case .enableFor:
case .disableFor:
return 8
case .peersInfo:
case .enableFor:
return 9
case .callsP2PHeader:
case .peersInfo:
return 10
case .callsP2PAlways:
case .callsP2PHeader:
return 11
case .callsP2PContacts:
case .callsP2PAlways:
return 12
case .callsP2PNever:
case .callsP2PContacts:
return 13
case .callsP2PInfo:
case .callsP2PNever:
return 14
case .callsP2PDisableFor:
case .callsP2PInfo:
return 15
case .callsP2PEnableFor:
case .callsP2PDisableFor:
return 16
case .callsP2PPeersInfo:
case .callsP2PEnableFor:
return 17
case .callsIntegrationEnabled:
case .callsP2PPeersInfo:
return 18
case .callsIntegrationInfo:
case .callsIntegrationEnabled:
return 19
case .callsIntegrationInfo:
return 20
}
}
@ -194,6 +202,12 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
} else {
return false
}
case let .exceptionsHeader(lhsTheme, lhsText):
if case let .exceptionsHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .settingInfo(lhsTheme, lhsText):
if case let .settingInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
@ -307,6 +321,8 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
})
case let .settingInfo(theme, text):
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
case let .exceptionsHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: 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(.main)
@ -355,19 +371,19 @@ private enum SelectivePrivacySettingsEntry: ItemListNodeEntry {
private struct SelectivePrivacySettingsControllerState: Equatable {
let setting: SelectivePrivacySettingType
let enableFor: Set<PeerId>
let disableFor: Set<PeerId>
let enableFor: [PeerId: SelectivePrivacyPeer]
let disableFor: [PeerId: SelectivePrivacyPeer]
let saving: Bool
let callDataSaving: VoiceCallDataSaving?
let callP2PMode: SelectivePrivacySettingType?
let callP2PEnableFor: Set<PeerId>?
let callP2PDisableFor: Set<PeerId>?
let callP2PEnableFor: [PeerId: SelectivePrivacyPeer]?
let callP2PDisableFor: [PeerId: SelectivePrivacyPeer]?
let callIntegrationAvailable: Bool?
let 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?) {
init(setting: SelectivePrivacySettingType, enableFor: [PeerId: SelectivePrivacyPeer], disableFor: [PeerId: SelectivePrivacyPeer], saving: Bool, callDataSaving: VoiceCallDataSaving?, callP2PMode: SelectivePrivacySettingType?, callP2PEnableFor: [PeerId: SelectivePrivacyPeer]?, callP2PDisableFor: [PeerId: SelectivePrivacyPeer]?, callIntegrationAvailable: Bool?, callIntegrationEnabled: Bool?) {
self.setting = setting
self.enableFor = enableFor
self.disableFor = disableFor
@ -419,11 +435,11 @@ private struct SelectivePrivacySettingsControllerState: Equatable {
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 {
func withUpdatedEnableFor(_ enableFor: [PeerId: SelectivePrivacyPeer]) -> SelectivePrivacySettingsControllerState {
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 {
func withUpdatedDisableFor(_ disableFor: [PeerId: SelectivePrivacyPeer]) -> SelectivePrivacySettingsControllerState {
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)
}
@ -435,11 +451,11 @@ private struct SelectivePrivacySettingsControllerState: Equatable {
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 {
func withUpdatedCallP2PEnableFor(_ enableFor: [PeerId: SelectivePrivacyPeer]) -> 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 {
func withUpdatedCallP2PDisableFor(_ disableFor: [PeerId: SelectivePrivacyPeer]) -> 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)
}
@ -481,6 +497,11 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
settingInfoText = presentationData.strings.Privacy_Forwards_CustomHelp
disableForText = presentationData.strings.Privacy_GroupsAndChannels_NeverAllow
enableForText = presentationData.strings.Privacy_GroupsAndChannels_AlwaysAllow
case .phoneNumber:
settingTitle = presentationData.strings.PrivacyPhoneNumberSettings_WhoCanSeeMyPhoneNumber
settingInfoText = presentationData.strings.PrivacyLastSeenSettings_CustomHelp
disableForText = presentationData.strings.PrivacyLastSeenSettings_NeverShareWith
enableForText = presentationData.strings.PrivacyLastSeenSettings_AlwaysShareWith
}
if case .forwards = kind {
@ -506,21 +527,23 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
entries.append(.everybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.setting == .everybody))
entries.append(.contacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.setting == .contacts))
switch kind {
case .presence, .voiceCalls, .forwards:
case .presence, .voiceCalls, .forwards, .phoneNumber:
entries.append(.nobody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenNobody, state.setting == .nobody))
case .groupInvitations, .profilePhoto:
break
}
entries.append(.settingInfo(presentationData.theme, settingInfoText))
entries.append(.exceptionsHeader(presentationData.theme, presentationData.strings.GroupInfo_Permissions_Exceptions))
switch state.setting {
case .everybody:
entries.append(.disableFor(presentationData.theme, disableForText, stringForUserCount(state.disableFor.count, strings: presentationData.strings)))
entries.append(.disableFor(presentationData.theme, disableForText, stringForUserCount(state.disableFor, strings: presentationData.strings)))
case .contacts:
entries.append(.disableFor(presentationData.theme, disableForText, stringForUserCount(state.disableFor.count, strings: presentationData.strings)))
entries.append(.enableFor(presentationData.theme, enableForText, stringForUserCount(state.enableFor.count, strings: presentationData.strings)))
entries.append(.disableFor(presentationData.theme, disableForText, stringForUserCount(state.disableFor, strings: presentationData.strings)))
entries.append(.enableFor(presentationData.theme, enableForText, stringForUserCount(state.enableFor, strings: presentationData.strings)))
case .nobody:
entries.append(.enableFor(presentationData.theme, enableForText, stringForUserCount(state.enableFor.count, strings: presentationData.strings)))
entries.append(.enableFor(presentationData.theme, enableForText, stringForUserCount(state.enableFor, strings: presentationData.strings)))
}
entries.append(.peersInfo(presentationData.theme, presentationData.strings.PrivacyLastSeenSettings_CustomShareSettingsHelp))
@ -535,12 +558,12 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
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)))
entries.append(.callsP2PDisableFor(presentationData.theme, disableForText, stringForUserCount(disableFor, 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)))
entries.append(.callsP2PDisableFor(presentationData.theme, disableForText, stringForUserCount(disableFor, strings: presentationData.strings)))
entries.append(.callsP2PEnableFor(presentationData.theme, enableForText, stringForUserCount(enableFor, strings: presentationData.strings)))
case .nobody:
entries.append(.callsP2PEnableFor(presentationData.theme, enableForText, stringForUserCount(enableFor.count, strings: presentationData.strings)))
entries.append(.callsP2PEnableFor(presentationData.theme, enableForText, stringForUserCount(enableFor, strings: presentationData.strings)))
}
}
entries.append(.callsP2PPeersInfo(presentationData.theme, presentationData.strings.PrivacyLastSeenSettings_CustomShareSettingsHelp))
@ -557,8 +580,8 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
func selectivePrivacySettingsController(context: AccountContext, kind: SelectivePrivacySettingsKind, current: SelectivePrivacySettings, callSettings: (SelectivePrivacySettings, VoiceCallSettings)? = nil, voipConfiguration: VoipConfiguration? = nil, callIntegrationAvailable: Bool? = nil, updated: @escaping (SelectivePrivacySettings, (SelectivePrivacySettings, VoiceCallSettings)?) -> Void) -> ViewController {
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
var initialEnableFor = Set<PeerId>()
var initialDisableFor = Set<PeerId>()
var initialEnableFor: [PeerId: SelectivePrivacyPeer] = [:]
var initialDisableFor: [PeerId: SelectivePrivacyPeer] = [:]
switch current {
case let .disableEveryone(enableFor):
initialEnableFor = enableFor
@ -568,18 +591,18 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
case let .enableEveryone(disableFor):
initialDisableFor = disableFor
}
var initialCallP2PEnableFor: Set<PeerId>?
var initialCallP2PDisableFor: Set<PeerId>?
var initialCallP2PEnableFor: [PeerId: SelectivePrivacyPeer]?
var initialCallP2PDisableFor: [PeerId: SelectivePrivacyPeer]?
if let callCurrent = callSettings?.0 {
switch callCurrent {
case let .disableEveryone(enableFor):
initialCallP2PEnableFor = enableFor
initialCallP2PDisableFor = Set<PeerId>()
initialCallP2PDisableFor = [:]
case let .enableContacts(enableFor, disableFor):
initialCallP2PEnableFor = enableFor
initialCallP2PDisableFor = disableFor
case let .enableEveryone(disableFor):
initialCallP2PEnableFor = Set<PeerId>()
initialCallP2PEnableFor = [:]
initialCallP2PDisableFor = disableFor
}
}
@ -616,27 +639,37 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
title = strings.Privacy_ProfilePhoto_AlwaysShareWith_Title
case .forwards:
title = strings.Privacy_Forwards_AlwaysAllow_Title
case .phoneNumber:
title = strings.PrivacyLastSeenSettings_AlwaysShareWith_Title
}
var peerIds = Set<PeerId>()
var peerIds: [PeerId: SelectivePrivacyPeer] = [:]
updateState { state in
switch target {
case .main:
peerIds = state.enableFor
case .callP2P:
if let callP2PEnableFor = state.callP2PEnableFor {
peerIds = callP2PEnableFor
}
case .main:
peerIds = state.enableFor
case .callP2P:
if let callP2PEnableFor = state.callP2PEnableFor {
peerIds = callP2PEnableFor
}
}
return state
}
pushControllerImpl?(selectivePrivacyPeersController(context: context, title: title, initialPeerIds: Array(peerIds), updated: { updatedPeerIds in
pushControllerImpl?(selectivePrivacyPeersController(context: context, title: title, initialPeers: peerIds, updated: { updatedPeerIds in
updateState { state in
switch target {
case .main:
return state.withUpdatedEnableFor(Set(updatedPeerIds)).withUpdatedDisableFor(state.disableFor.subtracting(Set(updatedPeerIds)))
var disableFor = state.disableFor
for (key, _) in updatedPeerIds {
disableFor.removeValue(forKey: key)
}
return state.withUpdatedEnableFor(updatedPeerIds).withUpdatedDisableFor(disableFor)
case .callP2P:
let callP2PDisableFor = state.callP2PDisableFor ?? Set()
return state.withUpdatedCallP2PEnableFor(Set(updatedPeerIds)).withUpdatedCallP2PDisableFor(callP2PDisableFor.subtracting(Set(updatedPeerIds)))
var callP2PDisableFor = state.callP2PDisableFor ?? [:]
var disableFor = state.disableFor
for (key, _) in updatedPeerIds {
callP2PDisableFor.removeValue(forKey: key)
}
return state.withUpdatedCallP2PEnableFor(updatedPeerIds).withUpdatedCallP2PDisableFor(callP2PDisableFor)
}
}
}))
@ -653,8 +686,10 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
title = strings.Privacy_ProfilePhoto_NeverShareWith_Title
case .forwards:
title = strings.Privacy_Forwards_NeverAllow_Title
case .phoneNumber:
title = strings.PrivacyLastSeenSettings_NeverShareWith_Title
}
var peerIds = Set<PeerId>()
var peerIds: [PeerId: SelectivePrivacyPeer] = [:]
updateState { state in
switch target {
case .main:
@ -666,14 +701,21 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
}
return state
}
pushControllerImpl?(selectivePrivacyPeersController(context: context, title: title, initialPeerIds: Array(peerIds), updated: { updatedPeerIds in
pushControllerImpl?(selectivePrivacyPeersController(context: context, title: title, initialPeers: peerIds, updated: { updatedPeerIds in
updateState { state in
switch target {
case .main:
return state.withUpdatedDisableFor(Set(updatedPeerIds)).withUpdatedEnableFor(state.enableFor.subtracting(Set(updatedPeerIds)))
var enableFor = state.enableFor
for (key, _) in updatedPeerIds {
enableFor.removeValue(forKey: key)
}
return state.withUpdatedDisableFor(updatedPeerIds).withUpdatedEnableFor(enableFor)
case .callP2P:
let callP2PEnableFor = state.callP2PEnableFor ?? Set()
return state.withUpdatedCallP2PDisableFor(Set(updatedPeerIds)).withUpdatedCallP2PEnableFor(callP2PEnableFor.subtracting(Set(updatedPeerIds)))
var callP2PEnableFor = state.callP2PEnableFor ?? [:]
for (key, _) in updatedPeerIds {
callP2PEnableFor.removeValue(forKey: key)
}
return state.withUpdatedCallP2PDisableFor(updatedPeerIds).withUpdatedCallP2PEnableFor(callP2PEnableFor)
}
}
}))
@ -711,6 +753,8 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
title = presentationData.strings.Privacy_ProfilePhoto
case .forwards:
title = presentationData.strings.Privacy_Forwards
case .phoneNumber:
title = presentationData.strings.Privacy_PhoneNumber
}
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
let listState = ItemListNodeState(entries: selectivePrivacySettingsControllerEntries(presentationData: presentationData, kind: kind, state: state, peerName: peerName), style: .blocks, animateChanges: false)
@ -767,6 +811,8 @@ func selectivePrivacySettingsController(context: AccountContext, kind: Selective
type = .profilePhoto
case .forwards:
type = .forwards
case .phoneNumber:
type = .phoneNumber
}
let updateSettingsSignal = updateSelectiveAccountPrivacySettings(account: context.account, type: type, settings: settings)

View File

@ -56,7 +56,7 @@ private enum SelectivePrivacyPeersEntryStableId: Hashable {
}
private enum SelectivePrivacyPeersEntry: ItemListNodeEntry {
case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, ItemListPeerItemEditing, Bool)
case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, SelectivePrivacyPeer, ItemListPeerItemEditing, Bool)
case addItem(PresentationTheme, String, Bool)
var section: ItemListSectionId {
@ -71,7 +71,7 @@ private enum SelectivePrivacyPeersEntry: ItemListNodeEntry {
var stableId: SelectivePrivacyPeersEntryStableId {
switch self {
case let .peerItem(_, _, _, _, _, peer, _, _):
return .peer(peer.id)
return .peer(peer.peer.id)
case .addItem:
return .add
}
@ -84,7 +84,7 @@ private enum SelectivePrivacyPeersEntry: ItemListNodeEntry {
if lhsIndex != rhsIndex {
return false
}
if !lhsPeer.isEqual(rhsPeer) {
if lhsPeer != rhsPeer {
return false
}
if lhsTheme !== rhsTheme {
@ -135,13 +135,28 @@ private enum SelectivePrivacyPeersEntry: ItemListNodeEntry {
func item(_ arguments: SelectivePrivacyPeersControllerArguments) -> ListViewItem {
switch self {
case let .peerItem(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer, editing, enabled):
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: editing, switchValue: nil, enabled: enabled, selectable: true, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { previousId, id in
var text: ItemListPeerItemText = .none
if let group = peer.peer as? TelegramGroup {
text = .text(strings.Conversation_StatusMembers(Int32(group.participantCount)))
} else if let channel = peer.peer as? TelegramChannel {
if let participantCount = peer.participantCount {
text = .text(strings.Conversation_StatusMembers(Int32(participantCount)))
} else {
switch channel.info {
case .group:
text = .text(strings.Group_Status)
case .broadcast:
text = .text(strings.Channel_Status)
}
}
}
return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer.peer, presence: nil, text: text, label: .none, editing: editing, switchValue: nil, enabled: enabled, selectable: true, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { previousId, id in
arguments.setPeerIdWithRevealedOptions(previousId, id)
}, removePeer: { peerId in
arguments.removePeer(peerId)
})
case let .addItem(theme, text, editing):
return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.addPersonIcon(theme), title: text, sectionId: self.section, editing: editing, action: {
return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.plusIconImage(theme), title: text, sectionId: self.section, editing: editing, action: {
arguments.addPeer()
})
}
@ -181,21 +196,21 @@ private struct SelectivePrivacyPeersControllerState: Equatable {
}
}
private func selectivePrivacyPeersControllerEntries(presentationData: PresentationData, state: SelectivePrivacyPeersControllerState, peers: [Peer]) -> [SelectivePrivacyPeersEntry] {
private func selectivePrivacyPeersControllerEntries(presentationData: PresentationData, state: SelectivePrivacyPeersControllerState, peers: [SelectivePrivacyPeer]) -> [SelectivePrivacyPeersEntry] {
var entries: [SelectivePrivacyPeersEntry] = []
var index: Int32 = 0
for peer in peers {
entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: state.editing, revealed: peer.id == state.peerIdWithRevealedOptions), true))
entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: state.editing, revealed: peer.peer.id == state.peerIdWithRevealedOptions), true))
index += 1
}
entries.append(.addItem(presentationData.theme, presentationData.strings.BlockedUsers_AddNew, state.editing))
entries.append(.addItem(presentationData.theme, presentationData.strings.Privacy_AddNewPeer, state.editing))
return entries
}
public func selectivePrivacyPeersController(context: AccountContext, title: String, initialPeerIds: [PeerId], updated: @escaping ([PeerId]) -> Void) -> ViewController {
public func selectivePrivacyPeersController(context: AccountContext, title: String, initialPeers: [PeerId: SelectivePrivacyPeer], updated: @escaping ([PeerId: SelectivePrivacyPeer]) -> Void) -> ViewController {
let statePromise = ValuePromise(SelectivePrivacyPeersControllerState(), ignoreRepeated: true)
let stateValue = Atomic(value: SelectivePrivacyPeersControllerState())
let updateState: ((SelectivePrivacyPeersControllerState) -> SelectivePrivacyPeersControllerState) -> Void = { f in
@ -212,15 +227,9 @@ public func selectivePrivacyPeersController(context: AccountContext, title: Stri
let removePeerDisposable = MetaDisposable()
actionsDisposable.add(removePeerDisposable)
let peersPromise = Promise<[Peer]>()
peersPromise.set(context.account.postbox.transaction { transaction -> [Peer] in
var result: [Peer] = []
for peerId in initialPeerIds {
if let peer = transaction.getPeer(peerId) {
result.append(peer)
}
}
return result
let peersPromise = Promise<[SelectivePrivacyPeer]>()
peersPromise.set(context.account.postbox.transaction { transaction -> [SelectivePrivacyPeer] in
return Array(initialPeers.values)
})
let arguments = SelectivePrivacyPeersControllerArguments(account: context.account, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
@ -233,39 +242,53 @@ public func selectivePrivacyPeersController(context: AccountContext, title: Stri
}
}, removePeer: { memberId in
let applyPeers: Signal<Void, NoError> = peersPromise.get()
|> take(1)
|> deliverOnMainQueue
|> mapToSignal { peers -> Signal<Void, NoError> in
var updatedPeers = peers
for i in 0 ..< updatedPeers.count {
if updatedPeers[i].id == memberId {
updatedPeers.remove(at: i)
break
}
|> take(1)
|> deliverOnMainQueue
|> mapToSignal { peers -> Signal<Void, NoError> in
var updatedPeers = peers
for i in 0 ..< updatedPeers.count {
if updatedPeers[i].peer.id == memberId {
updatedPeers.remove(at: i)
break
}
peersPromise.set(.single(updatedPeers))
updated(updatedPeers.map { $0.id })
return .complete()
}
peersPromise.set(.single(updatedPeers))
var updatedPeerDict: [PeerId: SelectivePrivacyPeer] = [:]
for peer in updatedPeers {
updatedPeerDict[peer.peer.id] = peer
}
updated(updatedPeerDict)
return .complete()
}
removePeerDisposable.set(applyPeers.start())
}, addPeer: {
let controller = ContactMultiselectionController(context: context, mode: .peerSelection(searchChatList: true), options: [])
addPeerDisposable.set((controller.result |> take(1) |> deliverOnMainQueue).start(next: { [weak controller] peerIds in
let controller = ContactMultiselectionController(context: context, mode: .peerSelection(searchChatList: true, searchGroups: true), options: [])
addPeerDisposable.set((controller.result
|> take(1)
|> deliverOnMainQueue).start(next: { [weak controller] peerIds in
let applyPeers: Signal<Void, NoError> = peersPromise.get()
|> take(1)
|> mapToSignal { peers -> Signal<[Peer], NoError> in
return context.account.postbox.transaction { transaction -> [Peer] in
|> mapToSignal { peers -> Signal<[SelectivePrivacyPeer], NoError> in
return context.account.postbox.transaction { transaction -> [SelectivePrivacyPeer] in
var updatedPeers = peers
var existingIds = Set(updatedPeers.map { $0.id })
var existingIds = Set(updatedPeers.map { $0.peer.id })
for peerId in peerIds {
guard case let .peer(peerId) = peerId else {
continue
}
if let peer = transaction.getPeer(peerId), !existingIds.contains(peerId) {
existingIds.insert(peerId)
updatedPeers.append(peer)
var participantCount: Int32?
if let channel = peer as? TelegramChannel, case .group = channel.info {
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData {
participantCount = cachedData.participantsSummary.memberCount
}
}
updatedPeers.append(SelectivePrivacyPeer(peer: peer, participantCount: participantCount))
}
}
return updatedPeers
@ -274,7 +297,13 @@ public func selectivePrivacyPeersController(context: AccountContext, title: Stri
|> deliverOnMainQueue
|> mapToSignal { updatedPeers -> Signal<Void, NoError> in
peersPromise.set(.single(updatedPeers))
updated(updatedPeers.map { $0.id })
var updatedPeerDict: [PeerId: SelectivePrivacyPeer] = [:]
for peer in updatedPeers {
updatedPeerDict[peer.peer.id] = peer
}
updated(updatedPeerDict)
return .complete()
}
@ -284,37 +313,38 @@ public func selectivePrivacyPeersController(context: AccountContext, title: Stri
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
})
var previousPeers: [Peer]?
var previousPeers: [SelectivePrivacyPeer]?
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), peersPromise.get())
|> deliverOnMainQueue
|> map { presentationData, state, peers -> (ItemListControllerState, (ItemListNodeState<SelectivePrivacyPeersEntry>, SelectivePrivacyPeersEntry.ItemGenerationArguments)) in
var rightNavigationButton: ItemListNavigationButton?
if !peers.isEmpty {
if state.editing {
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
updateState { state in
return state.withUpdatedEditing(false)
}
})
} else {
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: {
updateState { state in
return state.withUpdatedEditing(true)
}
})
}
|> deliverOnMainQueue
|> map { presentationData, state, peers -> (ItemListControllerState, (ItemListNodeState<SelectivePrivacyPeersEntry>, SelectivePrivacyPeersEntry.ItemGenerationArguments)) in
var rightNavigationButton: ItemListNavigationButton?
if !peers.isEmpty {
if state.editing {
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
updateState { state in
return state.withUpdatedEditing(false)
}
})
} else {
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: {
updateState { state in
return state.withUpdatedEditing(true)
}
})
}
let previous = previousPeers
previousPeers = peers
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(entries: selectivePrivacyPeersControllerEntries(presentationData: presentationData, state: state, peers: peers), style: .blocks, emptyStateItem: nil, animateChanges: previous != nil && previous!.count >= peers.count)
return (controllerState, (listState, arguments))
} |> afterDisposed {
actionsDisposable.dispose()
}
let previous = previousPeers
previousPeers = peers
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(entries: selectivePrivacyPeersControllerEntries(presentationData: presentationData, state: state, peers: peers), style: .blocks, emptyStateItem: nil, animateChanges: previous != nil && previous!.count >= peers.count)
return (controllerState, (listState, arguments))
}
|> afterDisposed {
actionsDisposable.dispose()
}
let controller = ItemListController(context: context, state: signal)

View File

@ -451,6 +451,8 @@ private func privacySearchableItems(context: AccountContext, privacySettings: Ac
current = info.profilePhoto
case .forwards:
current = info.forwards
case .phoneNumber:
current = info.phoneNumber
}
present(.push, selectivePrivacySettingsController(context: context, kind: kind, current: current, callSettings: callSettings != nil ? (info.voiceCallsP2P, callSettings!.0) : nil, voipConfiguration: callSettings?.1, callIntegrationAvailable: CallKitIntegration.isAvailable, updated: { updated, updatedCallSettings in

View File

@ -24,6 +24,7 @@ private final class UserInfoControllerArguments {
let displayCopyContextMenu: (UserInfoEntryTag, String) -> Void
let call: () -> Void
let openCallMenu: (String) -> Void
let requestPhoneNumber: () -> Void
let aboutLinkAction: (TextLinkItemActionType, TextLinkItem) -> Void
let displayAboutContextMenu: (String) -> Void
let openEncryptionKey: (SecretChatKeyFingerprint) -> Void
@ -34,7 +35,7 @@ private final class UserInfoControllerArguments {
let botPrivacy: () -> Void
let report: () -> Void
init(account: Account, avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, tapAvatarAction: @escaping () -> Void, openChat: @escaping () -> Void, addContact: @escaping () -> Void, shareContact: @escaping () -> Void, shareMyContact: @escaping () -> Void, startSecretChat: @escaping () -> Void, changeNotificationMuteSettings: @escaping () -> Void, openSharedMedia: @escaping () -> Void, openGroupsInCommon: @escaping () -> Void, updatePeerBlocked: @escaping (Bool) -> Void, deleteContact: @escaping () -> Void, displayUsernameContextMenu: @escaping (String) -> Void, displayCopyContextMenu: @escaping (UserInfoEntryTag, String) -> Void, call: @escaping () -> Void, openCallMenu: @escaping (String) -> Void, aboutLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void, displayAboutContextMenu: @escaping (String) -> Void, openEncryptionKey: @escaping (SecretChatKeyFingerprint) -> Void, addBotToGroup: @escaping () -> Void, shareBot: @escaping () -> Void, botSettings: @escaping () -> Void, botHelp: @escaping () -> Void, botPrivacy: @escaping () -> Void, report: @escaping () -> Void) {
init(account: Account, avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, tapAvatarAction: @escaping () -> Void, openChat: @escaping () -> Void, addContact: @escaping () -> Void, shareContact: @escaping () -> Void, shareMyContact: @escaping () -> Void, startSecretChat: @escaping () -> Void, changeNotificationMuteSettings: @escaping () -> Void, openSharedMedia: @escaping () -> Void, openGroupsInCommon: @escaping () -> Void, updatePeerBlocked: @escaping (Bool) -> Void, deleteContact: @escaping () -> Void, displayUsernameContextMenu: @escaping (String) -> Void, displayCopyContextMenu: @escaping (UserInfoEntryTag, String) -> Void, call: @escaping () -> Void, openCallMenu: @escaping (String) -> Void, requestPhoneNumber: @escaping () -> Void, aboutLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void, displayAboutContextMenu: @escaping (String) -> Void, openEncryptionKey: @escaping (SecretChatKeyFingerprint) -> Void, addBotToGroup: @escaping () -> Void, shareBot: @escaping () -> Void, botSettings: @escaping () -> Void, botHelp: @escaping () -> Void, botPrivacy: @escaping () -> Void, report: @escaping () -> Void) {
self.account = account
self.avatarAndNameInfoContext = avatarAndNameInfoContext
self.updateEditingName = updateEditingName
@ -54,6 +55,7 @@ private final class UserInfoControllerArguments {
self.displayCopyContextMenu = displayCopyContextMenu
self.call = call
self.openCallMenu = openCallMenu
self.requestPhoneNumber = requestPhoneNumber
self.aboutLinkAction = aboutLinkAction
self.displayAboutContextMenu = displayAboutContextMenu
self.openEncryptionKey = openEncryptionKey
@ -95,6 +97,7 @@ private enum UserInfoEntry: ItemListNodeEntry {
case calls(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, messages: [Message])
case about(PresentationTheme, Peer, String, String)
case phoneNumber(PresentationTheme, Int, String, String, Bool)
case requestPhoneNumber(PresentationTheme, String, String)
case userName(PresentationTheme, String, String)
case sendMessage(PresentationTheme, String)
case addContact(PresentationTheme, String)
@ -115,7 +118,7 @@ private enum UserInfoEntry: ItemListNodeEntry {
var section: ItemListSectionId {
switch self {
case .info, .calls, .about, .phoneNumber, .userName:
case .info, .calls, .about, .phoneNumber, .requestPhoneNumber, .userName:
return UserInfoSection.info.rawValue
case .sendMessage, .addContact, .shareContact, .shareMyContact, .startSecretChat, .botAddToGroup, .botShare:
return UserInfoSection.actions.rawValue
@ -203,6 +206,12 @@ private enum UserInfoEntry: ItemListNodeEntry {
} else {
return false
}
case let .requestPhoneNumber(lhsTheme, lhsLabel, lhsValue):
if case let .requestPhoneNumber(rhsTheme, rhsLabel, rhsValue) = rhs, lhsTheme === rhsTheme, lhsLabel == rhsLabel, lhsValue == rhsValue {
return true
} else {
return false
}
case let .userName(lhsTheme, lhsText, lhsValue):
if case let .userName(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
@ -316,6 +325,8 @@ private enum UserInfoEntry: ItemListNodeEntry {
return 1
case let .phoneNumber(_, index, _, _, _):
return 2 + index
case .requestPhoneNumber:
return 998
case .about:
return 999
case .userName:
@ -387,6 +398,10 @@ private enum UserInfoEntry: ItemListNodeEntry {
}, longTapAction: {
arguments.displayCopyContextMenu(.phoneNumber, value)
}, tag: UserInfoEntryTag.phoneNumber)
case let .requestPhoneNumber(theme, label, value):
return ItemListTextWithLabelItem(theme: theme, label: label, text: value, textColor: .accent, enabledEntitiyTypes: [], multiline: false, sectionId: self.section, action: {
arguments.requestPhoneNumber()
})
case let .userName(theme, text, value):
return ItemListTextWithLabelItem(theme: theme, label: text, text: "@\(value)", textColor: .accent, enabledEntitiyTypes: [], multiline: false, sectionId: self.section, action: {
arguments.displayUsernameContextMenu("@\(value)")
@ -602,6 +617,8 @@ private func userInfoEntries(account: Account, presentationData: PresentationDat
index += 1
}
}
} else {
entries.append(UserInfoEntry.requestPhoneNumber(presentationData.theme, "phone", "Request Number"))
}
let aboutTitle: String
@ -989,6 +1006,11 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Us
context.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(number).replacingOccurrences(of: " ", with: ""))")
}
})
}, requestPhoneNumber: {
let _ = (requestPhoneNumber(account: context.account, peerId: peerId)
|> deliverOnMainQueue).start(completed: {
})
}, aboutLinkAction: { action, itemLink in
aboutLinkActionImpl?(action, itemLink)
}, displayAboutContextMenu: { text in