Authentication: Support forgot password option

This commit is contained in:
giomfo
2016-05-03 15:54:05 +02:00
parent 8c3b8ffec8
commit 4f304ce01d
8 changed files with 732 additions and 82 deletions
+13 -1
View File
@@ -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"/>
+80 -59
View File
@@ -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>