mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-28 19:05:49 +00:00
Initial QR Auth UI
This commit is contained in:
parent
55b9935a25
commit
9f686dc0cb
@ -9,6 +9,7 @@ static_library(
|
||||
"//submodules/TelegramCore:TelegramCore#shared",
|
||||
"//submodules/SyncCore:SyncCore#shared",
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
|
||||
"//submodules/Display:Display#shared",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
@ -16,6 +17,8 @@ static_library(
|
||||
"//submodules/Camera:Camera",
|
||||
"//submodules/GlassButtonNode:GlassButtonNode",
|
||||
"//submodules/AlertUI:AlertUI",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
||||
@ -0,0 +1,166 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AppBundle
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SolidRoundedButtonNode
|
||||
import SwiftSignalKit
|
||||
import OverlayStatusController
|
||||
import AnimatedStickerNode
|
||||
import TelegramPresentationData
|
||||
import TelegramCore
|
||||
import AccountContext
|
||||
|
||||
final class AuthTransferConfirmationNode: ASDisplayNode {
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
private let tokenInfo: AuthTransferTokenInfo
|
||||
|
||||
private let containerNode: ASDisplayNode
|
||||
private let backgroundNode: ASImageNode
|
||||
private let iconNode: ASImageNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
private let appNameNode: ImmediateTextNode
|
||||
private let locationInfoNode: ImmediateTextNode
|
||||
private let acceptButtonNode: SolidRoundedButtonNode
|
||||
private let cancelButtonNode: SolidRoundedButtonNode
|
||||
|
||||
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||
|
||||
init(context: AccountContext, presentationData: PresentationData, tokenInfo: AuthTransferTokenInfo, accept: @escaping () -> Void, cancel: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.tokenInfo = tokenInfo
|
||||
|
||||
self.containerNode = ASDisplayNode()
|
||||
|
||||
self.backgroundNode = ASImageNode()
|
||||
self.backgroundNode.displayWithoutProcessing = true
|
||||
self.backgroundNode.displaysAsynchronously = false
|
||||
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 24.0, color: self.presentationData.theme.list.plainBackgroundColor)
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
self.iconNode.displayWithoutProcessing = true
|
||||
self.iconNode.displaysAsynchronously = false
|
||||
self.iconNode.image = UIImage(bundleImageName: "Settings/TransferAuthLaptop")
|
||||
|
||||
self.titleNode = ImmediateTextNode()
|
||||
self.titleNode.textAlignment = .center
|
||||
self.titleNode.maximumNumberOfLines = 2
|
||||
|
||||
self.appNameNode = ImmediateTextNode()
|
||||
self.appNameNode.textAlignment = .center
|
||||
self.appNameNode.maximumNumberOfLines = 2
|
||||
|
||||
self.locationInfoNode = ImmediateTextNode()
|
||||
self.locationInfoNode.textAlignment = .center
|
||||
self.locationInfoNode.maximumNumberOfLines = 0
|
||||
|
||||
self.acceptButtonNode = SolidRoundedButtonNode(title: "Confirm Log In", icon: nil, theme: SolidRoundedButtonTheme(backgroundColor: self.presentationData.theme.list.itemDestructiveColor, foregroundColor: self.presentationData.theme.list.itemCheckColors.foregroundColor), height: 50.0, cornerRadius: 10.0, gloss: false)
|
||||
self.cancelButtonNode = SolidRoundedButtonNode(title: self.presentationData.strings.Common_Cancel, icon: nil, theme: SolidRoundedButtonTheme(backgroundColor: self.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: self.presentationData.theme.list.itemCheckColors.foregroundColor), height: 50.0, cornerRadius: 10.0, gloss: false)
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.containerNode)
|
||||
self.containerNode.addSubnode(self.backgroundNode)
|
||||
self.containerNode.addSubnode(self.iconNode)
|
||||
self.containerNode.addSubnode(self.titleNode)
|
||||
self.containerNode.addSubnode(self.appNameNode)
|
||||
self.containerNode.addSubnode(self.locationInfoNode)
|
||||
self.containerNode.addSubnode(self.acceptButtonNode)
|
||||
self.containerNode.addSubnode(self.cancelButtonNode)
|
||||
|
||||
let titleFont = Font.bold(24.0)
|
||||
let subtitleFont = Font.regular(16.0)
|
||||
let textColor = self.presentationData.theme.list.itemPrimaryTextColor
|
||||
let seccondaryTextColor = self.presentationData.theme.list.itemSecondaryTextColor
|
||||
|
||||
self.titleNode.attributedText = NSAttributedString(string: "\(tokenInfo.appName)", font: titleFont, textColor: textColor)
|
||||
|
||||
self.appNameNode.attributedText = NSAttributedString(string: "\(tokenInfo.deviceModel), \(tokenInfo.platform) \(tokenInfo.systemVersion)", font: subtitleFont, textColor: seccondaryTextColor)
|
||||
|
||||
self.locationInfoNode.attributedText = NSAttributedString(string: "\(tokenInfo.region)\nIP: \(tokenInfo.ip)", font: subtitleFont, textColor: seccondaryTextColor)
|
||||
|
||||
self.acceptButtonNode.pressed = { [weak self] in
|
||||
accept()
|
||||
}
|
||||
self.cancelButtonNode.pressed = {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: self.containerNode.bounds.height), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
}
|
||||
|
||||
func animateOut(completion: @escaping () -> Void) {
|
||||
self.containerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: self.containerNode.bounds.height), duration: 0.3, removeOnCompletion: false, additive: true, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
var insets = layout.insets(options: [])
|
||||
let sideInset: CGFloat = 22.0
|
||||
|
||||
let buttonSideInset: CGFloat = 16.0
|
||||
let bottomInset = insets.bottom + 10.0
|
||||
let buttonWidth = layout.size.width - buttonSideInset * 2.0
|
||||
let buttonHeight: CGFloat = 50.0
|
||||
let buttonSpacing: CGFloat = 20.0
|
||||
let contentButtonSpacing: CGFloat = 35.0
|
||||
let titleSpacing: CGFloat = 1.0
|
||||
let locationSpacing: CGFloat = 35.0
|
||||
let iconSpacing: CGFloat = 35.0
|
||||
let topInset: CGFloat = 35.0
|
||||
|
||||
let iconSize = self.iconNode.image?.size ?? CGSize(width: 10.0, height: 1.0)
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: .greatestFiniteMagnitude))
|
||||
let appNameSize = self.appNameNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: .greatestFiniteMagnitude))
|
||||
let locationSize = self.locationInfoNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: .greatestFiniteMagnitude))
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
contentHeight += topInset + iconSize.height
|
||||
contentHeight += iconSpacing + titleSize.height
|
||||
contentHeight += titleSpacing + appNameSize.height
|
||||
contentHeight += locationSpacing + locationSize.height
|
||||
contentHeight += contentButtonSpacing + bottomInset + buttonHeight + buttonSpacing + buttonHeight
|
||||
|
||||
let iconFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: topInset), size: iconSize)
|
||||
transition.updateFrame(node: self.iconNode, frame: iconFrame)
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: iconFrame.maxY + iconSpacing), size: titleSize)
|
||||
transition.updateFrame(node: self.titleNode, frame: titleFrame)
|
||||
|
||||
let appNameFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - appNameSize.width) / 2.0), y: titleFrame.maxY + titleSpacing), size: appNameSize)
|
||||
transition.updateFrame(node: self.appNameNode, frame: appNameFrame)
|
||||
|
||||
let locationFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - locationSize.width) / 2.0), y: appNameFrame.maxY + locationSpacing), size: locationSize)
|
||||
transition.updateFrame(node: self.locationInfoNode, frame: locationFrame)
|
||||
|
||||
let cancelButtonFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - buttonWidth) / 2.0), y: contentHeight - bottomInset - buttonHeight), size: CGSize(width: buttonWidth, height: buttonHeight))
|
||||
transition.updateFrame(node: self.cancelButtonNode, frame: cancelButtonFrame)
|
||||
self.cancelButtonNode.updateLayout(width: cancelButtonFrame.width, transition: transition)
|
||||
|
||||
let acceptButtonFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - buttonWidth) / 2.0), y: cancelButtonFrame.minY - buttonSpacing - buttonHeight), size: CGSize(width: buttonWidth, height: buttonHeight))
|
||||
transition.updateFrame(node: self.acceptButtonNode, frame: acceptButtonFrame)
|
||||
self.acceptButtonNode.updateLayout(width: acceptButtonFrame.width, transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - contentHeight), size: CGSize(width: layout.size.width, height: contentHeight)))
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: contentHeight + 24.0)))
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if let result = self.cancelButtonNode.view.hitTest(self.view.convert(point, to: self.cancelButtonNode.view), with: event) {
|
||||
return result
|
||||
}
|
||||
if let result = self.acceptButtonNode.view.hitTest(self.view.convert(point, to: self.acceptButtonNode.view), with: event) {
|
||||
return result
|
||||
}
|
||||
return super.hitTest(point, with: event)
|
||||
}
|
||||
}
|
||||
@ -136,16 +136,34 @@ public final class AuthTransferScanScreen: ViewController {
|
||||
return
|
||||
}
|
||||
if let url = URL(string: code), let parsedToken = parseAuthTransferUrl(url) {
|
||||
print("import token: \(parsedToken.base64EncodedString())")
|
||||
|
||||
strongSelf.approveDisposable.set((approveAuthTransferToken(account: strongSelf.context.account, token: parsedToken)
|
||||
|> deliverOnMainQueue).start(error: { _ in
|
||||
}, completed: {
|
||||
let _ = (getAuthTransferTokenInfo(network: strongSelf.context.account.network, token: parsedToken)
|
||||
|> deliverOnMainQueue).start(next: { tokenInfo in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.dismiss()
|
||||
}))
|
||||
(strongSelf.displayNode as! AuthTransferScanScreenNode).updateTokenPreview(confirmationNode: AuthTransferConfirmationNode(context: strongSelf.context, presentationData: strongSelf.presentationData, tokenInfo: tokenInfo, accept: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.approveDisposable.set((approveAuthTransferToken(account: strongSelf.context.account, token: parsedToken)
|
||||
|> deliverOnMainQueue).start(error: { _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
(strongSelf.displayNode as! AuthTransferScanScreenNode).updateTokenPreview(confirmationNode: nil)
|
||||
}, completed: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.dismiss()
|
||||
}))
|
||||
}, cancel: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
(strongSelf.displayNode as! AuthTransferScanScreenNode).updateTokenPreview(confirmationNode: nil)
|
||||
}))
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -166,9 +184,11 @@ private final class AuthTransferScanScreenNode: ViewControllerTracingNode, UIScr
|
||||
private let bottomDimNode: ASDisplayNode
|
||||
private let leftDimNode: ASDisplayNode
|
||||
private let rightDimNode: ASDisplayNode
|
||||
private let centerDimNode: ASDisplayNode
|
||||
private let frameNode: ASImageNode
|
||||
private let torchButtonNode: GlassButtonNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
private let textNode: ImmediateTextNode
|
||||
|
||||
private let camera: Camera
|
||||
private let codeDisposable = MetaDisposable()
|
||||
@ -176,6 +196,8 @@ private final class AuthTransferScanScreenNode: ViewControllerTracingNode, UIScr
|
||||
fileprivate let focusedCode = ValuePromise<CameraCode?>(ignoreRepeated: true)
|
||||
private var focusedRect: CGRect?
|
||||
|
||||
private(set) var confirmationNode: AuthTransferConfirmationNode?
|
||||
|
||||
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||
|
||||
init(presentationData: PresentationData) {
|
||||
@ -204,6 +226,10 @@ private final class AuthTransferScanScreenNode: ViewControllerTracingNode, UIScr
|
||||
self.rightDimNode.alpha = 0.625
|
||||
self.rightDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8)
|
||||
|
||||
self.centerDimNode = ASDisplayNode()
|
||||
self.centerDimNode.alpha = 0.0
|
||||
self.centerDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8)
|
||||
|
||||
self.frameNode = ASImageNode()
|
||||
self.frameNode.image = generateFrameImage()
|
||||
|
||||
@ -215,6 +241,12 @@ private final class AuthTransferScanScreenNode: ViewControllerTracingNode, UIScr
|
||||
self.titleNode.maximumNumberOfLines = 0
|
||||
self.titleNode.textAlignment = .center
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.displaysAsynchronously = false
|
||||
self.textNode.attributedText = NSAttributedString(string: "Scan a QR code to log into\nthis account on another device.", font: Font.regular(16.0), textColor: .white)
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
self.textNode.textAlignment = .center
|
||||
|
||||
self.camera = Camera(configuration: .init(preset: .hd1920x1080, position: .back, audio: false))
|
||||
|
||||
super.init()
|
||||
@ -227,9 +259,11 @@ private final class AuthTransferScanScreenNode: ViewControllerTracingNode, UIScr
|
||||
self.addSubnode(self.bottomDimNode)
|
||||
self.addSubnode(self.leftDimNode)
|
||||
self.addSubnode(self.rightDimNode)
|
||||
self.addSubnode(self.centerDimNode)
|
||||
self.addSubnode(self.frameNode)
|
||||
self.addSubnode(self.torchButtonNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.textNode)
|
||||
|
||||
self.torchButtonNode.addTarget(self, action: #selector(self.torchPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
@ -281,6 +315,28 @@ private final class AuthTransferScanScreenNode: ViewControllerTracingNode, UIScr
|
||||
}
|
||||
}
|
||||
|
||||
func updateTokenPreview(confirmationNode: AuthTransferConfirmationNode?) {
|
||||
if let confirmationNode = self.confirmationNode {
|
||||
confirmationNode.animateOut { [weak confirmationNode] in
|
||||
confirmationNode?.removeFromSupernode()
|
||||
}
|
||||
self.confirmationNode = nil
|
||||
}
|
||||
self.confirmationNode = confirmationNode
|
||||
if let confirmationNode = self.confirmationNode {
|
||||
self.addSubnode(confirmationNode)
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
confirmationNode.updateLayout(layout: layout, transition: .immediate)
|
||||
confirmationNode.animateIn()
|
||||
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
}
|
||||
} else {
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (layout, navigationHeight)
|
||||
|
||||
@ -307,7 +363,21 @@ private final class AuthTransferScanScreenNode: ViewControllerTracingNode, UIScr
|
||||
let dimAlpha: CGFloat
|
||||
let dimRect: CGRect
|
||||
let controlsAlpha: CGFloat
|
||||
if let focusedRect = self.focusedRect {
|
||||
var centerDimAlpha: CGFloat = 0.0
|
||||
var frameAlpha: CGFloat = 1.0
|
||||
if self.confirmationNode != nil {
|
||||
controlsAlpha = 0.0
|
||||
dimAlpha = 0.625
|
||||
centerDimAlpha = 0.625
|
||||
frameAlpha = 0.0
|
||||
if let focusedRect = self.focusedRect {
|
||||
let side = max(bounds.width * focusedRect.width, bounds.height * focusedRect.height) * 0.6
|
||||
let center = CGPoint(x: (1.0 - focusedRect.center.y) * bounds.width, y: focusedRect.center.x * bounds.height)
|
||||
dimRect = CGRect(x: center.x - side / 2.0, y: center.y - side / 2.0, width: side, height: side)
|
||||
} else {
|
||||
dimRect = CGRect(x: dimInset, y: dimHeight, width: layout.size.width - dimInset * 2.0, height: layout.size.height - dimHeight * 2.0)
|
||||
}
|
||||
} else if let focusedRect = self.focusedRect {
|
||||
controlsAlpha = 0.0
|
||||
dimAlpha = 1.0
|
||||
let side = max(bounds.width * focusedRect.width, bounds.height * focusedRect.height) * 0.6
|
||||
@ -323,22 +393,33 @@ private final class AuthTransferScanScreenNode: ViewControllerTracingNode, UIScr
|
||||
transition.updateAlpha(node: self.bottomDimNode, alpha: dimAlpha)
|
||||
transition.updateAlpha(node: self.leftDimNode, alpha: dimAlpha)
|
||||
transition.updateAlpha(node: self.rightDimNode, alpha: dimAlpha)
|
||||
transition.updateAlpha(node: self.centerDimNode, alpha: centerDimAlpha)
|
||||
transition.updateAlpha(node: self.frameNode, alpha: frameAlpha)
|
||||
|
||||
transition.updateFrame(node: self.topDimNode, frame: CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: dimRect.minY))
|
||||
transition.updateFrame(node: self.bottomDimNode, frame: CGRect(x: 0.0, y: dimRect.maxY, width: layout.size.width, height: max(0.0, layout.size.height - dimRect.maxY)))
|
||||
transition.updateFrame(node: self.leftDimNode, frame: CGRect(x: 0.0, y: dimRect.minY, width: max(0.0, dimRect.minX), height: dimRect.height))
|
||||
transition.updateFrame(node: self.rightDimNode, frame: CGRect(x: dimRect.maxX, y: dimRect.minY, width: max(0.0, layout.size.width - dimRect.maxX), height: dimRect.height))
|
||||
transition.updateFrame(node: self.frameNode, frame: dimRect.insetBy(dx: -2.0, dy: -2.0))
|
||||
transition.updateFrame(node: self.centerDimNode, frame: dimRect)
|
||||
|
||||
let buttonSize = CGSize(width: 72.0, height: 72.0)
|
||||
transition.updateFrame(node: self.torchButtonNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - buttonSize.width) / 2.0), y: dimHeight + frameSide + 50.0), size: buttonSize))
|
||||
|
||||
transition.updateAlpha(node: self.titleNode, alpha: controlsAlpha)
|
||||
transition.updateAlpha(node: self.textNode, alpha: controlsAlpha)
|
||||
transition.updateAlpha(node: self.torchButtonNode, alpha: controlsAlpha)
|
||||
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height))
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: dimHeight - titleSize.height - titleSpacing), size: titleSize)
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height))
|
||||
let textFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width) / 2.0), y: dimHeight - textSize.height - titleSpacing), size: textSize)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: textFrame.minY - 18.0 - titleSize.height), size: titleSize)
|
||||
transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame)
|
||||
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
|
||||
|
||||
if let confirmationNode = self.confirmationNode {
|
||||
confirmationNode.updateLayout(layout: layout, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func torchPressed() {
|
||||
|
||||
@ -113,7 +113,7 @@ private final class SettingsItemArguments {
|
||||
let keepPhone: () -> Void
|
||||
let openPhoneNumberChange: () -> Void
|
||||
let accountContextAction: (AccountRecordId, ASDisplayNode, ContextGesture?) -> Void
|
||||
let openLoginDesktop: () -> Void
|
||||
let openDevices: () -> Void
|
||||
|
||||
init(
|
||||
accountManager: AccountManager,
|
||||
@ -147,7 +147,7 @@ private final class SettingsItemArguments {
|
||||
keepPhone: @escaping () -> Void,
|
||||
openPhoneNumberChange: @escaping () -> Void,
|
||||
accountContextAction: @escaping (AccountRecordId, ASDisplayNode, ContextGesture?) -> Void,
|
||||
openLoginDesktop: @escaping () -> Void
|
||||
openDevices: @escaping () -> Void
|
||||
) {
|
||||
self.accountManager = accountManager
|
||||
self.avatarAndNameInfoContext = avatarAndNameInfoContext
|
||||
@ -180,7 +180,7 @@ private final class SettingsItemArguments {
|
||||
self.keepPhone = keepPhone
|
||||
self.openPhoneNumberChange = openPhoneNumberChange
|
||||
self.accountContextAction = accountContextAction
|
||||
self.openLoginDesktop = openLoginDesktop
|
||||
self.openDevices = openDevices
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,7 +189,6 @@ private enum SettingsSection: Int32 {
|
||||
case phone
|
||||
case accounts
|
||||
case proxy
|
||||
case loginDesktop
|
||||
case media
|
||||
case generalSettings
|
||||
case advanced
|
||||
@ -210,7 +209,7 @@ private indirect enum SettingsEntry: ItemListNodeEntry {
|
||||
|
||||
case proxy(PresentationTheme, UIImage?, String, String)
|
||||
|
||||
case loginDesktop(PresentationTheme, UIImage?, String)
|
||||
case devices(PresentationTheme, UIImage?, String, String)
|
||||
|
||||
case savedMessages(PresentationTheme, UIImage?, String)
|
||||
case recentCalls(PresentationTheme, UIImage?, String)
|
||||
@ -238,8 +237,8 @@ private indirect enum SettingsEntry: ItemListNodeEntry {
|
||||
return SettingsSection.accounts.rawValue
|
||||
case .proxy:
|
||||
return SettingsSection.proxy.rawValue
|
||||
case .loginDesktop:
|
||||
return SettingsSection.loginDesktop.rawValue
|
||||
case .devices:
|
||||
return SettingsSection.media.rawValue
|
||||
case .savedMessages, .recentCalls, .stickers:
|
||||
return SettingsSection.media.rawValue
|
||||
case .notificationsAndSounds, .privacyAndSecurity, .dataAndStorage, .themes, .language:
|
||||
@ -271,13 +270,13 @@ private indirect enum SettingsEntry: ItemListNodeEntry {
|
||||
return 1002
|
||||
case .proxy:
|
||||
return 1003
|
||||
case .loginDesktop:
|
||||
return 1004
|
||||
case .savedMessages:
|
||||
return 1005
|
||||
return 1004
|
||||
case .recentCalls:
|
||||
return 1006
|
||||
return 1005
|
||||
case .stickers:
|
||||
return 1006
|
||||
case .devices:
|
||||
return 1007
|
||||
case .notificationsAndSounds:
|
||||
return 1008
|
||||
@ -390,8 +389,8 @@ private indirect enum SettingsEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .loginDesktop(lhsTheme, lhsImage, lhsText):
|
||||
if case let .loginDesktop(rhsTheme, rhsImage, rhsText) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText {
|
||||
case let .devices(lhsTheme, lhsImage, lhsText, lhsValue):
|
||||
if case let .devices(rhsTheme, rhsImage, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -545,9 +544,9 @@ private indirect enum SettingsEntry: ItemListNodeEntry {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: image, title: text, label: value, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
||||
arguments.openProxy()
|
||||
})
|
||||
case let .loginDesktop(theme, image, text):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: image, title: text, label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
||||
arguments.openLoginDesktop()
|
||||
case let .devices(theme, image, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: image, title: text, label: value, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
||||
arguments.openDevices()
|
||||
})
|
||||
case let .savedMessages(theme, image, text):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: image, title: text, label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
||||
@ -657,11 +656,10 @@ private func settingsEntries(account: Account, presentationData: PresentationDat
|
||||
entries.append(.proxy(presentationData.theme, PresentationResourcesSettings.proxy, presentationData.strings.Settings_Proxy, valueString))
|
||||
}
|
||||
|
||||
entries.append(.loginDesktop(presentationData.theme, PresentationResourcesSettings.security, "Telegram Desktop"))
|
||||
|
||||
entries.append(.savedMessages(presentationData.theme, PresentationResourcesSettings.savedMessages, presentationData.strings.Settings_SavedMessages))
|
||||
entries.append(.recentCalls(presentationData.theme, PresentationResourcesSettings.recentCalls, presentationData.strings.CallSettings_RecentCalls))
|
||||
entries.append(.stickers(presentationData.theme, PresentationResourcesSettings.stickers, presentationData.strings.ChatSettings_Stickers, unreadTrendingStickerPacks == 0 ? "" : "\(unreadTrendingStickerPacks)", archivedPacks))
|
||||
entries.append(.devices(presentationData.theme, PresentationResourcesSettings.stickers, "Devices", ""))
|
||||
|
||||
let notificationsWarning = shouldDisplayNotificationsPermissionWarning(status: notificationsAuthorizationStatus, suppressed: notificationsWarningSuppressed)
|
||||
entries.append(.notificationsAndSounds(presentationData.theme, PresentationResourcesSettings.notifications, presentationData.strings.Settings_NotificationsAndSounds, notifyExceptions, notificationsWarning))
|
||||
@ -1068,7 +1066,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
|
||||
} else {
|
||||
gesture?.cancel()
|
||||
}
|
||||
}, openLoginDesktop: {
|
||||
}, openDevices: {
|
||||
let _ = (contextValue.get()
|
||||
|> deliverOnMainQueue
|
||||
|> take(1)).start(next: { context in
|
||||
|
||||
@ -61,6 +61,12 @@ public class UnauthorizedAccount {
|
||||
public let testingEnvironment: Bool
|
||||
public let postbox: Postbox
|
||||
public let network: Network
|
||||
private let stateManager: UnauthorizedAccountStateManager
|
||||
|
||||
private let updateLoginTokenPipe = ValuePipe<Void>()
|
||||
public var updateLoginTokenEvents: Signal<Void, NoError> {
|
||||
return self.updateLoginTokenPipe.signal()
|
||||
}
|
||||
|
||||
public var masterDatacenterId: Int32 {
|
||||
return Int32(self.network.mtProto.datacenterId)
|
||||
@ -76,6 +82,10 @@ public class UnauthorizedAccount {
|
||||
self.testingEnvironment = testingEnvironment
|
||||
self.postbox = postbox
|
||||
self.network = network
|
||||
let updateLoginTokenPipe = self.updateLoginTokenPipe
|
||||
self.stateManager = UnauthorizedAccountStateManager(network: network, updateLoginToken: {
|
||||
updateLoginTokenPipe.putNext(Void())
|
||||
})
|
||||
|
||||
network.shouldKeepConnection.set(self.shouldBeServiceTaskMaster.get()
|
||||
|> map { mode -> Bool in
|
||||
@ -99,6 +109,8 @@ public class UnauthorizedAccount {
|
||||
}
|
||||
network.context.beginExplicitBackupAddressDiscovery()
|
||||
})
|
||||
|
||||
self.stateManager.reset()
|
||||
}
|
||||
|
||||
public func changedMasterDatacenterId(accountManager: AccountManager, masterDatacenterId: Int32) -> Signal<UnauthorizedAccount, NoError> {
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import TelegramApi
|
||||
import MtProtoKit
|
||||
import SyncCore
|
||||
|
||||
private final class UnauthorizedUpdateMessageService: NSObject, MTMessageService {
|
||||
let pipe: ValuePipe<[Api.Update]> = ValuePipe()
|
||||
var mtProto: MTProto?
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
func mtProtoWillAdd(_ mtProto: MTProto!) {
|
||||
self.mtProto = mtProto
|
||||
}
|
||||
|
||||
func mtProtoDidChangeSession(_ mtProto: MTProto!) {
|
||||
}
|
||||
|
||||
func mtProtoServerDidChangeSession(_ mtProto: MTProto!, firstValidMessageId: Int64, otherValidMessageIds: [Any]!) {
|
||||
}
|
||||
|
||||
func putNext(_ updates: [Api.Update]) {
|
||||
self.pipe.putNext(updates)
|
||||
}
|
||||
|
||||
func mtProto(_ mtProto: MTProto!, receivedMessage message: MTIncomingMessage!) {
|
||||
if let updates = (message.body as? BoxedMessage)?.body as? Api.Updates {
|
||||
self.addUpdates(updates)
|
||||
}
|
||||
}
|
||||
|
||||
func addUpdates(_ updates: Api.Updates) {
|
||||
switch updates {
|
||||
case let .updates(updates, _, _, _, _):
|
||||
self.putNext(updates)
|
||||
case let .updatesCombined(updates, _, _, _, _, _):
|
||||
self.putNext(updates)
|
||||
case let .updateShort(update, _):
|
||||
self.putNext([update])
|
||||
case .updateShortChatMessage, .updateShortMessage, .updatesTooLong, .updateShortSentMessage:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class UnauthorizedAccountStateManager {
|
||||
private let queue = Queue()
|
||||
private let network: Network
|
||||
private var updateService: UnauthorizedUpdateMessageService?
|
||||
private let updateServiceDisposable = MetaDisposable()
|
||||
private let updateLoginToken: () -> Void
|
||||
|
||||
init(network: Network, updateLoginToken: @escaping () -> Void) {
|
||||
self.network = network
|
||||
self.updateLoginToken = updateLoginToken
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.updateServiceDisposable.dispose()
|
||||
}
|
||||
|
||||
func reset() {
|
||||
self.queue.async {
|
||||
if self.updateService == nil {
|
||||
self.updateService = UnauthorizedUpdateMessageService()
|
||||
let updateLoginToken = self.updateLoginToken
|
||||
self.updateServiceDisposable.set(self.updateService!.pipe.signal().start(next: { updates in
|
||||
for update in updates {
|
||||
switch update {
|
||||
case .updateLoginToken:
|
||||
updateLoginToken()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}))
|
||||
self.network.mtProto.add(self.updateService)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
submodules/TelegramUI/Images.xcassets/Settings/TransferAuthLaptop.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/TransferAuthLaptop.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "EmojiComputer.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Settings/TransferAuthLaptop.imageset/EmojiComputer.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Settings/TransferAuthLaptop.imageset/EmojiComputer.pdf
vendored
Normal file
Binary file not shown.
@ -217,6 +217,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
|
||||
private var qrNode: ASImageNode?
|
||||
private let exportTokenDisposable = MetaDisposable()
|
||||
private let tokenEventsDisposable = MetaDisposable()
|
||||
var accountUpdated: ((UnauthorizedAccount) -> Void)?
|
||||
|
||||
private let debugAction: () -> Void
|
||||
@ -297,10 +298,16 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
self.phoneAndCountryNode.checkPhone = { [weak self] in
|
||||
self?.checkPhone?()
|
||||
}
|
||||
|
||||
self.tokenEventsDisposable.set((account.updateLoginTokenEvents
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
self?.refreshQrToken()
|
||||
}))
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.exportTokenDisposable.dispose()
|
||||
self.tokenEventsDisposable.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
@ -425,6 +432,10 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
strongSelf.exportTokenDisposable.set(nil)
|
||||
strongSelf.account = account
|
||||
strongSelf.accountUpdated?(account)
|
||||
strongSelf.tokenEventsDisposable.set((account.updateLoginTokenEvents
|
||||
|> deliverOnMainQueue).start(next: { _ in
|
||||
self?.refreshQrToken()
|
||||
}))
|
||||
strongSelf.refreshQrToken()
|
||||
case .loggedIn:
|
||||
strongSelf.exportTokenDisposable.set(nil)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user