mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-06 07:57:42 +02:00
Authentication: Support forgot password option
This commit is contained in:
@@ -42,12 +42,16 @@
|
||||
// Authentication
|
||||
"auth_login" = "Log in";
|
||||
"auth_register" = "Register";
|
||||
"auth_send_reset_email" = "Send Reset Email";
|
||||
"auth_return_to_login" = "Return to login screen";
|
||||
"auth_user_id_placeholder" = "Email or user name";
|
||||
"auth_password_placeholder" = "Password";
|
||||
"auth_new_password_placeholder" = "New password";
|
||||
"auth_user_name_placeholder" = "Your name";
|
||||
"auth_optional_email_placeholder" = "Email address (optional)";
|
||||
"auth_email_placeholder" = "Email address";
|
||||
"auth_repeat_password_placeholder" = "Repeat password";
|
||||
"auth_repeat_new_password_placeholder" = "Confirm your new password";
|
||||
"auth_invalid_login_param" = "Incorrect username and/or password";
|
||||
"auth_invalid_user_name" = "User names may only contain letters, numbers, dots, hyphens and underscores";
|
||||
"auth_invalid_password" = "Password too short (min 6)";
|
||||
@@ -56,11 +60,19 @@
|
||||
"auth_missing_optional_email" = "If you don't specify an email address, you won't be able to reset your password. Are you sure?";
|
||||
"auth_missing_email" = "Missing email address";
|
||||
"auth_password_dont_match" = "Passwords don't match";
|
||||
"auth_username_in_use" = "Username in use";
|
||||
"auth_forgot_password" = "Forgot password?";
|
||||
"auth_use_server_options" = "Use custom server options (advanced)";
|
||||
"auth_email_validation_message" = "Please check your email to continue registration";
|
||||
"auth_recaptcha_message" = "This Home Server would like to make sure you are not a robot";
|
||||
"auth_username_in_use" = "Username in use";
|
||||
"auth_reset_password_message" = "To reset your password, enter the email address linked to your account:";
|
||||
"auth_reset_password_missing_email" = "The email address linked to your account must be entered.";
|
||||
"auth_reset_password_missing_password" = "A new password must be entered.";
|
||||
"auth_reset_password_email_validation_message" = "An email has been sent to %@. Once you've followed the link it contains, click below.";
|
||||
"auth_reset_password_next_step_button" = "I have verified my email address";
|
||||
"auth_reset_password_error_unauthorized" = "Failed to verify email address: make sure you clicked the link in the email";
|
||||
"auth_reset_password_error_not_found" = "Your email address does not appear to be associated with a Matrix ID on this Homeserver.";
|
||||
"auth_reset_password_success_message" = "Your password has been reset.\nYou have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, re-log in on each device.";
|
||||
|
||||
// Chat creation
|
||||
"room_creation_title" = "New Chat";
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#import "AuthenticationViewController.h"
|
||||
|
||||
#import "AuthInputsView.h"
|
||||
#import "ForgotPasswordInputsView.h"
|
||||
|
||||
#import "RageShakeManager.h"
|
||||
|
||||
@@ -36,6 +37,8 @@
|
||||
bundle:[NSBundle bundleForClass:self]];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
@@ -65,10 +68,12 @@
|
||||
[self.submitButton setTitle:NSLocalizedStringFromTable(@"auth_login", @"Vector", nil) forState:UIControlStateHighlighted];
|
||||
self.submitButton.enabled = YES;
|
||||
|
||||
[self.forgotPasswordButton setTitle:NSLocalizedStringFromTable(@"auth_forgot_password", @"Vector", nil) forState:UIControlStateNormal];
|
||||
[self.forgotPasswordButton setTitle:NSLocalizedStringFromTable(@"auth_forgot_password", @"Vector", nil) forState:UIControlStateHighlighted];
|
||||
[self.forgotPasswordButton setTitleColor:kVectorTextColorGray forState:UIControlStateNormal];
|
||||
[self.forgotPasswordButton setTitleColor:kVectorTextColorGray forState:UIControlStateHighlighted];
|
||||
NSMutableAttributedString *forgotPasswordTitle = [[NSMutableAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"auth_forgot_password", @"Vector", nil)];
|
||||
[forgotPasswordTitle addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:NSMakeRange(0, forgotPasswordTitle.length)];
|
||||
[forgotPasswordTitle addAttribute:NSForegroundColorAttributeName value:kVectorColorGreen range:NSMakeRange(0, forgotPasswordTitle.length)];
|
||||
[self.forgotPasswordButton setAttributedTitle:forgotPasswordTitle forState:UIControlStateNormal];
|
||||
[self.forgotPasswordButton setAttributedTitle:forgotPasswordTitle forState:UIControlStateHighlighted];
|
||||
self.forgotPasswordButton.hidden = (self.authType != MXKAuthenticationTypeLogin);
|
||||
|
||||
[self.serverOptionsTickButton setImage:[UIImage imageNamed:@"selection_untick"] forState:UIControlStateNormal];
|
||||
[self.serverOptionsTickButton setImage:[UIImage imageNamed:@"selection_untick"] forState:UIControlStateHighlighted];
|
||||
@@ -85,15 +90,13 @@
|
||||
// Custom used authInputsView
|
||||
[self registerAuthInputsViewClass:AuthInputsView.class forAuthType:MXKAuthenticationTypeLogin];
|
||||
[self registerAuthInputsViewClass:AuthInputsView.class forAuthType:MXKAuthenticationTypeRegister];
|
||||
[self registerAuthInputsViewClass:ForgotPasswordInputsView.class forAuthType:MXKAuthenticationTypeForgotPassword];
|
||||
|
||||
// Initialize the auth inputs display
|
||||
AuthInputsView *authInputsView = [AuthInputsView authInputsView];
|
||||
MXAuthenticationSession *authSession = [MXAuthenticationSession modelFromJSON:@{@"flows":@[@{@"stages":@[kMXLoginFlowTypePassword]}]}];
|
||||
[authInputsView setAuthSession:authSession withAuthType:MXKAuthenticationTypeLogin];
|
||||
self.authInputsView = authInputsView;
|
||||
|
||||
// FIXME handle "Forgot password"
|
||||
self.forgotPasswordButton.hidden = YES;
|
||||
}
|
||||
|
||||
- (void)setAuthType:(MXKAuthenticationType)authType
|
||||
@@ -105,11 +108,26 @@
|
||||
[self.submitButton setTitle:NSLocalizedStringFromTable(@"auth_login", @"Vector", nil) forState:UIControlStateNormal];
|
||||
[self.submitButton setTitle:NSLocalizedStringFromTable(@"auth_login", @"Vector", nil) forState:UIControlStateHighlighted];
|
||||
}
|
||||
else
|
||||
else if (authType == MXKAuthenticationTypeRegister)
|
||||
{
|
||||
[self.submitButton setTitle:NSLocalizedStringFromTable(@"auth_register", @"Vector", nil) forState:UIControlStateNormal];
|
||||
[self.submitButton setTitle:NSLocalizedStringFromTable(@"auth_register", @"Vector", nil) forState:UIControlStateHighlighted];
|
||||
}
|
||||
else if (authType == MXKAuthenticationTypeForgotPassword)
|
||||
{
|
||||
if (isPasswordReseted)
|
||||
{
|
||||
[self.submitButton setTitle:NSLocalizedStringFromTable(@"auth_return_to_login", @"Vector", nil) forState:UIControlStateNormal];
|
||||
[self.submitButton setTitle:NSLocalizedStringFromTable(@"auth_return_to_login", @"Vector", nil) forState:UIControlStateHighlighted];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self.submitButton setTitle:NSLocalizedStringFromTable(@"auth_send_reset_email", @"Vector", nil) forState:UIControlStateNormal];
|
||||
[self.submitButton setTitle:NSLocalizedStringFromTable(@"auth_send_reset_email", @"Vector", nil) forState:UIControlStateHighlighted];
|
||||
}
|
||||
}
|
||||
|
||||
self.forgotPasswordButton.hidden = (authType != MXKAuthenticationTypeLogin);
|
||||
}
|
||||
|
||||
- (void)setUserInteractionEnabled:(BOOL)userInteractionEnabled
|
||||
@@ -132,10 +150,15 @@
|
||||
{
|
||||
self.rightBarButtonItem.title = NSLocalizedStringFromTable(@"auth_register", @"Vector", nil);
|
||||
}
|
||||
else
|
||||
else if (self.authType == MXKAuthenticationTypeRegister)
|
||||
{
|
||||
self.rightBarButtonItem.title = NSLocalizedStringFromTable(@"auth_login", @"Vector", nil);
|
||||
}
|
||||
else if (self.authType == MXKAuthenticationTypeForgotPassword)
|
||||
{
|
||||
// The right bar button is used to return to login.
|
||||
self.rightBarButtonItem.title = NSLocalizedStringFromTable(@"cancel", @"Vector", nil);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +170,8 @@
|
||||
}
|
||||
else if (sender == self.forgotPasswordButton)
|
||||
{
|
||||
// TODO
|
||||
// Update UI to reset password
|
||||
self.authType = MXKAuthenticationTypeForgotPassword;
|
||||
}
|
||||
else if (sender == self.rightBarButtonItem)
|
||||
{
|
||||
@@ -161,15 +185,11 @@
|
||||
{
|
||||
self.authType = MXKAuthenticationTypeRegister;
|
||||
self.rightBarButtonItem.title = NSLocalizedStringFromTable(@"auth_login", @"Vector", nil);
|
||||
// FIXME handle "Forgot password"
|
||||
// self.forgotPasswordButton.hidden = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.authType = MXKAuthenticationTypeLogin;
|
||||
self.rightBarButtonItem.title = NSLocalizedStringFromTable(@"auth_register", @"Vector", nil);
|
||||
// FIXME handle "Forgot password"
|
||||
// self.forgotPasswordButton.hidden = NO;
|
||||
}
|
||||
}
|
||||
else if (sender == self.submitButton)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9532" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10116" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9530"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="AuthenticationViewController">
|
||||
@@ -123,26 +123,33 @@
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Gg0-TE-OGb">
|
||||
<rect key="frame" x="0.0" y="300" width="600" height="300"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="AJ2-lJ-NUq">
|
||||
<rect key="frame" x="19" y="33" width="123" height="30"/>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" hasAttributedTitle="YES" translatesAutoresizingMaskIntoConstraints="NO" id="AJ2-lJ-NUq">
|
||||
<rect key="frame" x="19" y="33" width="122" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="1mr-dZ-KtP"/>
|
||||
</constraints>
|
||||
<state key="normal" title="Forgot password?">
|
||||
<color key="titleColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<state key="normal">
|
||||
<attributedString key="attributedTitle">
|
||||
<fragment content="Forgot password?">
|
||||
<attributes>
|
||||
<color key="NSColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<font key="NSFont" size="15" name="HelveticaNeue"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
</attributedString>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onButtonPressed:" destination="-1" eventType="touchUpInside" id="UVJ-Re-xe2"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="k3J-Eg-itz" userLabel="SubmitBtn">
|
||||
<rect key="frame" x="469" y="33" width="120" height="30"/>
|
||||
<rect key="frame" x="483" y="33" width="106" height="30"/>
|
||||
<color key="backgroundColor" red="0.028153735480000001" green="0.82494870580000002" blue="0.051896891280000003" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="120" id="H9x-Zc-0sE"/>
|
||||
<constraint firstAttribute="height" constant="30" id="rR8-KH-2z5"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/>
|
||||
<inset key="contentEdgeInsets" minX="30" minY="0.0" maxX="30" maxY="0.0"/>
|
||||
<state key="normal" title="Log In">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
|
||||
@@ -66,81 +66,102 @@
|
||||
submittedEmail = nil;
|
||||
}
|
||||
|
||||
-(void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
CGRect lastItemFrame;
|
||||
|
||||
if (self.recaptchaWebView.isHidden)
|
||||
{
|
||||
lastItemFrame = self.repeatPasswordContainer.frame;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastItemFrame = self.recaptchaWebView.frame;
|
||||
}
|
||||
|
||||
self.viewHeightConstraint.constant = lastItemFrame.origin.y + lastItemFrame.size.height;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (BOOL)setAuthSession:(MXAuthenticationSession *)authSession withAuthType:(MXKAuthenticationType)authType;
|
||||
{
|
||||
// Validate first the provided session
|
||||
MXAuthenticationSession *validSession = [self validateAuthenticationSession:authSession];
|
||||
|
||||
// Cancel email validation if any
|
||||
if (submittedEmail)
|
||||
if (type == MXKAuthenticationTypeLogin || type == MXKAuthenticationTypeRegister)
|
||||
{
|
||||
[submittedEmail cancelCurrentRequest];
|
||||
submittedEmail = nil;
|
||||
}
|
||||
|
||||
// Reset external registration parameters
|
||||
externalRegistrationParameters = nil;
|
||||
|
||||
// Reset UI by hidding all items
|
||||
[self hideInputsContainer];
|
||||
|
||||
if ([super setAuthSession:validSession withAuthType:authType])
|
||||
{
|
||||
if (authType == MXKAuthenticationTypeLogin)
|
||||
// Validate first the provided session
|
||||
MXAuthenticationSession *validSession = [self validateAuthenticationSession:authSession];
|
||||
|
||||
// Cancel email validation if any
|
||||
if (submittedEmail)
|
||||
{
|
||||
self.passWordTextField.returnKeyType = UIReturnKeyDone;
|
||||
|
||||
self.userLoginTextField.placeholder = NSLocalizedStringFromTable(@"auth_user_id_placeholder", @"Vector", nil);
|
||||
|
||||
self.userLoginContainerTopConstraint.constant = 0;
|
||||
self.passwordContainerTopConstraint.constant = 50;
|
||||
|
||||
self.userLoginContainer.hidden = NO;
|
||||
self.passwordContainer.hidden = NO;
|
||||
self.emailContainer.hidden = YES;
|
||||
self.repeatPasswordContainer.hidden = YES;
|
||||
[submittedEmail cancelCurrentRequest];
|
||||
submittedEmail = nil;
|
||||
}
|
||||
else
|
||||
|
||||
// Reset external registration parameters
|
||||
externalRegistrationParameters = nil;
|
||||
|
||||
// Reset UI by hidding all items
|
||||
[self hideInputsContainer];
|
||||
|
||||
if ([super setAuthSession:validSession withAuthType:authType])
|
||||
{
|
||||
self.passWordTextField.returnKeyType = UIReturnKeyNext;
|
||||
|
||||
self.userLoginTextField.placeholder = NSLocalizedStringFromTable(@"auth_user_name_placeholder", @"Vector", nil);
|
||||
|
||||
if (self.isEmailIdentityFlowSupported)
|
||||
if (authType == MXKAuthenticationTypeLogin)
|
||||
{
|
||||
if (self.isEmailIdentityFlowRequired)
|
||||
{
|
||||
self.emailTextField.placeholder = NSLocalizedStringFromTable(@"auth_email_placeholder", @"Vector", nil);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.emailTextField.placeholder = NSLocalizedStringFromTable(@"auth_optional_email_placeholder", @"Vector", nil);
|
||||
}
|
||||
self.passWordTextField.returnKeyType = UIReturnKeyDone;
|
||||
|
||||
self.userLoginContainerTopConstraint.constant = 50;
|
||||
self.passwordContainerTopConstraint.constant = 100;
|
||||
self.userLoginTextField.placeholder = NSLocalizedStringFromTable(@"auth_user_id_placeholder", @"Vector", nil);
|
||||
|
||||
self.emailContainer.hidden = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.userLoginContainerTopConstraint.constant = 0;
|
||||
self.passwordContainerTopConstraint.constant = 50;
|
||||
|
||||
self.userLoginContainer.hidden = NO;
|
||||
self.passwordContainer.hidden = NO;
|
||||
self.emailContainer.hidden = YES;
|
||||
self.repeatPasswordContainer.hidden = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.passWordTextField.returnKeyType = UIReturnKeyNext;
|
||||
|
||||
self.userLoginTextField.placeholder = NSLocalizedStringFromTable(@"auth_user_name_placeholder", @"Vector", nil);
|
||||
|
||||
if (self.isEmailIdentityFlowSupported)
|
||||
{
|
||||
if (self.isEmailIdentityFlowRequired)
|
||||
{
|
||||
self.emailTextField.placeholder = NSLocalizedStringFromTable(@"auth_email_placeholder", @"Vector", nil);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.emailTextField.placeholder = NSLocalizedStringFromTable(@"auth_optional_email_placeholder", @"Vector", nil);
|
||||
}
|
||||
|
||||
self.userLoginContainerTopConstraint.constant = 50;
|
||||
self.passwordContainerTopConstraint.constant = 100;
|
||||
|
||||
self.emailContainer.hidden = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.userLoginContainerTopConstraint.constant = 0;
|
||||
self.passwordContainerTopConstraint.constant = 50;
|
||||
|
||||
self.emailContainer.hidden = YES;
|
||||
}
|
||||
|
||||
self.userLoginContainer.hidden = NO;
|
||||
self.passwordContainer.hidden = NO;
|
||||
self.repeatPasswordContainer.hidden = NO;
|
||||
}
|
||||
|
||||
self.userLoginContainer.hidden = NO;
|
||||
self.passwordContainer.hidden = NO;
|
||||
self.repeatPasswordContainer.hidden = NO;
|
||||
CGRect frame = self.repeatPasswordContainer.frame;
|
||||
self.viewHeightConstraint.constant = frame.origin.y + frame.size.height;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
CGRect frame = self.repeatPasswordContainer.frame;
|
||||
self.viewHeightConstraint.constant = frame.origin.y + frame.size.height;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
@@ -177,7 +198,7 @@
|
||||
errorMsg = [NSBundle mxk_localizedStringForKey:@"not_supported_yet"];
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (type == MXKAuthenticationTypeRegister)
|
||||
{
|
||||
if (!self.userLoginTextField.text.length)
|
||||
{
|
||||
@@ -292,7 +313,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (type == MXKAuthenticationTypeRegister)
|
||||
{
|
||||
// Check whether an email has been set, and if it is not handled yet
|
||||
if (!self.emailContainer.isHidden && self.emailTextField.text.length && !self.isEmailIdentityFlowCompleted)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import <MatrixKit/MatrixKit.h>
|
||||
|
||||
@interface ForgotPasswordInputsView : MXKAuthInputsView
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UITextField *emailTextField;
|
||||
@property (weak, nonatomic) IBOutlet UITextField *passWordTextField;
|
||||
@property (weak, nonatomic) IBOutlet UITextField *repeatPasswordTextField;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *emailContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIView *passwordContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIView *repeatPasswordContainer;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *emailSeparator;
|
||||
@property (weak, nonatomic) IBOutlet UIView *passwordSeparator;
|
||||
@property (weak, nonatomic) IBOutlet UIView *repeatPasswordSeparator;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel *messageLabel;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIButton *nextStepButton;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import "ForgotPasswordInputsView.h"
|
||||
|
||||
#import "VectorDesignValues.h"
|
||||
|
||||
@interface ForgotPasswordInputsView ()
|
||||
{
|
||||
/**
|
||||
The current email validation
|
||||
*/
|
||||
MXK3PID *submittedEmail;
|
||||
|
||||
/**
|
||||
The current set of parameters ready to use.
|
||||
*/
|
||||
NSDictionary *parameters;
|
||||
|
||||
/**
|
||||
The block called when the parameters are ready and the user confirms he has checked his email.
|
||||
*/
|
||||
void (^didPrepareParametersCallback)(NSDictionary *parameters);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ForgotPasswordInputsView
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
return [UINib nibWithNibName:NSStringFromClass(self)
|
||||
bundle:[NSBundle bundleForClass:self]];
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
|
||||
self.emailTextField.placeholder = NSLocalizedStringFromTable(@"auth_email_placeholder", @"Vector", nil);
|
||||
self.emailTextField.textColor = kVectorTextColorBlack;
|
||||
|
||||
self.passWordTextField.placeholder = NSLocalizedStringFromTable(@"auth_new_password_placeholder", @"Vector", nil);
|
||||
self.passWordTextField.textColor = kVectorTextColorBlack;
|
||||
|
||||
self.repeatPasswordTextField.placeholder = NSLocalizedStringFromTable(@"auth_repeat_new_password_placeholder", @"Vector", nil);
|
||||
self.repeatPasswordTextField.textColor = kVectorTextColorBlack;
|
||||
|
||||
self.messageLabel.numberOfLines = 0;
|
||||
|
||||
[self.nextStepButton.layer setCornerRadius:5];
|
||||
self.nextStepButton.clipsToBounds = YES;
|
||||
self.nextStepButton.backgroundColor = kVectorColorGreen;
|
||||
[self.nextStepButton setTitle:[NSBundle mxk_localizedStringForKey:@"auth_reset_password_next_step_button"] forState:UIControlStateNormal];
|
||||
[self.nextStepButton setTitle:[NSBundle mxk_localizedStringForKey:@"auth_reset_password_next_step_button"] forState:UIControlStateHighlighted];
|
||||
self.nextStepButton.enabled = YES;
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
[super destroy];
|
||||
|
||||
submittedEmail = nil;
|
||||
|
||||
parameters = nil;
|
||||
didPrepareParametersCallback = nil;
|
||||
}
|
||||
|
||||
-(void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
CGRect lastItemFrame;
|
||||
|
||||
if (!self.repeatPasswordContainer.isHidden)
|
||||
{
|
||||
lastItemFrame = self.repeatPasswordContainer.frame;
|
||||
}
|
||||
else if (!self.nextStepButton.isHidden)
|
||||
{
|
||||
lastItemFrame = self.nextStepButton.frame;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastItemFrame = self.messageLabel.frame;
|
||||
}
|
||||
|
||||
self.viewHeightConstraint.constant = lastItemFrame.origin.y + lastItemFrame.size.height;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (BOOL)setAuthSession:(MXAuthenticationSession *)authSession withAuthType:(MXKAuthenticationType)authType;
|
||||
{
|
||||
if (authType == MXKAuthenticationTypeForgotPassword)
|
||||
{
|
||||
type = MXKAuthenticationTypeForgotPassword;
|
||||
|
||||
// authSession is not used here, filled it by default (it should be nil).
|
||||
currentSession = authSession;
|
||||
|
||||
// Reset UI in initial step
|
||||
[self reset];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSString*)validateParameters
|
||||
{
|
||||
// Check the validity of the parameters
|
||||
NSString *errorMsg = nil;
|
||||
|
||||
if (!self.emailTextField.text.length)
|
||||
{
|
||||
NSLog(@"[ForgotPasswordInputsView] Missing email");
|
||||
errorMsg = NSLocalizedStringFromTable(@"auth_reset_password_missing_email", @"Vector", nil);
|
||||
}
|
||||
else if (!self.passWordTextField.text.length)
|
||||
{
|
||||
NSLog(@"[ForgotPasswordInputsView] Missing Passwords");
|
||||
errorMsg = NSLocalizedStringFromTable(@"auth_reset_password_missing_password", @"Vector", nil);
|
||||
}
|
||||
else if (self.passWordTextField.text.length < 6)
|
||||
{
|
||||
NSLog(@"[ForgotPasswordInputsView] Invalid Passwords");
|
||||
errorMsg = NSLocalizedStringFromTable(@"auth_invalid_password", @"Vector", nil);
|
||||
}
|
||||
else if ([self.repeatPasswordTextField.text isEqualToString:self.passWordTextField.text] == NO)
|
||||
{
|
||||
NSLog(@"[ForgotPasswordInputsView] Passwords don't match");
|
||||
errorMsg = NSLocalizedStringFromTable(@"auth_password_dont_match", @"Vector", nil);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check validity of the non empty email
|
||||
if ([MXTools isEmailAddress:self.emailTextField.text] == NO)
|
||||
{
|
||||
NSLog(@"[ForgotPasswordInputsView] Invalid email");
|
||||
errorMsg = NSLocalizedStringFromTable(@"auth_invalid_email", @"Vector", nil);
|
||||
}
|
||||
}
|
||||
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
- (void)prepareParameters:(void (^)(NSDictionary *parameters))callback
|
||||
{
|
||||
if (callback)
|
||||
{
|
||||
// Prepare here parameters dict by checking each required fields.
|
||||
parameters = nil;
|
||||
didPrepareParametersCallback = nil;
|
||||
|
||||
// Check the validity of the parameters
|
||||
NSString *errorMsg = [self validateParameters];
|
||||
if (errorMsg)
|
||||
{
|
||||
if (inputsAlert)
|
||||
{
|
||||
[inputsAlert dismiss:NO];
|
||||
}
|
||||
|
||||
inputsAlert = [[MXKAlert alloc] initWithTitle:[NSBundle mxk_localizedStringForKey:@"error"] message:errorMsg style:MXKAlertStyleAlert];
|
||||
inputsAlert.cancelButtonIndex = [inputsAlert addActionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert) {
|
||||
inputsAlert = nil;
|
||||
}];
|
||||
|
||||
[self.delegate authInputsView:self presentMXKAlert:inputsAlert];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Retrieve the REST client from delegate
|
||||
MXRestClient *restClient;
|
||||
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(authInputsViewEmailValidationRestClient:)])
|
||||
{
|
||||
restClient = [self.delegate authInputsViewEmailValidationRestClient:self];
|
||||
}
|
||||
|
||||
if (restClient)
|
||||
{
|
||||
// Launch email validation
|
||||
submittedEmail = [[MXK3PID alloc] initWithMedium:kMX3PIDMediumEmail andAddress:self.emailTextField.text];
|
||||
|
||||
[submittedEmail requestValidationTokenWithMatrixRestClient:restClient
|
||||
nextLink:nil
|
||||
success:^{
|
||||
|
||||
didPrepareParametersCallback = callback;
|
||||
|
||||
NSURL *identServerURL = [NSURL URLWithString:restClient.identityServer];
|
||||
parameters = @{
|
||||
@"auth": @{@"threepid_creds": @{@"client_secret": submittedEmail.clientSecret, @"id_server": identServerURL.host, @"sid": submittedEmail.sid}, @"type": kMXLoginFlowTypeEmailIdentity, @"new_password": self.passWordTextField.text} };
|
||||
|
||||
[self hideInputsContainer];
|
||||
|
||||
self.messageLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"auth_reset_password_email_validation_message", @"Vector", nil), self.emailTextField.text];
|
||||
|
||||
self.messageLabel.hidden = NO;
|
||||
|
||||
[self.nextStepButton addTarget:self action:@selector(didCheckEmail:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
self.nextStepButton.hidden = NO;
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[ForgotPasswordInputsView] Failed to request email token: %@", error);
|
||||
|
||||
// Ignore connection cancellation error
|
||||
if (([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
callback(nil);
|
||||
|
||||
}];
|
||||
|
||||
// Async response
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"[ForgotPasswordInputsView] Operation failed during the email identity stage");
|
||||
}
|
||||
}
|
||||
|
||||
callback(parameters);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)areAllRequiredFieldsSet
|
||||
{
|
||||
// BOOL ret = [super areAllRequiredFieldsSet];
|
||||
//
|
||||
// // Check required fields
|
||||
// ret = (ret && self.passWordTextField.text.length && self.emailTextField.text.length && self.repeatPasswordTextField.text.length));
|
||||
//
|
||||
// return ret;
|
||||
|
||||
// Keep enable the submit button.
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)dismissKeyboard
|
||||
{
|
||||
[self.passWordTextField resignFirstResponder];
|
||||
[self.emailTextField resignFirstResponder];
|
||||
[self.repeatPasswordTextField resignFirstResponder];
|
||||
|
||||
[super dismissKeyboard];
|
||||
}
|
||||
|
||||
- (NSString*)password
|
||||
{
|
||||
return self.passWordTextField.text;
|
||||
}
|
||||
|
||||
- (void)nextStep
|
||||
{
|
||||
// Here the password has been reseted with success
|
||||
didPrepareParametersCallback = nil;
|
||||
parameters = nil;
|
||||
|
||||
[self hideInputsContainer];
|
||||
|
||||
self.messageLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"auth_reset_password_success_message", @"Vector", nil), self.emailTextField.text];
|
||||
|
||||
self.messageLabel.hidden = NO;
|
||||
}
|
||||
|
||||
#pragma mark - Internals
|
||||
|
||||
- (void)reset
|
||||
{
|
||||
// Cancel email validation if any
|
||||
if (submittedEmail)
|
||||
{
|
||||
[submittedEmail cancelCurrentRequest];
|
||||
submittedEmail = nil;
|
||||
}
|
||||
|
||||
parameters = nil;
|
||||
didPrepareParametersCallback = nil;
|
||||
|
||||
// Reset UI by hidding all items
|
||||
[self hideInputsContainer];
|
||||
|
||||
self.messageLabel.text = NSLocalizedStringFromTable(@"auth_reset_password_message", @"Vector", nil);
|
||||
self.messageLabel.hidden = NO;
|
||||
|
||||
self.emailContainer.hidden = NO;
|
||||
self.passwordContainer.hidden = NO;
|
||||
self.repeatPasswordContainer.hidden = NO;
|
||||
|
||||
[self layoutIfNeeded];
|
||||
|
||||
CGRect frame = self.repeatPasswordContainer.frame;
|
||||
self.viewHeightConstraint.constant = frame.origin.y + frame.size.height;
|
||||
}
|
||||
|
||||
#pragma mark - actions
|
||||
|
||||
- (void)didCheckEmail:(id)sender
|
||||
{
|
||||
if (sender == self.nextStepButton)
|
||||
{
|
||||
if (didPrepareParametersCallback)
|
||||
{
|
||||
didPrepareParametersCallback(parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UITextField delegate
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField*)textField
|
||||
{
|
||||
if (textField.returnKeyType == UIReturnKeyDone)
|
||||
{
|
||||
// "Done" key has been pressed
|
||||
[textField resignFirstResponder];
|
||||
|
||||
// Launch authentication now
|
||||
[self.delegate authInputsViewDidPressDoneKey:self];
|
||||
}
|
||||
else
|
||||
{
|
||||
//"Next" key has been pressed
|
||||
if (textField == self.emailTextField)
|
||||
{
|
||||
[self.passWordTextField becomeFirstResponder];
|
||||
}
|
||||
else if (textField == self.passWordTextField)
|
||||
{
|
||||
[self.repeatPasswordTextField becomeFirstResponder];
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)hideInputsContainer
|
||||
{
|
||||
// Hide all inputs container
|
||||
self.passwordContainer.hidden = YES;
|
||||
self.emailContainer.hidden = YES;
|
||||
self.repeatPasswordContainer.hidden = YES;
|
||||
|
||||
// Hide other items
|
||||
self.messageLabel.hidden = YES;
|
||||
self.nextStepButton.hidden = YES;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10116" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="x74-04-ezp" customClass="ForgotPasswordInputsView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="184"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="68j-f9-JG4">
|
||||
<rect key="frame" x="8" y="8" width="38" height="18"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bXz-VI-5FS">
|
||||
<rect key="frame" x="0.0" y="34" width="600" height="50"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Email address" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="odF-W1-Vdr">
|
||||
<rect key="frame" x="18" y="18" width="564" height="21"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="21" id="Kvu-hz-22A"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="emailAddress" returnKeyType="next"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="x74-04-ezp" id="ViI-x8-eWu"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="YQ4-Kj-XPh">
|
||||
<rect key="frame" x="10" y="49" width="580" height="1"/>
|
||||
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="ZLw-Hh-BkN"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="YQ4-Kj-XPh" firstAttribute="leading" secondItem="bXz-VI-5FS" secondAttribute="leading" constant="10" id="0Lj-tL-8mV"/>
|
||||
<constraint firstAttribute="trailing" secondItem="YQ4-Kj-XPh" secondAttribute="trailing" constant="10" id="JTP-Rx-FsE"/>
|
||||
<constraint firstItem="odF-W1-Vdr" firstAttribute="leading" secondItem="bXz-VI-5FS" secondAttribute="leading" constant="18" id="LlG-Lg-woQ"/>
|
||||
<constraint firstAttribute="trailing" secondItem="odF-W1-Vdr" secondAttribute="trailing" constant="18" id="VLl-bq-aHE"/>
|
||||
<constraint firstItem="odF-W1-Vdr" firstAttribute="top" secondItem="bXz-VI-5FS" secondAttribute="top" constant="18" id="fso-d2-GyZ"/>
|
||||
<constraint firstAttribute="bottom" secondItem="YQ4-Kj-XPh" secondAttribute="bottom" id="tOd-El-ztB"/>
|
||||
<constraint firstAttribute="height" constant="50" id="toP-dC-Aiz"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="UfH-jv-6w4">
|
||||
<rect key="frame" x="0.0" y="84" width="600" height="50"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Password" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="6rs-rR-DkS">
|
||||
<rect key="frame" x="18" y="18" width="564" height="21"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="21" id="iai-tB-l7T"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="next" secureTextEntry="YES"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="x74-04-ezp" id="GjY-xZ-s5J"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vUH-bJ-5gJ">
|
||||
<rect key="frame" x="10" y="49" width="580" height="1"/>
|
||||
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="31n-kG-Ka9"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="6rs-rR-DkS" firstAttribute="leading" secondItem="UfH-jv-6w4" secondAttribute="leading" constant="18" id="3x6-dS-z8n"/>
|
||||
<constraint firstItem="6rs-rR-DkS" firstAttribute="top" secondItem="UfH-jv-6w4" secondAttribute="top" constant="18" id="On3-vo-idU"/>
|
||||
<constraint firstAttribute="height" constant="50" id="Tfr-nK-79j"/>
|
||||
<constraint firstAttribute="trailing" secondItem="vUH-bJ-5gJ" secondAttribute="trailing" constant="10" id="Unk-xg-I4s"/>
|
||||
<constraint firstItem="vUH-bJ-5gJ" firstAttribute="leading" secondItem="UfH-jv-6w4" secondAttribute="leading" constant="10" id="kDs-d1-r3h"/>
|
||||
<constraint firstAttribute="trailing" secondItem="6rs-rR-DkS" secondAttribute="trailing" constant="18" id="p8i-KE-71B"/>
|
||||
<constraint firstAttribute="bottom" secondItem="vUH-bJ-5gJ" secondAttribute="bottom" id="zwd-xh-eDk"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rb1-L5-udI">
|
||||
<rect key="frame" x="0.0" y="134" width="600" height="50"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Repeat password" adjustsFontSizeToFit="NO" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="BQM-LP-8Eq">
|
||||
<rect key="frame" x="18" y="18" width="564" height="21"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="21" id="87Q-0f-X45"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done" secureTextEntry="YES"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="x74-04-ezp" id="28g-3U-BBM"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ddx-5u-PbG">
|
||||
<rect key="frame" x="10" y="49" width="580" height="1"/>
|
||||
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="XYi-5d-bZs"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="ddx-5u-PbG" secondAttribute="trailing" constant="10" id="1BD-IR-hbK"/>
|
||||
<constraint firstAttribute="bottom" secondItem="ddx-5u-PbG" secondAttribute="bottom" id="20V-Dg-8wr"/>
|
||||
<constraint firstAttribute="height" constant="50" id="3q4-4P-iJB"/>
|
||||
<constraint firstItem="ddx-5u-PbG" firstAttribute="leading" secondItem="rb1-L5-udI" secondAttribute="leading" constant="10" id="5hT-vS-fCs"/>
|
||||
<constraint firstItem="BQM-LP-8Eq" firstAttribute="leading" secondItem="rb1-L5-udI" secondAttribute="leading" constant="18" id="Q4d-UG-Pdq"/>
|
||||
<constraint firstAttribute="trailing" secondItem="BQM-LP-8Eq" secondAttribute="trailing" constant="18" id="szW-TY-aUw"/>
|
||||
<constraint firstItem="BQM-LP-8Eq" firstAttribute="top" secondItem="rb1-L5-udI" secondAttribute="top" constant="18" id="yhI-hm-zp5"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="PFH-GE-fzb">
|
||||
<rect key="frame" x="233" y="42" width="135" height="30"/>
|
||||
<color key="backgroundColor" red="0.028153735480000001" green="0.82494870580000002" blue="0.051896891280000003" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="Wkm-WC-hfn"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/>
|
||||
<inset key="contentEdgeInsets" minX="30" minY="0.0" maxX="30" maxY="0.0"/>
|
||||
<state key="normal" title="Next Step">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="UfH-jv-6w4" firstAttribute="leading" secondItem="x74-04-ezp" secondAttribute="leading" id="7Bk-GF-MZ0"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="68j-f9-JG4" secondAttribute="trailing" constant="8" id="8Aa-YT-MP5"/>
|
||||
<constraint firstAttribute="trailing" secondItem="UfH-jv-6w4" secondAttribute="trailing" id="8dz-wY-Kxx"/>
|
||||
<constraint firstItem="68j-f9-JG4" firstAttribute="top" secondItem="x74-04-ezp" secondAttribute="top" constant="8" id="BK1-XE-vz5"/>
|
||||
<constraint firstItem="bXz-VI-5FS" firstAttribute="leading" secondItem="x74-04-ezp" secondAttribute="leading" id="Frq-sH-HZT"/>
|
||||
<constraint firstItem="PFH-GE-fzb" firstAttribute="top" secondItem="68j-f9-JG4" secondAttribute="bottom" constant="16" id="IoK-N3-1Kz"/>
|
||||
<constraint firstAttribute="trailing" secondItem="bXz-VI-5FS" secondAttribute="trailing" id="NiV-pJ-PfV"/>
|
||||
<constraint firstItem="PFH-GE-fzb" firstAttribute="centerX" secondItem="x74-04-ezp" secondAttribute="centerX" id="VGS-lT-B1U"/>
|
||||
<constraint firstItem="rb1-L5-udI" firstAttribute="leading" secondItem="x74-04-ezp" secondAttribute="leading" id="XAJ-ST-sWV"/>
|
||||
<constraint firstItem="bXz-VI-5FS" firstAttribute="top" secondItem="68j-f9-JG4" secondAttribute="bottom" constant="8" id="YY8-L1-052"/>
|
||||
<constraint firstItem="68j-f9-JG4" firstAttribute="leading" secondItem="x74-04-ezp" secondAttribute="leading" constant="8" id="aYh-VJ-bss"/>
|
||||
<constraint firstAttribute="trailing" secondItem="rb1-L5-udI" secondAttribute="trailing" id="c49-Cf-H9a"/>
|
||||
<constraint firstItem="rb1-L5-udI" firstAttribute="top" secondItem="UfH-jv-6w4" secondAttribute="bottom" id="dM9-w0-Vem"/>
|
||||
<constraint firstItem="UfH-jv-6w4" firstAttribute="top" secondItem="bXz-VI-5FS" secondAttribute="bottom" id="icZ-sJ-KVQ"/>
|
||||
<constraint firstAttribute="height" constant="184" id="qBF-0J-3VM"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="emailContainer" destination="bXz-VI-5FS" id="4J7-D0-Dmf"/>
|
||||
<outlet property="emailSeparator" destination="YQ4-Kj-XPh" id="iub-NG-iJR"/>
|
||||
<outlet property="emailTextField" destination="odF-W1-Vdr" id="DOS-H7-MZy"/>
|
||||
<outlet property="messageLabel" destination="68j-f9-JG4" id="LfM-qs-gEM"/>
|
||||
<outlet property="nextStepButton" destination="PFH-GE-fzb" id="am6-tx-QPB"/>
|
||||
<outlet property="passWordTextField" destination="6rs-rR-DkS" id="VeL-kt-Fpp"/>
|
||||
<outlet property="passwordContainer" destination="UfH-jv-6w4" id="aGz-rZ-j5q"/>
|
||||
<outlet property="passwordSeparator" destination="vUH-bJ-5gJ" id="mub-of-o9s"/>
|
||||
<outlet property="repeatPasswordContainer" destination="rb1-L5-udI" id="NjO-O7-WYX"/>
|
||||
<outlet property="repeatPasswordSeparator" destination="ddx-5u-PbG" id="MtA-Rf-dhU"/>
|
||||
<outlet property="repeatPasswordTextField" destination="BQM-LP-8Eq" id="mgM-dU-mJo"/>
|
||||
<outlet property="viewHeightConstraint" destination="qBF-0J-3VM" id="GLh-gg-bh5"/>
|
||||
</connections>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
Reference in New Issue
Block a user