2
0

Search & AllSites improvements, sort by recency, rememberLogin setup.

[UPDATED]   Simplified AllSites VC in IB (removed the unused navVC).
[UPDATED]   Outdated sites are now shown in the AllSites VC.
[ADDED]     An update-all-sites button when showing outdated sites in the AllSites VC.
[REMOVED]   Search scopes.
[ADDED]     Sorting elements by recency and usage count now.
[FIXED]     Initial positioning of help container.
[ADDED]     Setup VC for rememberLogin.
This commit is contained in:
Maarten Billemont 2013-04-15 00:04:16 -04:00
parent 7cf2c7f5c6
commit ee93412dd1
17 changed files with 451 additions and 246 deletions

2
External/Pearl vendored

@ -1 +1 @@
Subproject commit 99c73fd3b37b4d2621548f5ae366c163231a346d
Subproject commit 0d30693440fd520715f22789d33d5551b3b681c4

View File

@ -20,6 +20,7 @@
#import "MPElementListController.h"
@interface MPElementListAllViewController : MPElementListController
@property (weak, nonatomic) IBOutlet UINavigationBar *navigationBar;
- (IBAction)close:(id)sender;
- (IBAction)add:(id)sender;

View File

@ -16,9 +16,32 @@
//
#import "MPElementListAllViewController.h"
#import "MPAppDelegate.h"
#import "MPAppDelegate_Store.h"
#define MPElementUpgradeOldContentKey @"MPElementUpgradeOldContentKey"
#define MPElementUpgradeNewContentKey @"MPElementUpgradeNewContentKey"
@implementation MPElementListAllViewController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if ([self.filter isEqualToString:MPElementListFilterNone]) {
self.navigationBar.topItem.title = @"All Sites";
self.navigationBar.topItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(add:)];
}
else if ([self.filter isEqualToString:MPElementListFilterOutdated]) {
self.navigationBar.topItem.title = @"Outdated";
self.navigationBar.topItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithTitle:@"Upgrade All" style:UIBarButtonItemStyleBordered target:self action:@selector(upgradeAll:)];
}
[self updateData];
}
- (IBAction)close:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
@ -40,11 +63,106 @@
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonOkay, nil];
}
- (void)viewWillAppear:(BOOL)animated {
- (IBAction)upgradeAll:(id)sender {
[super viewWillAppear:animated];
[PearlAlert showAlertWithTitle:@"Upgrading All Sites"
message:@"You are about to upgrade all outdated sites. This will cause passwords to change. "
@"Afterwards, you can get an overview of the changes."
viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:
^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
[self updateData];
PearlAlert *activity = [PearlAlert showActivityWithTitle:@"Upgrading Sites"];
[self performUpgradeAllWithCompletion:^(BOOL success, NSDictionary *changes) {
dispatch_async( dispatch_get_main_queue(), ^{
[self showUpgradeChanges:changes];
[activity cancelAlert];
} );
}];
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonContinue, nil];
}
- (void)performUpgradeAllWithCompletion:(void(^)(BOOL success, NSDictionary *changes))completion {
[MPAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
fetchRequest.fetchBatchSize = 20;
NSError *error = nil;
NSArray *elements = [moc executeFetchRequest:fetchRequest error:&error];
if (!elements) {
err(@"Failed to fetch outdated sites for upgrade: %@", error);
completion(NO, nil);
return;
}
NSMutableDictionary *elementChanges = [NSMutableDictionary dictionaryWithCapacity:[elements count]];
for (MPElementEntity *element in elements) {
id oldContent = [element content];
[element migrateExplicitly:YES];
id newContent = [element content];
if (!(element.type & MPElementFeatureDevicePrivate) && (!oldContent || ![oldContent isEqual:newContent]))
[elementChanges setObject:@{
MPElementUpgradeOldContentKey : oldContent,
MPElementUpgradeNewContentKey : newContent,
} forKey:element.name];
}
completion(YES, elementChanges);
}];
}
- (void)showUpgradeChanges:(NSDictionary *)changes {
if (![changes count])
return;
NSMutableString *formattedChanges = [NSMutableString new];
for (NSString *changedElementName in changes) {
NSDictionary *elementChanges = [changes objectForKey:changedElementName];
id oldContent = [elementChanges objectForKey:MPElementUpgradeOldContentKey];
id newContent = [elementChanges objectForKey:MPElementUpgradeNewContentKey];
[formattedChanges appendFormat:@"%@: %@ -> %@\n", changedElementName, oldContent, newContent];
}
[PearlAlert showAlertWithTitle:@"Sites Upgraded"
message:PearlString( @"This upgrade has caused %d passwords to change.\n"
@"To make updating the actual passwords of these accounts easier, "
@"you can email a summary of these changes to yourself.", [changes count])
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
[PearlEMail sendEMailTo:nil subject:@"[Master Password] Upgrade Changes" body:formattedChanges];
} cancelTitle:@"Don't Email" otherTitles:@"Send Email", nil];
}
- (NSFetchedResultsController *)fetchedResultsControllerByUses {
return nil;
}
- (void)configureFetchRequest:(NSFetchRequest *)fetchRequest {
fetchRequest.fetchBatchSize = 10;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return nil;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return nil;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

View File

@ -1,28 +1,22 @@
//
// Created by lhunath on 2013-02-09.
//
// To change the template use AppCode | Preferences | File Templates.
//
#import <Foundation/Foundation.h>
#import "MPElementListDelegate.h"
typedef enum {
MPSearchScopeAll,
MPSearchScopeOutdated,
} MPSearchScope;
#define MPElementListFilterNone @"MPElementListFilterNone"
#define MPElementListFilterOutdated @"MPElementListFilterOutdated"
@interface MPElementListController : UITableViewController <NSFetchedResultsControllerDelegate>
@property (weak, nonatomic) IBOutlet id<MPElementListDelegate> delegate;
@property (readonly) NSFetchedResultsController *fetchedResultsController;
@property (strong, nonatomic) NSString *filter;
@property (readonly) NSFetchedResultsController *fetchedResultsControllerByUses;
@property (readonly) NSFetchedResultsController *fetchedResultsControllerByLastUsed;
@property (readonly) NSDateFormatter *dateFormatter;
- (void)updateData;
- (void)addElementNamed:(NSString *)siteName completion:(void (^)(BOOL success))completion;
- (void)configureCell:(UITableViewCell *)cell inTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath;
- (void)configureCell:(UITableViewCell *)cell inTableView:(UITableView *)tableView atTableIndexPath:(NSIndexPath *)indexPath;
- (void)customTableViewUpdates;
@end

View File

@ -1,10 +1,3 @@
//
// Created by lhunath on 2013-02-09.
//
// To change the template use AppCode | Preferences | File Templates.
//
#import "MPElementListController.h"
#import "MPAppDelegate_Store.h"
@ -15,7 +8,8 @@
@implementation MPElementListController {
NSFetchedResultsController *_fetchedResultsController;
NSFetchedResultsController *_fetchedResultsControllerByUses;
NSFetchedResultsController *_fetchedResultsControllerByLastUsed;
NSDateFormatter *_dateFormatter;
}
@ -57,23 +51,47 @@
}];
}
- (NSFetchedResultsController *)fetchedResultsController {
- (NSFetchedResultsController *)fetchedResultsControllerByLastUsed {
if (!_fetchedResultsController) {
NSAssert([[NSThread currentThread] isMainThread], @"The fetchedResultsController must run on the main thread.");
if (!_fetchedResultsControllerByLastUsed) {
NSAssert([[NSThread currentThread] isMainThread], @"The fetchedResultsController must be accessed from the main thread.");
NSManagedObjectContext *moc = [MPAppDelegate managedObjectContextForThreadIfReady];
if (!moc)
return nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])];
fetchRequest.sortDescriptors = @[[[NSSortDescriptor alloc] initWithKey:@"uses_" ascending:NO]];
fetchRequest.fetchBatchSize = 20;
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc
fetchRequest.sortDescriptors = @[[[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector(lastUsed) ) ascending:NO]];
[self configureFetchRequest:fetchRequest];
_fetchedResultsControllerByLastUsed = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc
sectionNameKeyPath:nil cacheName:nil];
_fetchedResultsController.delegate = self;
_fetchedResultsControllerByLastUsed.delegate = self;
}
return _fetchedResultsController;
return _fetchedResultsControllerByLastUsed;
}
- (NSFetchedResultsController *)fetchedResultsControllerByUses {
if (!_fetchedResultsControllerByUses) {
NSAssert([[NSThread currentThread] isMainThread], @"The fetchedResultsController must be accessed from the main thread.");
NSManagedObjectContext *moc = [MPAppDelegate managedObjectContextForThreadIfReady];
if (!moc)
return nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector(uses_) ) ascending:NO] ];
[self configureFetchRequest:fetchRequest];
_fetchedResultsControllerByUses = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc
sectionNameKeyPath:nil cacheName:nil];
_fetchedResultsControllerByUses.delegate = self;
}
return _fetchedResultsControllerByUses;
}
- (void)configureFetchRequest:(NSFetchRequest *)fetchRequest {
fetchRequest.fetchLimit = 5;
}
- (NSDateFormatter *)dateFormatter {
@ -92,34 +110,33 @@
// Build predicate.
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"user == %@", activeUser];
// Add query predicate.
UISearchBar *searchBar = self.searchDisplayController.searchBar;
if (searchBar) {
NSString *query = [searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if (!query)
return;
// Add query predicate.
predicate = [NSCompoundPredicate andPredicateWithSubpredicates:
@[predicate, [NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] %@", query]]];
// Add scope predicate.
switch ((MPSearchScope) searchBar.selectedScopeButtonIndex) {
case MPSearchScopeAll:
break;
case MPSearchScopeOutdated:
predicate = [NSCompoundPredicate andPredicateWithSubpredicates:
@[[NSPredicate predicateWithFormat:@"requiresExplicitMigration_ == YES"], predicate]];
break;
}
}
self.fetchedResultsController.fetchRequest.predicate = predicate;
// Add filter predicate.
if ([self.filter isEqualToString:MPElementListFilterOutdated])
predicate = [NSCompoundPredicate andPredicateWithSubpredicates:
@[[NSPredicate predicateWithFormat:@"requiresExplicitMigration_ == YES"], predicate]];
// Fetch
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
self.fetchedResultsControllerByLastUsed.fetchRequest.predicate = predicate;
self.fetchedResultsControllerByUses.fetchRequest.predicate = predicate;
if (![self.fetchedResultsControllerByLastUsed performFetch:&error])
err(@"Couldn't fetch elements: %@", error);
else
[self.tableView reloadData];
if (![self.fetchedResultsControllerByUses performFetch:&error])
err(@"Couldn't fetch elements: %@", error);
[self.tableView reloadData];
}
- (void)customTableViewUpdates {
@ -140,52 +157,32 @@
case NSFetchedResultsChangeInsert:
dbg(@"%@ -- NSFetchedResultsChangeInsert:%@", NSStringFromSelector(_cmd), anObject);
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:newIndexPath] ]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
dbg(@"%@ -- NSFetchedResultsChangeDelete:%@", NSStringFromSelector(_cmd), anObject);
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView deleteRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeUpdate:
dbg(@"%@ -- NSFetchedResultsChangeUpdate:%@", NSStringFromSelector(_cmd), anObject);
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView reloadRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeMove:
dbg(@"%@ -- NSFetchedResultsChangeMove:%@", NSStringFromSelector(_cmd), anObject);
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
[self.tableView deleteRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ]
withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
[self.tableView insertRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:newIndexPath] ]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch (type) {
case NSFetchedResultsChangeInsert:
dbg(@"%@ -- NSFetchedResultsChangeInsert:%d", NSStringFromSelector(_cmd), sectionIndex);
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
dbg(@"%@ -- NSFetchedResultsChangeDelete:%d", NSStringFromSelector(_cmd), sectionIndex);
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeMove:
case NSFetchedResultsChangeUpdate:
Throw(@"Invalid change type for section changes: %d", type);
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
dbg(@"%@ on %@", NSStringFromSelector(_cmd), [NSThread currentThread].name);
@ -195,16 +192,18 @@
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSInteger integer = (NSInteger)[[self.fetchedResultsController sections] count];
dbg(@"%@ = %d", NSStringFromSelector(_cmd), integer);
return integer;
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger integer = (NSInteger)[[[self.fetchedResultsController sections] objectAtIndex:(unsigned)section] numberOfObjects];
dbg(@"%@%d = %d", NSStringFromSelector(_cmd), section, integer);
return integer;
if (section == 0)
return (NSInteger)[[[self.fetchedResultsControllerByLastUsed sections] lastObject] numberOfObjects];
if (section == 1)
return (NSInteger)[[[self.fetchedResultsControllerByUses sections] lastObject] numberOfObjects];
Throw(@"Unsupported section: %d", section);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
@ -213,50 +212,82 @@
if (!cell)
cell = (UITableViewCell *) [[UIViewController alloc] initWithNibName:@"MPElementListCellView" bundle:nil].view;
[self configureCell:cell inTableView:tableView atIndexPath:indexPath];
[self configureCell:cell inTableView:tableView atTableIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell inTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath {
- (void)configureCell:(UITableViewCell *)cell inTableView:(UITableView *)tableView atTableIndexPath:(NSIndexPath *)indexPath {
MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
MPElementEntity *element = [self elementForTableIndexPath:indexPath];
cell.textLabel.text = element.name;
cell.detailTextLabel.text = PearlString(@"%d views, last on %@: %@",
element.uses, [self.dateFormatter stringFromDate:element.lastUsed], [element.algorithm shortNameOfType:element.type]);
}
- (NSIndexPath *)tableIndexPathForFetchController:(NSFetchedResultsController *)fetchedResultsController indexPath:(NSIndexPath *)indexPath {
if (fetchedResultsController == self.fetchedResultsControllerByLastUsed)
return [NSIndexPath indexPathForRow:indexPath.row inSection:0];
if (fetchedResultsController == self.fetchedResultsControllerByUses)
return [NSIndexPath indexPathForRow:indexPath.row inSection:1];
Throw(@"Unknown fetched results controller: %@, for index path: %@", fetchedResultsController, indexPath);
}
- (NSIndexPath *)fetchedIndexPathForTableIndexPath:(NSIndexPath *)indexPath {
return [NSIndexPath indexPathForRow:indexPath.row inSection:0];
}
- (MPElementEntity *)elementForTableIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0)
return [self.fetchedResultsControllerByLastUsed objectAtIndexPath:[self fetchedIndexPathForTableIndexPath:indexPath]];
if (indexPath.section == 1)
return [self.fetchedResultsControllerByUses objectAtIndexPath:[self fetchedIndexPathForTableIndexPath:indexPath]];
Throw(@"Unsupported section: %d", indexPath.section);
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.delegate didSelectElement:[self.fetchedResultsController objectAtIndexPath:indexPath]];
[self.delegate didSelectElement:[self elementForTableIndexPath:indexPath]];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [[[self.fetchedResultsController sections] objectAtIndex:(unsigned)section] name];
if (section == 0)
return @"Most Recently Used";
if (section == 1)
return @"Most Commonly Used";
Throw(@"Unsupported section: %d", section);
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return [self.fetchedResultsController sectionIndexTitles];
return @[@"recency", @"uses"];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
return index;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSManagedObjectContext *moc = self.fetchedResultsController.managedObjectContext;
[moc performBlock:^{
MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
MPElementEntity *element = [self elementForTableIndexPath:indexPath];
[element.managedObjectContext performBlockAndWait:^{
inf(@"Deleting element: %@", element.name);
[moc deleteObject:element];
[element.managedObjectContext deleteObject:element];
[element.managedObjectContext saveToStore];
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:MPCheckpointDeleteElement];

View File

@ -10,7 +10,6 @@
#import "MPMainViewController.h"
#import "MPAppDelegate.h"
@interface MPElementListSearchController ()
@property (nonatomic) BOOL newSiteSectionWasNeeded;
@ -47,7 +46,7 @@
- (void)searchBarBookmarkButtonClicked:(UISearchBar *)searchBar {
[((MPMainViewController *)self.delegate) performSegueWithIdentifier:@"MP_AllSites" sender:self];
[((MPMainViewController *)self.delegate) performSegueWithIdentifier:@"MP_AllSites" sender:MPElementListFilterNone];
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
@ -70,12 +69,7 @@
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
controller.searchBar.showsScopeBar = controller.searchBar.selectedScopeButtonIndex != MPSearchScopeAll;
controller.searchBar.text = @"";
if (controller.searchBar.showsScopeBar)
controller.searchBar.scopeButtonTitles = @[@"All", @"Outdated"];
else
controller.searchBar.scopeButtonTitles = nil;
[UIView animateWithDuration:0.2f animations:^{
self.searchTipContainer.alpha = 0;
@ -91,8 +85,6 @@
controller.searchBar.prompt = nil;
controller.searchBar.searchResultsButtonSelected = NO;
controller.searchBar.selectedScopeButtonIndex = MPSearchScopeAll;
controller.searchBar.showsScopeBar = NO;
}
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView {
@ -110,13 +102,6 @@
return NO;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
[self updateData];
return NO;
}
- (void)updateData {
[super updateData];
@ -146,37 +131,39 @@
return NO;
__block BOOL hasExactQueryMatch = NO;
[[self.fetchedResultsController sections] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id <NSFetchedResultsSectionInfo> sectionInfo = obj;
[[sectionInfo objects] enumerateObjectsUsingBlock:^(id obj_, NSUInteger idx_, BOOL *stop_) {
if ([[obj_ name] isEqualToString:query]) {
hasExactQueryMatch = YES;
*stop_ = YES;
}
}];
if (hasExactQueryMatch)
*stop = YES;
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsControllerByUses sections] lastObject];
[[sectionInfo objects] enumerateObjectsUsingBlock:^(id obj_, NSUInteger idx_, BOOL *stop_) {
if ([[obj_ name] isEqualToString:query]) {
hasExactQueryMatch = YES;
*stop_ = YES;
}
}];
if (hasExactQueryMatch)
return NO;
return !hasExactQueryMatch;
sectionInfo = [[self.fetchedResultsControllerByLastUsed sections] lastObject];
[[sectionInfo objects] enumerateObjectsUsingBlock:^(id obj_, NSUInteger idx_, BOOL *stop_) {
if ([[obj_ name] isEqualToString:query]) {
hasExactQueryMatch = YES;
*stop_ = YES;
}
}];
if (hasExactQueryMatch)
return NO;
return YES;
}
- (void)customTableViewUpdates {
BOOL newSiteSectionIsNeeded = [self newSiteSectionNeeded];
dbg(@"isNeeded:%d, wasNeeded:%d", newSiteSectionIsNeeded, self.newSiteSectionWasNeeded);
if (newSiteSectionIsNeeded && !self.newSiteSectionWasNeeded) {
dbg(@"%@ -- insertSection:%d", NSStringFromSelector(_cmd), [[self.fetchedResultsController sections] count]);
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:[[self.fetchedResultsController sections] count]]
if (newSiteSectionIsNeeded && !self.newSiteSectionWasNeeded)
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:2]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
else if (!newSiteSectionIsNeeded && self.newSiteSectionWasNeeded) {
dbg(@"%@ -- deleteSection:%d", NSStringFromSelector(_cmd), [[self.fetchedResultsController sections] count]);
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:[[self.fetchedResultsController sections] count]]
else if (!newSiteSectionIsNeeded && self.newSiteSectionWasNeeded)
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:2]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
self.newSiteSectionWasNeeded = newSiteSectionIsNeeded;
dbg(@"wasNeeded->%d", self.newSiteSectionWasNeeded);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
@ -185,39 +172,37 @@
if ([self newSiteSectionNeeded])
++sectionCount;
dbg(@"%@ (actually) = %d", NSStringFromSelector(_cmd), sectionCount);
return sectionCount;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSUInteger fetchSections = [[self.fetchedResultsController sections] count];
if (section < (NSInteger)fetchSections)
if (section < [super numberOfSectionsInTableView:tableView])
// Section is one of super's sections.
return [super tableView:tableView numberOfRowsInSection:section];
dbg(@"%@%d = %d", NSStringFromSelector(_cmd), section, 1);
return 1;
}
- (void)configureCell:(UITableViewCell *)cell inTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath {
- (void)configureCell:(UITableViewCell *)cell inTableView:(UITableView *)tableView atTableIndexPath:(NSIndexPath *)indexPath {
NSUInteger fetchSections = [[self.fetchedResultsController sections] count];
if (indexPath.section < (NSInteger)fetchSections)
[super configureCell:cell inTableView:tableView atIndexPath:indexPath];
else {
// "New" section
NSString *query = [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
cell.textLabel.text = query;
cell.detailTextLabel.text = PearlString(@"Add new site: %@",
[MPAlgorithmDefault shortNameOfType:[[MPAppDelegate get].activeUser defaultType]]);
if (indexPath.section < [super numberOfSectionsInTableView:tableView]) {
// Section is one of super's sections.
[super configureCell:cell inTableView:tableView atTableIndexPath:indexPath];
return;
}
// "New" section
NSString *query = [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
cell.textLabel.text = query;
cell.detailTextLabel.text = PearlString(@"New site: %@",
[MPAlgorithmDefault shortNameOfType:[[MPAppDelegate get].activeUser defaultType]]);
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger fetchSections = [[self.fetchedResultsController sections] count];
if (indexPath.section < (NSInteger)fetchSections) {
if (indexPath.section < [super numberOfSectionsInTableView:tableView]) {
// Section is one of super's sections.
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
return;
}
@ -240,18 +225,18 @@
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSUInteger fetchSections = [[self.fetchedResultsController sections] count];
if (section < (NSInteger)fetchSections)
if (section < [super numberOfSectionsInTableView:tableView])
// Section is one of super's sections.
return [super tableView:tableView titleForHeaderInSection:section];
return @"";
return @"Create";
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger fetchSections = [[self.fetchedResultsController sections] count];
if (indexPath.section < (NSInteger)fetchSections)
if (indexPath.section < [super numberOfSectionsInTableView:tableView])
// Section is one of super's sections.
[super tableView:tableView commitEditingStyle:editingStyle forRowAtIndexPath:indexPath];
}

View File

@ -8,10 +8,8 @@
#import "MPMainViewController.h"
#import "MPAppDelegate.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h"
#import "MPElementListAllViewController.h"
#import "MPElementListSearchController.h"
@interface MPMainViewController()
@ -50,8 +48,10 @@
if ([[segue identifier] isEqualToString:@"MP_ChooseType"])
((MPTypeViewController *)[segue destinationViewController]).delegate = self;
if ([[segue identifier] isEqualToString:@"MP_AllSites"])
((MPElementListAllViewController *)[((UINavigationController *)[segue destinationViewController]) topViewController]).delegate = self;
if ([[segue identifier] isEqualToString:@"MP_AllSites"]) {
((MPElementListAllViewController *)[segue destinationViewController]).delegate = self;
((MPElementListAllViewController *)[segue destinationViewController]).filter = sender;
}
}
- (void)viewDidLoad {
@ -262,10 +262,10 @@
if ([[MPiOSConfig get].helpHidden boolValue]) {
self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, self.view.bounds.size.height - 44 /* search bar */);
self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, self.view.bounds.size.height - 20);
self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, self.view.bounds.size.height - 20 /* pull-up */);
} else {
self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, 225);
self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, 246);
[self.helpContainer setFrameFromCurrentSizeAndParentPaddingTop:CGFLOAT_MAX right:0 bottom:0 left:0];
}
}
@ -690,9 +690,7 @@
- (IBAction)searchOutdatedElements {
self.searchDisplayController.searchBar.selectedScopeButtonIndex = MPSearchScopeOutdated;
self.searchDisplayController.searchBar.searchResultsButtonSelected = YES;
[self.searchDisplayController.searchBar becomeFirstResponder];
[self performSegueWithIdentifier:@"MP_AllSites" sender:MPElementListFilterOutdated];
}
- (IBAction)closeAlert {

View File

@ -21,6 +21,7 @@
@interface MPSetupViewController : UIViewController
@property (weak, nonatomic) IBOutlet UISwitch *cloudSwitch;
@property (weak, nonatomic) IBOutlet UISwitch *rememberLoginSwitch;
- (IBAction)close:(UIBarButtonItem *)sender;

View File

@ -30,6 +30,8 @@
if (self.cloudSwitch && [[MPiOSConfig get].iCloudDecided boolValue])
self.cloudSwitch.on = [MPAppDelegate get].storeManager.cloudEnabled;
if (self.rememberLoginSwitch)
self.rememberLoginSwitch.on = [[MPiOSConfig get].rememberLogin boolValue];
}
- (void)viewWillDisappear:(BOOL)animated {
@ -40,6 +42,8 @@
[MPiOSConfig get].iCloudDecided = @YES;
[MPAppDelegate get].storeManager.cloudEnabled = self.cloudSwitch.on;
}
if (self.rememberLoginSwitch)
[MPiOSConfig get].rememberLogin = @(self.rememberLoginSwitch.on);
}
- (IBAction)close:(UIBarButtonItem *)sender {

View File

@ -212,7 +212,7 @@
if (!moc)
return;
self.tip.text = @"Tap and hold to delete or reset.";
self.tip.text = @"Tap and hold to delete or reset user.";
__block NSArray *users = nil;
[moc performBlockAndWait:^{
@ -266,15 +266,13 @@
avatar.backgroundColor = [UIColor clearColor];
} options:0];
[avatar onSelect:^(BOOL selected) {
if (selected) {
if ((self.selectedUser = user))
[self didToggleUserSelection];
else
[self didSelectNewUserAvatar:avatar];
} else {
if (!selected) {
self.selectedUser = nil;
[self didToggleUserSelection];
}
} else if ((self.selectedUser = user))
[self didToggleUserSelection];
else
[self didSelectNewUserAvatar:avatar];
} options:0];
[self.avatarToUserOID setObject:NilToNSNull([user objectID]) forKey:[NSValue valueWithNonretainedObject:avatar]];

View File

@ -705,10 +705,6 @@ Your passwords will be AES-encrypted with your master password.</string>
<gestureRecognizers/>
<color key="tintColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="1" colorSpace="calibratedRGB"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="URL"/>
<scopeButtonTitles>
<string>All</string>
<string>Outdated</string>
</scopeButtonTitles>
</searchBar>
<view userInteractionEnabled="NO" contentMode="scaleToFill" id="foz-tW-xGw" userLabel="View - Action Tip">
<rect key="frame" x="10" y="0.0" width="300" height="60"/>
@ -1006,7 +1002,7 @@ L4m3P4sSw0rD</string>
<outlet property="typeTipContainer" destination="g55-0m-WjS" id="KZ9-KV-NMh"/>
<segue destination="oLN-6u-GLb" kind="push" identifier="MP_UserProfile" id="tKN-Sw-S5J"/>
<segue destination="2Th-Tb-22a" kind="modal" identifier="MP_OtherApps" id="Io5-cz-v9Y"/>
<segue destination="KZ9-Bb-FN7" kind="modal" identifier="MP_AllSites" id="18w-AW-e8A"/>
<segue destination="idA-Pj-1U9" kind="modal" identifier="MP_AllSites" id="nVS-UW-cgK"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="mK2-p1-3zC" userLabel="First Responder" sceneMemberID="firstResponder"/>
@ -1101,7 +1097,7 @@ L4m3P4sSw0rD</string>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="ui_spinner.png" id="27q-lX-0vy">
<rect key="frame" x="105" y="40" width="110" height="110"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</imageView>
<view contentMode="scaleToFill" id="JTj-nh-BWs" userLabel="Word Wall">
<rect key="frame" x="0.0" y="0.0" width="960" height="200"/>
@ -1254,11 +1250,11 @@ L4m3P4sSw0rD</string>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="DBJ-Qi-ZcF">
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="Tap and hold to delete or reset user." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="DBJ-Qi-ZcF">
<rect key="frame" x="20" y="548" width="280" height="20"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" name="Copperplate" family="Copperplate" pointSize="12"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<color key="textColor" cocoaTouchSystemColor="lightTextColor"/>
<nil key="highlightedColor"/>
<color key="shadowColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</label>
@ -1287,7 +1283,7 @@ L4m3P4sSw0rD</string>
</scrollView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.0" contentMode="left" text="Maarten Billemont" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumFontSize="10" id="8s0-nT-Aoq">
<rect key="frame" x="90" y="377" width="140" height="15"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" name="Futura-CondensedExtraBold" family="Futura" pointSize="13"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
@ -1295,7 +1291,7 @@ L4m3P4sSw0rD</string>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Maarten Billemont" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumFontSize="10" id="0NM-NI-7UR">
<rect key="frame" x="90" y="174" width="140" height="15"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" name="Futura-CondensedExtraBold" family="Futura" pointSize="13"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
@ -1303,7 +1299,7 @@ L4m3P4sSw0rD</string>
</label>
<view userInteractionEnabled="NO" contentMode="scaleToFill" id="xWL-xQ-KjX">
<rect key="frame" x="20" y="60" width="280" height="100"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black.png" id="Xrk-S4-ZN5">
<rect key="frame" x="0.0" y="0.0" width="280" height="100"/>
@ -1325,7 +1321,7 @@ You could use the word wall for inspiration in finding a memorable master passw
</view>
<view contentMode="scaleToFill" id="7cc-yu-i0m">
<rect key="frame" x="20" y="168" width="280" height="88"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Enter your master password:" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="RhX-bA-EhC">
<rect key="frame" x="10" y="0.0" width="260" height="20"/>
@ -1433,7 +1429,7 @@ You could use the word wall for inspiration in finding a memorable master passw
</button>
<button opaque="NO" alpha="0.10000000000000001" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="2dq-bb-mPl" userLabel="add">
<rect key="frame" x="0.0" y="0.0" width="44" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="26"/>
<state key="normal" title="">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
@ -1557,7 +1553,7 @@ If you set a custom password, it will be encrypted before it is saved to the clo
<navigationItem key="navigationItem" title="iCloud" id="mNu-yP-9oW">
<barButtonItem key="rightBarButtonItem" title="Next" id="Kxp-c7-ErS">
<connections>
<segue destination="ZgN-2j-05b" kind="push" id="llz-jX-HRQ"/>
<segue destination="kSj-yX-DmT" kind="push" id="gbD-CB-5c8"/>
</connections>
</barButtonItem>
</navigationItem>
@ -1569,51 +1565,44 @@ If you set a custom password, it will be encrypted before it is saved to the clo
</objects>
<point key="canvasLocation" x="996" y="1425"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="rAg-OU-9QV">
<objects>
<navigationController definesPresentationContext="YES" id="KZ9-Bb-FN7" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" id="s0n-kY-htJ">
<rect key="frame" x="0.0" y="-44" width="0.0" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<color key="tintColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="1" colorSpace="calibratedRGB"/>
</navigationBar>
<connections>
<segue destination="idA-Pj-1U9" kind="relationship" relationship="rootViewController" id="dmZ-bA-SbA"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="vJs-4S-qNG" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1537" y="-495"/>
</scene>
<!--Element List All View Controller - All Sites-->
<!--Element List All View Controller-->
<scene sceneID="I7c-vt-d2s">
<objects>
<tableViewController id="idA-Pj-1U9" customClass="MPElementListAllViewController" sceneMemberID="viewController">
<tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="48" sectionHeaderHeight="22" sectionFooterHeight="22" id="N83-sj-4tl">
<rect key="frame" x="0.0" y="64" width="320" height="504"/>
<rect key="frame" x="0.0" y="20" width="320" height="548"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<navigationBar key="tableHeaderView" contentMode="scaleToFill" id="l0p-Tu-L9k">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<color key="tintColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="1" colorSpace="calibratedRGB"/>
<items>
<navigationItem title="All Sites" id="Ipv-Tt-WPc">
<barButtonItem key="leftBarButtonItem" title="Close" style="done" id="Mnn-6X-DgP">
<connections>
<action selector="close:" destination="idA-Pj-1U9" id="qqC-U7-8Ci"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" systemItem="add" id="CR8-WY-9Td">
<connections>
<action selector="add:" destination="idA-Pj-1U9" id="G7b-hR-IH0"/>
</connections>
</barButtonItem>
</navigationItem>
</items>
</navigationBar>
<connections>
<outlet property="dataSource" destination="idA-Pj-1U9" id="yPh-6k-Ba9"/>
<outlet property="delegate" destination="idA-Pj-1U9" id="bdk-Iu-Hpv"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="All Sites" id="Ipv-Tt-WPc">
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="Mnn-6X-DgP">
<connections>
<action selector="close:" destination="idA-Pj-1U9" id="qqC-U7-8Ci"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" systemItem="add" id="CR8-WY-9Td">
<connections>
<action selector="add:" destination="idA-Pj-1U9" id="G7b-hR-IH0"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="navigationBar" destination="l0p-Tu-L9k" id="9DR-L3-ggI"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="abw-PC-pyQ" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2078" y="-495"/>
<point key="canvasLocation" x="1537" y="-495"/>
</scene>
<!--Apps View Controller-->
<scene sceneID="3cC-Qq-rgU">
@ -1623,17 +1612,17 @@ If you set a custom password, it will be encrypted before it is saved to the clo
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="book.png" id="wjL-OU-K7k">
<imageView userInteractionEnabled="NO" contentMode="center" image="book.png" id="wjL-OU-K7k">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="page-gorillas.png" id="QQT-37-azo">
<rect key="frame" x="0.0" y="79" width="305" height="400"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<rect key="frame" x="0.0" y="81" width="305" height="400"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="drq-47-KK9">
<rect key="frame" x="85" y="448" width="150" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<rect key="frame" x="85" y="536" width="150" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<state key="normal">
<color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
@ -1647,7 +1636,7 @@ If you set a custom password, it will be encrypted before it is saved to the clo
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<color key="backgroundColor" cocoaTouchSystemColor="viewFlipsideBackgroundColor"/>
</view>
<nil key="simulatedStatusBarMetrics"/>
<connections>
@ -1809,7 +1798,7 @@ If you set a custom password, it will be encrypted before it is saved to the clo
<switch opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" id="ilG-0h-SOb">
<rect key="frame" x="221" y="8" width="79" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="onTintColor" red="0.12549020350000001" green="0.1411764771" blue="0.14901961389999999" alpha="1" colorSpace="calibratedRGB"/>
<color key="onTintColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<action selector="didToggleSwitch:" destination="oLN-6u-GLb" eventType="valueChanged" id="e5q-mf-XK7"/>
</connections>
@ -2066,7 +2055,7 @@ If you set a custom password, it will be encrypted before it is saved to the clo
</objects>
<point key="canvasLocation" x="1004" y="-495"/>
</scene>
<!--Setup View Controller-->
<!--Setup View Controller - About-->
<scene sceneID="p4k-3T-J0X">
<objects>
<viewController id="ZgN-2j-05b" customClass="MPSetupViewController" sceneMemberID="viewController">
@ -2086,35 +2075,50 @@ If you set a custom password, it will be encrypted before it is saved to the clo
<nil key="highlightedColor"/>
</label>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" usesAttributedText="YES" id="hwP-ds-GDh">
<rect key="frame" x="20" y="137" width="280" height="347"/>
<rect key="frame" x="20" y="137" width="280" height="367"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<attributedString key="attributedText">
<fragment>
<string key="content">The passwords created by this app are not stored anywhere but created on the spot.
It is vital that you </string>
<fragment content="The passwords created by this app are not stored anywhere but ">
<attributes>
<color key="NSColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<font key="NSFont" size="14" name="Helvetica"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
<fragment content="remember your master password">
<fragment content="created on the spot">
<attributes>
<color key="NSColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<font key="NSFont" size="14" name="Helvetica-Oblique"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
<fragment>
<string key="content">.
You don't need to remember any password generated by this app, but </string>
<attributes>
<color key="NSColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<font key="NSFont" size="14" name="Helvetica"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
<fragment content="it is vital that you ">
<attributes>
<color key="NSColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<font key="NSFont" size="14" name="Helvetica-Bold"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
<fragment content=" and that it is secure. A small nonsense sentence is a great password. Eg. ">
<fragment content="remember">
<attributes>
<color key="NSColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<font key="NSFont" size="14" name="Helvetica"/>
<font key="NSFont" size="14" name="Helvetica-BoldOblique"/>
<font key="NSOriginalFont" size="14" name="Helvetica-BoldOblique"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
<fragment content="Banana colored duckling">
<fragment content=" your master password">
<attributes>
<color key="NSColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<font key="NSFont" size="14" name="Helvetica-BoldOblique"/>
@ -2122,28 +2126,29 @@ It is vital that you </string>
</attributes>
</fragment>
<fragment>
<string key="content" base64-UTF8="YES">
Cgo
</string>
<string key="content"> and that it is secure.
Don't reuse an old password!
Don't give out your master password.
A small nonsense sentence is a great password. </string>
<attributes>
<color key="NSColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<font key="NSFont" size="14" name="Helvetica"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
<fragment>
<string key="content">Don't reuse an old password!
</string>
<fragment content="Eg. Banana colored duckling">
<attributes>
<color key="NSColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<font key="NSFont" size="14" name="Helvetica-Bold"/>
<font key="NSOriginalFont" size="14" name="Helvetica-Bold"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
<fragment>
<string key="content">Don't give out your master password, no matter how close you think you are with the other person.
<string key="content">
You can make passwords for all sorts of things, like email addresses, sites or real-world objects like your bike lock: if you can name it, you can get a password for it.</string>
You can make passwords for anything, like email addresses, sites or real-world things like a bike lock: if you can name it, you can get a password for it.</string>
<attributes>
<color key="NSColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<font key="NSFont" size="14" name="Helvetica"/>
@ -2160,13 +2165,13 @@ You can make passwords for all sorts of things, like email addresses, sites or r
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
<navigationItem key="navigationItem" id="oBl-x2-aUS">
<navigationItem key="navigationItem" title="About" id="oBl-x2-aUS">
<barButtonItem key="leftBarButtonItem" title="Close" style="done" id="yZJ-lt-O5m">
<connections>
<action selector="close:" destination="ZgN-2j-05b" id="aKm-zK-xbg"/>
</connections>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" title="Guide" id="rVv-Hh-KDw">
<barButtonItem key="rightBarButtonItem" title="Overview" id="rVv-Hh-KDw">
<connections>
<segue destination="myN-X7-9Tg" kind="push" id="jgo-j3-gbW"/>
</connections>
@ -2175,7 +2180,7 @@ You can make passwords for all sorts of things, like email addresses, sites or r
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="561-Zo-a0K" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1537" y="1425"/>
<point key="canvasLocation" x="2078" y="1425"/>
</scene>
<!--Guide View Controller-->
<scene sceneID="k6U-hl-fJt">
@ -2527,7 +2532,66 @@ You can make passwords for all sorts of things, like email addresses, sites or r
<panGestureRecognizer minimumNumberOfTouches="1" id="kYj-Dc-rXL"/>
<panGestureRecognizer minimumNumberOfTouches="1" id="ayP-1f-n2G"/>
</objects>
<point key="canvasLocation" x="2078" y="1425"/>
<point key="canvasLocation" x="2619" y="1425"/>
</scene>
<!--Setup View Controller - Security-->
<scene sceneID="XZC-qy-rbH">
<objects>
<viewController id="kSj-yX-DmT" customClass="MPSetupViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="sT4-Jb-e5D">
<rect key="frame" x="0.0" y="64" width="320" height="504"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="center" image="background.png" id="Eqt-R0-LTj">
<rect key="frame" x="0.0" y="-64" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="top" image="unlocked.png" id="4ah-P0-2DG">
<rect key="frame" x="20" y="20" width="280" height="80"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Stay Logged In?" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="R2t-Ug-fY6">
<rect key="frame" x="20" y="108" width="280" height="21"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="k4g-1Z-Hfw">
<rect key="frame" x="20" y="137" width="280" height="196"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<string key="text">The right balance between security and convenience is often very personal.
To make getting to your passwords faster, you can remain logged in after you close Master Password. This allows you to skip having to log in the next time.
However, it means that anyone who finds your device unlocked can do the same.</string>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<switch opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" id="xmc-hs-riu">
<rect key="frame" x="122" y="345" width="79" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="onTintColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="1" colorSpace="calibratedRGB"/>
</switch>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
<navigationItem key="navigationItem" title="Security" id="7XO-EZ-YRY">
<barButtonItem key="rightBarButtonItem" title="Next" id="77S-5d-d24">
<connections>
<segue destination="ZgN-2j-05b" kind="push" id="Osp-Zu-L6y"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="rememberLoginSwitch" destination="xmc-hs-riu" id="ROi-H8-rcY"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="M5H-qQ-ytd" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1537" y="1425"/>
</scene>
</scenes>
<resources>
@ -2567,6 +2631,7 @@ You can make passwords for all sorts of things, like email addresses, sites or r
<image name="ui_panel_display.png" width="300" height="86"/>
<image name="ui_spinner.png" width="75" height="75"/>
<image name="ui_textfield.png" width="158" height="34"/>
<image name="unlocked.png" width="84" height="80"/>
</resources>
<classes>
<class className="MPAppViewController" superclassName="UIViewController">
@ -2588,6 +2653,7 @@ You can make passwords for all sorts of things, like email addresses, sites or r
<relationships>
<relationship kind="action" name="add:"/>
<relationship kind="action" name="close:"/>
<relationship kind="outlet" name="navigationBar" candidateClass="UINavigationBar"/>
</relationships>
</class>
<class className="MPElementListController" superclassName="UITableViewController">
@ -2700,6 +2766,7 @@ You can make passwords for all sorts of things, like email addresses, sites or r
<relationship kind="action" name="close:" candidateClass="UIBarButtonItem"/>
<relationship kind="action" name="showGuide:" candidateClass="UIBarButtonItem"/>
<relationship kind="outlet" name="cloudSwitch" candidateClass="UISwitch"/>
<relationship kind="outlet" name="rememberLoginSwitch" candidateClass="UISwitch"/>
</relationships>
</class>
<class className="MPTypeViewController" superclassName="UITableViewController">
@ -2742,7 +2809,7 @@ You can make passwords for all sorts of things, like email addresses, sites or r
<simulatedScreenMetrics key="destination" type="retina4"/>
</simulatedMetricsContainer>
<inferredMetricsTieBreakers>
<segue reference="KIl-ZW-M7G"/>
<segue reference="jgo-j3-gbW"/>
<segue reference="9Bs-cD-ddF"/>
</inferredMetricsTieBreakers>
</document>

View File

@ -38,6 +38,8 @@
DA4DA1DA1564471F00F6F596 /* libuicolor-utilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */; };
DA5A09DF171A70E4005284AB /* play.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09DD171A70E4005284AB /* play.png */; };
DA5A09E0171A70E4005284AB /* play@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09DE171A70E4005284AB /* play@2x.png */; };
DA5A09EA171BB0F7005284AB /* unlocked.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09E8171BB0F7005284AB /* unlocked.png */; };
DA5A09EB171BB0F7005284AB /* unlocked@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09E9171BB0F7005284AB /* unlocked@2x.png */; };
DA5BFA49147E415C00F98B1E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA48147E415C00F98B1E /* UIKit.framework */; };
DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */; };
@ -1036,6 +1038,8 @@
DA497B9715E8C90E00B52167 /* libGoogle+.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libGoogle+.a"; sourceTree = BUILT_PRODUCTS_DIR; };
DA5A09DD171A70E4005284AB /* play.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = play.png; sourceTree = "<group>"; };
DA5A09DE171A70E4005284AB /* play@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "play@2x.png"; sourceTree = "<group>"; };
DA5A09E8171BB0F7005284AB /* unlocked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = unlocked.png; sourceTree = "<group>"; };
DA5A09E9171BB0F7005284AB /* unlocked@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "unlocked@2x.png"; sourceTree = "<group>"; };
DA5BFA44147E415C00F98B1E /* MasterPassword.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MasterPassword.app; sourceTree = BUILT_PRODUCTS_DIR; };
DA5BFA48147E415C00F98B1E /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
DA5BFA4A147E415C00F98B1E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
@ -2206,6 +2210,8 @@
DABD360D1711E29400CF925C /* Media */ = {
isa = PBXGroup;
children = (
DA5A09E8171BB0F7005284AB /* unlocked.png */,
DA5A09E9171BB0F7005284AB /* unlocked@2x.png */,
DA5A09DD171A70E4005284AB /* play.png */,
DA5A09DE171A70E4005284AB /* play@2x.png */,
DABD360E1711E29400CF925C /* Automaton */,
@ -4596,6 +4602,8 @@
DABD3FCF1714F45C00CF925C /* identity@2x.png in Resources */,
DA5A09DF171A70E4005284AB /* play.png in Resources */,
DA5A09E0171A70E4005284AB /* play@2x.png in Resources */,
DA5A09EA171BB0F7005284AB /* unlocked.png in Resources */,
DA5A09EB171BB0F7005284AB /* unlocked@2x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 21 KiB