mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-15 18:59:54 +00:00
Update Privacy and Security
This commit is contained in:
parent
87c91d57b2
commit
775e2cb852
12
Images.xcassets/Settings/MenuIcons/Blocked.imageset/Contents.json
vendored
Normal file
12
Images.xcassets/Settings/MenuIcons/Blocked.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "blocked.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
BIN
Images.xcassets/Settings/MenuIcons/Blocked.imageset/blocked.pdf
vendored
Normal file
BIN
Images.xcassets/Settings/MenuIcons/Blocked.imageset/blocked.pdf
vendored
Normal file
Binary file not shown.
12
Images.xcassets/Settings/MenuIcons/FaceId.imageset/Contents.json
vendored
Normal file
12
Images.xcassets/Settings/MenuIcons/FaceId.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "faceid.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
BIN
Images.xcassets/Settings/MenuIcons/FaceId.imageset/faceid.pdf
vendored
Normal file
BIN
Images.xcassets/Settings/MenuIcons/FaceId.imageset/faceid.pdf
vendored
Normal file
Binary file not shown.
12
Images.xcassets/Settings/MenuIcons/Sessions.imageset/Contents.json
vendored
Normal file
12
Images.xcassets/Settings/MenuIcons/Sessions.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "sessions.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
BIN
Images.xcassets/Settings/MenuIcons/Sessions.imageset/sessions.pdf
vendored
Normal file
BIN
Images.xcassets/Settings/MenuIcons/Sessions.imageset/sessions.pdf
vendored
Normal file
Binary file not shown.
12
Images.xcassets/Settings/MenuIcons/TouchId.imageset/Contents.json
vendored
Normal file
12
Images.xcassets/Settings/MenuIcons/TouchId.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "touchid.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
BIN
Images.xcassets/Settings/MenuIcons/TouchId.imageset/touchid.pdf
vendored
Normal file
BIN
Images.xcassets/Settings/MenuIcons/TouchId.imageset/touchid.pdf
vendored
Normal file
Binary file not shown.
BIN
Images.xcassets/Settings/MenuIcons/TwoStepAuth.imageset/2step.pdf
vendored
Normal file
BIN
Images.xcassets/Settings/MenuIcons/TwoStepAuth.imageset/2step.pdf
vendored
Normal file
Binary file not shown.
12
Images.xcassets/Settings/MenuIcons/TwoStepAuth.imageset/Contents.json
vendored
Normal file
12
Images.xcassets/Settings/MenuIcons/TwoStepAuth.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "2step.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@ -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 */,
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -200,6 +200,8 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
|
||||
}
|
||||
case .payment:
|
||||
break
|
||||
case .urlAuth:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -749,6 +749,8 @@ public class ChatMessageItemView: ListViewItemNode {
|
||||
}
|
||||
case .payment:
|
||||
item.controllerInteraction.openCheckoutOrReceipt(item.message.id)
|
||||
case .urlAuth:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
200
TelegramUI/ChatMessagePhoneNumberRequestContentNode.swift
Normal file
200
TelegramUI/ChatMessagePhoneNumberRequestContentNode.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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?
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)))
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
@ -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] = [
|
||||
|
||||
Binary file not shown.
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user