2019-12-09 02:33:40 +01:00
final class RestoreVC : UIViewController {
private var spacer1HeightConstraint : NSLayoutConstraint !
private var spacer2HeightConstraint : NSLayoutConstraint !
2019-12-09 03:24:03 +01:00
private var spacer3HeightConstraint : NSLayoutConstraint !
2019-12-09 02:33:40 +01:00
private var restoreButtonBottomOffsetConstraint : NSLayoutConstraint !
private var bottomConstraint : NSLayoutConstraint !
// MARK: C o m p o n e n t s
private lazy var mnemonicTextField : TextField = {
let result = TextField ( placeholder : NSLocalizedString ( " Enter your seed " , comment : " " ) )
result . layer . borderColor = Colors . text . cgColor
return result
} ( )
2019-12-09 03:24:03 +01:00
private lazy var legalLabel : UILabel = {
let result = UILabel ( )
result . textColor = Colors . text
result . font = . systemFont ( ofSize : Values . verySmallFontSize )
let text = " By using this service, you agree to our Terms and Conditions and Privacy Statement "
let attributedText = NSMutableAttributedString ( string : text )
attributedText . addAttribute ( . font , value : UIFont . boldSystemFont ( ofSize : Values . verySmallFontSize ) , range : ( text as NSString ) . range ( of : " Terms and Conditions " ) )
attributedText . addAttribute ( . font , value : UIFont . boldSystemFont ( ofSize : Values . verySmallFontSize ) , range : ( text as NSString ) . range ( of : " Privacy Statement " ) )
result . attributedText = attributedText
result . numberOfLines = 0
result . textAlignment = . center
result . lineBreakMode = . byWordWrapping
return result
} ( )
2019-12-09 02:33:40 +01:00
// MARK: S e t t i n g s
override var preferredStatusBarStyle : UIStatusBarStyle { return . lightContent }
// 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
// S e t u p l o g o i m a g e v i e w
let logoImageView = UIImageView ( )
2019-12-17 18:04:31 +01:00
logoImageView . image = # imageLiteral ( resourceName : " Session " )
2019-12-09 02:33:40 +01:00
logoImageView . contentMode = . scaleAspectFit
logoImageView . set ( . width , to : 32 )
logoImageView . set ( . height , to : 32 )
navigationItem . titleView = logoImageView
// S e t u p t i t l e l a b e l
let titleLabel = UILabel ( )
titleLabel . textColor = Colors . text
titleLabel . font = . boldSystemFont ( ofSize : Values . veryLargeFontSize )
titleLabel . text = NSLocalizedString ( " Restore your account using your seed " , comment : " " )
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 )
2019-12-13 05:02:05 +01:00
explanationLabel . text = " Enter the seed that was given to you when you signed up to restore your account. "
2019-12-09 02:33:40 +01:00
explanationLabel . numberOfLines = 0
explanationLabel . lineBreakMode = . byWordWrapping
2019-12-09 03:24:03 +01:00
// S e t u p l e g a l l a b e l
legalLabel . isUserInteractionEnabled = true
let legalLabelTapGestureRecognizer = UITapGestureRecognizer ( target : self , action : #selector ( handleLegalLabelTapped ) )
legalLabel . addGestureRecognizer ( legalLabelTapGestureRecognizer )
2019-12-09 02:33:40 +01:00
// S e t u p s p a c e r s
let topSpacer = UIView . vStretchingSpacer ( )
let spacer1 = UIView ( )
spacer1HeightConstraint = spacer1 . set ( . height , to : Values . veryLargeSpacing )
let spacer2 = UIView ( )
spacer2HeightConstraint = spacer2 . set ( . height , to : Values . veryLargeSpacing )
2019-12-09 03:24:03 +01:00
let spacer3 = UIView ( )
spacer3HeightConstraint = spacer3 . set ( . height , to : Values . veryLargeSpacing )
2019-12-09 02:33:40 +01:00
let bottomSpacer = UIView . vStretchingSpacer ( )
let restoreButtonBottomOffsetSpacer = UIView ( )
restoreButtonBottomOffsetConstraint = restoreButtonBottomOffsetSpacer . set ( . height , to : Values . onboardingButtonBottomOffset )
// S e t u p r e s t o r e b u t t o n
let restoreButton = Button ( style : . prominentFilled , size : . large )
restoreButton . setTitle ( NSLocalizedString ( " Continue " , comment : " " ) , for : UIControl . State . normal )
restoreButton . titleLabel ! . font = . boldSystemFont ( ofSize : Values . mediumFontSize )
restoreButton . addTarget ( self , action : #selector ( restore ) , for : UIControl . Event . touchUpInside )
// S e t u p r e s t o r e b u t t o n c o n t a i n e r
let restoreButtonContainer = UIView ( )
restoreButtonContainer . addSubview ( restoreButton )
restoreButton . pin ( . leading , to : . leading , of : restoreButtonContainer , withInset : Values . massiveSpacing )
restoreButton . pin ( . top , to : . top , of : restoreButtonContainer )
restoreButtonContainer . pin ( . trailing , to : . trailing , of : restoreButton , withInset : Values . massiveSpacing )
restoreButtonContainer . pin ( . bottom , to : . bottom , of : restoreButton )
// S e t u p t o p s t a c k v i e w
2019-12-09 03:24:03 +01:00
let topStackView = UIStackView ( arrangedSubviews : [ titleLabel , spacer1 , explanationLabel , spacer2 , mnemonicTextField , spacer3 , legalLabel ] )
2019-12-09 02:33:40 +01:00
topStackView . axis = . vertical
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 m a i n s t a c k v i e w
let mainStackView = UIStackView ( arrangedSubviews : [ topSpacer , topStackViewContainer , bottomSpacer , restoreButtonContainer , restoreButtonBottomOffsetSpacer ] )
mainStackView . axis = . vertical
mainStackView . alignment = . fill
view . addSubview ( mainStackView )
mainStackView . pin ( . leading , to : . leading , of : view )
mainStackView . pin ( . top , to : . top , of : view )
mainStackView . pin ( . trailing , to : . trailing , of : view )
bottomConstraint = mainStackView . pin ( . bottom , to : . bottom , of : view )
topSpacer . heightAnchor . constraint ( equalTo : bottomSpacer . heightAnchor , multiplier : 1 ) . isActive = true
// D i s m i s s k e y b o a r d o n t a p
let tapGestureRecognizer = UITapGestureRecognizer ( target : self , action : #selector ( dismissKeyboard ) )
view . addGestureRecognizer ( tapGestureRecognizer )
// L i s t e n t o k e y b o a r d n o t i f i c a t i o n s
let notificationCenter = NotificationCenter . default
notificationCenter . addObserver ( self , selector : #selector ( handleKeyboardWillChangeFrameNotification ( _ : ) ) , name : UIResponder . keyboardWillChangeFrameNotification , object : nil )
notificationCenter . addObserver ( self , selector : #selector ( handleKeyboardWillHideNotification ( _ : ) ) , name : UIResponder . keyboardWillHideNotification , object : nil )
}
override func viewDidAppear ( _ animated : Bool ) {
super . viewDidAppear ( animated )
mnemonicTextField . becomeFirstResponder ( )
}
deinit {
NotificationCenter . default . removeObserver ( self )
}
// MARK: G e n e r a l
@objc private func dismissKeyboard ( ) {
mnemonicTextField . resignFirstResponder ( )
}
// MARK: U p d a t i n g
@objc private func handleKeyboardWillChangeFrameNotification ( _ notification : Notification ) {
guard let newHeight = ( notification . userInfo ? [ UIResponder . keyboardFrameEndUserInfoKey ] as ? NSValue ) ? . cgRectValue . size . height else { return }
bottomConstraint . constant = - newHeight // N e g a t i v e d u e t o h o w t h e c o n s t r a i n t i s s e t u p
restoreButtonBottomOffsetConstraint . constant = Values . largeSpacing
spacer1HeightConstraint . constant = Values . mediumSpacing
spacer2HeightConstraint . constant = Values . mediumSpacing
2019-12-09 03:24:03 +01:00
spacer3HeightConstraint . constant = Values . mediumSpacing
2019-12-09 02:33:40 +01:00
UIView . animate ( withDuration : 0.25 ) {
self . view . layoutIfNeeded ( )
}
}
@objc private func handleKeyboardWillHideNotification ( _ notification : Notification ) {
bottomConstraint . constant = 0
restoreButtonBottomOffsetConstraint . constant = Values . onboardingButtonBottomOffset
spacer1HeightConstraint . constant = Values . veryLargeSpacing
spacer2HeightConstraint . constant = Values . veryLargeSpacing
2019-12-09 03:24:03 +01:00
spacer3HeightConstraint . constant = Values . veryLargeSpacing
2019-12-09 02:33:40 +01:00
UIView . animate ( withDuration : 0.25 ) {
self . view . layoutIfNeeded ( )
}
}
// MARK: I n t e r a c t i o n
@objc private func restore ( ) {
func showError ( title : String , message : String = " " ) {
let alert = UIAlertController ( title : title , message : message , preferredStyle : . alert )
alert . addAction ( UIAlertAction ( title : NSLocalizedString ( " OK " , comment : " " ) , style : . default , handler : nil ) )
presentAlert ( alert )
}
let mnemonic = mnemonicTextField . text !
do {
let hexEncodedSeed = try Mnemonic . decode ( mnemonic : mnemonic )
let seed = Data ( hex : hexEncodedSeed )
let keyPair = Curve25519 . generateKeyPair ( fromSeed : seed + seed )
let identityManager = OWSIdentityManager . shared ( )
let databaseConnection = identityManager . value ( forKey : " dbConnection " ) as ! YapDatabaseConnection
databaseConnection . setObject ( seed . toHexString ( ) , forKey : " LKLokiSeed " , inCollection : OWSPrimaryStorageIdentityKeyStoreCollection )
databaseConnection . setObject ( keyPair , forKey : OWSPrimaryStorageIdentityKeyStoreIdentityKey , inCollection : OWSPrimaryStorageIdentityKeyStoreCollection )
TSAccountManager . sharedInstance ( ) . phoneNumberAwaitingVerification = keyPair . hexEncodedPublicKey
mnemonicTextField . resignFirstResponder ( )
Timer . scheduledTimer ( withTimeInterval : 0.25 , repeats : false ) { _ in
let displayNameVC = DisplayNameVC ( )
self . navigationController ! . pushViewController ( displayNameVC , animated : true )
}
} catch let error {
let error = error as ? Mnemonic . DecodingError ? ? Mnemonic . DecodingError . generic
showError ( title : error . errorDescription ! )
}
}
2019-12-09 03:24:03 +01:00
@objc private func handleLegalLabelTapped ( _ tapGestureRecognizer : UITapGestureRecognizer ) {
let url = URL ( string : " https://github.com/loki-project/loki-messenger-ios/blob/master/privacy-policy.md " ) !
UIApplication . shared . open ( url )
}
2019-12-09 02:33:40 +01:00
}