Core Data context fixes and migration fixes.
[FIXED] More careful Core Data threading and context usage. [FIXED] iOS bug when migrating, now using USM's copy migration. [ADDED] Better handling of store changes while in Main VC and Element List VCs.
This commit is contained in:
parent
07e44a46ae
commit
d27a0fdbad
2
External/UbiquityStoreManager
vendored
2
External/UbiquityStoreManager
vendored
@ -1 +1 @@
|
||||
Subproject commit 25a30fadcb80e337a664127c6c86c4fc46e96e5f
|
||||
Subproject commit 9a4d52c382ae01f81cc9d94982b58222b3240f73
|
@ -10,7 +10,7 @@
|
||||
|
||||
@interface MPAppDelegate_Shared(Key)
|
||||
|
||||
- (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password;
|
||||
- (BOOL)signInAsUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc usingMasterPassword:(NSString *)password;
|
||||
- (void)signOutAnimated:(BOOL)animated;
|
||||
|
||||
- (void)storeSavedKeyFor:(MPUserEntity *)user;
|
||||
|
@ -77,18 +77,20 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSignedOutNotification object:self userInfo:@{ @"animated" : @(animated) }];
|
||||
}
|
||||
|
||||
- (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password {
|
||||
- (BOOL)signInAsUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc usingMasterPassword:(NSString *)password {
|
||||
|
||||
if (password)
|
||||
NSAssert(![NSThread isMainThread], @"Computing key may not happen from the main thread.");
|
||||
|
||||
assert(!password || ![NSThread isMainThread]); // If we need to computing a key, this operation shouldn't be on the main thread.
|
||||
MPKey *tryKey = nil;
|
||||
|
||||
// Method 1: When the user has no keyID set, set a new key from the given master password.
|
||||
if (!user.keyID) {
|
||||
if ([password length]) if ((tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) {
|
||||
if ([password length] && (tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) {
|
||||
user.keyID = tryKey.keyID;
|
||||
|
||||
// Migrate existing elements.
|
||||
[self migrateElementsForUser:user inContext:user.managedObjectContext toKey:tryKey];
|
||||
[self migrateElementsForUser:user inContext:moc toKey:tryKey];
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,7 +155,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
||||
}
|
||||
|
||||
user.lastUsed = [NSDate date];
|
||||
[user.managedObjectContext saveToStore];
|
||||
[moc saveToStore];
|
||||
self.activeUser = user;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSignedInNotification object:self];
|
||||
|
@ -26,19 +26,7 @@
|
||||
|
||||
- (MPUserEntity *)activeUserForThread {
|
||||
|
||||
if (!_activeUserOID)
|
||||
return nil;
|
||||
|
||||
NSManagedObjectContext *moc = [MPAppDelegate_Shared managedObjectContextForThreadIfReady];
|
||||
if (!moc)
|
||||
return nil;
|
||||
|
||||
NSError *error;
|
||||
MPUserEntity *activeUser = (MPUserEntity *)[moc existingObjectWithID:_activeUserOID error:&error];
|
||||
if (!activeUser)
|
||||
err(@"Failed to retrieve active user: %@", error);
|
||||
|
||||
return activeUser;
|
||||
return [self activeUserInContext:[MPAppDelegate_Shared managedObjectContextForThreadIfReady]];
|
||||
}
|
||||
|
||||
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)moc {
|
||||
|
@ -157,8 +157,6 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
||||
NSMigratePersistentStoresAutomaticallyOption : @YES,
|
||||
NSInferMappingModelAutomaticallyOption : @YES
|
||||
};
|
||||
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
|
||||
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
|
||||
|
||||
// Create the directory to hold the new local store.
|
||||
if (![[NSFileManager defaultManager] createDirectoryAtPath:[manager URLForLocalStoreDirectory].path
|
||||
@ -168,31 +166,13 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the old local store.
|
||||
NSPersistentStore *oldStore = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldLocalStoreURL
|
||||
options:oldLocalStoreOptions error:&error];
|
||||
if (!oldStore) {
|
||||
err(@"While opening old store for migration %@: %@", oldLocalStoreURL.path, error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Migrate to the new local store.
|
||||
if (![psc migratePersistentStore:oldStore toURL:newLocalStoreURL options:newLocalStoreOptions withType:NSSQLiteStoreType
|
||||
error:&error]) {
|
||||
err(@"While migrating local store from %@ -> %@: %@", oldLocalStoreURL, newLocalStoreURL, error);
|
||||
if (![manager copyMigrateStore:oldLocalStoreURL withOptions:oldLocalStoreOptions
|
||||
toStore:newLocalStoreURL withOptions:newLocalStoreOptions
|
||||
error:nil cause:nil context:nil]) {
|
||||
manager.localStoreURL = oldLocalStoreURL;
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean-up.
|
||||
for (NSPersistentStore *store in psc.persistentStores)
|
||||
if (![psc removePersistentStore:store error:&error]) {
|
||||
err(@"While removing the migrated store from the store context: %@", error);
|
||||
}
|
||||
if (![[NSFileManager defaultManager] removeItemAtURL:oldLocalStoreURL error:&error]) {
|
||||
err(@"While deleting the old local store: %@", error);
|
||||
}
|
||||
|
||||
inf(@"Successfully migrated old to new local store.");
|
||||
}
|
||||
|
||||
@ -260,32 +240,10 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
||||
return;
|
||||
}
|
||||
|
||||
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
|
||||
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
|
||||
|
||||
// Open the old cloud store.
|
||||
NSPersistentStore *oldStore = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldCloudStoreURL
|
||||
options:oldCloudStoreOptions error:&error];
|
||||
if (!oldStore) {
|
||||
err(@"While opening old store for migration %@: %@", oldCloudStoreURL.path, error);
|
||||
if (![manager copyMigrateStore:oldCloudStoreURL withOptions:oldCloudStoreOptions
|
||||
toStore:newCloudStoreURL withOptions:newCloudStoreOptions
|
||||
error:nil cause:nil context:nil])
|
||||
return;
|
||||
}
|
||||
|
||||
// Migrate to the new cloud store.
|
||||
if (![psc migratePersistentStore:oldStore toURL:newCloudStoreURL options:newCloudStoreOptions withType:NSSQLiteStoreType
|
||||
error:&error]) {
|
||||
err(@"While migrating cloud store from %@ -> %@: %@", oldCloudStoreURL.path, newCloudStoreURL.path, error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean-up.
|
||||
for (NSPersistentStore *store in psc.persistentStores)
|
||||
if (![psc removePersistentStore:store error:&error]) {
|
||||
err(@"While removing the migrated store from the store context: %@", error);
|
||||
}
|
||||
if (![[NSFileManager defaultManager] removeItemAtURL:oldCloudStoreURL error:&error]) {
|
||||
err(@"While deleting the old cloud store: %@", error);
|
||||
}
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"LocalUUIDKey"];
|
||||
inf(@"Successfully migrated old to new cloud store.");
|
||||
@ -322,7 +280,6 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
||||
|
||||
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager willLoadStoreIsCloud:(BOOL)isCloudStore {
|
||||
|
||||
// FIXME
|
||||
self.privateManagedObjectContext = nil;
|
||||
self.mainManagedObjectContext = nil;
|
||||
|
||||
@ -413,6 +370,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
||||
cancelTitle:[PearlStrings get].commonButtonBack otherTitles:@"Fix Anyway", nil];
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#pragma mark - Import / Export
|
||||
|
@ -13,6 +13,16 @@
|
||||
NSDateFormatter *_dateFormatter;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:
|
||||
^(NSNotification *note) {
|
||||
[self updateData];
|
||||
}];
|
||||
|
||||
[super viewDidLoad];
|
||||
}
|
||||
|
||||
- (void)addElementNamed:(NSString *)siteName completion:(void (^)(BOOL success))completion {
|
||||
|
||||
if (![siteName length]) {
|
||||
@ -105,8 +115,12 @@
|
||||
- (void)updateData {
|
||||
|
||||
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForThread];
|
||||
if (!activeUser)
|
||||
if (!activeUser) {
|
||||
_fetchedResultsControllerByLastUsed = nil;
|
||||
_fetchedResultsControllerByUses = nil;
|
||||
[self.tableView reloadData];
|
||||
return;
|
||||
}
|
||||
|
||||
// Build predicate.
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"user == %@", activeUser];
|
||||
|
@ -63,11 +63,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
|
||||
|
||||
[self.delegate didSelectElement:nil];
|
||||
}
|
||||
|
||||
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
|
||||
|
||||
controller.searchBar.text = @"";
|
||||
|
@ -77,8 +77,8 @@
|
||||
self.alertBody.text = nil;
|
||||
self.toolTipEditIcon.hidden = YES;
|
||||
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:UIApplicationDidEnterBackgroundNotification object:self queue:nil usingBlock:^(NSNotification *note) {
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:self queue:nil usingBlock:
|
||||
^(NSNotification *note) {
|
||||
self.suppressOutdatedAlert = NO;
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:MPElementUpdatedNotification object:nil queue:nil usingBlock:
|
||||
@ -107,6 +107,11 @@
|
||||
[self.navigationController popToRootViewControllerAnimated:animated];
|
||||
}];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:
|
||||
^(NSNotification *note) {
|
||||
if (!self.activeElementForThread)
|
||||
[self didSelectElement:nil];
|
||||
}];
|
||||
|
||||
[super viewDidLoad];
|
||||
}
|
||||
@ -584,14 +589,18 @@
|
||||
|
||||
- (void)changeActiveElementWithoutWarningDo:(BOOL (^)(MPElementEntity *activeElement))task; {
|
||||
|
||||
MPElementEntity *activeElement = [self activeElementForThread];
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPElementEntity *activeElement = [self activeElementInContext:context];
|
||||
if (!activeElement)
|
||||
return;
|
||||
|
||||
NSString *oldPassword = [activeElement.content description];
|
||||
if (!task( activeElement ))
|
||||
return;
|
||||
NSString *newPassword = [activeElement.content description];
|
||||
|
||||
// Save.
|
||||
[activeElement.managedObjectContext saveToStore];
|
||||
[context saveToStore];
|
||||
|
||||
// Update the UI.
|
||||
dispatch_async( dispatch_get_main_queue(), ^{
|
||||
@ -606,15 +615,17 @@
|
||||
@"Your old password was:\n"
|
||||
@"%@", activeElement.name, oldPassword )];
|
||||
} );
|
||||
}];
|
||||
}
|
||||
|
||||
- (MPElementEntity *)activeElementForThread {
|
||||
|
||||
if (!_activeElementOID)
|
||||
return nil;
|
||||
return [self activeElementInContext:[MPiOSAppDelegate managedObjectContextForThreadIfReady]];
|
||||
}
|
||||
|
||||
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
|
||||
if (!moc)
|
||||
- (MPElementEntity *)activeElementInContext:(NSManagedObjectContext *)moc {
|
||||
|
||||
if (!_activeElementOID)
|
||||
return nil;
|
||||
|
||||
NSError *error;
|
||||
@ -801,11 +812,11 @@
|
||||
|
||||
- (void)didSelectElement:(MPElementEntity *)element {
|
||||
|
||||
if (!element)
|
||||
return;
|
||||
|
||||
inf(@"Selected: %@", element.name);
|
||||
_activeElementOID = element.objectID;
|
||||
[self closeAlert];
|
||||
|
||||
if (element) {
|
||||
[self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement) {
|
||||
if ([activeElement use] == 1)
|
||||
[self showAlertWithTitle:@"New Site" message:
|
||||
@ -817,9 +828,6 @@
|
||||
return YES;
|
||||
}];
|
||||
|
||||
MPElementEntity *activeElement = [self activeElementForThread];
|
||||
inf(@"Selected: %@", activeElement.name);
|
||||
|
||||
if (![[MPiOSConfig get].typeTipShown boolValue])
|
||||
[UIView animateWithDuration:0.5f animations:^{
|
||||
self.typeTipContainer.alpha = 1;
|
||||
@ -835,13 +843,14 @@
|
||||
} );
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
[self.searchDisplayController setActive:NO animated:YES];
|
||||
self.searchDisplayController.searchBar.text = activeElement.name;
|
||||
self.searchDisplayController.searchBar.text = element.name;
|
||||
|
||||
MPCheckpoint( MPCheckpointUseType, @{
|
||||
@"type" : activeElement.typeName,
|
||||
@"version" : @(activeElement.version)
|
||||
@"type" : element.typeName,
|
||||
@"version" : @(element.version)
|
||||
} );
|
||||
|
||||
[self updateAnimated:YES];
|
||||
|
@ -14,8 +14,6 @@
|
||||
#import "MPAppDelegate_Key.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
|
||||
#import "GPPSignIn.h"
|
||||
|
||||
@interface MPUnlockViewController()
|
||||
|
||||
@property(strong, nonatomic) NSMutableDictionary *avatarToUserOID;
|
||||
@ -163,21 +161,21 @@
|
||||
[self initializeWordLabel:wordLabel];
|
||||
} recurse:NO];
|
||||
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:
|
||||
^(NSNotification *note) {
|
||||
[self updateUsers];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:UbiquityManagedStoreDidImportChangesNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidImportChangesNotification object:nil queue:nil
|
||||
usingBlock:^(NSNotification *note) {
|
||||
[self updateUsers];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:nil usingBlock:
|
||||
^(NSNotification *note) {
|
||||
[self emergencyCloseAnimated:NO];
|
||||
self.uiContainer.alpha = 0;
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:nil usingBlock:
|
||||
^(NSNotification *note) {
|
||||
[self updateLayoutAnimated:NO allowScroll:NO completion:nil];
|
||||
[UIView animateWithDuration:1 animations:^{
|
||||
self.uiContainer.alpha = 1;
|
||||
@ -356,16 +354,16 @@
|
||||
|
||||
- (void)didToggleUserSelection {
|
||||
|
||||
MPUserEntity *selectedUser = self.selectedUser;
|
||||
MPUserEntity *selectedUser = [self selectedUserForThread];
|
||||
if (!selectedUser)
|
||||
[self.passwordField resignFirstResponder];
|
||||
else if ([[MPiOSAppDelegate get] signInAsUser:selectedUser usingMasterPassword:nil]) {
|
||||
else if ([[MPiOSAppDelegate get] signInAsUser:selectedUser inContext:selectedUser.managedObjectContext usingMasterPassword:nil]) {
|
||||
[self performSegueWithIdentifier:@"MP_Unlock" sender:self];
|
||||
return;
|
||||
}
|
||||
|
||||
[self updateLayoutAnimated:YES allowScroll:YES completion:^(BOOL finished) {
|
||||
if (self.selectedUser)
|
||||
if ([self selectedUserForThread])
|
||||
[self.passwordField becomeFirstResponder];
|
||||
}];
|
||||
}
|
||||
@ -479,7 +477,8 @@
|
||||
}
|
||||
|
||||
// Lay out password entry and user selection views.
|
||||
if (self.selectedUser && !self.passwordView.alpha) {
|
||||
MPUserEntity *selectedUser = [self selectedUserForThread];
|
||||
if (selectedUser && !self.passwordView.alpha) {
|
||||
// User was just selected.
|
||||
self.passwordView.alpha = 1;
|
||||
self.avatarsView.center = CGPointMake( 160, 180 );
|
||||
@ -489,7 +488,7 @@
|
||||
self.oldNameLabel.center = self.nameLabel.center;
|
||||
self.avatarShadowColor = [UIColor whiteColor];
|
||||
}
|
||||
else if (!self.selectedUser && self.passwordView.alpha == 1) {
|
||||
else if (!selectedUser && self.passwordView.alpha == 1) {
|
||||
// User was just deselected.
|
||||
self.passwordField.text = nil;
|
||||
self.passwordView.alpha = 0;
|
||||
@ -502,7 +501,7 @@
|
||||
}
|
||||
|
||||
// Lay out the word wall.
|
||||
if (!self.selectedUser || self.selectedUser.keyID) {
|
||||
if (!selectedUser || selectedUser.keyID) {
|
||||
self.passwordFieldLabel.text = @"Enter your master password:";
|
||||
|
||||
self.wordWall.alpha = 0;
|
||||
@ -530,8 +529,8 @@
|
||||
}
|
||||
|
||||
// Lay out user targeting.
|
||||
MPUserEntity *targetedUser = self.selectedUser;
|
||||
UIButton *selectedAvatar = [self avatarForUser:self.selectedUser];
|
||||
UIButton *selectedAvatar = [self avatarForUser:selectedUser];
|
||||
MPUserEntity *targetedUser = selectedUser;
|
||||
UIButton *targetedAvatar = selectedAvatar;
|
||||
if (!targetedAvatar) {
|
||||
targetedAvatar = [self findTargetedAvatar];
|
||||
@ -547,7 +546,7 @@
|
||||
BOOL isTargeted = avatar == targetedAvatar;
|
||||
|
||||
avatar.userInteractionEnabled = isTargeted;
|
||||
avatar.alpha = isTargeted? 1: self.selectedUser? 0.1: 0.4;
|
||||
avatar.alpha = isTargeted? 1: [self selectedUserForThread]? 0.1: 0.4;
|
||||
|
||||
[self updateAvatarShadowColor:avatar isTargeted:isTargeted];
|
||||
} recurse:NO];
|
||||
@ -612,14 +611,15 @@
|
||||
|
||||
- (void)tryMasterPassword {
|
||||
|
||||
if (!self.selectedUser)
|
||||
if (![self selectedUserForThread])
|
||||
// No user selected, can't try sign-in.
|
||||
return;
|
||||
|
||||
[self setSpinnerActive:YES];
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
BOOL unlocked = [[MPiOSAppDelegate get] signInAsUser:self.selectedUser usingMasterPassword:self.passwordField.text];
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||
BOOL unlocked = [[MPiOSAppDelegate get] signInAsUser:[self selectedUserInContext:moc] inContext:moc
|
||||
usingMasterPassword:self.passwordField.text];
|
||||
|
||||
dispatch_async( dispatch_get_main_queue(), ^{
|
||||
if (unlocked)
|
||||
@ -632,7 +632,7 @@
|
||||
[self setSpinnerActive:NO];
|
||||
}
|
||||
} );
|
||||
} );
|
||||
}];
|
||||
}
|
||||
|
||||
- (UIButton *)findTargetedAvatar {
|
||||
@ -697,9 +697,9 @@
|
||||
self.spinner.alpha = active? 1: 0;
|
||||
|
||||
if (active)
|
||||
[self avatarForUser:self.selectedUser].backgroundColor = [UIColor clearColor];
|
||||
[self avatarForUser:[self selectedUserForThread]].backgroundColor = [UIColor clearColor];
|
||||
else
|
||||
[self avatarForUser:self.selectedUser].backgroundColor = self.avatarTemplate.backgroundColor;
|
||||
[self avatarForUser:[self selectedUserForThread]].backgroundColor = self.avatarTemplate.backgroundColor;
|
||||
}];
|
||||
});
|
||||
}
|
||||
@ -779,7 +779,7 @@
|
||||
else if (textField == self.passwordField) {
|
||||
[self setSpinnerActive:YES];
|
||||
|
||||
if (self.selectedUser.keyID)
|
||||
if ([self selectedUserForThread].keyID)
|
||||
[self tryMasterPassword];
|
||||
|
||||
else
|
||||
@ -980,7 +980,7 @@
|
||||
if (sender.state != UIGestureRecognizerStateBegan)
|
||||
return;
|
||||
|
||||
if (self.selectedUser)
|
||||
if ([self selectedUserForThread])
|
||||
return;
|
||||
|
||||
MPUserEntity *targetedUser = [self userForAvatar:[self findTargetedAvatar]];
|
||||
@ -1118,15 +1118,16 @@
|
||||
|
||||
#pragma mark - Core Data
|
||||
|
||||
- (MPUserEntity *)selectedUser {
|
||||
- (MPUserEntity *)selectedUserForThread {
|
||||
|
||||
return [self selectedUserInContext:[MPiOSAppDelegate managedObjectContextForThreadIfReady]];
|
||||
}
|
||||
|
||||
- (MPUserEntity *)selectedUserInContext:(NSManagedObjectContext *)moc {
|
||||
|
||||
if (!_selectedUserOID)
|
||||
return nil;
|
||||
|
||||
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
|
||||
if (!moc)
|
||||
return nil;
|
||||
|
||||
NSError *error;
|
||||
MPUserEntity *selectedUser = (MPUserEntity *)[moc existingObjectWithID:_selectedUserOID error:&error];
|
||||
if (!selectedUser)
|
||||
|
Loading…
Reference in New Issue
Block a user