From fa57b8817b33edd68ce22208843082d455f6febb Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Tue, 13 May 2014 07:27:11 -0400 Subject: [PATCH] Bugfixes with regards to swiping password types. --- External/Pearl | 2 +- MasterPassword/ObjC/MPAppDelegate_Shared.h | 2 +- MasterPassword/ObjC/MPAppDelegate_Shared.m | 11 +- .../ObjC/iOS/MPElementListController.m | 7 +- .../ObjC/iOS/MPPasswordElementCell.h | 2 + .../ObjC/iOS/MPPasswordElementCell.m | 10 +- MasterPassword/ObjC/iOS/MPPasswordLargeCell.m | 13 +- MasterPassword/ObjC/iOS/MPPasswordTypesCell.h | 1 + MasterPassword/ObjC/iOS/MPPasswordTypesCell.m | 138 ++++++++-------- .../ObjC/iOS/MPPasswordsViewController.h | 1 + .../ObjC/iOS/MPPasswordsViewController.m | 149 ++---------------- .../ObjC/iOS/MPUsersViewController.m | 42 +++-- MasterPassword/ObjC/iOS/Storyboard.storyboard | 12 +- 13 files changed, 135 insertions(+), 255 deletions(-) diff --git a/External/Pearl b/External/Pearl index 7ed74fd2..d37640cc 160000 --- a/External/Pearl +++ b/External/Pearl @@ -1 +1 @@ -Subproject commit 7ed74fd245f47339903ab2faeb3f36754155452c +Subproject commit d37640cc0133e56b1d58baf7ed44ce69dcb20f2f diff --git a/MasterPassword/ObjC/MPAppDelegate_Shared.h b/MasterPassword/ObjC/MPAppDelegate_Shared.h index 8c1cce86..96e2f0ff 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Shared.h +++ b/MasterPassword/ObjC/MPAppDelegate_Shared.h @@ -21,7 +21,7 @@ + (instancetype)get; - (MPUserEntity *)activeUserForMainThread; -- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)moc; +- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)context; - (void)setActiveUser:(MPUserEntity *)activeUser; @end diff --git a/MasterPassword/ObjC/MPAppDelegate_Shared.m b/MasterPassword/ObjC/MPAppDelegate_Shared.m index 9121422a..cdfcfe02 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Shared.m +++ b/MasterPassword/ObjC/MPAppDelegate_Shared.m @@ -28,18 +28,15 @@ return [self activeUserInContext:[MPAppDelegate_Shared managedObjectContextForMainThreadIfReady]]; } -- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)moc { +- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)context { NSManagedObjectID *activeUserOID = self.activeUserOID; - if (!activeUserOID || !moc) + if (!activeUserOID || !context) return nil; - NSError *error; - MPUserEntity *activeUser = (MPUserEntity *)[moc existingObjectWithID:activeUserOID error:&error]; - if (!activeUser) { + MPUserEntity *activeUser = [MPUserEntity existingObjectWithID:activeUserOID inContext:context]; + if (!activeUser) [self signOutAnimated:YES]; - err(@"Failed to retrieve active user: %@", error); - } return activeUser; } diff --git a/MasterPassword/ObjC/iOS/MPElementListController.m b/MasterPassword/ObjC/iOS/MPElementListController.m index 444748ba..edf657c6 100644 --- a/MasterPassword/ObjC/iOS/MPElementListController.m +++ b/MasterPassword/ObjC/iOS/MPElementListController.m @@ -260,12 +260,9 @@ forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { NSManagedObjectID *elementOID = [self elementForTableIndexPath:indexPath].objectID; [MPiOSAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) { - NSError *error = nil; - MPElementEntity *element = (MPElementEntity *)[context existingObjectWithID:elementOID error:&error]; - if (!element) { - err(@"Failed to retrieve element to delete: %@", error); + MPElementEntity *element = [MPElementEntity existingObjectWithID:elementOID inContext:context]; + if (!element) return; - } inf(@"Deleting element: %@", element.name); [context deleteObject:element]; diff --git a/MasterPassword/ObjC/iOS/MPPasswordElementCell.h b/MasterPassword/ObjC/iOS/MPPasswordElementCell.h index c1781f4c..ef90205e 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordElementCell.h +++ b/MasterPassword/ObjC/iOS/MPPasswordElementCell.h @@ -18,10 +18,12 @@ #import #import "MPPasswordCell.h" +#import "MPPasswordsViewController.h" @interface MPPasswordElementCell : MPPasswordCell @property(nonatomic, copy) NSString *transientSite; +@property(nonatomic, weak) MPPasswordsViewController *passwordsViewController; - (MPElementEntity *)mainElement; - (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context; diff --git a/MasterPassword/ObjC/iOS/MPPasswordElementCell.m b/MasterPassword/ObjC/iOS/MPPasswordElementCell.m index a5db1cd4..a2904b40 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordElementCell.m +++ b/MasterPassword/ObjC/iOS/MPPasswordElementCell.m @@ -69,15 +69,7 @@ - (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context { - if (!_elementOID) - return nil; - - NSError *error = nil; - MPElementEntity *element = _elementOID? (MPElementEntity *)[context existingObjectWithID:_elementOID error:&error]: nil; - if (_elementOID && !element) - err(@"Failed to load element: %@", error); - - return element; + return [MPElementEntity existingObjectWithID:_elementOID inContext:context]; } - (void)reloadData { diff --git a/MasterPassword/ObjC/iOS/MPPasswordLargeCell.m b/MasterPassword/ObjC/iOS/MPPasswordLargeCell.m index a21cc458..ec0f9f04 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordLargeCell.m +++ b/MasterPassword/ObjC/iOS/MPPasswordLargeCell.m @@ -92,8 +92,17 @@ [super reloadWithElement:mainElement]; + if (!mainElement) { + self.loginButton.alpha = 0; + self.nameLabel.text = @""; + self.contentField.text = @""; + return; + } + + self.nameLabel.alpha = 1; self.loginButton.alpha = 1; - self.typeLabel.text = [mainElement.algorithm nameOfType:self.type]; + if (self.type != (MPElementType)NSNotFound) + self.typeLabel.text = [mainElement.algorithm nameOfType:self.type]; if (mainElement.requiresExplicitMigration) self.upgradeButton.alpha = 1; @@ -166,6 +175,8 @@ [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { MPElementEntity *element = [[MPPasswordElementCell findAsSuperviewOf:self] elementInContext:context]; + if (!element) + return; switch (self.contentFieldMode) { case MPContentFieldModePassword: diff --git a/MasterPassword/ObjC/iOS/MPPasswordTypesCell.h b/MasterPassword/ObjC/iOS/MPPasswordTypesCell.h index 2a0ff63a..626dd495 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordTypesCell.h +++ b/MasterPassword/ObjC/iOS/MPPasswordTypesCell.h @@ -20,6 +20,7 @@ #import "MPCell.h" #import "MPPasswordCell.h" #import "MPPasswordElementCell.h" +#import "MPPasswordsViewController.h" @interface MPPasswordTypesCell : MPPasswordElementCell diff --git a/MasterPassword/ObjC/iOS/MPPasswordTypesCell.m b/MasterPassword/ObjC/iOS/MPPasswordTypesCell.m index 03e45553..f3dd3b79 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordTypesCell.m +++ b/MasterPassword/ObjC/iOS/MPPasswordTypesCell.m @@ -68,9 +68,7 @@ [super applyLayoutAttributes:layoutAttributes]; [self.contentCollectionView.collectionViewLayout invalidateLayout]; - if (self.activeType) - [self.contentCollectionView scrollToItemAtIndexPath:[self contentIndexPathForType:self.activeType] - atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO]; + [self scrollToActiveType]; } - (void)reloadWithTransientSite:(NSString *)siteName { @@ -104,7 +102,10 @@ if (!self.algorithm) dbg_return_tr( 0, @, @(section) ); - dbg_return_tr( [[self.algorithm allTypes] count] + 1, @, @(section) ); + if (self.transientSite) + dbg_return_tr( [[self.algorithm allTypes] count], @, @(section) ); + + dbg_return_tr( [[self.algorithm allTypes] count] + 1 /* Delete */, @, @(section) ); } - (MPPasswordLargeCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { @@ -125,16 +126,13 @@ NSString *newSiteName = self.transientSite; if (newSiteName) { + [[UIResponder findFirstResponder] resignFirstResponder]; [PearlAlert showAlertWithTitle:@"Create Site" message:strf( @"Do you want to create a new site named:\n%@", newSiteName ) viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { if (buttonIndex == [alert cancelButtonIndex]) { // Cancel -// NSIndexPath *indexPath_ = [collectionView indexPathForCell:cell]; -// [collectionView selectItemAtIndexPath:indexPath animated:NO -// scrollPosition:UICollectionViewScrollPositionCenteredHorizontally]; -// [collectionView deselectItemAtIndexPath:indexPath animated:YES]; for (NSIndexPath *selectedIndexPath in [collectionView indexPathsForSelectedItems]) [collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES]; return; @@ -142,70 +140,56 @@ // Create [[MPiOSAppDelegate get] addElementNamed:newSiteName completion:^(MPElementEntity *element) { + [self copyContentOfElement:element]; PearlMainQueue( ^{ - [PearlOverlay showTemporaryOverlayWithTitle:strf( @"Added %@", newSiteName ) dismissAfter:2]; - PearlMainQueueAfter( 0.2f, ^{ -// NSIndexPath *indexPath_ = [collectionView indexPathForCell:cell]; -// [collectionView selectItemAtIndexPath:indexPath animated:NO -// scrollPosition:UICollectionViewScrollPositionCenteredHorizontally]; -// [collectionView deselectItemAtIndexPath:indexPath animated:YES]; - for (NSIndexPath *selectedIndexPath in [collectionView indexPathsForSelectedItems]) - [collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES]; - } ); + [self.passwordsViewController updatePasswords]; } ); }]; } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil]; return; } - MPElementEntity *element = [self mainElement]; - if (!element) { -// [collectionView selectItemAtIndexPath:indexPath animated:NO -// scrollPosition:UICollectionViewScrollPositionCenteredHorizontally]; -// [collectionView deselectItemAtIndexPath:indexPath animated:YES]; - for (NSIndexPath *selectedIndexPath in [collectionView indexPathsForSelectedItems]) - [collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES]; - return; - } + [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { + BOOL used = NO; + MPElementEntity *element = [self elementInContext:context]; + if (!element) + wrn(@"No element to use for: %@", self); + else if (indexPath.item == 0) { + [context deleteObject:element]; + [context saveToStore]; + } else + used = [self copyContentOfElement:element]; + + PearlMainQueueAfter( 0.2f, ^{ + for (NSIndexPath *selectedIndexPath in [collectionView indexPathsForSelectedItems]) + [collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES]; + + if (used) + [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context_) { + [[self elementInContext:context_] use]; + [context_ saveToStore]; + }]; + } ); + }]; +} + +- (BOOL)copyContentOfElement:(MPElementEntity *)element { inf( @"Copying password for: %@", element.name ); MPCheckpoint( MPCheckpointCopyToPasteboard, @{ - @"type" : NilToNSNull( element.typeName ), - @"version" : @(element.version), - @"emergency" : @NO - } ); - - [element resolveContentUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) { - if (![result length]) { - PearlMainQueue( ^{ -// NSIndexPath *indexPath_ = [collectionView indexPathForCell:cell]; -// [collectionView selectItemAtIndexPath:indexPath animated:NO -// scrollPosition:UICollectionViewScrollPositionCenteredHorizontally]; -// [collectionView deselectItemAtIndexPath:indexPath animated:YES]; - for (NSIndexPath *selectedIndexPath in [collectionView indexPathsForSelectedItems]) - [collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES]; + @"type" : NilToNSNull( element.typeName ), + @"version" : @(element.version), + @"emergency" : @NO } ); - return; - } + NSString *result = [element resolveContentUsingKey:[MPAppDelegate_Shared get].key]; + if ([result length]) { [UIPasteboard generalPasteboard].string = result; - PearlMainQueue( ^{ - [PearlOverlay showTemporaryOverlayWithTitle:@"Password Copied" dismissAfter:2]; - PearlMainQueueAfter( 0.2f, ^{ -// NSIndexPath *indexPath_ = [collectionView indexPathForCell:cell]; -// [collectionView selectItemAtIndexPath:indexPath animated:NO -// scrollPosition:UICollectionViewScrollPositionCenteredHorizontally]; -// [collectionView deselectItemAtIndexPath:indexPath animated:YES]; - for (NSIndexPath *selectedIndexPath in [collectionView indexPathsForSelectedItems]) - [collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES]; + [PearlOverlay showTemporaryOverlayWithTitle:@"Password Copied" dismissAfter:2]; + return YES; + } - [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - [[self elementInContext:context] use]; - [context saveToStore]; - }]; - } ); - } ); - }]; + return NO; } #pragma mark - UIScrollViewDelegate @@ -235,8 +219,18 @@ #pragma mark - Private +- (void)scrollToActiveType { + + if (self.activeType && self.activeType != (MPElementType)NSNotFound) + [self.contentCollectionView scrollToItemAtIndexPath:[self contentIndexPathForType:self.activeType] + atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO]; +} + - (MPElementType)typeForContentIndexPath:(NSIndexPath *)indexPath { + if (self.transientSite) + return [[self.algorithm allTypesStartingWith:MPElementTypeGeneratedPIN][indexPath.item] unsignedIntegerValue]; + if (indexPath.item == 0) return (MPElementType)NSNotFound; @@ -247,20 +241,29 @@ NSArray *types = [self.algorithm allTypesStartingWith:MPElementTypeGeneratedPIN]; for (NSInteger t = 0; t < [types count]; ++t) - if ([types[t] unsignedIntegerValue] == type) - return [NSIndexPath indexPathForItem:t + 1 inSection:0]; + if ([types[t] unsignedIntegerValue] == type) { + if (self.transientSite) + return [NSIndexPath indexPathForItem:t inSection:0]; + else + return [NSIndexPath indexPathForItem:t + 1 inSection:0]; + } - Throw(@"Unsupported type: %d", type); + Throw(@"Unsupported type: %lud", (long)type); } - (void)saveContentType { - if (self.transientSite) - return; - CGPoint centerPoint = CGPointFromCGRectCenter( self.contentCollectionView.bounds ); NSIndexPath *centerIndexPath = [self.contentCollectionView indexPathForItemAtPoint:centerPoint]; - self.activeType = [self typeForContentIndexPath:centerIndexPath]; + MPElementType type = [self typeForContentIndexPath:centerIndexPath]; + if (type == ((MPElementType)NSNotFound)) + // Active cell is not a type cell. + return; + + self.activeType = type; + + if (self.transientSite) + return; [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { MPPasswordLargeCell *cell = (MPPasswordLargeCell *)[self.contentCollectionView cellForItemAtIndexPath:centerIndexPath]; @@ -270,7 +273,7 @@ } MPElementEntity *element = [self elementInContext:context]; - if (element.type == cell.type) + if (!element || element.type == cell.type) // Nothing changed. return; @@ -284,8 +287,7 @@ _activeType = activeType; - [self.contentCollectionView scrollToItemAtIndexPath:[self contentIndexPathForType:activeType] - atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO]; + [self scrollToActiveType]; } - (void)setSelected:(BOOL)selected { diff --git a/MasterPassword/ObjC/iOS/MPPasswordsViewController.h b/MasterPassword/ObjC/iOS/MPPasswordsViewController.h index e6302c40..eacf3ba8 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordsViewController.h +++ b/MasterPassword/ObjC/iOS/MPPasswordsViewController.h @@ -36,6 +36,7 @@ @property(nonatomic, readonly) MPCoachmark *coachmark; - (void)setActive:(BOOL)active animated:(BOOL)animated completion:(void (^)(BOOL finished))completion; +- (void)updatePasswords; - (IBAction)dismissPopdown:(id)sender; - (IBAction)signOut:(id)sender; diff --git a/MasterPassword/ObjC/iOS/MPPasswordsViewController.m b/MasterPassword/ObjC/iOS/MPPasswordsViewController.m index d656f038..2bdf9076 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordsViewController.m +++ b/MasterPassword/ObjC/iOS/MPPasswordsViewController.m @@ -40,7 +40,6 @@ __weak UITapGestureRecognizer *_passwordsDismissRecognizer; NSFetchedResultsController *_fetchedResultsController; BOOL _exactMatch; - NSMutableDictionary *_fetchedUpdates; UIColor *_backgroundColor; UIColor *_darkenedBackgroundColor; __weak UIViewController *_popdownVC; @@ -52,7 +51,6 @@ [super viewDidLoad]; - _fetchedUpdates = [NSMutableDictionary dictionaryWithCapacity:4]; _backgroundColor = self.passwordCollectionView.backgroundColor; _darkenedBackgroundColor = [_backgroundColor colorWithAlphaComponent:0.6f]; _coachmark = [MPCoachmark coachmarkForClass:[self class] version:0]; @@ -118,11 +116,7 @@ referenceSizeForHeaderInSection:(NSInteger)section { if (collectionView == self.passwordCollectionView) { UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout; CGFloat itemWidth = UIEdgeInsetsInsetRect(self.passwordCollectionView.bounds, layout.sectionInset).size.width; - - if (indexPath.item < 3 || indexPath.item >= ((id)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects) - return CGSizeMake( itemWidth, 100 ); - - return CGSizeMake( (itemWidth - layout.minimumInteritemSpacing) / 2, 44 ); + return CGSizeMake( itemWidth, 100 ); } Throw(@"Unexpected collection view: %@", collectionView); @@ -155,14 +149,12 @@ referenceSizeForHeaderInSection:(NSInteger)section { MPPasswordElementCell *cell; if (indexPath.item < ((id)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects) { MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath]; - if (indexPath.item < 3) - cell = [MPPasswordTypesCell dequeueCellForElement:element fromCollectionView:collectionView atIndexPath:indexPath]; - else - cell = [MPPasswordSmallCell dequeueCellForElement:element fromCollectionView:collectionView atIndexPath:indexPath]; + cell = [MPPasswordTypesCell dequeueCellForElement:element fromCollectionView:collectionView atIndexPath:indexPath]; } else - // New Site. + // New Site. cell = [MPPasswordTypesCell dequeueCellForTransientSite:self.query fromCollectionView:collectionView atIndexPath:indexPath]; + cell.passwordsViewController = self; [UIView setAnimationsEnabled:YES]; dbg_return(cell, indexPath); @@ -171,140 +163,30 @@ referenceSizeForHeaderInSection:(NSInteger)section { Throw(@"Unexpected collection view: %@", collectionView); } -#pragma mark - NSFetchedResultsControllerDelegate +- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind + atIndexPath:(NSIndexPath *)indexPath { -- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { - - if (controller == _fetchedResultsController) { - dbg(@"controllerWillChangeContent"); - NSAssert(![_fetchedUpdates count], @"Didn't finish a previous change update?"); - if ([_fetchedUpdates count]) { - [_fetchedUpdates removeAllObjects]; - [self.passwordCollectionView reloadData]; - } - } + return [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"MPPasswordHeader" forIndexPath:indexPath]; } +#pragma mark - NSFetchedResultsControllerDelegate + - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { if (controller == _fetchedResultsController) { - NSMutableArray *updatesForType = _fetchedUpdates[@(type)]; - if (!updatesForType) - _fetchedUpdates[@(type)] = updatesForType = [NSMutableArray new]; - - [updatesForType addObject:@{ - @"object" : NilToNSNull(anObject), - @"indexPath" : NilToNSNull(indexPath), - @"newIndexPath" : NilToNSNull(newIndexPath) - }]; - switch (type) { - case NSFetchedResultsChangeInsert: - dbg(@"didChangeObject: insert: %@", [updatesForType lastObject]); - break; - case NSFetchedResultsChangeDelete: - dbg(@"didChangeObject: delete: %@", [updatesForType lastObject]); - break; - case NSFetchedResultsChangeMove: - dbg(@"didChangeObject: move: %@", [updatesForType lastObject]); - break; - case NSFetchedResultsChangeUpdate: - dbg(@"didChangeObject: update: %@", [updatesForType lastObject]); - break; - } + [self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section]]; + [self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section]]; } } - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { - if (controller == _fetchedResultsController) { - NSMutableArray *updatesForType = _fetchedUpdates[@(type << 3)]; - if (!updatesForType) - _fetchedUpdates[@(type << 3)] = updatesForType = [NSMutableArray new]; - - [updatesForType addObject:@{ - @"sectionInfo" : NilToNSNull(sectionInfo), - @"index" : @(sectionIndex) - }]; - switch (type) { - case NSFetchedResultsChangeInsert: - dbg(@"didChangeSection: insert: %@", [updatesForType lastObject]); - break; - case NSFetchedResultsChangeDelete: - dbg(@"didChangeSection: delete: %@", [updatesForType lastObject]); - break; - case NSFetchedResultsChangeMove: - dbg(@"didChangeSection: move: %@", [updatesForType lastObject]); - break; - case NSFetchedResultsChangeUpdate: - dbg(@"didChangeSection: update: %@", [updatesForType lastObject]); - break; - } - } + if (controller == _fetchedResultsController) + [self.passwordCollectionView reloadData]; } -- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { - - if (controller == _fetchedResultsController && [_fetchedUpdates count]) { - [self.passwordCollectionView performBatchUpdates:^{ - [_fetchedUpdates enumerateKeysAndObjectsUsingBlock:^(NSNumber *typeNumber, NSArray *updates, BOOL *stop) { - BOOL updateIsSection = NO; - NSFetchedResultsChangeType type = [typeNumber unsignedIntegerValue]; - if (type >= 1 << 3) { - updateIsSection = YES; - type = type >> 3; - } - - switch (type) { - case NSFetchedResultsChangeInsert: - if (updateIsSection) { - for (NSDictionary *update in updates) { - dbg(@"insertSections:%@", update[@"index"]); - [self.passwordCollectionView insertSections: - [NSIndexSet indexSetWithIndex:[update[@"index"] unsignedIntegerValue]]]; - } - } - else { - dbg(@"insertItemsAtIndexPaths:%@", [updates valueForKeyPath:@"@unionOfObjects.newIndexPath"]); - [self.passwordCollectionView insertItemsAtIndexPaths:[updates valueForKeyPath:@"@unionOfObjects.newIndexPath"]]; - } - break; - case NSFetchedResultsChangeDelete: - if (updateIsSection) { - for (NSDictionary *update in updates) { - dbg(@"deleteSections:%@", update[@"index"]); - [self.passwordCollectionView deleteSections: - [NSIndexSet indexSetWithIndex:[update[@"index"] unsignedIntegerValue]]]; - } - } - else { - dbg(@"deleteItemsAtIndexPaths:%@", [updates valueForKeyPath:@"@unionOfObjects.indexPath"]); - [self.passwordCollectionView deleteItemsAtIndexPaths:[updates valueForKeyPath:@"@unionOfObjects.indexPath"]]; - } - break; - case NSFetchedResultsChangeMove: - NSAssert(!updateIsSection, @"Move not supported for sections"); - for (NSDictionary *update in updates) { - dbg(@"moveItemAtIndexPath:%@ toIndexPath:%@", update[@"indexPath"], update[@"newIndexPath"]); - [self.passwordCollectionView moveItemAtIndexPath:update[@"indexPath"] toIndexPath:update[@"newIndexPath"]]; - } - break; - case NSFetchedResultsChangeUpdate: - NSAssert(!updateIsSection, @"Update not supported for sections"); - dbg(@"reloadItemsAtIndexPaths:%@", [updates valueForKeyPath:@"@unionOfObjects.indexPath"]); - [self.passwordCollectionView reloadItemsAtIndexPaths:[updates valueForKeyPath:@"@unionOfObjects.indexPath"]]; - break; - } - }]; - } completion:nil]; - [_fetchedUpdates removeAllObjects]; - } -} - - -#pragma mark - UIScrollViewDelegate - #pragma mark - UISearchBarDelegate - (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar { @@ -319,13 +201,10 @@ referenceSizeForHeaderInSection:(NSInteger)section { - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar { - if (searchBar == self.passwordsSearchBar) { -// _passwordsDismissRecognizer = [self.view dismissKeyboardForField:self.passwordsSearchBar onTouchForced:NO]; - + if (searchBar == self.passwordsSearchBar) [UIView animateWithDuration:0.3f animations:^{ self.passwordCollectionView.backgroundColor = _darkenedBackgroundColor; }]; - } } - (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar { diff --git a/MasterPassword/ObjC/iOS/MPUsersViewController.m b/MasterPassword/ObjC/iOS/MPUsersViewController.m index 23cc8e71..9e0a9d3a 100644 --- a/MasterPassword/ObjC/iOS/MPUsersViewController.m +++ b/MasterPassword/ObjC/iOS/MPUsersViewController.m @@ -360,28 +360,31 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { if (buttonIndex == [sheet destructiveButtonIndex]) { // Delete User [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - NSManagedObject *user_ = [context existingObjectWithID:userID error:NULL]; - if (user_) { - [context deleteObject:user_]; - [context saveToStore]; - } + MPUserEntity *user_ = [MPUserEntity existingObjectWithID:userID inContext:context]; + if (!user_) + return; + + [context deleteObject:user_]; + [context saveToStore]; }]; return; } if (buttonIndex == [sheet firstOtherButtonIndex]) - // Reset Password + // Reset Password [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - MPUserEntity *user_ = (MPUserEntity *)[context existingObjectWithID:userID error:NULL]; - if (user_) - [[MPiOSAppDelegate get] changeMasterPasswordFor:user_ saveInContext:context didResetBlock:^{ - PearlMainQueue( ^{ - NSIndexPath *avatarIndexPath = [self.avatarCollectionView indexPathForCell:avatarCell]; - [self.avatarCollectionView selectItemAtIndexPath:avatarIndexPath animated:NO - scrollPosition:UICollectionViewScrollPositionNone]; - [self collectionView:self.avatarCollectionView didSelectItemAtIndexPath:avatarIndexPath]; - } ); - }]; + MPUserEntity *user_ = [MPUserEntity existingObjectWithID:userID inContext:context]; + if (!user_) + return; + + [[MPiOSAppDelegate get] changeMasterPasswordFor:user_ saveInContext:context didResetBlock:^{ + PearlMainQueue( ^{ + NSIndexPath *avatarIndexPath = [self.avatarCollectionView indexPathForCell:avatarCell]; + [self.avatarCollectionView selectItemAtIndexPath:avatarIndexPath animated:NO + scrollPosition:UICollectionViewScrollPositionNone]; + [self collectionView:self.avatarCollectionView didSelectItemAtIndexPath:avatarIndexPath]; + } ); + }]; }]; } cancelTitle:[PearlStrings get].commonButtonCancel destructiveTitle:@"Delete User" otherTitles:@"Reset Password", nil]; @@ -477,12 +480,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { if ((*isNew = indexPath.item >= [self.userIDs count])) return nil; - NSError *error = nil; - MPUserEntity *user = (MPUserEntity *)[context existingObjectWithID:self.userIDs[indexPath.item] error:&error]; - if (error) - wrn(@"Failed to load user into context: %@", error); - - return user; + return [MPUserEntity existingObjectWithID:self.userIDs[indexPath.item] inContext:context]; } - (void)updateAvatars { diff --git a/MasterPassword/ObjC/iOS/Storyboard.storyboard b/MasterPassword/ObjC/iOS/Storyboard.storyboard index 7d890ad5..8f3daff4 100644 --- a/MasterPassword/ObjC/iOS/Storyboard.storyboard +++ b/MasterPassword/ObjC/iOS/Storyboard.storyboard @@ -19,7 +19,7 @@ - + @@ -27,7 +27,7 @@ - + @@ -122,7 +122,7 @@ - - - + @@ -1307,7 +1307,7 @@ - +