Commit 882289b5 authored by Alexandre Lision's avatar Alexandre Lision

blockchain: register name at account creation

- Add signup with Blockchain checkbox on new Ring Account view
- Add indicator next to Username field to check availability

Tuleap: #1158
Change-Id: I52ba5fa32068c39f9059a6f4307c2ead89d4526a
parent 3652cfb0
......@@ -28,11 +28,15 @@
@property (nonatomic, weak)NSWindowController <RingWizardNewDelegate>* delegate;
@property (nonatomic, weak)NSString* alias;
@property (nonatomic, weak)NSString* registeredName;
@property (nonatomic, weak)NSString* password;
@property (nonatomic, weak)NSString* repeatPassword;
@property (readonly)BOOL isRepeatPasswordValid;
@property (readonly)BOOL isPasswordValid;
@property (assign)BOOL isUserNameAvailable;
@property (readonly)BOOL userNameAvailableORNotBlockchain;
@property (readonly)BOOL withBlockchain;
@property (assign)NSInteger signUpBlockchainState;
- (void)show;
@end
......@@ -53,23 +53,34 @@
__unsafe_unretained IBOutlet NSView* creationView;
__unsafe_unretained IBOutlet NSButton* photoView;
__unsafe_unretained IBOutlet NSTextField* nicknameField;
__unsafe_unretained IBOutlet NSTextField* displayNameField;
__unsafe_unretained IBOutlet NSTextField* registeredNameField;
__unsafe_unretained IBOutlet NSSecureTextField* passwordField;
__unsafe_unretained IBOutlet NSSecureTextField* passwordRepeatField;
__unsafe_unretained IBOutlet NSTextField* passwordLabel;
__unsafe_unretained IBOutlet NSTextField* passwordRepeatLabel;
__unsafe_unretained IBOutlet NSImageView* passwordCheck;
__unsafe_unretained IBOutlet NSImageView* passwordRepeatCheck;
__unsafe_unretained IBOutlet NSButton* createButton;
__unsafe_unretained IBOutlet NSButton* cancelButton;
__unsafe_unretained IBOutlet NSProgressIndicator* progressBar;
__unsafe_unretained IBOutlet NSButton* cbSignupRing;
__unsafe_unretained IBOutlet NSImageView* ivLookupResult;
__unsafe_unretained IBOutlet NSProgressIndicator* indicatorLookupResult;
__unsafe_unretained IBOutlet NSPopover* helpBlockchainContainer;
__unsafe_unretained IBOutlet NSPopover* helpPasswordContainer;
Account* accountToCreate;
NSTimer* errorTimer;
QMetaObject::Connection stateChanged;
QMetaObject::Connection registrationEnded;
QMetaObject::Connection registeredNameFound;
BOOL lookupQueued;
NSString* usernameWaitingForLookupResult;
}
NSInteger const NICKNAME_TAG = 1;
NSInteger const DISPLAY_NAME_TAG = 1;
NSInteger const BLOCKCHAIN_NAME_TAG = 2;
//ERROR CODE for textfields validations
NSInteger const ERROR_PASSWORD_TOO_SHORT = -1;
......@@ -85,12 +96,23 @@ NSInteger const ERROR_REPEAT_MISMATCH = -2;
return NO;
}
- (IBAction)showBlockchainHelp:(id)sender
{
[helpBlockchainContainer showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMaxYEdge];
}
- (IBAction)showPasswordHelp:(id)sender
{
[helpPasswordContainer showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMaxYEdge];
}
- (void)show
{
AppDelegate* appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
[nicknameField setTag:NICKNAME_TAG];
[nicknameField setStringValue:NSFullUserName()];
[self controlTextDidChange:[NSNotification notificationWithName:@"PlaceHolder" object:nicknameField]];
[displayNameField setTag:DISPLAY_NAME_TAG];
[registeredNameField setTag:BLOCKCHAIN_NAME_TAG];
[displayNameField setStringValue: NSFullUserName()];
[self controlTextDidChange:[NSNotification notificationWithName:@"PlaceHolder" object:displayNameField]];
NSData* imgData = [[[ABAddressBook sharedAddressBook] me] imageData];
if (imgData != nil) {
......@@ -129,8 +151,8 @@ NSInteger const ERROR_REPEAT_MISMATCH = -2;
}
- (void)pictureTakerDidEnd:(IKPictureTaker *) picker
returnCode:(NSInteger) code
contextInfo:(void*) contextInfo
returnCode:(NSInteger) code
contextInfo:(void*) contextInfo
{
if (auto outputImage = [picker outputImage]) {
[photoView setImage:outputImage];
......@@ -139,6 +161,7 @@ NSInteger const ERROR_REPEAT_MISMATCH = -2;
}
#pragma mark - Input validation
- (BOOL)isPasswordValid
{
return self.password.length >= 6;
......@@ -155,7 +178,7 @@ NSInteger const ERROR_REPEAT_MISMATCH = -2;
return [self produceError:error
withCode:ERROR_REPEAT_MISMATCH
andMessage:NSLocalizedString(@"Passwords don't match",
@"Indication for user")];
@"Indication for user")];
}
return YES;
}
......@@ -166,7 +189,7 @@ NSInteger const ERROR_REPEAT_MISMATCH = -2;
return [self produceError:error
withCode:ERROR_PASSWORD_TOO_SHORT
andMessage:NSLocalizedString(@"Password is too short",
@"Indication for user")];
@"Indication for user")];
}
return YES;
}
......@@ -198,17 +221,17 @@ NSInteger const ERROR_REPEAT_MISMATCH = -2;
[self display:loadingView];
[progressBar startAnimation:nil];
if ([self.alias isEqualToString:@""]) {
self.alias = NSLocalizedString(@"Unknown", @"Name used when user leave field empty");
NSString* displayName = displayNameField.stringValue;
if ([displayName isEqualToString:@""]) {
displayName = NSLocalizedString(@"Unknown", @"Name used when user leave field empty");
}
accountToCreate = AccountModel::instance().add(QString::fromNSString(self.alias), Account::Protocol::RING);
accountToCreate->setAlias([self.alias UTF8String]);
accountToCreate->setDisplayName([self.alias UTF8String]);
accountToCreate = AccountModel::instance().add(QString::fromNSString(displayName), Account::Protocol::RING);
accountToCreate->setAlias([displayName UTF8String]);
accountToCreate->setDisplayName([displayName UTF8String]);
if (auto profile = ProfileModel::instance().selectedProfile()) {
profile->person()->setFormattedName([self.alias UTF8String]);
profile->person()->setFormattedName([displayName UTF8String]);
QPixmap p;
auto smallImg = [NSImage imageResize:[photoView image] newSize:{100,100}];
if (p.loadFromData(QByteArray::fromNSData([smallImg TIFFRepresentation]))) {
......@@ -259,28 +282,62 @@ NSInteger const ERROR_REPEAT_MISMATCH = -2;
selector:@selector(didCreateFailed) userInfo:nil
repeats:NO];
stateChanged = QObject::connect(&AccountModel::instance(),
&AccountModel::accountStateChanged,
[=](Account *account, const Account::RegistrationState state) {
switch(state){
case Account::RegistrationState::READY:
case Account::RegistrationState::TRYING:
case Account::RegistrationState::UNREGISTERED:{
accountToCreate<< Account::EditAction::RELOAD;
QObject::disconnect(stateChanged);
[self.delegate didCreateAccountWithSuccess:YES];
break;
}
case Account::RegistrationState::ERROR:
QObject::disconnect(stateChanged);
[self.delegate didCreateAccountWithSuccess:NO];
break;
case Account::RegistrationState::INITIALIZING:
case Account::RegistrationState::COUNT__:{
//DO Nothing
break;
}
}
});
&AccountModel::accountStateChanged,
[=](Account *account, const Account::RegistrationState state) {
switch(state){
case Account::RegistrationState::READY:
case Account::RegistrationState::TRYING:
case Account::RegistrationState::UNREGISTERED:{
accountToCreate << Account::EditAction::RELOAD;
QObject::disconnect(stateChanged);
//try to register username
if (self.signUpBlockchainState == NSOnState){
[self startNameRegistration:account];
} else {
[self.delegate didCreateAccountWithSuccess:YES];
}
break;
}
case Account::RegistrationState::ERROR:
QObject::disconnect(stateChanged);
[self.delegate didCreateAccountWithSuccess:NO];
break;
case Account::RegistrationState::INITIALIZING:
case Account::RegistrationState::COUNT__:{
//Do Nothing
break;
}
}
});
}
- (void) startNameRegistration:(Account*) account
{
registrationEnded = QObject::connect(account,
&Account::nameRegistrationEnded,
[=] (NameDirectory::RegisterNameStatus status, const QString& name) {
QObject::disconnect(registrationEnded);
switch(status) {
case NameDirectory::RegisterNameStatus::WRONG_PASSWORD:
case NameDirectory::RegisterNameStatus::ALREADY_TAKEN:
case NameDirectory::RegisterNameStatus::NETWORK_ERROR: {
[self couldNotRegisterUsername];
break;
}
case NameDirectory::RegisterNameStatus::SUCCESS: {
break;
}
}
[self.delegate didCreateAccountWithSuccess:YES];
});
self.isUserNameAvailable = account->registerName(QString::fromNSString(self.password),
QString::fromNSString(self.registeredName));
if (!self.isUserNameAvailable){
NSLog(@"Could not initialize registerName operation");
QObject::disconnect(registrationEnded);
[self.delegate didCreateAccountWithSuccess:YES];
}
}
- (void)didCreateFailed
......@@ -293,6 +350,119 @@ NSInteger const ERROR_REPEAT_MISMATCH = -2;
[self.delegate didCreateAccountWithSuccess:NO];
}
#pragma mark - UserNameRegistration delegate methods
- (IBAction)toggleSignupRing:(id)sender
{
if (self.withBlockchain) {
[self lookupUserName];
}
}
- (void)couldNotRegisterUsername
{
// Do nothing
}
- (BOOL)withBlockchain
{
return self.signUpBlockchainState == NSOnState;
}
- (BOOL)userNameAvailableORNotBlockchain
{
return !self.withBlockchain || (self.registeredName.length > 0 && self.isUserNameAvailable);
}
- (void)showLookUpAvailable:(BOOL)available andText:(NSString *)message
{
[ivLookupResult setImage:[NSImage imageNamed:(available?@"ic_action_accept":@"ic_action_cancel")]] ;
[ivLookupResult setHidden:NO];
[ivLookupResult setToolTip:message];
}
- (void)onUsernameAvailabilityChangedWithNewAvailability:(BOOL)newAvailability
{
self.isUserNameAvailable = newAvailability;
}
- (void)hideLookupSpinner
{
[indicatorLookupResult setHidden:YES];
}
- (void)showLookupSpinner
{
[ivLookupResult setHidden:YES];
[indicatorLookupResult setHidden:NO];
[indicatorLookupResult startAnimation:nil];
}
- (BOOL)lookupUserName
{
[self showLookupSpinner];
QObject::disconnect(registeredNameFound);
registeredNameFound = QObject::connect(
&NameDirectory::instance(),
&NameDirectory::registeredNameFound,
[=] ( const Account* account, NameDirectory::LookupStatus status, const QString& address, const QString& name) {
NSLog(@"Name lookup ended");
lookupQueued = NO;
//If this is the username we are waiting for, we can disconnect.
if (name.compare(QString::fromNSString(usernameWaitingForLookupResult)) == 0) {
QObject::disconnect(registeredNameFound);
} else {
//Keep waiting...
return;
}
//We may now stop the spinner
[self hideLookupSpinner];
BOOL isAvailable = NO;
NSString* message;
switch(status)
{
case NameDirectory::LookupStatus::SUCCESS:
{
message = NSLocalizedString(@"The entered username is not available",
@"Text shown to user when his username is already registered");
isAvailable = NO;
break;
}
case NameDirectory::LookupStatus::NOT_FOUND:
{
message = NSLocalizedString(@"The entered username is available",
@"Text shown to user when his username is available to be registered");
isAvailable = YES;
break;
}
case NameDirectory::LookupStatus::INVALID_NAME:
{
message = NSLocalizedString(@"The entered username is invalid. It must have at leat 3 characters and contains only lowercase alphanumeric characters.",
@"Text shown to user when his username is invalid to be registered");
isAvailable = NO;
break;
}
case NameDirectory::LookupStatus::ERROR:
default:
{
message = NSLocalizedString(@"Failed to perform lookup",
@"Text shown to user when an error occur at registration");
isAvailable = NO;
break;
}
}
[self showLookUpAvailable:isAvailable andText: message];
[self onUsernameAvailabilityChangedWithNewAvailability:isAvailable];
});
//Start the lookup in a second so that the UI dosen't seem to freeze
BOOL result = NameDirectory::instance().lookupName(nullptr, QString(), QString::fromNSString(usernameWaitingForLookupResult));
}
#pragma mark - NSOpenSavePanelDelegate delegate methods
- (BOOL)panel:(id)sender validateURL:(NSURL *)url error:(NSError **)outError
......@@ -300,15 +470,41 @@ NSInteger const ERROR_REPEAT_MISMATCH = -2;
return YES;
}
-(void)controlTextDidChange:(NSNotification *)notif
- (void)controlTextDidChange:(NSNotification *)notif
{
NSTextField* textField = [notif object];
// else it is NICKNAME_TAG field
if (textField.tag != BLOCKCHAIN_NAME_TAG) {
return;
}
NSString* alias = textField.stringValue;
if ([alias isEqualToString:@""]) {
alias = NSLocalizedString(@"Unknown", @"Name used when user leave field empty");
[self showLookupSpinner];
[self onUsernameAvailabilityChangedWithNewAvailability:NO];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:@selector(lookUp:) withObject:alias afterDelay:0.5];
}
- (void) lookUp:(NSString*) name
{
if (self.withBlockchain && !lookupQueued) {
usernameWaitingForLookupResult = name;
lookupQueued = YES;
[self lookupUserName];
}
self.alias = alias;
}
+ (NSSet *)keyPathsForValuesAffectingUserNameAvailableORNotBlockchain
{
return [NSSet setWithObjects: NSStringFromSelector(@selector(signUpBlockchainState)),
NSStringFromSelector(@selector(isUserNameAvailable)),
nil];
}
+ (NSSet *)keyPathsForValuesAffectingWithBlockchain
{
return [NSSet setWithObjects: NSStringFromSelector(@selector(signUpBlockchainState)),
nil];
}
+ (NSSet *)keyPathsForValuesAffectingIsPasswordValid
......@@ -320,5 +516,4 @@ NSInteger const ERROR_REPEAT_MISMATCH = -2;
{
return [NSSet setWithObjects:@"password", @"repeatPassword", nil];
}
@end
/* Class = "NSTextFieldCell"; title = "By checking this, you will register a unique username on Ring network. Other people can use it to call you."; ObjectID = "0qE-Sz-hdc"; */
"0qE-Sz-hdc.title" = "By checking this, you will register a unique username on Ring network. Other people can use it to call you.";
/* Class = "NSTextFieldCell"; placeholderString = "Mandatory*"; ObjectID = "1TO-Ov-S8j"; */
"1TO-Ov-S8j.placeholderString" = "Mandatory*";
/* Class = "NSButtonCell"; title = "Register a username on the blockchain"; ObjectID = "AG5-GR-bii"; */
"AG5-GR-bii.title" = "Register a username on the blockchain";
/* Class = "NSTextFieldCell"; title = "Choose your password"; ObjectID = "B6x-jj-48R"; */
"B6x-jj-48R.title" = "Choose your password";
......@@ -11,17 +17,20 @@
/* Class = "NSTextFieldCell"; title = "Just a moment..."; ObjectID = "YIN-YL-JBs"; */
"YIN-YL-JBs.title" = "Just a moment...";
/* Class = "NSTextFieldCell"; placeholderString = "'Unknown' if empty..."; ObjectID = "bdC-Uc-Qhs"; */
"bdC-Uc-Qhs.placeholderString" = "'Unknown' if empty...";
/* Class = "NSTextFieldCell"; title = "Repeat your password"; ObjectID = "fEY-eO-HwI"; */
"fEY-eO-HwI.title" = "Repeat your password";
/* Class = "NSTextFieldCell"; placeholderString = "'Unknown' if empty..."; ObjectID = "gEy-ak-Cgq"; */
"gEy-ak-Cgq.placeholderString" = "'Unknown' if empty...";
/* Class = "NSTextFieldCell"; title = "Choose your username"; ObjectID = "tzf-88-Yn5"; */
"tzf-88-Yn5.title" = "Choose your username";
/* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "v7f-52-GJA"; */
"v7f-52-GJA.title" = "Cancel";
/* Class = "NSButtonCell"; title = "Next"; ObjectID = "zfA-n9-V4X"; */
"zfA-n9-V4X.title" = "Next";
/* Class = "NSTextFieldCell"; title = "Your password must be at least 6 characters. It will be used to encrypt your account data and to link new devices to your account."; ObjectID = "zUH-kc-cik"; */
"zUH-kc-cik.title" = "Your password must be at least 6 characters. It will be used to encrypt your account data and to link new devices to your account.";
/* Class = "NSButtonCell"; title = "Create"; ObjectID = "zfA-n9-V4X"; */
"zfA-n9-V4X.title" = "Create";
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment