Fixed saveKey getting unset and some MOC threading issues.
[FIXED] Don't unset saveKey when loading the key fails. This causes saveKey to fail as soon as it's synced to a device that hasn't saved the key yet. [FIXED] A bunch of threading violation issues with thread-confined MOCs.
This commit is contained in:
parent
cb860cec96
commit
0df322f648
@ -24,14 +24,12 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
- (MPKey *)loadSavedKeyFor:(MPUserEntity *)user {
|
- (MPKey *)loadSavedKeyFor:(MPUserEntity *)user {
|
||||||
|
|
||||||
NSData *keyData = [PearlKeyChain dataOfItemForQuery:keyQuery( user )];
|
NSData *keyData = [PearlKeyChain dataOfItemForQuery:keyQuery( user )];
|
||||||
if (keyData)
|
if (!keyData) {
|
||||||
inf(@"Found key in keychain for: %@", user.userID);
|
|
||||||
|
|
||||||
else {
|
|
||||||
user.saveKey = NO;
|
|
||||||
inf(@"No key found in keychain for: %@", user.userID);
|
inf(@"No key found in keychain for: %@", user.userID);
|
||||||
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inf(@"Found key in keychain for: %@", user.userID);
|
||||||
return [MPAlgorithmDefault keyFromKeyData:keyData];
|
return [MPAlgorithmDefault keyFromKeyData:keyData];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,15 +55,11 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
- (void)forgetSavedKeyFor:(MPUserEntity *)user {
|
- (void)forgetSavedKeyFor:(MPUserEntity *)user {
|
||||||
|
|
||||||
OSStatus result = [PearlKeyChain deleteItemForQuery:keyQuery( user )];
|
OSStatus result = [PearlKeyChain deleteItemForQuery:keyQuery( user )];
|
||||||
if (result == noErr || result == errSecItemNotFound) {
|
|
||||||
user.saveKey = NO;
|
|
||||||
|
|
||||||
if (result == noErr) {
|
if (result == noErr) {
|
||||||
inf(@"Removed key from keychain for: %@", user.userID);
|
inf(@"Removed key from keychain for: %@", user.userID);
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPKeyForgottenNotification object:self];
|
[[NSNotificationCenter defaultCenter] postNotificationName:MPKeyForgottenNotification object:self];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)signOutAnimated:(BOOL)animated {
|
- (void)signOutAnimated:(BOOL)animated {
|
||||||
@ -80,7 +74,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
- (BOOL)signInAsUser:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc usingMasterPassword:(NSString *)password {
|
- (BOOL)signInAsUser:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc usingMasterPassword:(NSString *)password {
|
||||||
|
|
||||||
if (password)
|
if (password)
|
||||||
NSAssert(![NSThread isMainThread], @"Computing key may not happen from the main thread.");
|
NSAssert(![NSThread isMainThread], @"Computing key must not happen from the main thread.");
|
||||||
|
|
||||||
MPKey *tryKey = nil;
|
MPKey *tryKey = nil;
|
||||||
|
|
||||||
@ -90,7 +84,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
user.keyID = tryKey.keyID;
|
user.keyID = tryKey.keyID;
|
||||||
|
|
||||||
// Migrate existing elements.
|
// Migrate existing elements.
|
||||||
[self migrateElementsForUser:user inContext:moc toKey:tryKey];
|
[self migrateElementsForUser:user saveInContext:moc toKey:tryKey];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +95,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
|
|
||||||
else if (!tryKey) {
|
else if (!tryKey) {
|
||||||
// Key should be saved in keychain. Load it.
|
// Key should be saved in keychain. Load it.
|
||||||
if ((tryKey = [self loadSavedKeyFor:user])) if (![user.keyID isEqual:tryKey.keyID]) {
|
if ((tryKey = [self loadSavedKeyFor:user]) && ![user.keyID isEqual:tryKey.keyID]) {
|
||||||
// Loaded password doesn't match user's keyID. Forget saved password: it is incorrect.
|
// Loaded password doesn't match user's keyID. Forget saved password: it is incorrect.
|
||||||
inf(@"Saved password doesn't match keyID for: %@", user.userID);
|
inf(@"Saved password doesn't match keyID for: %@", user.userID);
|
||||||
|
|
||||||
@ -164,7 +158,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)migrateElementsForUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc toKey:(MPKey *)newKey {
|
- (void)migrateElementsForUser:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc toKey:(MPKey *)newKey {
|
||||||
|
|
||||||
if (![user.elements count])
|
if (![user.elements count])
|
||||||
// Nothing to migrate.
|
// Nothing to migrate.
|
||||||
|
@ -21,8 +21,8 @@ typedef enum {
|
|||||||
@interface MPAppDelegate_Shared(Store)<UbiquityStoreManagerDelegate>
|
@interface MPAppDelegate_Shared(Store)<UbiquityStoreManagerDelegate>
|
||||||
|
|
||||||
+ (NSManagedObjectContext *)managedObjectContextForThreadIfReady;
|
+ (NSManagedObjectContext *)managedObjectContextForThreadIfReady;
|
||||||
+ (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *moc))mocBlock;
|
+ (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *context))mocBlock;
|
||||||
+ (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *))mocBlock;
|
+ (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *context))mocBlock;
|
||||||
|
|
||||||
- (UbiquityStoreManager *)storeManager;
|
- (UbiquityStoreManager *)storeManager;
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
return threadManagedObjectContext;
|
return threadManagedObjectContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *))mocBlock {
|
+ (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *context))mocBlock {
|
||||||
|
|
||||||
NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady];
|
NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady];
|
||||||
if (!mainManagedObjectContext)
|
if (!mainManagedObjectContext)
|
||||||
@ -69,7 +69,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *))mocBlock {
|
+ (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *context))mocBlock {
|
||||||
|
|
||||||
NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady];
|
NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady];
|
||||||
if (!mainManagedObjectContext)
|
if (!mainManagedObjectContext)
|
||||||
@ -430,6 +430,26 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
askImportPassword:(NSString *(^)(NSString *userName))importPassword
|
askImportPassword:(NSString *(^)(NSString *userName))importPassword
|
||||||
askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword {
|
askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword {
|
||||||
|
|
||||||
|
NSAssert(![[NSThread currentThread] isMainThread], @"This method should not be invoked from the main thread.");
|
||||||
|
|
||||||
|
__block MPImportResult result = MPImportResultCancelled;
|
||||||
|
do {
|
||||||
|
if ([MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
||||||
|
result = [self importSites:importedSitesString askImportPassword:importPassword askUserPassword:userPassword
|
||||||
|
saveInContext:context];
|
||||||
|
}])
|
||||||
|
break;
|
||||||
|
usleep( (useconds_t)(USEC_PER_SEC * 0.2) );
|
||||||
|
} while (YES);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (MPImportResult)importSites:(NSString *)importedSitesString
|
||||||
|
askImportPassword:(NSString *(^)(NSString *userName))importPassword
|
||||||
|
askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword
|
||||||
|
saveInContext:(NSManagedObjectContext *)context {
|
||||||
|
|
||||||
// Compile patterns.
|
// Compile patterns.
|
||||||
static NSRegularExpression *headerPattern, *sitePattern;
|
static NSRegularExpression *headerPattern, *sitePattern;
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
@ -452,12 +472,6 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a MOC.
|
|
||||||
NSAssert(![[NSThread currentThread] isMainThread], @"This method should not be invoked from the main thread.");
|
|
||||||
NSManagedObjectContext *moc;
|
|
||||||
while (!(moc = [MPAppDelegate_Shared managedObjectContextForThreadIfReady]))
|
|
||||||
usleep( (useconds_t)(USEC_PER_SEC * 0.2) );
|
|
||||||
|
|
||||||
// Parse import data.
|
// Parse import data.
|
||||||
inf(@"Importing sites.");
|
inf(@"Importing sites.");
|
||||||
__block MPUserEntity *user = nil;
|
__block MPUserEntity *user = nil;
|
||||||
@ -499,7 +513,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
|
|
||||||
NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
||||||
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName];
|
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName];
|
||||||
NSArray *users = [moc executeFetchRequest:userFetchRequest error:&error];
|
NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
|
||||||
if (!users) {
|
if (!users) {
|
||||||
err(@"While looking for user: %@, error: %@", importUserName, error);
|
err(@"While looking for user: %@, error: %@", importUserName, error);
|
||||||
return MPImportResultInternalError;
|
return MPImportResultInternalError;
|
||||||
@ -552,7 +566,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
// Find existing site.
|
// Find existing site.
|
||||||
if (user) {
|
if (user) {
|
||||||
elementFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", name, user];
|
elementFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", name, user];
|
||||||
NSArray *existingSites = [moc executeFetchRequest:elementFetchRequest error:&error];
|
NSArray *existingSites = [context executeFetchRequest:elementFetchRequest error:&error];
|
||||||
if (!existingSites) {
|
if (!existingSites) {
|
||||||
err(@"Lookup of existing sites failed for site: %@, user: %@, error: %@", name, user.userID, error);
|
err(@"Lookup of existing sites failed for site: %@, user: %@, error: %@", name, user.userID, error);
|
||||||
return MPImportResultInternalError;
|
return MPImportResultInternalError;
|
||||||
@ -585,13 +599,13 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
if (elementsToDelete.count)
|
if (elementsToDelete.count)
|
||||||
[elementsToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
|
[elementsToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
|
||||||
inf(@"Deleting site: %@, it will be replaced by an imported site.", [obj name]);
|
inf(@"Deleting site: %@, it will be replaced by an imported site.", [obj name]);
|
||||||
[moc deleteObject:obj];
|
[context deleteObject:obj];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// Make sure there is a user.
|
// Make sure there is a user.
|
||||||
if (!user) {
|
if (!user) {
|
||||||
user = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] )
|
user = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] )
|
||||||
inManagedObjectContext:moc];
|
inManagedObjectContext:context];
|
||||||
user.name = importUserName;
|
user.name = importUserName;
|
||||||
user.keyID = importKeyID;
|
user.keyID = importKeyID;
|
||||||
dbg(@"Created User: %@", [user debugDescription]);
|
dbg(@"Created User: %@", [user debugDescription]);
|
||||||
@ -609,7 +623,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
// Create new site.
|
// Create new site.
|
||||||
MPElementEntity
|
MPElementEntity
|
||||||
*element = [NSEntityDescription insertNewObjectForEntityForName:[MPAlgorithmForVersion( version ) classNameOfType:type]
|
*element = [NSEntityDescription insertNewObjectForEntityForName:[MPAlgorithmForVersion( version ) classNameOfType:type]
|
||||||
inManagedObjectContext:moc];
|
inManagedObjectContext:context];
|
||||||
element.name = name;
|
element.name = name;
|
||||||
element.user = user;
|
element.user = user;
|
||||||
element.type = type;
|
element.type = type;
|
||||||
@ -632,10 +646,8 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
dbg(@"Created Element: %@", [element debugDescription]);
|
dbg(@"Created Element: %@", [element debugDescription]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![moc save:&error]) {
|
if (![context saveToStore])
|
||||||
err(@"While saving imported sites: %@", error);
|
|
||||||
return MPImportResultInternalError;
|
return MPImportResultInternalError;
|
||||||
}
|
|
||||||
|
|
||||||
inf(@"Import completed successfully.");
|
inf(@"Import completed successfully.");
|
||||||
MPCheckpoint( MPCheckpointSitesImported, nil );
|
MPCheckpoint( MPCheckpointSitesImported, nil );
|
||||||
|
@ -126,13 +126,14 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
if (sender == self.rememberPasswordItem)
|
if (sender == self.rememberPasswordItem)
|
||||||
[MPConfig get].rememberLogin = [NSNumber numberWithBool:![[MPConfig get].rememberLogin boolValue]];
|
[MPConfig get].rememberLogin = [NSNumber numberWithBool:![[MPConfig get].rememberLogin boolValue]];
|
||||||
if (sender == self.savePasswordItem) {
|
if (sender == self.savePasswordItem) {
|
||||||
NSManagedObjectContext *moc = [MPMacAppDelegate managedObjectContextForThreadIfReady];
|
[MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
||||||
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc];
|
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:context];
|
||||||
if ((activeUser.saveKey = !activeUser.saveKey))
|
if ((activeUser.saveKey = !activeUser.saveKey))
|
||||||
[[MPMacAppDelegate get] storeSavedKeyFor:activeUser];
|
[[MPMacAppDelegate get] storeSavedKeyFor:activeUser];
|
||||||
else
|
else
|
||||||
[[MPMacAppDelegate get] forgetSavedKeyFor:activeUser];
|
[[MPMacAppDelegate get] forgetSavedKeyFor:activeUser];
|
||||||
[moc saveToStore];
|
[context saveToStore];
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
if (sender == self.dialogStyleRegular)
|
if (sender == self.dialogStyleRegular)
|
||||||
[MPMacConfig get].dialogStyleHUD = @NO;
|
[MPMacConfig get].dialogStyleHUD = @NO;
|
||||||
|
@ -81,7 +81,7 @@
|
|||||||
|
|
||||||
- (void)unlock {
|
- (void)unlock {
|
||||||
|
|
||||||
NSManagedObjectContext *moc = [MPMacAppDelegate managedObjectContextForThreadIfReady];
|
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||||
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc];
|
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc];
|
||||||
if (!activeUser)
|
if (!activeUser)
|
||||||
// No user to sign in with.
|
// No user to sign in with.
|
||||||
@ -114,6 +114,7 @@
|
|||||||
[alert beginSheetModalForWindow:self.window modalDelegate:self
|
[alert beginSheetModalForWindow:self.window modalDelegate:self
|
||||||
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertUnlockMP];
|
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertUnlockMP];
|
||||||
}];
|
}];
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
|
- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
|
||||||
@ -123,8 +124,6 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (contextInfo == MPAlertUnlockMP) {
|
if (contextInfo == MPAlertUnlockMP) {
|
||||||
NSManagedObjectContext *moc = [MPMacAppDelegate managedObjectContextForThreadIfReady];
|
|
||||||
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc];
|
|
||||||
switch (returnCode) {
|
switch (returnCode) {
|
||||||
case NSAlertAlternateReturn: {
|
case NSAlertAlternateReturn: {
|
||||||
// "Change" button.
|
// "Change" button.
|
||||||
@ -139,10 +138,13 @@
|
|||||||
runModal];
|
runModal];
|
||||||
|
|
||||||
if (returnCode_ == NSAlertDefaultReturn) {
|
if (returnCode_ == NSAlertDefaultReturn) {
|
||||||
|
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||||
|
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc];
|
||||||
activeUser.keyID = nil;
|
activeUser.keyID = nil;
|
||||||
[[MPMacAppDelegate get] forgetSavedKeyFor:activeUser];
|
[[MPMacAppDelegate get] forgetSavedKeyFor:activeUser];
|
||||||
[[MPMacAppDelegate get] signOutAnimated:YES];
|
[[MPMacAppDelegate get] signOutAnimated:YES];
|
||||||
[moc saveToStore];
|
[moc saveToStore];
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -160,13 +162,9 @@
|
|||||||
self.inProgress = YES;
|
self.inProgress = YES;
|
||||||
|
|
||||||
NSString *password = [(NSSecureTextField *)alert.accessoryView stringValue];
|
NSString *password = [(NSSecureTextField *)alert.accessoryView stringValue];
|
||||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc_) {
|
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||||
NSError *error = nil;
|
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc];
|
||||||
MPUserEntity *activeUser_ = (MPUserEntity *)[moc_ existingObjectWithID:activeUser.objectID error:&error];
|
BOOL success = [[MPMacAppDelegate get] signInAsUser:activeUser saveInContext:moc
|
||||||
if (!activeUser_)
|
|
||||||
err(@"Failed to retrieve active use while logging in: %@", error);
|
|
||||||
|
|
||||||
BOOL success = [[MPMacAppDelegate get] signInAsUser:activeUser saveInContext:moc_
|
|
||||||
usingMasterPassword:password];
|
usingMasterPassword:password];
|
||||||
self.inProgress = NO;
|
self.inProgress = NO;
|
||||||
|
|
||||||
@ -384,10 +382,10 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSManagedObjectContext *moc = [MPMacAppDelegate managedObjectContextForThreadIfReady];
|
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||||
MPElementEntity *activeElement = [self activeElementInContext:moc];
|
[[self activeElementInContext:moc] use];
|
||||||
[activeElement use];
|
|
||||||
[moc saveToStore];
|
[moc saveToStore];
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)createNewSite:(NSString *)siteName {
|
- (void)createNewSite:(NSString *)siteName {
|
||||||
|
@ -25,17 +25,18 @@
|
|||||||
|
|
||||||
- (NSFetchedResultsController *)fetchedResultsControllerByLastUsed {
|
- (NSFetchedResultsController *)fetchedResultsControllerByLastUsed {
|
||||||
|
|
||||||
if (!_fetchedResultsControllerByLastUsed) {
|
|
||||||
NSAssert([[NSThread currentThread] isMainThread], @"The fetchedResultsController must be accessed from the main thread.");
|
NSAssert([[NSThread currentThread] isMainThread], @"The fetchedResultsController must be accessed from the main thread.");
|
||||||
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
|
|
||||||
if (!moc)
|
if (!_fetchedResultsControllerByLastUsed) {
|
||||||
|
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
|
||||||
|
if (!mainContext)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
|
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
|
||||||
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector(lastUsed) ) ascending:NO] ];
|
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector(lastUsed) ) ascending:NO] ];
|
||||||
[self configureFetchRequest:fetchRequest];
|
[self configureFetchRequest:fetchRequest];
|
||||||
_fetchedResultsControllerByLastUsed = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc
|
_fetchedResultsControllerByLastUsed = [[NSFetchedResultsController alloc]
|
||||||
sectionNameKeyPath:nil cacheName:nil];
|
initWithFetchRequest:fetchRequest managedObjectContext:mainContext sectionNameKeyPath:nil cacheName:nil];
|
||||||
_fetchedResultsControllerByLastUsed.delegate = self;
|
_fetchedResultsControllerByLastUsed.delegate = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,17 +45,18 @@
|
|||||||
|
|
||||||
- (NSFetchedResultsController *)fetchedResultsControllerByUses {
|
- (NSFetchedResultsController *)fetchedResultsControllerByUses {
|
||||||
|
|
||||||
if (!_fetchedResultsControllerByUses) {
|
|
||||||
NSAssert([[NSThread currentThread] isMainThread], @"The fetchedResultsController must be accessed from the main thread.");
|
NSAssert([[NSThread currentThread] isMainThread], @"The fetchedResultsController must be accessed from the main thread.");
|
||||||
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
|
|
||||||
if (!moc)
|
if (!_fetchedResultsControllerByUses) {
|
||||||
|
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
|
||||||
|
if (!mainContext)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
|
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
|
||||||
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector(uses_) ) ascending:NO] ];
|
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector(uses_) ) ascending:NO] ];
|
||||||
[self configureFetchRequest:fetchRequest];
|
[self configureFetchRequest:fetchRequest];
|
||||||
_fetchedResultsControllerByUses = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc
|
_fetchedResultsControllerByUses = [[NSFetchedResultsController alloc]
|
||||||
sectionNameKeyPath:nil cacheName:nil];
|
initWithFetchRequest:fetchRequest managedObjectContext:mainContext sectionNameKeyPath:nil cacheName:nil];
|
||||||
_fetchedResultsControllerByUses.delegate = self;
|
_fetchedResultsControllerByUses.delegate = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,13 +47,10 @@
|
|||||||
} options:0];
|
} options:0];
|
||||||
[avatar onSelect:^(BOOL selected) {
|
[avatar onSelect:^(BOOL selected) {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||||
if (!moc)
|
[[MPiOSAppDelegate get] activeUserInContext:moc].avatar = (unsigned)avatar.tag;
|
||||||
return;
|
|
||||||
|
|
||||||
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:moc];
|
|
||||||
activeUser.avatar = (unsigned)avatar.tag;
|
|
||||||
[moc saveToStore];
|
[moc saveToStore];
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
} options:0];
|
} options:0];
|
||||||
avatar.selected = (a == [[MPiOSAppDelegate get] activeUserForThread].avatar);
|
avatar.selected = (a == [[MPiOSAppDelegate get] activeUserForThread].avatar);
|
||||||
@ -133,12 +130,10 @@
|
|||||||
[[MPiOSAppDelegate get] export];
|
[[MPiOSAppDelegate get] export];
|
||||||
|
|
||||||
else if (cell == self.changeMPCell) {
|
else if (cell == self.changeMPCell) {
|
||||||
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||||
if (!moc)
|
|
||||||
return;
|
|
||||||
|
|
||||||
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:moc];
|
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:moc];
|
||||||
[[MPiOSAppDelegate get] changeMasterPasswordFor:activeUser saveInContext:moc didResetBlock:nil];
|
[[MPiOSAppDelegate get] changeMasterPasswordFor:activeUser saveInContext:moc didResetBlock:nil];
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||||
@ -148,15 +143,13 @@
|
|||||||
|
|
||||||
- (void)didSelectType:(MPElementType)type {
|
- (void)didSelectType:(MPElementType)type {
|
||||||
|
|
||||||
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
|
self.defaultTypeLabel.text = [[MPiOSAppDelegate get].key.algorithm shortNameOfType:type];
|
||||||
if (!moc)
|
|
||||||
return;
|
|
||||||
|
|
||||||
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:moc];
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
|
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:context];
|
||||||
activeUser.defaultType = type;
|
activeUser.defaultType = type;
|
||||||
[moc saveToStore];
|
[context saveToStore];
|
||||||
|
}];
|
||||||
self.defaultTypeLabel.text = [[MPiOSAppDelegate get].key.algorithm shortNameOfType:activeUser.defaultType];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPElementType)selectedType {
|
- (MPElementType)selectedType {
|
||||||
@ -168,16 +161,15 @@
|
|||||||
|
|
||||||
- (IBAction)didToggleSwitch:(UISwitch *)sender {
|
- (IBAction)didToggleSwitch:(UISwitch *)sender {
|
||||||
|
|
||||||
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
|
if (sender == self.savePasswordSwitch)
|
||||||
if (!moc)
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||||
return;
|
|
||||||
|
|
||||||
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:moc];
|
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:moc];
|
||||||
if ((activeUser.saveKey = sender.on))
|
if ((activeUser.saveKey = sender.on))
|
||||||
[[MPiOSAppDelegate get] storeSavedKeyFor:activeUser];
|
[[MPiOSAppDelegate get] storeSavedKeyFor:activeUser];
|
||||||
else
|
else
|
||||||
[[MPiOSAppDelegate get] forgetSavedKeyFor:activeUser];
|
[[MPiOSAppDelegate get] forgetSavedKeyFor:activeUser];
|
||||||
[moc saveToStore];
|
[moc saveToStore];
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -298,19 +298,13 @@
|
|||||||
|
|
||||||
- (void)updateUsers {
|
- (void)updateUsers {
|
||||||
|
|
||||||
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
|
[MPiOSAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
||||||
if (!moc)
|
|
||||||
return;
|
|
||||||
|
|
||||||
__block NSArray *users = nil;
|
|
||||||
[moc performBlockAndWait:^{
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
||||||
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
|
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
|
||||||
users = [moc executeFetchRequest:fetchRequest error:&error];
|
NSArray *users = [context executeFetchRequest:fetchRequest error:&error];
|
||||||
if (!users)
|
if (!users)
|
||||||
err(@"Failed to load users: %@", error);
|
err(@"Failed to load users: %@", error);
|
||||||
}];
|
|
||||||
|
|
||||||
// Clean up avatars.
|
// Clean up avatars.
|
||||||
for (UIView *subview in [self.avatarsView subviews])
|
for (UIView *subview in [self.avatarsView subviews])
|
||||||
@ -320,7 +314,6 @@
|
|||||||
[self.avatarToUserOID removeAllObjects];
|
[self.avatarToUserOID removeAllObjects];
|
||||||
|
|
||||||
// Create avatars.
|
// Create avatars.
|
||||||
[moc performBlockAndWait:^{
|
|
||||||
for (MPUserEntity *user in users)
|
for (MPUserEntity *user in users)
|
||||||
[self setupAvatar:[self.avatarTemplate clone] forUser:user];
|
[self setupAvatar:[self.avatarTemplate clone] forUser:user];
|
||||||
[self setupAvatar:[self.avatarTemplate clone] forUser:nil];
|
[self setupAvatar:[self.avatarTemplate clone] forUser:nil];
|
||||||
@ -374,11 +367,13 @@
|
|||||||
|
|
||||||
- (void)didToggleUserSelection {
|
- (void)didToggleUserSelection {
|
||||||
|
|
||||||
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
|
NSAssert([[NSThread currentThread] isMainThread], @"User selection should only be toggled from the main thread.");
|
||||||
MPUserEntity *selectedUser = [self selectedUserInContext:moc];
|
|
||||||
|
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
|
||||||
|
MPUserEntity *selectedUser = [self selectedUserInContext:mainContext];
|
||||||
if (!selectedUser)
|
if (!selectedUser)
|
||||||
[self.passwordField resignFirstResponder];
|
[self.passwordField resignFirstResponder];
|
||||||
else if ([[MPiOSAppDelegate get] signInAsUser:selectedUser saveInContext:moc usingMasterPassword:nil]) {
|
else if ([[MPiOSAppDelegate get] signInAsUser:selectedUser saveInContext:mainContext usingMasterPassword:nil]) {
|
||||||
[self performSegueWithIdentifier:@"MP_Unlock" sender:self];
|
[self performSegueWithIdentifier:@"MP_Unlock" sender:self];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -391,20 +386,18 @@
|
|||||||
|
|
||||||
- (void)didSelectNewUserAvatar:(UIButton *)newUserAvatar {
|
- (void)didSelectNewUserAvatar:(UIButton *)newUserAvatar {
|
||||||
|
|
||||||
if (![MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
if (![MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPUserEntity
|
MPUserEntity *newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] )
|
||||||
*newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] )
|
inManagedObjectContext:context];
|
||||||
inManagedObjectContext:moc];
|
|
||||||
|
|
||||||
[self showNewUserNameAlertFor:newUser inContext:moc completion:^(BOOL finished) {
|
[self showNewUserNameAlertFor:newUser saveInContext:context completion:^(BOOL finished) {
|
||||||
newUserAvatar.selected = NO;
|
newUserAvatar.selected = NO;
|
||||||
self.selectedUser = newUser;
|
|
||||||
}];
|
}];
|
||||||
}])
|
}])
|
||||||
newUserAvatar.selected = NO;
|
newUserAvatar.selected = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)showNewUserNameAlertFor:(MPUserEntity *)newUser inContext:(NSManagedObjectContext *)moc
|
- (void)showNewUserNameAlertFor:(MPUserEntity *)newUser saveInContext:(NSManagedObjectContext *)context
|
||||||
completion:(void (^)(BOOL finished))completion {
|
completion:(void (^)(BOOL finished))completion {
|
||||||
|
|
||||||
[PearlAlert showAlertWithTitle:@"Enter Your Name"
|
[PearlAlert showAlertWithTitle:@"Enter Your Name"
|
||||||
@ -425,36 +418,36 @@
|
|||||||
if (!name.length) {
|
if (!name.length) {
|
||||||
[PearlAlert showAlertWithTitle:@"Name Is Required" message:nil viewStyle:UIAlertViewStyleDefault initAlert:nil
|
[PearlAlert showAlertWithTitle:@"Name Is Required" message:nil viewStyle:UIAlertViewStyleDefault initAlert:nil
|
||||||
tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||||
[self showNewUserNameAlertFor:newUser inContext:moc completion:completion];
|
[self showNewUserNameAlertFor:newUser saveInContext:context completion:completion];
|
||||||
} cancelTitle:@"Try Again" otherTitles:nil];
|
} cancelTitle:@"Try Again" otherTitles:nil];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
[moc performBlockAndWait:^{
|
[context performBlockAndWait:^{
|
||||||
newUser.name = name;
|
newUser.name = name;
|
||||||
}];
|
}];
|
||||||
[self showNewUserAvatarAlertFor:newUser inContext:moc completion:completion];
|
[self showNewUserAvatarAlertFor:newUser saveInContext:context completion:completion];
|
||||||
}
|
}
|
||||||
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonSave, nil];
|
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonSave, nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)showNewUserAvatarAlertFor:(MPUserEntity *)newUser inContext:(NSManagedObjectContext *)moc
|
- (void)showNewUserAvatarAlertFor:(MPUserEntity *)newUser saveInContext:(NSManagedObjectContext *)context
|
||||||
completion:(void (^)(BOOL finished))completion {
|
completion:(void (^)(BOOL finished))completion {
|
||||||
|
|
||||||
[PearlAlert showAlertWithTitle:@"Choose Your Avatar"
|
[PearlAlert showAlertWithTitle:@"Choose Your Avatar"
|
||||||
message:@"\n\n\n\n\n\n" viewStyle:UIAlertViewStyleDefault
|
message:@"\n\n\n\n\n\n" viewStyle:UIAlertViewStyleDefault
|
||||||
initAlert:^(UIAlertView *_alert, UITextField *_firstField) {
|
initAlert:^(UIAlertView *_alert, UITextField *_firstField) {
|
||||||
[self initializeAvatarAlert:_alert forUser:newUser inContext:moc];
|
[self initializeAvatarAlert:_alert forUser:newUser inContext:context];
|
||||||
}
|
}
|
||||||
tappedButtonBlock:^(UIAlertView *_alert, NSInteger _buttonIndex) {
|
tappedButtonBlock:^(UIAlertView *_alert, NSInteger _buttonIndex) {
|
||||||
|
|
||||||
// Okay
|
// Okay
|
||||||
[self showNewUserConfirmationAlertFor:newUser inContext:moc completion:completion];
|
[self showNewUserConfirmationAlertFor:newUser saveInContext:context completion:completion];
|
||||||
} cancelTitle:nil otherTitles:[PearlStrings get].commonButtonOkay, nil];
|
} cancelTitle:nil otherTitles:[PearlStrings get].commonButtonOkay, nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)showNewUserConfirmationAlertFor:(MPUserEntity *)newUser inContext:(NSManagedObjectContext *)moc
|
- (void)showNewUserConfirmationAlertFor:(MPUserEntity *)newUser saveInContext:(NSManagedObjectContext *)context
|
||||||
completion:(void (^)(BOOL finished))completion {
|
completion:(void (^)(BOOL finished))completion {
|
||||||
|
|
||||||
[PearlAlert showAlertWithTitle:@"Is this correct?"
|
[PearlAlert showAlertWithTitle:@"Is this correct?"
|
||||||
@ -467,16 +460,17 @@
|
|||||||
}
|
}
|
||||||
tappedButtonBlock:^void(UIAlertView *__alert, NSInteger __buttonIndex) {
|
tappedButtonBlock:^void(UIAlertView *__alert, NSInteger __buttonIndex) {
|
||||||
if (__buttonIndex == [__alert cancelButtonIndex]) {
|
if (__buttonIndex == [__alert cancelButtonIndex]) {
|
||||||
[self showNewUserNameAlertFor:newUser inContext:moc completion:completion];
|
[self showNewUserNameAlertFor:newUser saveInContext:context completion:completion];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Confirm
|
// Confirm
|
||||||
[moc performBlockAndWait:^{
|
[context performBlockAndWait:^{
|
||||||
[moc saveToStore];
|
[context saveToStore];
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
if (![moc obtainPermanentIDsForObjects:@[ newUser ] error:&error])
|
if (![context obtainPermanentIDsForObjects:@[ newUser ] error:&error])
|
||||||
err(@"Failed to obtain permanent object ID for new user: %@", error);
|
err(@"Failed to obtain permanent object ID for new user: %@", error);
|
||||||
|
self.selectedUser = newUser;
|
||||||
}];
|
}];
|
||||||
completion( YES );
|
completion( YES );
|
||||||
|
|
||||||
@ -684,14 +678,14 @@
|
|||||||
return avatar;
|
return avatar;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPUserEntity *)userForAvatar:(UIButton *)avatar inContext:(NSManagedObjectContext *)moc {
|
- (MPUserEntity *)userForAvatar:(UIButton *)avatar inContext:(NSManagedObjectContext *)context {
|
||||||
|
|
||||||
NSManagedObjectID *userOID = NSNullToNil([self.avatarToUserOID objectForKey:[NSValue valueWithNonretainedObject:avatar]]);
|
NSManagedObjectID *userOID = NSNullToNil([self.avatarToUserOID objectForKey:[NSValue valueWithNonretainedObject:avatar]]);
|
||||||
if (!userOID)
|
if (!userOID)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
NSError *error;
|
NSError *error;
|
||||||
MPUserEntity *user = (MPUserEntity *)[moc existingObjectWithID:userOID error:&error];
|
MPUserEntity *user = (MPUserEntity *)[context existingObjectWithID:userOID error:&error];
|
||||||
if (!user)
|
if (!user)
|
||||||
err(@"Failed retrieving user for avatar: %@", error);
|
err(@"Failed retrieving user for avatar: %@", error);
|
||||||
|
|
||||||
@ -1008,8 +1002,8 @@
|
|||||||
if ([self selectedUserForThread])
|
if ([self selectedUserForThread])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPUserEntity *targetedUser = [self userForAvatar:[self findTargetedAvatar] inContext:moc];
|
MPUserEntity *targetedUser = [self userForAvatar:[self findTargetedAvatar] inContext:context];
|
||||||
if (!targetedUser)
|
if (!targetedUser)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1020,9 +1014,9 @@
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (buttonIndex == [sheet destructiveButtonIndex]) {
|
if (buttonIndex == [sheet destructiveButtonIndex]) {
|
||||||
[moc performBlock:^{
|
[context performBlock:^{
|
||||||
[moc deleteObject:targetedUser];
|
[context deleteObject:targetedUser];
|
||||||
[moc saveToStore];
|
[context saveToStore];
|
||||||
|
|
||||||
dispatch_async( dispatch_get_main_queue(), ^{
|
dispatch_async( dispatch_get_main_queue(), ^{
|
||||||
[self updateUsers];
|
[self updateUsers];
|
||||||
@ -1032,13 +1026,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (buttonIndex == [sheet firstOtherButtonIndex])
|
if (buttonIndex == [sheet firstOtherButtonIndex])
|
||||||
[[MPiOSAppDelegate get] changeMasterPasswordFor:targetedUser saveInContext:moc didResetBlock:^{
|
[[MPiOSAppDelegate get] changeMasterPasswordFor:targetedUser saveInContext:context didResetBlock:^{
|
||||||
dispatch_async( dispatch_get_main_queue(), ^{
|
dispatch_async( dispatch_get_main_queue(), ^{
|
||||||
[[self avatarForUser:targetedUser] setSelected:YES];
|
[[self avatarForUser:targetedUser] setSelected:YES];
|
||||||
} );
|
} );
|
||||||
}];
|
}];
|
||||||
} cancelTitle:[PearlStrings get].commonButtonCancel
|
} cancelTitle:[PearlStrings get].commonButtonCancel
|
||||||
destructiveTitle:@"Delete User" otherTitles:@"Reset Password", nil];
|
destructiveTitle:@"Delete User" otherTitles:@"Reset Password", nil];
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)facebook:(UIButton *)sender {
|
- (IBAction)facebook:(UIButton *)sender {
|
||||||
|
Loading…
Reference in New Issue
Block a user