2019-12-12 03:17:14 +01:00
2020-01-30 10:09:02 +01:00
final class SeedVC : UIViewController {
2019-12-12 03:17:14 +01:00
private let mnemonic : String = {
let identityManager = OWSIdentityManager . shared ( )
let databaseConnection = identityManager . value ( forKey : " dbConnection " ) as ! YapDatabaseConnection
var hexEncodedSeed : String ! = databaseConnection . object ( forKey : " LKLokiSeed " , inCollection : OWSPrimaryStorageIdentityKeyStoreCollection ) as ! String ?
if hexEncodedSeed = = nil {
hexEncodedSeed = identityManager . identityKeyPair ( ) ! . hexEncodedPrivateKey // L e g a c y a c c o u n t
}
return Mnemonic . encode ( hexEncodedString : hexEncodedSeed )
} ( )
private lazy var redactedMnemonic : NSAttributedString = {
var mnemonic = self . mnemonic
let regex = try ! NSRegularExpression ( pattern : " \\ w* " , options : [ ] )
let matches = regex . matches ( in : mnemonic , options : . withoutAnchoringBounds , range : NSRange ( location : 0 , length : mnemonic . count ) )
let result = NSMutableAttributedString ( string : mnemonic )
matches . forEach { match in
result . addAttribute ( . strikethroughStyle , value : NSUnderlineStyle . thick . rawValue , range : match . range )
result . addAttribute ( . strikethroughColor , value : Colors . accent , range : match . range )
}
return result
} ( )
// MARK: C o m p o n e n t s
private lazy var seedReminderView : SeedReminderView = {
let result = SeedReminderView ( hasContinueButton : false )
let title = " You're almost finished! 90% "
let attributedTitle = NSMutableAttributedString ( string : title )
attributedTitle . addAttribute ( . foregroundColor , value : Colors . accent , range : ( title as NSString ) . range ( of : " 90% " ) )
result . title = attributedTitle
2020-01-23 05:01:31 +01:00
result . subtitle = NSLocalizedString ( " Tap and hold the redacted words to reveal your recovery phrase, then store it safely to secure your Session ID. " , comment : " " )
2019-12-12 03:17:14 +01:00
result . setProgress ( 0.9 , animated : false )
return result
} ( )
private lazy var mnemonicLabel : UILabel = {
let result = UILabel ( )
result . textColor = Colors . text
result . font = Fonts . spaceMono ( ofSize : Values . mediumFontSize )
result . numberOfLines = 0
result . textAlignment = . center
result . lineBreakMode = . byWordWrapping
return result
} ( )
private lazy var copyButton : Button = {
let result = Button ( style : . prominentOutline , size : . large )
result . setTitle ( NSLocalizedString ( " Copy " , comment : " " ) , for : UIControl . State . normal )
result . addTarget ( self , action : #selector ( copyMnemonic ) , for : UIControl . Event . touchUpInside )
return result
} ( )
// MARK: S e t t i n g s
2020-02-17 02:08:46 +01:00
override var preferredStatusBarStyle : UIStatusBarStyle { return isLightMode ? . default : . lightContent }
2019-12-12 03:17:14 +01:00
// MARK: L i f e c y c l e
override func viewDidLoad ( ) {
// S e t g r a d i e n t b a c k g r o u n d
view . backgroundColor = . clear
let gradient = Gradients . defaultLokiBackground
view . setGradient ( gradient )
// S e t u p n a v i g a t i o n b a r
let navigationBar = navigationController ! . navigationBar
navigationBar . setBackgroundImage ( UIImage ( ) , for : UIBarMetrics . default )
navigationBar . shadowImage = UIImage ( )
navigationBar . isTranslucent = false
navigationBar . barTintColor = Colors . navigationBarBackground
// C u s t o m i z e t i t l e
let navigationBarTitleLabel = UILabel ( )
2020-01-08 06:10:12 +01:00
navigationBarTitleLabel . text = NSLocalizedString ( " Your Recovery Phrase " , comment : " " )
2019-12-12 03:17:14 +01:00
navigationBarTitleLabel . textColor = Colors . text
navigationBarTitleLabel . font = . boldSystemFont ( ofSize : Values . veryLargeFontSize )
navigationItem . titleView = navigationBarTitleLabel
// S e t u p n a v i g a t i o n b a r b u t t o n s
let closeButton = UIBarButtonItem ( image : # imageLiteral ( resourceName : " X " ) , style : . plain , target : self , action : #selector ( close ) )
closeButton . tintColor = Colors . text
navigationItem . leftBarButtonItem = closeButton
// S e t u p t i t l e l a b e l
let titleLabel = UILabel ( )
titleLabel . textColor = Colors . text
2020-01-17 05:53:56 +01:00
titleLabel . font = . boldSystemFont ( ofSize : isSmallScreen ? Values . largeFontSize : Values . veryLargeFontSize )
2020-01-08 06:10:12 +01:00
titleLabel . text = NSLocalizedString ( " Meet your recovery phrase " , comment : " " )
2019-12-12 03:17:14 +01:00
titleLabel . numberOfLines = 0
titleLabel . lineBreakMode = . byWordWrapping
// S e t u p e x p l a n a t i o n l a b e l
let explanationLabel = UILabel ( )
explanationLabel . textColor = Colors . text
explanationLabel . font = . systemFont ( ofSize : Values . smallFontSize )
2020-01-23 05:01:31 +01:00
explanationLabel . text = NSLocalizedString ( " Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and don’ t give it to anyone. To restore your Session ID, launch Session and tap Continue your Session. " , comment : " " )
2019-12-12 03:17:14 +01:00
explanationLabel . numberOfLines = 0
explanationLabel . lineBreakMode = . byWordWrapping
// S e t u p m n e m o n i c l a b e l
mnemonicLabel . attributedText = redactedMnemonic
let mnemonicLabelGestureRecognizer = UILongPressGestureRecognizer ( target : self , action : #selector ( revealMnemonic ) )
mnemonicLabel . addGestureRecognizer ( mnemonicLabelGestureRecognizer )
mnemonicLabel . isUserInteractionEnabled = true
mnemonicLabel . isEnabled = true
// S e t u p m n e m o n i c l a b e l c o n t a i n e r
let mnemonicLabelContainer = UIView ( )
mnemonicLabelContainer . addSubview ( mnemonicLabel )
2020-01-23 05:01:31 +01:00
mnemonicLabel . pin ( to : mnemonicLabelContainer , withInset : isSmallScreen ? Values . smallSpacing : Values . mediumSpacing )
2019-12-12 03:17:14 +01:00
mnemonicLabelContainer . layer . cornerRadius = Values . textFieldCornerRadius
mnemonicLabelContainer . layer . borderWidth = Values . borderThickness
mnemonicLabelContainer . layer . borderColor = Colors . text . cgColor
// S e t u p c a l l t o a c t i o n l a b e l
let callToActionLabel = UILabel ( )
callToActionLabel . textColor = Colors . text . withAlphaComponent ( Values . unimportantElementOpacity )
2020-01-17 05:53:56 +01:00
callToActionLabel . font = . systemFont ( ofSize : isSmallScreen ? Values . smallFontSize : Values . mediumFontSize )
2019-12-12 03:17:14 +01:00
callToActionLabel . text = NSLocalizedString ( " Hold to reveal " , comment : " " )
callToActionLabel . textAlignment = . center
let callToActionLabelGestureRecognizer = UILongPressGestureRecognizer ( target : self , action : #selector ( revealMnemonic ) )
callToActionLabel . addGestureRecognizer ( callToActionLabelGestureRecognizer )
callToActionLabel . isUserInteractionEnabled = true
callToActionLabel . isEnabled = true
// S e t u p s p a c e r s
let topSpacer = UIView . vStretchingSpacer ( )
let bottomSpacer = UIView . vStretchingSpacer ( )
// S e t u p c o p y b u t t o n c o n t a i n e r
let copyButtonContainer = UIView ( )
copyButtonContainer . addSubview ( copyButton )
copyButton . pin ( . leading , to : . leading , of : copyButtonContainer , withInset : Values . massiveSpacing )
copyButton . pin ( . top , to : . top , of : copyButtonContainer )
copyButtonContainer . pin ( . trailing , to : . trailing , of : copyButton , withInset : Values . massiveSpacing )
copyButtonContainer . pin ( . bottom , to : . bottom , of : copyButton )
// S e t u p t o p s t a c k v i e w
let topStackView = UIStackView ( arrangedSubviews : [ titleLabel , explanationLabel , mnemonicLabelContainer , callToActionLabel ] )
topStackView . axis = . vertical
2020-01-17 05:53:56 +01:00
topStackView . spacing = isSmallScreen ? Values . smallSpacing : Values . largeSpacing
2019-12-12 03:17:14 +01:00
topStackView . alignment = . fill
// S e t u p t o p s t a c k v i e w c o n t a i n e r
let topStackViewContainer = UIView ( )
topStackViewContainer . addSubview ( topStackView )
topStackView . pin ( . leading , to : . leading , of : topStackViewContainer , withInset : Values . veryLargeSpacing )
topStackView . pin ( . top , to : . top , of : topStackViewContainer )
topStackViewContainer . pin ( . trailing , to : . trailing , of : topStackView , withInset : Values . veryLargeSpacing )
topStackViewContainer . pin ( . bottom , to : . bottom , of : topStackView )
// S e t u p s e e d r e m i n d e r v i e w
view . addSubview ( seedReminderView )
seedReminderView . pin ( . leading , to : . leading , of : view )
seedReminderView . pin ( . top , to : . top , of : view )
seedReminderView . pin ( . trailing , to : . trailing , of : view )
// S e t u p m a i n s t a c k v i e w
let mainStackView = UIStackView ( arrangedSubviews : [ topSpacer , topStackViewContainer , bottomSpacer , copyButtonContainer ] )
mainStackView . axis = . vertical
mainStackView . alignment = . fill
2020-01-17 05:53:56 +01:00
mainStackView . layoutMargins = UIEdgeInsets ( top : 0 , leading : 0 , bottom : isSmallScreen ? Values . smallSpacing : Values . mediumSpacing , trailing : 0 )
2019-12-12 03:17:14 +01:00
mainStackView . isLayoutMarginsRelativeArrangement = true
view . addSubview ( mainStackView )
mainStackView . pin ( . leading , to : . leading , of : view )
mainStackView . pin ( . top , to : . bottom , of : seedReminderView )
mainStackView . pin ( . trailing , to : . trailing , of : view )
mainStackView . pin ( . bottom , to : . bottom , of : view )
topSpacer . heightAnchor . constraint ( equalTo : bottomSpacer . heightAnchor , multiplier : 1 ) . isActive = true
}
// MARK: G e n e r a l
@objc private func enableCopyButton ( ) {
copyButton . isUserInteractionEnabled = true
UIView . transition ( with : copyButton , duration : 0.25 , options : . transitionCrossDissolve , animations : {
self . copyButton . setTitle ( NSLocalizedString ( " Copy " , comment : " " ) , for : UIControl . State . normal )
} , completion : nil )
}
// MARK: I n t e r a c t i o n
@objc private func close ( ) {
dismiss ( animated : true , completion : nil )
}
@objc private func revealMnemonic ( ) {
UIView . transition ( with : mnemonicLabel , duration : 0.25 , options : . transitionCrossDissolve , animations : {
self . mnemonicLabel . attributedText = NSAttributedString ( string : self . mnemonic )
} , completion : nil )
UIView . transition ( with : seedReminderView . titleLabel , duration : 0.25 , options : . transitionCrossDissolve , animations : {
let title = " Account Secured! 100% "
let attributedTitle = NSMutableAttributedString ( string : title )
attributedTitle . addAttribute ( . foregroundColor , value : Colors . accent , range : ( title as NSString ) . range ( of : " 100% " ) )
self . seedReminderView . title = attributedTitle
} , completion : nil )
UIView . transition ( with : seedReminderView . subtitleLabel , duration : 1 , options : . transitionCrossDissolve , animations : {
2020-01-08 06:10:12 +01:00
self . seedReminderView . subtitle = NSLocalizedString ( " Make sure to store your recovery phrase in a safe place " , comment : " " )
2019-12-12 03:17:14 +01:00
} , completion : nil )
seedReminderView . setProgress ( 1 , animated : true )
UserDefaults . standard . set ( true , forKey : " hasViewedSeed " )
NotificationCenter . default . post ( name : . seedViewed , object : nil )
}
@objc private func copyMnemonic ( ) {
revealMnemonic ( )
UIPasteboard . general . string = mnemonic
copyButton . isUserInteractionEnabled = false
UIView . transition ( with : copyButton , duration : 0.25 , options : . transitionCrossDissolve , animations : {
self . copyButton . setTitle ( NSLocalizedString ( " Copied " , comment : " " ) , for : UIControl . State . normal )
} , completion : nil )
Timer . scheduledTimer ( timeInterval : 4 , target : self , selector : #selector ( enableCopyButton ) , userInfo : nil , repeats : false )
}
}