final class LandingVC : BaseVC , LinkDeviceVCDelegate , DeviceLinkingModalDelegate {
private var fakeChatViewContentOffset : CGPoint !
// MARK: C o m p o n e n t s
private lazy var fakeChatView : FakeChatView = {
let result = FakeChatView ( )
result . set ( . height , to : Values . fakeChatViewHeight )
return result
} ( )
private lazy var registerButton : Button = {
let result = Button ( style : . prominentFilled , size : . large )
result . setTitle ( NSLocalizedString ( " vc_landing_register_button_title " , comment : " " ) , for : UIControl . State . normal )
result . titleLabel ! . font = . boldSystemFont ( ofSize : Values . mediumFontSize )
result . addTarget ( self , action : #selector ( register ) , for : UIControl . Event . touchUpInside )
return result
} ( )
private lazy var restoreButton : Button = {
let result = Button ( style : . prominentOutline , size : . large )
result . setTitle ( NSLocalizedString ( " vc_landing_restore_button_title " , comment : " " ) , for : UIControl . State . normal )
result . titleLabel ! . font = . boldSystemFont ( ofSize : Values . mediumFontSize )
result . addTarget ( self , action : #selector ( restore ) , for : UIControl . Event . touchUpInside )
return result
} ( )
private lazy var linkButton : Button = {
let result = Button ( style : . regularBorderless , size : . small )
result . setTitle ( NSLocalizedString ( " vc_landing_link_button_title " , comment : " " ) , for : UIControl . State . normal )
result . titleLabel ! . font = . systemFont ( ofSize : Values . smallFontSize )
result . addTarget ( self , action : #selector ( linkDevice ) , for : UIControl . Event . touchUpInside )
return result
} ( )
// MARK: L i f e c y c l e
override func viewDidLoad ( ) {
super . viewDidLoad ( )
setUpGradientBackground ( )
setUpNavBarStyle ( )
setUpNavBarSessionIcon ( )
// 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 : isIPhone5OrSmaller ? Values . largeFontSize : Values . veryLargeFontSize )
titleLabel . text = NSLocalizedString ( " vc_landing_title_2 " , comment : " " )
titleLabel . numberOfLines = 0
titleLabel . lineBreakMode = . byWordWrapping
// S e t u p t i t l e l a b e l c o n t a i n e r
let titleLabelContainer = UIView ( )
titleLabelContainer . addSubview ( titleLabel )
titleLabel . pin ( . leading , to : . leading , of : titleLabelContainer , withInset : Values . veryLargeSpacing )
titleLabel . pin ( . top , to : . top , of : titleLabelContainer )
titleLabelContainer . pin ( . trailing , to : . trailing , of : titleLabel , withInset : Values . veryLargeSpacing )
titleLabelContainer . pin ( . bottom , to : . bottom , of : titleLabel )
// 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 l i n k b u t t o n c o n t a i n e r
let linkButtonContainer = UIView ( )
linkButtonContainer . set ( . height , to : Values . onboardingButtonBottomOffset )
// l i n k B u t t o n C o n t a i n e r . a d d S u b v i e w ( l i n k B u t t o n )
// l i n k B u t t o n . p i n ( . l e a d i n g , t o : . l e a d i n g , o f : l i n k B u t t o n C o n t a i n e r , w i t h I n s e t : V a l u e s . m a s s i v e S p a c i n g )
// l i n k B u t t o n . p i n ( . t o p , t o : . t o p , o f : l i n k B u t t o n C o n t a i n e r )
// l i n k B u t t o n C o n t a i n e r . p i n ( . t r a i l i n g , t o : . t r a i l i n g , o f : l i n k B u t t o n , w i t h I n s e t : V a l u e s . m a s s i v e S p a c i n g )
// l i n k B u t t o n C o n t a i n e r . p i n ( . b o t t o m , t o : . b o t t o m , o f : l i n k B u t t o n , w i t h I n s e t : i s I P h o n e 5 O r S m a l l e r ? 6 : 1 0 )
// S e t u p b u t t o n s t a c k v i e w
let buttonStackView = UIStackView ( arrangedSubviews : [ registerButton , restoreButton ] )
buttonStackView . axis = . vertical
buttonStackView . spacing = isIPhone5OrSmaller ? Values . smallSpacing : Values . mediumSpacing
buttonStackView . alignment = . fill
// S e t u p b u t t o n s t a c k v i e w c o n t a i n e r
let buttonStackViewContainer = UIView ( )
buttonStackViewContainer . addSubview ( buttonStackView )
buttonStackView . pin ( . leading , to : . leading , of : buttonStackViewContainer , withInset : isIPhone5OrSmaller ? CGFloat ( 52 ) : Values . massiveSpacing )
buttonStackView . pin ( . top , to : . top , of : buttonStackViewContainer )
buttonStackViewContainer . pin ( . trailing , to : . trailing , of : buttonStackView , withInset : isIPhone5OrSmaller ? CGFloat ( 52 ) : Values . massiveSpacing )
buttonStackViewContainer . pin ( . bottom , to : . bottom , of : buttonStackView )
// S e t u p m a i n s t a c k v i e w
let mainStackView = UIStackView ( arrangedSubviews : [ topSpacer , titleLabelContainer , UIView . spacer ( withHeight : isIPhone5OrSmaller ? Values . smallSpacing : Values . mediumSpacing ) , fakeChatView , bottomSpacer , buttonStackViewContainer , linkButtonContainer ] )
mainStackView . axis = . vertical
mainStackView . alignment = . fill
view . addSubview ( mainStackView )
mainStackView . pin ( to : view )
topSpacer . heightAnchor . constraint ( equalTo : bottomSpacer . heightAnchor , multiplier : 1 ) . isActive = true
// S h o w d e v i c e u n l i n k e d a l e r t i f n e e d e d
if UserDefaults . standard [ . wasUnlinked ] {
let alert = UIAlertController ( title : " Device Unlinked " , message : NSLocalizedString ( " vc_landing_device_unlinked_modal_title " , comment : " " ) , preferredStyle : . alert )
alert . addAction ( UIAlertAction ( title : NSLocalizedString ( " OK " , comment : " " ) , accessibilityIdentifier : nil , style : . default , handler : nil ) )
present ( alert , animated : true , completion : nil )
UserDefaults . removeAll ( )
}
}
override func viewDidDisappear ( _ animated : Bool ) {
super . viewDidAppear ( animated )
if let fakeChatViewContentOffset = fakeChatViewContentOffset {
fakeChatView . contentOffset = fakeChatViewContentOffset
}
}
// MARK: I n t e r a c t i o n
@objc private func register ( ) {
fakeChatViewContentOffset = fakeChatView . contentOffset
DispatchQueue . main . async {
self . fakeChatView . contentOffset = self . fakeChatViewContentOffset
}
let registerVC = RegisterVC ( )
navigationController ! . pushViewController ( registerVC , animated : true )
}
@objc private func restore ( ) {
fakeChatViewContentOffset = fakeChatView . contentOffset
DispatchQueue . main . async {
self . fakeChatView . contentOffset = self . fakeChatViewContentOffset
}
let restoreVC = RestoreVC ( )
navigationController ! . pushViewController ( restoreVC , animated : true )
}
@objc private func linkDevice ( ) {
let linkDeviceVC = LinkDeviceVC ( )
linkDeviceVC . delegate = self
let navigationController = OWSNavigationController ( rootViewController : linkDeviceVC )
present ( navigationController , animated : true , completion : nil )
}
// MARK: D e v i c e L i n k i n g
func requestDeviceLink ( with hexEncodedPublicKey : String ) {
guard ECKeyPair . isValidHexEncodedPublicKey ( candidate : hexEncodedPublicKey ) else {
let alert = UIAlertController ( title : NSLocalizedString ( " invalid_session_id " , comment : " " ) , message : " Please make sure the Session ID you entered is correct and try again. " , preferredStyle : . alert )
alert . addAction ( UIAlertAction ( title : NSLocalizedString ( " OK " , comment : " " ) , accessibilityIdentifier : nil , style : . default , handler : nil ) )
return present ( alert , animated : true , completion : nil )
}
let seed = Randomness . generateRandomBytes ( 16 )
preconditionFailure ( " This code path shouldn't be invoked. " )
let keyPair = Curve25519 . generateKeyPair ( )
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
TSAccountManager . sharedInstance ( ) . didRegister ( )
let appDelegate = UIApplication . shared . delegate as ! AppDelegate
appDelegate . startPollerIfNeeded ( )
let deviceLinkingModal = DeviceLinkingModal ( mode : . slave , delegate : self )
deviceLinkingModal . modalPresentationStyle = . overFullScreen
deviceLinkingModal . modalTransitionStyle = . crossDissolve
self . present ( deviceLinkingModal , animated : true , completion : nil )
let linkingRequestMessage = DeviceLinkingUtilities . getLinkingRequestMessage ( for : hexEncodedPublicKey )
ThreadUtil . enqueue ( linkingRequestMessage )
}
func handleDeviceLinkAuthorized ( _ deviceLink : DeviceLink ) {
UserDefaults . standard [ . masterHexEncodedPublicKey ] = deviceLink . master . publicKey
fakeChatViewContentOffset = fakeChatView . contentOffset
DispatchQueue . main . async {
self . fakeChatView . contentOffset = self . fakeChatViewContentOffset
}
let homeVC = HomeVC ( )
navigationController ! . setViewControllers ( [ homeVC ] , animated : true )
}
func handleDeviceLinkingModalDismissed ( ) {
let appDelegate = UIApplication . shared . delegate as ! AppDelegate
appDelegate . stopPoller ( )
TSAccountManager . sharedInstance ( ) . resetForReregistration ( )
}
// MARK: C o n v e n i e n c e
private func setUserInteractionEnabled ( _ isEnabled : Bool ) {
[ registerButton , restoreButton , linkButton ] . forEach {
$0 . isUserInteractionEnabled = isEnabled
}
}
}