mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Add account deletion
This commit is contained in:
parent
411b8682c1
commit
16d6010385
BIN
Telegram/Telegram-iOS/Resources/Delete1.tgs
Normal file
BIN
Telegram/Telegram-iOS/Resources/Delete1.tgs
Normal file
Binary file not shown.
BIN
Telegram/Telegram-iOS/Resources/Delete2.tgs
Normal file
BIN
Telegram/Telegram-iOS/Resources/Delete2.tgs
Normal file
Binary file not shown.
BIN
Telegram/Telegram-iOS/Resources/Delete3.tgs
Normal file
BIN
Telegram/Telegram-iOS/Resources/Delete3.tgs
Normal file
Binary file not shown.
BIN
Telegram/Telegram-iOS/Resources/Delete4.tgs
Normal file
BIN
Telegram/Telegram-iOS/Resources/Delete4.tgs
Normal file
Binary file not shown.
BIN
Telegram/Telegram-iOS/Resources/Delete5.tgs
Normal file
BIN
Telegram/Telegram-iOS/Resources/Delete5.tgs
Normal file
Binary file not shown.
Binary file not shown.
@ -868,7 +868,6 @@
|
|||||||
"MessageTimer.Years_2" = "%@ years";
|
"MessageTimer.Years_2" = "%@ years";
|
||||||
"MessageTimer.Years_3_10" = "%@ years";
|
"MessageTimer.Years_3_10" = "%@ years";
|
||||||
"MessageTimer.Years_any" = "%@ years";
|
"MessageTimer.Years_any" = "%@ years";
|
||||||
"MessageTimer.Months_many" = "%@ years";
|
|
||||||
|
|
||||||
"MessageTimer.ShortSeconds_1" = "%@s";
|
"MessageTimer.ShortSeconds_1" = "%@s";
|
||||||
"MessageTimer.ShortSeconds_2" = "%@s";
|
"MessageTimer.ShortSeconds_2" = "%@s";
|
||||||
@ -7725,7 +7724,6 @@ Sorry for the inconvenience.";
|
|||||||
"Premium.Restore.Success" = "Done";
|
"Premium.Restore.Success" = "Done";
|
||||||
"Premium.Restore.ErrorUnknown" = "An error occurred. Please try again.";
|
"Premium.Restore.ErrorUnknown" = "An error occurred. Please try again.";
|
||||||
|
|
||||||
|
|
||||||
"Settings.Premium" = "Telegram Premium";
|
"Settings.Premium" = "Telegram Premium";
|
||||||
|
|
||||||
"Settings.AddAnotherAccount.PremiumHelp" = "You can add up to four accounts with different phone numbers.";
|
"Settings.AddAnotherAccount.PremiumHelp" = "You can add up to four accounts with different phone numbers.";
|
||||||
@ -7735,3 +7733,67 @@ Sorry for the inconvenience.";
|
|||||||
"Appearance.AppIconPremium" = "Premium";
|
"Appearance.AppIconPremium" = "Premium";
|
||||||
"Appearance.AppIconBlack" = "Black";
|
"Appearance.AppIconBlack" = "Black";
|
||||||
"Appearance.AppIconTurbo" = "Turbo";
|
"Appearance.AppIconTurbo" = "Turbo";
|
||||||
|
|
||||||
|
"PrivacySettings.DeleteAccountNow" = "Delete Account Now";
|
||||||
|
|
||||||
|
"DeleteAccount.AlternativeOptionsTitle" = "Alternative Options";
|
||||||
|
|
||||||
|
"DeleteAccount.Options.ChangePhoneNumberTitle" = "Change Phone Number";
|
||||||
|
"DeleteAccount.Options.ChangePhoneNumberText" = "Move your contacts, chats and media to a new number.";
|
||||||
|
|
||||||
|
"DeleteAccount.Options.AddAccountTitle" = "Add Another Account";
|
||||||
|
"DeleteAccount.Options.AddAccountText" = "You can use up to 3 accounts in one app at the same time.";
|
||||||
|
"DeleteAccount.Options.AddAccountPremiumText" = "You can use up to 4 accounts in one app at the same time.";
|
||||||
|
|
||||||
|
"DeleteAccount.Options.ChangePrivacyTitle" = "Change Your Privacy Settings";
|
||||||
|
"DeleteAccount.Options.ChangePrivacyText" = "Choose who exactly can see which of your info.";
|
||||||
|
|
||||||
|
"DeleteAccount.Options.SetTwoStepAuthTitle" = "Enable Two-Step Verification";
|
||||||
|
"DeleteAccount.Options.SetTwoStepAuthText" = "Set a password that will be required each time you log in.";
|
||||||
|
|
||||||
|
"DeleteAccount.Options.SetPasscodeTitle" = "Set a Passcode";
|
||||||
|
"DeleteAccount.Options.SetPasscodeText" = "Lock the app with a passcode so that others can't open it.";
|
||||||
|
|
||||||
|
"DeleteAccount.Options.ClearCacheTitle" = "Clear Cache";
|
||||||
|
"DeleteAccount.Options.ClearCacheText" = "Free up disk space on your device; your media will stay in the cloud.";
|
||||||
|
|
||||||
|
"DeleteAccount.Options.ClearSyncedContactsTitle" = "Clear Synced Contacts";
|
||||||
|
"DeleteAccount.Options.ClearSyncedContactsText" = "Remove any unnecessary contacts you may have synced.";
|
||||||
|
|
||||||
|
"DeleteAccount.Options.DeleteChatsTitle" = "Quickly Delete Your Chats";
|
||||||
|
"DeleteAccount.Options.DeleteChatsText" = "Learn how to remove any info you don’t need in a few taps.";
|
||||||
|
|
||||||
|
"DeleteAccount.Options.ContactSupportTitle" = "Contact Support";
|
||||||
|
"DeleteAccount.Options.ContactSupportText" = "Tell us about any issues; deleting account doesn't usually help.";
|
||||||
|
|
||||||
|
"DeleteAccount.DeleteMyAccountTitle" = "Delete My Account";
|
||||||
|
"DeleteAccount.DeleteMyAccount" = "Delete My Account";
|
||||||
|
|
||||||
|
"DeleteAccount.SavedMessages" = "Saved";
|
||||||
|
|
||||||
|
"DeleteAccount.ComeBackLater" = "Come Back Later";
|
||||||
|
"DeleteAccount.Continue" = "Continue";
|
||||||
|
|
||||||
|
"DeleteAccount.CloudStorageTitle" = "Your Free Cloud Storage";
|
||||||
|
"DeleteAccount.CloudStorageText" = "You will lose access to all your Saved Messages as well as all messages, media and files from your chats.";
|
||||||
|
|
||||||
|
"DeleteAccount.GroupsAndChannelsTitle" = "Your Groups and Channels";
|
||||||
|
"DeleteAccount.GroupsAndChannelsText" = "The groups and channels you created will either get new admins or become orphaned.";
|
||||||
|
"DeleteAccount.GroupsAndChannelsInfo" = "You can transfer group and channel ownership to other users via Chat Info > Edit > Admins.";
|
||||||
|
|
||||||
|
"DeleteAccount.MessageHistoryTitle" = "Your Message History";
|
||||||
|
"DeleteAccount.MessageHistoryText" = "Your chat partners will keep their message history with you, including the messages you shared in secret chats.\n\nYou can remove any messages for both sides at any time, but this will not be possible if you delete your account.";
|
||||||
|
|
||||||
|
"DeleteAccount.DeleteMessagesURL" = "https://telegram.org/faq#q-can-i-delete-my-messages";
|
||||||
|
|
||||||
|
"DeleteAccount.EnterPhoneNumber" = "Enter Your Phone Number";
|
||||||
|
"DeleteAccount.InvalidPhoneNumberError" = "Invalid phone number. Please try again.";
|
||||||
|
|
||||||
|
"DeleteAccount.EnterPassword" = "Enter Your Password";
|
||||||
|
"DeleteAccount.InvalidPasswordError" = "Invalid password. Please try again.";
|
||||||
|
|
||||||
|
"DeleteAccount.ConfirmationAlertTitle" = "Proceed to Delete Your Account?";
|
||||||
|
"DeleteAccount.ConfirmationAlertText" = "Deleting your account will permanently delete your data!\n\nIt is imposible to reverse this action!";
|
||||||
|
"DeleteAccount.ConfirmationAlertDelete" = "Delete My Account";
|
||||||
|
|
||||||
|
"DeleteAccount.Success" = "The account has been successfully deleted.";
|
||||||
|
@ -5,6 +5,7 @@ import TelegramUIPreferences
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
|
|
||||||
public let maximumNumberOfAccounts = 3
|
public let maximumNumberOfAccounts = 3
|
||||||
|
public let maximumPremiumNumberOfAccounts = 4
|
||||||
|
|
||||||
public func activeAccountsAndPeers(context: AccountContext, includePrimary: Bool = false) -> Signal<((AccountContext, EnginePeer)?, [(AccountContext, EnginePeer, Int32)]), NoError> {
|
public func activeAccountsAndPeers(context: AccountContext, includePrimary: Bool = false) -> Signal<((AccountContext, EnginePeer)?, [(AccountContext, EnginePeer, Int32)]), NoError> {
|
||||||
let sharedContext = context.sharedContext
|
let sharedContext = context.sharedContext
|
||||||
|
@ -120,7 +120,7 @@ class ChatListRecentPeersListItemNode: ListViewItemNode {
|
|||||||
peersNode = currentPeersNode
|
peersNode = currentPeersNode
|
||||||
peersNode.updateThemeAndStrings(theme: item.theme, strings: item.strings)
|
peersNode.updateThemeAndStrings(theme: item.theme, strings: item.strings)
|
||||||
} else {
|
} else {
|
||||||
peersNode = ChatListSearchRecentPeersNode(context: item.context, theme: item.theme, mode: .list, strings: item.strings, peerSelected: { peer in
|
peersNode = ChatListSearchRecentPeersNode(context: item.context, theme: item.theme, mode: .list(compact: false), strings: item.strings, peerSelected: { peer in
|
||||||
self?.item?.peerSelected(peer)
|
self?.item?.peerSelected(peer)
|
||||||
}, peerContextAction: { peer, node, gesture in
|
}, peerContextAction: { peer, node, gesture in
|
||||||
self?.item?.peerContextAction(peer, node, gesture)
|
self?.item?.peerContextAction(peer, node, gesture)
|
||||||
|
@ -312,6 +312,15 @@ public class CheckLayer: CALayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override init() {
|
||||||
|
self.theme = CheckNodeTheme(backgroundColor: .white, strokeColor: .blue, borderColor: .white, overlayBorder: false, hasInset: false, hasShadow: false)
|
||||||
|
self.content = .check
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.isOpaque = false
|
||||||
|
}
|
||||||
|
|
||||||
public init(theme: CheckNodeTheme, content: CheckNodeContent = .check) {
|
public init(theme: CheckNodeTheme, content: CheckNodeContent = .check) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.content = content
|
self.content = content
|
||||||
|
@ -1271,7 +1271,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||||||
|
|
||||||
let badgeNode = ASImageNode()
|
let badgeNode = ASImageNode()
|
||||||
badgeNode.displaysAsynchronously = false
|
badgeNode.displaysAsynchronously = false
|
||||||
badgeNode.image = UIImage(bundleImageName: "Components/BadgeTest")
|
badgeNode.image = UIImage(bundleImageName: "Components/AppBadge")
|
||||||
self.badgeNode = badgeNode
|
self.badgeNode = badgeNode
|
||||||
self.displayNode.addSubnode(badgeNode)
|
self.displayNode.addSubnode(badgeNode)
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import ContextUI
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
|
|
||||||
public enum HorizontalPeerItemMode {
|
public enum HorizontalPeerItemMode {
|
||||||
case list
|
case list(compact: Bool)
|
||||||
case actionSheet
|
case actionSheet
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,13 +25,13 @@ public final class HorizontalPeerItem: ListViewItem {
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
public let peer: EnginePeer
|
public let peer: EnginePeer
|
||||||
let action: (EnginePeer) -> Void
|
let action: (EnginePeer) -> Void
|
||||||
let contextAction: (EnginePeer, ASDisplayNode, ContextGesture?) -> Void
|
let contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
let isPeerSelected: (EnginePeer.Id) -> Bool
|
let isPeerSelected: (EnginePeer.Id) -> Bool
|
||||||
let customWidth: CGFloat?
|
let customWidth: CGFloat?
|
||||||
let presence: EnginePeer.Presence?
|
let presence: EnginePeer.Presence?
|
||||||
let unreadBadge: (Int32, Bool)?
|
let unreadBadge: (Int32, Bool)?
|
||||||
|
|
||||||
public init(theme: PresentationTheme, strings: PresentationStrings, mode: HorizontalPeerItemMode, context: AccountContext, peer: EnginePeer, presence: EnginePeer.Presence?, unreadBadge: (Int32, Bool)?, action: @escaping (EnginePeer) -> Void, contextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, customWidth: CGFloat?) {
|
public init(theme: PresentationTheme, strings: PresentationStrings, mode: HorizontalPeerItemMode, context: AccountContext, peer: EnginePeer, presence: EnginePeer.Presence?, unreadBadge: (Int32, Bool)?, action: @escaping (EnginePeer) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, customWidth: CGFloat?) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
@ -111,11 +111,6 @@ public final class HorizontalPeerItemNode: ListViewItemNode {
|
|||||||
item.action(item.peer)
|
item.action(item.peer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.peerNode.contextAction = { [weak self] node, gesture in
|
|
||||||
if let item = self?.item {
|
|
||||||
item.contextAction(item.peer, node, gesture)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func didLoad() {
|
override public func didLoad() {
|
||||||
@ -186,10 +181,25 @@ public final class HorizontalPeerItemNode: ListViewItemNode {
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.item = item
|
strongSelf.item = item
|
||||||
strongSelf.peerNode.theme = itemTheme
|
strongSelf.peerNode.theme = itemTheme
|
||||||
|
if case let .list(compact) = item.mode {
|
||||||
|
strongSelf.peerNode.compact = compact
|
||||||
|
} else {
|
||||||
|
strongSelf.peerNode.compact = false
|
||||||
|
}
|
||||||
strongSelf.peerNode.setup(context: item.context, theme: item.theme, strings: item.strings, peer: EngineRenderedPeer(peer: item.peer), numberOfLines: 1, synchronousLoad: synchronousLoads)
|
strongSelf.peerNode.setup(context: item.context, theme: item.theme, strings: item.strings, peer: EngineRenderedPeer(peer: item.peer), numberOfLines: 1, synchronousLoad: synchronousLoads)
|
||||||
strongSelf.peerNode.frame = CGRect(origin: CGPoint(), size: itemLayout.size)
|
strongSelf.peerNode.frame = CGRect(origin: CGPoint(), size: itemLayout.size)
|
||||||
strongSelf.peerNode.updateSelection(selected: item.isPeerSelected(item.peer.id), animated: false)
|
strongSelf.peerNode.updateSelection(selected: item.isPeerSelected(item.peer.id), animated: false)
|
||||||
|
|
||||||
|
if let contextAction = item.contextAction {
|
||||||
|
strongSelf.peerNode.contextAction = { [weak item] node, gesture in
|
||||||
|
if let item = item {
|
||||||
|
contextAction(item.peer, node, gesture)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
strongSelf.peerNode.contextAction = nil
|
||||||
|
}
|
||||||
|
|
||||||
let badgeBackgroundWidth: CGFloat
|
let badgeBackgroundWidth: CGFloat
|
||||||
if let currentBadgeBackgroundImage = currentBadgeBackgroundImage {
|
if let currentBadgeBackgroundImage = currentBadgeBackgroundImage {
|
||||||
strongSelf.badgeBackgroundNode.image = currentBadgeBackgroundImage
|
strongSelf.badgeBackgroundNode.image = currentBadgeBackgroundImage
|
||||||
|
@ -15,14 +15,16 @@ import TextFormat
|
|||||||
public class InviteLinkHeaderItem: ListViewItem, ItemListItem {
|
public class InviteLinkHeaderItem: ListViewItem, ItemListItem {
|
||||||
public let context: AccountContext
|
public let context: AccountContext
|
||||||
public let theme: PresentationTheme
|
public let theme: PresentationTheme
|
||||||
|
public let title: String?
|
||||||
public let text: String
|
public let text: String
|
||||||
public let animationName: String
|
public let animationName: String
|
||||||
public let sectionId: ItemListSectionId
|
public let sectionId: ItemListSectionId
|
||||||
public let linkAction: ((ItemListTextItemLinkAction) -> Void)?
|
public let linkAction: ((ItemListTextItemLinkAction) -> Void)?
|
||||||
|
|
||||||
public init(context: AccountContext, theme: PresentationTheme, text: String, animationName: String, sectionId: ItemListSectionId, linkAction: ((ItemListTextItemLinkAction) -> Void)? = nil) {
|
public init(context: AccountContext, theme: PresentationTheme, title: String? = nil, text: String, animationName: String, sectionId: ItemListSectionId, linkAction: ((ItemListTextItemLinkAction) -> Void)? = nil) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
self.title = title
|
||||||
self.text = text
|
self.text = text
|
||||||
self.animationName = animationName
|
self.animationName = animationName
|
||||||
self.sectionId = sectionId
|
self.sectionId = sectionId
|
||||||
@ -66,10 +68,12 @@ public class InviteLinkHeaderItem: ListViewItem, ItemListItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let titleFont = Font.regular(13.0)
|
private let titleFont = Font.semibold(17.0)
|
||||||
|
private let textFont = Font.regular(14.0)
|
||||||
|
|
||||||
class InviteLinkHeaderItemNode: ListViewItemNode {
|
class InviteLinkHeaderItemNode: ListViewItemNode {
|
||||||
private let titleNode: TextNode
|
private let titleNode: TextNode
|
||||||
|
private let textNode: TextNode
|
||||||
private var animationNode: AnimatedStickerNode
|
private var animationNode: AnimatedStickerNode
|
||||||
|
|
||||||
private var item: InviteLinkHeaderItem?
|
private var item: InviteLinkHeaderItem?
|
||||||
@ -80,11 +84,17 @@ class InviteLinkHeaderItemNode: ListViewItemNode {
|
|||||||
self.titleNode.contentMode = .left
|
self.titleNode.contentMode = .left
|
||||||
self.titleNode.contentsScale = UIScreen.main.scale
|
self.titleNode.contentsScale = UIScreen.main.scale
|
||||||
|
|
||||||
|
self.textNode = TextNode()
|
||||||
|
self.textNode.isUserInteractionEnabled = false
|
||||||
|
self.textNode.contentMode = .left
|
||||||
|
self.textNode.contentsScale = UIScreen.main.scale
|
||||||
|
|
||||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||||
|
|
||||||
super.init(layerBacked: false, dynamicBounce: false)
|
super.init(layerBacked: false, dynamicBounce: false)
|
||||||
|
|
||||||
self.addSubnode(self.titleNode)
|
self.addSubnode(self.titleNode)
|
||||||
|
self.addSubnode(self.textNode)
|
||||||
self.addSubnode(self.animationNode)
|
self.addSubnode(self.animationNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,18 +110,33 @@ class InviteLinkHeaderItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
func asyncLayout() -> (_ item: InviteLinkHeaderItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
func asyncLayout() -> (_ item: InviteLinkHeaderItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||||
|
let makeTextLayout = TextNode.asyncLayout(self.textNode)
|
||||||
|
|
||||||
return { item, params, neighbors in
|
return { item, params, neighbors in
|
||||||
let leftInset: CGFloat = 32.0 + params.leftInset
|
let leftInset: CGFloat = 24.0 + params.leftInset
|
||||||
let topInset: CGFloat = 124.0
|
let iconSize: CGSize
|
||||||
|
if params.width > params.availableHeight && params.width > 320.0 {
|
||||||
|
iconSize = CGSize(width: 140.0, height: 140.0)
|
||||||
|
} else {
|
||||||
|
iconSize = CGSize(width: 124.0, height: 124.0)
|
||||||
|
}
|
||||||
|
let topInset: CGFloat = iconSize.height - 4.0
|
||||||
|
let spacing: CGFloat = 5.0
|
||||||
|
|
||||||
let attributedText = parseMarkdownIntoAttributedString(item.text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: item.theme.list.freeTextColor), bold: MarkdownAttributeSet(font: titleFont, textColor: item.theme.list.freeTextColor), link: MarkdownAttributeSet(font: titleFont, textColor: item.theme.list.itemAccentColor), linkAttribute: { contents in
|
let attributedTitle = NSAttributedString(string: item.title ?? "", font: titleFont, textColor: item.theme.list.itemPrimaryTextColor, paragraphAlignment: .center)
|
||||||
|
|
||||||
|
let attributedText = parseMarkdownIntoAttributedString(item.text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: item.theme.list.freeTextColor), bold: MarkdownAttributeSet(font: textFont, textColor: item.theme.list.freeTextColor), link: MarkdownAttributeSet(font: textFont, textColor: item.theme.list.itemAccentColor), linkAttribute: { contents in
|
||||||
return (TelegramTextAttributes.URL, contents)
|
return (TelegramTextAttributes.URL, contents)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
|
||||||
|
|
||||||
let contentSize = CGSize(width: params.width, height: topInset + titleLayout.size.height)
|
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: attributedTitle, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
|
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
|
var contentSize = CGSize(width: params.width, height: topInset + textLayout.size.height)
|
||||||
|
if let _ = item.title {
|
||||||
|
contentSize.height += titleLayout.size.height + spacing
|
||||||
|
}
|
||||||
let insets = itemListNeighborsGroupedInsets(neighbors, params)
|
let insets = itemListNeighborsGroupedInsets(neighbors, params)
|
||||||
|
|
||||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||||
@ -119,18 +144,25 @@ class InviteLinkHeaderItemNode: ListViewItemNode {
|
|||||||
return (layout, { [weak self] in
|
return (layout, { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if strongSelf.item == nil {
|
if strongSelf.item == nil {
|
||||||
strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: item.animationName), width: 192, height: 192, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
strongSelf.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: item.animationName), width: 256, height: 256, playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
||||||
strongSelf.animationNode.visibility = true
|
strongSelf.animationNode.visibility = true
|
||||||
}
|
}
|
||||||
strongSelf.item = item
|
strongSelf.item = item
|
||||||
strongSelf.accessibilityLabel = attributedText.string
|
strongSelf.accessibilityLabel = attributedText.string
|
||||||
|
|
||||||
let iconSize = CGSize(width: 128.0, height: 128.0)
|
|
||||||
strongSelf.animationNode.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: -10.0), size: iconSize)
|
strongSelf.animationNode.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: -10.0), size: iconSize)
|
||||||
strongSelf.animationNode.updateLayout(size: iconSize)
|
strongSelf.animationNode.updateLayout(size: iconSize)
|
||||||
|
|
||||||
|
var origin: CGFloat = topInset + 8.0
|
||||||
|
|
||||||
let _ = titleApply()
|
let _ = titleApply()
|
||||||
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleLayout.size.width) / 2.0), y: topInset + 8.0), size: titleLayout.size)
|
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleLayout.size.width) / 2.0), y: origin), size: titleLayout.size)
|
||||||
|
if titleLayout.size.height > 0.0 {
|
||||||
|
origin += titleLayout.size.height + spacing
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = textApply()
|
||||||
|
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - textLayout.size.width) / 2.0), y: origin), size: textLayout.size)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -150,9 +182,9 @@ class InviteLinkHeaderItemNode: ListViewItemNode {
|
|||||||
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
||||||
switch gesture {
|
switch gesture {
|
||||||
case .tap:
|
case .tap:
|
||||||
let titleFrame = self.titleNode.frame
|
let textFrame = self.textNode.frame
|
||||||
if let item = self.item, titleFrame.contains(location) {
|
if let item = self.item, textFrame.contains(location) {
|
||||||
if let (_, attributes) = self.titleNode.attributesAtPoint(CGPoint(x: location.x - titleFrame.minX, y: location.y - titleFrame.minY)) {
|
if let (_, attributes) = self.textNode.attributesAtPoint(CGPoint(x: location.x - textFrame.minX, y: location.y - textFrame.minY)) {
|
||||||
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||||
item.linkAction?(.tap(url))
|
item.linkAction?(.tap(url))
|
||||||
}
|
}
|
||||||
|
@ -1147,7 +1147,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
|
|||||||
let _ = combineLatest(
|
let _ = combineLatest(
|
||||||
queue: Queue.mainQueue(),
|
queue: Queue.mainQueue(),
|
||||||
adminedPublicChannels.get() |> filter { $0 != nil } |> take(1),
|
adminedPublicChannels.get() |> filter { $0 != nil } |> take(1),
|
||||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)),
|
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)),
|
||||||
context.engine.data.get(
|
context.engine.data.get(
|
||||||
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false),
|
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false),
|
||||||
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true)
|
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true)
|
||||||
|
@ -23,7 +23,7 @@ final class IncreaseLimitFooterItem: ItemListControllerFooterItem {
|
|||||||
|
|
||||||
func isEqual(to: ItemListControllerFooterItem) -> Bool {
|
func isEqual(to: ItemListControllerFooterItem) -> Bool {
|
||||||
if let item = to as? IncreaseLimitFooterItem {
|
if let item = to as? IncreaseLimitFooterItem {
|
||||||
return self.theme === item.theme && self.title == item.title
|
return self.theme === item.theme && self.title == item.title && self.colorful == item.colorful
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,6 @@ public final class MatrixView: MTKView, MTKViewDelegate, PhoneDemoDecorationView
|
|||||||
|
|
||||||
private var displayLink: CADisplayLink?
|
private var displayLink: CADisplayLink?
|
||||||
|
|
||||||
// private var metalLayer: CAMetalLayer {
|
|
||||||
// return self.layer as! CAMetalLayer
|
|
||||||
// }
|
|
||||||
|
|
||||||
private let symbolTexture: MTLTexture
|
private let symbolTexture: MTLTexture
|
||||||
private let randomTexture: MTLTexture
|
private let randomTexture: MTLTexture
|
||||||
|
|
||||||
|
@ -410,7 +410,7 @@ class PremiumStarComponent: Component {
|
|||||||
|
|
||||||
if explode, let node = scene.rootNode.childNode(withName: "swirl", recursively: false), let particles = scene.rootNode.childNode(withName: "particles", recursively: false) {
|
if explode, let node = scene.rootNode.childNode(withName: "swirl", recursively: false), let particles = scene.rootNode.childNode(withName: "particles", recursively: false) {
|
||||||
if let particleSystem = particles.particleSystems?.first {
|
if let particleSystem = particles.particleSystems?.first {
|
||||||
particleSystem.particleColorVariation = SCNVector4(0.15, 0.2, 0.35, 0.3)
|
particleSystem.particleColorVariation = SCNVector4(0.15, 0.2, 0.15, 0.3)
|
||||||
particleSystem.speedFactor = 2.0
|
particleSystem.speedFactor = 2.0
|
||||||
particleSystem.particleVelocity = 2.2
|
particleSystem.particleVelocity = 2.2
|
||||||
particleSystem.birthRate = 4.0
|
particleSystem.birthRate = 4.0
|
||||||
|
@ -84,6 +84,8 @@ public final class SelectablePeerNode: ASDisplayNode {
|
|||||||
|
|
||||||
private var peer: EngineRenderedPeer?
|
private var peer: EngineRenderedPeer?
|
||||||
|
|
||||||
|
public var compact = false
|
||||||
|
|
||||||
public var theme: SelectablePeerNodeTheme = SelectablePeerNodeTheme(textColor: .black, secretTextColor: .green, selectedTextColor: .blue, checkBackgroundColor: .white, checkFillColor: .blue, checkColor: .white, avatarPlaceholderColor: .white) {
|
public var theme: SelectablePeerNodeTheme = SelectablePeerNodeTheme(textColor: .black, secretTextColor: .green, selectedTextColor: .blue, checkBackgroundColor: .white, checkFillColor: .blue, checkColor: .white, avatarPlaceholderColor: .white) {
|
||||||
didSet {
|
didSet {
|
||||||
if !self.theme.isEqual(to: oldValue) {
|
if !self.theme.isEqual(to: oldValue) {
|
||||||
@ -147,7 +149,7 @@ public final class SelectablePeerNode: ASDisplayNode {
|
|||||||
let text: String
|
let text: String
|
||||||
var overrideImage: AvatarNodeImageOverride?
|
var overrideImage: AvatarNodeImageOverride?
|
||||||
if peer.peerId == context.account.peerId {
|
if peer.peerId == context.account.peerId {
|
||||||
text = strings.DialogList_SavedMessages
|
text = self.compact ? strings.DeleteAccount_SavedMessages : strings.DialogList_SavedMessages
|
||||||
overrideImage = .savedMessagesIcon
|
overrideImage = .savedMessagesIcon
|
||||||
} else if peer.peerId.isReplies {
|
} else if peer.peerId.isReplies {
|
||||||
text = strings.DialogList_Replies
|
text = strings.DialogList_Replies
|
||||||
|
@ -101,6 +101,8 @@ swift_library(
|
|||||||
"//submodules/ListMessageItem:ListMessageItem",
|
"//submodules/ListMessageItem:ListMessageItem",
|
||||||
"//submodules/PaymentMethodUI:PaymentMethodUI",
|
"//submodules/PaymentMethodUI:PaymentMethodUI",
|
||||||
"//submodules/PremiumUI:PremiumUI",
|
"//submodules/PremiumUI:PremiumUI",
|
||||||
|
"//submodules/InviteLinksUI:InviteLinksUI",
|
||||||
|
"//submodules/HorizontalPeerItem:HorizontalPeerItem",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -286,5 +286,4 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode {
|
|||||||
@objc func countryPressed() {
|
@objc func countryPressed() {
|
||||||
self.selectCountryCode?()
|
self.selectCountryCode?()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
567
submodules/SettingsUI/Sources/DeleteAccountDataController.swift
Normal file
567
submodules/SettingsUI/Sources/DeleteAccountDataController.swift
Normal file
@ -0,0 +1,567 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
import LegacyComponents
|
||||||
|
import TelegramPresentationData
|
||||||
|
import ItemListUI
|
||||||
|
import PresentationDataUtils
|
||||||
|
import AccountContext
|
||||||
|
import AlertUI
|
||||||
|
import PresentationDataUtils
|
||||||
|
import UrlHandling
|
||||||
|
import InviteLinksUI
|
||||||
|
import CountrySelectionUI
|
||||||
|
import PhoneInputNode
|
||||||
|
import UndoUI
|
||||||
|
|
||||||
|
private struct DeleteAccountDataArguments {
|
||||||
|
let context: AccountContext
|
||||||
|
let openLink: (String) -> Void
|
||||||
|
let selectCountryCode: () -> Void
|
||||||
|
let updatePassword: (String) -> Void
|
||||||
|
let proceed: () -> Void
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum DeleteAccountDataSection: Int32 {
|
||||||
|
case header
|
||||||
|
case main
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum DeleteAccountEntryTag: Equatable, ItemListItemTag {
|
||||||
|
case password
|
||||||
|
|
||||||
|
func isEqual(to other: ItemListItemTag) -> Bool {
|
||||||
|
if let other = other as? DeleteAccountEntryTag {
|
||||||
|
return self == other
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private enum DeleteAccountDataEntry: ItemListNodeEntry, Equatable {
|
||||||
|
case header(PresentationTheme, String, String, String)
|
||||||
|
case peers(PresentationTheme, [EnginePeer])
|
||||||
|
case phone(PresentationTheme, PresentationStrings)
|
||||||
|
case password(PresentationTheme, String)
|
||||||
|
case info(PresentationTheme, String)
|
||||||
|
|
||||||
|
var section: ItemListSectionId {
|
||||||
|
switch self {
|
||||||
|
case .header:
|
||||||
|
return DeleteAccountDataSection.header.rawValue
|
||||||
|
case .peers, .info, .phone, .password:
|
||||||
|
return DeleteAccountDataSection.main.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stableId: Int32 {
|
||||||
|
switch self {
|
||||||
|
case .header:
|
||||||
|
return 0
|
||||||
|
case .peers:
|
||||||
|
return 1
|
||||||
|
case .info:
|
||||||
|
return 2
|
||||||
|
case .phone:
|
||||||
|
return 3
|
||||||
|
case .password:
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func == (lhs: DeleteAccountDataEntry, rhs: DeleteAccountDataEntry) -> Bool {
|
||||||
|
switch lhs {
|
||||||
|
case let .header(lhsTheme, lhsAnimation, lhsTitle, lhsText):
|
||||||
|
if case let .header(rhsTheme, rhsAnimation, rhsTitle, rhsText) = rhs, lhsTheme === rhsTheme, lhsAnimation == rhsAnimation, lhsTitle == rhsTitle, lhsText == rhsText {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .peers(lhsTheme, lhsPeers):
|
||||||
|
if case let .peers(rhsTheme, rhsPeers) = rhs, lhsTheme === rhsTheme, lhsPeers == rhsPeers {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .info(lhsTheme, lhsText):
|
||||||
|
if case let .info(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .phone(lhsTheme, lhsStrings):
|
||||||
|
if case let .phone(rhsTheme, rhsStrings) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .password(lhsTheme, lhsPlaceholder):
|
||||||
|
if case let .password(rhsTheme, rhsPlaceholder) = rhs, lhsTheme === rhsTheme, lhsPlaceholder == rhsPlaceholder {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func <(lhs: DeleteAccountDataEntry, rhs: DeleteAccountDataEntry) -> Bool {
|
||||||
|
return lhs.stableId < rhs.stableId
|
||||||
|
}
|
||||||
|
|
||||||
|
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||||
|
let arguments = arguments as! DeleteAccountDataArguments
|
||||||
|
switch self {
|
||||||
|
case let .header(theme, animation, title, text):
|
||||||
|
return InviteLinkHeaderItem(context: arguments.context, theme: theme, title: title, text: text, animationName: animation, sectionId: self.section, linkAction: nil)
|
||||||
|
case let .peers(_, peers):
|
||||||
|
return DeleteAccountPeersItem(context: arguments.context, theme: presentationData.theme, strings: presentationData.strings, peers: peers, sectionId: self.section)
|
||||||
|
case let .info(_, text):
|
||||||
|
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
|
||||||
|
case .phone:
|
||||||
|
return DeleteAccountPhoneItem(theme: presentationData.theme, strings: presentationData.strings, value: (nil, nil, ""), sectionId: self.section, selectCountryCode: {
|
||||||
|
arguments.selectCountryCode()
|
||||||
|
}, updated: { _ in
|
||||||
|
|
||||||
|
})
|
||||||
|
case let .password(_, placeholder):
|
||||||
|
return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(), text: "", placeholder: placeholder, type: .password, returnKeyType: .done, tag: DeleteAccountEntryTag.password, sectionId: self.section, textUpdated: { value in
|
||||||
|
arguments.updatePassword(value)
|
||||||
|
}, action: {
|
||||||
|
arguments.proceed()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func deleteAccountDataEntries(presentationData: PresentationData, mode: DeleteAccountDataMode, peers: [EnginePeer]) -> [DeleteAccountDataEntry] {
|
||||||
|
var entries: [DeleteAccountDataEntry] = []
|
||||||
|
|
||||||
|
let headerTitle: String
|
||||||
|
let headerText: String
|
||||||
|
let headerAnimation: String
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case .peers:
|
||||||
|
headerAnimation = "Delete1"
|
||||||
|
headerTitle = presentationData.strings.DeleteAccount_CloudStorageTitle
|
||||||
|
headerText = presentationData.strings.DeleteAccount_CloudStorageText
|
||||||
|
case .groups:
|
||||||
|
headerAnimation = "Delete2"
|
||||||
|
headerTitle = presentationData.strings.DeleteAccount_GroupsAndChannelsTitle
|
||||||
|
headerText = presentationData.strings.DeleteAccount_GroupsAndChannelsText
|
||||||
|
case .messages:
|
||||||
|
headerAnimation = "Delete3"
|
||||||
|
headerTitle = presentationData.strings.DeleteAccount_MessageHistoryTitle
|
||||||
|
headerText = presentationData.strings.DeleteAccount_MessageHistoryText
|
||||||
|
case .phone:
|
||||||
|
headerAnimation = "Delete4"
|
||||||
|
headerTitle = presentationData.strings.DeleteAccount_EnterPhoneNumber
|
||||||
|
headerText = ""
|
||||||
|
case .password:
|
||||||
|
headerAnimation = "Delete5"
|
||||||
|
headerTitle = presentationData.strings.DeleteAccount_EnterPassword
|
||||||
|
headerText = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.append(.header(presentationData.theme, headerAnimation, headerTitle, headerText))
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case .peers:
|
||||||
|
if !peers.isEmpty {
|
||||||
|
entries.append(.peers(presentationData.theme, peers))
|
||||||
|
}
|
||||||
|
case .groups:
|
||||||
|
if !peers.isEmpty {
|
||||||
|
entries.append(.peers(presentationData.theme, peers))
|
||||||
|
entries.append(.info(presentationData.theme, presentationData.strings.DeleteAccount_GroupsAndChannelsInfo))
|
||||||
|
}
|
||||||
|
case .messages:
|
||||||
|
break
|
||||||
|
case .phone:
|
||||||
|
entries.append(.phone(presentationData.theme, presentationData.strings))
|
||||||
|
case .password:
|
||||||
|
entries.append(.password(presentationData.theme, presentationData.strings.LoginPassword_PasswordPlaceholder))
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DeleteAccountDataMode {
|
||||||
|
case peers
|
||||||
|
case groups([EnginePeer])
|
||||||
|
case messages
|
||||||
|
case phone
|
||||||
|
case password
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct DeleteAccountDataState: Equatable {
|
||||||
|
var password: String
|
||||||
|
var isLoading: Bool
|
||||||
|
|
||||||
|
static func == (lhs: DeleteAccountDataState, rhs: DeleteAccountDataState) -> Bool {
|
||||||
|
return lhs.password == rhs.password && lhs.isLoading == rhs.isLoading
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteAccountDataController(context: AccountContext, mode: DeleteAccountDataMode, twoStepAuthData: TwoStepVerificationAccessConfiguration?) -> ViewController {
|
||||||
|
let initialState = DeleteAccountDataState(password: "", isLoading: false)
|
||||||
|
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||||
|
let stateValue = Atomic(value: initialState)
|
||||||
|
let updateState: ((DeleteAccountDataState) -> DeleteAccountDataState) -> Void = { f in
|
||||||
|
statePromise.set(stateValue.modify { f($0) })
|
||||||
|
}
|
||||||
|
|
||||||
|
var presentControllerImpl: ((ViewController) -> Void)?
|
||||||
|
var pushControllerImpl: ((ViewController) -> Void)?
|
||||||
|
var replaceTopControllerImpl: ((ViewController) -> Void)?
|
||||||
|
var dismissImpl: (() -> Void)?
|
||||||
|
var updateCodeImpl: (() -> Void)?
|
||||||
|
|
||||||
|
var activateInputImpl: (() -> Void)?
|
||||||
|
var dismissInputImpl: (() -> Void)?
|
||||||
|
|
||||||
|
if case .phone = mode {
|
||||||
|
loadServerCountryCodes(accountManager: context.sharedContext.accountManager, engine: context.engine, completion: {
|
||||||
|
updateCodeImpl?()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var updateCountryCodeImpl: ((Int32, String) -> Void)?
|
||||||
|
var proceedImpl: (() -> Void)?
|
||||||
|
|
||||||
|
let arguments = DeleteAccountDataArguments(context: context, openLink: { _ in
|
||||||
|
|
||||||
|
}, selectCountryCode: {
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let controller = AuthorizationSequenceCountrySelectionController(strings: presentationData.strings, theme: presentationData.theme)
|
||||||
|
controller.completeWithCountryCode = { code, name in
|
||||||
|
updateCountryCodeImpl?(Int32(code), name)
|
||||||
|
activateInputImpl?()
|
||||||
|
}
|
||||||
|
dismissInputImpl?()
|
||||||
|
pushControllerImpl?(controller)
|
||||||
|
}, updatePassword: { password in
|
||||||
|
updateState { current in
|
||||||
|
var updated = current
|
||||||
|
updated.password = password
|
||||||
|
return updated
|
||||||
|
}
|
||||||
|
}, proceed: {
|
||||||
|
proceedImpl?()
|
||||||
|
})
|
||||||
|
|
||||||
|
let preloadedGroupPeers = Promise<[EnginePeer]>([])
|
||||||
|
|
||||||
|
let peers: Signal<[EnginePeer], NoError>
|
||||||
|
switch mode {
|
||||||
|
case .peers:
|
||||||
|
peers = combineLatest(
|
||||||
|
context.engine.peers.recentPeers()
|
||||||
|
|> map { recentPeers -> [EnginePeer] in
|
||||||
|
if case let .peers(peers) = recentPeers {
|
||||||
|
return peers.map { EnginePeer($0) }
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||||
|
) |> map { recentPeers, accountPeer -> [EnginePeer] in
|
||||||
|
var peers: [EnginePeer] = []
|
||||||
|
if let accountPeer = accountPeer {
|
||||||
|
peers.append(accountPeer)
|
||||||
|
}
|
||||||
|
peers.append(contentsOf: recentPeers.prefix(9))
|
||||||
|
return peers
|
||||||
|
}
|
||||||
|
|
||||||
|
preloadedGroupPeers.set(context.engine.peers.adminedPublicChannels(scope: .all)
|
||||||
|
|> map { peers -> [EnginePeer] in
|
||||||
|
return peers.map { EnginePeer($0) }
|
||||||
|
})
|
||||||
|
case let .groups(preloadedPeers):
|
||||||
|
peers = .single(preloadedPeers.shuffled())
|
||||||
|
default:
|
||||||
|
peers = .single([])
|
||||||
|
}
|
||||||
|
|
||||||
|
let cancelImpl = {
|
||||||
|
dismissImpl?()
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case .peers:
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.step_cloud_cancel")
|
||||||
|
case .groups:
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.step_groups_cancel")
|
||||||
|
case .messages:
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.step_messages_cancel")
|
||||||
|
case .phone:
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.step_phone_cancel")
|
||||||
|
case .password:
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.step_2fa_cancel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let signal = combineLatest(queue: .mainQueue(),
|
||||||
|
context.sharedContext.presentationData,
|
||||||
|
peers,
|
||||||
|
statePromise.get()
|
||||||
|
)
|
||||||
|
|> map { presentationData, peers, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
|
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
||||||
|
cancelImpl()
|
||||||
|
})
|
||||||
|
|
||||||
|
var focusItemTag: DeleteAccountEntryTag?
|
||||||
|
var buttonTitle: String
|
||||||
|
switch mode {
|
||||||
|
case .phone:
|
||||||
|
buttonTitle = ""
|
||||||
|
case .password:
|
||||||
|
buttonTitle = ""
|
||||||
|
focusItemTag = .password
|
||||||
|
default:
|
||||||
|
buttonTitle = presentationData.strings.DeleteAccount_ComeBackLater
|
||||||
|
}
|
||||||
|
|
||||||
|
let rightNavigationButton: ItemListNavigationButton?
|
||||||
|
if state.isLoading {
|
||||||
|
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {})
|
||||||
|
} else {
|
||||||
|
rightNavigationButton = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let footerItem = DeleteAccountFooterItem(theme: presentationData.theme, title: buttonTitle, secondaryTitle: presentationData.strings.DeleteAccount_Continue, action: {
|
||||||
|
cancelImpl()
|
||||||
|
}, secondaryAction: {
|
||||||
|
proceedImpl?()
|
||||||
|
})
|
||||||
|
|
||||||
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.DeleteAccount_DeleteMyAccountTitle), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||||
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: deleteAccountDataEntries(presentationData: presentationData, mode: mode, peers: peers), style: .blocks, focusItemTag: focusItemTag, footerItem: footerItem)
|
||||||
|
|
||||||
|
return (controllerState, (listState, arguments))
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = ItemListController(context: context, state: signal, tabBarItem: nil)
|
||||||
|
presentControllerImpl = { [weak controller] c in
|
||||||
|
controller?.present(c, in: .window(.root))
|
||||||
|
}
|
||||||
|
pushControllerImpl = { [weak controller] c in
|
||||||
|
controller?.push(c)
|
||||||
|
}
|
||||||
|
replaceTopControllerImpl = { [weak controller] c in
|
||||||
|
if let navigationController = controller?.navigationController as? NavigationController {
|
||||||
|
navigationController.pushViewController(c, completion: { [weak navigationController, weak controller, weak c] in
|
||||||
|
if let navigationController = navigationController {
|
||||||
|
let controllers = navigationController.viewControllers.filter { $0 !== controller }
|
||||||
|
c?.navigationPresentation = .modal
|
||||||
|
navigationController.setViewControllers(controllers, animated: false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dismissImpl = { [weak controller] in
|
||||||
|
let _ = controller?.dismiss()
|
||||||
|
}
|
||||||
|
updateCodeImpl = { [weak controller] in
|
||||||
|
controller?.forEachItemNode { itemNode in
|
||||||
|
if let itemNode = itemNode as? DeleteAccountPhoneItemNode {
|
||||||
|
itemNode.updateCountryCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activateInputImpl = { [weak controller] in
|
||||||
|
controller?.forEachItemNode { itemNode in
|
||||||
|
if let itemNode = itemNode as? DeleteAccountPhoneItemNode {
|
||||||
|
itemNode.activateInput()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dismissInputImpl = { [weak controller] in
|
||||||
|
controller?.view.endEditing(true)
|
||||||
|
}
|
||||||
|
controller.didAppear = { firstTime in
|
||||||
|
if !firstTime {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
activateInputImpl?()
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCountryCodeImpl = { [weak controller] code, name in
|
||||||
|
controller?.forEachItemNode { itemNode in
|
||||||
|
if let itemNode = itemNode as? DeleteAccountPhoneItemNode {
|
||||||
|
itemNode.updateCountryCode(code: code, name: name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proceedImpl = { [weak controller] in
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
|
let action: ([EnginePeer], String?) -> Void = { preloadedPeers, password in
|
||||||
|
let nextMode: DeleteAccountDataMode?
|
||||||
|
switch mode {
|
||||||
|
case .peers:
|
||||||
|
if !preloadedPeers.isEmpty {
|
||||||
|
nextMode = .groups(preloadedPeers)
|
||||||
|
} else {
|
||||||
|
nextMode = .messages
|
||||||
|
}
|
||||||
|
case .groups:
|
||||||
|
nextMode = .messages
|
||||||
|
case .messages:
|
||||||
|
nextMode = .phone
|
||||||
|
case .phone:
|
||||||
|
if let twoStepAuthData = twoStepAuthData, case .set = twoStepAuthData {
|
||||||
|
nextMode = .password
|
||||||
|
} else {
|
||||||
|
nextMode = nil
|
||||||
|
}
|
||||||
|
case .password:
|
||||||
|
nextMode = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let nextMode = nextMode {
|
||||||
|
let controller = deleteAccountDataController(context: context, mode: nextMode, twoStepAuthData: twoStepAuthData)
|
||||||
|
replaceTopControllerImpl?(controller)
|
||||||
|
} else {
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.step_confirmation_show")
|
||||||
|
|
||||||
|
presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.DeleteAccount_ConfirmationAlertTitle, text: presentationData.strings.DeleteAccount_ConfirmationAlertText, actions: [TextAlertAction(type: .destructiveAction, title: presentationData.strings.DeleteAccount_ConfirmationAlertDelete, action: {
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.final")
|
||||||
|
|
||||||
|
invokeAppLogEventsSynchronization(postbox: context.account.postbox)
|
||||||
|
|
||||||
|
updateState { current in
|
||||||
|
var updated = current
|
||||||
|
updated.isLoading = true
|
||||||
|
return updated
|
||||||
|
}
|
||||||
|
|
||||||
|
let accountId = context.account.id
|
||||||
|
let accountManager = context.sharedContext.accountManager
|
||||||
|
let _ = (context.engine.auth.deleteAccount(reason: "Manual", password: password)
|
||||||
|
|> deliverOnMainQueue).start(error: { _ in
|
||||||
|
updateState { current in
|
||||||
|
var updated = current
|
||||||
|
updated.isLoading = false
|
||||||
|
return updated
|
||||||
|
}
|
||||||
|
|
||||||
|
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]))
|
||||||
|
}, completed: {
|
||||||
|
dismissImpl?()
|
||||||
|
|
||||||
|
let presentGlobalController = context.sharedContext.presentGlobalController
|
||||||
|
let _ = logoutFromAccount(id: accountId, accountManager: accountManager, alreadyLoggedOutRemotely: true).start(completed: {
|
||||||
|
Queue.mainQueue().after(0.1) {
|
||||||
|
presentGlobalController(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.DeleteAccount_Success), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.step_confirmation_cancel")
|
||||||
|
|
||||||
|
dismissImpl?()
|
||||||
|
})]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case .peers:
|
||||||
|
let _ = (preloadedGroupPeers.get()
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { peers in
|
||||||
|
action(peers, nil)
|
||||||
|
})
|
||||||
|
case .phone:
|
||||||
|
var phoneNumber: String?
|
||||||
|
controller?.forEachItemNode { itemNode in
|
||||||
|
if let itemNode = itemNode as? DeleteAccountPhoneItemNode {
|
||||||
|
phoneNumber = itemNode.phoneNumber
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let phoneNumber = phoneNumber, phoneNumber.count > 4 {
|
||||||
|
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||||
|
|> deliverOnMainQueue)
|
||||||
|
.start(next: { accountPeer in
|
||||||
|
if let accountPeer = accountPeer, case let .user(user) = accountPeer, var phone = user.phone {
|
||||||
|
if !phone.hasPrefix("+") {
|
||||||
|
phone = "+\(phone)"
|
||||||
|
}
|
||||||
|
if phone != phoneNumber {
|
||||||
|
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.DeleteAccount_InvalidPhoneNumberError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
action([], nil)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case .password:
|
||||||
|
let state = stateValue.with { $0 }
|
||||||
|
if !state.password.isEmpty {
|
||||||
|
updateState { current in
|
||||||
|
var updated = current
|
||||||
|
updated.isLoading = true
|
||||||
|
return updated
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = (context.engine.auth.requestTwoStepVerifiationSettings(password: state.password)
|
||||||
|
|> deliverOnMainQueue).start(error: { error in
|
||||||
|
updateState { current in
|
||||||
|
var updated = current
|
||||||
|
updated.isLoading = false
|
||||||
|
return updated
|
||||||
|
}
|
||||||
|
|
||||||
|
let text: String
|
||||||
|
switch error {
|
||||||
|
case .limitExceeded:
|
||||||
|
text = presentationData.strings.LoginPassword_FloodError
|
||||||
|
case .invalidPassword:
|
||||||
|
text = presentationData.strings.DeleteAccount_InvalidPasswordError
|
||||||
|
default:
|
||||||
|
text = presentationData.strings.Login_UnknownError
|
||||||
|
}
|
||||||
|
presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]))
|
||||||
|
}, completed: {
|
||||||
|
updateState { current in
|
||||||
|
var updated = current
|
||||||
|
updated.isLoading = false
|
||||||
|
return updated
|
||||||
|
}
|
||||||
|
|
||||||
|
action([], state.password)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
action([], nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case .peers:
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.step_cloud_show")
|
||||||
|
case .groups:
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.step_groups_show")
|
||||||
|
case .messages:
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.step_messages_show")
|
||||||
|
case .phone:
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.step_phone_show")
|
||||||
|
case .password:
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.step_2fa_show")
|
||||||
|
}
|
||||||
|
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
170
submodules/SettingsUI/Sources/DeleteAccountFooterItem.swift
Normal file
170
submodules/SettingsUI/Sources/DeleteAccountFooterItem.swift
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import Display
|
||||||
|
import TelegramPresentationData
|
||||||
|
import ItemListUI
|
||||||
|
import PresentationDataUtils
|
||||||
|
import SolidRoundedButtonNode
|
||||||
|
import AppBundle
|
||||||
|
|
||||||
|
final class DeleteAccountFooterItem: ItemListControllerFooterItem {
|
||||||
|
let theme: PresentationTheme
|
||||||
|
let title: String
|
||||||
|
let secondaryTitle: String
|
||||||
|
let action: () -> Void
|
||||||
|
let secondaryAction: () -> Void
|
||||||
|
|
||||||
|
init(theme: PresentationTheme, title: String, secondaryTitle: String, action: @escaping () -> Void, secondaryAction: @escaping () -> Void) {
|
||||||
|
self.theme = theme
|
||||||
|
self.title = title
|
||||||
|
self.secondaryTitle = secondaryTitle
|
||||||
|
self.action = action
|
||||||
|
self.secondaryAction = secondaryAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEqual(to: ItemListControllerFooterItem) -> Bool {
|
||||||
|
if let item = to as? DeleteAccountFooterItem {
|
||||||
|
return self.theme === item.theme && self.title == item.title && self.secondaryTitle == item.secondaryTitle
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func node(current: ItemListControllerFooterItemNode?) -> ItemListControllerFooterItemNode {
|
||||||
|
if let current = current as? DeleteAccountFooterItemNode {
|
||||||
|
current.item = self
|
||||||
|
return current
|
||||||
|
} else {
|
||||||
|
return DeleteAccountFooterItemNode(item: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class DeleteAccountFooterItemNode: ItemListControllerFooterItemNode {
|
||||||
|
private let backgroundNode: NavigationBackgroundNode
|
||||||
|
private let separatorNode: ASDisplayNode
|
||||||
|
private let clipNode: ASDisplayNode
|
||||||
|
private let buttonNode: SolidRoundedButtonNode
|
||||||
|
private let secondaryButtonNode: HighlightableButtonNode
|
||||||
|
|
||||||
|
private var validLayout: ContainerViewLayout?
|
||||||
|
|
||||||
|
var item: DeleteAccountFooterItem {
|
||||||
|
didSet {
|
||||||
|
self.updateItem()
|
||||||
|
if let layout = self.validLayout {
|
||||||
|
let _ = self.updateLayout(layout: layout, transition: .immediate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init(item: DeleteAccountFooterItem) {
|
||||||
|
self.item = item
|
||||||
|
|
||||||
|
self.backgroundNode = NavigationBackgroundNode(color: item.theme.rootController.tabBar.backgroundColor)
|
||||||
|
self.separatorNode = ASDisplayNode()
|
||||||
|
|
||||||
|
self.clipNode = ASDisplayNode()
|
||||||
|
self.clipNode.clipsToBounds = true
|
||||||
|
|
||||||
|
self.buttonNode = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(backgroundColor: .black, foregroundColor: .white), height: 50.0, cornerRadius: 11.0, gloss: true)
|
||||||
|
|
||||||
|
self.secondaryButtonNode = HighlightableButtonNode()
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.addSubnode(self.backgroundNode)
|
||||||
|
self.addSubnode(self.separatorNode)
|
||||||
|
self.addSubnode(self.clipNode)
|
||||||
|
self.clipNode.addSubnode(self.buttonNode)
|
||||||
|
self.clipNode.addSubnode(self.secondaryButtonNode)
|
||||||
|
|
||||||
|
self.secondaryButtonNode.addTarget(self, action: #selector(self.secondaryButtonPressed), forControlEvents: .touchUpInside)
|
||||||
|
|
||||||
|
self.updateItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func secondaryButtonPressed() {
|
||||||
|
self.item.secondaryAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateItem() {
|
||||||
|
self.backgroundNode.updateColor(color: self.item.theme.rootController.tabBar.backgroundColor, transition: .immediate)
|
||||||
|
self.separatorNode.backgroundColor = self.item.theme.rootController.tabBar.separatorColor
|
||||||
|
|
||||||
|
let backgroundColor = self.item.theme.list.itemCheckColors.fillColor
|
||||||
|
let textColor = self.item.theme.list.itemCheckColors.foregroundColor
|
||||||
|
|
||||||
|
self.buttonNode.updateTheme(SolidRoundedButtonTheme(backgroundColor: backgroundColor, foregroundColor: textColor), animated: false)
|
||||||
|
self.buttonNode.title = self.item.title
|
||||||
|
|
||||||
|
self.buttonNode.pressed = { [weak self] in
|
||||||
|
self?.item.action()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.secondaryButtonNode.setTitle(self.item.secondaryTitle, with: Font.regular(17.0), with: self.item.theme.list.itemAccentColor, for: .normal)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
|
transition.updateAlpha(node: self.backgroundNode, alpha: alpha)
|
||||||
|
transition.updateAlpha(node: self.separatorNode, alpha: alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||||
|
self.validLayout = layout
|
||||||
|
|
||||||
|
let buttonInset: CGFloat = 16.0
|
||||||
|
let buttonWidth = layout.size.width - layout.safeInsets.left - layout.safeInsets.right - buttonInset * 2.0
|
||||||
|
let buttonHeight = self.buttonNode.updateLayout(width: buttonWidth, transition: transition)
|
||||||
|
let topInset: CGFloat = 9.0
|
||||||
|
let bottomInset: CGFloat = 23.0
|
||||||
|
let spacing: CGFloat = 23.0
|
||||||
|
|
||||||
|
let insets = layout.insets(options: [.input])
|
||||||
|
|
||||||
|
let secondaryButtonSize = self.secondaryButtonNode.measure(CGSize(width: buttonWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||||
|
|
||||||
|
var panelHeight: CGFloat = buttonHeight + topInset + spacing + secondaryButtonSize.height + bottomInset
|
||||||
|
|
||||||
|
var buttonOffset: CGFloat = 0.0
|
||||||
|
let totalPanelHeight: CGFloat
|
||||||
|
|
||||||
|
if (self.buttonNode.title?.isEmpty ?? false) {
|
||||||
|
buttonOffset = -buttonHeight - topInset
|
||||||
|
self.buttonNode.alpha = 0.0
|
||||||
|
} else {
|
||||||
|
self.buttonNode.alpha = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
if let inputHeight = layout.inputHeight, inputHeight > 0.0 {
|
||||||
|
panelHeight += buttonOffset
|
||||||
|
totalPanelHeight = panelHeight + insets.bottom
|
||||||
|
} else {
|
||||||
|
panelHeight += insets.bottom
|
||||||
|
totalPanelHeight = panelHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - totalPanelHeight), size: CGSize(width: layout.size.width, height: panelHeight))
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.backgroundNode, frame: panelFrame)
|
||||||
|
self.backgroundNode.update(size: panelFrame.size, transition: transition)
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.clipNode, frame: panelFrame)
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + buttonInset, y: topInset + buttonOffset), size: CGSize(width: buttonWidth, height: buttonHeight)))
|
||||||
|
transition.updateFrame(node: self.secondaryButtonNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - secondaryButtonSize.width) / 2.0), y: topInset + buttonHeight + spacing + buttonOffset), size: secondaryButtonSize))
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: panelFrame.origin, size: CGSize(width: panelFrame.width, height: UIScreenPixel)))
|
||||||
|
|
||||||
|
return panelHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||||
|
if self.backgroundNode.frame.contains(point) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,441 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
import LegacyComponents
|
||||||
|
import TelegramPresentationData
|
||||||
|
import ItemListUI
|
||||||
|
import PresentationDataUtils
|
||||||
|
import OverlayStatusController
|
||||||
|
import AccountContext
|
||||||
|
import AlertUI
|
||||||
|
import PresentationDataUtils
|
||||||
|
import UrlHandling
|
||||||
|
import AccountUtils
|
||||||
|
import PremiumUI
|
||||||
|
import PasswordSetupUI
|
||||||
|
|
||||||
|
private struct DeleteAccountOptionsArguments {
|
||||||
|
let changePhoneNumber: () -> Void
|
||||||
|
let addAccount: () -> Void
|
||||||
|
let setupPrivacy: () -> Void
|
||||||
|
let setupTwoStepAuth: () -> Void
|
||||||
|
let setPasscode: () -> Void
|
||||||
|
let clearCache: () -> Void
|
||||||
|
let clearSyncedContacts: () -> Void
|
||||||
|
let deleteChats: () -> Void
|
||||||
|
let contactSupport: () -> Void
|
||||||
|
let deleteAccount: () -> Void
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum DeleteAccountOptionsSection: Int32 {
|
||||||
|
case add
|
||||||
|
case privacy
|
||||||
|
case remove
|
||||||
|
case support
|
||||||
|
case delete
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum DeleteAccountOptionsEntry: ItemListNodeEntry, Equatable {
|
||||||
|
case changePhoneNumber(PresentationTheme, String, String)
|
||||||
|
case addAccount(PresentationTheme, String, String)
|
||||||
|
|
||||||
|
case changePrivacy(PresentationTheme, String, String)
|
||||||
|
case setTwoStepAuth(PresentationTheme, String, String)
|
||||||
|
case setPasscode(PresentationTheme, String, String)
|
||||||
|
|
||||||
|
case clearCache(PresentationTheme, String, String)
|
||||||
|
case clearSyncedContacts(PresentationTheme, String, String)
|
||||||
|
case deleteChats(PresentationTheme, String, String)
|
||||||
|
|
||||||
|
case contactSupport(PresentationTheme, String, String)
|
||||||
|
|
||||||
|
case deleteAccount(PresentationTheme, String)
|
||||||
|
|
||||||
|
var section: ItemListSectionId {
|
||||||
|
switch self {
|
||||||
|
case .changePhoneNumber, .addAccount:
|
||||||
|
return DeleteAccountOptionsSection.add.rawValue
|
||||||
|
case .changePrivacy, .setTwoStepAuth, .setPasscode:
|
||||||
|
return DeleteAccountOptionsSection.privacy.rawValue
|
||||||
|
case .clearCache, .clearSyncedContacts, .deleteChats:
|
||||||
|
return DeleteAccountOptionsSection.remove.rawValue
|
||||||
|
case .contactSupport:
|
||||||
|
return DeleteAccountOptionsSection.support.rawValue
|
||||||
|
case .deleteAccount:
|
||||||
|
return DeleteAccountOptionsSection.delete.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stableId: Int32 {
|
||||||
|
switch self {
|
||||||
|
case .changePhoneNumber:
|
||||||
|
return 0
|
||||||
|
case .addAccount:
|
||||||
|
return 1
|
||||||
|
case .changePrivacy:
|
||||||
|
return 2
|
||||||
|
case .setTwoStepAuth:
|
||||||
|
return 3
|
||||||
|
case .setPasscode:
|
||||||
|
return 4
|
||||||
|
case .clearCache:
|
||||||
|
return 5
|
||||||
|
case .clearSyncedContacts:
|
||||||
|
return 6
|
||||||
|
case .deleteChats:
|
||||||
|
return 7
|
||||||
|
case .contactSupport:
|
||||||
|
return 8
|
||||||
|
case .deleteAccount:
|
||||||
|
return 9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func <(lhs: DeleteAccountOptionsEntry, rhs: DeleteAccountOptionsEntry) -> Bool {
|
||||||
|
return lhs.stableId < rhs.stableId
|
||||||
|
}
|
||||||
|
|
||||||
|
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||||
|
let arguments = arguments as! DeleteAccountOptionsArguments
|
||||||
|
switch self {
|
||||||
|
case let .changePhoneNumber(_, title, text):
|
||||||
|
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.changePhoneNumber, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||||
|
arguments.changePhoneNumber()
|
||||||
|
})
|
||||||
|
case let .addAccount(_, title, text):
|
||||||
|
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.deleteAddAccount, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||||
|
arguments.addAccount()
|
||||||
|
})
|
||||||
|
case let .changePrivacy(_, title, text):
|
||||||
|
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.security, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||||
|
arguments.setupPrivacy()
|
||||||
|
})
|
||||||
|
case let .setTwoStepAuth(_, title, text):
|
||||||
|
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.deleteSetTwoStepAuth, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||||
|
arguments.setupTwoStepAuth()
|
||||||
|
})
|
||||||
|
case let .setPasscode(_, title, text):
|
||||||
|
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.deleteSetPasscode, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||||
|
arguments.setPasscode()
|
||||||
|
})
|
||||||
|
case let .clearCache(_, title, text):
|
||||||
|
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.dataAndStorage, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||||
|
arguments.clearCache()
|
||||||
|
})
|
||||||
|
case let .clearSyncedContacts(_, title, text):
|
||||||
|
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.clearSynced, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||||
|
arguments.clearSyncedContacts()
|
||||||
|
})
|
||||||
|
case let .deleteChats(_, title, text):
|
||||||
|
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.deleteChats, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||||
|
arguments.deleteChats()
|
||||||
|
})
|
||||||
|
case let .contactSupport(_, title, text):
|
||||||
|
return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.support, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
|
||||||
|
arguments.contactSupport()
|
||||||
|
})
|
||||||
|
case let .deleteAccount(_, title):
|
||||||
|
return ItemListActionItem(presentationData: presentationData, title: title, kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||||
|
arguments.deleteAccount()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func deleteAccountOptionsEntries(presentationData: PresentationData, canAddAccounts: Bool, hasTwoStepAuth: Bool, hasPasscode: Bool) -> [DeleteAccountOptionsEntry] {
|
||||||
|
var entries: [DeleteAccountOptionsEntry] = []
|
||||||
|
|
||||||
|
entries.append(.changePhoneNumber(presentationData.theme, presentationData.strings.DeleteAccount_Options_ChangePhoneNumberTitle, presentationData.strings.DeleteAccount_Options_ChangePhoneNumberText))
|
||||||
|
if canAddAccounts {
|
||||||
|
entries.append(.addAccount(presentationData.theme, presentationData.strings.DeleteAccount_Options_AddAccountTitle, presentationData.strings.DeleteAccount_Options_AddAccountText))
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.append(.changePrivacy(presentationData.theme, presentationData.strings.DeleteAccount_Options_ChangePrivacyTitle, presentationData.strings.DeleteAccount_Options_ChangePrivacyText))
|
||||||
|
if !hasTwoStepAuth {
|
||||||
|
entries.append(.setTwoStepAuth(presentationData.theme, presentationData.strings.DeleteAccount_Options_SetTwoStepAuthTitle, presentationData.strings.DeleteAccount_Options_SetTwoStepAuthText))
|
||||||
|
}
|
||||||
|
if !hasPasscode {
|
||||||
|
entries.append(.setPasscode(presentationData.theme, presentationData.strings.DeleteAccount_Options_SetPasscodeTitle, presentationData.strings.DeleteAccount_Options_SetPasscodeText))
|
||||||
|
}
|
||||||
|
entries.append(.clearCache(presentationData.theme, presentationData.strings.DeleteAccount_Options_ClearCacheTitle, presentationData.strings.DeleteAccount_Options_ClearCacheText))
|
||||||
|
entries.append(.clearSyncedContacts(presentationData.theme, presentationData.strings.DeleteAccount_Options_ClearSyncedContactsTitle, presentationData.strings.DeleteAccount_Options_ClearSyncedContactsText))
|
||||||
|
entries.append(.deleteChats(presentationData.theme, presentationData.strings.DeleteAccount_Options_DeleteChatsTitle, presentationData.strings.DeleteAccount_Options_DeleteChatsText))
|
||||||
|
|
||||||
|
entries.append(.contactSupport(presentationData.theme, presentationData.strings.DeleteAccount_Options_ContactSupportTitle, presentationData.strings.DeleteAccount_Options_ContactSupportText))
|
||||||
|
|
||||||
|
entries.append(.deleteAccount(presentationData.theme, presentationData.strings.DeleteAccount_DeleteMyAccount))
|
||||||
|
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
public func deleteAccountOptionsController(context: AccountContext, navigationController: NavigationController, hasTwoStepAuth: Bool, twoStepAuthData: TwoStepVerificationAccessConfiguration?) -> ViewController {
|
||||||
|
var pushControllerImpl: ((ViewController) -> Void)?
|
||||||
|
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||||
|
var replaceTopControllerImpl: ((ViewController, Bool) -> Void)?
|
||||||
|
var dismissImpl: (() -> Void)?
|
||||||
|
|
||||||
|
let supportPeerDisposable = MetaDisposable()
|
||||||
|
|
||||||
|
let arguments = DeleteAccountOptionsArguments(changePhoneNumber: {
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.options_phone_change_tap")
|
||||||
|
|
||||||
|
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.engine.account.peerId))
|
||||||
|
|> deliverOnMainQueue).start(next: { accountPeer in
|
||||||
|
guard let accountPeer = accountPeer, case let .user(user) = accountPeer else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let introController = PrivacyIntroController(context: context, mode: .changePhoneNumber(user.phone ?? ""), proceedAction: {
|
||||||
|
replaceTopControllerImpl?(ChangePhoneNumberController(context: context), false)
|
||||||
|
})
|
||||||
|
pushControllerImpl?(introController)
|
||||||
|
dismissImpl?()
|
||||||
|
})
|
||||||
|
}, addAccount: {
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.options_add_account_tap")
|
||||||
|
|
||||||
|
let _ = (activeAccountsAndPeers(context: context)
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue
|
||||||
|
).start(next: { accountAndPeer, accountsAndPeers in
|
||||||
|
var maximumAvailableAccounts: Int = 3
|
||||||
|
if accountAndPeer?.1.isPremium == true && !context.account.testingEnvironment {
|
||||||
|
maximumAvailableAccounts = 4
|
||||||
|
}
|
||||||
|
var count: Int = 1
|
||||||
|
for (accountContext, peer, _) in accountsAndPeers {
|
||||||
|
if !accountContext.account.testingEnvironment {
|
||||||
|
if peer.isPremium {
|
||||||
|
maximumAvailableAccounts = 4
|
||||||
|
}
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if count >= maximumAvailableAccounts {
|
||||||
|
var replaceImpl: ((ViewController) -> Void)?
|
||||||
|
let controller = PremiumLimitScreen(context: context, subject: .accounts, count: Int32(count), action: {
|
||||||
|
let controller = PremiumIntroScreen(context: context, source: .accounts)
|
||||||
|
replaceImpl?(controller)
|
||||||
|
})
|
||||||
|
replaceImpl = { [weak controller] c in
|
||||||
|
controller?.replace(with: c)
|
||||||
|
}
|
||||||
|
pushControllerImpl?(controller)
|
||||||
|
} else {
|
||||||
|
context.sharedContext.beginNewAuth(testingEnvironment: context.account.testingEnvironment)
|
||||||
|
|
||||||
|
dismissImpl?()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, setupPrivacy: {
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.options_privacy_tap")
|
||||||
|
|
||||||
|
replaceTopControllerImpl?(makePrivacyAndSecurityController(context: context), false)
|
||||||
|
}, setupTwoStepAuth: {
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.options_2fa_tap")
|
||||||
|
|
||||||
|
if let data = twoStepAuthData {
|
||||||
|
switch data {
|
||||||
|
case .set:
|
||||||
|
break
|
||||||
|
case let .notSet(pendingEmail):
|
||||||
|
if pendingEmail == nil {
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let controller = TwoFactorAuthSplashScreen(sharedContext: context.sharedContext, engine: .authorized(context.engine), mode: .intro(.init(
|
||||||
|
title: presentationData.strings.TwoFactorSetup_Intro_Title,
|
||||||
|
text: presentationData.strings.TwoFactorSetup_Intro_Text,
|
||||||
|
actionText: presentationData.strings.TwoFactorSetup_Intro_Action,
|
||||||
|
doneText: presentationData.strings.TwoFactorSetup_Done_Action
|
||||||
|
)))
|
||||||
|
|
||||||
|
replaceTopControllerImpl?(controller, false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = twoStepVerificationUnlockSettingsController(context: context, mode: .access(intro: false, data: twoStepAuthData.flatMap({ Signal<TwoStepVerificationUnlockSettingsControllerData, NoError>.single(.access(configuration: $0)) })))
|
||||||
|
replaceTopControllerImpl?(controller, false)
|
||||||
|
}, setPasscode: {
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.options_passcode_tap")
|
||||||
|
|
||||||
|
let _ = passcodeOptionsAccessController(context: context, pushController: { controller in
|
||||||
|
replaceTopControllerImpl?(controller, false)
|
||||||
|
}, completion: { _ in
|
||||||
|
replaceTopControllerImpl?(passcodeOptionsController(context: context), false)
|
||||||
|
}).start(next: { controller in
|
||||||
|
if let controller = controller {
|
||||||
|
pushControllerImpl?(controller)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
dismissImpl?()
|
||||||
|
}, clearCache: {
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.options_clear_cache_tap")
|
||||||
|
|
||||||
|
pushControllerImpl?(storageUsageController(context: context))
|
||||||
|
dismissImpl?()
|
||||||
|
}, clearSyncedContacts: {
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.options_clear_contacts_tap")
|
||||||
|
|
||||||
|
replaceTopControllerImpl?(dataPrivacyController(context: context), false)
|
||||||
|
}, deleteChats: {
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.options_delete_chats_tap")
|
||||||
|
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
|
var faqUrl = presentationData.strings.DeleteAccount_DeleteMessagesURL
|
||||||
|
if faqUrl == "DeleteAccount.DeleteMessagesURL" || faqUrl.isEmpty {
|
||||||
|
faqUrl = "https://telegram.org/faq#q-can-i-delete-my-messages"
|
||||||
|
}
|
||||||
|
let resolvedUrl = resolveInstantViewUrl(account: context.account, url: faqUrl)
|
||||||
|
|
||||||
|
let resolvedUrlPromise = Promise<ResolvedUrl>()
|
||||||
|
resolvedUrlPromise.set(resolvedUrl)
|
||||||
|
|
||||||
|
let openFaq: (Promise<ResolvedUrl>) -> Void = { resolvedUrl in
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
|
||||||
|
presentControllerImpl?(controller, nil)
|
||||||
|
let _ = (resolvedUrl.get()
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak controller] resolvedUrl in
|
||||||
|
controller?.dismiss()
|
||||||
|
dismissImpl?()
|
||||||
|
|
||||||
|
context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigation in
|
||||||
|
}, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { controller, arguments in
|
||||||
|
pushControllerImpl?(controller)
|
||||||
|
}, dismissInput: {}, contentContext: nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
openFaq(resolvedUrlPromise)
|
||||||
|
}, contactSupport: { [weak navigationController] in
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.options_support_tap")
|
||||||
|
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
|
let supportPeer = Promise<PeerId?>()
|
||||||
|
supportPeer.set(context.engine.peers.supportPeerId())
|
||||||
|
|
||||||
|
var faqUrl = presentationData.strings.Settings_FAQ_URL
|
||||||
|
if faqUrl == "Settings.FAQ_URL" || faqUrl.isEmpty {
|
||||||
|
faqUrl = "https://telegram.org/faq#general"
|
||||||
|
}
|
||||||
|
let resolvedUrl = resolveInstantViewUrl(account: context.account, url: faqUrl)
|
||||||
|
|
||||||
|
let resolvedUrlPromise = Promise<ResolvedUrl>()
|
||||||
|
resolvedUrlPromise.set(resolvedUrl)
|
||||||
|
|
||||||
|
let openFaq: (Promise<ResolvedUrl>) -> Void = { resolvedUrl in
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
|
||||||
|
presentControllerImpl?(controller, nil)
|
||||||
|
let _ = (resolvedUrl.get()
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak controller] resolvedUrl in
|
||||||
|
controller?.dismiss()
|
||||||
|
dismissImpl?()
|
||||||
|
|
||||||
|
context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigation in
|
||||||
|
}, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { controller, arguments in
|
||||||
|
pushControllerImpl?(controller)
|
||||||
|
}, dismissInput: {}, contentContext: nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let alertController = textAlertController(context: context, title: nil, text: presentationData.strings.Settings_FAQ_Intro, actions: [
|
||||||
|
TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_FAQ_Button, action: {
|
||||||
|
openFaq(resolvedUrlPromise)
|
||||||
|
dismissImpl?()
|
||||||
|
}),
|
||||||
|
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
||||||
|
supportPeerDisposable.set((supportPeer.get()
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { peerId in
|
||||||
|
if let peerId = peerId, let navigationController = navigationController {
|
||||||
|
dismissImpl?()
|
||||||
|
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: peerId)))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
])
|
||||||
|
alertController.dismissed = {
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.options_support_cancel")
|
||||||
|
}
|
||||||
|
presentControllerImpl?(alertController, nil)
|
||||||
|
}, deleteAccount: {
|
||||||
|
let controller = deleteAccountDataController(context: context, mode: .peers, twoStepAuthData: twoStepAuthData)
|
||||||
|
replaceTopControllerImpl?(controller, true)
|
||||||
|
})
|
||||||
|
|
||||||
|
let signal = combineLatest(queue: .mainQueue(),
|
||||||
|
context.sharedContext.presentationData,
|
||||||
|
context.sharedContext.accountManager.accessChallengeData(),
|
||||||
|
activeAccountsAndPeers(context: context)
|
||||||
|
)
|
||||||
|
|> map { presentationData, accessChallengeData, accountsAndPeers -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
|
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
||||||
|
dismissImpl?()
|
||||||
|
})
|
||||||
|
|
||||||
|
var hasPasscode = false
|
||||||
|
switch accessChallengeData.data {
|
||||||
|
case .numericalPassword, .plaintextPassword:
|
||||||
|
hasPasscode = true
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
let canAddAccounts = accountsAndPeers.1.count + 1 < maximumNumberOfAccounts
|
||||||
|
|
||||||
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.DeleteAccount_AlternativeOptionsTitle), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||||
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: deleteAccountOptionsEntries(presentationData: presentationData, canAddAccounts: canAddAccounts, hasTwoStepAuth: hasTwoStepAuth, hasPasscode: hasPasscode), style: .blocks)
|
||||||
|
|
||||||
|
return (controllerState, (listState, arguments))
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = ItemListController(context: context, state: signal, tabBarItem: nil)
|
||||||
|
controller.navigationPresentation = .modal
|
||||||
|
pushControllerImpl = { [weak navigationController] value in
|
||||||
|
navigationController?.pushViewController(value, animated: false)
|
||||||
|
}
|
||||||
|
presentControllerImpl = { [weak controller] value, arguments in
|
||||||
|
controller?.present(value, in: .window(.root), with: arguments ?? ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
|
}
|
||||||
|
replaceTopControllerImpl = { [weak navigationController] c, complex in
|
||||||
|
if complex {
|
||||||
|
navigationController?.pushViewController(c, completion: { [weak navigationController, weak controller, weak c] in
|
||||||
|
if let navigationController = navigationController {
|
||||||
|
let controllers = navigationController.viewControllers.filter { $0 !== controller }
|
||||||
|
c?.navigationPresentation = .modal
|
||||||
|
navigationController.setViewControllers(controllers, animated: false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if c is PrivacyAndSecurityControllerImpl {
|
||||||
|
if let navigationController = navigationController {
|
||||||
|
if let existing = navigationController.viewControllers.first(where: { $0 is PrivacyAndSecurityControllerImpl }) as? ViewController {
|
||||||
|
existing.scrollToTop?()
|
||||||
|
dismissImpl?()
|
||||||
|
} else {
|
||||||
|
navigationController.replaceTopController(c, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
navigationController?.replaceTopController(c, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dismissImpl = { [weak controller] in
|
||||||
|
let _ = controller?.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
addAppLogEvent(postbox: context.account.postbox, type: "deactivate.options_show")
|
||||||
|
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
288
submodules/SettingsUI/Sources/DeleteAccountPeersItem.swift
Normal file
288
submodules/SettingsUI/Sources/DeleteAccountPeersItem.swift
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import SwiftSignalKit
|
||||||
|
import TelegramCore
|
||||||
|
import TelegramPresentationData
|
||||||
|
import TelegramUIPreferences
|
||||||
|
import ItemListUI
|
||||||
|
import PresentationDataUtils
|
||||||
|
import HorizontalPeerItem
|
||||||
|
import AccountContext
|
||||||
|
import MergeLists
|
||||||
|
|
||||||
|
private struct PeersEntry: Comparable, Identifiable {
|
||||||
|
let index: Int
|
||||||
|
let peer: EnginePeer
|
||||||
|
let theme: PresentationTheme
|
||||||
|
let strings: PresentationStrings
|
||||||
|
|
||||||
|
var stableId: EnginePeer.Id {
|
||||||
|
return self.peer.id
|
||||||
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: PeersEntry, rhs: PeersEntry) -> Bool {
|
||||||
|
if lhs.index != rhs.index {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.peer != rhs.peer {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.theme !== rhs.theme {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.strings !== rhs.strings {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
static func <(lhs: PeersEntry, rhs: PeersEntry) -> Bool {
|
||||||
|
return lhs.index < rhs.index
|
||||||
|
}
|
||||||
|
|
||||||
|
func item(context: AccountContext) -> ListViewItem {
|
||||||
|
return HorizontalPeerItem(theme: self.theme, strings: self.strings, mode: .list(compact: true), context: context, peer: self.peer, presence: nil, unreadBadge: nil, action: { _ in }, contextAction: nil, isPeerSelected: { _ in return false }, customWidth: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct DeleteAccountPeersItemNodeTransition {
|
||||||
|
let deletions: [ListViewDeleteItem]
|
||||||
|
let insertions: [ListViewInsertItem]
|
||||||
|
let updates: [ListViewUpdateItem]
|
||||||
|
let firstTime: Bool
|
||||||
|
let animated: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
private func preparedPeersTransition(context: AccountContext, from fromEntries: [PeersEntry], to toEntries: [PeersEntry], firstTime: Bool, animated: Bool) -> DeleteAccountPeersItemNodeTransition {
|
||||||
|
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||||
|
|
||||||
|
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||||
|
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context), directionHint: .Down) }
|
||||||
|
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context), directionHint: nil) }
|
||||||
|
|
||||||
|
return DeleteAccountPeersItemNodeTransition(deletions: deletions, insertions: insertions, updates: updates, firstTime: firstTime, animated: animated)
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeleteAccountPeersItem: ListViewItem, ItemListItem {
|
||||||
|
var sectionId: ItemListSectionId
|
||||||
|
|
||||||
|
let context: AccountContext
|
||||||
|
let theme: PresentationTheme
|
||||||
|
let strings: PresentationStrings
|
||||||
|
let peers: [EnginePeer]
|
||||||
|
|
||||||
|
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peers: [EnginePeer], sectionId: ItemListSectionId) {
|
||||||
|
self.context = context
|
||||||
|
self.theme = theme
|
||||||
|
self.strings = strings
|
||||||
|
self.peers = peers
|
||||||
|
self.sectionId = sectionId
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||||
|
async {
|
||||||
|
let node = DeleteAccountPeersItemNode()
|
||||||
|
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||||
|
|
||||||
|
node.contentSize = layout.contentSize
|
||||||
|
node.insets = layout.insets
|
||||||
|
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
completion(node, {
|
||||||
|
return (nil, { _ in apply() })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
if let nodeValue = node() as? DeleteAccountPeersItemNode {
|
||||||
|
let makeLayout = nodeValue.asyncLayout()
|
||||||
|
|
||||||
|
async {
|
||||||
|
let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
completion(layout, { _ in
|
||||||
|
apply()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeleteAccountPeersItemNode: ListViewItemNode, ItemListItemNode {
|
||||||
|
private let backgroundNode: ASDisplayNode
|
||||||
|
private let topStripeNode: ASDisplayNode
|
||||||
|
private let bottomStripeNode: ASDisplayNode
|
||||||
|
private let maskNode: ASImageNode
|
||||||
|
|
||||||
|
private let dataPromise = Promise<(AccountContext, [EnginePeer], PresentationTheme, PresentationStrings)>()
|
||||||
|
private var disposable: Disposable?
|
||||||
|
|
||||||
|
private var item: DeleteAccountPeersItem?
|
||||||
|
private var layoutParams: ListViewItemLayoutParams?
|
||||||
|
|
||||||
|
private let listView: ListView
|
||||||
|
private var queuedTransitions: [DeleteAccountPeersItemNodeTransition] = []
|
||||||
|
|
||||||
|
var tag: ItemListItemTag? {
|
||||||
|
return self.item?.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
self.backgroundNode = ASDisplayNode()
|
||||||
|
self.backgroundNode.isLayerBacked = true
|
||||||
|
|
||||||
|
self.topStripeNode = ASDisplayNode()
|
||||||
|
self.topStripeNode.isLayerBacked = true
|
||||||
|
|
||||||
|
self.bottomStripeNode = ASDisplayNode()
|
||||||
|
self.bottomStripeNode.isLayerBacked = true
|
||||||
|
|
||||||
|
self.maskNode = ASImageNode()
|
||||||
|
self.maskNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.listView = ListView()
|
||||||
|
self.listView.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||||
|
|
||||||
|
super.init(layerBacked: false, dynamicBounce: false)
|
||||||
|
|
||||||
|
self.addSubnode(self.listView)
|
||||||
|
|
||||||
|
let previous: Atomic<[PeersEntry]> = Atomic(value: [])
|
||||||
|
let firstTime:Atomic<Bool> = Atomic(value: true)
|
||||||
|
|
||||||
|
self.disposable = (self.dataPromise.get() |> deliverOnMainQueue).start(next: { [weak self] data in
|
||||||
|
if let strongSelf = self {
|
||||||
|
let (context, peers, theme, strings) = data
|
||||||
|
|
||||||
|
var entries: [PeersEntry] = []
|
||||||
|
for peer in peers {
|
||||||
|
entries.append(PeersEntry(index: entries.count, peer: peer, theme: theme, strings: strings))
|
||||||
|
}
|
||||||
|
|
||||||
|
let animated = !firstTime.swap(false)
|
||||||
|
|
||||||
|
let transition = preparedPeersTransition(context: context, from: previous.swap(entries), to: entries, firstTime: !animated, animated: animated)
|
||||||
|
strongSelf.enqueueTransition(transition)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.disposable?.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func enqueueTransition(_ transition: DeleteAccountPeersItemNodeTransition) {
|
||||||
|
self.queuedTransitions.append(transition)
|
||||||
|
self.dequeueTransitions()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func dequeueTransitions() {
|
||||||
|
while !self.queuedTransitions.isEmpty {
|
||||||
|
let transition = self.queuedTransitions.removeFirst()
|
||||||
|
|
||||||
|
var options = ListViewDeleteAndInsertOptions()
|
||||||
|
if transition.firstTime {
|
||||||
|
options.insert(.PreferSynchronousResourceLoading)
|
||||||
|
options.insert(.PreferSynchronousDrawing)
|
||||||
|
options.insert(.Synchronous)
|
||||||
|
options.insert(.LowLatency)
|
||||||
|
} else if transition.animated {
|
||||||
|
options.insert(.AnimateInsertion)
|
||||||
|
}
|
||||||
|
self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateOpaqueState: nil, completion: { _ in })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func asyncLayout() -> (_ item: DeleteAccountPeersItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||||
|
return { item, params, neighbors in
|
||||||
|
let contentSize: CGSize
|
||||||
|
var insets: UIEdgeInsets
|
||||||
|
let separatorHeight = UIScreenPixel
|
||||||
|
|
||||||
|
contentSize = CGSize(width: params.width, height: 109.0)
|
||||||
|
insets = itemListNeighborsGroupedInsets(neighbors, params)
|
||||||
|
|
||||||
|
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||||
|
let layoutSize = layout.size
|
||||||
|
|
||||||
|
return (layout, { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.item = item
|
||||||
|
strongSelf.layoutParams = params
|
||||||
|
|
||||||
|
strongSelf.dataPromise.set(.single((item.context, item.peers, item.theme, item.strings)))
|
||||||
|
|
||||||
|
strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
||||||
|
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||||
|
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||||
|
|
||||||
|
if strongSelf.backgroundNode.supernode == nil {
|
||||||
|
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
||||||
|
}
|
||||||
|
if strongSelf.topStripeNode.supernode == nil {
|
||||||
|
strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1)
|
||||||
|
}
|
||||||
|
if strongSelf.bottomStripeNode.supernode == nil {
|
||||||
|
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
|
||||||
|
}
|
||||||
|
if strongSelf.maskNode.supernode == nil {
|
||||||
|
strongSelf.addSubnode(strongSelf.maskNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||||
|
var hasTopCorners = false
|
||||||
|
var hasBottomCorners = false
|
||||||
|
switch neighbors.top {
|
||||||
|
case .sameSection(false):
|
||||||
|
strongSelf.topStripeNode.isHidden = true
|
||||||
|
default:
|
||||||
|
hasTopCorners = true
|
||||||
|
strongSelf.topStripeNode.isHidden = hasCorners
|
||||||
|
}
|
||||||
|
let bottomStripeInset: CGFloat
|
||||||
|
let bottomStripeOffset: CGFloat
|
||||||
|
switch neighbors.bottom {
|
||||||
|
case .sameSection(false):
|
||||||
|
bottomStripeInset = params.leftInset + 16.0
|
||||||
|
bottomStripeOffset = -separatorHeight
|
||||||
|
strongSelf.bottomStripeNode.isHidden = false
|
||||||
|
default:
|
||||||
|
bottomStripeInset = 0.0
|
||||||
|
bottomStripeOffset = 0.0
|
||||||
|
hasBottomCorners = true
|
||||||
|
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||||
|
|
||||||
|
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||||
|
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||||
|
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||||
|
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||||
|
|
||||||
|
let listInsets = UIEdgeInsets(top: params.leftInset, left: 0.0, bottom: params.rightInset, right: 0.0)
|
||||||
|
strongSelf.listView.bounds = CGRect(x: 0.0, y: 0.0, width: 92.0, height: params.width)
|
||||||
|
strongSelf.listView.position = CGPoint(x: params.width / 2.0, y: contentSize.height / 2.0)
|
||||||
|
strongSelf.listView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: CGSize(width: 92.0, height: params.width), insets: listInsets, duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||||
|
|
||||||
|
strongSelf.dequeueTransitions()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
|
||||||
|
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||||
|
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||||
|
}
|
||||||
|
}
|
413
submodules/SettingsUI/Sources/DeleteAccountPhoneItem.swift
Normal file
413
submodules/SettingsUI/Sources/DeleteAccountPhoneItem.swift
Normal file
@ -0,0 +1,413 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import SwiftSignalKit
|
||||||
|
import TelegramCore
|
||||||
|
import TelegramPresentationData
|
||||||
|
import TelegramUIPreferences
|
||||||
|
import ItemListUI
|
||||||
|
import PresentationDataUtils
|
||||||
|
import PhoneInputNode
|
||||||
|
import CountrySelectionUI
|
||||||
|
import CoreTelephony
|
||||||
|
|
||||||
|
private func generateCountryButtonBackground(color: UIColor, strokeColor: UIColor) -> UIImage? {
|
||||||
|
return generateImage(CGSize(width: 56, height: 44.0 + 6.0), rotatedContext: { size, context in
|
||||||
|
let arrowSize: CGFloat = 6.0
|
||||||
|
let lineWidth = UIScreenPixel
|
||||||
|
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
context.setFillColor(color.cgColor)
|
||||||
|
context.fill(CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height - arrowSize)))
|
||||||
|
context.move(to: CGPoint(x: size.width, y: size.height - arrowSize))
|
||||||
|
context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - arrowSize))
|
||||||
|
context.addLine(to: CGPoint(x: size.width - 1.0 - arrowSize, y: size.height))
|
||||||
|
context.addLine(to: CGPoint(x: size.width - 1.0 - arrowSize - arrowSize, y: size.height - arrowSize))
|
||||||
|
context.closePath()
|
||||||
|
context.fillPath()
|
||||||
|
|
||||||
|
context.setStrokeColor(strokeColor.cgColor)
|
||||||
|
context.setLineWidth(lineWidth)
|
||||||
|
|
||||||
|
context.move(to: CGPoint(x: size.width, y: size.height - arrowSize - lineWidth / 2.0))
|
||||||
|
context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - arrowSize - lineWidth / 2.0))
|
||||||
|
context.addLine(to: CGPoint(x: size.width - 1.0 - arrowSize, y: size.height - lineWidth / 2.0))
|
||||||
|
context.addLine(to: CGPoint(x: size.width - 1.0 - arrowSize - arrowSize, y: size.height - arrowSize - lineWidth / 2.0))
|
||||||
|
context.addLine(to: CGPoint(x: 15.0, y: size.height - arrowSize - lineWidth / 2.0))
|
||||||
|
context.strokePath()
|
||||||
|
|
||||||
|
context.move(to: CGPoint(x: 0.0, y: lineWidth / 2.0))
|
||||||
|
context.addLine(to: CGPoint(x: size.width, y: lineWidth / 2.0))
|
||||||
|
context.strokePath()
|
||||||
|
})?.stretchableImage(withLeftCapWidth: 55, topCapHeight: 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func generateCountryButtonHighlightedBackground(color: UIColor) -> UIImage? {
|
||||||
|
return generateImage(CGSize(width: 56.0, height: 44.0 + 6.0), rotatedContext: { size, context in
|
||||||
|
let arrowSize: CGFloat = 6.0
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
context.setFillColor(color.cgColor)
|
||||||
|
context.fill(CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height - arrowSize)))
|
||||||
|
context.move(to: CGPoint(x: size.width, y: size.height - arrowSize))
|
||||||
|
context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - arrowSize))
|
||||||
|
context.addLine(to: CGPoint(x: size.width - 1.0 - arrowSize, y: size.height))
|
||||||
|
context.addLine(to: CGPoint(x: size.width - 1.0 - arrowSize - arrowSize, y: size.height - arrowSize))
|
||||||
|
context.closePath()
|
||||||
|
context.fillPath()
|
||||||
|
})?.stretchableImage(withLeftCapWidth: 55, topCapHeight: 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func generatePhoneInputBackground(color: UIColor, strokeColor: UIColor) -> UIImage? {
|
||||||
|
return generateImage(CGSize(width: 82.0, height: 44.0), rotatedContext: { size, context in
|
||||||
|
let lineWidth = UIScreenPixel
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
context.setFillColor(color.cgColor)
|
||||||
|
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||||
|
context.setStrokeColor(strokeColor.cgColor)
|
||||||
|
context.setLineWidth(lineWidth)
|
||||||
|
context.move(to: CGPoint(x: 0.0, y: size.height - lineWidth / 2.0))
|
||||||
|
context.addLine(to: CGPoint(x: size.width, y: size.height - lineWidth / 2.0))
|
||||||
|
context.strokePath()
|
||||||
|
context.move(to: CGPoint(x: size.width - 2.0 + lineWidth / 2.0, y: size.height - lineWidth / 2.0))
|
||||||
|
context.addLine(to: CGPoint(x: size.width - 2.0 + lineWidth / 2.0, y: 0.0))
|
||||||
|
context.strokePath()
|
||||||
|
})?.stretchableImage(withLeftCapWidth: 81, topCapHeight: 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteAccountPhoneItem: ListViewItem, ItemListItem {
|
||||||
|
let theme: PresentationTheme
|
||||||
|
let strings: PresentationStrings
|
||||||
|
let value: (Int32?, String?, String)
|
||||||
|
let sectionId: ItemListSectionId
|
||||||
|
let selectCountryCode: () -> Void
|
||||||
|
let updated: (Int) -> Void
|
||||||
|
|
||||||
|
init(theme: PresentationTheme, strings: PresentationStrings, value: (Int32?, String?, String), sectionId: ItemListSectionId, selectCountryCode: @escaping () -> Void, updated: @escaping (Int) -> Void) {
|
||||||
|
self.theme = theme
|
||||||
|
self.strings = strings
|
||||||
|
self.value = value
|
||||||
|
self.sectionId = sectionId
|
||||||
|
self.selectCountryCode = selectCountryCode
|
||||||
|
self.updated = updated
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||||
|
async {
|
||||||
|
let node = DeleteAccountPhoneItemNode()
|
||||||
|
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||||
|
|
||||||
|
node.contentSize = layout.contentSize
|
||||||
|
node.insets = layout.insets
|
||||||
|
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
completion(node, {
|
||||||
|
return (nil, { _ in apply() })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
if let nodeValue = node() as? DeleteAccountPhoneItemNode {
|
||||||
|
let makeLayout = nodeValue.asyncLayout()
|
||||||
|
|
||||||
|
async {
|
||||||
|
let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
completion(layout, { _ in
|
||||||
|
apply()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeleteAccountPhoneItemNode: ListViewItemNode, ItemListItemNode {
|
||||||
|
private let backgroundNode: ASDisplayNode
|
||||||
|
private let topStripeNode: ASDisplayNode
|
||||||
|
private let bottomStripeNode: ASDisplayNode
|
||||||
|
private let maskNode: ASImageNode
|
||||||
|
|
||||||
|
private let countryButton: ASButtonNode
|
||||||
|
private let phoneBackground: ASImageNode
|
||||||
|
private let phoneInputNode: PhoneInputNode
|
||||||
|
|
||||||
|
private var item: DeleteAccountPhoneItem?
|
||||||
|
private var layoutParams: ListViewItemLayoutParams?
|
||||||
|
|
||||||
|
var preferredCountryIdForCode: [String: String] = [:]
|
||||||
|
|
||||||
|
var tag: ItemListItemTag? {
|
||||||
|
return self.item?.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
self.backgroundNode = ASDisplayNode()
|
||||||
|
self.backgroundNode.isLayerBacked = true
|
||||||
|
|
||||||
|
self.topStripeNode = ASDisplayNode()
|
||||||
|
self.topStripeNode.isLayerBacked = true
|
||||||
|
|
||||||
|
self.bottomStripeNode = ASDisplayNode()
|
||||||
|
self.bottomStripeNode.isLayerBacked = true
|
||||||
|
|
||||||
|
self.maskNode = ASImageNode()
|
||||||
|
self.maskNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.countryButton = ASButtonNode()
|
||||||
|
|
||||||
|
self.phoneBackground = ASImageNode()
|
||||||
|
self.phoneBackground.displaysAsynchronously = false
|
||||||
|
self.phoneBackground.displayWithoutProcessing = true
|
||||||
|
self.phoneBackground.isLayerBacked = true
|
||||||
|
|
||||||
|
self.phoneInputNode = PhoneInputNode(fontSize: 17.0)
|
||||||
|
|
||||||
|
super.init(layerBacked: false, dynamicBounce: false)
|
||||||
|
|
||||||
|
self.addSubnode(self.phoneBackground)
|
||||||
|
self.addSubnode(self.countryButton)
|
||||||
|
self.addSubnode(self.phoneInputNode)
|
||||||
|
|
||||||
|
self.countryButton.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 15.0, bottom: 4.0, right: 0.0)
|
||||||
|
self.countryButton.contentHorizontalAlignment = .left
|
||||||
|
|
||||||
|
self.countryButton.addTarget(self, action: #selector(self.countryPressed), forControlEvents: .touchUpInside)
|
||||||
|
|
||||||
|
let processNumberChange: (String) -> Bool = { [weak self] number in
|
||||||
|
guard let strongSelf = self, let item = strongSelf.item else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if let (country, _) = AuthorizationSequenceCountrySelectionController.lookupCountryIdByNumber(number, preferredCountries: strongSelf.preferredCountryIdForCode) {
|
||||||
|
let flagString = emojiFlagForISOCountryCode(country.id)
|
||||||
|
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(country.id, strings: item.strings) ?? country.name
|
||||||
|
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(17.0), with: item.theme.list.itemPrimaryTextColor, for: [])
|
||||||
|
|
||||||
|
let maskFont = Font.with(size: 20.0, design: .regular, traits: [.monospacedNumbers])
|
||||||
|
if let mask = AuthorizationSequenceCountrySelectionController.lookupPatternByNumber(number, preferredCountries: strongSelf.preferredCountryIdForCode).flatMap({ NSAttributedString(string: $0, font: maskFont, textColor: item.theme.list.itemPlaceholderTextColor) }) {
|
||||||
|
strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = nil
|
||||||
|
strongSelf.phoneInputNode.mask = mask
|
||||||
|
} else {
|
||||||
|
strongSelf.phoneInputNode.mask = nil
|
||||||
|
strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: item.strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: item.theme.list.itemPlaceholderTextColor)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.phoneInputNode.numberTextUpdated = { [weak self] number in
|
||||||
|
if let strongSelf = self {
|
||||||
|
let _ = processNumberChange(strongSelf.phoneInputNode.number)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.phoneInputNode.countryCodeUpdated = { [weak self] code, name in
|
||||||
|
if let strongSelf = self, let item = strongSelf.item {
|
||||||
|
if let name = name {
|
||||||
|
strongSelf.preferredCountryIdForCode[code] = name
|
||||||
|
}
|
||||||
|
|
||||||
|
if processNumberChange(strongSelf.phoneInputNode.number) {
|
||||||
|
} else if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] {
|
||||||
|
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(name, strings: item.strings) ?? countryName
|
||||||
|
strongSelf.countryButton.setTitle(localizedName, with: Font.regular(17.0), with: item.theme.list.itemPrimaryTextColor, for: [])
|
||||||
|
} else if let code = Int(code), let (_, countryName) = countryCodeToIdAndName[code] {
|
||||||
|
strongSelf.countryButton.setTitle(countryName, with: Font.regular(17.0), with: item.theme.list.itemPrimaryTextColor, for: [])
|
||||||
|
} else {
|
||||||
|
strongSelf.countryButton.setTitle(item.strings.Login_CountryCode, with: Font.regular(17.0), with: item.theme.list.itemPrimaryTextColor, for: [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.phoneInputNode.customFormatter = { number in
|
||||||
|
if let (_, code) = AuthorizationSequenceCountrySelectionController.lookupCountryIdByNumber(number, preferredCountries: [:]) {
|
||||||
|
return code.code
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var countryId: String? = nil
|
||||||
|
let networkInfo = CTTelephonyNetworkInfo()
|
||||||
|
if let carrier = networkInfo.subscriberCellularProvider {
|
||||||
|
countryId = carrier.isoCountryCode
|
||||||
|
}
|
||||||
|
|
||||||
|
if countryId == nil {
|
||||||
|
countryId = (Locale.current as NSLocale).object(forKey: .countryCode) as? String
|
||||||
|
}
|
||||||
|
|
||||||
|
var countryCodeAndId: (Int32, String) = (1, "US")
|
||||||
|
|
||||||
|
if let countryId = countryId {
|
||||||
|
let normalizedId = countryId.uppercased()
|
||||||
|
for (code, idAndName) in countryCodeToIdAndName {
|
||||||
|
if idAndName.0 == normalizedId {
|
||||||
|
countryCodeAndId = (Int32(code), idAndName.0.uppercased())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.phoneInputNode.number = "+\(countryCodeAndId.0)"
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func countryPressed() {
|
||||||
|
if let item = self.item {
|
||||||
|
item.selectCountryCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var phoneNumber: String {
|
||||||
|
return self.phoneInputNode.number
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateCountryCode() {
|
||||||
|
self.phoneInputNode.codeAndNumber = self.phoneInputNode.codeAndNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateCountryCode(code: Int32, name: String) {
|
||||||
|
self.phoneInputNode.codeAndNumber = (code, name, self.phoneInputNode.codeAndNumber.2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func activateInput() {
|
||||||
|
self.phoneInputNode.numberField.textField.becomeFirstResponder()
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateError() {
|
||||||
|
self.phoneInputNode.countryCodeField.layer.addShakeAnimation()
|
||||||
|
self.phoneInputNode.numberField.layer.addShakeAnimation()
|
||||||
|
}
|
||||||
|
|
||||||
|
func asyncLayout() -> (_ item: DeleteAccountPhoneItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||||
|
let currentItem = self.item
|
||||||
|
|
||||||
|
return { item, params, neighbors in
|
||||||
|
var updatedCountryButtonBackground: UIImage?
|
||||||
|
var updatedCountryButtonHighlightedBackground: UIImage?
|
||||||
|
var updatedPhoneBackground: UIImage?
|
||||||
|
|
||||||
|
if currentItem?.theme !== item.theme {
|
||||||
|
updatedCountryButtonBackground = generateCountryButtonBackground(color: item.theme.list.itemBlocksBackgroundColor, strokeColor: item.theme.list.itemBlocksSeparatorColor)
|
||||||
|
updatedCountryButtonHighlightedBackground = generateCountryButtonHighlightedBackground(color: item.theme.list.itemHighlightedBackgroundColor)
|
||||||
|
updatedPhoneBackground = generatePhoneInputBackground(color: item.theme.list.itemBlocksBackgroundColor, strokeColor: item.theme.list.itemBlocksSeparatorColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
let contentSize: CGSize
|
||||||
|
var insets: UIEdgeInsets
|
||||||
|
let separatorHeight = UIScreenPixel
|
||||||
|
|
||||||
|
let countryButtonHeight: CGFloat = 44.0
|
||||||
|
let inputFieldsHeight: CGFloat = 44.0
|
||||||
|
|
||||||
|
contentSize = CGSize(width: params.width, height: countryButtonHeight + inputFieldsHeight)
|
||||||
|
insets = itemListNeighborsGroupedInsets(neighbors, params)
|
||||||
|
|
||||||
|
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||||
|
let layoutSize = layout.size
|
||||||
|
|
||||||
|
return (layout, { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.item = item
|
||||||
|
strongSelf.layoutParams = params
|
||||||
|
|
||||||
|
strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
||||||
|
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||||
|
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||||
|
|
||||||
|
if strongSelf.backgroundNode.supernode == nil {
|
||||||
|
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
||||||
|
}
|
||||||
|
if strongSelf.topStripeNode.supernode == nil {
|
||||||
|
strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1)
|
||||||
|
}
|
||||||
|
if strongSelf.bottomStripeNode.supernode == nil {
|
||||||
|
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
|
||||||
|
}
|
||||||
|
if strongSelf.maskNode.supernode == nil {
|
||||||
|
strongSelf.addSubnode(strongSelf.maskNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||||
|
var hasTopCorners = false
|
||||||
|
var hasBottomCorners = false
|
||||||
|
switch neighbors.top {
|
||||||
|
case .sameSection(false):
|
||||||
|
strongSelf.topStripeNode.isHidden = true
|
||||||
|
default:
|
||||||
|
hasTopCorners = true
|
||||||
|
strongSelf.topStripeNode.isHidden = hasCorners
|
||||||
|
}
|
||||||
|
let bottomStripeInset: CGFloat
|
||||||
|
let bottomStripeOffset: CGFloat
|
||||||
|
switch neighbors.bottom {
|
||||||
|
case .sameSection(false):
|
||||||
|
bottomStripeInset = params.leftInset + 16.0
|
||||||
|
bottomStripeOffset = -separatorHeight
|
||||||
|
strongSelf.bottomStripeNode.isHidden = false
|
||||||
|
default:
|
||||||
|
bottomStripeInset = 0.0
|
||||||
|
bottomStripeOffset = 0.0
|
||||||
|
hasBottomCorners = true
|
||||||
|
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||||
|
|
||||||
|
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||||
|
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||||
|
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||||
|
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||||
|
|
||||||
|
if let updatedCountryButtonBackground = updatedCountryButtonBackground {
|
||||||
|
strongSelf.countryButton.setBackgroundImage(updatedCountryButtonBackground, for: [])
|
||||||
|
}
|
||||||
|
if let updatedCountryButtonHighlightedBackground = updatedCountryButtonHighlightedBackground {
|
||||||
|
strongSelf.countryButton.setBackgroundImage(updatedCountryButtonHighlightedBackground, for: .highlighted)
|
||||||
|
}
|
||||||
|
if let updatedPhoneBackground = updatedPhoneBackground {
|
||||||
|
strongSelf.phoneBackground.image = updatedPhoneBackground
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.phoneInputNode.countryCodeField.textField.textColor = item.theme.list.itemPrimaryTextColor
|
||||||
|
strongSelf.phoneInputNode.countryCodeField.textField.keyboardAppearance = item.theme.rootController.keyboardColor.keyboardAppearance
|
||||||
|
strongSelf.phoneInputNode.countryCodeField.textField.tintColor = item.theme.list.itemAccentColor
|
||||||
|
strongSelf.phoneInputNode.numberField.textField.textColor = item.theme.list.itemPrimaryTextColor
|
||||||
|
strongSelf.phoneInputNode.numberField.textField.keyboardAppearance = item.theme.rootController.keyboardColor.keyboardAppearance
|
||||||
|
strongSelf.phoneInputNode.numberField.textField.tintColor = item.theme.list.itemAccentColor
|
||||||
|
|
||||||
|
strongSelf.countryButton.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: params.leftInset + 15.0, bottom: 4.0, right: 0.0)
|
||||||
|
|
||||||
|
strongSelf.countryButton.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: 44.0 + 6.0))
|
||||||
|
strongSelf.phoneBackground.frame = CGRect(origin: CGPoint(x: 0.0, y: 44.0), size: CGSize(width: params.width, height: 44.0))
|
||||||
|
|
||||||
|
let countryCodeFrame = CGRect(origin: CGPoint(x: 11.0, y: 44.0), size: CGSize(width: 67.0, height: 44.0))
|
||||||
|
let numberFrame = CGRect(origin: CGPoint(x: 92.0, y: 44.0), size: CGSize(width: layout.size.width - 70.0 - 8.0, height: 44.0))
|
||||||
|
let placeholderFrame = numberFrame.offsetBy(dx: 0.0, dy: 8.0)
|
||||||
|
|
||||||
|
let phoneInputFrame = countryCodeFrame.union(numberFrame)
|
||||||
|
|
||||||
|
strongSelf.phoneInputNode.frame = phoneInputFrame
|
||||||
|
strongSelf.phoneInputNode.countryCodeField.frame = countryCodeFrame.offsetBy(dx: -phoneInputFrame.minX, dy: -phoneInputFrame.minY)
|
||||||
|
strongSelf.phoneInputNode.numberField.frame = numberFrame.offsetBy(dx: -phoneInputFrame.minX, dy: -phoneInputFrame.minY)
|
||||||
|
strongSelf.phoneInputNode.placeholderNode.frame = placeholderFrame.offsetBy(dx: -phoneInputFrame.minX, dy: -phoneInputFrame.minY)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
|
||||||
|
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||||
|
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||||
|
}
|
||||||
|
}
|
@ -482,6 +482,10 @@ private func privacyAndSecurityControllerEntries(presentationData: PresentationD
|
|||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PrivacyAndSecurityControllerImpl: ItemListController {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public func privacyAndSecurityController(context: AccountContext, initialSettings: AccountPrivacySettings? = nil, updatedSettings: ((AccountPrivacySettings?) -> Void)? = nil, updatedBlockedPeers: ((BlockedPeersContext?) -> Void)? = nil, updatedHasTwoStepAuth: ((Bool) -> Void)? = nil, focusOnItemTag: PrivacyAndSecurityEntryTag? = nil, activeSessionsContext: ActiveSessionsContext? = nil, webSessionsContext: WebSessionsContext? = nil, blockedPeersContext: BlockedPeersContext? = nil, hasTwoStepAuth: Bool? = nil) -> ViewController {
|
public func privacyAndSecurityController(context: AccountContext, initialSettings: AccountPrivacySettings? = nil, updatedSettings: ((AccountPrivacySettings?) -> Void)? = nil, updatedBlockedPeers: ((BlockedPeersContext?) -> Void)? = nil, updatedHasTwoStepAuth: ((Bool) -> Void)? = nil, focusOnItemTag: PrivacyAndSecurityEntryTag? = nil, activeSessionsContext: ActiveSessionsContext? = nil, webSessionsContext: WebSessionsContext? = nil, blockedPeersContext: BlockedPeersContext? = nil, hasTwoStepAuth: Bool? = nil) -> ViewController {
|
||||||
let statePromise = ValuePromise(PrivacyAndSecurityControllerState(), ignoreRepeated: true)
|
let statePromise = ValuePromise(PrivacyAndSecurityControllerState(), ignoreRepeated: true)
|
||||||
let stateValue = Atomic(value: PrivacyAndSecurityControllerState())
|
let stateValue = Atomic(value: PrivacyAndSecurityControllerState())
|
||||||
@ -492,6 +496,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|
|||||||
var pushControllerImpl: ((ViewController, Bool) -> Void)?
|
var pushControllerImpl: ((ViewController, Bool) -> Void)?
|
||||||
var replaceTopControllerImpl: ((ViewController) -> Void)?
|
var replaceTopControllerImpl: ((ViewController) -> Void)?
|
||||||
var presentControllerImpl: ((ViewController) -> Void)?
|
var presentControllerImpl: ((ViewController) -> Void)?
|
||||||
|
var getNavigationControllerImpl: (() -> NavigationController?)?
|
||||||
|
|
||||||
let actionsDisposable = DisposableSet()
|
let actionsDisposable = DisposableSet()
|
||||||
|
|
||||||
@ -822,12 +827,26 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|
|||||||
6 * 30 * 24 * 60 * 60,
|
6 * 30 * 24 * 60 * 60,
|
||||||
365 * 24 * 60 * 60
|
365 * 24 * 60 * 60
|
||||||
]
|
]
|
||||||
let timeoutItems: [ActionSheetItem] = timeoutValues.map { value in
|
var timeoutItems: [ActionSheetItem] = timeoutValues.map { value in
|
||||||
return ActionSheetButtonItem(title: timeIntervalString(strings: presentationData.strings, value: value), action: {
|
return ActionSheetButtonItem(title: timeIntervalString(strings: presentationData.strings, value: value), action: {
|
||||||
dismissAction()
|
dismissAction()
|
||||||
timeoutAction(value)
|
timeoutAction(value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
timeoutItems.append(ActionSheetButtonItem(title: presentationData.strings.PrivacySettings_DeleteAccountNow, color: .destructive, action: {
|
||||||
|
dismissAction()
|
||||||
|
|
||||||
|
guard let navigationController = getNavigationControllerImpl?() else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = (combineLatest(twoStepAuth.get(), twoStepAuthDataValue.get())
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { hasTwoStepAuth, twoStepAuthData in
|
||||||
|
let optionsController = deleteAccountOptionsController(context: context, navigationController: navigationController, hasTwoStepAuth: hasTwoStepAuth ?? false, twoStepAuthData: twoStepAuthData)
|
||||||
|
pushControllerImpl?(optionsController, true)
|
||||||
|
})
|
||||||
|
}))
|
||||||
controller.setItemGroups([
|
controller.setItemGroups([
|
||||||
ActionSheetItemGroup(items: timeoutItems),
|
ActionSheetItemGroup(items: timeoutItems),
|
||||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||||
@ -886,7 +905,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|
|||||||
actionsDisposable.dispose()
|
actionsDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = ItemListController(context: context, state: signal)
|
let controller = PrivacyAndSecurityControllerImpl(context: context, state: signal)
|
||||||
pushControllerImpl = { [weak controller] c, animated in
|
pushControllerImpl = { [weak controller] c, animated in
|
||||||
(controller?.navigationController as? NavigationController)?.pushViewController(c, animated: animated)
|
(controller?.navigationController as? NavigationController)?.pushViewController(c, animated: animated)
|
||||||
}
|
}
|
||||||
@ -896,7 +915,10 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|
|||||||
presentControllerImpl = { [weak controller] c in
|
presentControllerImpl = { [weak controller] c in
|
||||||
controller?.present(c, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
controller?.present(c, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
}
|
}
|
||||||
|
getNavigationControllerImpl = { [weak controller] in
|
||||||
|
return (controller?.navigationController as? NavigationController)
|
||||||
|
}
|
||||||
|
|
||||||
controller.didAppear = { _ in
|
controller.didAppear = { _ in
|
||||||
updateHasTwoStepAuth()
|
updateHasTwoStepAuth()
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,7 @@ private func twoStepVerificationUnlockSettingsControllerEntries(presentationData
|
|||||||
if let pendingEmail = pendingEmail {
|
if let pendingEmail = pendingEmail {
|
||||||
entries.append(.pendingEmailConfirmInfo(presentationData.theme, presentationData.strings.TwoStepAuth_SetupPendingEmail(pendingEmail.email.pattern).string))
|
entries.append(.pendingEmailConfirmInfo(presentationData.theme, presentationData.strings.TwoStepAuth_SetupPendingEmail(pendingEmail.email.pattern).string))
|
||||||
entries.append(.pendingEmailConfirmCode(presentationData.theme, presentationData.strings, presentationData.strings.TwoStepAuth_RecoveryCode, state.emailCode))
|
entries.append(.pendingEmailConfirmCode(presentationData.theme, presentationData.strings, presentationData.strings.TwoStepAuth_RecoveryCode, state.emailCode))
|
||||||
entries.append(.pendingEmailInfo(presentationData.theme, "[" + presentationData.strings.TwoStepAuth_ConfirmationAbort + "]()"))
|
entries.append(.pendingEmailInfo(presentationData.theme, "[" + presentationData.strings.TwoStepAuth_ConfirmationAbort + "]()"))
|
||||||
|
|
||||||
/*entries.append(.pendingEmailInfo(presentationData.theme, presentationData.strings.TwoStepAuth_ConfirmationText + "\n\n\(pendingEmailAndValue.pendingEmail.pattern)\n\n[" + presentationData.strings.TwoStepAuth_ConfirmationAbort + "]()"))*/
|
/*entries.append(.pendingEmailInfo(presentationData.theme, presentationData.strings.TwoStepAuth_ConfirmationText + "\n\n\(pendingEmailAndValue.pendingEmail.pattern)\n\n[" + presentationData.strings.TwoStepAuth_ConfirmationAbort + "]()"))*/
|
||||||
} else {
|
} else {
|
||||||
|
@ -424,7 +424,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
}
|
}
|
||||||
|
|
||||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||||
if premiumConfiguration.isPremiumDisabled {
|
if premiumConfiguration.isPremiumDisabled || context.account.testingEnvironment {
|
||||||
appIcons = appIcons.filter { !$0.isPremium }
|
appIcons = appIcons.filter { !$0.isPremium }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,6 +317,8 @@ class TabBarNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var reduceMotion: Bool = false
|
||||||
|
|
||||||
var selectedIndex: Int? {
|
var selectedIndex: Int? {
|
||||||
didSet {
|
didSet {
|
||||||
if self.selectedIndex != oldValue {
|
if self.selectedIndex != oldValue {
|
||||||
@ -574,12 +576,14 @@ class TabBarNode: ASDisplayNode {
|
|||||||
node.contentWidth = max(contentWidth, imageContentWidth)
|
node.contentWidth = max(contentWidth, imageContentWidth)
|
||||||
node.isSelected = true
|
node.isSelected = true
|
||||||
|
|
||||||
ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut).updateTransformScale(node: node.ringImageNode, scale: 1.0, delay: 0.1)
|
if !self.reduceMotion && item.item.ringSelection {
|
||||||
node.imageNode.layer.animateScale(from: 1.0, to: 0.87, duration: 0.1, removeOnCompletion: false, completion: { [weak node] _ in
|
ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut).updateTransformScale(node: node.ringImageNode, scale: 1.0, delay: 0.1)
|
||||||
node?.imageNode.layer.animateScale(from: 0.87, to: 1.0, duration: 0.14, removeOnCompletion: false, completion: { [weak node] _ in
|
node.imageNode.layer.animateScale(from: 1.0, to: 0.87, duration: 0.1, removeOnCompletion: false, completion: { [weak node] _ in
|
||||||
node?.imageNode.layer.removeAllAnimations()
|
node?.imageNode.layer.animateScale(from: 0.87, to: 1.0, duration: 0.14, removeOnCompletion: false, completion: { [weak node] _ in
|
||||||
|
node?.imageNode.layer.removeAllAnimations()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
} else {
|
} else {
|
||||||
let (textImage, contentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
let (textImage, contentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
|
||||||
|
|
||||||
|
@ -156,11 +156,13 @@ public extension Api.functions.account {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public extension Api.functions.account {
|
public extension Api.functions.account {
|
||||||
static func deleteAccount(reason: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
static func deleteAccount(flags: Int32, reason: String, password: Api.InputCheckPasswordSRP?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(1099779595)
|
buffer.appendInt32(-1564422284)
|
||||||
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeString(reason, buffer: buffer, boxed: false)
|
serializeString(reason, buffer: buffer, boxed: false)
|
||||||
return (FunctionDescription(name: "account.deleteAccount", parameters: [("reason", String(describing: reason))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
if Int(flags) & Int(1 << 0) != 0 {password!.serialize(buffer, true)}
|
||||||
|
return (FunctionDescription(name: "account.deleteAccount", parameters: [("flags", String(describing: flags)), ("reason", String(describing: reason)), ("password", String(describing: password))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||||
let reader = BufferReader(buffer)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.Bool?
|
var result: Api.Bool?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
|
@ -494,7 +494,7 @@ public enum AccountResetError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func performAccountReset(account: UnauthorizedAccount) -> Signal<Void, AccountResetError> {
|
public func performAccountReset(account: UnauthorizedAccount) -> Signal<Void, AccountResetError> {
|
||||||
return account.network.request(Api.functions.account.deleteAccount(reason: ""))
|
return account.network.request(Api.functions.account.deleteAccount(flags: 0, reason: "", password: nil))
|
||||||
|> map { _ -> Int32? in return nil }
|
|> map { _ -> Int32? in return nil }
|
||||||
|> `catch` { error -> Signal<Int32?, AccountResetError> in
|
|> `catch` { error -> Signal<Int32?, AccountResetError> in
|
||||||
if error.errorDescription.hasPrefix("2FA_CONFIRM_WAIT_") {
|
if error.errorDescription.hasPrefix("2FA_CONFIRM_WAIT_") {
|
||||||
|
@ -90,12 +90,41 @@ public extension TelegramEngine {
|
|||||||
return _internal_updateTwoStepVerificationPassword(network: self.account.network, currentPassword: currentPassword, updatedPassword: updatedPassword)
|
return _internal_updateTwoStepVerificationPassword(network: self.account.network, currentPassword: currentPassword, updatedPassword: updatedPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func deleteAccount(reason: String) -> Signal<Never, DeleteAccountError> {
|
public func deleteAccount(reason: String, password: String?) -> Signal<Never, DeleteAccountError> {
|
||||||
return self.account.network.request(Api.functions.account.deleteAccount(reason: reason))
|
let network = self.account.network
|
||||||
|> mapError { _ -> DeleteAccountError in
|
|
||||||
return .generic
|
let passwordSignal: Signal<Api.InputCheckPasswordSRP?, DeleteAccountError>
|
||||||
|
if let password = password {
|
||||||
|
passwordSignal = _internal_twoStepAuthData(network)
|
||||||
|
|> mapError { _ -> DeleteAccountError in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
|> mapToSignal { authData -> Signal<Api.InputCheckPasswordSRP?, DeleteAccountError> in
|
||||||
|
if let currentPasswordDerivation = authData.currentPasswordDerivation, let srpSessionData = authData.srpSessionData {
|
||||||
|
guard let kdfResult = passwordKDF(encryptionProvider: network.encryptionProvider, password: password, derivation: currentPasswordDerivation, srpSessionData: srpSessionData) else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
return .single(.inputCheckPasswordSRP(srpId: kdfResult.id, A: Buffer(data: kdfResult.A), M1: Buffer(data: kdfResult.M1)))
|
||||||
|
} else {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
passwordSignal = .single(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return passwordSignal
|
||||||
|
|> mapToSignal { password -> Signal<Never, DeleteAccountError> in
|
||||||
|
var flags: Int32 = 0
|
||||||
|
if let _ = password {
|
||||||
|
flags |= (1 << 0)
|
||||||
|
}
|
||||||
|
return self.account.network.request(Api.functions.account.deleteAccount(flags: flags, reason: reason, password: password))
|
||||||
|
|> mapError { _ -> DeleteAccountError in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
|> ignoreValues
|
||||||
}
|
}
|
||||||
|> ignoreValues
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateTwoStepVerificationEmail(currentPassword: String, updatedEmail: String) -> Signal<UpdateTwoStepVerificationPasswordResult, UpdateTwoStepVerificationPasswordError> {
|
public func updateTwoStepVerificationEmail(currentPassword: String, updatedEmail: String) -> Signal<UpdateTwoStepVerificationPasswordResult, UpdateTwoStepVerificationPasswordError> {
|
||||||
|
@ -90,5 +90,11 @@ public struct PresentationResourcesSettings {
|
|||||||
public static let clearCache = renderIcon(name: "Settings/Menu/ClearCache")
|
public static let clearCache = renderIcon(name: "Settings/Menu/ClearCache")
|
||||||
public static let changePhoneNumber = renderIcon(name: "Settings/Menu/ChangePhoneNumber")
|
public static let changePhoneNumber = renderIcon(name: "Settings/Menu/ChangePhoneNumber")
|
||||||
|
|
||||||
|
public static let deleteAddAccount = renderIcon(name: "Settings/Menu/DeleteAddAccount")
|
||||||
|
public static let deleteSetTwoStepAuth = renderIcon(name: "Settings/Menu/DeleteTwoStepAuth")
|
||||||
|
public static let deleteSetPasscode = renderIcon(name: "Settings/Menu/FaceId")
|
||||||
|
public static let deleteChats = renderIcon(name: "Settings/Menu/DeleteChats")
|
||||||
|
public static let clearSynced = renderIcon(name: "Settings/Menu/ClearSynced")
|
||||||
|
|
||||||
public static let websites = renderIcon(name: "Settings/Menu/Websites")
|
public static let websites = renderIcon(name: "Settings/Menu/Websites")
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"filename" : "ic_tb_settings.pdf",
|
||||||
"filename" : "ic_tb_settings.pdf"
|
"idiom" : "universal"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "xcode",
|
||||||
"author" : "xcode"
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Gift.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Gift.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "gift.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
512
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Gift.imageset/gift.pdf
vendored
Normal file
512
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Gift.imageset/gift.pdf
vendored
Normal file
@ -0,0 +1,512 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< /Type /XObject
|
||||||
|
/Length 2 0 R
|
||||||
|
/Group << /Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
>>
|
||||||
|
/Subtype /Form
|
||||||
|
/Resources << >>
|
||||||
|
/BBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 7.992188 7.880981 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
3.800998 1.276080 m
|
||||||
|
1.993796 0.168980 l
|
||||||
|
1.805881 0.053862 1.560225 0.112875 1.445107 0.300790 c
|
||||||
|
1.388883 0.392569 1.372121 0.503168 1.398624 0.607484 c
|
||||||
|
1.678378 1.708605 l
|
||||||
|
1.779364 2.106091 2.051364 2.438358 2.421074 2.615862 c
|
||||||
|
4.392641 3.562446 l
|
||||||
|
4.484557 3.606576 4.523294 3.716863 4.479164 3.808778 c
|
||||||
|
4.443426 3.883215 4.362605 3.924868 4.281243 3.910783 c
|
||||||
|
2.086636 3.530841 l
|
||||||
|
1.640524 3.453608 1.183040 3.576797 0.836031 3.867601 c
|
||||||
|
0.142737 4.448601 l
|
||||||
|
-0.026168 4.590147 -0.048346 4.841818 0.093201 5.010722 c
|
||||||
|
0.162044 5.092872 0.261042 5.143870 0.367897 5.152233 c
|
||||||
|
2.486118 5.318005 l
|
||||||
|
2.635765 5.329716 2.766179 5.424419 2.823627 5.563095 c
|
||||||
|
3.640796 7.535693 l
|
||||||
|
3.725137 7.739288 3.958555 7.835961 4.162150 7.751620 c
|
||||||
|
4.259909 7.711123 4.337579 7.633452 4.378077 7.535693 c
|
||||||
|
5.195247 5.563095 l
|
||||||
|
5.252695 5.424419 5.383109 5.329716 5.532755 5.318005 c
|
||||||
|
7.662615 5.151322 l
|
||||||
|
7.882316 5.134129 8.046480 4.942087 8.029286 4.722386 c
|
||||||
|
8.021015 4.616698 7.971026 4.518645 7.890352 4.449868 c
|
||||||
|
6.265997 3.065068 l
|
||||||
|
6.151649 2.967583 6.101762 2.814124 6.136929 2.668034 c
|
||||||
|
6.636304 0.593517 l
|
||||||
|
6.687879 0.379265 6.556002 0.163769 6.341750 0.112194 c
|
||||||
|
6.238800 0.087412 6.130221 0.104568 6.039927 0.159883 c
|
||||||
|
4.217875 1.276080 l
|
||||||
|
4.089963 1.354439 3.928910 1.354439 3.800998 1.276080 c
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
1398
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
<< /Type /XObject
|
||||||
|
/Length 4 0 R
|
||||||
|
/Group << /Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
>>
|
||||||
|
/Subtype /Form
|
||||||
|
/Resources << >>
|
||||||
|
/BBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 6.000000 5.500000 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
0.000000 6.000000 m
|
||||||
|
0.000000 9.313708 2.686292 12.000000 6.000000 12.000000 c
|
||||||
|
6.000000 12.000000 l
|
||||||
|
9.313708 12.000000 12.000000 9.313708 12.000000 6.000000 c
|
||||||
|
12.000000 6.000000 l
|
||||||
|
12.000000 2.686292 9.313708 0.000000 6.000000 0.000000 c
|
||||||
|
6.000000 0.000000 l
|
||||||
|
2.686292 0.000000 0.000000 2.686292 0.000000 6.000000 c
|
||||||
|
0.000000 6.000000 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
459
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /XObject << /X1 1 0 R >>
|
||||||
|
/ExtGState << /E1 << /SMask << /Type /Mask
|
||||||
|
/G 3 0 R
|
||||||
|
/S /Alpha
|
||||||
|
>>
|
||||||
|
/Type /ExtGState
|
||||||
|
>> >>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Length 7 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 5.000000 2.669434 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
0.665000 9.330566 m
|
||||||
|
0.665000 9.697836 0.367269 9.995566 0.000000 9.995566 c
|
||||||
|
-0.367269 9.995566 -0.665000 9.697836 -0.665000 9.330566 c
|
||||||
|
0.665000 9.330566 l
|
||||||
|
h
|
||||||
|
14.665000 9.330566 m
|
||||||
|
14.665000 9.697836 14.367270 9.995566 14.000000 9.995566 c
|
||||||
|
13.632730 9.995566 13.335000 9.697836 13.335000 9.330566 c
|
||||||
|
14.665000 9.330566 l
|
||||||
|
h
|
||||||
|
1.092019 1.548553 m
|
||||||
|
1.393923 2.141073 l
|
||||||
|
1.092019 1.548553 l
|
||||||
|
h
|
||||||
|
12.907981 1.548553 m
|
||||||
|
12.606077 2.141073 l
|
||||||
|
12.907981 1.548553 l
|
||||||
|
h
|
||||||
|
13.782013 2.422585 m
|
||||||
|
13.189494 2.724489 l
|
||||||
|
13.782013 2.422585 l
|
||||||
|
h
|
||||||
|
10.800000 1.995566 m
|
||||||
|
3.200000 1.995566 l
|
||||||
|
3.200000 0.665566 l
|
||||||
|
10.800000 0.665566 l
|
||||||
|
10.800000 1.995566 l
|
||||||
|
h
|
||||||
|
0.665000 4.530566 m
|
||||||
|
0.665000 9.330566 l
|
||||||
|
-0.665000 9.330566 l
|
||||||
|
-0.665000 4.530566 l
|
||||||
|
0.665000 4.530566 l
|
||||||
|
h
|
||||||
|
13.335000 9.330566 m
|
||||||
|
13.335000 4.530566 l
|
||||||
|
14.665000 4.530566 l
|
||||||
|
14.665000 9.330566 l
|
||||||
|
13.335000 9.330566 l
|
||||||
|
h
|
||||||
|
3.200000 1.995566 m
|
||||||
|
2.628974 1.995566 2.240699 1.996084 1.940556 2.020606 c
|
||||||
|
1.648176 2.044495 1.498463 2.087807 1.393923 2.141073 c
|
||||||
|
0.790115 0.956034 l
|
||||||
|
1.113398 0.791313 1.457623 0.725632 1.832252 0.695024 c
|
||||||
|
2.199117 0.665050 2.650920 0.665566 3.200000 0.665566 c
|
||||||
|
3.200000 1.995566 l
|
||||||
|
h
|
||||||
|
-0.665000 4.530566 m
|
||||||
|
-0.665000 3.981487 -0.665517 3.529683 -0.635543 3.162818 c
|
||||||
|
-0.604935 2.788190 -0.539253 2.443964 -0.374532 2.120682 c
|
||||||
|
0.810506 2.724489 l
|
||||||
|
0.757240 2.829030 0.713928 2.978743 0.690040 3.271122 c
|
||||||
|
0.665517 3.571266 0.665000 3.959541 0.665000 4.530566 c
|
||||||
|
-0.665000 4.530566 l
|
||||||
|
h
|
||||||
|
1.393923 2.141073 m
|
||||||
|
1.142726 2.269063 0.938497 2.473293 0.810506 2.724489 c
|
||||||
|
-0.374532 2.120682 l
|
||||||
|
-0.119030 1.619230 0.288663 1.211536 0.790115 0.956034 c
|
||||||
|
1.393923 2.141073 l
|
||||||
|
h
|
||||||
|
10.800000 0.665566 m
|
||||||
|
11.349079 0.665566 11.800883 0.665050 12.167748 0.695024 c
|
||||||
|
12.542377 0.725632 12.886601 0.791313 13.209885 0.956034 c
|
||||||
|
12.606077 2.141073 l
|
||||||
|
12.501536 2.087807 12.351824 2.044495 12.059443 2.020606 c
|
||||||
|
11.759300 1.996084 11.371026 1.995566 10.800000 1.995566 c
|
||||||
|
10.800000 0.665566 l
|
||||||
|
h
|
||||||
|
13.335000 4.530566 m
|
||||||
|
13.335000 3.959541 13.334483 3.571266 13.309960 3.271122 c
|
||||||
|
13.286072 2.978743 13.242760 2.829030 13.189494 2.724489 c
|
||||||
|
14.374533 2.120682 l
|
||||||
|
14.539253 2.443964 14.604935 2.788189 14.635543 3.162818 c
|
||||||
|
14.665517 3.529683 14.665000 3.981487 14.665000 4.530566 c
|
||||||
|
13.335000 4.530566 l
|
||||||
|
h
|
||||||
|
13.209885 0.956034 m
|
||||||
|
13.711337 1.211536 14.119030 1.619230 14.374533 2.120682 c
|
||||||
|
13.189494 2.724489 l
|
||||||
|
13.061502 2.473293 12.857274 2.269063 12.606077 2.141073 c
|
||||||
|
13.209885 0.956034 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 4.000000 11.169189 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
2.000000 0.665811 m
|
||||||
|
2.367269 0.665811 2.665000 0.963541 2.665000 1.330811 c
|
||||||
|
2.665000 1.698080 2.367269 1.995811 2.000000 1.995811 c
|
||||||
|
2.000000 0.665811 l
|
||||||
|
h
|
||||||
|
14.000000 1.995811 m
|
||||||
|
13.632730 1.995811 13.335000 1.698080 13.335000 1.330811 c
|
||||||
|
13.335000 0.963541 13.632730 0.665811 14.000000 0.665811 c
|
||||||
|
14.000000 1.995811 l
|
||||||
|
h
|
||||||
|
0.115213 2.017745 m
|
||||||
|
0.716366 2.302069 l
|
||||||
|
0.115213 2.017745 l
|
||||||
|
h
|
||||||
|
0.686934 1.446023 m
|
||||||
|
0.402610 0.844871 l
|
||||||
|
0.686934 1.446023 l
|
||||||
|
h
|
||||||
|
15.884788 2.017745 m
|
||||||
|
16.485941 1.733420 l
|
||||||
|
15.884788 2.017745 l
|
||||||
|
h
|
||||||
|
15.313066 1.446023 m
|
||||||
|
15.597390 0.844871 l
|
||||||
|
15.313066 1.446023 l
|
||||||
|
h
|
||||||
|
14.855110 6.138789 m
|
||||||
|
15.139435 6.739942 l
|
||||||
|
14.855110 6.138789 l
|
||||||
|
h
|
||||||
|
15.807979 5.185921 m
|
||||||
|
15.206825 4.901597 l
|
||||||
|
15.807979 5.185921 l
|
||||||
|
h
|
||||||
|
3.125000 5.665811 m
|
||||||
|
12.875000 5.665811 l
|
||||||
|
12.875000 6.995811 l
|
||||||
|
3.125000 6.995811 l
|
||||||
|
3.125000 5.665811 l
|
||||||
|
h
|
||||||
|
1.875000 0.665811 m
|
||||||
|
2.000000 0.665811 l
|
||||||
|
2.000000 1.995811 l
|
||||||
|
1.875000 1.995811 l
|
||||||
|
1.875000 0.665811 l
|
||||||
|
h
|
||||||
|
14.125000 1.995811 m
|
||||||
|
14.000000 1.995811 l
|
||||||
|
14.000000 0.665811 l
|
||||||
|
14.125000 0.665811 l
|
||||||
|
14.125000 1.995811 l
|
||||||
|
h
|
||||||
|
-0.665000 3.205811 m
|
||||||
|
-0.665000 2.901255 -0.665455 2.635354 -0.648653 2.416179 c
|
||||||
|
-0.631310 2.189949 -0.592755 1.959262 -0.485940 1.733420 c
|
||||||
|
0.716366 2.302069 l
|
||||||
|
0.707968 2.319824 0.688916 2.368348 0.677456 2.517840 c
|
||||||
|
0.665455 2.674387 0.665000 2.880721 0.665000 3.205811 c
|
||||||
|
-0.665000 3.205811 l
|
||||||
|
h
|
||||||
|
1.875000 1.995811 m
|
||||||
|
1.549910 1.995811 1.343576 1.996265 1.187029 2.008266 c
|
||||||
|
1.037537 2.019727 0.989013 2.038779 0.971258 2.047176 c
|
||||||
|
0.402610 0.844871 l
|
||||||
|
0.628452 0.738055 0.859138 0.699501 1.085368 0.682158 c
|
||||||
|
1.304543 0.665356 1.570444 0.665811 1.875000 0.665811 c
|
||||||
|
1.875000 1.995811 l
|
||||||
|
h
|
||||||
|
-0.485940 1.733420 m
|
||||||
|
-0.301460 1.343369 0.012559 1.029351 0.402610 0.844871 c
|
||||||
|
0.971258 2.047176 l
|
||||||
|
0.859367 2.100097 0.769286 2.190177 0.716366 2.302069 c
|
||||||
|
-0.485940 1.733420 l
|
||||||
|
h
|
||||||
|
15.335000 3.205811 m
|
||||||
|
15.335000 2.880721 15.334545 2.674387 15.322544 2.517839 c
|
||||||
|
15.311084 2.368348 15.292032 2.319824 15.283634 2.302069 c
|
||||||
|
16.485941 1.733420 l
|
||||||
|
16.592754 1.959262 16.631310 2.189949 16.648653 2.416179 c
|
||||||
|
16.665455 2.635354 16.665001 2.901255 16.665001 3.205811 c
|
||||||
|
15.335000 3.205811 l
|
||||||
|
h
|
||||||
|
14.125000 0.665811 m
|
||||||
|
14.429556 0.665811 14.695457 0.665356 14.914632 0.682158 c
|
||||||
|
15.140862 0.699501 15.371549 0.738055 15.597390 0.844871 c
|
||||||
|
15.028742 2.047176 l
|
||||||
|
15.010986 2.038779 14.962463 2.019727 14.812971 2.008266 c
|
||||||
|
14.656424 1.996265 14.450090 1.995811 14.125000 1.995811 c
|
||||||
|
14.125000 0.665811 l
|
||||||
|
h
|
||||||
|
15.283634 2.302069 m
|
||||||
|
15.230714 2.190177 15.140634 2.100097 15.028742 2.047176 c
|
||||||
|
15.597390 0.844871 l
|
||||||
|
15.987441 1.029351 16.301460 1.343369 16.485941 1.733420 c
|
||||||
|
15.283634 2.302069 l
|
||||||
|
h
|
||||||
|
12.875000 5.665811 m
|
||||||
|
13.409972 5.665811 13.773717 5.665356 14.055506 5.643754 c
|
||||||
|
14.330238 5.622692 14.471831 5.584438 14.570786 5.537636 c
|
||||||
|
15.139435 6.739942 l
|
||||||
|
14.832394 6.885161 14.508637 6.942918 14.157166 6.969862 c
|
||||||
|
13.812751 6.996265 13.389438 6.995811 12.875000 6.995811 c
|
||||||
|
12.875000 5.665811 l
|
||||||
|
h
|
||||||
|
16.665001 3.205811 m
|
||||||
|
16.665001 3.720248 16.665455 4.143561 16.639051 4.487977 c
|
||||||
|
16.612108 4.839448 16.554352 5.163204 16.409132 5.470245 c
|
||||||
|
15.206825 4.901597 l
|
||||||
|
15.253628 4.802642 15.291882 4.661048 15.312943 4.386316 c
|
||||||
|
15.334545 4.104527 15.335000 3.740782 15.335000 3.205811 c
|
||||||
|
16.665001 3.205811 l
|
||||||
|
h
|
||||||
|
14.570786 5.537636 m
|
||||||
|
14.849992 5.405582 15.074771 5.180802 15.206825 4.901597 c
|
||||||
|
16.409132 5.470245 l
|
||||||
|
16.145517 6.027610 15.696799 6.476328 15.139435 6.739942 c
|
||||||
|
14.570786 5.537636 l
|
||||||
|
h
|
||||||
|
3.125000 6.995811 m
|
||||||
|
2.610562 6.995811 2.187250 6.996265 1.842834 6.969862 c
|
||||||
|
1.491363 6.942918 1.167606 6.885161 0.860566 6.739942 c
|
||||||
|
1.429214 5.537636 l
|
||||||
|
1.528168 5.584438 1.669762 5.622692 1.944495 5.643754 c
|
||||||
|
2.226283 5.665356 2.590028 5.665811 3.125000 5.665811 c
|
||||||
|
3.125000 6.995811 l
|
||||||
|
h
|
||||||
|
0.665000 3.205811 m
|
||||||
|
0.665000 3.740783 0.665455 4.104527 0.687057 4.386316 c
|
||||||
|
0.708118 4.661048 0.746372 4.802642 0.793174 4.901597 c
|
||||||
|
-0.409131 5.470245 l
|
||||||
|
-0.554351 5.163204 -0.612108 4.839448 -0.639052 4.487977 c
|
||||||
|
-0.665455 4.143561 -0.665000 3.720249 -0.665000 3.205811 c
|
||||||
|
0.665000 3.205811 l
|
||||||
|
h
|
||||||
|
0.860566 6.739942 m
|
||||||
|
0.303200 6.476328 -0.145517 6.027610 -0.409131 5.470245 c
|
||||||
|
0.793174 4.901597 l
|
||||||
|
0.925229 5.180802 1.150008 5.405582 1.429214 5.537636 c
|
||||||
|
0.860566 6.739942 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 8.000000 16.169922 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
4.000000 1.330078 m
|
||||||
|
4.000000 0.665078 l
|
||||||
|
4.665000 0.665078 l
|
||||||
|
4.665000 1.330078 l
|
||||||
|
4.000000 1.330078 l
|
||||||
|
h
|
||||||
|
3.335000 3.330078 m
|
||||||
|
3.335000 1.330078 l
|
||||||
|
4.665000 1.330078 l
|
||||||
|
4.665000 3.330078 l
|
||||||
|
3.335000 3.330078 l
|
||||||
|
h
|
||||||
|
4.000000 1.995078 m
|
||||||
|
2.000000 1.995078 l
|
||||||
|
2.000000 0.665078 l
|
||||||
|
4.000000 0.665078 l
|
||||||
|
4.000000 1.995078 l
|
||||||
|
h
|
||||||
|
2.000000 1.995078 m
|
||||||
|
1.262700 1.995078 0.665000 2.592778 0.665000 3.330078 c
|
||||||
|
-0.665000 3.330078 l
|
||||||
|
-0.665000 1.858239 0.528161 0.665078 2.000000 0.665078 c
|
||||||
|
2.000000 1.995078 l
|
||||||
|
h
|
||||||
|
2.000000 4.665078 m
|
||||||
|
2.737300 4.665078 3.335000 4.067378 3.335000 3.330078 c
|
||||||
|
4.665000 3.330078 l
|
||||||
|
4.665000 4.801917 3.471839 5.995078 2.000000 5.995078 c
|
||||||
|
2.000000 4.665078 l
|
||||||
|
h
|
||||||
|
2.000000 5.995078 m
|
||||||
|
0.528161 5.995078 -0.665000 4.801917 -0.665000 3.330078 c
|
||||||
|
0.665000 3.330078 l
|
||||||
|
0.665000 4.067378 1.262700 4.665078 2.000000 4.665078 c
|
||||||
|
2.000000 5.995078 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 12.000000 16.169922 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
0.000000 1.330078 m
|
||||||
|
-0.665000 1.330078 l
|
||||||
|
-0.665000 0.665078 l
|
||||||
|
0.000000 0.665078 l
|
||||||
|
0.000000 1.330078 l
|
||||||
|
h
|
||||||
|
2.000000 1.995078 m
|
||||||
|
0.000000 1.995078 l
|
||||||
|
0.000000 0.665078 l
|
||||||
|
2.000000 0.665078 l
|
||||||
|
2.000000 1.995078 l
|
||||||
|
h
|
||||||
|
0.665000 1.330078 m
|
||||||
|
0.665000 3.330078 l
|
||||||
|
-0.665000 3.330078 l
|
||||||
|
-0.665000 1.330078 l
|
||||||
|
0.665000 1.330078 l
|
||||||
|
h
|
||||||
|
3.335000 3.330078 m
|
||||||
|
3.335000 2.592778 2.737300 1.995078 2.000000 1.995078 c
|
||||||
|
2.000000 0.665078 l
|
||||||
|
3.471839 0.665078 4.665000 1.858239 4.665000 3.330078 c
|
||||||
|
3.335000 3.330078 l
|
||||||
|
h
|
||||||
|
2.000000 4.665078 m
|
||||||
|
2.737300 4.665078 3.335000 4.067378 3.335000 3.330078 c
|
||||||
|
4.665000 3.330078 l
|
||||||
|
4.665000 4.801917 3.471839 5.995078 2.000000 5.995078 c
|
||||||
|
2.000000 4.665078 l
|
||||||
|
h
|
||||||
|
2.000000 5.995078 m
|
||||||
|
0.528161 5.995078 -0.665000 4.801917 -0.665000 3.330078 c
|
||||||
|
0.665000 3.330078 l
|
||||||
|
0.665000 4.067378 1.262700 4.665078 2.000000 4.665078 c
|
||||||
|
2.000000 5.995078 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 12.000000 3.169922 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
0.665000 3.330078 m
|
||||||
|
0.665000 3.697347 0.367269 3.995078 0.000000 3.995078 c
|
||||||
|
-0.367269 3.995078 -0.665000 3.697347 -0.665000 3.330078 c
|
||||||
|
0.665000 3.330078 l
|
||||||
|
h
|
||||||
|
-0.665000 1.330078 m
|
||||||
|
-0.665000 0.962809 -0.367269 0.665078 0.000000 0.665078 c
|
||||||
|
0.367269 0.665078 0.665000 0.962809 0.665000 1.330078 c
|
||||||
|
-0.665000 1.330078 l
|
||||||
|
h
|
||||||
|
-0.665000 3.330078 m
|
||||||
|
-0.665000 1.330078 l
|
||||||
|
0.665000 1.330078 l
|
||||||
|
0.665000 3.330078 l
|
||||||
|
-0.665000 3.330078 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
/E1 gs
|
||||||
|
/X1 Do
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
9085
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||||
|
/Resources 5 0 R
|
||||||
|
/Contents 6 0 R
|
||||||
|
/Parent 9 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
<< /Kids [ 8 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<< /Pages 9 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 11
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000001656 00000 n
|
||||||
|
0000001679 00000 n
|
||||||
|
0000002386 00000 n
|
||||||
|
0000002408 00000 n
|
||||||
|
0000002706 00000 n
|
||||||
|
0000011847 00000 n
|
||||||
|
0000011870 00000 n
|
||||||
|
0000012043 00000 n
|
||||||
|
0000012117 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 10 0 R
|
||||||
|
/Size 11
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
12177
|
||||||
|
%%EOF
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/ClearSynced.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/ClearSynced.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "clearsynced.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
136
submodules/TelegramUI/Images.xcassets/Settings/Menu/ClearSynced.imageset/clearsynced.pdf
vendored
Normal file
136
submodules/TelegramUI/Images.xcassets/Settings/Menu/ClearSynced.imageset/clearsynced.pdf
vendored
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
1.000000 0.584314 0.000000 scn
|
||||||
|
0.000000 18.799999 m
|
||||||
|
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
|
||||||
|
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
|
||||||
|
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
|
||||||
|
18.799999 30.000000 l
|
||||||
|
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
|
||||||
|
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
|
||||||
|
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
|
||||||
|
30.000000 11.200001 l
|
||||||
|
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
|
||||||
|
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
|
||||||
|
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
|
||||||
|
11.200000 0.000000 l
|
||||||
|
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
|
||||||
|
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
|
||||||
|
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
|
||||||
|
0.000000 18.799999 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 2.652100 8.000000 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
15.347914 11.500000 m
|
||||||
|
15.347914 9.843145 14.004768 8.500000 12.347914 8.500000 c
|
||||||
|
10.691060 8.500000 9.347914 9.843145 9.347914 11.500000 c
|
||||||
|
9.347914 13.156855 10.691060 14.500000 12.347914 14.500000 c
|
||||||
|
14.004768 14.500000 15.347914 13.156855 15.347914 11.500000 c
|
||||||
|
h
|
||||||
|
16.206648 5.096860 m
|
||||||
|
17.131365 4.487454 17.732334 3.680024 18.122902 2.887923 c
|
||||||
|
18.464052 2.196046 18.353537 1.522406 17.970762 1.000000 c
|
||||||
|
17.531406 0.400373 16.733355 -0.000002 15.847914 -0.000002 c
|
||||||
|
8.847914 -0.000002 l
|
||||||
|
7.962472 -0.000002 7.164421 0.400373 6.725066 1.000000 c
|
||||||
|
6.701973 1.031517 6.679871 1.063584 6.658800 1.096172 c
|
||||||
|
6.330600 1.603753 6.252357 2.237787 6.572926 2.887924 c
|
||||||
|
6.987937 3.729597 7.640509 4.588578 8.666627 5.208897 c
|
||||||
|
9.571874 5.756145 10.767847 6.117645 12.347914 6.117645 c
|
||||||
|
13.927979 6.117645 15.123951 5.756145 16.029198 5.208899 c
|
||||||
|
16.089634 5.172363 16.148775 5.134999 16.206648 5.096860 c
|
||||||
|
h
|
||||||
|
5.347910 6.000000 m
|
||||||
|
6.038868 6.000000 6.650654 5.923974 7.192287 5.790671 c
|
||||||
|
6.346507 5.094717 5.770174 4.267296 5.380054 3.476105 c
|
||||||
|
4.957832 2.619810 4.950275 1.751801 5.228493 1.000000 c
|
||||||
|
2.054037 1.000000 l
|
||||||
|
0.595407 1.000000 -0.530585 2.298071 0.261390 3.522970 c
|
||||||
|
1.064612 4.765267 2.563349 6.000000 5.347910 6.000000 c
|
||||||
|
h
|
||||||
|
19.315775 3.476104 m
|
||||||
|
19.737995 2.619809 19.745552 1.751801 19.467335 1.000000 c
|
||||||
|
22.641785 1.000000 l
|
||||||
|
24.100414 1.000000 25.226404 2.298071 24.434431 3.522971 c
|
||||||
|
23.631208 4.765267 22.132471 6.000000 19.347910 6.000000 c
|
||||||
|
18.656954 6.000000 18.045172 5.923975 17.503540 5.790672 c
|
||||||
|
18.349321 5.094717 18.925655 4.267296 19.315775 3.476104 c
|
||||||
|
h
|
||||||
|
7.847914 10.500000 m
|
||||||
|
7.847914 9.119288 6.728626 8.000000 5.347914 8.000000 c
|
||||||
|
3.967202 8.000000 2.847914 9.119288 2.847914 10.500000 c
|
||||||
|
2.847914 11.880713 3.967202 13.000000 5.347914 13.000000 c
|
||||||
|
6.728626 13.000000 7.847914 11.880713 7.847914 10.500000 c
|
||||||
|
h
|
||||||
|
21.847914 10.500000 m
|
||||||
|
21.847914 9.119288 20.728626 8.000000 19.347914 8.000000 c
|
||||||
|
17.967201 8.000000 16.847914 9.119288 16.847914 10.500000 c
|
||||||
|
16.847914 11.880713 17.967201 13.000000 19.347914 13.000000 c
|
||||||
|
20.728626 13.000000 21.847914 11.880713 21.847914 10.500000 c
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
3113
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Pages 5 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000003203 00000 n
|
||||||
|
0000003226 00000 n
|
||||||
|
0000003399 00000 n
|
||||||
|
0000003473 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
3532
|
||||||
|
%%EOF
|
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteAddAccount.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteAddAccount.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "addaccount.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
102
submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteAddAccount.imageset/addaccount.pdf
vendored
Normal file
102
submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteAddAccount.imageset/addaccount.pdf
vendored
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
1.000000 0.584314 0.000000 scn
|
||||||
|
0.000000 18.799999 m
|
||||||
|
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
|
||||||
|
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
|
||||||
|
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
|
||||||
|
18.799999 30.000000 l
|
||||||
|
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
|
||||||
|
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
|
||||||
|
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
|
||||||
|
30.000000 11.200001 l
|
||||||
|
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
|
||||||
|
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
|
||||||
|
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
|
||||||
|
11.200000 0.000000 l
|
||||||
|
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
|
||||||
|
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
|
||||||
|
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
|
||||||
|
0.000000 18.799999 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 6.730286 6.000000 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
8.269731 10.500000 m
|
||||||
|
10.478869 10.500000 12.269731 12.290861 12.269731 14.500000 c
|
||||||
|
12.269731 16.709139 10.478869 18.500000 8.269731 18.500000 c
|
||||||
|
6.060592 18.500000 4.269731 16.709139 4.269731 14.500000 c
|
||||||
|
4.269731 12.290861 6.060592 10.500000 8.269731 10.500000 c
|
||||||
|
h
|
||||||
|
16.238016 3.829396 m
|
||||||
|
15.145898 5.884616 12.897719 8.000000 8.269734 8.000000 c
|
||||||
|
3.641746 8.000000 1.393565 5.884617 0.301445 3.829397 c
|
||||||
|
-0.735194 1.878584 1.060591 0.000000 3.269730 0.000000 c
|
||||||
|
13.269731 0.000000 l
|
||||||
|
15.478869 0.000000 17.274656 1.878582 16.238016 3.829396 c
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
1580
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Pages 5 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000001670 00000 n
|
||||||
|
0000001693 00000 n
|
||||||
|
0000001866 00000 n
|
||||||
|
0000001940 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
1999
|
||||||
|
%%EOF
|
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteChats.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteChats.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "deletechats.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
175
submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteChats.imageset/deletechats.pdf
vendored
Normal file
175
submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteChats.imageset/deletechats.pdf
vendored
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
1.000000 0.176471 0.333333 scn
|
||||||
|
0.000000 18.799999 m
|
||||||
|
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
|
||||||
|
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
|
||||||
|
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
|
||||||
|
18.799999 30.000000 l
|
||||||
|
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
|
||||||
|
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
|
||||||
|
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
|
||||||
|
30.000000 11.200001 l
|
||||||
|
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
|
||||||
|
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
|
||||||
|
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
|
||||||
|
11.200000 0.000000 l
|
||||||
|
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
|
||||||
|
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
|
||||||
|
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
|
||||||
|
0.000000 18.799999 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 6.834961 5.000000 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
7.038761 21.165039 m
|
||||||
|
7.064992 21.165039 l
|
||||||
|
9.264992 21.165039 l
|
||||||
|
9.291224 21.165039 l
|
||||||
|
9.291246 21.165039 l
|
||||||
|
9.688916 21.165049 10.026937 21.165058 10.304341 21.142393 c
|
||||||
|
10.595594 21.118597 10.878077 21.066540 11.147882 20.929068 c
|
||||||
|
11.555252 20.721500 11.886456 20.390299 12.094021 19.982927 c
|
||||||
|
12.231494 19.713121 12.283551 19.430639 12.307348 19.139387 c
|
||||||
|
12.330013 18.861979 12.330004 18.523952 12.329992 18.126278 c
|
||||||
|
12.329992 18.100037 l
|
||||||
|
12.329992 17.665022 l
|
||||||
|
15.665000 17.665022 l
|
||||||
|
16.032269 17.665022 16.330000 17.367292 16.330000 17.000023 c
|
||||||
|
16.330000 16.632753 16.032269 16.335022 15.665000 16.335022 c
|
||||||
|
0.665000 16.335022 l
|
||||||
|
0.297731 16.335022 0.000000 16.632753 0.000000 17.000023 c
|
||||||
|
0.000000 17.367292 0.297731 17.665022 0.665000 17.665022 c
|
||||||
|
3.999992 17.665022 l
|
||||||
|
3.999992 18.100037 l
|
||||||
|
3.999992 18.126268 l
|
||||||
|
3.999981 18.523949 3.999972 18.861977 4.022637 19.139387 c
|
||||||
|
4.046433 19.430639 4.098491 19.713121 4.235963 19.982927 c
|
||||||
|
4.443529 20.390299 4.774732 20.721500 5.182103 20.929068 c
|
||||||
|
5.451908 21.066540 5.734391 21.118597 6.025643 21.142393 c
|
||||||
|
6.303048 21.165058 6.641069 21.165049 7.038738 21.165039 c
|
||||||
|
7.038761 21.165039 l
|
||||||
|
h
|
||||||
|
10.999992 18.100037 m
|
||||||
|
10.999992 17.665022 l
|
||||||
|
5.329992 17.665022 l
|
||||||
|
5.329992 18.100037 l
|
||||||
|
5.329992 18.531050 5.330510 18.814316 5.348220 19.031082 c
|
||||||
|
5.365296 19.240086 5.394984 19.328056 5.421002 19.379120 c
|
||||||
|
5.501056 19.536236 5.628795 19.663975 5.785910 19.744028 c
|
||||||
|
5.836973 19.770046 5.924943 19.799734 6.133947 19.816811 c
|
||||||
|
6.350715 19.834520 6.633980 19.835037 7.064992 19.835037 c
|
||||||
|
9.264992 19.835037 l
|
||||||
|
9.696005 19.835037 9.979270 19.834520 10.196037 19.816811 c
|
||||||
|
10.405041 19.799734 10.493011 19.770046 10.544075 19.744028 c
|
||||||
|
10.701189 19.663975 10.828928 19.536236 10.908983 19.379120 c
|
||||||
|
10.935000 19.328056 10.964688 19.240086 10.981765 19.031082 c
|
||||||
|
10.999475 18.814316 10.999992 18.531050 10.999992 18.100037 c
|
||||||
|
h
|
||||||
|
1.866287 4.480732 m
|
||||||
|
1.278763 13.293592 l
|
||||||
|
1.239206 13.886940 1.219428 14.183613 1.322701 14.412016 c
|
||||||
|
1.413458 14.612740 1.567953 14.777878 1.762195 14.881785 c
|
||||||
|
1.983222 15.000023 2.280554 15.000023 2.875219 15.000023 c
|
||||||
|
13.454782 15.000023 l
|
||||||
|
14.049447 15.000023 14.346780 15.000023 14.567807 14.881785 c
|
||||||
|
14.762049 14.777878 14.916544 14.612740 15.007301 14.412016 c
|
||||||
|
15.110574 14.183613 15.090796 13.886940 15.051239 13.293592 c
|
||||||
|
14.463715 4.480732 l
|
||||||
|
14.358499 2.902485 14.305890 2.113361 13.965018 1.515018 c
|
||||||
|
13.664913 0.988235 13.212244 0.564739 12.666664 0.300339 c
|
||||||
|
12.046970 0.000023 11.256096 0.000023 9.674346 0.000023 c
|
||||||
|
6.655656 0.000023 l
|
||||||
|
5.073906 0.000023 4.283031 0.000023 3.663338 0.300339 c
|
||||||
|
3.117758 0.564739 2.665089 0.988235 2.364984 1.515018 c
|
||||||
|
2.024112 2.113361 1.971503 2.902485 1.866287 4.480732 c
|
||||||
|
h
|
||||||
|
5.420216 12.030214 m
|
||||||
|
5.403539 12.397105 5.092596 12.681009 4.725706 12.664333 c
|
||||||
|
4.358815 12.647655 4.074911 12.336713 4.091588 11.969823 c
|
||||||
|
4.500678 2.969837 l
|
||||||
|
4.517354 2.602947 4.828298 2.319042 5.195188 2.335720 c
|
||||||
|
5.562078 2.352396 5.845983 2.663338 5.829306 3.030230 c
|
||||||
|
5.420216 12.030214 l
|
||||||
|
h
|
||||||
|
11.604276 12.664333 m
|
||||||
|
11.971167 12.647655 12.255072 12.336713 12.238394 11.969823 c
|
||||||
|
11.829304 2.969837 l
|
||||||
|
11.812627 2.602947 11.501684 2.319042 11.134793 2.335720 c
|
||||||
|
10.767903 2.352396 10.483999 2.663338 10.500675 3.030230 c
|
||||||
|
10.909766 12.030214 l
|
||||||
|
10.926443 12.397105 11.237386 12.681009 11.604276 12.664333 c
|
||||||
|
h
|
||||||
|
8.164989 12.665019 m
|
||||||
|
8.532259 12.665019 8.829989 12.367289 8.829989 12.000019 c
|
||||||
|
8.829989 3.000034 l
|
||||||
|
8.829989 2.632763 8.532259 2.335033 8.164989 2.335033 c
|
||||||
|
7.797720 2.335033 7.499990 2.632763 7.499990 3.000034 c
|
||||||
|
7.499990 12.000019 l
|
||||||
|
7.499990 12.367289 7.797720 12.665019 8.164989 12.665019 c
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
4588
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Pages 5 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000004678 00000 n
|
||||||
|
0000004701 00000 n
|
||||||
|
0000004874 00000 n
|
||||||
|
0000004948 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
5007
|
||||||
|
%%EOF
|
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteTwoStepAuth.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteTwoStepAuth.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Icon.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
114
submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteTwoStepAuth.imageset/Icon.pdf
vendored
Normal file
114
submodules/TelegramUI/Images.xcassets/Settings/Menu/DeleteTwoStepAuth.imageset/Icon.pdf
vendored
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
0.345098 0.337255 0.839216 scn
|
||||||
|
0.000000 18.799999 m
|
||||||
|
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
|
||||||
|
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
|
||||||
|
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
|
||||||
|
18.799999 30.000000 l
|
||||||
|
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
|
||||||
|
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
|
||||||
|
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
|
||||||
|
30.000000 11.200001 l
|
||||||
|
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
|
||||||
|
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
|
||||||
|
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
|
||||||
|
11.200000 0.000000 l
|
||||||
|
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
|
||||||
|
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
|
||||||
|
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
|
||||||
|
0.000000 18.799999 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
-1.000000 -0.000000 -0.000000 1.000000 25.000000 6.000000 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
6.650000 19.000000 m
|
||||||
|
2.945000 19.000000 0.000000 16.055000 0.000000 12.350000 c
|
||||||
|
0.000000 8.645000 2.945000 5.700001 6.650000 5.700001 c
|
||||||
|
7.500165 5.700001 8.310955 5.861581 9.054688 6.145313 c
|
||||||
|
10.450001 4.750000 l
|
||||||
|
12.350000 4.750000 l
|
||||||
|
12.350000 2.850000 l
|
||||||
|
14.250000 2.850000 l
|
||||||
|
14.250000 0.950001 l
|
||||||
|
14.903126 0.296875 l
|
||||||
|
15.093125 0.106874 15.300938 0.000000 15.585938 0.000000 c
|
||||||
|
18.049999 0.000000 l
|
||||||
|
18.619999 0.000000 19.000000 0.380001 19.000000 0.950001 c
|
||||||
|
19.000000 3.414062 l
|
||||||
|
19.000000 3.699062 18.893126 3.906875 18.703125 4.096874 c
|
||||||
|
12.854687 9.945312 l
|
||||||
|
13.138419 10.689045 13.299999 11.499835 13.299999 12.350000 c
|
||||||
|
13.299999 16.055000 10.355000 19.000000 6.650000 19.000000 c
|
||||||
|
h
|
||||||
|
5.225000 16.150000 m
|
||||||
|
6.555000 16.150000 7.600000 15.105000 7.600000 13.775000 c
|
||||||
|
7.600000 12.445000 6.555000 11.400000 5.225000 11.400000 c
|
||||||
|
3.895000 11.400000 2.850000 12.445000 2.850000 13.775000 c
|
||||||
|
2.850000 15.105000 3.895000 16.150000 5.225000 16.150000 c
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
1987
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Pages 5 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000002077 00000 n
|
||||||
|
0000002100 00000 n
|
||||||
|
0000002273 00000 n
|
||||||
|
0000002347 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
2406
|
||||||
|
%%EOF
|
@ -486,7 +486,7 @@ final class AuthorizedApplicationContext {
|
|||||||
}
|
}
|
||||||
let accountId = strongSelf.context.account.id
|
let accountId = strongSelf.context.account.id
|
||||||
let accountManager = strongSelf.context.sharedContext.accountManager
|
let accountManager = strongSelf.context.sharedContext.accountManager
|
||||||
let _ = (strongSelf.context.engine.auth.deleteAccount(reason: "GDPR")
|
let _ = (strongSelf.context.engine.auth.deleteAccount(reason: "GDPR", password: nil)
|
||||||
|> deliverOnMainQueue).start(error: { _ in
|
|> deliverOnMainQueue).start(error: { _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
|
@ -7972,8 +7972,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
|
|||||||
icon = UIImage(bundleImageName: "Chat List/Tabs/IconSettings")
|
icon = UIImage(bundleImageName: "Chat List/Tabs/IconSettings")
|
||||||
}
|
}
|
||||||
|
|
||||||
let tabBarItem: Signal<(String, UIImage?, UIImage?, String?, Bool), NoError> = combineLatest(queue: .mainQueue(), self.context.sharedContext.presentationData, notificationsAuthorizationStatus.get(), notificationsWarningSuppressed.get(), getServerProvidedSuggestions(account: self.context.account), accountTabBarAvatar, accountTabBarAvatarBadge)
|
let tabBarItem: Signal<(String, UIImage?, UIImage?, String?, Bool, Bool), NoError> = combineLatest(queue: .mainQueue(), self.context.sharedContext.presentationData, notificationsAuthorizationStatus.get(), notificationsWarningSuppressed.get(), getServerProvidedSuggestions(account: self.context.account), accountTabBarAvatar, accountTabBarAvatarBadge)
|
||||||
|> map { presentationData, notificationsAuthorizationStatus, notificationsWarningSuppressed, suggestions, accountTabBarAvatar, accountTabBarAvatarBadge -> (String, UIImage?, UIImage?, String?, Bool) in
|
|> map { presentationData, notificationsAuthorizationStatus, notificationsWarningSuppressed, suggestions, accountTabBarAvatar, accountTabBarAvatarBadge -> (String, UIImage?, UIImage?, String?, Bool, Bool) in
|
||||||
let notificationsWarning = shouldDisplayNotificationsPermissionWarning(status: notificationsAuthorizationStatus, suppressed: notificationsWarningSuppressed)
|
let notificationsWarning = shouldDisplayNotificationsPermissionWarning(status: notificationsAuthorizationStatus, suppressed: notificationsWarningSuppressed)
|
||||||
let phoneNumberWarning = suggestions.contains(.validatePhoneNumber)
|
let phoneNumberWarning = suggestions.contains(.validatePhoneNumber)
|
||||||
let passwordWarning = suggestions.contains(.validatePassword)
|
let passwordWarning = suggestions.contains(.validatePassword)
|
||||||
@ -7981,15 +7981,15 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
|
|||||||
if accountTabBarAvatarBadge > 0 {
|
if accountTabBarAvatarBadge > 0 {
|
||||||
otherAccountsBadge = compactNumericCountString(Int(accountTabBarAvatarBadge), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator)
|
otherAccountsBadge = compactNumericCountString(Int(accountTabBarAvatarBadge), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator)
|
||||||
}
|
}
|
||||||
return (presentationData.strings.Settings_Title, accountTabBarAvatar?.0 ?? icon, accountTabBarAvatar?.1 ?? icon, notificationsWarning || phoneNumberWarning || passwordWarning ? "!" : otherAccountsBadge, accountTabBarAvatar != nil || presentationData.reduceMotion)
|
return (presentationData.strings.Settings_Title, accountTabBarAvatar?.0 ?? icon, accountTabBarAvatar?.1 ?? icon, notificationsWarning || phoneNumberWarning || passwordWarning ? "!" : otherAccountsBadge, accountTabBarAvatar != nil, presentationData.reduceMotion)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tabBarItemDisposable = (tabBarItem |> deliverOnMainQueue).start(next: { [weak self] title, image, selectedImage, badgeValue, isAvatar in
|
self.tabBarItemDisposable = (tabBarItem |> deliverOnMainQueue).start(next: { [weak self] title, image, selectedImage, badgeValue, isAvatar, reduceMotion in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.tabBarItem.title = title
|
strongSelf.tabBarItem.title = title
|
||||||
strongSelf.tabBarItem.image = image
|
strongSelf.tabBarItem.image = image
|
||||||
strongSelf.tabBarItem.selectedImage = selectedImage
|
strongSelf.tabBarItem.selectedImage = selectedImage
|
||||||
strongSelf.tabBarItem.animationName = isAvatar ? nil : "TabSettings"
|
strongSelf.tabBarItem.animationName = isAvatar || reduceMotion ? nil : "TabSettings"
|
||||||
strongSelf.tabBarItem.ringSelection = isAvatar
|
strongSelf.tabBarItem.ringSelection = isAvatar
|
||||||
strongSelf.tabBarItem.badgeValue = badgeValue
|
strongSelf.tabBarItem.badgeValue = badgeValue
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user