From 9f686dc0cb4fd9f0b8b083fb9974b4f7c8eb8b2e Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 22 Nov 2019 03:18:45 +0400 Subject: [PATCH] Initial QR Auth UI --- submodules/AuthTransferUI/BUCK | 3 + .../AuthTransferConfirmationScreen.swift | 166 ++++++++++++++++++ .../Sources/AuthTransferScanScreen.swift | 99 ++++++++++- .../Sources/SettingsController.swift | 36 ++-- submodules/TelegramCore/Sources/Account.swift | 12 ++ .../UnauthorizedAccountStateManager.swift | 86 +++++++++ .../TransferAuthLaptop.imageset/Contents.json | 12 ++ .../EmojiComputer.pdf | Bin 0 -> 22801 bytes ...tionSequencePhoneEntryControllerNode.swift | 11 ++ 9 files changed, 397 insertions(+), 28 deletions(-) create mode 100644 submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift create mode 100644 submodules/TelegramCore/Sources/UnauthorizedAccountStateManager.swift create mode 100644 submodules/TelegramUI/Images.xcassets/Settings/TransferAuthLaptop.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Settings/TransferAuthLaptop.imageset/EmojiComputer.pdf diff --git a/submodules/AuthTransferUI/BUCK b/submodules/AuthTransferUI/BUCK index 494377d70f..c496f26116 100644 --- a/submodules/AuthTransferUI/BUCK +++ b/submodules/AuthTransferUI/BUCK @@ -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", diff --git a/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift b/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift new file mode 100644 index 0000000000..8282fc23a2 --- /dev/null +++ b/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift @@ -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) + } +} diff --git a/submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift b/submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift index 05bf3e34ba..7ac48824ca 100644 --- a/submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift +++ b/submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift @@ -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(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() { diff --git a/submodules/SettingsUI/Sources/SettingsController.swift b/submodules/SettingsUI/Sources/SettingsController.swift index 5292743553..dcd5241e6d 100644 --- a/submodules/SettingsUI/Sources/SettingsController.swift +++ b/submodules/SettingsUI/Sources/SettingsController.swift @@ -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 diff --git a/submodules/TelegramCore/Sources/Account.swift b/submodules/TelegramCore/Sources/Account.swift index f1fecc8907..55c03cea20 100644 --- a/submodules/TelegramCore/Sources/Account.swift +++ b/submodules/TelegramCore/Sources/Account.swift @@ -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() + public var updateLoginTokenEvents: Signal { + 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 { diff --git a/submodules/TelegramCore/Sources/UnauthorizedAccountStateManager.swift b/submodules/TelegramCore/Sources/UnauthorizedAccountStateManager.swift new file mode 100644 index 0000000000..19a87d91f3 --- /dev/null +++ b/submodules/TelegramCore/Sources/UnauthorizedAccountStateManager.swift @@ -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) + } + } + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Settings/TransferAuthLaptop.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/TransferAuthLaptop.imageset/Contents.json new file mode 100644 index 0000000000..c55acc952e --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/TransferAuthLaptop.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "EmojiComputer.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/TransferAuthLaptop.imageset/EmojiComputer.pdf b/submodules/TelegramUI/Images.xcassets/Settings/TransferAuthLaptop.imageset/EmojiComputer.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1bf7b79ae073c613792885866ddee46e680624f7 GIT binary patch literal 22801 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/TelegramUI/AuthorizationSequencePhoneEntryControllerNode.swift b/submodules/TelegramUI/TelegramUI/AuthorizationSequencePhoneEntryControllerNode.swift index 0aec588b88..54bd615a18 100644 --- a/submodules/TelegramUI/TelegramUI/AuthorizationSequencePhoneEntryControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/AuthorizationSequencePhoneEntryControllerNode.swift @@ -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)