@ -5,79 +5,264 @@
import UIKit
import PromiseKit
private protocol OnboardingCodeViewTextFieldDelegate {
func textFieldDidDeletePrevious ( )
}
// MARK: -
// E d i t i n g a c o d e s h o u l d f e e l s e a m l e s s , a s e v e n t h o u g h
// t h e U I T e x t F i e l d o n l y l e t s y o u e d i t a s i n g l e d i g i t a t
// a t i m e . F o r d e l e t e s t o w o r k p r o p e r l y , w e n e e d t o
// d e t e c t d e l e t e e v e n t s t h a t w o u l d a f f e c t t h e _ p r e v i o u s _
// d i g i t .
private class OnboardingCodeViewTextField : UITextField {
fileprivate var codeDelegate : OnboardingCodeViewTextFieldDelegate ?
override func deleteBackward ( ) {
var isDeletePrevious = false
if let selectedTextRange = selectedTextRange {
let cursorPosition = offset ( from : beginningOfDocument , to : selectedTextRange . start )
if cursorPosition = = 0 {
isDeletePrevious = true
}
}
super . deleteBackward ( )
if isDeletePrevious {
codeDelegate ? . textFieldDidDeletePrevious ( )
}
}
}
// MARK: -
protocol OnboardingCodeViewDelegate {
func codeViewDidChange ( )
}
// MARK: -
// T h e O n b o a r d i n g C o d e V i e w i s a s p e c i a l " v e r i f i c a t i o n c o d e "
// e d i t o r t h a t s h o u l d f e e l l i k e e d i t i n g a s i n g l e p i e c e
// o f t e x t ( a l a U I T e x t F i e l d ) e v e n t h o u g h t h e i n d i v i d u a l
// d i g i t s o f t h e c o d e a r e v i s u a l l y s e p a r a t e d .
//
// W e u s e a s e p a r a t e U I L a b e l f o r e a c h d i g i t , a n d m o v e
// a r o u n d a s i n g l e U I T e x t f i e l d t o l e t t h e u s e r e d i t t h e
// l a s t / n e x t d i g i t .
private class OnboardingCodeView : UIView {
var delegate : OnboardingCodeViewDelegate ?
public init ( ) {
super . init ( frame : . zero )
createSubviews ( )
updateViewState ( )
}
required init ? ( coder aDecoder : NSCoder ) {
fatalError ( " init(coder:) has not been implemented " )
}
private let digitCount = 6
private var digitLabels = [ UILabel ] ( )
// W e u s e a s i n g l e t e x t f i e l d t o e d i t t h e " c u r r e n t " d i g i t .
// T h e " c u r r e n t " d i g i t i s u s u a l l y t h e " l a s t "
fileprivate let textfield = OnboardingCodeViewTextField ( )
private var currentDigitIndex = 0
private var textfieldConstraints = [ NSLayoutConstraint ] ( )
// T h e c u r r e n t c o m p l e t e t e x t - t h e " m o d e l " f o r t h i s v i e w .
private var digitText = " "
var isComplete : Bool {
return digitText . count = = digitCount
}
private func createSubviews ( ) {
textfield . textAlignment = . left
textfield . delegate = self
textfield . keyboardType = . numberPad
textfield . textColor = Theme . primaryColor
textfield . font = UIFont . ows_dynamicTypeLargeTitle1Clamped
textfield . codeDelegate = self
var digitViews = [ UIView ] ( )
( 0. . < digitCount ) . forEach { ( _ ) in
let ( digitView , digitLabel ) = makeCellView ( text : " " , hasStroke : true )
digitLabels . append ( digitLabel )
digitViews . append ( digitView )
}
let ( hyphenView , _ ) = makeCellView ( text : " - " , hasStroke : false )
digitViews . insert ( hyphenView , at : 3 )
let stackView = UIStackView ( arrangedSubviews : digitViews )
stackView . axis = . horizontal
stackView . alignment = . center
stackView . spacing = 8
addSubview ( stackView )
stackView . autoPinHeightToSuperview ( )
stackView . autoHCenterInSuperview ( )
self . addSubview ( textfield )
}
private func makeCellView ( text : String , hasStroke : Bool ) -> ( UIView , UILabel ) {
let digitView = UIView ( )
let digitLabel = UILabel ( )
digitLabel . text = text
digitLabel . font = UIFont . ows_dynamicTypeLargeTitle1Clamped
digitLabel . textColor = Theme . primaryColor
digitLabel . textAlignment = . center
digitView . addSubview ( digitLabel )
digitLabel . autoCenterInSuperview ( )
if hasStroke {
let strokeView = UIView . container ( )
strokeView . backgroundColor = Theme . primaryColor
digitView . addSubview ( strokeView )
strokeView . autoPinWidthToSuperview ( )
strokeView . autoPinEdge ( toSuperviewEdge : . bottom )
strokeView . autoSetDimension ( . height , toSize : 1 )
}
let vMargin : CGFloat = 4
let cellHeight : CGFloat = digitLabel . font . lineHeight + vMargin * 2
let cellWidth : CGFloat = cellHeight * 2 / 3
digitView . autoSetDimensions ( to : CGSize ( width : cellWidth , height : cellHeight ) )
return ( digitView , digitLabel )
}
private func digit ( at index : Int ) -> String {
guard index < digitText . count else {
return " "
}
return digitText . substring ( from : index ) . trim ( after : 1 )
}
// E n s u r e t h a t a l l l a b e l s a r e d i s p l a y i n g t h e c o r r e c t
// d i g i t ( i f a n y ) a n d t h a t t h e U I T e x t F i e l d h a s r e p l a c e d
// t h e " c u r r e n t " d i g i t .
private func updateViewState ( ) {
currentDigitIndex = min ( digitCount - 1 ,
digitText . count )
( 0. . < digitCount ) . forEach { ( index ) in
let digitLabel = digitLabels [ index ]
digitLabel . text = digit ( at : index )
digitLabel . isHidden = index = = currentDigitIndex
}
NSLayoutConstraint . deactivate ( textfieldConstraints )
textfieldConstraints . removeAll ( )
let digitLabelToReplace = digitLabels [ currentDigitIndex ]
textfield . text = digit ( at : currentDigitIndex )
textfieldConstraints . append ( textfield . autoAlignAxis ( . horizontal , toSameAxisOf : digitLabelToReplace ) )
textfieldConstraints . append ( textfield . autoAlignAxis ( . vertical , toSameAxisOf : digitLabelToReplace ) )
// M o v e c u r s o r t o e n d o f t e x t .
let newPosition = textfield . endOfDocument
textfield . selectedTextRange = textfield . textRange ( from : newPosition , to : newPosition )
}
public override func becomeFirstResponder ( ) -> Bool {
return textfield . becomeFirstResponder ( )
}
}
// MARK: -
extension OnboardingCodeView : UITextFieldDelegate {
public func textField ( _ textField : UITextField , shouldChangeCharactersIn range : NSRange , replacementString newString : String ) -> Bool {
var oldText = " "
if let textFieldText = textField . text {
oldText = textFieldText
}
let left = oldText . substring ( to : range . location )
let right = oldText . substring ( from : range . location + range . length )
let unfiltered = left + newString + right
let characterSet = CharacterSet ( charactersIn : " 0123456789 " )
let filtered = unfiltered . components ( separatedBy : characterSet . inverted ) . joined ( )
let filteredAndTrimmed = filtered . trim ( after : 1 )
textField . text = filteredAndTrimmed
digitText = digitText . trim ( after : currentDigitIndex ) + filteredAndTrimmed
updateViewState ( )
// I n f o r m o u r c a l l e r t h a t w e t o o k c a r e o f p e r f o r m i n g t h e c h a n g e .
return false
}
public func textFieldShouldReturn ( _ textField : UITextField ) -> Bool {
return false
}
}
// MARK: -
extension OnboardingCodeView : OnboardingCodeViewTextFieldDelegate {
public func textFieldDidDeletePrevious ( ) {
guard digitText . count > 0 else {
return
}
digitText = digitText . substring ( to : currentDigitIndex - 1 )
updateViewState ( )
}
}
// MARK: -
@objc
public class OnboardingVerificationViewController : OnboardingBaseViewController {
// / / MARK: - D e p e n d e n c i e s
//
// p r i v a t e v a r t s A c c o u n t M a n a g e r : T S A c c o u n t M a n a g e r {
// r e t u r n T S A c c o u n t M a n a g e r . s h a r e d I n s t a n c e ( )
// }
private enum CodeState {
case pending
case possiblyNotDelivered
case resent
}
// MARK: -
private var codeState = CodeState . pending
private var titleLabel : UILabel ?
private let phoneNumberTextField = UITextField ( )
// p r i v a t e v a r n e x t B u t t o n : O W S F l a t B u t t o n ?
private var resendCodeLabel : OWSFlatButton ?
private var resendCodeLink : OWSFlatButton ?
private let onboardingCodeView = OnboardingCodeView ( )
private var codeStateLink : OWSFlatButton ?
override public func loadView ( ) {
super . loadView ( )
// p o p u l a t e D e f a u l t s ( )
view . backgroundColor = Theme . backgroundColor
view . layoutMargins = . zero
var e164PhoneNumber = " "
if let phoneNumber = onboardingController . phoneNumber {
e164PhoneNumber = phoneNumber . e164
}
let formattedPhoneNumber = PhoneNumber . bestEffortLocalizedPhoneNumber ( withE164 : e164PhoneNumber )
let titleText = String ( format : NSLocalizedString ( " ONBOARDING_VERIFICATION_TITLE_FORMAT " ,
comment : " Format for the title of the 'onboarding verification' view. Embeds {{the user's phone number}}. " ) ,
formattedPhoneNumber )
let titleLabel = self . titleLabel ( text : titleText )
let titleLabel = self . titleLabel ( text : " " )
self . titleLabel = titleLabel
let backLink = self . linkButton ( title : NSLocalizedString ( " ONBOARDING_VERIFICATION_BACK_LINK " ,
comment : " Label for the link that lets users change their phone number. " ) ,
selector : #selector ( backLinkTapped ) )
let onboardingCodeView = OnboardingCodeView ( )
onboardingCodeView . addRedBorder ( )
// r e s e n d C o d e L a b e l . t e x t = N S L o c a l i z e d S t r i n g ( " O N B O A R D I N G _ V E R I F I C A T I O N _ B A C K _ L I N K " ,
// c o m m e n t : " L a b e l f o r t h e l i n k t h a t l e t s u s e r s c h a n g e t h e i r p h o n e n u m b e r . " ) ,
// r e s e n d C o d e L a b e l . t e x t = " T O D O "
// r e s e n d C o d e L a b e l . t e x t C o l o r = T h e m e . s e c o n d a r y C o l o r
// r e s e n d C o d e L a b e l . f o n t = U I F o n t . o w s _ d y n a m i c T y p e B o d y C l a m p e d
// TODO: C o p y .
let resendCodeLabel = disabledLinkButton ( title : NSLocalizedString ( " ONBOARDING_VERIFICATION_RESEND_CODE_LINK " ,
comment : " Label for the link that lets users request another verification code. " ) ,
selector : #selector ( ignoreEvent ) )
self . resendCodeLabel = resendCodeLabel
let resendCodeLink = self . linkButton ( title : NSLocalizedString ( " ONBOARDING_VERIFICATION_RESEND_CODE_LINK " ,
comment : " Label for the link that lets users request another verification code. " ) ,
let codeStateLink = self . linkButton ( title : " " ,
selector : #selector ( resendCodeLinkTapped ) )
self . resendCodeLink = resendCodeLink
let resentCodeWrapper = UIView . container ( )
resentCodeWrapper . addSubview ( resendCodeLabel )
resentCodeWrapper . addSubview ( resendCodeLink )
resendCodeLabel . autoPinEdgesToSuperviewEdges ( )
resendCodeLink . autoPinEdgesToSuperviewEdges ( )
// TODO: F i n a l i z e c o p y .
codeStateLink . enableMultilineLabel ( )
self . codeStateLink = codeStateLink
// l e t n e x t B u t t o n = s e l f . b u t t o n ( t i t l e : N S L o c a l i z e d S t r i n g ( " B U T T O N _ N E X T " ,
// c o m m e n t : " L a b e l f o r t h e ' n e x t ' b u t t o n . " ) ,
// s e l e c t o r : # s e l e c t o r ( n e x t P r e s s e d ) )
// s e l f . n e x t B u t t o n = n e x t B u t t o n
let topSpacer = UIView . vStretchingSpacer ( )
let bottomSpacer = UIView . vStretchingSpacer ( )
@ -87,11 +272,8 @@ public class OnboardingVerificationViewController: OnboardingBaseViewController
backLink ,
topSpacer ,
onboardingCodeView ,
// c o u n t r y R o w ,
// U I V i e w . s p a c e r ( w i t h H e i g h t : 8 ) ,
// p h o n e N u m b e r R o w ,
bottomSpacer ,
resentCodeWrapper
codeStateLink
] )
stackView . axis = . vertical
stackView . alignment = . fill
@ -104,209 +286,122 @@ public class OnboardingVerificationViewController: OnboardingBaseViewController
// E n s u r e w h i t e s p a c e i s b a l a n c e d , s o i n p u t s a r e v e r t i c a l l y c e n t e r e d .
topSpacer . autoMatch ( . height , to : . height , of : bottomSpacer )
startCodeCountdown ( )
updateCodeState ( )
}
// p r i v a t e f u n c a d d B o t t o m S t r o k e ( _ v i e w : U I V i e w ) {
// l e t s t r o k e V i e w = U I V i e w ( )
// s t r o k e V i e w . b a c k g r o u n d C o l o r = T h e m e . m i d d l e G r a y C o l o r
// v i e w . a d d S u b v i e w ( s t r o k e V i e w )
// s t r o k e V i e w . a u t o S e t D i m e n s i o n ( . h e i g h t , t o S i z e : C G H a i r l i n e W i d t h ( ) )
// s t r o k e V i e w . a u t o P i n W i d t h T o S u p e r v i e w ( )
// s t r o k e V i e w . a u t o P i n E d g e ( t o S u p e r v i e w E d g e : . b o t t o m )
// }
//
// p u b l i c o v e r r i d e f u n c v i e w D i d A p p e a r ( _ a n i m a t e d : B o o l ) {
// s u p e r . v i e w D i d A p p e a r ( a n i m a t e d )
//
// p h o n e N u m b e r T e x t F i e l d . b e c o m e F i r s t R e s p o n d e r ( )
//
// i f t s A c c o u n t M a n a g e r . i s R e r e g i s t e r i n g ( ) {
// / / I f r e - r e g i s t e r i n g , p r e - p o p u l a t e t h e c o u n t r y ( c o u n t r y c o d e , c a l l i n g c o d e , c o u n t r y n a m e )
// / / a n d p h o n e n u m b e r s t a t e .
// g u a r d l e t p h o n e N u m b e r E 1 6 4 = t s A c c o u n t M a n a g e r . r e r e g i s t e r a t i o n P h o n e N u m b e r ( ) e l s e {
// o w s F a i l D e b u g ( " C o u l d n o t r e s u m e r e - r e g i s t r a t i o n ; m i s s i n g p h o n e n u m b e r . " )
// r e t u r n
// }
// t r y T o R e r e g i s t e r ( p h o n e N u m b e r E 1 6 4 : p h o n e N u m b e r E 1 6 4 )
// }
// }
//
// p r i v a t e f u n c t r y T o R e r e g i s t e r ( p h o n e N u m b e r E 1 6 4 : S t r i n g ) {
// g u a r d p h o n e N u m b e r E 1 6 4 . c o u n t > 0 e l s e {
// o w s F a i l D e b u g ( " C o u l d n o t r e s u m e r e - r e g i s t r a t i o n ; i n v a l i d p h o n e N u m b e r E 1 6 4 . " )
// r e t u r n
// }
// g u a r d l e t p a r s e d P h o n e N u m b e r = P h o n e N u m b e r ( f r o m E 1 6 4 : p h o n e N u m b e r E 1 6 4 ) e l s e {
// o w s F a i l D e b u g ( " C o u l d n o t r e s u m e r e - r e g i s t r a t i o n ; c o u l d n ' t p a r s e p h o n e N u m b e r E 1 6 4 . " )
// r e t u r n
// }
// g u a r d l e t c a l l i n g C o d e N u m e r i c = p a r s e d P h o n e N u m b e r . g e t C o u n t r y C o d e ( ) e l s e {
// o w s F a i l D e b u g ( " C o u l d n o t r e s u m e r e - r e g i s t r a t i o n ; m i s s i n g c a l l i n g C o d e . " )
// r e t u r n
// }
// l e t c a l l i n g C o d e = " \ ( C O U N T R Y _ C O D E _ P R E F I X ) \ ( c a l l i n g C o d e N u m e r i c ) "
// l e t c o u n t r y C o d e s : [ S t r i n g ] =
// P h o n e N u m b e r U t i l . s h a r e d T h r e a d L o c a l ( ) . c o u n t r y C o d e s ( f r o m C a l l i n g C o d e : c a l l i n g C o d e )
// g u a r d l e t c o u n t r y C o d e = c o u n t r y C o d e s . f i r s t e l s e {
// o w s F a i l D e b u g ( " C o u l d n o t r e s u m e r e - r e g i s t r a t i o n ; u n k n o w n c o u n t r y C o d e . " )
// r e t u r n
// }
// g u a r d l e t c o u n t r y N a m e = P h o n e N u m b e r U t i l . c o u n t r y N a m e ( f r o m C o u n t r y C o d e : c o u n t r y C o d e ) e l s e {
// o w s F a i l D e b u g ( " C o u l d n o t r e s u m e r e - r e g i s t r a t i o n ; u n k n o w n c o u n t r y N a m e . " )
// r e t u r n
// }
// i f ! p h o n e N u m b e r E 1 6 4 . h a s P r e f i x ( c a l l i n g C o d e ) {
// o w s F a i l D e b u g ( " C o u l d n o t r e s u m e r e - r e g i s t r a t i o n ; n o n - m a t c h i n g c a l l i n g c o d e . " )
// r e t u r n
// }
// l e t p h o n e N u m b e r W i t h o u t C a l l i n g C o d e = p h o n e N u m b e r E 1 6 4 . s u b s t r i n g ( f r o m : c a l l i n g C o d e . c o u n t )
//
// g u a r d c o u n t r y C o d e . c o u n t > 0 e l s e {
// o w s F a i l D e b u g ( " I n v a l i d c o u n t r y c o d e . " )
// r e t u r n
// }
// g u a r d c o u n t r y N a m e . c o u n t > 0 e l s e {
// o w s F a i l D e b u g ( " I n v a l i d c o u n t r y n a m e . " )
// r e t u r n
// }
// g u a r d c a l l i n g C o d e . c o u n t > 0 e l s e {
// o w s F a i l D e b u g ( " I n v a l i d c a l l i n g c o d e . " )
// r e t u r n
// }
//
// l e t c o u n t r y S t a t e = O n b o a r d i n g C o u n t r y S t a t e ( c o u n t r y N a m e : c o u n t r y N a m e , c a l l i n g C o d e : c a l l i n g C o d e , c o u n t r y C o d e : c o u n t r y C o d e )
// o n b o a r d i n g C o n t r o l l e r . u p d a t e ( c o u n t r y S t a t e : c o u n t r y S t a t e )
//
// u p d a t e S t a t e ( )
//
// p h o n e N u m b e r T e x t F i e l d . t e x t = p h o n e N u m b e r W i t h o u t C a l l i n g C o d e
// / / D o n ' t l e t u s e r e d i t t h e i r p h o n e n u m b e r w h i l e r e - r e g i s t e r i n g .
// p h o n e N u m b e r T e x t F i e l d . i s E n a b l e d = f a l s e
// }
//
// / / MARK: -
//
// p r i v a t e v a r c o u n t r y N a m e : S t r i n g {
// g e t {
// r e t u r n o n b o a r d i n g C o n t r o l l e r . c o u n t r y S t a t e . c o u n t r y N a m e
// }
// }
// p r i v a t e v a r c a l l i n g C o d e : S t r i n g {
// g e t {
// A s s e r t I s O n M a i n T h r e a d ( )
//
// r e t u r n o n b o a r d i n g C o n t r o l l e r . c o u n t r y S t a t e . c a l l i n g C o d e
// }
// }
// p r i v a t e v a r c o u n t r y C o d e : S t r i n g {
// g e t {
// A s s e r t I s O n M a i n T h r e a d ( )
//
// r e t u r n o n b o a r d i n g C o n t r o l l e r . c o u n t r y S t a t e . c o u n t r y C o d e
// }
// }
//
// p r i v a t e f u n c p o p u l a t e D e f a u l t s ( ) {
// i f l e t l a s t R e g i s t e r e d P h o n e N u m b e r = O n b o a r d i n g C o n t r o l l e r . l a s t R e g i s t e r e d P h o n e N u m b e r ( ) ,
// l a s t R e g i s t e r e d P h o n e N u m b e r . c o u n t > 0 ,
// l a s t R e g i s t e r e d P h o n e N u m b e r . h a s P r e f i x ( c a l l i n g C o d e ) {
// p h o n e N u m b e r T e x t F i e l d . t e x t = l a s t R e g i s t e r e d P h o n e N u m b e r . s u b s t r i n g ( f r o m : c a l l i n g C o d e . c o u n t )
// } e l s e i f l e t p h o n e N u m b e r = o n b o a r d i n g C o n t r o l l e r . p h o n e N u m b e r {
// p h o n e N u m b e r T e x t F i e l d . t e x t = p h o n e N u m b e r . u s e r I n p u t
// }
//
// u p d a t e S t a t e ( )
// }
//
// p r i v a t e f u n c u p d a t e S t a t e ( ) {
// A s s e r t I s O n M a i n T h r e a d ( )
//
// c o u n t r y N a m e L a b e l . t e x t = c o u n t r y N a m e
// c a l l i n g C o d e L a b e l . t e x t = c a l l i n g C o d e
//
// s e l f . p h o n e N u m b e r T e x t F i e l d . p l a c e h o l d e r = V i e w C o n t r o l l e r U t i l s . e x a m p l e P h o n e N u m b e r ( f o r C o u n t r y C o d e : c o u n t r y C o d e , c a l l i n g C o d e : c a l l i n g C o d e )
// }
//
// / / MARK: - E v e n t s
//
// @ o b j c f u n c c o u n t r y R o w T a p p e d ( s e n d e r : U I G e s t u r e R e c o g n i z e r ) {
// g u a r d s e n d e r . s t a t e = = . r e c o g n i z e d e l s e {
// r e t u r n
// }
// s h o w C o u n t r y P i c k e r ( )
// }
//
// @ o b j c f u n c c o u n t r y C o d e T a p p e d ( s e n d e r : U I G e s t u r e R e c o g n i z e r ) {
// g u a r d s e n d e r . s t a t e = = . r e c o g n i z e d e l s e {
// r e t u r n
// }
// s h o w C o u n t r y P i c k e r ( )
// }
//
// @ o b j c f u n c n e x t P r e s s e d ( ) {
// L o g g e r . i n f o ( " " )
//
// p a r s e A n d T r y T o R e g i s t e r ( )
// }
//
// / / MARK: - C o u n t r y P i c k e r
//
// p r i v a t e f u n c s h o w C o u n t r y P i c k e r ( ) {
// g u a r d ! t s A c c o u n t M a n a g e r . i s R e r e g i s t e r i n g ( ) e l s e {
// r e t u r n
// }
//
// l e t c o u n t r y C o d e C o n t r o l l e r = C o u n t r y C o d e V i e w C o n t r o l l e r ( )
// c o u n t r y C o d e C o n t r o l l e r . c o u n t r y C o d e D e l e g a t e = s e l f
// c o u n t r y C o d e C o n t r o l l e r . i n t e r f a c e O r i e n t a t i o n M a s k = . p o r t r a i t
// l e t n a v i g a t i o n C o n t r o l l e r = O W S N a v i g a t i o n C o n t r o l l e r ( r o o t V i e w C o n t r o l l e r : c o u n t r y C o d e C o n t r o l l e r )
// s e l f . p r e s e n t ( n a v i g a t i o n C o n t r o l l e r , a n i m a t e d : t r u e , c o m p l e t i o n : n i l )
// }
//
// / / MARK: - R e g i s t e r
//
// p r i v a t e f u n c p a r s e A n d T r y T o R e g i s t e r ( ) {
// g u a r d l e t p h o n e N u m b e r T e x t = p h o n e N u m b e r T e x t F i e l d . t e x t ? . o w s _ s t r i p p e d ( ) ,
// p h o n e N u m b e r T e x t . c o u n t > 0 e l s e {
// O W S A l e r t s . s h o w A l e r t ( t i t l e :
// N S L o c a l i z e d S t r i n g ( " R E G I S T R A T I O N _ V I E W _ N O _ V E R I F I C A T I O N _ A L E R T _ T I T L E " ,
// c o m m e n t : " T i t l e o f a l e r t i n d i c a t i n g t h a t u s e r s n e e d s t o e n t e r a p h o n e n u m b e r t o r e g i s t e r . " ) ,
// m e s s a g e :
// N S L o c a l i z e d S t r i n g ( " R E G I S T R A T I O N _ V I E W _ N O _ V E R I F I C A T I O N _ A L E R T _ M E S S A G E " ,
// c o m m e n t : " M e s s a g e o f a l e r t i n d i c a t i n g t h a t u s e r s n e e d s t o e n t e r a p h o n e n u m b e r t o r e g i s t e r . " ) )
// r e t u r n
// }
//
// l e t p h o n e N u m b e r = " \ ( c a l l i n g C o d e ) \ ( p h o n e N u m b e r T e x t ) "
// g u a r d l e t l o c a l N u m b e r = P h o n e N u m b e r . t r y P a r s e P h o n e N u m b e r ( f r o m U s e r S p e c i f i e d T e x t : p h o n e N u m b e r ) ,
// l o c a l N u m b e r . t o E 1 6 4 ( ) . c o u n t > 0 ,
// P h o n e N u m b e r V a l i d a t o r ( ) . i s V a l i d F o r R e g i s t r a t i o n ( p h o n e N u m b e r : l o c a l N u m b e r ) e l s e {
// O W S A l e r t s . s h o w A l e r t ( t i t l e :
// N S L o c a l i z e d S t r i n g ( " R E G I S T R A T I O N _ V I E W _ I N V A L I D _ V E R I F I C A T I O N _ A L E R T _ T I T L E " ,
// c o m m e n t : " T i t l e o f a l e r t i n d i c a t i n g t h a t u s e r s n e e d s t o e n t e r a v a l i d p h o n e n u m b e r t o r e g i s t e r . " ) ,
// m e s s a g e :
// N S L o c a l i z e d S t r i n g ( " R E G I S T R A T I O N _ V I E W _ I N V A L I D _ V E R I F I C A T I O N _ A L E R T _ M E S S A G E " ,
// c o m m e n t : " M e s s a g e o f a l e r t i n d i c a t i n g t h a t u s e r s n e e d s t o e n t e r a v a l i d p h o n e n u m b e r t o r e g i s t e r . " ) )
// r e t u r n
// }
// l e t e 1 6 4 P h o n e N u m b e r = l o c a l N u m b e r . t o E 1 6 4 ( )
//
// o n b o a r d i n g C o n t r o l l e r . u p d a t e ( p h o n e N u m b e r : O n b o a r d i n g P h o n e N u m b e r ( e 1 6 4 : e 1 6 4 P h o n e N u m b e r , u s e r I n p u t : p h o n e N u m b e r T e x t ) )
//
// i f U I D e v i c e . c u r r e n t . i s I P a d {
// O W S A l e r t s . s h o w C o n f i r m a t i o n A l e r t ( t i t l e : N S L o c a l i z e d S t r i n g ( " R E G I S T R A T I O N _ I P A D _ C O N F I R M _ T I T L E " ,
// c o m m e n t : " a l e r t t i t l e w h e n r e g i s t e r i n g a n i P a d " ) ,
// m e s s a g e : N S L o c a l i z e d S t r i n g ( " R E G I S T R A T I O N _ I P A D _ C O N F I R M _ B O D Y " ,
// c o m m e n t : " a l e r t b o d y w h e n r e g i s t e r i n g a n i P a d " ) ,
// p r o c e e d T i t l e : N S L o c a l i z e d S t r i n g ( " R E G I S T R A T I O N _ I P A D _ C O N F I R M _ B U T T O N " ,
// c o m m e n t : " b u t t o n t e x t t o p r o c e e d w i t h r e g i s t r a t i o n w h e n o n a n i P a d " ) ,
// p r o c e e d A c t i o n : { ( _ ) i n
// s e l f . o n b o a r d i n g C o n t r o l l e r . t r y T o R e g i s t e r ( f r o m V i e w C o n t r o l l e r : s e l f , s m s V e r i f i c a t i o n : f a l s e )
// } )
// } e l s e {
// o n b o a r d i n g C o n t r o l l e r . t r y T o R e g i s t e r ( f r o m V i e w C o n t r o l l e r : s e l f , s m s V e r i f i c a t i o n : f a l s e )
// }
// }
// MARK: - C o d e S t a t e
private let countdownDuration : TimeInterval = 60
private var codeCountdownTimer : Timer ?
private var codeCountdownStart : NSDate ?
deinit {
if let codeCountdownTimer = codeCountdownTimer {
codeCountdownTimer . invalidate ( )
}
}
private func startCodeCountdown ( ) {
codeCountdownStart = NSDate ( )
codeCountdownTimer = Timer . weakScheduledTimer ( withTimeInterval : 1 , target : self , selector : #selector ( codeCountdownTimerFired ) , userInfo : nil , repeats : true )
}
@objc
public func codeCountdownTimerFired ( ) {
guard let codeCountdownStart = codeCountdownStart else {
owsFailDebug ( " Missing codeCountdownStart. " )
return
}
guard let codeCountdownTimer = codeCountdownTimer else {
owsFailDebug ( " Missing codeCountdownTimer. " )
return
}
let countdownInterval = abs ( codeCountdownStart . timeIntervalSinceNow )
guard countdownInterval < countdownDuration else {
// C o u n t d o w n c o m p l e t e .
codeCountdownTimer . invalidate ( )
self . codeCountdownTimer = nil
if codeState != . pending {
owsFailDebug ( " Unexpected codeState: \( codeState ) " )
}
codeState = . possiblyNotDelivered
updateCodeState ( )
return
}
// U p d a t e t h e " c o d e s t a t e " U I t o r e f l e c t t h e c o u n t d o w n .
updateCodeState ( )
}
private func updateCodeState ( ) {
AssertIsOnMainThread ( )
guard let codeCountdownStart = codeCountdownStart else {
owsFailDebug ( " Missing codeCountdownStart. " )
return
}
guard let titleLabel = titleLabel else {
owsFailDebug ( " Missing titleLabel. " )
return
}
guard let codeStateLink = codeStateLink else {
owsFailDebug ( " Missing codeStateLink. " )
return
}
var e164PhoneNumber = " "
if let phoneNumber = onboardingController . phoneNumber {
e164PhoneNumber = phoneNumber . e164
}
let formattedPhoneNumber = PhoneNumber . bestEffortLocalizedPhoneNumber ( withE164 : e164PhoneNumber )
// U p d a t e t i t l e L a b e l
switch codeState {
case . pending , . possiblyNotDelivered :
titleLabel . text = String ( format : NSLocalizedString ( " ONBOARDING_VERIFICATION_TITLE_DEFAULT_FORMAT " ,
comment : " Format for the title of the 'onboarding verification' view. Embeds {{the user's phone number}}. " ) ,
formattedPhoneNumber )
case . resent :
titleLabel . text = String ( format : NSLocalizedString ( " ONBOARDING_VERIFICATION_TITLE_RESENT_FORMAT " ,
comment : " Format for the title of the 'onboarding verification' view after the verification code has been resent. Embeds {{the user's phone number}}. " ) ,
formattedPhoneNumber )
}
// U p d a t e c o d e S t a t e L i n k
switch codeState {
case . pending :
let countdownInterval = abs ( codeCountdownStart . timeIntervalSinceNow )
let countdownRemaining = max ( 0 , countdownDuration - countdownInterval )
let formattedCountdown = OWSFormat . formatDurationSeconds ( Int ( round ( countdownRemaining ) ) )
let text = String ( format : NSLocalizedString ( " ONBOARDING_VERIFICATION_CODE_COUNTDOWN_FORMAT " ,
comment : " Format for the label of the 'pending code' label of the 'onboarding verification' view. Embeds {{the time until the code can be resent}}. " ) ,
formattedCountdown )
codeStateLink . setTitle ( title : text , font : . ows_dynamicTypeBodyClamped , titleColor : Theme . secondaryColor )
// c o d e S t a t e L i n k . s e t B a c k g r o u n d C o l o r s ( u p C o l o r : T h e m e . b a c k g r o u n d C o l o r )
case . possiblyNotDelivered :
codeStateLink . setTitle ( title : NSLocalizedString ( " ONBOARDING_VERIFICATION_ORIGINAL_CODE_MISSING_LINK " ,
comment : " Label for link that can be used when the original code did not arrive. " ) ,
font : . ows_dynamicTypeBodyClamped ,
titleColor : . ows_materialBlue )
case . resent :
codeStateLink . setTitle ( title : NSLocalizedString ( " ONBOARDING_VERIFICATION_RESENT_CODE_MISSING_LINK " ,
comment : " Label for link that can be used when the resent code did not arrive. " ) ,
font : . ows_dynamicTypeBodyClamped ,
titleColor : . ows_materialBlue )
}
}
public override func viewDidAppear ( _ animated : Bool ) {
super . viewDidAppear ( animated )
_ = onboardingCodeView . becomeFirstResponder ( )
}
// MARK: - E v e n t s
@ -323,52 +418,44 @@ public class OnboardingVerificationViewController: OnboardingBaseViewController
@objc func resendCodeLinkTapped ( ) {
Logger . info ( " " )
// TODO:
// s e l f . n a v i g a t i o n C o n t r o l l e r ? . p o p V i e w C o n t r o l l e r ( a n i m a t e d : t r u e )
switch codeState {
case . pending :
// I g n o r e t a p s u n t i l t h e c o u n t d o w n e x p i r e s .
break
case . possiblyNotDelivered , . resent :
showResendActionSheet ( )
}
}
private func showResendActionSheet ( ) {
Logger . info ( " " )
let actionSheet = UIAlertController ( title : NSLocalizedString ( " ONBOARDING_VERIFICATION_RESEND_CODE_ALERT_TITLE " ,
comment : " Title for the 'resend code' alert in the 'onboarding verification' view. " ) ,
message : NSLocalizedString ( " ONBOARDING_VERIFICATION_RESEND_CODE_ALERT_MESSAGE " ,
comment : " Message for the 'resend code' alert in the 'onboarding verification' view. " ) ,
preferredStyle : . actionSheet )
actionSheet . addAction ( UIAlertAction ( title : NSLocalizedString ( " ONBOARDING_VERIFICATION_RESEND_CODE_BY_SMS_BUTTON " ,
comment : " Label for the 'resend code by SMS' button in the 'onboarding verification' view. " ) ,
style : . default ) { _ in
self . onboardingController . tryToRegister ( fromViewController : self , smsVerification : true )
} )
actionSheet . addAction ( UIAlertAction ( title : NSLocalizedString ( " ONBOARDING_VERIFICATION_RESEND_CODE_BY_VOICE_BUTTON " ,
comment : " Label for the 'resend code by voice' button in the 'onboarding verification' view. " ) ,
style : . default ) { _ in
self . onboardingController . tryToRegister ( fromViewController : self , smsVerification : false )
} )
actionSheet . addAction ( OWSAlerts . cancelAction )
self . present ( actionSheet , animated : true )
}
}
// / / MARK: -
//
// e x t e n s i o n O n b o a r d i n g V e r i f i c a t i o n V i e w C o n t r o l l e r : U I T e x t F i e l d D e l e g a t e {
// p u b l i c f u n c t e x t F i e l d ( _ t e x t F i e l d : U I T e x t F i e l d , s h o u l d C h a n g e C h a r a c t e r s I n r a n g e : N S R a n g e , r e p l a c e m e n t S t r i n g s t r i n g : S t r i n g ) - > B o o l {
// / / TODO: F i x a u t o - f o r m a t o f p h o n e n u m b e r s .
// V i e w C o n t r o l l e r U t i l s . p h o n e N u m b e r ( t e x t F i e l d , s h o u l d C h a n g e C h a r a c t e r s I n : r a n g e , r e p l a c e m e n t S t r i n g : s t r i n g , c o u n t r y C o d e : c o u n t r y C o d e )
//
// / / I n f o r m o u r c a l l e r t h a t w e t o o k c a r e o f p e r f o r m i n g t h e c h a n g e .
// r e t u r n f a l s e
// }
//
// p u b l i c f u n c t e x t F i e l d S h o u l d R e t u r n ( _ t e x t F i e l d : U I T e x t F i e l d ) - > B o o l {
// p a r s e A n d T r y T o R e g i s t e r ( )
// r e t u r n f a l s e
// }
// }
//
// / / MARK: -
//
// e x t e n s i o n O n b o a r d i n g V e r i f i c a t i o n V i e w C o n t r o l l e r : C o u n t r y C o d e V i e w C o n t r o l l e r D e l e g a t e {
// p u b l i c f u n c c o u n t r y C o d e V i e w C o n t r o l l e r ( _ v c : C o u n t r y C o d e V i e w C o n t r o l l e r , d i d S e l e c t C o u n t r y C o d e c o u n t r y C o d e : S t r i n g , c o u n t r y N a m e : S t r i n g , c a l l i n g C o d e : S t r i n g ) {
// g u a r d c o u n t r y C o d e . c o u n t > 0 e l s e {
// o w s F a i l D e b u g ( " I n v a l i d c o u n t r y c o d e . " )
// r e t u r n
// }
// g u a r d c o u n t r y N a m e . c o u n t > 0 e l s e {
// o w s F a i l D e b u g ( " I n v a l i d c o u n t r y n a m e . " )
// r e t u r n
// }
// g u a r d c a l l i n g C o d e . c o u n t > 0 e l s e {
// o w s F a i l D e b u g ( " I n v a l i d c a l l i n g c o d e . " )
// r e t u r n
// }
//
// l e t c o u n t r y S t a t e = O n b o a r d i n g C o u n t r y S t a t e ( c o u n t r y N a m e : c o u n t r y N a m e , c a l l i n g C o d e : c a l l i n g C o d e , c o u n t r y C o d e : c o u n t r y C o d e )
//
// o n b o a r d i n g C o n t r o l l e r . u p d a t e ( c o u n t r y S t a t e : c o u n t r y S t a t e )
//
// u p d a t e S t a t e ( )
//
// / / T r i g g e r t h e f o r m a t t i n g l o g i c w i t h a n o - o p e d i t .
// _ = t e x t F i e l d ( p h o n e N u m b e r T e x t F i e l d , s h o u l d C h a n g e C h a r a c t e r s I n : N S R a n g e ( l o c a t i o n : 0 , l e n g t h : 0 ) , r e p l a c e m e n t S t r i n g : " " )
// }
// }
// MARK: -
extension OnboardingVerificationViewController : OnboardingCodeViewDelegate {
public func codeViewDidChange ( ) {
// TODO:
}
}