mirror of https://github.com/oxen-io/session-ios
Merge branch 'charlesmchen/onboardingRemoveOldViews'
commit
1ec1a9aa63
@ -1,15 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <SignalMessaging/OWSViewController.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface OWS2FARegistrationViewController : OWSViewController
|
|
||||||
|
|
||||||
@property (nonatomic) NSString *verificationCode;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
@ -1,161 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "OWS2FARegistrationViewController.h"
|
|
||||||
#import "PinEntryView.h"
|
|
||||||
#import "ProfileViewController.h"
|
|
||||||
#import "Signal-Swift.h"
|
|
||||||
#import <PromiseKit/AnyPromise.h>
|
|
||||||
#import <SignalMessaging/SignalMessaging-Swift.h>
|
|
||||||
#import <SignalMessaging/UIViewController+OWS.h>
|
|
||||||
#import <SignalServiceKit/OWS2FAManager.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface OWS2FARegistrationViewController () <PinEntryViewDelegate>
|
|
||||||
|
|
||||||
@property (nonatomic) PinEntryView *entryView;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
@implementation OWS2FARegistrationViewController
|
|
||||||
|
|
||||||
#pragma mark - Dependencies
|
|
||||||
|
|
||||||
- (AccountManager *)accountManager
|
|
||||||
{
|
|
||||||
return AppEnvironment.shared.accountManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - View Lifecycle
|
|
||||||
|
|
||||||
- (void)viewDidLoad
|
|
||||||
{
|
|
||||||
[super viewDidLoad];
|
|
||||||
|
|
||||||
// The navigation bar is hidden in the registration workflow.
|
|
||||||
if (self.navigationController.navigationBarHidden) {
|
|
||||||
[self.navigationController setNavigationBarHidden:NO animated:YES];
|
|
||||||
}
|
|
||||||
self.navigationItem.hidesBackButton = YES;
|
|
||||||
|
|
||||||
self.title = NSLocalizedString(@"REGISTRATION_ENTER_LOCK_PIN_NAV_TITLE",
|
|
||||||
@"Navigation title shown when user is re-registering after having enabled registration lock");
|
|
||||||
|
|
||||||
self.view.backgroundColor = [Theme backgroundColor];
|
|
||||||
|
|
||||||
PinEntryView *entryView = [PinEntryView new];
|
|
||||||
self.entryView = entryView;
|
|
||||||
entryView.delegate = self;
|
|
||||||
[self.view addSubview:entryView];
|
|
||||||
|
|
||||||
entryView.instructionsText = NSLocalizedString(
|
|
||||||
@"REGISTER_2FA_INSTRUCTIONS", @"Instructions to enter the 'two-factor auth pin' in the 2FA registration view.");
|
|
||||||
|
|
||||||
// Layout
|
|
||||||
[entryView autoPinToTopLayoutGuideOfViewController:self withInset:0];
|
|
||||||
[entryView autoPinEdgeToSuperviewMargin:ALEdgeLeft];
|
|
||||||
[entryView autoPinEdgeToSuperviewMargin:ALEdgeRight];
|
|
||||||
[entryView autoPinToBottomLayoutGuideOfViewController:self withInset:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewWillAppear:(BOOL)animated
|
|
||||||
{
|
|
||||||
[super viewWillAppear:animated];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewDidAppear:(BOOL)animated
|
|
||||||
{
|
|
||||||
[super viewDidAppear:animated];
|
|
||||||
|
|
||||||
[self.entryView makePinTextFieldFirstResponder];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - PinEntryViewDelegate
|
|
||||||
|
|
||||||
- (void)pinEntryView:(PinEntryView *)entryView submittedPinCode:(NSString *)pinCode
|
|
||||||
{
|
|
||||||
OWSAssertDebug(self.entryView.hasValidPin);
|
|
||||||
|
|
||||||
[self tryToRegisterWithPinCode:pinCode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)pinEntryViewForgotPinLinkTapped:(PinEntryView *)entryView
|
|
||||||
{
|
|
||||||
NSString *alertBody = NSLocalizedString(@"REGISTER_2FA_FORGOT_PIN_ALERT_MESSAGE",
|
|
||||||
@"Alert message explaining what happens if you forget your 'two-factor auth pin'.");
|
|
||||||
[OWSAlerts showAlertWithTitle:nil message:alertBody];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Registration
|
|
||||||
|
|
||||||
- (void)tryToRegisterWithPinCode:(NSString *)pinCode
|
|
||||||
{
|
|
||||||
OWSAssertDebug(self.entryView.hasValidPin);
|
|
||||||
OWSAssertDebug(self.verificationCode.length > 0);
|
|
||||||
OWSAssertDebug(pinCode.length > 0);
|
|
||||||
|
|
||||||
OWSLogInfo(@"");
|
|
||||||
|
|
||||||
__weak OWS2FARegistrationViewController *weakSelf = self;
|
|
||||||
|
|
||||||
[ModalActivityIndicatorViewController
|
|
||||||
presentFromViewController:self
|
|
||||||
canCancel:NO
|
|
||||||
backgroundBlock:^(ModalActivityIndicatorViewController *modalActivityIndicator) {
|
|
||||||
OWSProdInfo([OWSAnalyticsEvents registrationRegisteringCode]);
|
|
||||||
[[self.accountManager registerObjcWithVerificationCode:self.verificationCode pin:pinCode]
|
|
||||||
.then(^{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
OWSProdInfo([OWSAnalyticsEvents registrationRegisteringSubmittedCode]);
|
|
||||||
[[OWS2FAManager sharedManager] mark2FAAsEnabledWithPin:pinCode];
|
|
||||||
|
|
||||||
OWSLogInfo(@"Successfully registered Signal account.");
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[modalActivityIndicator dismissWithCompletion:^{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
[weakSelf verificationWasCompleted];
|
|
||||||
}];
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(^(NSError *error) {
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
OWSProdInfo([OWSAnalyticsEvents registrationRegistrationFailed]);
|
|
||||||
OWSLogError(@"error verifying challenge: %@", error);
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[modalActivityIndicator dismissWithCompletion:^{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
|
|
||||||
[OWSAlerts
|
|
||||||
showAlertWithTitle:NSLocalizedString(
|
|
||||||
@"REGISTER_2FA_REGISTRATION_FAILED_ALERT_TITLE",
|
|
||||||
@"Title for alert indicating that attempt to "
|
|
||||||
@"register with 'two-factor auth' failed.")
|
|
||||||
message:error.localizedDescription];
|
|
||||||
|
|
||||||
[weakSelf.entryView makePinTextFieldFirstResponder];
|
|
||||||
}];
|
|
||||||
});
|
|
||||||
}) retainUntilComplete];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)verificationWasCompleted
|
|
||||||
{
|
|
||||||
[RegistrationController verificationWasCompletedFromView:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Orientation
|
|
||||||
|
|
||||||
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
|
|
||||||
{
|
|
||||||
return UIInterfaceOrientationMaskPortrait;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
@ -1,15 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <SignalMessaging/OWSViewController.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface CodeVerificationViewController : OWSViewController
|
|
||||||
|
|
||||||
- (void)setVerificationCodeAndTryToVerify:(NSString *)verificationCode;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
@ -1,509 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "CodeVerificationViewController.h"
|
|
||||||
#import "OWS2FARegistrationViewController.h"
|
|
||||||
#import "ProfileViewController.h"
|
|
||||||
#import "Signal-Swift.h"
|
|
||||||
#import <PromiseKit/AnyPromise.h>
|
|
||||||
#import <SignalMessaging/UIViewController+OWS.h>
|
|
||||||
#import <SignalServiceKit/OWSError.h>
|
|
||||||
#import <SignalServiceKit/TSAccountManager.h>
|
|
||||||
#import <SignalServiceKit/TSNetworkManager.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface CodeVerificationViewController () <UITextFieldDelegate>
|
|
||||||
|
|
||||||
// Where the user enters the verification code they wish to document
|
|
||||||
@property (nonatomic) UITextField *challengeTextField;
|
|
||||||
|
|
||||||
@property (nonatomic) UILabel *phoneNumberLabel;
|
|
||||||
|
|
||||||
//// User action buttons
|
|
||||||
@property (nonatomic) OWSFlatButton *submitButton;
|
|
||||||
@property (nonatomic) UIButton *sendCodeViaSMSAgainButton;
|
|
||||||
@property (nonatomic) UIButton *sendCodeViaVoiceButton;
|
|
||||||
|
|
||||||
@property (nonatomic) UIActivityIndicatorView *submitCodeSpinner;
|
|
||||||
@property (nonatomic) UIActivityIndicatorView *requestCodeAgainSpinner;
|
|
||||||
@property (nonatomic) UIActivityIndicatorView *requestCallSpinner;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
@implementation CodeVerificationViewController
|
|
||||||
|
|
||||||
#pragma mark - Dependencies
|
|
||||||
|
|
||||||
- (TSAccountManager *)tsAccountManager
|
|
||||||
{
|
|
||||||
OWSAssertDebug(SSKEnvironment.shared.tsAccountManager);
|
|
||||||
|
|
||||||
return SSKEnvironment.shared.tsAccountManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (AccountManager *)accountManager
|
|
||||||
{
|
|
||||||
return AppEnvironment.shared.accountManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - View Lifecycle
|
|
||||||
|
|
||||||
- (void)viewDidLoad
|
|
||||||
{
|
|
||||||
[super viewDidLoad];
|
|
||||||
|
|
||||||
self.shouldUseTheme = NO;
|
|
||||||
|
|
||||||
[self createViews];
|
|
||||||
|
|
||||||
[self initializeKeyboardHandlers];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewWillAppear:(BOOL)animated
|
|
||||||
{
|
|
||||||
[super viewWillAppear:animated];
|
|
||||||
[self enableServerActions:YES];
|
|
||||||
[self updatePhoneNumberLabel];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewDidAppear:(BOOL)animated
|
|
||||||
{
|
|
||||||
[super viewDidAppear:animated];
|
|
||||||
[_challengeTextField becomeFirstResponder];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
- (void)createViews
|
|
||||||
{
|
|
||||||
self.view.backgroundColor = [UIColor whiteColor];
|
|
||||||
self.view.opaque = YES;
|
|
||||||
|
|
||||||
UIColor *signalBlueColor = [UIColor ows_signalBrandBlueColor];
|
|
||||||
|
|
||||||
UIView *header = [UIView new];
|
|
||||||
header.backgroundColor = signalBlueColor;
|
|
||||||
[self.view addSubview:header];
|
|
||||||
[header autoPinWidthToSuperview];
|
|
||||||
[header autoPinEdgeToSuperviewEdge:ALEdgeTop];
|
|
||||||
// The header will grow to accomodate the titleLabel's height.
|
|
||||||
|
|
||||||
UILabel *titleLabel = [UILabel new];
|
|
||||||
titleLabel.textColor = [UIColor whiteColor];
|
|
||||||
titleLabel.text = [self phoneNumberText];
|
|
||||||
titleLabel.font = [UIFont ows_mediumFontWithSize:20.f];
|
|
||||||
[header addSubview:titleLabel];
|
|
||||||
[titleLabel autoPinToTopLayoutGuideOfViewController:self withInset:0];
|
|
||||||
[titleLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom];
|
|
||||||
[titleLabel autoSetDimension:ALDimensionHeight toSize:40];
|
|
||||||
[titleLabel autoHCenterInSuperview];
|
|
||||||
|
|
||||||
// This view is used in more than one context.
|
|
||||||
//
|
|
||||||
// * Usually, it is pushed atop RegistrationViewController in which
|
|
||||||
// case we want a "back" button.
|
|
||||||
// * It can also be used to re-register from the app's "de-registration"
|
|
||||||
// views, in which case RegistrationViewController is not used and we
|
|
||||||
// do _not_ want a "back" button.
|
|
||||||
if (self.navigationController.viewControllers.count > 1) {
|
|
||||||
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
||||||
[backButton
|
|
||||||
setTitle:NSLocalizedString(@"VERIFICATION_BACK_BUTTON", @"button text for back button on verification view")
|
|
||||||
forState:UIControlStateNormal];
|
|
||||||
[backButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
|
||||||
backButton.titleLabel.font = [UIFont ows_mediumFontWithSize:14.f];
|
|
||||||
[header addSubview:backButton];
|
|
||||||
[backButton autoPinLeadingToSuperviewMarginWithInset:10.f];
|
|
||||||
[backButton autoAlignAxis:ALAxisHorizontal toSameAxisOfView:titleLabel];
|
|
||||||
[backButton addTarget:self action:@selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
|
||||||
}
|
|
||||||
|
|
||||||
_phoneNumberLabel = [UILabel new];
|
|
||||||
_phoneNumberLabel.textColor = [UIColor ows_darkGrayColor];
|
|
||||||
_phoneNumberLabel.font = [UIFont ows_regularFontWithSize:20.f];
|
|
||||||
_phoneNumberLabel.numberOfLines = 2;
|
|
||||||
_phoneNumberLabel.adjustsFontSizeToFitWidth = YES;
|
|
||||||
_phoneNumberLabel.textAlignment = NSTextAlignmentCenter;
|
|
||||||
[self.view addSubview:_phoneNumberLabel];
|
|
||||||
[_phoneNumberLabel autoPinWidthToSuperviewWithMargin:ScaleFromIPhone5(32)];
|
|
||||||
[_phoneNumberLabel autoPinEdge:ALEdgeTop
|
|
||||||
toEdge:ALEdgeBottom
|
|
||||||
ofView:header
|
|
||||||
withOffset:ScaleFromIPhone5To7Plus(30, 100)];
|
|
||||||
|
|
||||||
const CGFloat kHMargin = 36;
|
|
||||||
|
|
||||||
if (UIDevice.currentDevice.isShorterThanIPhone5) {
|
|
||||||
_challengeTextField = [DismissableTextField new];
|
|
||||||
} else {
|
|
||||||
_challengeTextField = [OWSTextField new];
|
|
||||||
}
|
|
||||||
|
|
||||||
_challengeTextField.textColor = [UIColor blackColor];
|
|
||||||
_challengeTextField.placeholder = NSLocalizedString(@"VERIFICATION_CHALLENGE_DEFAULT_TEXT",
|
|
||||||
@"Text field placeholder for SMS verification code during registration");
|
|
||||||
_challengeTextField.font = [UIFont ows_lightFontWithSize:21.f];
|
|
||||||
_challengeTextField.textAlignment = NSTextAlignmentCenter;
|
|
||||||
_challengeTextField.keyboardType = UIKeyboardTypeNumberPad;
|
|
||||||
_challengeTextField.delegate = self;
|
|
||||||
[self.view addSubview:_challengeTextField];
|
|
||||||
[_challengeTextField autoPinWidthToSuperviewWithMargin:kHMargin];
|
|
||||||
[_challengeTextField autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:_phoneNumberLabel withOffset:25];
|
|
||||||
|
|
||||||
UIView *underscoreView = [UIView new];
|
|
||||||
underscoreView.backgroundColor = [UIColor colorWithWhite:0.5 alpha:1.f];
|
|
||||||
[self.view addSubview:underscoreView];
|
|
||||||
[underscoreView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:_challengeTextField withOffset:3];
|
|
||||||
[underscoreView autoPinWidthToSuperviewWithMargin:kHMargin];
|
|
||||||
[underscoreView autoSetDimension:ALDimensionHeight toSize:1.f];
|
|
||||||
|
|
||||||
const CGFloat kSubmitButtonHeight = 47.f;
|
|
||||||
// NOTE: We use ows_signalBrandBlueColor instead of ows_materialBlueColor
|
|
||||||
// throughout the onboarding flow to be consistent with the headers.
|
|
||||||
OWSFlatButton *submitButton =
|
|
||||||
[OWSFlatButton buttonWithTitle:NSLocalizedString(@"VERIFICATION_CHALLENGE_SUBMIT_CODE",
|
|
||||||
@"button text during registration to submit your SMS verification code.")
|
|
||||||
font:[OWSFlatButton fontForHeight:kSubmitButtonHeight]
|
|
||||||
titleColor:[UIColor whiteColor]
|
|
||||||
backgroundColor:[UIColor ows_signalBrandBlueColor]
|
|
||||||
target:self
|
|
||||||
selector:@selector(submitVerificationCode)];
|
|
||||||
self.submitButton = submitButton;
|
|
||||||
[self.view addSubview:_submitButton];
|
|
||||||
[_submitButton autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:underscoreView withOffset:15];
|
|
||||||
[_submitButton autoPinWidthToSuperviewWithMargin:kHMargin];
|
|
||||||
[_submitButton autoSetDimension:ALDimensionHeight toSize:kSubmitButtonHeight];
|
|
||||||
|
|
||||||
const CGFloat kSpinnerSize = 20;
|
|
||||||
const CGFloat kSpinnerSpacing = ScaleFromIPhone5To7Plus(5, 15);
|
|
||||||
|
|
||||||
_submitCodeSpinner =
|
|
||||||
[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
|
|
||||||
[_submitButton addSubview:_submitCodeSpinner];
|
|
||||||
[_submitCodeSpinner autoSetDimension:ALDimensionWidth toSize:kSpinnerSize];
|
|
||||||
[_submitCodeSpinner autoSetDimension:ALDimensionHeight toSize:kSpinnerSize];
|
|
||||||
[_submitCodeSpinner autoVCenterInSuperview];
|
|
||||||
[_submitCodeSpinner autoPinTrailingToSuperviewMarginWithInset:kSpinnerSpacing];
|
|
||||||
|
|
||||||
_sendCodeViaSMSAgainButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
||||||
_sendCodeViaSMSAgainButton.backgroundColor = [UIColor whiteColor];
|
|
||||||
[_sendCodeViaSMSAgainButton setTitle:NSLocalizedString(@"VERIFICATION_CHALLENGE_SUBMIT_AGAIN",
|
|
||||||
@"button text during registration to request another SMS code be sent")
|
|
||||||
forState:UIControlStateNormal];
|
|
||||||
[_sendCodeViaSMSAgainButton setTitleColor:signalBlueColor forState:UIControlStateNormal];
|
|
||||||
_sendCodeViaSMSAgainButton.titleLabel.font = [UIFont ows_mediumFontWithSize:14.f];
|
|
||||||
[_sendCodeViaSMSAgainButton addTarget:self
|
|
||||||
action:@selector(sendCodeViaSMSAction:)
|
|
||||||
forControlEvents:UIControlEventTouchUpInside];
|
|
||||||
[self.view addSubview:_sendCodeViaSMSAgainButton];
|
|
||||||
[_sendCodeViaSMSAgainButton autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:_submitButton withOffset:10];
|
|
||||||
[_sendCodeViaSMSAgainButton autoPinWidthToSuperviewWithMargin:kHMargin];
|
|
||||||
[_sendCodeViaSMSAgainButton autoSetDimension:ALDimensionHeight toSize:35];
|
|
||||||
|
|
||||||
_requestCodeAgainSpinner =
|
|
||||||
[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
|
|
||||||
[_sendCodeViaSMSAgainButton addSubview:_requestCodeAgainSpinner];
|
|
||||||
[_requestCodeAgainSpinner autoSetDimension:ALDimensionWidth toSize:kSpinnerSize];
|
|
||||||
[_requestCodeAgainSpinner autoSetDimension:ALDimensionHeight toSize:kSpinnerSize];
|
|
||||||
[_requestCodeAgainSpinner autoVCenterInSuperview];
|
|
||||||
[_requestCodeAgainSpinner autoPinTrailingToSuperviewMarginWithInset:kSpinnerSpacing];
|
|
||||||
|
|
||||||
_sendCodeViaVoiceButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
||||||
_sendCodeViaVoiceButton.backgroundColor = [UIColor whiteColor];
|
|
||||||
[_sendCodeViaVoiceButton
|
|
||||||
setTitle:NSLocalizedString(@"VERIFICATION_CHALLENGE_SEND_VIA_VOICE",
|
|
||||||
@"button text during registration to request phone number verification be done via phone call")
|
|
||||||
forState:UIControlStateNormal];
|
|
||||||
[_sendCodeViaVoiceButton setTitleColor:signalBlueColor forState:UIControlStateNormal];
|
|
||||||
_sendCodeViaVoiceButton.titleLabel.font = [UIFont ows_mediumFontWithSize:14.f];
|
|
||||||
[_sendCodeViaVoiceButton addTarget:self
|
|
||||||
action:@selector(sendCodeViaVoiceAction:)
|
|
||||||
forControlEvents:UIControlEventTouchUpInside];
|
|
||||||
[self.view addSubview:_sendCodeViaVoiceButton];
|
|
||||||
[_sendCodeViaVoiceButton autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:_sendCodeViaSMSAgainButton];
|
|
||||||
[_sendCodeViaVoiceButton autoPinWidthToSuperviewWithMargin:kHMargin];
|
|
||||||
[_sendCodeViaVoiceButton autoSetDimension:ALDimensionHeight toSize:35];
|
|
||||||
|
|
||||||
_requestCallSpinner =
|
|
||||||
[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
|
|
||||||
[_sendCodeViaVoiceButton addSubview:_requestCallSpinner];
|
|
||||||
[_requestCallSpinner autoSetDimension:ALDimensionWidth toSize:kSpinnerSize];
|
|
||||||
[_requestCallSpinner autoSetDimension:ALDimensionHeight toSize:kSpinnerSize];
|
|
||||||
[_requestCallSpinner autoVCenterInSuperview];
|
|
||||||
[_requestCallSpinner autoPinTrailingToSuperviewMarginWithInset:kSpinnerSpacing];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)phoneNumberText
|
|
||||||
{
|
|
||||||
OWSAssertDebug([TSAccountManager localNumber] != nil);
|
|
||||||
return [PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:[TSAccountManager localNumber]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)updatePhoneNumberLabel
|
|
||||||
{
|
|
||||||
_phoneNumberLabel.text =
|
|
||||||
[NSString stringWithFormat:NSLocalizedString(@"VERIFICATION_PHONE_NUMBER_FORMAT",
|
|
||||||
@"Label indicating the phone number currently being verified."),
|
|
||||||
[self phoneNumberText]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)startActivityIndicator
|
|
||||||
{
|
|
||||||
[self.submitCodeSpinner startAnimating];
|
|
||||||
[self enableServerActions:NO];
|
|
||||||
[self.challengeTextField resignFirstResponder];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)stopActivityIndicator
|
|
||||||
{
|
|
||||||
[self enableServerActions:YES];
|
|
||||||
[self.submitCodeSpinner stopAnimating];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)submitVerificationCode
|
|
||||||
{
|
|
||||||
[self startActivityIndicator];
|
|
||||||
OWSProdInfo([OWSAnalyticsEvents registrationRegisteringCode]);
|
|
||||||
__weak CodeVerificationViewController *weakSelf = self;
|
|
||||||
[[self.accountManager registerObjcWithVerificationCode:[self validationCodeFromTextField] pin:nil]
|
|
||||||
.then(^{
|
|
||||||
OWSProdInfo([OWSAnalyticsEvents registrationRegisteringSubmittedCode]);
|
|
||||||
|
|
||||||
OWSLogInfo(@"Successfully registered Signal account.");
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[weakSelf stopActivityIndicator];
|
|
||||||
[weakSelf verificationWasCompleted];
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(^(NSError *error) {
|
|
||||||
OWSLogError(@"error: %@, %@, %zd", [error class], error.domain, error.code);
|
|
||||||
OWSProdInfo([OWSAnalyticsEvents registrationRegistrationFailed]);
|
|
||||||
OWSLogError(@"error verifying challenge: %@", error);
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[weakSelf stopActivityIndicator];
|
|
||||||
|
|
||||||
if ([error.domain isEqualToString:OWSSignalServiceKitErrorDomain]
|
|
||||||
&& error.code == OWSErrorCodeRegistrationMissing2FAPIN) {
|
|
||||||
CodeVerificationViewController *strongSelf = weakSelf;
|
|
||||||
if (!strongSelf) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OWSLogInfo(@"Showing 2FA registration view.");
|
|
||||||
OWS2FARegistrationViewController *viewController = [OWS2FARegistrationViewController new];
|
|
||||||
viewController.verificationCode = strongSelf.validationCodeFromTextField;
|
|
||||||
[strongSelf.navigationController pushViewController:viewController animated:YES];
|
|
||||||
} else {
|
|
||||||
[weakSelf presentAlertWithVerificationError:error];
|
|
||||||
[weakSelf.challengeTextField becomeFirstResponder];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}) retainUntilComplete];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)verificationWasCompleted
|
|
||||||
{
|
|
||||||
[RegistrationController verificationWasCompletedFromView:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)presentAlertWithVerificationError:(NSError *)error
|
|
||||||
{
|
|
||||||
UIAlertController *alert;
|
|
||||||
alert = [UIAlertController
|
|
||||||
alertControllerWithTitle:NSLocalizedString(@"REGISTRATION_VERIFICATION_FAILED_TITLE", @"Alert view title")
|
|
||||||
message:error.localizedDescription
|
|
||||||
preferredStyle:UIAlertControllerStyleAlert];
|
|
||||||
[alert addAction:[UIAlertAction actionWithTitle:CommonStrings.dismissButton
|
|
||||||
style:UIAlertActionStyleDefault
|
|
||||||
handler:^(UIAlertAction *action) {
|
|
||||||
[self.challengeTextField becomeFirstResponder];
|
|
||||||
}]];
|
|
||||||
|
|
||||||
[self presentViewController:alert animated:YES completion:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)validationCodeFromTextField
|
|
||||||
{
|
|
||||||
return [self.challengeTextField.text stringByReplacingOccurrencesOfString:@"-" withString:@""];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Actions
|
|
||||||
|
|
||||||
- (void)sendCodeViaSMSAction:(id)sender
|
|
||||||
{
|
|
||||||
OWSProdInfo([OWSAnalyticsEvents registrationRegisteringRequestedNewCodeBySms]);
|
|
||||||
|
|
||||||
[self enableServerActions:NO];
|
|
||||||
|
|
||||||
[_requestCodeAgainSpinner startAnimating];
|
|
||||||
__weak CodeVerificationViewController *weakSelf = self;
|
|
||||||
[self.tsAccountManager rerequestSMSWithCaptchaToken:nil
|
|
||||||
success:^{
|
|
||||||
OWSLogInfo(@"Successfully requested SMS code");
|
|
||||||
[weakSelf enableServerActions:YES];
|
|
||||||
[weakSelf.requestCodeAgainSpinner stopAnimating];
|
|
||||||
}
|
|
||||||
failure:^(NSError *error) {
|
|
||||||
OWSLogError(@"Failed to request SMS code with error: %@", error);
|
|
||||||
[weakSelf showRegistrationErrorMessage:error];
|
|
||||||
[weakSelf enableServerActions:YES];
|
|
||||||
[weakSelf.requestCodeAgainSpinner stopAnimating];
|
|
||||||
[weakSelf.challengeTextField becomeFirstResponder];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)sendCodeViaVoiceAction:(id)sender
|
|
||||||
{
|
|
||||||
OWSProdInfo([OWSAnalyticsEvents registrationRegisteringRequestedNewCodeByVoice]);
|
|
||||||
|
|
||||||
[self enableServerActions:NO];
|
|
||||||
|
|
||||||
[_requestCallSpinner startAnimating];
|
|
||||||
__weak CodeVerificationViewController *weakSelf = self;
|
|
||||||
[self.tsAccountManager rerequestVoiceWithCaptchaToken:nil
|
|
||||||
success:^{
|
|
||||||
OWSLogInfo(@"Successfully requested voice code");
|
|
||||||
|
|
||||||
[weakSelf enableServerActions:YES];
|
|
||||||
[weakSelf.requestCallSpinner stopAnimating];
|
|
||||||
}
|
|
||||||
failure:^(NSError *error) {
|
|
||||||
OWSLogError(@"Failed to request voice code with error: %@", error);
|
|
||||||
[weakSelf showRegistrationErrorMessage:error];
|
|
||||||
[weakSelf enableServerActions:YES];
|
|
||||||
[weakSelf.requestCallSpinner stopAnimating];
|
|
||||||
[weakSelf.challengeTextField becomeFirstResponder];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)showRegistrationErrorMessage:(NSError *)registrationError
|
|
||||||
{
|
|
||||||
[OWSAlerts showAlertWithTitle:registrationError.localizedDescription
|
|
||||||
message:registrationError.localizedRecoverySuggestion];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)enableServerActions:(BOOL)enabled
|
|
||||||
{
|
|
||||||
[_submitButton setEnabled:enabled];
|
|
||||||
[_sendCodeViaSMSAgainButton setEnabled:enabled];
|
|
||||||
[_sendCodeViaVoiceButton setEnabled:enabled];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)backButtonPressed:(id)sender
|
|
||||||
{
|
|
||||||
OWSProdInfo([OWSAnalyticsEvents registrationVerificationBack]);
|
|
||||||
|
|
||||||
[self.navigationController popViewControllerAnimated:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Keyboard notifications
|
|
||||||
|
|
||||||
- (void)initializeKeyboardHandlers
|
|
||||||
{
|
|
||||||
UITapGestureRecognizer *outsideTabRecognizer =
|
|
||||||
[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboardFromAppropriateSubView)];
|
|
||||||
[self.view addGestureRecognizer:outsideTabRecognizer];
|
|
||||||
self.view.userInteractionEnabled = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dismissKeyboardFromAppropriateSubView
|
|
||||||
{
|
|
||||||
[self.view endEditing:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)textField:(UITextField *)textField
|
|
||||||
shouldChangeCharactersInRange:(NSRange)range
|
|
||||||
replacementString:(NSString *)insertionText
|
|
||||||
{
|
|
||||||
|
|
||||||
// Verification codes take this form: "123-456".
|
|
||||||
//
|
|
||||||
// * We only want to let the user "6 decimal digits + 1 hyphen = 7".
|
|
||||||
// * The user shouldn't have to enter the hyphen - it should be added automatically.
|
|
||||||
// * The user should be able to copy and paste freely.
|
|
||||||
// * Invalid input (including extraneous hyphens) should be simply ignored.
|
|
||||||
//
|
|
||||||
// We accomplish this by being permissive and trying to "take as much of the user
|
|
||||||
// input as possible".
|
|
||||||
//
|
|
||||||
// * Always accept deletes.
|
|
||||||
// * Ignore invalid input.
|
|
||||||
// * Take partial input if possible.
|
|
||||||
|
|
||||||
NSString *oldText = textField.text;
|
|
||||||
// Construct the new contents of the text field by:
|
|
||||||
// 1. Determining the "left" substring: the contents of the old text _before_ the deletion range.
|
|
||||||
// Filtering will remove non-decimal digit characters like hyphen "-".
|
|
||||||
NSString *left = [oldText substringToIndex:range.location].digitsOnly;
|
|
||||||
// 2. Determining the "right" substring: the contents of the old text _after_ the deletion range.
|
|
||||||
NSString *right = [oldText substringFromIndex:range.location + range.length].digitsOnly;
|
|
||||||
// 3. Determining the "center" substring: the contents of the new insertion text.
|
|
||||||
NSString *center = insertionText.digitsOnly;
|
|
||||||
// 3a. Trim the tail of the "center" substring to ensure that we don't end up
|
|
||||||
// with more than 6 decimal digits.
|
|
||||||
while (center.length > 0 && left.length + center.length + right.length > 6) {
|
|
||||||
center = [center substringToIndex:center.length - 1];
|
|
||||||
}
|
|
||||||
// 4. Construct the "raw" new text by concatenating left, center and right.
|
|
||||||
NSString *rawNewText = [[left stringByAppendingString:center] stringByAppendingString:right];
|
|
||||||
// 5. Construct the "formatted" new text by inserting a hyphen if necessary.
|
|
||||||
NSString *formattedNewText
|
|
||||||
= (rawNewText.length <= 3 ? rawNewText
|
|
||||||
: [[[rawNewText substringToIndex:3] stringByAppendingString:@"-"]
|
|
||||||
stringByAppendingString:[rawNewText substringFromIndex:3]]);
|
|
||||||
textField.text = formattedNewText;
|
|
||||||
|
|
||||||
// Move the cursor after the newly inserted text.
|
|
||||||
NSUInteger newInsertionPoint = left.length + center.length;
|
|
||||||
if (newInsertionPoint > 3) {
|
|
||||||
// Nudge the cursor to the right to reflect the hyphen
|
|
||||||
// if necessary.
|
|
||||||
newInsertionPoint++;
|
|
||||||
}
|
|
||||||
UITextPosition *newPosition =
|
|
||||||
[textField positionFromPosition:textField.beginningOfDocument offset:(NSInteger)newInsertionPoint];
|
|
||||||
textField.selectedTextRange = [textField textRangeFromPosition:newPosition toPosition:newPosition];
|
|
||||||
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField
|
|
||||||
{
|
|
||||||
[self submitVerificationCode];
|
|
||||||
[textField resignFirstResponder];
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setVerificationCodeAndTryToVerify:(NSString *)verificationCode
|
|
||||||
{
|
|
||||||
NSString *rawNewText = verificationCode.digitsOnly;
|
|
||||||
NSString *formattedNewText
|
|
||||||
= (rawNewText.length <= 3 ? rawNewText
|
|
||||||
: [[[rawNewText substringToIndex:3] stringByAppendingString:@"-"]
|
|
||||||
stringByAppendingString:[rawNewText substringFromIndex:3]]);
|
|
||||||
self.challengeTextField.text = formattedNewText;
|
|
||||||
// Move the cursor after the newly inserted text.
|
|
||||||
UITextPosition *newPosition = [self.challengeTextField endOfDocument];
|
|
||||||
self.challengeTextField.selectedTextRange =
|
|
||||||
[self.challengeTextField textRangeFromPosition:newPosition toPosition:newPosition];
|
|
||||||
[self submitVerificationCode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
|
||||||
{
|
|
||||||
return UIStatusBarStyleLightContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Orientation
|
|
||||||
|
|
||||||
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
|
|
||||||
{
|
|
||||||
return UIInterfaceOrientationMaskPortrait;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
@ -1,13 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <SignalMessaging/OWSViewController.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface RegistrationViewController : OWSViewController
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
@ -1,646 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "RegistrationViewController.h"
|
|
||||||
#import "CodeVerificationViewController.h"
|
|
||||||
#import "CountryCodeViewController.h"
|
|
||||||
#import "PhoneNumber.h"
|
|
||||||
#import "PhoneNumberUtil.h"
|
|
||||||
#import "Signal-Swift.h"
|
|
||||||
#import "TSAccountManager.h"
|
|
||||||
#import "UIView+OWS.h"
|
|
||||||
#import "ViewControllerUtils.h"
|
|
||||||
#import <SignalMessaging/Environment.h>
|
|
||||||
#import <SignalMessaging/OWSNavigationController.h>
|
|
||||||
#import <SignalServiceKit/NSString+SSK.h>
|
|
||||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
|
||||||
#import <SignalMessaging/UIUtil.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
|
|
||||||
NSString *const kKeychainService_LastRegistered = @"kKeychainService_LastRegistered";
|
|
||||||
NSString *const kKeychainKey_LastRegisteredCountryCode = @"kKeychainKey_LastRegisteredCountryCode";
|
|
||||||
NSString *const kKeychainKey_LastRegisteredPhoneNumber = @"kKeychainKey_LastRegisteredPhoneNumber";
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@interface RegistrationViewController () <CountryCodeViewControllerDelegate, UITextFieldDelegate>
|
|
||||||
|
|
||||||
@property (nonatomic) NSString *countryCode;
|
|
||||||
@property (nonatomic) NSString *callingCode;
|
|
||||||
|
|
||||||
@property (nonatomic) UILabel *countryCodeLabel;
|
|
||||||
@property (nonatomic) UITextField *phoneNumberTextField;
|
|
||||||
@property (nonatomic) UILabel *examplePhoneNumberLabel;
|
|
||||||
@property (nonatomic) OWSFlatButton *activateButton;
|
|
||||||
@property (nonatomic) UIActivityIndicatorView *spinnerView;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
@implementation RegistrationViewController
|
|
||||||
|
|
||||||
#pragma mark - Dependencies
|
|
||||||
|
|
||||||
- (TSAccountManager *)tsAccountManager
|
|
||||||
{
|
|
||||||
OWSAssertDebug(SSKEnvironment.shared.tsAccountManager);
|
|
||||||
|
|
||||||
return SSKEnvironment.shared.tsAccountManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
- (void)loadView
|
|
||||||
{
|
|
||||||
[super loadView];
|
|
||||||
|
|
||||||
self.shouldUseTheme = NO;
|
|
||||||
|
|
||||||
[self createViews];
|
|
||||||
|
|
||||||
// Do any additional setup after loading the view.
|
|
||||||
[self populateDefaultCountryNameAndCode];
|
|
||||||
OWSAssertDebug([self.navigationController isKindOfClass:[OWSNavigationController class]]);
|
|
||||||
[SignalApp.sharedApp setSignUpFlowNavigationController:(OWSNavigationController *)self.navigationController];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewDidLoad
|
|
||||||
{
|
|
||||||
[super viewDidLoad];
|
|
||||||
|
|
||||||
OWSProdInfo([OWSAnalyticsEvents registrationBegan]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)createViews
|
|
||||||
{
|
|
||||||
self.view.backgroundColor = [UIColor whiteColor];
|
|
||||||
self.view.userInteractionEnabled = YES;
|
|
||||||
[self.view
|
|
||||||
addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backgroundTapped:)]];
|
|
||||||
self.view.accessibilityIdentifier = SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, @"root_view");
|
|
||||||
|
|
||||||
UIView *headerWrapper = [UIView containerView];
|
|
||||||
[self.view addSubview:headerWrapper];
|
|
||||||
headerWrapper.backgroundColor = UIColor.ows_signalBrandBlueColor;
|
|
||||||
[headerWrapper autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsZero excludingEdge:ALEdgeBottom];
|
|
||||||
|
|
||||||
UILabel *headerLabel = [UILabel new];
|
|
||||||
headerLabel.text = NSLocalizedString(@"REGISTRATION_TITLE_LABEL", @"");
|
|
||||||
headerLabel.textColor = [UIColor whiteColor];
|
|
||||||
headerLabel.font = [UIFont ows_mediumFontWithSize:ScaleFromIPhone5To7Plus(20.f, 24.f)];
|
|
||||||
|
|
||||||
NSString *legalTopMatterFormat = NSLocalizedString(@"REGISTRATION_LEGAL_TOP_MATTER_FORMAT",
|
|
||||||
@"legal disclaimer, embeds a tappable {{link title}} which is styled as a hyperlink");
|
|
||||||
NSString *legalTopMatterLinkWord = NSLocalizedString(
|
|
||||||
@"REGISTRATION_LEGAL_TOP_MATTER_LINK_TITLE", @"embedded in legal topmatter, styled as a link");
|
|
||||||
NSString *legalTopMatter = [NSString stringWithFormat:legalTopMatterFormat, legalTopMatterLinkWord];
|
|
||||||
NSMutableAttributedString *attributedLegalTopMatter =
|
|
||||||
[[NSMutableAttributedString alloc] initWithString:legalTopMatter];
|
|
||||||
NSRange linkRange = [legalTopMatter rangeOfString:legalTopMatterLinkWord];
|
|
||||||
NSDictionary *linkStyleAttributes = @{
|
|
||||||
NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid),
|
|
||||||
};
|
|
||||||
[attributedLegalTopMatter setAttributes:linkStyleAttributes range:linkRange];
|
|
||||||
|
|
||||||
UILabel *legalTopMatterLabel = [UILabel new];
|
|
||||||
legalTopMatterLabel.textColor = UIColor.whiteColor;
|
|
||||||
legalTopMatterLabel.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(13.f, 15.f)];
|
|
||||||
legalTopMatterLabel.numberOfLines = 0;
|
|
||||||
legalTopMatterLabel.textAlignment = NSTextAlignmentCenter;
|
|
||||||
legalTopMatterLabel.attributedText = attributedLegalTopMatter;
|
|
||||||
legalTopMatterLabel.userInteractionEnabled = YES;
|
|
||||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, legalTopMatterLabel);
|
|
||||||
|
|
||||||
UITapGestureRecognizer *tapGesture =
|
|
||||||
[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapLegalTerms:)];
|
|
||||||
[legalTopMatterLabel addGestureRecognizer:tapGesture];
|
|
||||||
|
|
||||||
UIStackView *headerContent = [[UIStackView alloc] initWithArrangedSubviews:@[ headerLabel ]];
|
|
||||||
[headerContent addArrangedSubview:legalTopMatterLabel];
|
|
||||||
headerContent.axis = UILayoutConstraintAxisVertical;
|
|
||||||
headerContent.alignment = UIStackViewAlignmentCenter;
|
|
||||||
headerContent.spacing = ScaleFromIPhone5To7Plus(8, 16);
|
|
||||||
headerContent.layoutMarginsRelativeArrangement = YES;
|
|
||||||
|
|
||||||
{
|
|
||||||
CGFloat topMargin = ScaleFromIPhone5To7Plus(4, 16);
|
|
||||||
CGFloat bottomMargin = ScaleFromIPhone5To7Plus(8, 16);
|
|
||||||
headerContent.layoutMargins = UIEdgeInsetsMake(topMargin, 40, bottomMargin, 40);
|
|
||||||
}
|
|
||||||
|
|
||||||
[headerWrapper addSubview:headerContent];
|
|
||||||
[headerContent autoPinToTopLayoutGuideOfViewController:self withInset:0];
|
|
||||||
[headerContent autoPinEdgesToSuperviewMarginsExcludingEdge:ALEdgeTop];
|
|
||||||
|
|
||||||
const CGFloat kRowHeight = 60.f;
|
|
||||||
const CGFloat kRowHMargin = 20.f;
|
|
||||||
const CGFloat kSeparatorHeight = 1.f;
|
|
||||||
const CGFloat kExamplePhoneNumberVSpacing = 8.f;
|
|
||||||
const CGFloat fontSizePoints = ScaleFromIPhone5To7Plus(16.f, 20.f);
|
|
||||||
|
|
||||||
UIView *contentView = [UIView containerView];
|
|
||||||
[contentView setHLayoutMargins:kRowHMargin];
|
|
||||||
contentView.backgroundColor = [UIColor whiteColor];
|
|
||||||
[self.view addSubview:contentView];
|
|
||||||
[contentView autoPinToBottomLayoutGuideOfViewController:self withInset:0];
|
|
||||||
[contentView autoPinWidthToSuperview];
|
|
||||||
[contentView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:headerContent];
|
|
||||||
|
|
||||||
// Country
|
|
||||||
UIView *countryRow = [UIView containerView];
|
|
||||||
[contentView addSubview:countryRow];
|
|
||||||
[countryRow autoPinLeadingAndTrailingToSuperviewMargin];
|
|
||||||
[countryRow autoPinEdgeToSuperviewEdge:ALEdgeTop];
|
|
||||||
[countryRow autoSetDimension:ALDimensionHeight toSize:kRowHeight];
|
|
||||||
[countryRow
|
|
||||||
addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
|
|
||||||
action:@selector(countryCodeRowWasTapped:)]];
|
|
||||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, countryRow);
|
|
||||||
|
|
||||||
UILabel *countryNameLabel = [UILabel new];
|
|
||||||
countryNameLabel.text
|
|
||||||
= NSLocalizedString(@"REGISTRATION_DEFAULT_COUNTRY_NAME", @"Label for the country code field");
|
|
||||||
countryNameLabel.textColor = [UIColor blackColor];
|
|
||||||
countryNameLabel.font = [UIFont ows_mediumFontWithSize:fontSizePoints];
|
|
||||||
[countryRow addSubview:countryNameLabel];
|
|
||||||
[countryNameLabel autoVCenterInSuperview];
|
|
||||||
[countryNameLabel autoPinLeadingToSuperviewMargin];
|
|
||||||
|
|
||||||
UILabel *countryCodeLabel = [UILabel new];
|
|
||||||
self.countryCodeLabel = countryCodeLabel;
|
|
||||||
countryCodeLabel.textColor = [UIColor ows_materialBlueColor];
|
|
||||||
countryCodeLabel.font = [UIFont ows_mediumFontWithSize:fontSizePoints + 2.f];
|
|
||||||
[countryRow addSubview:countryCodeLabel];
|
|
||||||
[countryCodeLabel autoVCenterInSuperview];
|
|
||||||
[countryCodeLabel autoPinTrailingToSuperviewMargin];
|
|
||||||
|
|
||||||
UIView *separatorView1 = [UIView new];
|
|
||||||
separatorView1.backgroundColor = [UIColor colorWithWhite:0.75f alpha:1.f];
|
|
||||||
[contentView addSubview:separatorView1];
|
|
||||||
[separatorView1 autoPinWidthToSuperview];
|
|
||||||
[separatorView1 autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:countryRow];
|
|
||||||
[separatorView1 autoSetDimension:ALDimensionHeight toSize:kSeparatorHeight];
|
|
||||||
|
|
||||||
// Phone Number
|
|
||||||
UIView *phoneNumberRow = [UIView containerView];
|
|
||||||
[contentView addSubview:phoneNumberRow];
|
|
||||||
[phoneNumberRow autoPinLeadingAndTrailingToSuperviewMargin];
|
|
||||||
[phoneNumberRow autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:separatorView1];
|
|
||||||
[phoneNumberRow autoSetDimension:ALDimensionHeight toSize:kRowHeight];
|
|
||||||
|
|
||||||
UILabel *phoneNumberLabel = [UILabel new];
|
|
||||||
phoneNumberLabel.text
|
|
||||||
= NSLocalizedString(@"REGISTRATION_PHONENUMBER_BUTTON", @"Label for the phone number textfield");
|
|
||||||
phoneNumberLabel.textColor = [UIColor blackColor];
|
|
||||||
phoneNumberLabel.font = [UIFont ows_mediumFontWithSize:fontSizePoints];
|
|
||||||
[phoneNumberRow addSubview:phoneNumberLabel];
|
|
||||||
[phoneNumberLabel autoVCenterInSuperview];
|
|
||||||
[phoneNumberLabel autoPinLeadingToSuperviewMargin];
|
|
||||||
|
|
||||||
UITextField *phoneNumberTextField;
|
|
||||||
if (UIDevice.currentDevice.isShorterThanIPhone5) {
|
|
||||||
phoneNumberTextField = [DismissableTextField new];
|
|
||||||
} else {
|
|
||||||
phoneNumberTextField = [OWSTextField new];
|
|
||||||
}
|
|
||||||
|
|
||||||
phoneNumberTextField.textAlignment = NSTextAlignmentRight;
|
|
||||||
phoneNumberTextField.delegate = self;
|
|
||||||
phoneNumberTextField.keyboardType = UIKeyboardTypeNumberPad;
|
|
||||||
phoneNumberTextField.placeholder = NSLocalizedString(
|
|
||||||
@"REGISTRATION_ENTERNUMBER_DEFAULT_TEXT", @"Placeholder text for the phone number textfield");
|
|
||||||
self.phoneNumberTextField = phoneNumberTextField;
|
|
||||||
phoneNumberTextField.textColor = [UIColor ows_materialBlueColor];
|
|
||||||
phoneNumberTextField.font = [UIFont ows_mediumFontWithSize:fontSizePoints + 2];
|
|
||||||
[phoneNumberRow addSubview:phoneNumberTextField];
|
|
||||||
[phoneNumberTextField autoVCenterInSuperview];
|
|
||||||
[phoneNumberTextField autoPinTrailingToSuperviewMargin];
|
|
||||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, phoneNumberTextField);
|
|
||||||
|
|
||||||
UILabel *examplePhoneNumberLabel = [UILabel new];
|
|
||||||
self.examplePhoneNumberLabel = examplePhoneNumberLabel;
|
|
||||||
examplePhoneNumberLabel.font = [UIFont ows_regularFontWithSize:fontSizePoints - 2.f];
|
|
||||||
examplePhoneNumberLabel.textColor = Theme.middleGrayColor;
|
|
||||||
[contentView addSubview:examplePhoneNumberLabel];
|
|
||||||
[examplePhoneNumberLabel autoPinTrailingToSuperviewMargin];
|
|
||||||
[examplePhoneNumberLabel autoPinEdge:ALEdgeTop
|
|
||||||
toEdge:ALEdgeBottom
|
|
||||||
ofView:phoneNumberTextField
|
|
||||||
withOffset:kExamplePhoneNumberVSpacing];
|
|
||||||
|
|
||||||
UIView *separatorView2 = [UIView new];
|
|
||||||
separatorView2.backgroundColor = [UIColor colorWithWhite:0.75f alpha:1.f];
|
|
||||||
[contentView addSubview:separatorView2];
|
|
||||||
[separatorView2 autoPinWidthToSuperview];
|
|
||||||
[separatorView2 autoPinEdge:ALEdgeTop
|
|
||||||
toEdge:ALEdgeBottom
|
|
||||||
ofView:phoneNumberRow
|
|
||||||
withOffset:examplePhoneNumberLabel.font.lineHeight];
|
|
||||||
[separatorView2 autoSetDimension:ALDimensionHeight toSize:kSeparatorHeight];
|
|
||||||
|
|
||||||
// Activate Button
|
|
||||||
const CGFloat kActivateButtonHeight = 47.f;
|
|
||||||
// NOTE: We use ows_signalBrandBlueColor instead of ows_materialBlueColor
|
|
||||||
// throughout the onboarding flow to be consistent with the headers.
|
|
||||||
OWSFlatButton *activateButton = [OWSFlatButton buttonWithTitle:NSLocalizedString(@"REGISTRATION_VERIFY_DEVICE", @"")
|
|
||||||
font:[OWSFlatButton fontForHeight:kActivateButtonHeight]
|
|
||||||
titleColor:[UIColor whiteColor]
|
|
||||||
backgroundColor:[UIColor ows_signalBrandBlueColor]
|
|
||||||
target:self
|
|
||||||
selector:@selector(didTapRegisterButton)];
|
|
||||||
self.activateButton = activateButton;
|
|
||||||
[contentView addSubview:activateButton];
|
|
||||||
[activateButton autoPinLeadingAndTrailingToSuperviewMargin];
|
|
||||||
[activateButton autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:separatorView2 withOffset:15];
|
|
||||||
[activateButton autoSetDimension:ALDimensionHeight toSize:kActivateButtonHeight];
|
|
||||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, activateButton);
|
|
||||||
|
|
||||||
UIActivityIndicatorView *spinnerView =
|
|
||||||
[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
|
|
||||||
self.spinnerView = spinnerView;
|
|
||||||
[activateButton addSubview:spinnerView];
|
|
||||||
[spinnerView autoVCenterInSuperview];
|
|
||||||
[spinnerView autoSetDimension:ALDimensionWidth toSize:20.f];
|
|
||||||
[spinnerView autoSetDimension:ALDimensionHeight toSize:20.f];
|
|
||||||
[spinnerView autoPinTrailingToSuperviewMarginWithInset:20.f];
|
|
||||||
[spinnerView stopAnimating];
|
|
||||||
|
|
||||||
NSString *bottomTermsLinkText = NSLocalizedString(@"REGISTRATION_LEGAL_TERMS_LINK",
|
|
||||||
@"one line label below submit button on registration screen, which links to an external webpage.");
|
|
||||||
UIButton *bottomLegalLinkButton = [UIButton new];
|
|
||||||
bottomLegalLinkButton.titleLabel.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(13.f, 15.f)];
|
|
||||||
[bottomLegalLinkButton setTitleColor:UIColor.ows_materialBlueColor forState:UIControlStateNormal];
|
|
||||||
[bottomLegalLinkButton setTitle:bottomTermsLinkText forState:UIControlStateNormal];
|
|
||||||
[contentView addSubview:bottomLegalLinkButton];
|
|
||||||
[bottomLegalLinkButton addTarget:self
|
|
||||||
action:@selector(didTapLegalTerms:)
|
|
||||||
forControlEvents:UIControlEventTouchUpInside];
|
|
||||||
|
|
||||||
[bottomLegalLinkButton autoPinLeadingAndTrailingToSuperviewMargin];
|
|
||||||
[bottomLegalLinkButton autoPinEdge:ALEdgeTop
|
|
||||||
toEdge:ALEdgeBottom
|
|
||||||
ofView:activateButton
|
|
||||||
withOffset:ScaleFromIPhone5To7Plus(8, 12)];
|
|
||||||
[bottomLegalLinkButton setCompressionResistanceHigh];
|
|
||||||
[bottomLegalLinkButton setContentHuggingHigh];
|
|
||||||
|
|
||||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, bottomLegalLinkButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewDidAppear:(BOOL)animated
|
|
||||||
{
|
|
||||||
[super viewDidAppear:animated];
|
|
||||||
|
|
||||||
[self.activateButton setEnabled:YES];
|
|
||||||
[self.spinnerView stopAnimating];
|
|
||||||
[self.phoneNumberTextField becomeFirstResponder];
|
|
||||||
|
|
||||||
if (self.tsAccountManager.isReregistering) {
|
|
||||||
// If re-registering, pre-populate the country (country code, calling code, country name)
|
|
||||||
// and phone number state.
|
|
||||||
NSString *_Nullable phoneNumberE164 = self.tsAccountManager.reregisterationPhoneNumber;
|
|
||||||
if (!phoneNumberE164) {
|
|
||||||
OWSFailDebug(@"Could not resume re-registration; missing phone number.");
|
|
||||||
} else if ([self tryToApplyPhoneNumberE164:phoneNumberE164]) {
|
|
||||||
// Don't let user edit their phone number while re-registering.
|
|
||||||
self.phoneNumberTextField.enabled = NO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)tryToApplyPhoneNumberE164:(NSString *)phoneNumberE164
|
|
||||||
{
|
|
||||||
OWSAssertDebug(phoneNumberE164);
|
|
||||||
|
|
||||||
if (phoneNumberE164.length < 1) {
|
|
||||||
OWSFailDebug(@"Could not resume re-registration; invalid phoneNumberE164.");
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
PhoneNumber *_Nullable parsedPhoneNumber = [PhoneNumber phoneNumberFromE164:phoneNumberE164];
|
|
||||||
if (!parsedPhoneNumber) {
|
|
||||||
OWSFailDebug(@"Could not resume re-registration; couldn't parse phoneNumberE164.");
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
NSNumber *_Nullable callingCode = parsedPhoneNumber.getCountryCode;
|
|
||||||
if (!callingCode) {
|
|
||||||
OWSFailDebug(@"Could not resume re-registration; missing callingCode.");
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
NSString *callingCodeText = [NSString stringWithFormat:@"+%d", callingCode.intValue];
|
|
||||||
NSArray<NSString *> *_Nullable countryCodes =
|
|
||||||
[PhoneNumberUtil.sharedThreadLocal countryCodesFromCallingCode:callingCodeText];
|
|
||||||
if (countryCodes.count < 1) {
|
|
||||||
OWSFailDebug(@"Could not resume re-registration; unknown countryCode.");
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
NSString *countryCode = countryCodes.firstObject;
|
|
||||||
NSString *_Nullable countryName = [PhoneNumberUtil countryNameFromCountryCode:countryCode];
|
|
||||||
if (!countryName) {
|
|
||||||
OWSFailDebug(@"Could not resume re-registration; unknown countryName.");
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
if (![phoneNumberE164 hasPrefix:callingCodeText]) {
|
|
||||||
OWSFailDebug(@"Could not resume re-registration; non-matching calling code.");
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
NSString *phoneNumberWithoutCallingCode = [phoneNumberE164 substringFromIndex:callingCodeText.length];
|
|
||||||
|
|
||||||
[self updateCountryWithName:countryName callingCode:callingCodeText countryCode:countryCode];
|
|
||||||
self.phoneNumberTextField.text = phoneNumberWithoutCallingCode;
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Country
|
|
||||||
|
|
||||||
- (void)populateDefaultCountryNameAndCode
|
|
||||||
{
|
|
||||||
NSString *countryCode = [PhoneNumber defaultCountryCode];
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
if ([self lastRegisteredCountryCode].length > 0) {
|
|
||||||
countryCode = [self lastRegisteredCountryCode];
|
|
||||||
}
|
|
||||||
self.phoneNumberTextField.text = [self lastRegisteredPhoneNumber];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
NSNumber *callingCode = [[PhoneNumberUtil sharedThreadLocal].nbPhoneNumberUtil getCountryCodeForRegion:countryCode];
|
|
||||||
NSString *countryName = [PhoneNumberUtil countryNameFromCountryCode:countryCode];
|
|
||||||
[self updateCountryWithName:countryName
|
|
||||||
callingCode:[NSString stringWithFormat:@"%@%@", COUNTRY_CODE_PREFIX, callingCode]
|
|
||||||
countryCode:countryCode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)updateCountryWithName:(NSString *)countryName
|
|
||||||
callingCode:(NSString *)callingCode
|
|
||||||
countryCode:(NSString *)countryCode
|
|
||||||
{
|
|
||||||
OWSAssertIsOnMainThread();
|
|
||||||
OWSAssertDebug(countryName.length > 0);
|
|
||||||
OWSAssertDebug(callingCode.length > 0);
|
|
||||||
OWSAssertDebug(countryCode.length > 0);
|
|
||||||
|
|
||||||
_countryCode = countryCode;
|
|
||||||
_callingCode = callingCode;
|
|
||||||
|
|
||||||
NSString *title = [NSString stringWithFormat:@"%@ (%@)", callingCode, countryCode.localizedUppercaseString];
|
|
||||||
self.countryCodeLabel.text = title;
|
|
||||||
[self.countryCodeLabel setNeedsLayout];
|
|
||||||
|
|
||||||
self.examplePhoneNumberLabel.text =
|
|
||||||
[ViewControllerUtils examplePhoneNumberForCountryCode:countryCode callingCode:callingCode];
|
|
||||||
[self.examplePhoneNumberLabel setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Actions
|
|
||||||
|
|
||||||
- (void)didTapRegisterButton
|
|
||||||
{
|
|
||||||
NSString *phoneNumberText = [_phoneNumberTextField.text ows_stripped];
|
|
||||||
if (phoneNumberText.length < 1) {
|
|
||||||
[OWSAlerts
|
|
||||||
showAlertWithTitle:NSLocalizedString(@"REGISTRATION_VIEW_NO_PHONE_NUMBER_ALERT_TITLE",
|
|
||||||
@"Title of alert indicating that users needs to enter a phone number to register.")
|
|
||||||
message:
|
|
||||||
NSLocalizedString(@"REGISTRATION_VIEW_NO_PHONE_NUMBER_ALERT_MESSAGE",
|
|
||||||
@"Message of alert indicating that users needs to enter a phone number to register.")];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *countryCode = self.countryCode;
|
|
||||||
NSString *phoneNumber = [NSString stringWithFormat:@"%@%@", _callingCode, phoneNumberText];
|
|
||||||
PhoneNumber *localNumber = [PhoneNumber tryParsePhoneNumberFromUserSpecifiedText:phoneNumber];
|
|
||||||
NSString *parsedPhoneNumber = localNumber.toE164;
|
|
||||||
if (parsedPhoneNumber.length < 1
|
|
||||||
|| ![[PhoneNumberValidator new] isValidForRegistrationWithPhoneNumber:localNumber]) {
|
|
||||||
[OWSAlerts showAlertWithTitle:
|
|
||||||
NSLocalizedString(@"REGISTRATION_VIEW_INVALID_PHONE_NUMBER_ALERT_TITLE",
|
|
||||||
@"Title of alert indicating that users needs to enter a valid phone number to register.")
|
|
||||||
message:NSLocalizedString(@"REGISTRATION_VIEW_INVALID_PHONE_NUMBER_ALERT_MESSAGE",
|
|
||||||
@"Message of alert indicating that users needs to enter a valid phone number "
|
|
||||||
@"to register.")];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UIDevice.currentDevice.isIPad) {
|
|
||||||
[OWSAlerts showConfirmationAlertWithTitle:NSLocalizedString(@"REGISTRATION_IPAD_CONFIRM_TITLE",
|
|
||||||
@"alert title when registering an iPad")
|
|
||||||
message:NSLocalizedString(@"REGISTRATION_IPAD_CONFIRM_BODY",
|
|
||||||
@"alert body when registering an iPad")
|
|
||||||
proceedTitle:NSLocalizedString(@"REGISTRATION_IPAD_CONFIRM_BUTTON",
|
|
||||||
@"button text to proceed with registration when on an iPad")
|
|
||||||
proceedAction:^(UIAlertAction *_Nonnull action) {
|
|
||||||
[self sendCodeActionWithParsedPhoneNumber:parsedPhoneNumber
|
|
||||||
phoneNumberText:phoneNumberText
|
|
||||||
countryCode:countryCode];
|
|
||||||
}];
|
|
||||||
} else {
|
|
||||||
[self sendCodeActionWithParsedPhoneNumber:parsedPhoneNumber
|
|
||||||
phoneNumberText:phoneNumberText
|
|
||||||
countryCode:countryCode];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)sendCodeActionWithParsedPhoneNumber:(NSString *)parsedPhoneNumber
|
|
||||||
phoneNumberText:(NSString *)phoneNumberText
|
|
||||||
countryCode:(NSString *)countryCode
|
|
||||||
{
|
|
||||||
[self.activateButton setEnabled:NO];
|
|
||||||
[self.spinnerView startAnimating];
|
|
||||||
[self.phoneNumberTextField resignFirstResponder];
|
|
||||||
|
|
||||||
__weak RegistrationViewController *weakSelf = self;
|
|
||||||
[self.tsAccountManager registerWithPhoneNumber:parsedPhoneNumber
|
|
||||||
captchaToken:nil
|
|
||||||
success:^{
|
|
||||||
OWSProdInfo([OWSAnalyticsEvents registrationRegisteredPhoneNumber]);
|
|
||||||
|
|
||||||
[weakSelf.spinnerView stopAnimating];
|
|
||||||
|
|
||||||
CodeVerificationViewController *vc = [CodeVerificationViewController new];
|
|
||||||
[weakSelf.navigationController pushViewController:vc animated:YES];
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
[weakSelf setLastRegisteredCountryCode:countryCode];
|
|
||||||
[weakSelf setLastRegisteredPhoneNumber:phoneNumberText];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
failure:^(NSError *error) {
|
|
||||||
if (error.code == 400) {
|
|
||||||
[OWSAlerts showAlertWithTitle:NSLocalizedString(@"REGISTRATION_ERROR", nil)
|
|
||||||
message:NSLocalizedString(@"REGISTRATION_NON_VALID_NUMBER", nil)];
|
|
||||||
} else {
|
|
||||||
[OWSAlerts showAlertWithTitle:error.localizedDescription message:error.localizedRecoverySuggestion];
|
|
||||||
}
|
|
||||||
|
|
||||||
[weakSelf.activateButton setEnabled:YES];
|
|
||||||
[weakSelf.spinnerView stopAnimating];
|
|
||||||
[weakSelf.phoneNumberTextField becomeFirstResponder];
|
|
||||||
}
|
|
||||||
smsVerification:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)countryCodeRowWasTapped:(UIGestureRecognizer *)sender
|
|
||||||
{
|
|
||||||
if (self.tsAccountManager.isReregistering) {
|
|
||||||
// Don't let user edit their phone number while re-registering.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sender.state == UIGestureRecognizerStateRecognized) {
|
|
||||||
[self changeCountryCodeTapped];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)didTapLegalTerms:(UIButton *)sender
|
|
||||||
{
|
|
||||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:kLegalTermsUrlString]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)changeCountryCodeTapped
|
|
||||||
{
|
|
||||||
CountryCodeViewController *countryCodeController = [CountryCodeViewController new];
|
|
||||||
countryCodeController.countryCodeDelegate = self;
|
|
||||||
countryCodeController.interfaceOrientationMask = UIInterfaceOrientationMaskPortrait;
|
|
||||||
OWSNavigationController *navigationController =
|
|
||||||
[[OWSNavigationController alloc] initWithRootViewController:countryCodeController];
|
|
||||||
[self presentViewController:navigationController animated:YES completion:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)backgroundTapped:(UIGestureRecognizer *)sender
|
|
||||||
{
|
|
||||||
if (sender.state == UIGestureRecognizerStateRecognized) {
|
|
||||||
[self.phoneNumberTextField becomeFirstResponder];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - CountryCodeViewControllerDelegate
|
|
||||||
|
|
||||||
- (void)countryCodeViewController:(CountryCodeViewController *)vc
|
|
||||||
didSelectCountryCode:(NSString *)countryCode
|
|
||||||
countryName:(NSString *)countryName
|
|
||||||
callingCode:(NSString *)callingCode
|
|
||||||
{
|
|
||||||
OWSAssertDebug(countryCode.length > 0);
|
|
||||||
OWSAssertDebug(countryName.length > 0);
|
|
||||||
OWSAssertDebug(callingCode.length > 0);
|
|
||||||
|
|
||||||
[self updateCountryWithName:countryName callingCode:callingCode countryCode:countryCode];
|
|
||||||
|
|
||||||
// Trigger the formatting logic with a no-op edit.
|
|
||||||
[self textField:self.phoneNumberTextField shouldChangeCharactersInRange:NSMakeRange(0, 0) replacementString:@""];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Keyboard notifications
|
|
||||||
|
|
||||||
- (void)initializeKeyboardHandlers
|
|
||||||
{
|
|
||||||
UITapGestureRecognizer *outsideTabRecognizer =
|
|
||||||
[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboardFromAppropriateSubView)];
|
|
||||||
[self.view addGestureRecognizer:outsideTabRecognizer];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dismissKeyboardFromAppropriateSubView
|
|
||||||
{
|
|
||||||
[self.view endEditing:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - UITextFieldDelegate
|
|
||||||
|
|
||||||
- (BOOL)textField:(UITextField *)textField
|
|
||||||
shouldChangeCharactersInRange:(NSRange)range
|
|
||||||
replacementString:(NSString *)insertionText
|
|
||||||
{
|
|
||||||
|
|
||||||
[ViewControllerUtils phoneNumberTextField:textField
|
|
||||||
shouldChangeCharactersInRange:range
|
|
||||||
replacementString:insertionText
|
|
||||||
callingCode:_callingCode];
|
|
||||||
|
|
||||||
return NO; // inform our caller that we took care of performing the change
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField
|
|
||||||
{
|
|
||||||
[self didTapRegisterButton];
|
|
||||||
[textField resignFirstResponder];
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Debug
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
|
|
||||||
- (NSString *_Nullable)debugValueForKey:(NSString *)key
|
|
||||||
{
|
|
||||||
OWSCAssertDebug([NSThread isMainThread]);
|
|
||||||
OWSCAssertDebug(key.length > 0);
|
|
||||||
|
|
||||||
NSError *error;
|
|
||||||
NSString *_Nullable value =
|
|
||||||
[CurrentAppContext().keychainStorage stringForService:kKeychainService_LastRegistered key:key error:&error];
|
|
||||||
if (error || !value) {
|
|
||||||
OWSLogWarn(@"Could not retrieve 'last registered' value from keychain: %@.", error);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setDebugValue:(NSString *)value forKey:(NSString *)key
|
|
||||||
{
|
|
||||||
OWSCAssertDebug([NSThread isMainThread]);
|
|
||||||
OWSCAssertDebug(key.length > 0);
|
|
||||||
OWSCAssertDebug(value.length > 0);
|
|
||||||
|
|
||||||
NSError *error;
|
|
||||||
BOOL success = [CurrentAppContext().keychainStorage setString:value
|
|
||||||
service:kKeychainService_LastRegistered
|
|
||||||
key:key
|
|
||||||
error:&error];
|
|
||||||
if (!success || error) {
|
|
||||||
OWSLogError(@"Error persisting 'last registered' value in keychain: %@", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *_Nullable)lastRegisteredCountryCode
|
|
||||||
{
|
|
||||||
return [self debugValueForKey:kKeychainKey_LastRegisteredCountryCode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setLastRegisteredCountryCode:(NSString *)value
|
|
||||||
{
|
|
||||||
[self setDebugValue:value forKey:kKeychainKey_LastRegisteredCountryCode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *_Nullable)lastRegisteredPhoneNumber
|
|
||||||
{
|
|
||||||
return [self debugValueForKey:kKeychainKey_LastRegisteredPhoneNumber];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setLastRegisteredPhoneNumber:(NSString *)value
|
|
||||||
{
|
|
||||||
[self setDebugValue:value forKey:kKeychainKey_LastRegisteredPhoneNumber];
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
|
||||||
{
|
|
||||||
return UIStatusBarStyleLightContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Orientation
|
|
||||||
|
|
||||||
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
|
|
||||||
{
|
|
||||||
return UIInterfaceOrientationMaskPortrait;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
Loading…
Reference in New Issue