2020-04-15 05:40:19 +02:00
import PromiseKit
2020-04-14 07:07:37 +02:00
final class PNModeVC : BaseVC , OptionViewDelegate {
private var optionViews : [ OptionView ] {
2020-04-15 06:39:55 +02:00
[ apnsOptionView , backgroundPollingOptionView ]
2020-04-14 07:07:37 +02:00
}
private var selectedOptionView : OptionView ? {
return optionViews . first { $0 . isSelected }
}
// MARK: C o m p o n e n t s
2020-04-15 07:55:32 +02:00
private lazy var apnsOptionView = OptionView ( title : NSLocalizedString ( " Apple Push Notification Service " , comment : " " ) , explanation : NSLocalizedString ( " Session will use the Apple Push Notification Service to receive push notifications. You’ ll be notified of new messages reliably and immediately. Using APNs means that this device will communicate directly with Apple’ s servers to retrieve push notifications, which will expose your IP address to Apple. Your messages will still be onion-routed and end-to-end encrypted, so the contents of your messages will remain completely private. " , comment : " " ) , delegate : self , isRecommended : true )
private lazy var backgroundPollingOptionView = OptionView ( title : NSLocalizedString ( " Background Polling " , comment : " " ) , explanation : NSLocalizedString ( " Session will occasionally check for new messages in the background. This guarantees full privacy protection, but message notifications may be significantly delayed. " , comment : " " ) , delegate : self )
2020-04-14 07:07:37 +02:00
// MARK: L i f e c y c l e
override func viewDidLoad ( ) {
super . 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 ( )
logoImageView . image = # imageLiteral ( resourceName : " SessionGreen32 " )
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 : isSmallScreen ? Values . largeFontSize : Values . veryLargeFontSize )
2020-04-15 07:55:32 +02:00
titleLabel . text = NSLocalizedString ( " Push Notifications " , comment : " " )
2020-04-14 07:07:37 +02: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-04-15 07:55:32 +02:00
explanationLabel . text = NSLocalizedString ( " There are two ways Session can handle push notifications. Make sure to read the descriptions carefully before you choose. " , comment : " " )
2020-04-14 07:07:37 +02:00
explanationLabel . numberOfLines = 0
explanationLabel . lineBreakMode = . byWordWrapping
// S e t u p s p a c e r s
let topSpacer = UIView . vStretchingSpacer ( )
let bottomSpacer = UIView . vStretchingSpacer ( )
let registerButtonBottomOffsetSpacer = UIView ( )
registerButtonBottomOffsetSpacer . set ( . height , to : Values . onboardingButtonBottomOffset )
// S e t u p r e g i s t e r b u t t o n
let registerButton = Button ( style : . prominentFilled , size : . large )
registerButton . setTitle ( NSLocalizedString ( " Continue " , comment : " " ) , for : UIControl . State . normal )
registerButton . titleLabel ! . font = . boldSystemFont ( ofSize : Values . mediumFontSize )
registerButton . addTarget ( self , action : #selector ( register ) , for : UIControl . Event . touchUpInside )
// S e t u p r e g i s t e r b u t t o n c o n t a i n e r
let registerButtonContainer = UIView ( wrapping : registerButton , withInsets : UIEdgeInsets ( top : 0 , leading : Values . massiveSpacing , bottom : 0 , trailing : Values . massiveSpacing ) )
// S e t u p o p t i o n s s t a c k v i e w
let optionsStackView = UIStackView ( arrangedSubviews : optionViews )
optionsStackView . axis = . vertical
optionsStackView . spacing = Values . smallSpacing
optionsStackView . alignment = . fill
// S e t u p t o p s t a c k v i e w
let topStackView = UIStackView ( arrangedSubviews : [ titleLabel , explanationLabel , optionsStackView ] )
topStackView . axis = . vertical
topStackView . spacing = isSmallScreen ? Values . smallSpacing : Values . veryLargeSpacing
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 ( wrapping : topStackView , withInsets : UIEdgeInsets ( top : 0 , leading : Values . veryLargeSpacing , bottom : 0 , trailing : Values . veryLargeSpacing ) )
// 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 , registerButtonContainer , registerButtonBottomOffsetSpacer ] )
mainStackView . axis = . vertical
mainStackView . alignment = . fill
view . addSubview ( mainStackView )
mainStackView . pin ( to : view )
topSpacer . heightAnchor . constraint ( equalTo : bottomSpacer . heightAnchor , multiplier : 1 ) . isActive = true
}
// MARK: I n t e r a c t i o n
fileprivate func optionViewDidActivate ( _ optionView : OptionView ) {
optionViews . filter { $0 != optionView } . forEach { $0 . isSelected = false }
}
@objc private func register ( ) {
2020-04-14 08:57:32 +02:00
guard selectedOptionView != nil else {
let title = NSLocalizedString ( " Please Pick an Option " , comment : " " )
let alert = UIAlertController ( title : title , message : nil , preferredStyle : . alert )
alert . addAction ( UIAlertAction ( title : NSLocalizedString ( " OK " , comment : " " ) , style : . default , handler : nil ) )
return present ( alert , animated : true , completion : nil )
}
2020-04-14 08:32:10 +02:00
UserDefaults . standard [ . isUsingFullAPNs ] = ( selectedOptionView = = apnsOptionView )
2020-04-14 08:11:22 +02:00
TSAccountManager . sharedInstance ( ) . didRegister ( )
let homeVC = HomeVC ( )
navigationController ! . setViewControllers ( [ homeVC ] , animated : true )
2020-04-15 06:39:55 +02:00
let _ : Promise < Void > = SyncPushTokensJob . run ( accountManager : AppEnvironment . shared . accountManager , preferences : Environment . shared . preferences )
2020-04-14 07:07:37 +02:00
}
}
// MARK: O p t i o n V i e w
private extension PNModeVC {
final class OptionView : UIView {
private let title : String
private let explanation : String
private let delegate : OptionViewDelegate
2020-04-15 05:40:19 +02:00
private let isRecommended : Bool
2020-04-14 07:07:37 +02:00
var isSelected = false { didSet { handleIsSelectedChanged ( ) } }
2020-04-15 05:40:19 +02:00
init ( title : String , explanation : String , delegate : OptionViewDelegate , isRecommended : Bool = false ) {
2020-04-14 07:07:37 +02:00
self . title = title
self . explanation = explanation
self . delegate = delegate
2020-04-15 05:40:19 +02:00
self . isRecommended = isRecommended
2020-04-14 07:07:37 +02:00
super . init ( frame : CGRect . zero )
setUpViewHierarchy ( )
}
override init ( frame : CGRect ) {
preconditionFailure ( " Use init(string:explanation:) instead. " )
}
required init ? ( coder : NSCoder ) {
preconditionFailure ( " Use init(string:explanation:) instead. " )
}
private func setUpViewHierarchy ( ) {
backgroundColor = Colors . pnOptionBackground
// R o u n d c o r n e r s
layer . cornerRadius = Values . pnOptionCornerRadius
// S e t u p b o r d e r
layer . borderWidth = Values . borderThickness
layer . borderColor = Colors . pnOptionBorder . cgColor
// S e t u p s h a d o w
layer . shadowColor = UIColor . black . cgColor
layer . shadowOffset = CGSize ( width : 0 , height : 0.8 )
layer . shadowOpacity = isLightMode ? 0.4 : 1
layer . shadowRadius = isLightMode ? 4 : 6
// 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 . mediumFontSize )
titleLabel . text = title
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 . verySmallFontSize )
explanationLabel . text = explanation
explanationLabel . numberOfLines = 0
explanationLabel . lineBreakMode = . byWordWrapping
// S e t u p s t a c k v i e w
let stackView = UIStackView ( arrangedSubviews : [ titleLabel , explanationLabel ] )
stackView . axis = . vertical
2020-04-15 05:57:03 +02:00
stackView . spacing = 4
2020-04-14 07:07:37 +02:00
stackView . alignment = . fill
addSubview ( stackView )
stackView . pin ( . leading , to : . leading , of : self , withInset : 12 )
stackView . pin ( . top , to : . top , of : self , withInset : 12 )
self . pin ( . trailing , to : . trailing , of : stackView , withInset : 12 )
self . pin ( . bottom , to : . bottom , of : stackView , withInset : 12 )
2020-04-15 05:40:19 +02:00
// S e t u p r e c o m m e n d e d l a b e l i f n e e d e d
if isRecommended {
let recommendedLabel = UILabel ( )
recommendedLabel . textColor = Colors . accent
recommendedLabel . font = . boldSystemFont ( ofSize : Values . verySmallFontSize )
2020-04-15 07:55:32 +02:00
recommendedLabel . text = NSLocalizedString ( " Recommended " , comment : " " )
2020-04-15 05:40:19 +02:00
stackView . addArrangedSubview ( recommendedLabel )
}
2020-04-14 07:07:37 +02:00
// S e t u p t a p g e s t u r e r e c o g n i z e r
let tapGestureRecognizer = UITapGestureRecognizer ( target : self , action : #selector ( handleTap ) )
addGestureRecognizer ( tapGestureRecognizer )
}
@objc private func handleTap ( ) {
isSelected = ! isSelected
}
private func handleIsSelectedChanged ( ) {
let animationDuration : TimeInterval = 0.25
2020-04-14 08:11:22 +02:00
// A n i m a t e b o r d e r c o l o r
let newBorderColor = isSelected ? Colors . accent . cgColor : Colors . pnOptionBorder . cgColor
let borderAnimation = CABasicAnimation ( keyPath : " borderColor " )
borderAnimation . fromValue = layer . shadowColor
borderAnimation . toValue = newBorderColor
borderAnimation . duration = animationDuration
layer . add ( borderAnimation , forKey : borderAnimation . keyPath )
layer . borderColor = newBorderColor
// A n i m a t e s h a d o w c o l o r
2020-04-14 07:07:37 +02:00
let newShadowColor = isSelected ? Colors . newConversationButtonShadow . cgColor : UIColor . black . cgColor
let shadowAnimation = CABasicAnimation ( keyPath : " shadowColor " )
shadowAnimation . fromValue = layer . shadowColor
shadowAnimation . toValue = newShadowColor
shadowAnimation . duration = animationDuration
layer . add ( shadowAnimation , forKey : shadowAnimation . keyPath )
layer . shadowColor = newShadowColor
2020-04-14 08:11:22 +02:00
// N o t i f y d e l e g a t e
2020-04-14 07:07:37 +02:00
if isSelected { delegate . optionViewDidActivate ( self ) }
}
}
}
// MARK: O p t i o n V i e w D e l e g a t e
private protocol OptionViewDelegate {
func optionViewDidActivate ( _ optionView : PNModeVC . OptionView )
}