2014-03-20 00:09:25 +00:00
|
|
|
/**
|
2014-07-21 03:54:32 +00:00
|
|
|
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
|
|
*
|
|
|
|
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
|
|
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
|
|
*
|
|
|
|
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
|
|
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
|
|
*/
|
2014-03-20 00:09:25 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// MPPasswordsViewController.h
|
|
|
|
// MPPasswordsViewController
|
|
|
|
//
|
|
|
|
// Created by lhunath on 2014-03-08.
|
|
|
|
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "MPPasswordsViewController.h"
|
|
|
|
#import "MPiOSAppDelegate.h"
|
|
|
|
#import "MPAppDelegate_Store.h"
|
2014-04-20 15:09:49 +00:00
|
|
|
#import "MPPopdownSegue.h"
|
2014-04-22 03:35:29 +00:00
|
|
|
#import "MPAppDelegate_Key.h"
|
2014-07-21 03:54:32 +00:00
|
|
|
#import "MPPasswordCell.h"
|
2014-09-22 02:45:21 +00:00
|
|
|
#import "MPAnswersViewController.h"
|
2014-03-20 00:09:25 +00:00
|
|
|
|
2014-09-29 02:15:55 +00:00
|
|
|
typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
|
|
|
MPPasswordsBadNameTip = 1 << 0,
|
|
|
|
};
|
|
|
|
|
2014-03-20 11:15:37 +00:00
|
|
|
@interface MPPasswordsViewController()<NSFetchedResultsControllerDelegate>
|
2014-03-20 00:09:25 +00:00
|
|
|
|
2014-04-07 03:34:18 +00:00
|
|
|
@property(nonatomic, strong) IBOutlet UINavigationBar *navigationBar;
|
2014-03-20 11:15:37 +00:00
|
|
|
@property(nonatomic, readonly) NSString *query;
|
2014-04-07 03:34:18 +00:00
|
|
|
|
2014-03-20 00:09:25 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation MPPasswordsViewController {
|
2014-03-20 20:49:33 +00:00
|
|
|
__weak UITapGestureRecognizer *_passwordsDismissRecognizer;
|
2014-04-07 03:34:18 +00:00
|
|
|
NSFetchedResultsController *_fetchedResultsController;
|
|
|
|
UIColor *_backgroundColor;
|
|
|
|
UIColor *_darkenedBackgroundColor;
|
2014-04-20 15:09:49 +00:00
|
|
|
__weak UIViewController *_popdownVC;
|
2014-08-22 01:51:47 +00:00
|
|
|
BOOL _showTransientItem;
|
|
|
|
NSUInteger _transientItem;
|
2014-09-29 02:15:55 +00:00
|
|
|
NSCharacterSet *_siteNameAcceptableCharactersSet;
|
2014-03-20 00:09:25 +00:00
|
|
|
}
|
|
|
|
|
2014-04-20 15:09:49 +00:00
|
|
|
#pragma mark - Life
|
|
|
|
|
2014-03-20 00:09:25 +00:00
|
|
|
- (void)viewDidLoad {
|
|
|
|
|
|
|
|
[super viewDidLoad];
|
|
|
|
|
2014-09-29 02:15:55 +00:00
|
|
|
NSMutableCharacterSet *siteNameAcceptableCharactersSet = [[NSCharacterSet alphanumericCharacterSet] mutableCopy];
|
|
|
|
[siteNameAcceptableCharactersSet formIntersectionWithCharacterSet:[[NSCharacterSet uppercaseLetterCharacterSet] invertedSet]];
|
|
|
|
[siteNameAcceptableCharactersSet addCharactersInString:@"@.-+~&_;:/"];
|
|
|
|
_siteNameAcceptableCharactersSet = siteNameAcceptableCharactersSet;
|
|
|
|
|
2014-04-25 01:43:47 +00:00
|
|
|
_backgroundColor = self.passwordCollectionView.backgroundColor;
|
|
|
|
_darkenedBackgroundColor = [_backgroundColor colorWithAlphaComponent:0.6f];
|
2014-08-22 01:51:47 +00:00
|
|
|
_transientItem = NSNotFound;
|
2014-04-07 03:34:18 +00:00
|
|
|
|
2014-03-20 00:09:25 +00:00
|
|
|
self.view.backgroundColor = [UIColor clearColor];
|
2014-04-07 03:34:18 +00:00
|
|
|
[self.passwordCollectionView automaticallyAdjustInsetsForKeyboard];
|
2014-10-01 11:35:57 +00:00
|
|
|
self.passwordsSearchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
|
|
|
if ([self.passwordsSearchBar respondsToSelector:@selector(keyboardAppearance)])
|
|
|
|
self.passwordsSearchBar.keyboardAppearance = UIKeyboardAppearanceDark;
|
|
|
|
else
|
|
|
|
[self.passwordsSearchBar enumerateViews:^(UIView *subview, BOOL *stop, BOOL *recurse) {
|
|
|
|
if ([subview isKindOfClass:[UITextField class]])
|
|
|
|
((UITextField *)subview).keyboardAppearance = UIKeyboardAppearanceDark;
|
|
|
|
} recurse:YES];
|
2014-03-20 00:09:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)viewWillAppear:(BOOL)animated {
|
|
|
|
|
|
|
|
[super viewWillAppear:animated];
|
|
|
|
|
|
|
|
[self registerObservers];
|
2014-06-06 01:43:06 +00:00
|
|
|
[self updateConfigKey:nil];
|
2014-04-12 18:43:41 +00:00
|
|
|
[self updatePasswords];
|
2014-03-20 00:09:25 +00:00
|
|
|
}
|
|
|
|
|
2014-09-24 11:58:23 +00:00
|
|
|
- (void)viewDidAppear:(BOOL)animated {
|
|
|
|
|
|
|
|
[super viewDidAppear:animated];
|
|
|
|
|
|
|
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
|
|
|
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:context];
|
|
|
|
if (![MPAlgorithmDefault tryMigrateUser:activeUser inContext:context])
|
|
|
|
[PearlOverlay showTemporaryOverlayWithTitle:@"Some Sites Need Upgrade" dismissAfter:2];
|
|
|
|
[context saveToStore];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2014-03-20 00:09:25 +00:00
|
|
|
- (void)viewWillDisappear:(BOOL)animated {
|
|
|
|
|
|
|
|
[super viewWillDisappear:animated];
|
|
|
|
|
2014-09-27 05:27:05 +00:00
|
|
|
PearlRemoveNotificationObservers();
|
2014-03-20 00:09:25 +00:00
|
|
|
}
|
|
|
|
|
2014-04-20 15:09:49 +00:00
|
|
|
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
|
|
|
|
|
|
|
if ([segue.identifier isEqualToString:@"popdown"])
|
|
|
|
_popdownVC = segue.destinationViewController;
|
2014-09-22 02:45:21 +00:00
|
|
|
if ([segue.identifier isEqualToString:@"answers"])
|
|
|
|
((MPAnswersViewController *)segue.destinationViewController).site =
|
|
|
|
[[MPPasswordCell findAsSuperviewOf:sender] siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
2014-04-20 15:09:49 +00:00
|
|
|
}
|
|
|
|
|
2014-05-10 13:18:46 +00:00
|
|
|
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
|
|
|
|
|
|
|
|
[self.passwordCollectionView.collectionViewLayout invalidateLayout];
|
|
|
|
|
|
|
|
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
|
|
|
|
}
|
|
|
|
|
2014-04-07 03:34:18 +00:00
|
|
|
#pragma mark - UICollectionViewDelegateFlowLayout
|
2014-03-20 00:09:25 +00:00
|
|
|
|
2014-04-07 03:34:18 +00:00
|
|
|
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout
|
|
|
|
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
|
2014-03-20 00:09:25 +00:00
|
|
|
|
2014-07-21 03:54:32 +00:00
|
|
|
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout;
|
2014-09-12 21:13:33 +00:00
|
|
|
CGFloat itemWidth = UIEdgeInsetsInsetRect( collectionView.bounds, layout.sectionInset ).size.width;
|
2014-07-21 03:54:32 +00:00
|
|
|
return CGSizeMake( itemWidth, 100 );
|
2014-03-20 00:09:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - UICollectionViewDataSource
|
|
|
|
|
2014-03-20 11:15:37 +00:00
|
|
|
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
|
|
|
|
|
2014-07-21 03:54:32 +00:00
|
|
|
return [self.fetchedResultsController.sections count];
|
2014-03-20 11:15:37 +00:00
|
|
|
}
|
|
|
|
|
2014-03-20 00:09:25 +00:00
|
|
|
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
|
|
|
|
2014-09-15 05:00:23 +00:00
|
|
|
if (![MPiOSAppDelegate get].activeUserOID || !_fetchedResultsController)
|
2014-08-22 01:51:47 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
NSUInteger objects = ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[section]).numberOfObjects;
|
|
|
|
_transientItem = _showTransientItem? objects: NSNotFound;
|
|
|
|
return objects + (_showTransientItem? 1: 0);
|
2014-03-20 00:09:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
|
|
|
|
2014-07-21 03:54:32 +00:00
|
|
|
MPPasswordCell *cell = [MPPasswordCell dequeueCellFromCollectionView:collectionView indexPath:indexPath];
|
|
|
|
if (indexPath.item < ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects)
|
2014-09-21 15:47:53 +00:00
|
|
|
[cell setSite:[self.fetchedResultsController objectAtIndexPath:indexPath] animated:NO];
|
2014-07-21 03:54:32 +00:00
|
|
|
else
|
|
|
|
[cell setTransientSite:self.query animated:NO];
|
2014-03-20 00:09:25 +00:00
|
|
|
|
2014-07-21 03:54:32 +00:00
|
|
|
return cell;
|
2014-03-20 00:09:25 +00:00
|
|
|
}
|
|
|
|
|
2014-07-21 03:54:32 +00:00
|
|
|
#pragma mark - UIScrollDelegate
|
|
|
|
|
|
|
|
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
|
|
|
|
|
|
|
|
if (scrollView == self.passwordCollectionView)
|
|
|
|
for (MPPasswordCell *cell in [self.passwordCollectionView visibleCells])
|
|
|
|
[cell setMode:MPPasswordCellModePassword animated:YES];
|
|
|
|
}
|
|
|
|
|
2014-05-13 11:27:11 +00:00
|
|
|
#pragma mark - NSFetchedResultsControllerDelegate
|
|
|
|
|
2014-04-07 03:34:18 +00:00
|
|
|
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath
|
|
|
|
forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
|
|
|
|
|
|
|
|
if (controller == _fetchedResultsController) {
|
2014-08-22 01:51:47 +00:00
|
|
|
[self.passwordCollectionView performBatchUpdates:^{
|
|
|
|
[self fetchedItemsDidUpdate];
|
|
|
|
switch (type) {
|
|
|
|
case NSFetchedResultsChangeInsert:
|
|
|
|
[self.passwordCollectionView insertItemsAtIndexPaths:@[ newIndexPath ]];
|
|
|
|
break;
|
|
|
|
case NSFetchedResultsChangeDelete:
|
|
|
|
[self.passwordCollectionView deleteItemsAtIndexPaths:@[ indexPath ]];
|
|
|
|
break;
|
|
|
|
case NSFetchedResultsChangeMove:
|
|
|
|
[self.passwordCollectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath];
|
|
|
|
break;
|
|
|
|
case NSFetchedResultsChangeUpdate:
|
|
|
|
[self.passwordCollectionView reloadItemsAtIndexPaths:@[ indexPath ]];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} completion:nil];
|
2014-04-07 03:34:18 +00:00
|
|
|
}
|
2014-03-20 00:09:25 +00:00
|
|
|
}
|
|
|
|
|
2014-04-07 03:34:18 +00:00
|
|
|
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo
|
|
|
|
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
|
|
|
|
|
2014-05-13 11:27:11 +00:00
|
|
|
if (controller == _fetchedResultsController)
|
|
|
|
[self.passwordCollectionView reloadData];
|
2014-03-20 00:09:25 +00:00
|
|
|
}
|
|
|
|
|
2014-03-20 11:15:37 +00:00
|
|
|
#pragma mark - UISearchBarDelegate
|
|
|
|
|
2014-04-13 17:04:18 +00:00
|
|
|
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
|
|
|
|
|
|
|
|
if (searchBar == self.passwordsSearchBar) {
|
|
|
|
searchBar.text = nil;
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2014-03-20 11:15:37 +00:00
|
|
|
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
|
|
|
|
|
2014-06-15 23:51:03 +00:00
|
|
|
if (searchBar == self.passwordsSearchBar) {
|
|
|
|
[self.passwordsSearchBar setShowsCancelButton:YES animated:YES];
|
2014-04-07 03:34:18 +00:00
|
|
|
[UIView animateWithDuration:0.3f animations:^{
|
2014-04-25 01:43:47 +00:00
|
|
|
self.passwordCollectionView.backgroundColor = _darkenedBackgroundColor;
|
2014-04-07 03:34:18 +00:00
|
|
|
}];
|
2014-06-15 23:51:03 +00:00
|
|
|
}
|
2014-03-20 11:15:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
|
|
|
|
|
|
|
|
if (searchBar == self.passwordsSearchBar) {
|
2014-06-15 23:51:03 +00:00
|
|
|
[self.passwordsSearchBar setShowsCancelButton:NO animated:YES];
|
2014-03-20 20:49:33 +00:00
|
|
|
if (_passwordsDismissRecognizer)
|
|
|
|
[self.view removeGestureRecognizer:_passwordsDismissRecognizer];
|
2014-03-20 11:15:37 +00:00
|
|
|
|
2014-09-11 04:26:01 +00:00
|
|
|
[self updatePasswords];
|
2014-03-20 11:15:37 +00:00
|
|
|
[UIView animateWithDuration:0.3f animations:^{
|
2014-04-25 01:43:47 +00:00
|
|
|
self.passwordCollectionView.backgroundColor = _backgroundColor;
|
2014-03-20 11:15:37 +00:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-15 23:51:03 +00:00
|
|
|
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
|
|
|
|
|
2014-09-11 04:26:01 +00:00
|
|
|
searchBar.text = nil;
|
2014-06-15 23:51:03 +00:00
|
|
|
[searchBar resignFirstResponder];
|
|
|
|
}
|
|
|
|
|
2014-03-20 20:49:33 +00:00
|
|
|
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
|
|
|
|
|
|
|
|
[searchBar resignFirstResponder];
|
|
|
|
}
|
|
|
|
|
2014-03-20 11:15:37 +00:00
|
|
|
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
|
|
|
|
|
2014-09-29 02:15:55 +00:00
|
|
|
if (searchBar == self.passwordsSearchBar) {
|
|
|
|
if ([self.query length] && [[self.query stringByTrimmingCharactersInSet:_siteNameAcceptableCharactersSet] length])
|
|
|
|
[self showTips:MPPasswordsBadNameTip];
|
|
|
|
|
2014-03-20 11:15:37 +00:00
|
|
|
[self updatePasswords];
|
2014-09-29 02:15:55 +00:00
|
|
|
}
|
2014-03-20 11:15:37 +00:00
|
|
|
}
|
|
|
|
|
2014-03-20 00:09:25 +00:00
|
|
|
#pragma mark - Private
|
|
|
|
|
2014-09-29 02:15:55 +00:00
|
|
|
- (void)showTips:(MPPasswordsTips)showTips {
|
|
|
|
|
|
|
|
[UIView animateWithDuration:0.3f animations:^{
|
|
|
|
if (showTips & MPPasswordsBadNameTip)
|
|
|
|
self.badNameTipContainer.alpha = 1;
|
|
|
|
} completion:^(BOOL finished) {
|
|
|
|
if (finished)
|
|
|
|
PearlMainQueueAfter( 5, ^{
|
|
|
|
[UIView animateWithDuration:0.3f animations:^{
|
|
|
|
if (showTips & MPPasswordsBadNameTip)
|
|
|
|
self.badNameTipContainer.alpha = 0;
|
|
|
|
}];
|
|
|
|
} );
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2014-08-22 01:51:47 +00:00
|
|
|
- (void)fetchedItemsDidUpdate {
|
|
|
|
|
|
|
|
NSString *query = self.query;
|
|
|
|
_showTransientItem = [query length] > 0;
|
|
|
|
NSUInteger objects = ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[0]).numberOfObjects;
|
|
|
|
if (_showTransientItem && objects == 1 &&
|
|
|
|
[[[self.fetchedResultsController.fetchedObjects firstObject] name] isEqualToString:query])
|
|
|
|
_showTransientItem = NO;
|
|
|
|
if ([self.passwordCollectionView numberOfSections] > 0) {
|
2014-08-22 04:56:02 +00:00
|
|
|
if (!_showTransientItem && _transientItem != NSNotFound)
|
2014-08-22 01:51:47 +00:00
|
|
|
[self.passwordCollectionView deleteItemsAtIndexPaths:
|
|
|
|
@[ [NSIndexPath indexPathForItem:_transientItem inSection:0] ]];
|
2014-08-22 04:56:02 +00:00
|
|
|
else if (_showTransientItem && _transientItem == NSNotFound)
|
2014-08-22 01:51:47 +00:00
|
|
|
[self.passwordCollectionView insertItemsAtIndexPaths:
|
|
|
|
@[ [NSIndexPath indexPathForItem:objects inSection:0] ]];
|
2014-08-22 04:56:02 +00:00
|
|
|
else if (_transientItem != NSNotFound)
|
2014-08-22 01:51:47 +00:00
|
|
|
[self.passwordCollectionView reloadItemsAtIndexPaths:
|
|
|
|
@[ [NSIndexPath indexPathForItem:_transientItem inSection:0] ]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-20 00:09:25 +00:00
|
|
|
- (void)registerObservers {
|
|
|
|
|
2014-09-27 05:27:05 +00:00
|
|
|
PearlRemoveNotificationObservers();
|
|
|
|
PearlAddNotificationObserver( UIApplicationDidEnterBackgroundNotification, nil, [NSOperationQueue mainQueue],
|
|
|
|
^(MPPasswordsViewController *self, NSNotification *note) {
|
|
|
|
self.passwordSelectionContainer.alpha = 0;
|
|
|
|
} );
|
|
|
|
PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue],
|
|
|
|
^(MPPasswordsViewController *self, NSNotification *note) {
|
|
|
|
[self updatePasswords];
|
|
|
|
} );
|
|
|
|
PearlAddNotificationObserver( UIApplicationDidBecomeActiveNotification, nil, [NSOperationQueue mainQueue],
|
|
|
|
^(MPPasswordsViewController *self, NSNotification *note) {
|
|
|
|
[UIView animateWithDuration:0.7f animations:^{
|
|
|
|
self.passwordSelectionContainer.alpha = 1;
|
|
|
|
}];
|
|
|
|
} );
|
|
|
|
PearlAddNotificationObserver( MPSignedOutNotification, nil, [NSOperationQueue mainQueue],
|
|
|
|
^(MPPasswordsViewController *self, NSNotification *note) {
|
|
|
|
_fetchedResultsController = nil;
|
|
|
|
self.passwordsSearchBar.text = nil;
|
|
|
|
[self.passwordCollectionView reloadData];
|
|
|
|
} );
|
|
|
|
PearlAddNotificationObserver( MPCheckConfigNotification, nil, [NSOperationQueue mainQueue],
|
|
|
|
^(MPPasswordsViewController *self, NSNotification *note) {
|
|
|
|
[self updateConfigKey:note.object];
|
|
|
|
} );
|
|
|
|
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, nil, nil,
|
|
|
|
^(MPPasswordsViewController *self, NSNotification *note) {
|
|
|
|
self->_fetchedResultsController = nil;
|
|
|
|
[self.passwordCollectionView reloadData];
|
|
|
|
} );
|
|
|
|
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresDidChangeNotification, nil, nil,
|
|
|
|
^(MPPasswordsViewController *self, NSNotification *note) {
|
|
|
|
[self updatePasswords];
|
|
|
|
[self registerObservers];
|
|
|
|
} );
|
2014-03-20 00:09:25 +00:00
|
|
|
|
2014-03-20 11:15:37 +00:00
|
|
|
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
|
2014-09-27 05:27:05 +00:00
|
|
|
if (mainContext)
|
|
|
|
PearlAddNotificationObserver( NSManagedObjectContextDidSaveNotification, mainContext, nil,
|
|
|
|
^(MPPasswordsViewController *self, NSNotification *note) {
|
|
|
|
if (![[MPiOSAppDelegate get] activeUserInContext:note.object])
|
2014-08-23 05:39:47 +00:00
|
|
|
[[MPiOSAppDelegate get] signOutAnimated:YES];
|
2014-09-27 05:27:05 +00:00
|
|
|
} );
|
2014-03-20 00:09:25 +00:00
|
|
|
}
|
|
|
|
|
2014-06-06 01:43:06 +00:00
|
|
|
- (void)updateConfigKey:(NSString *)key {
|
2014-04-26 18:03:44 +00:00
|
|
|
|
2014-07-21 03:54:32 +00:00
|
|
|
if (!key || [key isEqualToString:NSStringFromSelector( @selector( dictationSearch ) )])
|
2014-06-06 01:43:06 +00:00
|
|
|
self.passwordsSearchBar.keyboardType = [[MPiOSConfig get].dictationSearch boolValue]? UIKeyboardTypeDefault: UIKeyboardTypeURL;
|
2014-07-21 03:54:32 +00:00
|
|
|
if (!key || [key isEqualToString:NSStringFromSelector( @selector( hidePasswords ) )])
|
2014-09-16 04:34:22 +00:00
|
|
|
[self.passwordCollectionView reloadData];
|
2014-04-26 18:03:44 +00:00
|
|
|
}
|
|
|
|
|
2014-03-20 11:15:37 +00:00
|
|
|
- (void)updatePasswords {
|
|
|
|
|
2014-04-07 03:34:18 +00:00
|
|
|
NSString *query = self.query;
|
|
|
|
NSManagedObjectID *activeUserOID = [MPiOSAppDelegate get].activeUserOID;
|
2014-04-12 18:43:41 +00:00
|
|
|
if (!activeUserOID) {
|
2014-04-22 03:35:29 +00:00
|
|
|
PearlMainQueue( ^{
|
2014-08-23 05:39:47 +00:00
|
|
|
self.passwordsSearchBar.text = nil;
|
2014-04-22 03:35:29 +00:00
|
|
|
[self.passwordCollectionView reloadData];
|
|
|
|
[self.passwordCollectionView setContentOffset:CGPointMake( 0, -self.passwordCollectionView.contentInset.top ) animated:YES];
|
|
|
|
} );
|
2014-04-07 03:34:18 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-03-20 11:15:37 +00:00
|
|
|
|
2014-04-07 03:34:18 +00:00
|
|
|
[self.fetchedResultsController.managedObjectContext performBlock:^{
|
2014-09-03 01:40:53 +00:00
|
|
|
NSArray *oldSectionInfos = [self.fetchedResultsController sections];
|
|
|
|
NSMutableArray *oldSections = [[NSMutableArray alloc] initWithCapacity:[oldSectionInfos count]];
|
|
|
|
for (id<NSFetchedResultsSectionInfo> sectionInfo in oldSectionInfos)
|
|
|
|
[oldSections addObject:[sectionInfo.objects copy]];
|
2014-08-22 01:51:47 +00:00
|
|
|
|
2014-03-20 11:15:37 +00:00
|
|
|
NSError *error = nil;
|
2014-04-12 18:43:41 +00:00
|
|
|
self.fetchedResultsController.fetchRequest.predicate =
|
|
|
|
[query length]?
|
|
|
|
[NSPredicate predicateWithFormat:@"user == %@ AND name BEGINSWITH[cd] %@", activeUserOID, query]:
|
|
|
|
[NSPredicate predicateWithFormat:@"user == %@", activeUserOID];
|
2014-03-20 11:15:37 +00:00
|
|
|
if (![self.fetchedResultsController performFetch:&error])
|
2014-09-22 03:28:50 +00:00
|
|
|
err( @"Couldn't fetch sites: %@", [error fullDescription] );
|
2014-03-20 11:15:37 +00:00
|
|
|
|
2014-08-22 01:51:47 +00:00
|
|
|
[self.passwordCollectionView performBatchUpdates:^{
|
|
|
|
[self fetchedItemsDidUpdate];
|
2014-04-07 03:34:18 +00:00
|
|
|
|
2014-09-18 00:50:27 +00:00
|
|
|
NSInteger fromSections = self.passwordCollectionView.numberOfSections;
|
2014-08-22 01:51:47 +00:00
|
|
|
NSInteger toSections = [self numberOfSectionsInCollectionView:self.passwordCollectionView];
|
|
|
|
for (NSInteger section = 0; section < MAX( toSections, fromSections ); ++section) {
|
2014-08-22 04:56:02 +00:00
|
|
|
if (section >= fromSections)
|
2014-08-22 01:51:47 +00:00
|
|
|
[self.passwordCollectionView insertSections:[NSIndexSet indexSetWithIndex:section]];
|
2014-08-22 04:56:02 +00:00
|
|
|
else if (section >= toSections)
|
2014-08-22 01:51:47 +00:00
|
|
|
[self.passwordCollectionView deleteSections:[NSIndexSet indexSetWithIndex:section]];
|
2014-09-18 00:50:27 +00:00
|
|
|
else if (section < [oldSections count])
|
2014-09-03 01:40:53 +00:00
|
|
|
[self.passwordCollectionView reloadItemsFromArray:oldSections[section]
|
2014-08-22 01:51:47 +00:00
|
|
|
toArray:[[self.fetchedResultsController sections][section] objects]
|
|
|
|
inSection:section];
|
2014-09-18 00:50:27 +00:00
|
|
|
else
|
|
|
|
[self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:section]];
|
2014-08-22 01:51:47 +00:00
|
|
|
}
|
|
|
|
} completion:^(BOOL finished) {
|
|
|
|
if (finished)
|
|
|
|
[self.passwordCollectionView setContentOffset:CGPointMake( 0, -self.passwordCollectionView.contentInset.top )
|
|
|
|
animated:YES];
|
|
|
|
}];
|
2014-03-20 11:15:37 +00:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2014-03-20 00:09:25 +00:00
|
|
|
#pragma mark - Properties
|
|
|
|
|
2014-03-20 11:15:37 +00:00
|
|
|
- (NSString *)query {
|
|
|
|
|
|
|
|
return [self.passwordsSearchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSFetchedResultsController *)fetchedResultsController {
|
|
|
|
|
2014-04-07 03:34:18 +00:00
|
|
|
if (!_fetchedResultsController) {
|
2014-09-15 05:00:23 +00:00
|
|
|
_showTransientItem = NO;
|
2014-04-07 03:34:18 +00:00
|
|
|
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
|
2014-09-21 14:39:09 +00:00
|
|
|
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
2014-03-20 11:15:37 +00:00
|
|
|
fetchRequest.sortDescriptors = @[
|
2014-07-21 03:54:32 +00:00
|
|
|
[[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
|
2014-03-20 11:15:37 +00:00
|
|
|
];
|
|
|
|
fetchRequest.fetchBatchSize = 10;
|
|
|
|
_fetchedResultsController = [[NSFetchedResultsController alloc]
|
2014-04-07 03:34:18 +00:00
|
|
|
initWithFetchRequest:fetchRequest managedObjectContext:mainContext sectionNameKeyPath:nil cacheName:nil];
|
2014-03-20 11:15:37 +00:00
|
|
|
_fetchedResultsController.delegate = self;
|
|
|
|
}];
|
2014-09-27 05:27:05 +00:00
|
|
|
[self registerObservers];
|
2014-04-07 03:34:18 +00:00
|
|
|
}
|
2014-03-20 11:15:37 +00:00
|
|
|
|
|
|
|
return _fetchedResultsController;
|
|
|
|
}
|
|
|
|
|
2014-03-20 00:09:25 +00:00
|
|
|
- (void)setActive:(BOOL)active {
|
|
|
|
|
2014-04-12 18:43:41 +00:00
|
|
|
[self setActive:active animated:NO completion:nil];
|
2014-03-20 00:09:25 +00:00
|
|
|
}
|
|
|
|
|
2014-07-21 03:54:32 +00:00
|
|
|
- (void)setActive:(BOOL)active animated:(BOOL)animated completion:(void ( ^ )(BOOL finished))completion {
|
2014-03-20 00:09:25 +00:00
|
|
|
|
|
|
|
_active = active;
|
|
|
|
|
2014-04-12 18:43:41 +00:00
|
|
|
[UIView animateWithDuration:animated? 0.4f: 0 animations:^{
|
2014-08-22 02:27:47 +00:00
|
|
|
[self.navigationBarToTopConstraint updatePriority:active? 1: UILayoutPriorityDefaultHigh];
|
|
|
|
[self.passwordsToBottomConstraint updatePriority:active? 1: UILayoutPriorityDefaultHigh];
|
|
|
|
[self.view layoutIfNeeded];
|
2014-04-12 18:43:41 +00:00
|
|
|
} completion:completion];
|
2014-03-20 00:09:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - Actions
|
|
|
|
|
2014-04-20 15:09:49 +00:00
|
|
|
- (IBAction)dismissPopdown:(id)sender {
|
2014-04-12 18:43:41 +00:00
|
|
|
|
2014-04-20 15:09:49 +00:00
|
|
|
if (_popdownVC)
|
|
|
|
[[[MPPopdownSegue alloc] initWithIdentifier:@"unwind-popdown" source:_popdownVC destination:self] perform];
|
|
|
|
else
|
|
|
|
self.popdownToTopConstraint.priority = UILayoutPriorityDefaultHigh;
|
2014-04-12 18:43:41 +00:00
|
|
|
}
|
|
|
|
|
2014-03-20 00:09:25 +00:00
|
|
|
@end
|