Finish TouchID support.
This commit is contained in:
parent
a42edec918
commit
61b4ea4525
2
External/Pearl
vendored
2
External/Pearl
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 2642d720cc874635c336406a01f1e8d2ee5bcb09
|
Subproject commit 1c02f68934e0a32adf8f4901fe9e77cff43d93a3
|
@ -18,64 +18,57 @@
|
|||||||
|
|
||||||
@implementation MPAppDelegate_Shared(Key)
|
@implementation MPAppDelegate_Shared(Key)
|
||||||
|
|
||||||
static NSDictionary *keyQuery(MPUserEntity *user, BOOL newItem) {
|
static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigin *keyOrigin) {
|
||||||
|
|
||||||
|
if (user.touchID && kSecUseOperationPrompt) {
|
||||||
|
if (keyOrigin)
|
||||||
|
*keyOrigin = MPKeyOriginKeyChainBiometric;
|
||||||
|
|
||||||
if (user.touchID && &SecAccessControlCreateWithFlags) {
|
|
||||||
CFErrorRef acError = NULL;
|
CFErrorRef acError = NULL;
|
||||||
SecAccessControlRef accessControl = SecAccessControlCreateWithFlags( nil,
|
SecAccessControlRef accessControl = SecAccessControlCreateWithFlags( kCFAllocatorDefault,
|
||||||
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlTouchIDCurrentSet, &acError );
|
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlTouchIDCurrentSet, &acError );
|
||||||
if (!accessControl || acError)
|
if (!accessControl || acError)
|
||||||
err( @"Could not use TouchID on this device: %@", acError );
|
err( @"Could not use TouchID on this device: %@", acError );
|
||||||
|
|
||||||
else {
|
|
||||||
LAContext *context = [LAContext new];
|
|
||||||
dispatch_group_t waitGroup = dispatch_group_create();
|
|
||||||
dispatch_group_enter( waitGroup );
|
|
||||||
__block BOOL contextSuccess = NO;
|
|
||||||
__block NSError *contextError = nil;
|
|
||||||
[context evaluateAccessControl:accessControl
|
|
||||||
operation:newItem? LAAccessControlOperationCreateItem: LAAccessControlOperationUseItem
|
|
||||||
localizedReason:@"Moo"
|
|
||||||
reply:^(BOOL success, NSError *error) {
|
|
||||||
contextSuccess = success;
|
|
||||||
contextError = error;
|
|
||||||
dispatch_group_leave( waitGroup );
|
|
||||||
}];
|
|
||||||
dispatch_group_wait( waitGroup, DISPATCH_TIME_FOREVER );
|
|
||||||
|
|
||||||
if (!contextSuccess || contextError)
|
|
||||||
err( @"TouchID authentication failed: %@", contextError );
|
|
||||||
|
|
||||||
else
|
else
|
||||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
||||||
attributes:@{
|
attributes:@{
|
||||||
(__bridge id)kSecAttrService : @"Saved Master Password",
|
(__bridge id)kSecAttrService : @"Saved Master Password",
|
||||||
(__bridge id)kSecAttrAccount : user.name?: @"",
|
(__bridge id)kSecAttrAccount : user.name?: @"",
|
||||||
(__bridge id)kSecUseAuthenticationUI : (__bridge id)kSecUseAuthenticationUIAllow,
|
|
||||||
(__bridge id)kSecAttrAccessControl : (__bridge id)accessControl,
|
(__bridge id)kSecAttrAccessControl : (__bridge id)accessControl,
|
||||||
|
(__bridge id)kSecUseAuthenticationUI : (__bridge id)kSecUseAuthenticationUIAllow,
|
||||||
|
(__bridge id)kSecUseOperationPrompt :
|
||||||
|
strf( @"Access %@'s master password.", user.name ),
|
||||||
}
|
}
|
||||||
matches:nil];
|
matches:nil];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (keyOrigin)
|
||||||
|
*keyOrigin = MPKeyOriginKeyChain;
|
||||||
|
|
||||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
||||||
attributes:@{
|
attributes:@{
|
||||||
(__bridge id)kSecAttrService : @"Saved Master Password",
|
(__bridge id)kSecAttrService : @"Saved Master Password",
|
||||||
(__bridge id)kSecAttrAccount : user.name?: @"",
|
(__bridge id)kSecAttrAccount : user.name?: @"",
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
(__bridge id)kSecAttrAccessible : (__bridge id)(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly?: kSecAttrAccessibleWhenUnlockedThisDeviceOnly),
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
matches:nil];
|
matches:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPKey *)loadSavedKeyFor:(MPUserEntity *)user {
|
- (MPKey *)loadSavedKeyFor:(MPUserEntity *)user {
|
||||||
|
|
||||||
NSData *keyData = [PearlKeyChain dataOfItemForQuery:keyQuery( user, NO )];
|
MPKeyOrigin keyOrigin;
|
||||||
|
NSDictionary *keyQuery = createKeyQuery( user, NO, &keyOrigin );
|
||||||
|
NSData *keyData = [PearlKeyChain dataOfItemForQuery:keyQuery];
|
||||||
if (!keyData) {
|
if (!keyData) {
|
||||||
inf( @"No key found in keychain for user: %@", user.userID );
|
inf( @"No key found in keychain for user: %@", user.userID );
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
inf( @"Found key in keychain for user: %@", user.userID );
|
inf( @"Found key in keychain for user: %@", user.userID );
|
||||||
return [[MPKey alloc] initForFullName:user.name withKeyData:keyData forAlgorithm:user.algorithm];
|
return [[MPKey alloc] initForFullName:user.name withKeyData:keyData forAlgorithm:user.algorithm keyOrigin:keyOrigin];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)storeSavedKeyFor:(MPUserEntity *)user {
|
- (void)storeSavedKeyFor:(MPUserEntity *)user {
|
||||||
@ -83,19 +76,16 @@ static NSDictionary *keyQuery(MPUserEntity *user, BOOL newItem) {
|
|||||||
if (user.saveKey) {
|
if (user.saveKey) {
|
||||||
inf( @"Saving key in keychain for user: %@", user.userID );
|
inf( @"Saving key in keychain for user: %@", user.userID );
|
||||||
|
|
||||||
[PearlKeyChain addOrUpdateItemForQuery:keyQuery( user, YES )
|
[PearlKeyChain addOrUpdateItemForQuery:createKeyQuery( user, YES, nil )
|
||||||
withAttributes:@{
|
withAttributes:@{
|
||||||
(__bridge id)kSecValueData : [self.key keyDataForAlgorithm:user.algorithm],
|
(__bridge id)kSecValueData : [self.key keyDataForAlgorithm:user.algorithm],
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
(__bridge id)kSecAttrAccessible : (__bridge id)(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly?: kSecAttrAccessibleWhenUnlockedThisDeviceOnly),
|
|
||||||
#endif
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)forgetSavedKeyFor:(MPUserEntity *)user {
|
- (void)forgetSavedKeyFor:(MPUserEntity *)user {
|
||||||
|
|
||||||
OSStatus result = [PearlKeyChain deleteItemForQuery:keyQuery( user, NO )];
|
OSStatus result = [PearlKeyChain deleteItemForQuery:createKeyQuery( user, NO, nil )];
|
||||||
if (result == noErr) {
|
if (result == noErr) {
|
||||||
inf( @"Removed key from keychain for user: %@", user.userID );
|
inf( @"Removed key from keychain for user: %@", user.userID );
|
||||||
|
|
||||||
@ -114,8 +104,7 @@ static NSDictionary *keyQuery(MPUserEntity *user, BOOL newItem) {
|
|||||||
|
|
||||||
- (BOOL)signInAsUser:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc usingMasterPassword:(NSString *)password {
|
- (BOOL)signInAsUser:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc usingMasterPassword:(NSString *)password {
|
||||||
|
|
||||||
if (password)
|
NSAssert( ![NSThread isMainThread], @"Authentication should not happen on the main thread." );
|
||||||
NSAssert( ![NSThread isMainThread], @"Computing key must not happen from the main thread." );
|
|
||||||
if (!user)
|
if (!user)
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
@ -179,7 +168,17 @@ static NSDictionary *keyQuery(MPUserEntity *user, BOOL newItem) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.key = tryKey;
|
self.key = tryKey;
|
||||||
|
|
||||||
|
// Update the key chain if necessary.
|
||||||
|
switch (self.key.origin) {
|
||||||
|
case MPKeyOriginMasterPassword:
|
||||||
[self storeSavedKeyFor:user];
|
[self storeSavedKeyFor:user];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MPKeyOriginKeyChain:
|
||||||
|
case MPKeyOriginKeyChainBiometric:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@try {
|
@try {
|
||||||
|
@ -20,12 +20,20 @@
|
|||||||
|
|
||||||
@protocol MPAlgorithm;
|
@protocol MPAlgorithm;
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSUInteger, MPKeyOrigin) {
|
||||||
|
MPKeyOriginMasterPassword,
|
||||||
|
MPKeyOriginKeyChain,
|
||||||
|
MPKeyOriginKeyChainBiometric,
|
||||||
|
};
|
||||||
|
|
||||||
@interface MPKey : NSObject
|
@interface MPKey : NSObject
|
||||||
|
|
||||||
@property(nonatomic, readonly) NSString *fullName;
|
@property(nonatomic, readonly) NSString *fullName;
|
||||||
|
@property(nonatomic, readonly) MPKeyOrigin origin;
|
||||||
|
|
||||||
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
|
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
|
||||||
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData forAlgorithm:(id<MPAlgorithm>)algorithm;
|
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData
|
||||||
|
forAlgorithm:(id<MPAlgorithm>)algorithm keyOrigin:(MPKeyOrigin)origin;
|
||||||
|
|
||||||
- (NSData *)keyIDForAlgorithm:(id<MPAlgorithm>)algorithm;
|
- (NSData *)keyIDForAlgorithm:(id<MPAlgorithm>)algorithm;
|
||||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm;
|
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm;
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
@interface MPKey()
|
@interface MPKey()
|
||||||
|
|
||||||
@property(nonatomic) NSString *fullName;
|
@property(nonatomic) NSString *fullName;
|
||||||
|
@property(nonatomic) MPKeyOrigin origin;
|
||||||
@property(nonatomic) NSString *masterPassword;
|
@property(nonatomic) NSString *masterPassword;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@ -35,16 +36,19 @@
|
|||||||
|
|
||||||
_keyCache = [NSCache new];
|
_keyCache = [NSCache new];
|
||||||
self.fullName = fullName;
|
self.fullName = fullName;
|
||||||
|
self.origin = MPKeyOriginMasterPassword;
|
||||||
self.masterPassword = masterPassword;
|
self.masterPassword = masterPassword;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData forAlgorithm:(id<MPAlgorithm>)algorithm {
|
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData
|
||||||
|
forAlgorithm:(id<MPAlgorithm>)algorithm keyOrigin:(MPKeyOrigin)origin {
|
||||||
|
|
||||||
if (!(self = [self initForFullName:fullName withMasterPassword:nil]))
|
if (!(self = [self initForFullName:fullName withMasterPassword:nil]))
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
|
self.origin = origin;
|
||||||
[_keyCache setObject:keyData forKey:algorithm];
|
[_keyCache setObject:keyData forKey:algorithm];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
@ -33,7 +33,7 @@ typedef NS_ENUM(NSUInteger, MPAvatarMode) {
|
|||||||
|
|
||||||
@interface MPAvatarCell : UICollectionViewCell
|
@interface MPAvatarCell : UICollectionViewCell
|
||||||
@property (copy, nonatomic) NSString *name;
|
@property (copy, nonatomic) NSString *name;
|
||||||
@property (assign, nonatomic) long avatar;
|
@property (assign, nonatomic) NSUInteger avatar;
|
||||||
@property (assign, nonatomic) MPAvatarMode mode;
|
@property (assign, nonatomic) MPAvatarMode mode;
|
||||||
@property (assign, nonatomic) CGFloat visibility;
|
@property (assign, nonatomic) CGFloat visibility;
|
||||||
@property (assign, nonatomic) BOOL spinnerActive;
|
@property (assign, nonatomic) BOOL spinnerActive;
|
||||||
|
@ -111,7 +111,7 @@ const long MPAvatarAdd = 10000;
|
|||||||
|
|
||||||
#pragma mark - Properties
|
#pragma mark - Properties
|
||||||
|
|
||||||
- (void)setAvatar:(long)avatar {
|
- (void)setAvatar:(NSUInteger)avatar {
|
||||||
|
|
||||||
_avatar = avatar == MPAvatarAdd? MPAvatarAdd: (avatar + MPAvatarCount) % MPAvatarCount;
|
_avatar = avatar == MPAvatarAdd? MPAvatarAdd: (avatar + MPAvatarCount) % MPAvatarCount;
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ const long MPAvatarAdd = 10000;
|
|||||||
_newUser = YES;
|
_newUser = YES;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
self.avatarImageView.image = [UIImage imageNamed:strf( @"avatar-%ld", _avatar )];
|
self.avatarImageView.image = [UIImage imageNamed:strf( @"avatar-%lu", (unsigned long)_avatar )];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)name {
|
- (NSString *)name {
|
||||||
|
@ -35,14 +35,20 @@
|
|||||||
if (![[NSUserDefaults standardUserDefaults] synchronize])
|
if (![[NSUserDefaults standardUserDefaults] synchronize])
|
||||||
wrn( @"Couldn't synchronize after preferences appearance." );
|
wrn( @"Couldn't synchronize after preferences appearance." );
|
||||||
|
|
||||||
|
self.tableView.contentInset = UIEdgeInsetsMake( 64, 0, 49, 0 );
|
||||||
|
|
||||||
|
[self reload];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)reload {
|
||||||
|
|
||||||
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForMainThread];
|
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForMainThread];
|
||||||
self.generatedTypeControl.selectedSegmentIndex = [self generatedSegmentIndexForType:activeUser.defaultType];
|
self.generatedTypeControl.selectedSegmentIndex = [self generatedSegmentIndexForType:activeUser.defaultType];
|
||||||
self.storedTypeControl.selectedSegmentIndex = [self storedSegmentIndexForType:activeUser.defaultType];
|
self.storedTypeControl.selectedSegmentIndex = [self storedSegmentIndexForType:activeUser.defaultType];
|
||||||
self.avatarImage.image = [UIImage imageNamed:strf( @"avatar-%ld", (long)activeUser.avatar )];
|
self.avatarImage.image = [UIImage imageNamed:strf( @"avatar-%lu", (unsigned long)activeUser.avatar )];
|
||||||
self.savePasswordSwitch.on = activeUser.saveKey;
|
self.savePasswordSwitch.on = activeUser.saveKey;
|
||||||
self.touchIDSwitch.on = activeUser.touchID;
|
self.touchIDSwitch.on = activeUser.touchID;
|
||||||
|
self.touchIDSwitch.enabled = self.savePasswordSwitch.on;
|
||||||
self.tableView.contentInset = UIEdgeInsetsMake( 64, 0, 49, 0 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - UITableViewDelegate
|
#pragma mark - UITableViewDelegate
|
||||||
@ -97,6 +103,10 @@
|
|||||||
else
|
else
|
||||||
[[MPiOSAppDelegate get] forgetSavedKeyFor:activeUser];
|
[[MPiOSAppDelegate get] forgetSavedKeyFor:activeUser];
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
|
|
||||||
|
PearlMainQueue(^{
|
||||||
|
[self reload];
|
||||||
|
});
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if (sender == self.touchIDSwitch)
|
if (sender == self.touchIDSwitch)
|
||||||
@ -107,6 +117,10 @@
|
|||||||
else
|
else
|
||||||
[[MPiOSAppDelegate get] forgetSavedKeyFor:activeUser];
|
[[MPiOSAppDelegate get] forgetSavedKeyFor:activeUser];
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
|
|
||||||
|
PearlMainQueue( ^{
|
||||||
|
[self reload];
|
||||||
|
} );
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if (sender == self.generatedTypeControl || sender == self.storedTypeControl) {
|
if (sender == self.generatedTypeControl || sender == self.storedTypeControl) {
|
||||||
@ -115,13 +129,13 @@
|
|||||||
else if (sender == self.storedTypeControl)
|
else if (sender == self.storedTypeControl)
|
||||||
self.generatedTypeControl.selectedSegmentIndex = -1;
|
self.generatedTypeControl.selectedSegmentIndex = -1;
|
||||||
|
|
||||||
|
MPSiteType defaultType = [self typeForSelectedSegment];
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPSiteType defaultType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType = [self typeForSelectedSegment];
|
[[MPiOSAppDelegate get] activeUserInContext:context].defaultType = defaultType;
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
|
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
self.generatedTypeControl.selectedSegmentIndex = [self generatedSegmentIndexForType:defaultType];
|
[self reload];
|
||||||
self.storedTypeControl.selectedSegmentIndex = [self storedSegmentIndexForType:defaultType];
|
|
||||||
} );
|
} );
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@ -134,9 +148,9 @@
|
|||||||
activeUser.avatar = (activeUser.avatar - 1 + MPAvatarCount) % MPAvatarCount;
|
activeUser.avatar = (activeUser.avatar - 1 + MPAvatarCount) % MPAvatarCount;
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
|
|
||||||
long avatar = activeUser.avatar;
|
NSUInteger avatar = activeUser.avatar;
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
self.avatarImage.image = [UIImage imageNamed:strf( @"avatar-%ld", avatar )];
|
self.avatarImage.image = [UIImage imageNamed:strf( @"avatar-%lu", (unsigned long)avatar )];
|
||||||
} );
|
} );
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@ -148,9 +162,9 @@
|
|||||||
activeUser.avatar = (activeUser.avatar + 1 + MPAvatarCount) % MPAvatarCount;
|
activeUser.avatar = (activeUser.avatar + 1 + MPAvatarCount) % MPAvatarCount;
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
|
|
||||||
long avatar = activeUser.avatar;
|
NSUInteger avatar = activeUser.avatar;
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
self.avatarImage.image = [UIImage imageNamed:strf( @"avatar-%ld", avatar )];
|
self.avatarImage.image = [UIImage imageNamed:strf( @"avatar-%lu", (unsigned long)avatar )];
|
||||||
} );
|
} );
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
@ -147,12 +147,13 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
|||||||
case MPActiveUserStateLogin: {
|
case MPActiveUserStateLogin: {
|
||||||
self.entryField.enabled = NO;
|
self.entryField.enabled = NO;
|
||||||
[self selectedAvatar].spinnerActive = YES;
|
[self selectedAvatar].spinnerActive = YES;
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
NSString *masterPassword = self.entryField.text;
|
||||||
|
if (![MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
BOOL signedIn = NO, isNew = NO;
|
BOOL signedIn = NO, isNew = NO;
|
||||||
MPUserEntity *user = [self selectedUserInContext:context isNew:&isNew];
|
MPUserEntity *user = [self selectedUserInContext:context isNew:&isNew];
|
||||||
if (!isNew && user)
|
if (!isNew && user)
|
||||||
signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context
|
signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context
|
||||||
usingMasterPassword:self.entryField.text];
|
usingMasterPassword:masterPassword];
|
||||||
|
|
||||||
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
||||||
self.entryField.text = @"";
|
self.entryField.text = @"";
|
||||||
@ -165,7 +166,10 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}];
|
}]) {
|
||||||
|
self.entryField.enabled = YES;
|
||||||
|
[self selectedAvatar].spinnerActive = NO;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MPActiveUserStateUserName: {
|
case MPActiveUserStateUserName: {
|
||||||
@ -209,21 +213,24 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
|||||||
self.entryField.enabled = NO;
|
self.entryField.enabled = NO;
|
||||||
MPAvatarCell *avatarCell = [self selectedAvatar];
|
MPAvatarCell *avatarCell = [self selectedAvatar];
|
||||||
avatarCell.spinnerActive = YES;
|
avatarCell.spinnerActive = YES;
|
||||||
|
NSUInteger newUserAvatar = avatarCell.avatar;
|
||||||
|
NSString *newUserName = avatarCell.name;
|
||||||
if (![MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
if (![MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
BOOL isNew = NO;
|
BOOL isNew = NO;
|
||||||
MPUserEntity *user = [self userForAvatar:avatarCell inContext:context isNew:&isNew];
|
MPUserEntity *user = [self userForAvatar:avatarCell inContext:context isNew:&isNew];
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
user = [MPUserEntity insertNewObjectInContext:context];
|
user = [MPUserEntity insertNewObjectInContext:context];
|
||||||
user.algorithm = MPAlgorithmDefault;
|
user.algorithm = MPAlgorithmDefault;
|
||||||
user.avatar = avatarCell.avatar;
|
user.avatar = newUserAvatar;
|
||||||
user.name = avatarCell.name;
|
user.name = newUserName;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context usingMasterPassword:masterPassword];
|
BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context
|
||||||
|
usingMasterPassword:masterPassword];
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
self.entryField.text = @"";
|
self.entryField.text = @"";
|
||||||
self.entryField.enabled = YES;
|
self.entryField.enabled = YES;
|
||||||
[self selectedAvatar].spinnerActive = NO;
|
avatarCell.spinnerActive = NO;
|
||||||
|
|
||||||
if (!signedIn) {
|
if (!signedIn) {
|
||||||
// Sign in failed, shouldn't happen for a new user.
|
// Sign in failed, shouldn't happen for a new user.
|
||||||
@ -232,8 +239,10 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
}])
|
}]) {
|
||||||
|
self.entryField.enabled = YES;
|
||||||
avatarCell.spinnerActive = NO;
|
avatarCell.spinnerActive = NO;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -356,16 +365,27 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
|||||||
self.activeUserState = MPActiveUserStateLogin;
|
self.activeUserState = MPActiveUserStateLogin;
|
||||||
|
|
||||||
self.entryField.enabled = NO;
|
self.entryField.enabled = NO;
|
||||||
[self selectedAvatar].spinnerActive = YES;
|
MPAvatarCell *userAvatar = [self selectedAvatar];
|
||||||
BOOL signedIn = NO;
|
userAvatar.spinnerActive = YES;
|
||||||
if (!isNew && mainUser)
|
if (!isNew && mainUser && [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
signedIn = [[MPiOSAppDelegate get] signInAsUser:mainUser saveInContext:mainContext usingMasterPassword:nil];
|
MPUserEntity *user = [MPUserEntity existingObjectWithID:mainUser.objectID inContext:context];
|
||||||
|
BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:mainContext usingMasterPassword:nil];
|
||||||
|
|
||||||
|
PearlMainQueue(^{
|
||||||
|
self.entryField.text = @"";
|
||||||
|
self.entryField.enabled = YES;
|
||||||
|
userAvatar.spinnerActive = NO;
|
||||||
|
|
||||||
|
if (!signedIn)
|
||||||
|
[self.entryField becomeFirstResponder];
|
||||||
|
});
|
||||||
|
}])
|
||||||
|
return;
|
||||||
|
|
||||||
self.entryField.text = @"";
|
self.entryField.text = @"";
|
||||||
self.entryField.enabled = YES;
|
self.entryField.enabled = YES;
|
||||||
[self selectedAvatar].spinnerActive = NO;
|
userAvatar.spinnerActive = NO;
|
||||||
|
|
||||||
if (!signedIn)
|
|
||||||
[self.entryField becomeFirstResponder];
|
[self.entryField becomeFirstResponder];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user