diff --git a/submodules/AuthTransferUI/BUILD b/submodules/AuthTransferUI/BUILD deleted file mode 100644 index 1dd094a4db..0000000000 --- a/submodules/AuthTransferUI/BUILD +++ /dev/null @@ -1,37 +0,0 @@ -load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") - -swift_library( - name = "AuthTransferUI", - module_name = "AuthTransferUI", - srcs = glob([ - "Sources/**/*.swift", - ]), - copts = [ - "-warnings-as-errors", - ], - deps = [ - "//submodules/TelegramCore:TelegramCore", - "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", - "//submodules/AsyncDisplayKit:AsyncDisplayKit", - "//submodules/Display:Display", - "//submodules/TelegramPresentationData:TelegramPresentationData", - "//submodules/AccountContext:AccountContext", - "//submodules/QrCode:QrCode", - "//submodules/Camera:Camera", - "//submodules/GlassButtonNode:GlassButtonNode", - "//submodules/AlertUI:AlertUI", - "//submodules/AppBundle:AppBundle", - "//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode", - "//submodules/AnimatedStickerNode:AnimatedStickerNode", - "//submodules/AnimationUI:AnimationUI", - "//submodules/PresentationDataUtils:PresentationDataUtils", - "//submodules/DeviceAccess:DeviceAccess", - "//submodules/UndoUI:UndoUI", - "//submodules/TextFormat:TextFormat", - "//submodules/Markdown:Markdown", - "//submodules/QrCodeUI:QrCodeUI", - ], - visibility = [ - "//visibility:public", - ], -) diff --git a/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift b/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift deleted file mode 100644 index 9588ef451e..0000000000 --- a/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift +++ /dev/null @@ -1,343 +0,0 @@ -import Foundation -import UIKit -import AppBundle -import AsyncDisplayKit -import Display -import SolidRoundedButtonNode -import SwiftSignalKit -import AnimationUI -import AccountContext -import TelegramPresentationData -import PresentationDataUtils -import TelegramCore -import Markdown -import DeviceAccess -import QrCodeUI - -private func transformedWithTheme(data: Data, theme: PresentationTheme) -> Data { - return transformedWithColors(data: data, colors: [(UIColor(rgb: 0x333333), theme.list.itemPrimaryTextColor.mixedWith(.white, alpha: 0.2)), (UIColor(rgb: 0xFFFFFF), theme.list.plainBackgroundColor), (UIColor(rgb: 0x50A7EA), theme.list.itemAccentColor), (UIColor(rgb: 0x212121), theme.list.plainBackgroundColor)]) -} - -public final class AuthDataTransferSplashScreen: ViewController { - private let context: AccountContext - private let activeSessionsContext: ActiveSessionsContext - private var presentationData: PresentationData - - public init(context: AccountContext, activeSessionsContext: ActiveSessionsContext) { - self.context = context - self.activeSessionsContext = activeSessionsContext - - self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - - let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme) - let navigationBarTheme = NavigationBarTheme(buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) - - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) - - self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationPresentation = .modalInLargeLayout - self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) - self.navigationBar?.intrinsicCanTransitionInline = false - - self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) - } - - required init(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - deinit { - } - - override public func loadDisplayNode() { - self.displayNode = AuthDataTransferSplashScreenNode(context: self.context, presentationData: self.presentationData, action: { [weak self] in - guard let strongSelf = self else { - return - } - - DeviceAccess.authorizeAccess(to: .camera(.qrCode), presentationData: strongSelf.presentationData, present: { c, a in - guard let strongSelf = self else { - return - } - c.presentationArguments = a - strongSelf.context.sharedContext.mainWindow?.present(c, on: .root) - }, openSettings: { - self?.context.sharedContext.applicationBindings.openSettings() - }, { granted in - guard let strongSelf = self else { - return - } - guard granted else { - return - } - (strongSelf.navigationController as? NavigationController)?.replaceController(strongSelf, with: QrCodeScanScreen(context: strongSelf.context, subject: .authTransfer(activeSessionsContext: strongSelf.activeSessionsContext)), animated: true) - }) - }) - - self.displayNodeDidLoad() - } - - override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { - super.containerLayoutUpdated(layout, transition: transition) - - (self.displayNode as! AuthDataTransferSplashScreenNode).containerLayoutUpdated(layout: layout, navigationHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) - } -} - -private final class AuthDataTransferSplashScreenNode: ViewControllerTracingNode { - private var presentationData: PresentationData - - private var animationSize: CGSize = CGSize() - private var animationOffset: CGPoint = CGPoint() - private let animationNode: AnimationNode? - private let titleNode: ImmediateTextNode - private let badgeBackgroundNodes: [ASImageNode] - private let badgeTextNodes: [ImmediateTextNode] - private let textNodes: [ImmediateTextNode] - let buttonNode: SolidRoundedButtonNode - - private let hierarchyTrackingNode: HierarchyTrackingNode - - var inProgress: Bool = false { - didSet { - self.buttonNode.isUserInteractionEnabled = !self.inProgress - self.buttonNode.alpha = self.inProgress ? 0.6 : 1.0 - } - } - - private var validLayout: ContainerViewLayout? - - init(context: AccountContext, presentationData: PresentationData, action: @escaping () -> Void) { - self.presentationData = presentationData - - if let url = getAppBundle().url(forResource: "anim_qr", withExtension: "json"), let data = try? Data(contentsOf: url) { - self.animationNode = AnimationNode(animationData: transformedWithTheme(data: data, theme: presentationData.theme)) - } else { - self.animationNode = nil - } - - let buttonText: String - - let badgeFont = Font.with(size: 13.0, design: .round, weight: .bold) - let textFont = Font.regular(16.0) - let textColor = self.presentationData.theme.list.itemPrimaryTextColor - - var badgeBackgroundNodes: [ASImageNode] = [] - var badgeTextNodes: [ImmediateTextNode] = [] - var textNodes: [ImmediateTextNode] = [] - - let badgeBackground = generateFilledCircleImage(diameter: 20.0, color: self.presentationData.theme.list.itemCheckColors.fillColor) - - for i in 0 ..< 3 { - let badgeBackgroundNode = ASImageNode() - badgeBackgroundNode.displaysAsynchronously = false - badgeBackgroundNode.displayWithoutProcessing = true - badgeBackgroundNode.image = badgeBackground - badgeBackgroundNodes.append(badgeBackgroundNode) - - let badgeTextNode = ImmediateTextNode() - badgeTextNode.displaysAsynchronously = false - badgeTextNode.attributedText = NSAttributedString(string: "\(i + 1)", font: badgeFont, textColor: self.presentationData.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .natural) - badgeTextNode.maximumNumberOfLines = 0 - badgeTextNode.lineSpacing = 0.1 - badgeTextNodes.append(badgeTextNode) - - let string: String - switch i { - case 0: - string = self.presentationData.strings.AuthSessions_AddDeviceIntro_Text1 - case 1: - string = self.presentationData.strings.AuthSessions_AddDeviceIntro_Text2 - default: - string = self.presentationData.strings.AuthSessions_AddDeviceIntro_Text3 - } - - let body = MarkdownAttributeSet(font: textFont, textColor: textColor) - let link = MarkdownAttributeSet(font: textFont, textColor: self.presentationData.theme.list.itemAccentColor, additionalAttributes: ["URL": true as NSNumber]) - - let text = parseMarkdownIntoAttributedString(string, attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in - return nil - })) - - let textNode = ImmediateTextNode() - textNode.displaysAsynchronously = false - textNode.attributedText = text - textNode.maximumNumberOfLines = 0 - textNode.lineSpacing = 0.1 - textNodes.append(textNode) - } - - self.badgeBackgroundNodes = badgeBackgroundNodes - self.badgeTextNodes = badgeTextNodes - self.textNodes = textNodes - - buttonText = self.presentationData.strings.AuthSessions_AddDeviceIntro_Action - - self.titleNode = ImmediateTextNode() - self.titleNode.displaysAsynchronously = false - self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.AuthSessions_AddDeviceIntro_Title, font: Font.bold(24.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor) - self.titleNode.maximumNumberOfLines = 0 - self.titleNode.textAlignment = .center - - self.buttonNode = SolidRoundedButtonNode(title: buttonText, theme: SolidRoundedButtonTheme(backgroundColor: self.presentationData.theme.list.itemCheckColors.fillColor, foregroundColor: self.presentationData.theme.list.itemCheckColors.foregroundColor), height: 50.0, cornerRadius: 10.0, gloss: false) - self.buttonNode.isHidden = buttonText.isEmpty - - var updateInHierarchy: ((Bool) -> Void)? - self.hierarchyTrackingNode = HierarchyTrackingNode({ value in - updateInHierarchy?(value) - }) - - super.init() - - self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor - - self.addSubnode(self.hierarchyTrackingNode) - - if let animationNode = self.animationNode { - self.addSubnode(animationNode) - } - self.addSubnode(self.titleNode) - - self.badgeBackgroundNodes.forEach(self.addSubnode) - self.badgeTextNodes.forEach(self.addSubnode) - self.textNodes.forEach(self.addSubnode) - - self.addSubnode(self.buttonNode) - - self.buttonNode.pressed = { - action() - } - - for textNode in self.textNodes { - textNode.linkHighlightColor = self.presentationData.theme.list.itemAccentColor.withAlphaComponent(0.5) - textNode.highlightAttributeAction = { attributes in - if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] { - return NSAttributedString.Key(rawValue: "URL") - } else { - return nil - } - } - textNode.tapAttributeAction = { attributes, _ in - if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] { - context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: "https://desktop.telegram.org", forceExternal: true, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {}) - } - } - } - - updateInHierarchy = { [weak self] value in - if value { - self?.animationNode?.play() - } else { - self?.animationNode?.reset() - } - } - } - - override func didLoad() { - super.didLoad() - } - - func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) { - let firstTime = self.validLayout == nil - self.validLayout = layout - - let sideInset: CGFloat = 22.0 - let textSideInset: CGFloat = 54.0 - let buttonSideInset: CGFloat = 16.0 - let titleSpacing: CGFloat = 25.0 - let buttonHeight: CGFloat = 50.0 - let buttonSpacing: CGFloat = 16.0 - let textSpacing: CGFloat = 25.0 - let badgeSize: CGFloat = 20.0 - - let animationFitSize = CGSize(width: min(500.0, layout.size.width - sideInset + 20.0), height: 500.0) - let animationSize = self.animationNode?.preferredSize()?.fitted(animationFitSize) ?? animationFitSize - let iconSize: CGSize = animationSize - let iconOffset = CGPoint() - - let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height)) - - var badgeTextSizes: [CGSize] = [] - var textSizes: [CGSize] = [] - var textContentHeight: CGFloat = 0.0 - for i in 0 ..< self.badgeTextNodes.count { - let badgeTextSize = self.badgeTextNodes[i].updateLayout(CGSize(width: 100.0, height: .greatestFiniteMagnitude)) - badgeTextSizes.append(badgeTextSize) - let textSize = self.textNodes[i].updateLayout(CGSize(width: layout.size.width - sideInset * 2.0 - 40.0, height: .greatestFiniteMagnitude)) - textSizes.append(textSize) - - if i != 0 { - textContentHeight += textSpacing - } - textContentHeight += textSize.height - } - - var contentHeight = iconSize.height + titleSize.height + titleSpacing + textContentHeight - - let bottomInset = layout.intrinsicInsets.bottom + 20.0 - let contentTopInset = navigationHeight - let contentBottomInset = bottomInset + buttonHeight + buttonSpacing - - let iconSpacing: CGFloat = max(20.0, min(61.0, layout.size.height - contentTopInset - contentBottomInset - contentHeight - 40.0)) - - contentHeight += iconSpacing - - var contentVerticalOrigin = contentTopInset + floor((layout.size.height - contentTopInset - contentBottomInset - contentHeight) / 2.0) - - let buttonWidth = layout.size.width - buttonSideInset * 2.0 - - let buttonFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - buttonWidth) / 2.0), y: layout.size.height - bottomInset - buttonHeight), size: CGSize(width: buttonWidth, height: buttonHeight)) - transition.updateFrame(node: self.buttonNode, frame: buttonFrame) - let _ = self.buttonNode.updateLayout(width: buttonFrame.width, transition: transition) - - let maxContentVerticalOrigin = buttonFrame.minY - 12.0 - contentHeight - - contentVerticalOrigin = min(contentVerticalOrigin, maxContentVerticalOrigin) - - var contentY = contentVerticalOrigin - let iconFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0) + self.animationOffset.x, y: contentY), size: iconSize).offsetBy(dx: iconOffset.x, dy: iconOffset.y) - contentY += iconSize.height + iconSpacing - if let animationNode = self.animationNode { - transition.updateFrameAdditive(node: animationNode, frame: iconFrame) - if iconFrame.minY < 0.0 { - transition.updateAlpha(node: animationNode, alpha: 0.0) - } else { - transition.updateAlpha(node: animationNode, alpha: 1.0) - } - } - - let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: contentY), size: titleSize) - transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame) - contentY += titleSize.height + titleSpacing - - for i in 0 ..< self.badgeTextNodes.count { - if i != 0 { - contentY += textSpacing - } - - let badgeTextSize = badgeTextSizes[i] - let textSize = textSizes[i] - - let textFrame = CGRect(origin: CGPoint(x: textSideInset, y: contentY), size: textSize) - transition.updateFrameAdditive(node: self.textNodes[i], frame: textFrame) - - let badgeFrame = CGRect(origin: CGPoint(x: sideInset, y: textFrame.minY), size: CGSize(width: badgeSize, height: badgeSize)) - transition.updateFrameAdditive(node: self.badgeBackgroundNodes[i], frame: badgeFrame) - - let badgeTextOffsetX: CGFloat - if i == 0 { - badgeTextOffsetX = 0.5 - } else { - badgeTextOffsetX = 1.0 - } - - transition.updateFrameAdditive(node: self.badgeTextNodes[i], frame: CGRect(origin: CGPoint(x: badgeFrame.minX + floor((badgeFrame.width - badgeTextSize.width) / 2.0) + badgeTextOffsetX, y: badgeFrame.minY + floor((badgeFrame.height - badgeTextSize.height) / 2.0) + 0.5), size: badgeTextSize)) - - contentY += textSize.height - } - - if firstTime { - self.animationNode?.play() - } - } -} diff --git a/submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift b/submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift deleted file mode 100644 index ff89bba3a6..0000000000 --- a/submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift +++ /dev/null @@ -1,544 +0,0 @@ -import Foundation -import UIKit -import AccountContext -import AsyncDisplayKit -import Display -import SwiftSignalKit -import Camera -import GlassButtonNode -import CoreImage -import AlertUI -import TelegramPresentationData -import TelegramCore -import UndoUI -import Markdown -import TextFormat - -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) - - let 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 let activeSessionsContext: ActiveSessionsContext - private var presentationData: PresentationData - - private var codeDisposable: Disposable? - private var inForegroundDisposable: Disposable? - private let approveDisposable = MetaDisposable() - - private var controllerNode: AuthTransferScanScreenNode { - return self.displayNode as! AuthTransferScanScreenNode - } - - public init(context: AccountContext, activeSessionsContext: ActiveSessionsContext) { - self.context = context - self.activeSessionsContext = activeSessionsContext - - self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - - let navigationBarTheme = NavigationBarTheme(buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, 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.Common_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) - }) - - #if DEBUG - self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Test", style: .plain, target: self, action: #selector(self.testPressed)) - #endif - } - - 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 testPressed() { - self.dismissWithSuccess(session: nil) - } - - private func dismissWithSuccess(session: RecentAccountSession?) { - if let navigationController = navigationController as? NavigationController { - let activeSessionsContext = self.activeSessionsContext - - self.present(UndoOverlayController(presentationData: self.presentationData, content: .actionSucceeded(title: self.presentationData.strings.AuthSessions_AddedDeviceTitle, text: session?.appName ?? "Telegram for macOS", cancel: self.presentationData.strings.AuthSessions_AddedDeviceTerminate), elevatedLayout: false, animateInAsReplacement: false, action: { value in - if value == .undo, let session = session { - let _ = activeSessionsContext.remove(hash: session.hash).start() - return true - } else { - return false - } - }), in: .window(.root)) - - var viewControllers = navigationController.viewControllers - viewControllers = viewControllers.filter { controller in - if controller is RecentSessionsController { - return false - } - if controller === self { - return false - } - return true - } - viewControllers.append(self.context.sharedContext.makeRecentSessionsController(context: self.context, activeSessionsContext: activeSessionsContext)) - navigationController.setViewControllers(viewControllers, animated: true) - } else { - self.dismiss() - } - } - - override public func loadDisplayNode() { - self.displayNode = AuthTransferScanScreenNode(context: self.context, presentationData: self.presentationData) - - self.displayNodeDidLoad() - - self.codeDisposable = ((self.displayNode as! AuthTransferScanScreenNode).focusedCode.get() - |> map { code -> String? in - return code?.message - } - |> distinctUntilChanged - |> mapToSignal { code -> Signal in - return .single(code) - |> delay(0.5, queue: Queue.mainQueue()) - }).start(next: { [weak self] code in - guard let strongSelf = self else { - return - } - guard let code = code else { - return - } - if let url = URL(string: code), let parsedToken = parseAuthTransferUrl(url) { - strongSelf.approveDisposable.set((approveAuthTransferToken(account: strongSelf.context.account, token: parsedToken, activeSessionsContext: strongSelf.activeSessionsContext) - |> deliverOnMainQueue).start(next: { session in - guard let strongSelf = self else { - return - } - strongSelf.controllerNode.codeWithError = nil - let activeSessionsContext = strongSelf.activeSessionsContext - Queue.mainQueue().after(1.5, { - activeSessionsContext.loadMore() - }) - strongSelf.dismissWithSuccess(session: session) - }, error: { _ in - guard let strongSelf = self else { - return - } - strongSelf.controllerNode.codeWithError = code - strongSelf.controllerNode.updateFocusedRect(nil) - })) - } - }) - } - - override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { - super.containerLayoutUpdated(layout, transition: transition) - - (self.displayNode as! AuthTransferScanScreenNode).containerLayoutUpdated(layout: layout, navigationHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) - } -} - -private final class AuthTransferScanScreenNode: ViewControllerTracingNode, UIScrollViewDelegate { - private let context: AccountContext - 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 centerDimNode: ASDisplayNode - private let frameNode: ASImageNode - private let torchButtonNode: GlassButtonNode - private let titleNode: ImmediateTextNode - private let textNode: ImmediateTextNode - private let errorTextNode: ImmediateTextNode - - private let camera: Camera - private let codeDisposable = MetaDisposable() - private var torchDisposable: Disposable? - - fileprivate let focusedCode = ValuePromise(ignoreRepeated: true) - private var focusedRect: CGRect? - - private var validLayout: (ContainerViewLayout, CGFloat)? - - var codeWithError: String? { - didSet { - if self.codeWithError != oldValue { - if self.codeWithError != nil { - self.errorTextNode.isHidden = false - } else { - self.errorTextNode.isHidden = true - } - } - } - } - - private var highlightViews: [UIVisualEffectView] = [] - - init(context: AccountContext, presentationData: PresentationData) { - self.context = context - 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.centerDimNode = ASDisplayNode() - self.centerDimNode.alpha = 0.0 - self.centerDimNode.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.AuthSessions_AddDevice_ScanTitle, font: Font.bold(32.0), textColor: .white) - self.titleNode.maximumNumberOfLines = 0 - self.titleNode.textAlignment = .center - - let textFont = Font.regular(17.0) - let boldFont = Font.bold(17.0) - - var text = presentationData.strings.AuthSessions_AddDevice_ScanInstallInfo - text = text.replacingOccurrences(of: " [", with: " [").replacingOccurrences(of: ") ", with: ") ") - - let attributedText = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: .white), bold: MarkdownAttributeSet(font: boldFont, textColor: .white), link: MarkdownAttributeSet(font: boldFont, textColor: .white), linkAttribute: { contents in - return (TelegramTextAttributes.URL, contents) - }))) - - self.textNode = ImmediateTextNode() - self.textNode.displaysAsynchronously = false - self.textNode.attributedText = attributedText - self.textNode.maximumNumberOfLines = 0 - self.textNode.textAlignment = .center - self.textNode.lineSpacing = 0.5 - - self.errorTextNode = ImmediateTextNode() - self.errorTextNode.displaysAsynchronously = false - self.errorTextNode.attributedText = NSAttributedString(string: presentationData.strings.AuthSessions_AddDevice_InvalidQRCode, font: Font.medium(16.0), textColor: .white) - self.errorTextNode.maximumNumberOfLines = 0 - self.errorTextNode.textAlignment = .center - self.errorTextNode.isHidden = true - - self.camera = Camera(configuration: .init(preset: .hd1920x1080, position: .back, audio: false)) - - super.init() - - self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor - - self.torchDisposable = (self.camera.hasTorch - |> deliverOnMainQueue).start(next: { [weak self] hasTorch in - if let strongSelf = self { - strongSelf.torchButtonNode.isHidden = !hasTorch - } - }) - - 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.centerDimNode) - self.addSubnode(self.frameNode) - self.addSubnode(self.torchButtonNode) - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - self.addSubnode(self.errorTextNode) - - self.torchButtonNode.addTarget(self, action: #selector(self.torchPressed), forControlEvents: .touchUpInside) - } - - deinit { - self.codeDisposable.dispose() - self.torchDisposable?.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) { - if strongSelf.codeWithError != code.message { - strongSelf.codeWithError = nil - } - if strongSelf.codeWithError == code.message { - strongSelf.focusedCode.set(nil) - strongSelf.updateFocusedRect(nil) - } else { - strongSelf.focusedCode.set(code) - strongSelf.updateFocusedRect(code.boundingBox) - } - } else { - strongSelf.codeWithError = nil - strongSelf.focusedCode.set(nil) - strongSelf.updateFocusedRect(nil) - } - })) - - let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:))) - recognizer.tapActionAtPoint = { _ in - return .waitForSingleTap - } - self.textNode.view.addGestureRecognizer(recognizer) - } - - @objc private func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) { - switch recognizer.state { - case .ended: - if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation { - switch gesture { - case .tap: - if let (_, attributes) = self.textNode.attributesAtPoint(location) { - if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { - switch url { - case "desktop": - self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: "https://getdesktop.telegram.org", forceExternal: true, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {}) - case "web": - self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: "https://web.telegram.org", forceExternal: true, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {}) - default: - break - } - } - } - default: - break - } - } - default: - break - } - } - - 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 - let centerDimAlpha: CGFloat = 0.0 - let frameAlpha: CGFloat = 1.0 - 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.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) - var torchFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - buttonSize.width) / 2.0), y: dimHeight + frameSide + 98.0), size: buttonSize) - let updatedTorchY = min(torchFrame.minY, layout.size.height - torchFrame.height - 10.0) - let additionalTorchOffset: CGFloat = updatedTorchY - torchFrame.minY - torchFrame.origin.y = updatedTorchY - transition.updateFrame(node: self.torchButtonNode, frame: torchFrame) - - transition.updateAlpha(node: self.textNode, alpha: controlsAlpha) - transition.updateAlpha(node: self.errorTextNode, alpha: controlsAlpha) - transition.updateAlpha(node: self.torchButtonNode, alpha: controlsAlpha) - for view in self.highlightViews { - transition.updateAlpha(layer: view.layer, alpha: controlsAlpha) - } - - let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - 16.0, height: layout.size.height)) - let textSize = self.textNode.updateLayout(CGSize(width: layout.size.width - 16.0, height: layout.size.height)) - let errorTextSize = self.errorTextNode.updateLayout(CGSize(width: layout.size.width - 16.0, height: layout.size.height)) - var textFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width) / 2.0), y: max(dimHeight - textSize.height - titleSpacing, navigationHeight + floorToScreenPixels((dimHeight - navigationHeight - textSize.height) / 2.0) + 5.0)), 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) - if titleFrame.minY < navigationHeight { - transition.updateAlpha(node: self.titleNode, alpha: 0.0) - textFrame = textFrame.offsetBy(dx: 0.0, dy: -5.0) - } else { - transition.updateAlpha(node: self.titleNode, alpha: controlsAlpha) - } - var errorTextFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - errorTextSize.width) / 2.0), y: dimHeight + frameSide + 48.0), size: errorTextSize) - errorTextFrame.origin.y += floor(additionalTorchOffset / 2.0) - - transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame) - transition.updateFrameAdditive(node: self.textNode, frame: textFrame) - transition.updateFrameAdditive(node: self.errorTextNode, frame: errorTextFrame) - - if self.highlightViews.isEmpty { - let urlAttributesAndRects = self.textNode.cachedLayout?.allAttributeRects(name: "UrlAttributeT") ?? [] - - for (_, rect) in urlAttributesAndRects { - let view = UIVisualEffectView(effect: UIBlurEffect(style: .light)) - view.clipsToBounds = true - view.layer.cornerRadius = 5.0 - view.frame = rect.offsetBy(dx: self.textNode.frame.minX, dy: self.textNode.frame.minY).insetBy(dx: -4.0, dy: -2.0) - self.view.insertSubview(view, belowSubview: self.textNode.view) - self.highlightViews.append(view) - } - } - } - - @objc private func torchPressed() { - self.torchButtonNode.isSelected = !self.torchButtonNode.isSelected - self.camera.setTorchActive(self.torchButtonNode.isSelected) - } -} - diff --git a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift index 04597be426..0ab14cb9a5 100644 --- a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift +++ b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift @@ -291,6 +291,98 @@ public final class QrCodeScanScreen: ViewController { } } +private final class FrameNode: ASDisplayNode { + let topLeftLine: CAShapeLayer + let topRightLine: CAShapeLayer + let bottomLeftLine: CAShapeLayer + let bottomRightLine: CAShapeLayer + + override init() { + self.topLeftLine = CAShapeLayer() + self.topRightLine = CAShapeLayer() + self.bottomLeftLine = CAShapeLayer() + self.bottomRightLine = CAShapeLayer() + + super.init() + + for line in self.lines { + line.strokeColor = UIColor.white.cgColor + line.lineWidth = 4.0 + line.lineCap = .round + self.layer.addSublayer(line) + } + } + + private var lines: [CAShapeLayer] { + return [ + self.topLeftLine, + self.topRightLine, + self.bottomLeftLine, + self.bottomRightLine + ] + } + + func animateIn() { + let strokeStart = self.topLeftLine.strokeStart + let strokeEnd = self.topLeftLine.strokeEnd + + let duration: Double = 0.85 + let delay: Double = 0.15 + + for line in self.lines { + line.animateSpring(from: 0.0 as NSNumber, to: strokeStart as NSNumber, keyPath: "strokeStart", duration: duration, delay: delay) + line.animateSpring(from: 1.0 as NSNumber, to: strokeEnd as NSNumber, keyPath: "strokeEnd", duration: duration, delay: delay) + } + } + + func updateLayout(size: CGSize) { + let cornerRadius: CGFloat = 6.0 + + let lineLength = size.width / 2.0 - cornerRadius + let targetLineLength = 24.0 + let fraction = targetLineLength / lineLength + let strokeFraction = (1.0 - fraction) / 2.0 + let strokeStart = strokeFraction + let strokeEnd = 1.0 - strokeFraction + + let topLeftPath = CGMutablePath() + topLeftPath.move(to: CGPoint(x: 0.0, y: size.height / 2.0)) + topLeftPath.addArc(center: CGPoint(x: cornerRadius, y: cornerRadius), radius: cornerRadius, startAngle: -.pi, endAngle: -.pi / 2.0, clockwise: false) + topLeftPath.addLine(to: CGPoint(x: size.width / 2.0, y: 0.0)) + self.topLeftLine.path = topLeftPath + self.topLeftLine.strokeStart = strokeStart + self.topLeftLine.strokeEnd = strokeEnd + + let topRightPath = CGMutablePath() + topRightPath.move(to: CGPoint(x: size.width / 2.0, y: 0.0)) + topRightPath.addArc(center: CGPoint(x: size.width - cornerRadius, y: cornerRadius), radius: cornerRadius, startAngle: -.pi / 2.0, endAngle: 0.0, clockwise: false) + topRightPath.addLine(to: CGPoint(x: size.width, y: size.height / 2.0)) + self.topRightLine.path = topRightPath + self.topRightLine.strokeStart = strokeStart + self.topRightLine.strokeEnd = strokeEnd + + let bottomRightPath = CGMutablePath() + bottomRightPath.move(to: CGPoint(x: size.width, y: size.height / 2.0)) + bottomRightPath.addArc(center: CGPoint(x: size.width - cornerRadius, y: size.height - cornerRadius), radius: cornerRadius, startAngle: 0.0, endAngle: .pi / 2.0, clockwise: false) + bottomRightPath.addLine(to: CGPoint(x: size.width / 2.0, y: size.height)) + self.bottomRightLine.path = bottomRightPath + self.bottomRightLine.strokeStart = strokeStart + self.bottomRightLine.strokeEnd = strokeEnd + + let bottomLeftPath = CGMutablePath() + bottomLeftPath.move(to: CGPoint(x: size.width / 2.0, y: size.height)) + bottomLeftPath.addArc(center: CGPoint(x: cornerRadius, y: size.height - cornerRadius), radius: cornerRadius, startAngle: .pi / 2.0, endAngle: .pi, clockwise: false) + bottomLeftPath.addLine(to: CGPoint(x: 0.0, y: size.height / 2.0)) + self.bottomLeftLine.path = bottomLeftPath + self.bottomLeftLine.strokeStart = strokeStart + self.bottomLeftLine.strokeEnd = strokeEnd + + for line in self.lines { + line.frame = CGRect(origin: .zero, size: size) + } + } +} + private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollViewDelegate { private let context: AccountContext private var presentationData: PresentationData @@ -304,7 +396,7 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie private let leftDimNode: ASDisplayNode private let rightDimNode: ASDisplayNode private let centerDimNode: ASDisplayNode - private let frameNode: ASImageNode + private let frameNode: FrameNode private let galleryButtonNode: GlassButtonNode private let torchButtonNode: GlassButtonNode private let titleNode: ImmediateTextNode @@ -350,28 +442,29 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie self.fadeNode.alpha = 0.0 self.fadeNode.backgroundColor = .black + let dimColor = UIColor(rgb: 0x000000, alpha: 0.8) + self.topDimNode = ASDisplayNode() self.topDimNode.alpha = 0.625 - self.topDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8) + self.topDimNode.backgroundColor = dimColor self.bottomDimNode = ASDisplayNode() self.bottomDimNode.alpha = 0.625 - self.bottomDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8) + self.bottomDimNode.backgroundColor = dimColor self.leftDimNode = ASDisplayNode() self.leftDimNode.alpha = 0.625 - self.leftDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8) + self.leftDimNode.backgroundColor = dimColor self.rightDimNode = ASDisplayNode() self.rightDimNode.alpha = 0.625 - self.rightDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8) + self.rightDimNode.backgroundColor = dimColor self.centerDimNode = ASDisplayNode() self.centerDimNode.alpha = 0.0 - self.centerDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8) + self.centerDimNode.backgroundColor = dimColor - self.frameNode = ASImageNode() - self.frameNode.image = generateFrameImage() + self.frameNode = FrameNode() self.galleryButtonNode = GlassButtonNode(icon: UIImage(bundleImageName: "Wallet/CameraGalleryIcon")!, label: nil) self.torchButtonNode = GlassButtonNode(icon: UIImage(bundleImageName: "Wallet/CameraFlashIcon")!, label: nil) @@ -517,6 +610,16 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie self.textNode.view.addGestureRecognizer(recognizer) } + private var animatedIn = false + func animateIn() { + guard !self.animatedIn else { + return + } + self.animatedIn = true + + self.frameNode.animateIn() + } + @objc private func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) { switch recognizer.state { case .ended: @@ -551,9 +654,16 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie } } - func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) { + private var animatingIn = false + func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, animateIn: Bool = false, transition: ContainedViewLayoutTransition) { + let hadLayout = self.validLayout != nil self.validLayout = (layout, navigationHeight) + var prepareForAnimateIn = false + if !hadLayout { + prepareForAnimateIn = true + } + let sideInset: CGFloat = 66.0 let titleSpacing: CGFloat = 48.0 let bounds = CGRect(origin: CGPoint(), size: layout.size) @@ -571,11 +681,20 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie transition.updateFrame(node: self.fadeNode, frame: bounds) let frameSide = max(240.0, layout.size.width - sideInset * 2.0) + let animateInScale: CGFloat = 0.4 + var effectiveFrameSide = frameSide + if prepareForAnimateIn { + effectiveFrameSide = round(effectiveFrameSide * animateInScale) + } + let dimHeight = ceil((layout.size.height - frameSide) / 2.0) + let effectiveDimHeight = ceil((layout.size.height - effectiveFrameSide) / 2.0) let dimInset = (layout.size.width - frameSide) / 2.0 + let effectiveDimInset = (layout.size.width - effectiveFrameSide) / 2.0 let dimAlpha: CGFloat let dimRect: CGRect + let frameRect: CGRect let controlsAlpha: CGFloat let centerDimAlpha: CGFloat = 0.0 let frameAlpha: CGFloat = 1.0 @@ -585,10 +704,12 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie 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) + frameRect = dimRect } 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) + dimRect = CGRect(x: effectiveDimInset, y: effectiveDimHeight, width: layout.size.width - effectiveDimInset * 2.0, height: layout.size.height - effectiveDimHeight * 2.0) + frameRect = 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) @@ -598,12 +719,25 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie 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) + if !self.animatingIn { + var delay: Double = 0.0 + if animateIn { + self.animatingIn = true + delay = 0.1 + } + transition.updateFrame(node: self.topDimNode, frame: CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: dimRect.minY), delay: delay, completion: { _ in + self.animatingIn = false + }) + 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)), delay: delay) + transition.updateFrame(node: self.leftDimNode, frame: CGRect(x: 0.0, y: dimRect.minY, width: max(0.0, dimRect.minX), height: dimRect.height), delay: delay) + 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), delay: delay) + transition.updateFrame(node: self.frameNode, frame: frameRect) + self.frameNode.updateLayout(size: frameRect.size) + transition.updateFrame(node: self.centerDimNode, frame: frameRect) + if animateIn { + transition.animateTransformScale(node: self.frameNode, from: CGPoint(x: animateInScale, y: animateInScale), delay: delay) + } + } let buttonSize = CGSize(width: 72.0, height: 72.0) var torchFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - buttonSize.width) / 2.0), y: dimHeight + frameSide + 98.0), size: buttonSize) @@ -657,6 +791,11 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie self.highlightViews.append(view) } } + + if prepareForAnimateIn { + self.animateIn() + self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, animateIn: true, transition: .animated(duration: 0.8, curve: .customSpring(damping: 88.0, initialVelocity: 0.0))) + } } @objc private func galleryPressed() { diff --git a/submodules/SettingsUI/BUILD b/submodules/SettingsUI/BUILD index 92705d888a..38f6451297 100644 --- a/submodules/SettingsUI/BUILD +++ b/submodules/SettingsUI/BUILD @@ -88,7 +88,6 @@ swift_library( "//submodules/PhoneNumberFormat:PhoneNumberFormat", "//submodules/OpenInExternalAppUI:OpenInExternalAppUI", "//submodules/AccountUtils:AccountUtils", - "//submodules/AuthTransferUI:AuthTransferUI", "//submodules/UIKitRuntimeUtils:UIKitRuntimeUtils", "//submodules/DebugSettingsUI:DebugSettingsUI", "//submodules/WallpaperBackgroundNode:WallpaperBackgroundNode", diff --git a/submodules/SettingsUI/Sources/Privacy and Security/GlobalAutoremoveScreen.swift b/submodules/SettingsUI/Sources/Privacy and Security/GlobalAutoremoveScreen.swift index 55fbaead88..0492707e9b 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/GlobalAutoremoveScreen.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/GlobalAutoremoveScreen.swift @@ -9,7 +9,6 @@ import TelegramUIPreferences import ItemListUI import PresentationDataUtils import AccountContext -import AuthTransferUI import ItemListPeerActionItem import DeviceAccess import QrCodeUI diff --git a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift index db65fcdc95..293df2023a 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift @@ -9,7 +9,6 @@ import TelegramUIPreferences import ItemListUI import PresentationDataUtils import AccountContext -import AuthTransferUI import ItemListPeerActionItem import DeviceAccess import QrCodeUI diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 81ee929b36..2d2a8a7280 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -268,7 +268,6 @@ swift_library( "//submodules/Svg:Svg", "//submodules/ManagedAnimationNode:ManagedAnimationNode", "//submodules/TooltipUI:TooltipUI", - "//submodules/AuthTransferUI:AuthTransferUI", "//submodules/ListMessageItem:ListMessageItem", "//submodules/FileMediaResourceStatus:FileMediaResourceStatus", "//submodules/ChatMessageInteractiveMediaBadge:ChatMessageInteractiveMediaBadge", diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 5fcc48a636..eb195fc3a0 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -42,7 +42,6 @@ import ChatListUI import CallListUI import AccountUtils import PassportUI -import AuthTransferUI import DeviceAccess import LegacyMediaPickerUI import TelegramNotices