mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
324 lines
17 KiB
Swift
324 lines
17 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import AppBundle
|
|
import AccountContext
|
|
import TelegramPresentationData
|
|
import AsyncDisplayKit
|
|
import Display
|
|
import Postbox
|
|
import TelegramCore
|
|
import SolidRoundedButtonNode
|
|
import UndoUI
|
|
import AlertUI
|
|
|
|
public final class WalletWordDisplayScreen: ViewController {
|
|
private let context: AccountContext
|
|
private let tonContext: TonContext
|
|
private var presentationData: PresentationData
|
|
private let walletInfo: WalletInfo
|
|
private let wordList: [String]
|
|
|
|
private let startTime: Double
|
|
|
|
public init(context: AccountContext, tonContext: TonContext, walletInfo: WalletInfo, wordList: [String]) {
|
|
self.context = context
|
|
self.tonContext = tonContext
|
|
self.walletInfo = walletInfo
|
|
self.wordList = wordList
|
|
|
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
|
|
let defaultNavigationPresentationData = NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings)
|
|
let navigationBarTheme = NavigationBarTheme(buttonColor: defaultNavigationPresentationData.theme.buttonColor, disabledButtonColor: defaultNavigationPresentationData.theme.disabledButtonColor, primaryTextColor: defaultNavigationPresentationData.theme.primaryTextColor, backgroundColor: .clear, separatorColor: .clear, badgeBackgroundColor: defaultNavigationPresentationData.theme.badgeBackgroundColor, badgeStrokeColor: defaultNavigationPresentationData.theme.badgeStrokeColor, badgeTextColor: defaultNavigationPresentationData.theme.badgeTextColor)
|
|
|
|
self.startTime = Date().timeIntervalSince1970
|
|
|
|
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: defaultNavigationPresentationData.strings))
|
|
|
|
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")
|
|
}
|
|
|
|
@objc private func backPressed() {
|
|
self.dismiss()
|
|
}
|
|
|
|
override public func loadDisplayNode() {
|
|
self.displayNode = WalletWordDisplayScreenNode(presentationData: self.presentationData, wordList: self.wordList, action: { [weak self] in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
let deltaTime = Date().timeIntervalSince1970 - strongSelf.startTime
|
|
let minimalTimeout: Double
|
|
#if DEBUG
|
|
minimalTimeout = 1.0
|
|
#else
|
|
minimalTimeout = 60.0
|
|
#endif
|
|
if deltaTime < minimalTimeout {
|
|
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: "Sure Done?", text: "You didn't have enough time to write those words down.", actions: [TextAlertAction(type: .defaultAction, title: "OK, Sorry", action: {
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
if let path = getAppBundle().path(forResource: "thumbsup", ofType: "tgs") {
|
|
strongSelf.present(UndoOverlayController(context: strongSelf.context, content: UndoOverlayContent.emoji(account: strongSelf.context.account, path: path, text: "Apologies Accepted"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in }), in: .current)
|
|
}
|
|
})]), in: .window(.root))
|
|
} else {
|
|
strongSelf.push(WalletWordCheckScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .verify(strongSelf.walletInfo, strongSelf.wordList)))
|
|
}
|
|
})
|
|
|
|
self.displayNodeDidLoad()
|
|
}
|
|
|
|
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
|
super.containerLayoutUpdated(layout, transition: transition)
|
|
|
|
(self.displayNode as! WalletWordDisplayScreenNode).containerLayoutUpdated(layout: layout, navigationHeight: self.navigationHeight, transition: transition)
|
|
}
|
|
}
|
|
|
|
private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UIScrollViewDelegate {
|
|
private var presentationData: PresentationData
|
|
private let action: () -> Void
|
|
|
|
private let navigationBackgroundNode: ASDisplayNode
|
|
private let navigationSeparatorNode: ASDisplayNode
|
|
private let navigationTitleNode: ImmediateTextNode
|
|
private let scrollNode: ASScrollNode
|
|
private let iconNode: ASImageNode
|
|
private let titleNode: ImmediateTextNode
|
|
private let textNode: ImmediateTextNode
|
|
private let wordNodes: [(ImmediateTextNode, ImmediateTextNode, ImmediateTextNode)]
|
|
private let buttonNode: SolidRoundedButtonNode
|
|
|
|
private var navigationHeight: CGFloat?
|
|
|
|
init(presentationData: PresentationData, wordList: [String], action: @escaping () -> Void) {
|
|
self.presentationData = presentationData
|
|
self.action = action
|
|
|
|
self.navigationBackgroundNode = ASDisplayNode()
|
|
self.navigationBackgroundNode.backgroundColor = self.presentationData.theme.rootController.navigationBar.backgroundColor
|
|
self.navigationBackgroundNode.alpha = 0.0
|
|
self.navigationSeparatorNode = ASDisplayNode()
|
|
self.navigationSeparatorNode.backgroundColor = self.presentationData.theme.rootController.navigationBar.separatorColor
|
|
|
|
self.scrollNode = ASScrollNode()
|
|
|
|
self.iconNode = ASImageNode()
|
|
self.iconNode.displayWithoutProcessing = true
|
|
self.iconNode.displaysAsynchronously = false
|
|
|
|
let title: String = "24 Secret Words"
|
|
let text: String = "Write down these 24 words in the correct order and store them in a secret place.\n\nUse these secret words to restore access to your wallet if you lose your passcode or Telegram account."
|
|
let buttonText: String = "Done"
|
|
|
|
self.iconNode.image = UIImage(bundleImageName: "Settings/Wallet/WordsDisplayIcon")
|
|
|
|
self.titleNode = ImmediateTextNode()
|
|
self.titleNode.displaysAsynchronously = false
|
|
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(32.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
|
|
self.titleNode.maximumNumberOfLines = 0
|
|
self.titleNode.textAlignment = .center
|
|
|
|
self.navigationTitleNode = ImmediateTextNode()
|
|
self.navigationTitleNode.displaysAsynchronously = false
|
|
self.navigationTitleNode.attributedText = NSAttributedString(string: title, font: Font.bold(17.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
|
|
self.navigationTitleNode.maximumNumberOfLines = 0
|
|
self.navigationTitleNode.textAlignment = .center
|
|
|
|
self.textNode = ImmediateTextNode()
|
|
self.textNode.displaysAsynchronously = false
|
|
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
|
|
self.textNode.maximumNumberOfLines = 0
|
|
self.textNode.lineSpacing = 0.1
|
|
self.textNode.textAlignment = .center
|
|
|
|
var wordNodes: [(ImmediateTextNode, ImmediateTextNode, ImmediateTextNode)] = []
|
|
|
|
for i in 0 ..< wordList.count {
|
|
let indexNode = ImmediateTextNode()
|
|
indexNode.displaysAsynchronously = false
|
|
indexNode.attributedText = NSAttributedString(string: "\(i + 1)", font: Font.regular(18.0), textColor: self.presentationData.theme.list.itemSecondaryTextColor)
|
|
indexNode.maximumNumberOfLines = 1
|
|
indexNode.textAlignment = .left
|
|
|
|
let indexDotNode = ImmediateTextNode()
|
|
indexDotNode.displaysAsynchronously = false
|
|
indexDotNode.attributedText = NSAttributedString(string: ".", font: Font.regular(18.0), textColor: self.presentationData.theme.list.itemSecondaryTextColor)
|
|
indexDotNode.maximumNumberOfLines = 1
|
|
indexDotNode.textAlignment = .left
|
|
|
|
let wordNode = ImmediateTextNode()
|
|
wordNode.displaysAsynchronously = false
|
|
wordNode.attributedText = NSAttributedString(string: wordList[i], font: Font.semibold(18.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
|
|
wordNode.maximumNumberOfLines = 1
|
|
wordNode.textAlignment = .left
|
|
|
|
wordNodes.append((indexNode, indexDotNode, wordNode))
|
|
}
|
|
|
|
self.wordNodes = wordNodes
|
|
|
|
self.buttonNode = SolidRoundedButtonNode(title: buttonText, theme: self.presentationData.theme, height: 50.0, cornerRadius: 10.0, gloss: false)
|
|
|
|
super.init()
|
|
|
|
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
|
|
|
|
self.addSubnode(self.scrollNode)
|
|
|
|
self.scrollNode.addSubnode(self.iconNode)
|
|
self.scrollNode.addSubnode(self.titleNode)
|
|
self.scrollNode.addSubnode(self.textNode)
|
|
self.scrollNode.addSubnode(self.buttonNode)
|
|
|
|
for (indexNode, indexDotNode, wordNode) in self.wordNodes {
|
|
self.scrollNode.addSubnode(indexNode)
|
|
self.scrollNode.addSubnode(indexDotNode)
|
|
self.scrollNode.addSubnode(wordNode)
|
|
}
|
|
|
|
self.navigationBackgroundNode.addSubnode(self.navigationSeparatorNode)
|
|
self.navigationBackgroundNode.addSubnode(self.navigationTitleNode)
|
|
self.addSubnode(self.navigationBackgroundNode)
|
|
|
|
self.buttonNode.pressed = {
|
|
action()
|
|
}
|
|
}
|
|
|
|
override func didLoad() {
|
|
super.didLoad()
|
|
|
|
self.scrollNode.view.alwaysBounceVertical = false
|
|
self.scrollNode.view.showsVerticalScrollIndicator = false
|
|
self.scrollNode.view.showsHorizontalScrollIndicator = false
|
|
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
|
self.scrollNode.view.contentInsetAdjustmentBehavior = .never
|
|
}
|
|
self.scrollNode.view.delegate = self
|
|
}
|
|
|
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
|
let navigationHeight = self.navigationHeight ?? 0.0
|
|
let alpha: CGFloat = scrollView.contentOffset.y >= (self.titleNode.frame.maxY - navigationHeight) ? 1.0 : 0.0
|
|
if self.navigationBackgroundNode.alpha != alpha {
|
|
let transition: ContainedViewLayoutTransition = .animated(duration: 0.12, curve: .easeInOut)
|
|
transition.updateAlpha(node: self.navigationBackgroundNode, alpha: alpha, beginWithCurrentState: true)
|
|
}
|
|
}
|
|
|
|
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
|
self.navigationHeight = navigationHeight
|
|
|
|
let sideInset: CGFloat = 32.0
|
|
let buttonSideInset: CGFloat = 48.0
|
|
let iconSpacing: CGFloat = 5.0
|
|
let titleSpacing: CGFloat = 19.0
|
|
let textSpacing: CGFloat = 37.0
|
|
let buttonHeight: CGFloat = 50.0
|
|
let buttonSpacing: CGFloat = 45.0
|
|
let wordSpacing: CGFloat = 12.0
|
|
let indexSpacing: CGFloat = 4.0
|
|
|
|
transition.updateFrame(node: self.navigationBackgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: navigationHeight)))
|
|
transition.updateFrame(node: self.navigationSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
|
|
|
|
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
|
|
|
let iconSize = self.iconNode.image?.size ?? CGSize(width: 50.0, height: 50.0)
|
|
|
|
let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height))
|
|
let navigationTitleSize = self.navigationTitleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height))
|
|
let textSize = self.textNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height))
|
|
|
|
var contentHeight: CGFloat = 0.0
|
|
|
|
let contentVerticalOrigin = navigationHeight + 10.0
|
|
|
|
let iconFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: contentVerticalOrigin), size: iconSize)
|
|
transition.updateFrameAdditive(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.updateFrameAdditive(node: self.titleNode, frame: titleFrame)
|
|
let textFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width) / 2.0), y: titleFrame.maxY + titleSpacing), size: textSize)
|
|
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
|
|
|
|
transition.updateFrameAdditive(node: self.navigationTitleNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - navigationTitleSize.width) / 2.0), y: navigationHeight - 44.0 + floor((44.0 - navigationTitleSize.height) / 2.0)), size: navigationTitleSize))
|
|
|
|
contentHeight = textFrame.maxY + textSpacing
|
|
|
|
let rowWidth = layout.size.width - buttonSideInset * 2.0
|
|
let rowCount = self.wordNodes.count / 2
|
|
|
|
let indexWidth: CGFloat = 16.0
|
|
|
|
var wordSizes: [(CGSize, CGSize)] = []
|
|
var columnIndexWidth: [CGFloat] = [0.0, 0.0]
|
|
var columnWordWidth: [CGFloat] = [0.0, 0.0]
|
|
var dotSize: CGSize = CGSize()
|
|
|
|
for i in 0 ..< self.wordNodes.count {
|
|
let indexSize = self.wordNodes[i].0.updateLayout(CGSize(width: 200.0, height: 100.0))
|
|
dotSize = self.wordNodes[i].1.updateLayout(CGSize(width: 200.0, height: 100.0))
|
|
let wordSize = self.wordNodes[i].2.updateLayout(CGSize(width: 200.0, height: 100.0))
|
|
wordSizes.append((indexSize, wordSize))
|
|
let column = i / rowCount
|
|
columnIndexWidth[column] = max(columnIndexWidth[column], indexSize.width)
|
|
columnWordWidth[column] = max(columnWordWidth[column], wordSize.width)
|
|
}
|
|
|
|
for column in 0 ..< 2 {
|
|
var columnHeight: CGFloat = 0.0
|
|
for i in 0 ..< self.wordNodes.count {
|
|
if !columnHeight.isZero {
|
|
columnHeight += wordSpacing
|
|
}
|
|
if i / rowCount != column {
|
|
continue
|
|
}
|
|
|
|
let horizontalOrigin: CGFloat
|
|
let verticalOrigin: CGFloat = contentHeight + columnHeight
|
|
if column == 0 {
|
|
horizontalOrigin = buttonSideInset + columnIndexWidth[column]
|
|
} else {
|
|
horizontalOrigin = layout.size.width - buttonSideInset - columnWordWidth[column] - indexSpacing
|
|
}
|
|
let indexSize = self.wordNodes[i].0.updateLayout(CGSize(width: 200.0, height: 100.0))
|
|
let wordSize = self.wordNodes[i].2.updateLayout(CGSize(width: 200.0, height: 100.0))
|
|
transition.updateFrameAdditive(node: self.wordNodes[i].0, frame: CGRect(origin: CGPoint(x: horizontalOrigin - indexSize.width - dotSize.width, y: verticalOrigin), size: indexSize))
|
|
transition.updateFrameAdditive(node: self.wordNodes[i].1, frame: CGRect(origin: CGPoint(x: horizontalOrigin - dotSize.width, y: verticalOrigin), size: indexSize))
|
|
transition.updateFrameAdditive(node: self.wordNodes[i].2, frame: CGRect(origin: CGPoint(x: horizontalOrigin + indexSpacing, y: verticalOrigin), size: wordSize))
|
|
columnHeight += wordSize.height
|
|
}
|
|
|
|
if column == 1 {
|
|
contentHeight += columnHeight
|
|
}
|
|
}
|
|
|
|
let minimalFullscreenBottomInset: CGFloat = 74.0
|
|
let minimalScrollBottomInset: CGFloat = 30.0
|
|
let fullscreenBottomInset = layout.intrinsicInsets.bottom + minimalFullscreenBottomInset
|
|
let scrollBottomInset = layout.intrinsicInsets.bottom + minimalScrollBottomInset
|
|
|
|
let buttonWidth = layout.size.width - buttonSideInset * 2.0
|
|
|
|
let buttonFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - buttonWidth) / 2.0), y: max(contentHeight + buttonSpacing, layout.size.height - scrollBottomInset - buttonHeight)), size: CGSize(width: buttonWidth, height: buttonHeight))
|
|
transition.updateFrame(node: self.buttonNode, frame: buttonFrame)
|
|
self.buttonNode.updateLayout(width: buttonFrame.width, transition: transition)
|
|
|
|
self.scrollNode.view.contentSize = CGSize(width: layout.size.width, height: max(layout.size.height, buttonFrame.maxY + scrollBottomInset))
|
|
}
|
|
}
|