mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Initial QR login API [skip ci]
This commit is contained in:
parent
94b880c4dc
commit
40b0b3479e
@ -38,7 +38,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
|
||||
|
||||
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
|
||||
|
||||
self.impl = NotificationViewControllerImpl(initializationData: NotificationViewControllerInitializationData(appGroupPath: appGroupUrl.path, apiId: buildConfig.apiId, languagesCategory: languagesCategory, encryptionParameters: encryptionParameters, appVersion: appVersion, bundleData: buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), setPreferredContentSize: { [weak self] size in
|
||||
self.impl = NotificationViewControllerImpl(initializationData: NotificationViewControllerInitializationData(appGroupPath: appGroupUrl.path, apiId: buildConfig.apiId, apiHash: buildConfig.apiHash, languagesCategory: languagesCategory, encryptionParameters: encryptionParameters, appVersion: appVersion, bundleData: buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), setPreferredContentSize: { [weak self] size in
|
||||
self?.preferredContentSize = size
|
||||
})
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ class ShareRootController: UIViewController {
|
||||
|
||||
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
|
||||
|
||||
self.impl = ShareRootControllerImpl(initializationData: ShareRootControllerInitializationData(appGroupPath: appGroupUrl.path, apiId: buildConfig.apiId, languagesCategory: languagesCategory, encryptionParameters: encryptionParameters, appVersion: appVersion, bundleData: buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), getExtensionContext: { [weak self] in
|
||||
self.impl = ShareRootControllerImpl(initializationData: ShareRootControllerInitializationData(appGroupPath: appGroupUrl.path, apiId: buildConfig.apiId, apiHash: buildConfig.apiHash, languagesCategory: languagesCategory, encryptionParameters: encryptionParameters, appVersion: appVersion, bundleData: buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), getExtensionContext: { [weak self] in
|
||||
return self?.extensionContext
|
||||
})
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ public class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
|
||||
let buildConfig = BuildConfig(baseAppBundleId: baseAppBundleId)
|
||||
|
||||
let apiId: Int32 = buildConfig.apiId
|
||||
let apiHash: String = buildConfig.apiHash
|
||||
let languagesCategory = "ios"
|
||||
|
||||
let appGroupName = "group.\(baseAppBundleId)"
|
||||
@ -100,7 +101,7 @@ public class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
|
||||
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
|
||||
let encryptionParameters = ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
|
||||
|
||||
account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider()), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods, encryptionParameters: encryptionParameters)
|
||||
account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider()), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods, encryptionParameters: encryptionParameters)
|
||||
|> mapToSignal { account -> Signal<Account?, NoError> in
|
||||
if let account = account {
|
||||
switch account {
|
||||
|
23
submodules/AuthTransferUI/BUCK
Normal file
23
submodules/AuthTransferUI/BUCK
Normal file
@ -0,0 +1,23 @@
|
||||
load("//Config:buck_rule_macros.bzl", "static_library")
|
||||
|
||||
static_library(
|
||||
name = "AuthTransferUI",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
deps = [
|
||||
"//submodules/TelegramCore:TelegramCore#shared",
|
||||
"//submodules/SyncCore:SyncCore#shared",
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
|
||||
"//submodules/Display:Display#shared",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/QrCode:QrCode",
|
||||
"//submodules/Camera:Camera",
|
||||
"//submodules/GlassButtonNode:GlassButtonNode",
|
||||
"//submodules/AlertUI:AlertUI",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
],
|
||||
)
|
349
submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift
Normal file
349
submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift
Normal file
@ -0,0 +1,349 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AccountContext
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Camera
|
||||
import GlassButtonNode
|
||||
import CoreImage
|
||||
import AlertUI
|
||||
import TelegramPresentationData
|
||||
import TelegramCore
|
||||
|
||||
private func parseAuthTransferUrl(_ url: URL) -> Data? {
|
||||
var tokenString: String?
|
||||
if let query = url.query, let components = URLComponents(string: "/?" + query), let queryItems = components.queryItems {
|
||||
for queryItem in queryItems {
|
||||
if let value = queryItem.value {
|
||||
if queryItem.name == "token", !value.isEmpty {
|
||||
tokenString = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if var tokenString = tokenString {
|
||||
tokenString = tokenString.replacingOccurrences(of: "-", with: "+")
|
||||
tokenString = tokenString.replacingOccurrences(of: "_", with: "/")
|
||||
while tokenString.count % 4 != 0 {
|
||||
tokenString.append("=")
|
||||
}
|
||||
if let data = Data(base64Encoded: tokenString) {
|
||||
return data
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func generateFrameImage() -> UIImage? {
|
||||
return generateImage(CGSize(width: 64.0, height: 64.0), contextGenerator: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
context.clear(bounds)
|
||||
context.setStrokeColor(UIColor.white.cgColor)
|
||||
context.setLineWidth(4.0)
|
||||
context.setLineCap(.round)
|
||||
|
||||
var path = CGMutablePath();
|
||||
path.move(to: CGPoint(x: 2.0, y: 2.0 + 26.0))
|
||||
path.addArc(tangent1End: CGPoint(x: 2.0, y: 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: 2.0), radius: 6.0)
|
||||
path.addLine(to: CGPoint(x: 2.0 + 26.0, y: 2.0))
|
||||
context.addPath(path)
|
||||
context.strokePath()
|
||||
|
||||
path.move(to: CGPoint(x: size.width - 2.0, y: 2.0 + 26.0))
|
||||
path.addArc(tangent1End: CGPoint(x: size.width - 2.0, y: 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: 2.0), radius: 6.0)
|
||||
path.addLine(to: CGPoint(x: size.width - 2.0 - 26.0, y: 2.0))
|
||||
context.addPath(path)
|
||||
context.strokePath()
|
||||
|
||||
path.move(to: CGPoint(x: 2.0, y: size.height - 2.0 - 26.0))
|
||||
path.addArc(tangent1End: CGPoint(x: 2.0, y: size.height - 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: size.height - 2.0), radius: 6.0)
|
||||
path.addLine(to: CGPoint(x: 2.0 + 26.0, y: size.height - 2.0))
|
||||
context.addPath(path)
|
||||
context.strokePath()
|
||||
|
||||
path.move(to: CGPoint(x: size.width - 2.0, y: size.height - 2.0 - 26.0))
|
||||
path.addArc(tangent1End: CGPoint(x: size.width - 2.0, y: size.height - 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: size.height - 2.0), radius: 6.0)
|
||||
path.addLine(to: CGPoint(x: size.width - 2.0 - 26.0, y: size.height - 2.0))
|
||||
context.addPath(path)
|
||||
context.strokePath()
|
||||
})?.stretchableImage(withLeftCapWidth: 32, topCapHeight: 32)
|
||||
}
|
||||
|
||||
public final class AuthTransferScanScreen: ViewController {
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
|
||||
private var codeDisposable: Disposable?
|
||||
private var inForegroundDisposable: Disposable?
|
||||
private let approveDisposable = MetaDisposable()
|
||||
|
||||
public init(context: AccountContext) {
|
||||
self.context = context
|
||||
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let navigationBarTheme = NavigationBarTheme(buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear)
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close)))
|
||||
|
||||
self.statusBar.statusBarStyle = .White
|
||||
|
||||
self.navigationPresentation = .modalInLargeLayout
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
self.navigationBar?.intrinsicCanTransitionInline = false
|
||||
|
||||
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Wallet_Navigation_Back, style: .plain, target: nil, action: nil)
|
||||
|
||||
self.inForegroundDisposable = (context.sharedContext.applicationBindings.applicationInForeground
|
||||
|> deliverOnMainQueue).start(next: { [weak self] inForeground in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
(strongSelf.displayNode as! AuthTransferScanScreenNode).updateInForeground(inForeground)
|
||||
})
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.codeDisposable?.dispose()
|
||||
self.inForegroundDisposable?.dispose()
|
||||
self.approveDisposable.dispose()
|
||||
}
|
||||
|
||||
@objc private func backPressed() {
|
||||
self.dismiss()
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = AuthTransferScanScreenNode(presentationData: self.presentationData)
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self.codeDisposable = ((self.displayNode as! AuthTransferScanScreenNode).focusedCode.get()
|
||||
|> map { code -> String? in
|
||||
return code?.message
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> mapToSignal { code -> Signal<String?, NoError> in
|
||||
return .single(code)
|
||||
|> delay(0.5, queue: Queue.mainQueue())
|
||||
}).start(next: { [weak self] code in
|
||||
guard let strongSelf = self, let code = code else {
|
||||
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: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.dismiss()
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
(self.displayNode as! AuthTransferScanScreenNode).containerLayoutUpdated(layout: layout, navigationHeight: self.navigationHeight, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private final class AuthTransferScanScreenNode: ViewControllerTracingNode, UIScrollViewDelegate {
|
||||
private var presentationData: PresentationData
|
||||
|
||||
private let previewNode: CameraPreviewNode
|
||||
private let fadeNode: ASDisplayNode
|
||||
private let topDimNode: ASDisplayNode
|
||||
private let bottomDimNode: ASDisplayNode
|
||||
private let leftDimNode: ASDisplayNode
|
||||
private let rightDimNode: ASDisplayNode
|
||||
private let frameNode: ASImageNode
|
||||
private let torchButtonNode: GlassButtonNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
|
||||
private let camera: Camera
|
||||
private let codeDisposable = MetaDisposable()
|
||||
|
||||
fileprivate let focusedCode = ValuePromise<CameraCode?>(ignoreRepeated: true)
|
||||
private var focusedRect: CGRect?
|
||||
|
||||
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||
|
||||
init(presentationData: PresentationData) {
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.previewNode = CameraPreviewNode()
|
||||
self.previewNode.backgroundColor = .black
|
||||
|
||||
self.fadeNode = ASDisplayNode()
|
||||
self.fadeNode.alpha = 0.0
|
||||
self.fadeNode.backgroundColor = .black
|
||||
|
||||
self.topDimNode = ASDisplayNode()
|
||||
self.topDimNode.alpha = 0.625
|
||||
self.topDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8)
|
||||
|
||||
self.bottomDimNode = ASDisplayNode()
|
||||
self.bottomDimNode.alpha = 0.625
|
||||
self.bottomDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8)
|
||||
|
||||
self.leftDimNode = ASDisplayNode()
|
||||
self.leftDimNode.alpha = 0.625
|
||||
self.leftDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8)
|
||||
|
||||
self.rightDimNode = ASDisplayNode()
|
||||
self.rightDimNode.alpha = 0.625
|
||||
self.rightDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8)
|
||||
|
||||
self.frameNode = ASImageNode()
|
||||
self.frameNode.image = generateFrameImage()
|
||||
|
||||
self.torchButtonNode = GlassButtonNode(icon: UIImage(bundleImageName: "Wallet/CameraFlashIcon")!, label: nil)
|
||||
|
||||
self.titleNode = ImmediateTextNode()
|
||||
self.titleNode.displaysAsynchronously = false
|
||||
self.titleNode.attributedText = NSAttributedString(string: presentationData.strings.Wallet_Qr_ScanCode, font: Font.bold(32.0), textColor: .white)
|
||||
self.titleNode.maximumNumberOfLines = 0
|
||||
self.titleNode.textAlignment = .center
|
||||
|
||||
self.camera = Camera(configuration: .init(preset: .hd1920x1080, position: .back, audio: false))
|
||||
|
||||
super.init()
|
||||
|
||||
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
||||
|
||||
self.addSubnode(self.previewNode)
|
||||
self.addSubnode(self.fadeNode)
|
||||
self.addSubnode(self.topDimNode)
|
||||
self.addSubnode(self.bottomDimNode)
|
||||
self.addSubnode(self.leftDimNode)
|
||||
self.addSubnode(self.rightDimNode)
|
||||
self.addSubnode(self.frameNode)
|
||||
self.addSubnode(self.torchButtonNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
|
||||
self.torchButtonNode.addTarget(self, action: #selector(self.torchPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.codeDisposable.dispose()
|
||||
self.camera.stopCapture(invalidate: true)
|
||||
}
|
||||
|
||||
fileprivate func updateInForeground(_ inForeground: Bool) {
|
||||
if !inForeground {
|
||||
self.camera.stopCapture(invalidate: false)
|
||||
} else {
|
||||
self.camera.startCapture()
|
||||
}
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.camera.attachPreviewNode(self.previewNode)
|
||||
self.camera.startCapture()
|
||||
|
||||
let throttledSignal = self.camera.detectedCodes
|
||||
|> mapToThrottled { next -> Signal<[CameraCode], NoError> in
|
||||
return .single(next) |> then(.complete() |> delay(0.3, queue: Queue.concurrentDefaultQueue()))
|
||||
}
|
||||
|
||||
self.codeDisposable.set((throttledSignal
|
||||
|> deliverOnMainQueue).start(next: { [weak self] codes in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let filteredCodes = codes.filter { $0.message.hasPrefix("tg://") }
|
||||
if let code = filteredCodes.first, CGRect(x: 0.3, y: 0.3, width: 0.4, height: 0.4).contains(code.boundingBox.center) {
|
||||
strongSelf.focusedCode.set(code)
|
||||
strongSelf.updateFocusedRect(code.boundingBox)
|
||||
} else {
|
||||
strongSelf.focusedCode.set(nil)
|
||||
strongSelf.updateFocusedRect(nil)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
private func updateFocusedRect(_ rect: CGRect?) {
|
||||
self.focusedRect = rect
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (layout, navigationHeight)
|
||||
|
||||
let sideInset: CGFloat = 66.0
|
||||
let titleSpacing: CGFloat = 48.0
|
||||
let bounds = CGRect(origin: CGPoint(), size: layout.size)
|
||||
|
||||
if case .tablet = layout.deviceMetrics.type {
|
||||
if UIDevice.current.orientation == .landscapeLeft {
|
||||
self.previewNode.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||
} else if UIDevice.current.orientation == .landscapeRight {
|
||||
self.previewNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||
} else {
|
||||
self.previewNode.transform = CATransform3DIdentity
|
||||
}
|
||||
}
|
||||
transition.updateFrame(node: self.previewNode, frame: bounds)
|
||||
transition.updateFrame(node: self.fadeNode, frame: bounds)
|
||||
|
||||
let frameSide = max(240.0, layout.size.width - sideInset * 2.0)
|
||||
let dimHeight = ceil((layout.size.height - frameSide) / 2.0)
|
||||
let dimInset = (layout.size.width - frameSide) / 2.0
|
||||
|
||||
let dimAlpha: CGFloat
|
||||
let dimRect: CGRect
|
||||
let controlsAlpha: CGFloat
|
||||
if let focusedRect = self.focusedRect {
|
||||
controlsAlpha = 0.0
|
||||
dimAlpha = 1.0
|
||||
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 {
|
||||
controlsAlpha = 1.0
|
||||
dimAlpha = 0.625
|
||||
dimRect = CGRect(x: dimInset, y: dimHeight, width: layout.size.width - dimInset * 2.0, height: layout.size.height - dimHeight * 2.0)
|
||||
}
|
||||
|
||||
transition.updateAlpha(node: self.topDimNode, alpha: dimAlpha)
|
||||
transition.updateAlpha(node: self.bottomDimNode, alpha: dimAlpha)
|
||||
transition.updateAlpha(node: self.leftDimNode, alpha: dimAlpha)
|
||||
transition.updateAlpha(node: self.rightDimNode, alpha: dimAlpha)
|
||||
|
||||
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))
|
||||
|
||||
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.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)
|
||||
transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame)
|
||||
}
|
||||
|
||||
@objc private func torchPressed() {
|
||||
self.torchButtonNode.isSelected = !self.torchButtonNode.isSelected
|
||||
self.camera.setTorchActive(self.torchButtonNode.isSelected)
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ static_library(
|
||||
"//submodules/DeleteChatPeerActionSheetItem:DeleteChatPeerActionSheetItem",
|
||||
"//submodules/PhoneNumberFormat:PhoneNumberFormat",
|
||||
"//submodules/OpenInExternalAppUI:OpenInExternalAppUI",
|
||||
"//submodules/AuthTransferUI:AuthTransferUI",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
@ -35,6 +35,7 @@ import AppBundle
|
||||
import ContextUI
|
||||
import WalletUI
|
||||
import PhoneNumberFormat
|
||||
import AuthTransferUI
|
||||
|
||||
private let maximumNumberOfAccounts = 3
|
||||
|
||||
@ -112,6 +113,7 @@ private final class SettingsItemArguments {
|
||||
let keepPhone: () -> Void
|
||||
let openPhoneNumberChange: () -> Void
|
||||
let accountContextAction: (AccountRecordId, ASDisplayNode, ContextGesture?) -> Void
|
||||
let openLoginDesktop: () -> Void
|
||||
|
||||
init(
|
||||
accountManager: AccountManager,
|
||||
@ -144,7 +146,8 @@ private final class SettingsItemArguments {
|
||||
removeAccount: @escaping (AccountRecordId) -> Void,
|
||||
keepPhone: @escaping () -> Void,
|
||||
openPhoneNumberChange: @escaping () -> Void,
|
||||
accountContextAction: @escaping (AccountRecordId, ASDisplayNode, ContextGesture?) -> Void
|
||||
accountContextAction: @escaping (AccountRecordId, ASDisplayNode, ContextGesture?) -> Void,
|
||||
openLoginDesktop: @escaping () -> Void
|
||||
) {
|
||||
self.accountManager = accountManager
|
||||
self.avatarAndNameInfoContext = avatarAndNameInfoContext
|
||||
@ -177,6 +180,7 @@ private final class SettingsItemArguments {
|
||||
self.keepPhone = keepPhone
|
||||
self.openPhoneNumberChange = openPhoneNumberChange
|
||||
self.accountContextAction = accountContextAction
|
||||
self.openLoginDesktop = openLoginDesktop
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,6 +189,7 @@ private enum SettingsSection: Int32 {
|
||||
case phone
|
||||
case accounts
|
||||
case proxy
|
||||
case loginDesktop
|
||||
case media
|
||||
case generalSettings
|
||||
case advanced
|
||||
@ -205,6 +210,8 @@ private indirect enum SettingsEntry: ItemListNodeEntry {
|
||||
|
||||
case proxy(PresentationTheme, UIImage?, String, String)
|
||||
|
||||
case loginDesktop(PresentationTheme, UIImage?, String)
|
||||
|
||||
case savedMessages(PresentationTheme, UIImage?, String)
|
||||
case recentCalls(PresentationTheme, UIImage?, String)
|
||||
case stickers(PresentationTheme, UIImage?, String, String, [ArchivedStickerPackItem]?)
|
||||
@ -223,22 +230,24 @@ private indirect enum SettingsEntry: ItemListNodeEntry {
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .userInfo, .setProfilePhoto, .setUsername:
|
||||
return SettingsSection.info.rawValue
|
||||
case .phoneInfo, .keepPhone, .changePhone:
|
||||
return SettingsSection.phone.rawValue
|
||||
case .account, .addAccount:
|
||||
return SettingsSection.accounts.rawValue
|
||||
case .proxy:
|
||||
return SettingsSection.proxy.rawValue
|
||||
case .savedMessages, .recentCalls, .stickers:
|
||||
return SettingsSection.media.rawValue
|
||||
case .notificationsAndSounds, .privacyAndSecurity, .dataAndStorage, .themes, .language:
|
||||
return SettingsSection.generalSettings.rawValue
|
||||
case .passport, .wallet, .watch :
|
||||
return SettingsSection.advanced.rawValue
|
||||
case .askAQuestion, .faq:
|
||||
return SettingsSection.help.rawValue
|
||||
case .userInfo, .setProfilePhoto, .setUsername:
|
||||
return SettingsSection.info.rawValue
|
||||
case .phoneInfo, .keepPhone, .changePhone:
|
||||
return SettingsSection.phone.rawValue
|
||||
case .account, .addAccount:
|
||||
return SettingsSection.accounts.rawValue
|
||||
case .proxy:
|
||||
return SettingsSection.proxy.rawValue
|
||||
case .loginDesktop:
|
||||
return SettingsSection.loginDesktop.rawValue
|
||||
case .savedMessages, .recentCalls, .stickers:
|
||||
return SettingsSection.media.rawValue
|
||||
case .notificationsAndSounds, .privacyAndSecurity, .dataAndStorage, .themes, .language:
|
||||
return SettingsSection.generalSettings.rawValue
|
||||
case .passport, .wallet, .watch :
|
||||
return SettingsSection.advanced.rawValue
|
||||
case .askAQuestion, .faq:
|
||||
return SettingsSection.help.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
@ -262,32 +271,34 @@ private indirect enum SettingsEntry: ItemListNodeEntry {
|
||||
return 1002
|
||||
case .proxy:
|
||||
return 1003
|
||||
case .savedMessages:
|
||||
case .loginDesktop:
|
||||
return 1004
|
||||
case .recentCalls:
|
||||
case .savedMessages:
|
||||
return 1005
|
||||
case .stickers:
|
||||
case .recentCalls:
|
||||
return 1006
|
||||
case .notificationsAndSounds:
|
||||
case .stickers:
|
||||
return 1007
|
||||
case .privacyAndSecurity:
|
||||
case .notificationsAndSounds:
|
||||
return 1008
|
||||
case .dataAndStorage:
|
||||
case .privacyAndSecurity:
|
||||
return 1009
|
||||
case .themes:
|
||||
case .dataAndStorage:
|
||||
return 1010
|
||||
case .language:
|
||||
case .themes:
|
||||
return 1011
|
||||
case .wallet:
|
||||
case .language:
|
||||
return 1012
|
||||
case .passport:
|
||||
case .wallet:
|
||||
return 1013
|
||||
case .watch:
|
||||
case .passport:
|
||||
return 1014
|
||||
case .askAQuestion:
|
||||
case .watch:
|
||||
return 1015
|
||||
case .faq:
|
||||
case .askAQuestion:
|
||||
return 1016
|
||||
case .faq:
|
||||
return 1017
|
||||
}
|
||||
}
|
||||
|
||||
@ -379,6 +390,12 @@ 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 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .savedMessages(lhsTheme, lhsImage, lhsText):
|
||||
if case let .savedMessages(rhsTheme, rhsImage, rhsText) = rhs, lhsTheme === rhsTheme, lhsImage === rhsImage, lhsText == rhsText {
|
||||
return true
|
||||
@ -528,6 +545,10 @@ 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 .savedMessages(theme, image, text):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: image, title: text, label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: {
|
||||
arguments.openSavedMessages()
|
||||
@ -636,6 +657,8 @@ 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))
|
||||
@ -1045,6 +1068,12 @@ public func settingsController(context: AccountContext, accountManager: AccountM
|
||||
} else {
|
||||
gesture?.cancel()
|
||||
}
|
||||
}, openLoginDesktop: {
|
||||
let _ = (contextValue.get()
|
||||
|> deliverOnMainQueue
|
||||
|> take(1)).start(next: { context in
|
||||
pushControllerImpl?(AuthTransferScanScreen(context: context))
|
||||
})
|
||||
})
|
||||
|
||||
changeProfilePhotoImpl = {
|
||||
|
@ -53,6 +53,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-614138572] = { return Api.account.TmpPassword.parse_tmpPassword($0) }
|
||||
dict[-2103600678] = { return Api.SecureRequiredType.parse_secureRequiredType($0) }
|
||||
dict[41187252] = { return Api.SecureRequiredType.parse_secureRequiredTypeOneOf($0) }
|
||||
dict[1654593920] = { return Api.auth.LoginToken.parse_loginToken($0) }
|
||||
dict[110008598] = { return Api.auth.LoginToken.parse_loginTokenMigrateTo($0) }
|
||||
dict[957176926] = { return Api.auth.LoginToken.parse_loginTokenSuccess($0) }
|
||||
dict[1064139624] = { return Api.JSONValue.parse_jsonNull($0) }
|
||||
dict[-952869270] = { return Api.JSONValue.parse_jsonBool($0) }
|
||||
dict[736157604] = { return Api.JSONValue.parse_jsonNumber($0) }
|
||||
@ -242,6 +245,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1870238482] = { return Api.Update.parse_updateDeleteScheduledMessages($0) }
|
||||
dict[-2112423005] = { return Api.Update.parse_updateTheme($0) }
|
||||
dict[357013699] = { return Api.Update.parse_updateMessageReactions($0) }
|
||||
dict[-2027964103] = { return Api.Update.parse_updateGeoLiveViewed($0) }
|
||||
dict[1448076945] = { return Api.Update.parse_updateLoginToken($0) }
|
||||
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
|
||||
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
|
||||
dict[367766557] = { return Api.ChannelParticipant.parse_channelParticipant($0) }
|
||||
@ -594,6 +599,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[70813275] = { return Api.InputStickeredMedia.parse_inputStickeredMediaDocument($0) }
|
||||
dict[82699215] = { return Api.messages.FeaturedStickers.parse_featuredStickersNotModified($0) }
|
||||
dict[-123893531] = { return Api.messages.FeaturedStickers.parse_featuredStickers($0) }
|
||||
dict[1375940666] = { return Api.auth.LoginTokenInfo.parse_loginTokenInfo($0) }
|
||||
dict[-2048646399] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonMissed($0) }
|
||||
dict[-527056480] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonDisconnect($0) }
|
||||
dict[1471006352] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonHangup($0) }
|
||||
@ -868,6 +874,8 @@ public struct Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.SecureRequiredType:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.auth.LoginToken:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.JSONValue:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.Photo:
|
||||
@ -1244,6 +1252,8 @@ public struct Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.FeaturedStickers:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.auth.LoginTokenInfo:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.PhoneCallDiscardReason:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.NearestDc:
|
||||
|
@ -4145,6 +4145,8 @@ public extension Api {
|
||||
case updateDeleteScheduledMessages(peer: Api.Peer, messages: [Int32])
|
||||
case updateTheme(theme: Api.Theme)
|
||||
case updateMessageReactions(peer: Api.Peer, msgId: Int32, reactions: Api.MessageReactions)
|
||||
case updateGeoLiveViewed(peer: Api.Peer, msgId: Int32)
|
||||
case updateLoginToken
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -4776,6 +4778,19 @@ public extension Api {
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
reactions.serialize(buffer, true)
|
||||
break
|
||||
case .updateGeoLiveViewed(let peer, let msgId):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2027964103)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .updateLoginToken:
|
||||
if boxed {
|
||||
buffer.appendInt32(1448076945)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -4932,6 +4947,10 @@ public extension Api {
|
||||
return ("updateTheme", [("theme", theme)])
|
||||
case .updateMessageReactions(let peer, let msgId, let reactions):
|
||||
return ("updateMessageReactions", [("peer", peer), ("msgId", msgId), ("reactions", reactions)])
|
||||
case .updateGeoLiveViewed(let peer, let msgId):
|
||||
return ("updateGeoLiveViewed", [("peer", peer), ("msgId", msgId)])
|
||||
case .updateLoginToken:
|
||||
return ("updateLoginToken", [])
|
||||
}
|
||||
}
|
||||
|
||||
@ -6211,6 +6230,25 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateGeoLiveViewed(_ reader: BufferReader) -> Update? {
|
||||
var _1: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.Update.updateGeoLiveViewed(peer: _1!, msgId: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateLoginToken(_ reader: BufferReader) -> Update? {
|
||||
return Api.Update.updateLoginToken
|
||||
}
|
||||
|
||||
}
|
||||
public enum PopularContact: TypeConstructorDescription {
|
||||
|
@ -494,6 +494,90 @@ public struct payments {
|
||||
}
|
||||
public extension Api {
|
||||
public struct auth {
|
||||
public enum LoginToken: TypeConstructorDescription {
|
||||
case loginToken(expires: Int32, token: Buffer)
|
||||
case loginTokenMigrateTo(dcId: Int32, token: Buffer)
|
||||
case loginTokenSuccess(authorization: Api.auth.Authorization)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .loginToken(let expires, let token):
|
||||
if boxed {
|
||||
buffer.appendInt32(1654593920)
|
||||
}
|
||||
serializeInt32(expires, buffer: buffer, boxed: false)
|
||||
serializeBytes(token, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .loginTokenMigrateTo(let dcId, let token):
|
||||
if boxed {
|
||||
buffer.appendInt32(110008598)
|
||||
}
|
||||
serializeInt32(dcId, buffer: buffer, boxed: false)
|
||||
serializeBytes(token, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .loginTokenSuccess(let authorization):
|
||||
if boxed {
|
||||
buffer.appendInt32(957176926)
|
||||
}
|
||||
authorization.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .loginToken(let expires, let token):
|
||||
return ("loginToken", [("expires", expires), ("token", token)])
|
||||
case .loginTokenMigrateTo(let dcId, let token):
|
||||
return ("loginTokenMigrateTo", [("dcId", dcId), ("token", token)])
|
||||
case .loginTokenSuccess(let authorization):
|
||||
return ("loginTokenSuccess", [("authorization", authorization)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_loginToken(_ reader: BufferReader) -> LoginToken? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Buffer?
|
||||
_2 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.auth.LoginToken.loginToken(expires: _1!, token: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_loginTokenMigrateTo(_ reader: BufferReader) -> LoginToken? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Buffer?
|
||||
_2 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.auth.LoginToken.loginTokenMigrateTo(dcId: _1!, token: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_loginTokenSuccess(_ reader: BufferReader) -> LoginToken? {
|
||||
var _1: Api.auth.Authorization?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.auth.Authorization
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.auth.LoginToken.loginTokenSuccess(authorization: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum Authorization: TypeConstructorDescription {
|
||||
case authorization(flags: Int32, tmpSessions: Int32?, user: Api.User)
|
||||
case authorizationSignUpRequired(flags: Int32, termsOfService: Api.help.TermsOfService?)
|
||||
@ -775,6 +859,76 @@ public struct auth {
|
||||
return Api.auth.CodeType.codeTypeFlashCall
|
||||
}
|
||||
|
||||
}
|
||||
public enum LoginTokenInfo: TypeConstructorDescription {
|
||||
case loginTokenInfo(dcId: Int32, authKeyId: Int64, deviceModel: String, platform: String, systemVersion: String, apiId: Int32, appName: String, appVersion: String, ip: String, region: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .loginTokenInfo(let dcId, let authKeyId, let deviceModel, let platform, let systemVersion, let apiId, let appName, let appVersion, let ip, let region):
|
||||
if boxed {
|
||||
buffer.appendInt32(1375940666)
|
||||
}
|
||||
serializeInt32(dcId, buffer: buffer, boxed: false)
|
||||
serializeInt64(authKeyId, buffer: buffer, boxed: false)
|
||||
serializeString(deviceModel, buffer: buffer, boxed: false)
|
||||
serializeString(platform, buffer: buffer, boxed: false)
|
||||
serializeString(systemVersion, buffer: buffer, boxed: false)
|
||||
serializeInt32(apiId, buffer: buffer, boxed: false)
|
||||
serializeString(appName, buffer: buffer, boxed: false)
|
||||
serializeString(appVersion, buffer: buffer, boxed: false)
|
||||
serializeString(ip, buffer: buffer, boxed: false)
|
||||
serializeString(region, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .loginTokenInfo(let dcId, let authKeyId, let deviceModel, let platform, let systemVersion, let apiId, let appName, let appVersion, let ip, let region):
|
||||
return ("loginTokenInfo", [("dcId", dcId), ("authKeyId", authKeyId), ("deviceModel", deviceModel), ("platform", platform), ("systemVersion", systemVersion), ("apiId", apiId), ("appName", appName), ("appVersion", appVersion), ("ip", ip), ("region", region)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_loginTokenInfo(_ reader: BufferReader) -> LoginTokenInfo? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
var _5: String?
|
||||
_5 = parseString(reader)
|
||||
var _6: Int32?
|
||||
_6 = reader.readInt32()
|
||||
var _7: String?
|
||||
_7 = parseString(reader)
|
||||
var _8: String?
|
||||
_8 = parseString(reader)
|
||||
var _9: String?
|
||||
_9 = parseString(reader)
|
||||
var _10: String?
|
||||
_10 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
let _c9 = _9 != nil
|
||||
let _c10 = _10 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
|
||||
return Api.auth.LoginTokenInfo.loginTokenInfo(dcId: _1!, authKeyId: _2!, deviceModel: _3!, platform: _4!, systemVersion: _5!, apiId: _6!, appName: _7!, appVersion: _8!, ip: _9!, region: _10!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum SentCodeType: TypeConstructorDescription {
|
||||
case sentCodeTypeApp(length: Int32)
|
||||
|
@ -4139,6 +4139,63 @@ public extension Api {
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func exportLoginToken(apiId: Int32, apiHash: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.LoginToken>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(434003159)
|
||||
serializeInt32(apiId, buffer: buffer, boxed: false)
|
||||
serializeString(apiHash, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "auth.exportLoginToken", parameters: [("apiId", apiId), ("apiHash", apiHash)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.LoginToken? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.auth.LoginToken?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.auth.LoginToken
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func importLoginToken(token: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.LoginToken>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1783866140)
|
||||
serializeBytes(token, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "auth.importLoginToken", parameters: [("token", token)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.LoginToken? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.auth.LoginToken?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.auth.LoginToken
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func acceptLoginToken(token: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1122447801)
|
||||
serializeBytes(token, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "auth.acceptLoginToken", parameters: [("token", token)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func checkLoginToken(token: Buffer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.LoginTokenInfo>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(2102383792)
|
||||
serializeBytes(token, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "auth.checkLoginToken", parameters: [("token", token)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.LoginTokenInfo? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.auth.LoginTokenInfo?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.auth.LoginTokenInfo
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public struct bots {
|
||||
public static func sendCustomRequest(customMethod: String, params: Api.DataJSON) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.DataJSON>) {
|
||||
|
144
submodules/TelegramCore/Sources/AuthTransfer.swift
Normal file
144
submodules/TelegramCore/Sources/AuthTransfer.swift
Normal file
@ -0,0 +1,144 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
import TelegramApi
|
||||
import SyncCore
|
||||
import SwiftSignalKit
|
||||
|
||||
public struct AuthTransferExportedToken {
|
||||
public let value: Data
|
||||
public let validUntil: Int32
|
||||
}
|
||||
|
||||
public struct AuthTransferTokenInfo {
|
||||
public let datacenterId: Int32
|
||||
public let authKeyId: Int64
|
||||
public let deviceModel: String
|
||||
public let platform: String
|
||||
public let systemVersion: String
|
||||
public let apiId: Int32
|
||||
public let appName: String
|
||||
public let appVersion: String
|
||||
public let ip: String
|
||||
public let region: String
|
||||
}
|
||||
|
||||
public enum ExportAuthTransferTokenError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public enum ExportAuthTransferTokenResult {
|
||||
case displayToken(AuthTransferExportedToken)
|
||||
case changeAccountAndRetry(UnauthorizedAccount)
|
||||
case loggedIn
|
||||
}
|
||||
|
||||
public func exportAuthTransferToken(accountManager: AccountManager, account: UnauthorizedAccount, syncContacts: Bool) -> Signal<ExportAuthTransferTokenResult, ExportAuthTransferTokenError> {
|
||||
return account.network.request(Api.functions.auth.exportLoginToken(apiId: account.networkArguments.apiId, apiHash: account.networkArguments.apiHash))
|
||||
|> mapError { _ -> ExportAuthTransferTokenError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<ExportAuthTransferTokenResult, ExportAuthTransferTokenError> in
|
||||
switch result {
|
||||
case let .loginToken(expires, token):
|
||||
return .single(.displayToken(AuthTransferExportedToken(value: token.makeData(), validUntil: expires)))
|
||||
case let .loginTokenMigrateTo(dcId, token):
|
||||
let updatedAccount = account.changedMasterDatacenterId(accountManager: accountManager, masterDatacenterId: dcId)
|
||||
return updatedAccount
|
||||
|> castError(ExportAuthTransferTokenError.self)
|
||||
|> mapToSignal { updatedAccount -> Signal<ExportAuthTransferTokenResult, ExportAuthTransferTokenError> in
|
||||
return updatedAccount.network.request(Api.functions.auth.importLoginToken(token: token))
|
||||
|> mapError { _ -> ExportAuthTransferTokenError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<ExportAuthTransferTokenResult, ExportAuthTransferTokenError> in
|
||||
switch result {
|
||||
case let .loginTokenSuccess(authorization):
|
||||
switch authorization {
|
||||
case let .authorization(_, _, user):
|
||||
return updatedAccount.postbox.transaction { transaction -> Signal<ExportAuthTransferTokenResult, ExportAuthTransferTokenError> in
|
||||
let user = TelegramUser(user: user)
|
||||
let state = AuthorizedAccountState(isTestingEnvironment: updatedAccount.testingEnvironment, masterDatacenterId: updatedAccount.masterDatacenterId, peerId: user.id, state: nil)
|
||||
initializedAppSettingsAfterLogin(transaction: transaction, appVersion: updatedAccount.networkArguments.appVersion, syncContacts: syncContacts)
|
||||
transaction.setState(state)
|
||||
return accountManager.transaction { transaction -> ExportAuthTransferTokenResult in
|
||||
switchToAuthorizedAccount(transaction: transaction, account: updatedAccount)
|
||||
return .loggedIn
|
||||
}
|
||||
|> castError(ExportAuthTransferTokenError.self)
|
||||
}
|
||||
|> castError(ExportAuthTransferTokenError.self)
|
||||
|> switchToLatest
|
||||
default:
|
||||
return .fail(.generic)
|
||||
}
|
||||
default:
|
||||
return .single(.changeAccountAndRetry(updatedAccount))
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .loginTokenSuccess(authorization):
|
||||
switch authorization {
|
||||
case let .authorization(_, _, user):
|
||||
return account.postbox.transaction { transaction -> Signal<ExportAuthTransferTokenResult, ExportAuthTransferTokenError> in
|
||||
let user = TelegramUser(user: user)
|
||||
let state = AuthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, peerId: user.id, state: nil)
|
||||
initializedAppSettingsAfterLogin(transaction: transaction, appVersion: account.networkArguments.appVersion, syncContacts: syncContacts)
|
||||
transaction.setState(state)
|
||||
return accountManager.transaction { transaction -> ExportAuthTransferTokenResult in
|
||||
switchToAuthorizedAccount(transaction: transaction, account: account)
|
||||
return .loggedIn
|
||||
}
|
||||
|> castError(ExportAuthTransferTokenError.self)
|
||||
}
|
||||
|> castError(ExportAuthTransferTokenError.self)
|
||||
|> switchToLatest
|
||||
case let .authorizationSignUpRequired:
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum GetAuthTransferTokenInfoError {
|
||||
case generic
|
||||
case invalid
|
||||
case expired
|
||||
case alreadyAccepted
|
||||
}
|
||||
|
||||
public func getAuthTransferTokenInfo(network: Network, token: Data) -> Signal<AuthTransferTokenInfo, GetAuthTransferTokenInfoError> {
|
||||
return network.request(Api.functions.auth.checkLoginToken(token: Buffer(data: token)))
|
||||
|> mapError { error -> GetAuthTransferTokenInfoError in
|
||||
switch error.errorDescription {
|
||||
case "AUTH_TOKEN_INVALID":
|
||||
return .invalid
|
||||
case "AUTH_TOKEN_EXPIRED":
|
||||
return .expired
|
||||
case "AUTH_TOKEN_ALREADY_ACCEPTED":
|
||||
return .alreadyAccepted
|
||||
default:
|
||||
return .generic
|
||||
}
|
||||
}
|
||||
|> map { result -> AuthTransferTokenInfo in
|
||||
switch result {
|
||||
case let .loginTokenInfo(dcId, authKeyId, deviceModel, platform, systemVersion, apiId, appName, appVersion, ip, region):
|
||||
return AuthTransferTokenInfo(datacenterId: dcId, authKeyId: authKeyId, deviceModel: deviceModel, platform: platform, systemVersion: systemVersion, apiId: apiId, appName: appName, appVersion: appVersion, ip: ip, region: region)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum ApproveAuthTransferTokenError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public func approveAuthTransferToken(account: Account, token: Data) -> Signal<Never, ApproveAuthTransferTokenError> {
|
||||
return account.network.request(Api.functions.auth.acceptLoginToken(token: Buffer(data: token)))
|
||||
|> mapError { _ -> ApproveAuthTransferTokenError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { updates -> Signal<Never, ApproveAuthTransferTokenError> in
|
||||
account.stateManager.addUpdates(updates)
|
||||
return .complete()
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ public enum AuthorizationCodeRequestError {
|
||||
case timeout
|
||||
}
|
||||
|
||||
private func switchToAuthorizedAccount(transaction: AccountManagerModifier, account: UnauthorizedAccount) {
|
||||
func switchToAuthorizedAccount(transaction: AccountManagerModifier, account: UnauthorizedAccount) {
|
||||
let nextSortOrder = (transaction.getRecords().map({ record -> Int32 in
|
||||
for attribute in record.attributes {
|
||||
if let attribute = attribute as? AccountSortOrderAttribute {
|
||||
|
@ -399,6 +399,7 @@ func networkUsageStats(basePath: String, reset: ResetNetworkUsageStats) -> Signa
|
||||
|
||||
public struct NetworkInitializationArguments {
|
||||
public let apiId: Int32
|
||||
public let apiHash: String
|
||||
public let languagesCategory: String
|
||||
public let appVersion: String
|
||||
public let voipMaxLayer: Int32
|
||||
@ -406,8 +407,9 @@ public struct NetworkInitializationArguments {
|
||||
public let autolockDeadine: Signal<Int32?, NoError>
|
||||
public let encryptionProvider: EncryptionProvider
|
||||
|
||||
public init(apiId: Int32, languagesCategory: String, appVersion: String, voipMaxLayer: Int32, appData: Signal<Data?, NoError>, autolockDeadine: Signal<Int32?, NoError>, encryptionProvider: EncryptionProvider) {
|
||||
public init(apiId: Int32, apiHash: String, languagesCategory: String, appVersion: String, voipMaxLayer: Int32, appData: Signal<Data?, NoError>, autolockDeadine: Signal<Int32?, NoError>, encryptionProvider: EncryptionProvider) {
|
||||
self.apiId = apiId
|
||||
self.apiHash = apiHash
|
||||
self.languagesCategory = languagesCategory
|
||||
self.appVersion = appVersion
|
||||
self.voipMaxLayer = voipMaxLayer
|
||||
|
@ -370,9 +370,10 @@ final class SharedApplicationContext {
|
||||
let signatureDict = BuildConfigExtra.signatureDict()
|
||||
|
||||
let apiId: Int32 = buildConfig.apiId
|
||||
let apiHash: String = buildConfig.apiHash
|
||||
let languagesCategory = "ios"
|
||||
|
||||
let networkArguments = NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: PresentationCallManagerImpl.voipMaxLayer, appData: self.deviceToken.get()
|
||||
let networkArguments = NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: PresentationCallManagerImpl.voipMaxLayer, appData: self.deviceToken.get()
|
||||
|> map { token in
|
||||
let data = buildConfig.bundleData(withAppToken: token, signatureDict: signatureDict)
|
||||
if let data = data, let jsonString = String(data: data, encoding: .utf8) {
|
||||
|
@ -144,7 +144,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
if let currentController = currentController {
|
||||
controller = currentController
|
||||
} else {
|
||||
controller = AuthorizationSequencePhoneEntryController(sharedContext: self.sharedContext, isTestingEnvironment: self.account.testingEnvironment, otherAccountPhoneNumbers: self.otherAccountPhoneNumbers, network: self.account.network, presentationData: self.presentationData, openUrl: { [weak self] url in
|
||||
controller = AuthorizationSequencePhoneEntryController(sharedContext: self.sharedContext, account: self.account, isTestingEnvironment: self.account.testingEnvironment, otherAccountPhoneNumbers: self.otherAccountPhoneNumbers, network: self.account.network, presentationData: self.presentationData, openUrl: { [weak self] url in
|
||||
self?.openUrl(url)
|
||||
}, back: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
@ -160,6 +160,12 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
}).start()
|
||||
}
|
||||
})
|
||||
controller.accountUpdated = { [weak self] updatedAccount in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.account = updatedAccount
|
||||
}
|
||||
controller.loginWithNumber = { [weak self, weak controller] number, syncContacts in
|
||||
if let strongSelf = self {
|
||||
controller?.inProgress = true
|
||||
|
@ -19,6 +19,7 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
||||
}
|
||||
|
||||
private let sharedContext: SharedAccountContext
|
||||
private var account: UnauthorizedAccount
|
||||
private let isTestingEnvironment: Bool
|
||||
private let otherAccountPhoneNumbers: ((String, AccountRecordId, Bool)?, [(String, AccountRecordId, Bool)])
|
||||
private let network: Network
|
||||
@ -41,13 +42,15 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
||||
}
|
||||
}
|
||||
var loginWithNumber: ((String, Bool) -> Void)?
|
||||
var accountUpdated: ((UnauthorizedAccount) -> Void)?
|
||||
|
||||
private let termsDisposable = MetaDisposable()
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
init(sharedContext: SharedAccountContext, isTestingEnvironment: Bool, otherAccountPhoneNumbers: ((String, AccountRecordId, Bool)?, [(String, AccountRecordId, Bool)]), network: Network, presentationData: PresentationData, openUrl: @escaping (String) -> Void, back: @escaping () -> Void) {
|
||||
init(sharedContext: SharedAccountContext, account: UnauthorizedAccount, isTestingEnvironment: Bool, otherAccountPhoneNumbers: ((String, AccountRecordId, Bool)?, [(String, AccountRecordId, Bool)]), network: Network, presentationData: PresentationData, openUrl: @escaping (String) -> Void, back: @escaping () -> Void) {
|
||||
self.sharedContext = sharedContext
|
||||
self.account = account
|
||||
self.isTestingEnvironment = isTestingEnvironment
|
||||
self.otherAccountPhoneNumbers = otherAccountPhoneNumbers
|
||||
self.network = network
|
||||
@ -95,13 +98,21 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = AuthorizationSequencePhoneEntryControllerNode(strings: self.presentationData.strings, theme: self.presentationData.theme, debugAction: { [weak self] in
|
||||
self.displayNode = AuthorizationSequencePhoneEntryControllerNode(accountManager: self.sharedContext.accountManager, account: self.account, strings: self.presentationData.strings, theme: self.presentationData.theme, debugAction: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.view.endEditing(true)
|
||||
self?.present(debugController(sharedContext: strongSelf.sharedContext, context: nil, modal: true), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}, hasOtherAccounts: self.otherAccountPhoneNumbers.0 != nil)
|
||||
}, hasOtherAccounts: self.otherAccountPhoneNumbers.0 != nil)
|
||||
self.controllerNode.accountUpdated = { [weak self] account in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.account = account
|
||||
strongSelf.accountUpdated?(account)
|
||||
}
|
||||
|
||||
if let (code, name, number) = self.currentData {
|
||||
self.controllerNode.codeAndNumber = (code, name, number)
|
||||
}
|
||||
|
@ -8,6 +8,9 @@ import TelegramPresentationData
|
||||
import PhoneInputNode
|
||||
import CountrySelectionUI
|
||||
import AuthorizationUI
|
||||
import QrCode
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
|
||||
private func emojiFlagForISOCountryCode(_ countryCode: NSString) -> String {
|
||||
if countryCode.length != 2 {
|
||||
@ -201,6 +204,8 @@ private final class ContactSyncNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
private let accountManager: AccountManager
|
||||
private var account: UnauthorizedAccount
|
||||
private let strings: PresentationStrings
|
||||
private let theme: PresentationTheme
|
||||
private let hasOtherAccounts: Bool
|
||||
@ -210,6 +215,10 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
private let phoneAndCountryNode: PhoneAndCountryNode
|
||||
private let contactSyncNode: ContactSyncNode
|
||||
|
||||
private var qrNode: ASImageNode?
|
||||
private let exportTokenDisposable = MetaDisposable()
|
||||
var accountUpdated: ((UnauthorizedAccount) -> Void)?
|
||||
|
||||
private let debugAction: () -> Void
|
||||
|
||||
var currentNumber: String {
|
||||
@ -245,7 +254,10 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
init(strings: PresentationStrings, theme: PresentationTheme, debugAction: @escaping () -> Void, hasOtherAccounts: Bool) {
|
||||
init(accountManager: AccountManager, account: UnauthorizedAccount, strings: PresentationStrings, theme: PresentationTheme, debugAction: @escaping () -> Void, hasOtherAccounts: Bool) {
|
||||
self.accountManager = accountManager
|
||||
self.account = account
|
||||
|
||||
self.strings = strings
|
||||
self.theme = theme
|
||||
self.debugAction = debugAction
|
||||
@ -257,7 +269,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
self.titleNode.attributedText = NSAttributedString(string: strings.Login_PhoneTitle, font: Font.light(30.0), textColor: theme.list.itemPrimaryTextColor)
|
||||
|
||||
self.noticeNode = ASTextNode()
|
||||
self.noticeNode.isUserInteractionEnabled = false
|
||||
self.noticeNode.isUserInteractionEnabled = true
|
||||
self.noticeNode.displaysAsynchronously = false
|
||||
self.noticeNode.attributedText = NSAttributedString(string: strings.Login_PhoneAndCountryHelp, font: Font.regular(16.0), textColor: theme.list.itemPrimaryTextColor, paragraphAlignment: .center)
|
||||
|
||||
@ -287,10 +299,15 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.exportTokenDisposable.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.titleNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.debugTap(_:))))
|
||||
self.noticeNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.debugQrTap(_:))))
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
@ -356,4 +373,62 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func debugQrTap(_ recognizer: UITapGestureRecognizer) {
|
||||
if self.qrNode == nil {
|
||||
let qrNode = ASImageNode()
|
||||
qrNode.frame = CGRect(origin: CGPoint(x: 16.0, y: 64.0 + 16.0), size: CGSize(width: 200.0, height: 200.0))
|
||||
self.qrNode = qrNode
|
||||
self.addSubnode(qrNode)
|
||||
|
||||
self.refreshQrToken()
|
||||
}
|
||||
}
|
||||
|
||||
private func refreshQrToken() {
|
||||
let tokenSignal = exportAuthTransferToken(accountManager: self.accountManager, account: self.account, syncContacts: true)
|
||||
|
||||
self.exportTokenDisposable.set((tokenSignal
|
||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
switch result {
|
||||
case let .displayToken(token):
|
||||
var tokenString = token.value.base64EncodedString()
|
||||
print("export token \(tokenString)")
|
||||
tokenString = tokenString.replacingOccurrences(of: "+", with: "-")
|
||||
tokenString = tokenString.replacingOccurrences(of: "/", with: "_")
|
||||
let urlString = "tg://login?token=\(tokenString)"
|
||||
let _ = (qrCode(string: urlString, color: .black, backgroundColor: .white, icon: .none)
|
||||
|> deliverOnMainQueue).start(next: { _, generate in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let context = generate(TransformImageArguments(corners: ImageCorners(), imageSize: CGSize(width: 200.0, height: 200.0), boundingSize: CGSize(width: 200.0, height: 200.0), intrinsicInsets: UIEdgeInsets()))
|
||||
if let image = context?.generateImage() {
|
||||
strongSelf.qrNode?.image = image
|
||||
}
|
||||
})
|
||||
|
||||
let timestamp = Int32(Date().timeIntervalSince1970)
|
||||
let timeout = max(5, token.validUntil - timestamp)
|
||||
strongSelf.exportTokenDisposable.set((Signal<Never, NoError>.complete()
|
||||
|> delay(Double(timeout), queue: .mainQueue())).start(completed: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.refreshQrToken()
|
||||
}))
|
||||
case let .changeAccountAndRetry(account):
|
||||
strongSelf.exportTokenDisposable.set(nil)
|
||||
strongSelf.account = account
|
||||
strongSelf.accountUpdated?(account)
|
||||
strongSelf.refreshQrToken()
|
||||
case .loggedIn:
|
||||
strongSelf.exportTokenDisposable.set(nil)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -127,6 +127,7 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
|
||||
super.didLoad()
|
||||
|
||||
self.listView.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
self.listView.view.disablesInteractiveKeyboardGestureRecognizer = true
|
||||
self.view.addGestureRecognizer(PeekControllerGestureRecognizer(contentAtPoint: { [weak self] point in
|
||||
if let strongSelf = self {
|
||||
let convertedPoint = strongSelf.listView.view.convert(point, from: strongSelf.view)
|
||||
|
@ -55,14 +55,16 @@ private func parseFileLocationResource(_ dict: [AnyHashable: Any]) -> TelegramMe
|
||||
public struct NotificationViewControllerInitializationData {
|
||||
public let appGroupPath: String
|
||||
public let apiId: Int32
|
||||
public let apiHash: String
|
||||
public let languagesCategory: String
|
||||
public let encryptionParameters: (Data, Data)
|
||||
public let appVersion: String
|
||||
public let bundleData: Data?
|
||||
|
||||
public init(appGroupPath: String, apiId: Int32, languagesCategory: String, encryptionParameters: (Data, Data), appVersion: String, bundleData: Data?) {
|
||||
public init(appGroupPath: String, apiId: Int32, apiHash: String, languagesCategory: String, encryptionParameters: (Data, Data), appVersion: String, bundleData: Data?) {
|
||||
self.appGroupPath = appGroupPath
|
||||
self.apiId = apiId
|
||||
self.apiHash = apiHash
|
||||
self.languagesCategory = languagesCategory
|
||||
self.encryptionParameters = encryptionParameters
|
||||
self.appVersion = appVersion
|
||||
@ -152,7 +154,7 @@ public final class NotificationViewControllerImpl {
|
||||
return nil
|
||||
})
|
||||
|
||||
sharedAccountContext = SharedAccountContextImpl(mainWindow: nil, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider()), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
|
||||
sharedAccountContext = SharedAccountContextImpl(mainWindow: nil, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider()), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
|
||||
|
||||
presentationDataPromise.set(sharedAccountContext!.presentationData)
|
||||
}
|
||||
|
@ -48,14 +48,16 @@ private enum ShareAuthorizationError {
|
||||
public struct ShareRootControllerInitializationData {
|
||||
public let appGroupPath: String
|
||||
public let apiId: Int32
|
||||
public let apiHash: String
|
||||
public let languagesCategory: String
|
||||
public let encryptionParameters: (Data, Data)
|
||||
public let appVersion: String
|
||||
public let bundleData: Data?
|
||||
|
||||
public init(appGroupPath: String, apiId: Int32, languagesCategory: String, encryptionParameters: (Data, Data), appVersion: String, bundleData: Data?) {
|
||||
public init(appGroupPath: String, apiId: Int32, apiHash: String, languagesCategory: String, encryptionParameters: (Data, Data), appVersion: String, bundleData: Data?) {
|
||||
self.appGroupPath = appGroupPath
|
||||
self.apiId = apiId
|
||||
self.apiHash = apiHash
|
||||
self.languagesCategory = languagesCategory
|
||||
self.encryptionParameters = encryptionParameters
|
||||
self.appVersion = appVersion
|
||||
@ -190,7 +192,7 @@ public class ShareRootControllerImpl {
|
||||
return nil
|
||||
})
|
||||
|
||||
let sharedContext = SharedAccountContextImpl(mainWindow: nil, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider()), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
|
||||
let sharedContext = SharedAccountContextImpl(mainWindow: nil, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider()), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
|
||||
presentationDataPromise.set(sharedContext.presentationData)
|
||||
internalContext = InternalContext(sharedContext: sharedContext)
|
||||
globalInternalContext = internalContext
|
||||
|
Loading…
x
Reference in New Issue
Block a user