Listing all sites now with bookmark button and modal VC.
[ADDED] Modal VC to show all sites. [UPDATED] All sites button on search bar now uses bookmark button. [REMOVED] Query string hack for search results button.
This commit is contained in:
parent
2bb0e2e3ef
commit
9a68cbf533
@ -17,6 +17,7 @@
|
||||
93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; };
|
||||
93D399B873AF89808151D2F5 /* MPAppsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390DABAE4368E90E37340 /* MPAppsViewController.m */; };
|
||||
93D39BCE5F69D8EBE7E9F6EC /* MPAppViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E070BD3F45B3045A1DA /* MPAppViewController.m */; };
|
||||
93D39BD0F5C7C76678712500 /* MPAllSitesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39F16ADA770D8C13B4555 /* MPAllSitesViewController.m */; };
|
||||
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
|
||||
93D39DC7A7282137B08C8D82 /* MPAlgorithmV1.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E9D7B9005211E7D5262 /* MPAlgorithmV1.m */; };
|
||||
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
|
||||
@ -972,6 +973,7 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = "<group>"; };
|
||||
93D390B3209212B3049BEC2D /* MPAllSitesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAllSitesViewController.h; sourceTree = "<group>"; };
|
||||
93D390DABAE4368E90E37340 /* MPAppsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppsViewController.m; sourceTree = "<group>"; };
|
||||
93D3938863322199C3E7E2E3 /* MPAlgorithmV0.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV0.m; sourceTree = "<group>"; };
|
||||
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
|
||||
@ -989,6 +991,8 @@
|
||||
93D39E070BD3F45B3045A1DA /* MPAppViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppViewController.m; sourceTree = "<group>"; };
|
||||
93D39E81EFABC6085AC8AE69 /* MPKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPKey.m; sourceTree = "<group>"; };
|
||||
93D39E9D7B9005211E7D5262 /* MPAlgorithmV1.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV1.m; sourceTree = "<group>"; };
|
||||
93D39F16ADA770D8C13B4555 /* MPAllSitesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAllSitesViewController.m; sourceTree = "<group>"; };
|
||||
93D39F37240730C6311B8FBD /* MPElementPickerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementPickerDelegate.h; sourceTree = "<group>"; };
|
||||
93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlEMail.h; sourceTree = "<group>"; };
|
||||
DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
|
||||
DA0A1D0315690A9A0092735D /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Default.png; path = Resources/Default.png; sourceTree = SOURCE_ROOT; };
|
||||
@ -2542,6 +2546,9 @@
|
||||
DAB8D45215036BCF00CED3BC /* MPUnlockViewController.h */,
|
||||
DAB8D45315036BCF00CED3BC /* MPUnlockViewController.m */,
|
||||
DAB8D45415036BCF00CED3BC /* Settings.bundle */,
|
||||
93D39F16ADA770D8C13B4555 /* MPAllSitesViewController.m */,
|
||||
93D390B3209212B3049BEC2D /* MPAllSitesViewController.h */,
|
||||
93D39F37240730C6311B8FBD /* MPElementPickerDelegate.h */,
|
||||
);
|
||||
path = iOS;
|
||||
sourceTree = "<group>";
|
||||
@ -4716,6 +4723,7 @@
|
||||
DA81253816B8546B00F4732F /* MPElementStoredEntity.m in Sources */,
|
||||
DA81253B16B8546B00F4732F /* MPUserEntity.m in Sources */,
|
||||
DA81253E16B8546C00F4732F /* MPElementGeneratedEntity.m in Sources */,
|
||||
93D39BD0F5C7C76678712500 /* MPAllSitesViewController.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -18,6 +18,8 @@
|
||||
@property (strong, nonatomic) MPUserEntity *activeUser;
|
||||
@property (strong, nonatomic) MPKey *key;
|
||||
|
||||
+ (instancetype)get;
|
||||
|
||||
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)moc;
|
||||
|
||||
@end
|
||||
|
@ -28,9 +28,4 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (MPConfig *)get {
|
||||
|
||||
return (MPConfig *)[super get];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -42,11 +42,6 @@ static EventHotKeyID MPLockHotKey = {.signature = 'lock', .id = 1};
|
||||
});
|
||||
}
|
||||
|
||||
+ (MPAppDelegate *)get {
|
||||
|
||||
return (MPAppDelegate *)[super get];
|
||||
}
|
||||
|
||||
static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData) {
|
||||
|
||||
// Extract the hotkey ID.
|
||||
|
@ -20,9 +20,4 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (MPMacConfig *)get {
|
||||
|
||||
return (MPMacConfig *)[super get];
|
||||
}
|
||||
|
||||
@end
|
||||
|
28
MasterPassword/iOS/MPAllSitesViewController.h
Normal file
28
MasterPassword/iOS/MPAllSitesViewController.h
Normal file
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
//
|
||||
// MPAllSitesViewController
|
||||
//
|
||||
// Created by Maarten Billemont on 2013-01-31.
|
||||
// Copyright 2013 lhunath (Maarten Billemont). All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "MPElementPickerDelegate.h"
|
||||
|
||||
@interface MPAllSitesViewController : UITableViewController
|
||||
|
||||
@property (weak, nonatomic) IBOutlet id<MPElementPickerDelegate> delegate;
|
||||
|
||||
- (IBAction)close:(id)sender;
|
||||
|
||||
@end
|
240
MasterPassword/iOS/MPAllSitesViewController.m
Normal file
240
MasterPassword/iOS/MPAllSitesViewController.m
Normal file
@ -0,0 +1,240 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
//
|
||||
// MPAllSitesViewController
|
||||
//
|
||||
// Created by Maarten Billemont on 2013-01-31.
|
||||
// Copyright 2013 lhunath (Maarten Billemont). All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAllSitesViewController.h"
|
||||
|
||||
#import "MPAppDelegate.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
|
||||
|
||||
@interface MPAllSitesViewController() <NSFetchedResultsControllerDelegate>
|
||||
|
||||
@property (nonatomic,strong)NSDateFormatter *dateFormatter;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPAllSitesViewController {
|
||||
|
||||
NSFetchedResultsController *_fetchedResultsController;
|
||||
}
|
||||
|
||||
|
||||
- (void)viewDidLoad {
|
||||
|
||||
[super viewDidLoad];
|
||||
|
||||
self.dateFormatter = [NSDateFormatter new];
|
||||
self.dateFormatter.dateStyle = NSDateFormatterShortStyle;
|
||||
}
|
||||
|
||||
- (IBAction)close:(id)sender {
|
||||
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (NSFetchedResultsController *)fetchedResultsController {
|
||||
|
||||
if (!_fetchedResultsController) {
|
||||
NSAssert([[NSThread currentThread] isMainThread], @"The fetchedResultsController must run on 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
|
||||
sectionNameKeyPath:nil cacheName:nil];
|
||||
_fetchedResultsController.delegate = self;
|
||||
}
|
||||
|
||||
return _fetchedResultsController;
|
||||
}
|
||||
|
||||
- (void)fetchData {
|
||||
|
||||
MPUserEntity *activeUser = [MPAppDelegate get].activeUser;
|
||||
if (!activeUser)
|
||||
return;
|
||||
|
||||
self.fetchedResultsController.fetchRequest.predicate = [NSPredicate predicateWithFormat:@"user == %@", activeUser];
|
||||
|
||||
NSError *error;
|
||||
if (![self.fetchedResultsController performFetch:&error])
|
||||
err(@"Couldn't fetch elements: %@", error);
|
||||
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[self fetchData];
|
||||
}
|
||||
|
||||
|
||||
// See MP-14, also crashes easily on internal assertions etc..
|
||||
//- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
|
||||
//
|
||||
// [self.searchDisplayController.searchResultsTableView beginUpdates];
|
||||
//}
|
||||
//
|
||||
//- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
|
||||
// atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
|
||||
//
|
||||
// UITableView *tableView = self.searchDisplayController.searchResultsTableView;
|
||||
// switch(type) {
|
||||
//
|
||||
// case NSFetchedResultsChangeInsert:
|
||||
// [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
|
||||
// break;
|
||||
//
|
||||
// case NSFetchedResultsChangeDelete:
|
||||
// [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
|
||||
// break;
|
||||
//
|
||||
// case NSFetchedResultsChangeUpdate:
|
||||
// [self configureCell:[tableView cellForRowAtIndexPath:indexPath]
|
||||
// inTableView:tableView atIndexPath:indexPath];
|
||||
// break;
|
||||
//
|
||||
// case NSFetchedResultsChangeMove:
|
||||
// [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
|
||||
// withRowAnimation:UITableViewRowAnimationFade];
|
||||
// [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
|
||||
// withRowAnimation:UITableViewRowAnimationFade];
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//
|
||||
//- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo
|
||||
// atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
|
||||
//
|
||||
// UITableView *tableView = self.searchDisplayController.searchResultsTableView;
|
||||
// switch(type) {
|
||||
//
|
||||
// case NSFetchedResultsChangeInsert:
|
||||
// [tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
|
||||
// withRowAnimation:UITableViewRowAnimationFade];
|
||||
// break;
|
||||
//
|
||||
// case NSFetchedResultsChangeDelete:
|
||||
// [tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
|
||||
// withRowAnimation:UITableViewRowAnimationFade];
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
|
||||
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
|
||||
|
||||
dbg(@"controllerDidChangeContent on thread: %@", [NSThread currentThread].name);
|
||||
[self.tableView reloadData];
|
||||
//[self.tableView endUpdates];
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
|
||||
return (NSInteger)[[self.fetchedResultsController sections] count];
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
|
||||
return (NSInteger)[[[self.fetchedResultsController sections] objectAtIndex:(unsigned)section] numberOfObjects];
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MPElementSearch"];
|
||||
if (!cell.backgroundView) {
|
||||
// cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"MPElementSearch"];
|
||||
|
||||
UIImage *backgroundImage = [[UIImage imageNamed:@"ui_list_middle"] resizableImageWithCapInsets:UIEdgeInsetsMake(3, 3, 3, 3)
|
||||
resizingMode:UIImageResizingModeStretch];
|
||||
UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:backgroundImage];
|
||||
backgroundImageView.frame = CGRectMake(-5, 0, 330, 34);
|
||||
backgroundImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 34)];
|
||||
[backgroundView addSubview:backgroundImageView];
|
||||
backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
|
||||
cell.backgroundView = backgroundView;
|
||||
// cell.textLabel.backgroundColor = [UIColor clearColor];
|
||||
// cell.textLabel.textColor = [UIColor whiteColor];
|
||||
// cell.detailTextLabel.backgroundColor = [UIColor clearColor];
|
||||
// cell.detailTextLabel.textColor = [UIColor lightGrayColor];
|
||||
// cell.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
// cell.clipsToBounds = YES;
|
||||
}
|
||||
|
||||
[self configureCell:cell inTableView:tableView atIndexPath:indexPath];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)configureCell:(UITableViewCell *)cell inTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
|
||||
|
||||
cell.textLabel.text = element.name;
|
||||
cell.detailTextLabel.text = PearlString(@"Used %d times, last on %@",
|
||||
element.uses, [self.dateFormatter stringFromDate:element.lastUsed]);
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
[self.delegate didSelectElement:[self.fetchedResultsController objectAtIndexPath:indexPath]];
|
||||
[self close:nil];
|
||||
}
|
||||
|
||||
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
|
||||
|
||||
return [[[self.fetchedResultsController sections] objectAtIndex:(unsigned)section] name];
|
||||
}
|
||||
|
||||
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
|
||||
|
||||
return [self.fetchedResultsController sectionIndexTitles];
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
|
||||
|
||||
return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
|
||||
forRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
if (editingStyle == UITableViewCellEditingStyleDelete)
|
||||
[self.fetchedResultsController.managedObjectContext performBlock:^{
|
||||
MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
|
||||
|
||||
inf(@"Deleting element: %@", element.name);
|
||||
[self.fetchedResultsController.managedObjectContext deleteObject:element];
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointDeleteElement];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointDeleteElement attributes:@{
|
||||
@"type" : element.typeName,
|
||||
@"version" : @(element.version)}];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@end
|
@ -92,6 +92,16 @@
|
||||
[super viewWillDisappear:animated];
|
||||
}
|
||||
|
||||
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
|
||||
|
||||
return UIInterfaceOrientationPortrait;
|
||||
}
|
||||
|
||||
- (BOOL)shouldAutorotate {
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
|
||||
viewControllerBeforeViewController:(UIViewController *)viewController {
|
||||
|
24
MasterPassword/iOS/MPElementPickerDelegate.h
Normal file
24
MasterPassword/iOS/MPElementPickerDelegate.h
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
//
|
||||
// MPElementPickerDelegate
|
||||
//
|
||||
// Created by Maarten Billemont on 2013-01-31.
|
||||
// Copyright 2013 lhunath (Maarten Billemont). All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPElementEntity.h"
|
||||
|
||||
@protocol MPElementPickerDelegate<NSObject>
|
||||
|
||||
- (void)didSelectElement:(MPElementEntity *)element;
|
||||
|
||||
@end
|
@ -10,7 +10,7 @@
|
||||
#import "MPTypeViewController.h"
|
||||
#import "MPSearchDelegate.h"
|
||||
|
||||
@interface MPMainViewController : UIViewController<MPTypeDelegate, UITextFieldDelegate, MPSearchResultsDelegate, UIWebViewDelegate, UIGestureRecognizerDelegate>
|
||||
@interface MPMainViewController : UIViewController<MPTypeDelegate, UITextFieldDelegate, MPElementPickerDelegate, UIWebViewDelegate, UIGestureRecognizerDelegate>
|
||||
|
||||
@property (assign, nonatomic) BOOL siteInfoHidden;
|
||||
@property (strong, nonatomic) IBOutlet MPSearchDelegate *searchDelegate;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#import "MPAppDelegate.h"
|
||||
#import "MPAppDelegate_Key.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import "MPAllSitesViewController.h"
|
||||
|
||||
|
||||
@interface MPMainViewController()
|
||||
@ -48,6 +49,8 @@
|
||||
|
||||
if ([[segue identifier] isEqualToString:@"MP_ChooseType"])
|
||||
((MPTypeViewController *)[segue destinationViewController]).delegate = self;
|
||||
if ([[segue identifier] isEqualToString:@"MP_AllSites"])
|
||||
((MPAllSitesViewController *)[((UINavigationController *)[segue destinationViewController]) topViewController]).delegate = self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
@ -753,12 +756,12 @@
|
||||
}
|
||||
case 2: {
|
||||
inf(@"Action: Preferences");
|
||||
[self performSegueWithIdentifier:@"UserProfile" sender:self];
|
||||
[self performSegueWithIdentifier:@"MP_UserProfile" sender:self];
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
inf(@"Action: Other Apps");
|
||||
[self performSegueWithIdentifier:@"OtherApps" sender:self];
|
||||
[self performSegueWithIdentifier:@"MP_OtherApps" sender:self];
|
||||
break;
|
||||
}
|
||||
//#if defined(ADHOC) && defined(TESTFLIGHT_SDK_VERSION)
|
||||
|
@ -7,27 +7,22 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "MPElementEntity.h"
|
||||
#import "MPElementPickerDelegate.h"
|
||||
|
||||
typedef enum {
|
||||
MPSearchScopeAll,
|
||||
MPSearchScopeOutdated,
|
||||
} MPSearchScope;
|
||||
|
||||
@protocol MPSearchResultsDelegate<NSObject>
|
||||
|
||||
- (void)didSelectElement:(MPElementEntity *)element;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPSearchDelegate : NSObject<UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UISearchDisplayDelegate, NSFetchedResultsControllerDelegate>
|
||||
|
||||
@property (strong, nonatomic) NSDateFormatter *dateFormatter;
|
||||
@property (strong, readonly) NSFetchedResultsController *fetchedResultsController;
|
||||
@property (strong, nonatomic) NSString *query;
|
||||
@property (strong, nonatomic) UILabel *tipView;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet id<MPSearchResultsDelegate> delegate;
|
||||
@property (weak, nonatomic) IBOutlet id<MPElementPickerDelegate> delegate;
|
||||
@property (strong, nonatomic) IBOutlet UISearchDisplayController *searchDisplayController;
|
||||
@property (weak, nonatomic) IBOutlet UIView *searchTipContainer;
|
||||
|
||||
|
@ -9,12 +9,7 @@
|
||||
#import "MPSearchDelegate.h"
|
||||
#import "MPAppDelegate.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
|
||||
@interface MPSearchDelegate (Private)
|
||||
|
||||
- (void)configureCell:(UITableViewCell *)cell inTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
@end
|
||||
#import "MPMainViewController.h"
|
||||
|
||||
@implementation MPSearchDelegate {
|
||||
|
||||
@ -28,7 +23,6 @@
|
||||
|
||||
self.dateFormatter = [NSDateFormatter new];
|
||||
self.dateFormatter.dateStyle = NSDateFormatterShortStyle;
|
||||
self.query = @"";
|
||||
|
||||
self.tipView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 170)];
|
||||
self.tipView.textAlignment = NSTextAlignmentCenter;
|
||||
@ -60,6 +54,7 @@
|
||||
|
||||
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
|
||||
sectionNameKeyPath:nil cacheName:nil];
|
||||
_fetchedResultsController.delegate = self;
|
||||
@ -68,6 +63,11 @@
|
||||
return _fetchedResultsController;
|
||||
}
|
||||
|
||||
- (void)searchBarBookmarkButtonClicked:(UISearchBar *)searchBar {
|
||||
|
||||
[((MPMainViewController *)self.delegate) performSegueWithIdentifier:@"MP_AllSites" sender:self];
|
||||
}
|
||||
|
||||
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
|
||||
|
||||
UITableView *tableView = self.searchDisplayController.searchResultsTableView;
|
||||
@ -89,12 +89,6 @@
|
||||
|
||||
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
|
||||
|
||||
if (searchBar.searchResultsButtonSelected && !searchText.length)
|
||||
searchBar.text = @" ";
|
||||
|
||||
self.query = [searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
if (!self.query)
|
||||
self.query = @"";
|
||||
}
|
||||
|
||||
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
|
||||
@ -113,8 +107,6 @@
|
||||
|
||||
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
|
||||
|
||||
controller.searchBar.text = controller.searchBar.searchResultsButtonSelected? @" ": @"";
|
||||
self.query = @"";
|
||||
}
|
||||
|
||||
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
|
||||
@ -154,31 +146,30 @@
|
||||
|
||||
- (void)fetchData {
|
||||
|
||||
|
||||
NSString *query = [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
if (!query)
|
||||
return;
|
||||
|
||||
MPUserEntity *activeUser = [MPAppDelegate get].activeUser;
|
||||
assert(self.query);
|
||||
assert(activeUser);
|
||||
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"user == %@", activeUser];
|
||||
if (self.query.length)
|
||||
predicate = [NSCompoundPredicate
|
||||
andPredicateWithSubpredicates:@[[NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] %@", self.query],
|
||||
predicate]];
|
||||
if (!activeUser)
|
||||
return;
|
||||
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"user == %@ AND name BEGINSWITH[cd] %@", activeUser, query];
|
||||
switch ((MPSearchScope)self.searchDisplayController.searchBar.selectedScopeButtonIndex) {
|
||||
|
||||
case MPSearchScopeAll:
|
||||
break;
|
||||
case MPSearchScopeOutdated:
|
||||
predicate = [NSCompoundPredicate
|
||||
andPredicateWithSubpredicates:@[[NSPredicate predicateWithFormat:@"requiresExplicitMigration_ == YES"],
|
||||
predicate]];
|
||||
andPredicateWithSubpredicates:@[[NSPredicate predicateWithFormat:@"requiresExplicitMigration_ == YES"], predicate]];
|
||||
break;
|
||||
}
|
||||
self.fetchedResultsController.fetchRequest.predicate = predicate;
|
||||
|
||||
NSError *error;
|
||||
if (![self.fetchedResultsController performFetch:&error])
|
||||
err(@"Couldn't fetch elements: %@", error);
|
||||
err(@"Couldn't fetch elements: %@", error);
|
||||
|
||||
[self.searchDisplayController.searchBar.superview enumerateSubviews:^(UIView *subview, BOOL *stop, BOOL *recurse) {
|
||||
CGRect searchBarFrame = self.searchDisplayController.searchBar.frame;
|
||||
@ -258,12 +249,13 @@
|
||||
NSArray *sections = [self.fetchedResultsController sections];
|
||||
NSUInteger sectionCount = [sections count];
|
||||
|
||||
if ([self.query length]) {
|
||||
NSString *query = [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
if ([query length]) {
|
||||
__block BOOL hasExactQueryMatch = NO;
|
||||
[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:self.query]) {
|
||||
if ([[obj_ name] isEqualToString:query]) {
|
||||
hasExactQueryMatch = YES;
|
||||
*stop_ = YES;
|
||||
}
|
||||
@ -327,7 +319,8 @@
|
||||
element.uses, [self.dateFormatter stringFromDate:element.lastUsed]];
|
||||
} else {
|
||||
// "New" section
|
||||
cell.textLabel.text = self.query;
|
||||
NSString *query = [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
cell.textLabel.text = query;
|
||||
cell.detailTextLabel.text = @"Create a new site.";
|
||||
}
|
||||
}
|
||||
@ -339,7 +332,7 @@
|
||||
|
||||
else {
|
||||
// "New" section.
|
||||
NSString *siteName = self.query;
|
||||
NSString *siteName = [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
[PearlAlert showAlertWithTitle:@"New Site"
|
||||
message:PearlString(@"Do you want to create a new site named:\n%@", siteName)
|
||||
viewStyle:UIAlertViewStyleDefault
|
||||
|
@ -496,7 +496,6 @@
|
||||
|
||||
// Lay out user name label.
|
||||
self.nameLabel.text = targetedAvatar? (targetedUser? targetedUser.name: @"New User"): nil;
|
||||
dbg(@"targetedAvatar: %@, targetedUser: %@, nameLabel: %@", targetedAvatar, targetedUser, self.nameLabel.text);
|
||||
self.nameLabel.bounds = CGRectSetHeight(self.nameLabel.bounds,
|
||||
[self.nameLabel.text sizeWithFont:self.nameLabel.font
|
||||
constrainedToSize:CGSizeMake(self.nameLabel.bounds.size.width - 10, 100)
|
||||
|
@ -25,9 +25,4 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (MPiOSConfig *)get {
|
||||
|
||||
return (MPiOSConfig *)[super get];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -633,7 +633,7 @@ Your passwords will be AES-encrypted with your master password.</string>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<searchBar contentMode="redraw" barStyle="blackOpaque" placeholder="Site name" showsSearchResultsButton="YES" id="qeo-n2-WVh">
|
||||
<searchBar contentMode="redraw" barStyle="blackOpaque" placeholder="Site name" showsBookmarkButton="YES" id="qeo-n2-WVh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<gestureRecognizers/>
|
||||
@ -643,25 +643,6 @@ Your passwords will be AES-encrypted with your master password.</string>
|
||||
<string>Outdated</string>
|
||||
</scopeButtonTitles>
|
||||
</searchBar>
|
||||
<view userInteractionEnabled="NO" contentMode="scaleToFill" id="zOR-Du-qRL" userLabel="View - Search Tip">
|
||||
<rect key="frame" x="10" y="15" width="300" height="60"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black_top.png" id="ORD-Xv-bOQ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="300" height="60"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<rect key="contentStretch" x="0.15000000000000002" y="0.15000000000000002" width="0.69999999999999973" height="0.69999999999999973"/>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Begin by entering the name of your site." textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" id="21b-bH-lR9">
|
||||
<rect key="frame" x="-20" y="26" width="340" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<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"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
@ -685,6 +666,25 @@ Your passwords will be AES-encrypted with your master password.</string>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<view userInteractionEnabled="NO" contentMode="scaleToFill" id="zOR-Du-qRL" userLabel="View - Search Tip">
|
||||
<rect key="frame" x="10" y="15" width="300" height="60"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black_top.png" id="ORD-Xv-bOQ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="300" height="60"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<rect key="contentStretch" x="0.15000000000000002" y="0.15000000000000002" width="0.69999999999999973" height="0.69999999999999973"/>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Begin by entering the name of your site." textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" id="21b-bH-lR9">
|
||||
<rect key="frame" x="-20" y="26" width="340" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<view userInteractionEnabled="NO" contentMode="scaleToFill" id="UM8-56-kAO" userLabel="View - Tool Tip">
|
||||
<rect key="frame" x="10" y="15" width="266" height="60"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
@ -935,8 +935,9 @@ L4m3P4sSw0rD</string>
|
||||
<outlet property="toolTipEditIcon" destination="KEn-n3-qhX" id="zEV-tR-Egq"/>
|
||||
<outlet property="typeButton" destination="xYM-e3-BMg" id="xvn-nR-MRV"/>
|
||||
<outlet property="typeTipContainer" destination="g55-0m-WjS" id="KZ9-KV-NMh"/>
|
||||
<segue destination="oLN-6u-GLb" kind="push" identifier="UserProfile" id="tKN-Sw-S5J"/>
|
||||
<segue destination="2Th-Tb-22a" kind="modal" identifier="OtherApps" id="Io5-cz-v9Y"/>
|
||||
<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"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="mK2-p1-3zC" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
@ -1214,7 +1215,7 @@ Pink fluffy door frame.</string>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="hkm-U7-Dm7" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="463" y="-381"/>
|
||||
<point key="canvasLocation" x="455" y="-381"/>
|
||||
</scene>
|
||||
<!--Unlock View Controller-->
|
||||
<scene sceneID="HkH-JR-Fhy">
|
||||
@ -1628,6 +1629,77 @@ You could use the word wall for inspiration in finding a memorable master passw
|
||||
</objects>
|
||||
<point key="canvasLocation" x="455" y="1425"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="rAg-OU-9QV">
|
||||
<objects>
|
||||
<navigationController definesPresentationContext="YES" id="KZ9-Bb-FN7" sceneMemberID="viewController">
|
||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" barStyle="blackOpaque" prompted="NO"/>
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" barStyle="blackOpaque" id="s0n-kY-htJ">
|
||||
<rect key="frame" x="0.0" y="-44" width="0.0" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</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="996" y="1425"/>
|
||||
</scene>
|
||||
<!--All Sites View Controller - All Sites-->
|
||||
<scene sceneID="I7c-vt-d2s">
|
||||
<objects>
|
||||
<tableViewController id="idA-Pj-1U9" customClass="MPAllSitesViewController" 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="416"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<prototypes>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="MPElementSearch" textLabel="UGt-vQ-i5K" detailTextLabel="Xv8-kG-Tap" style="IBUITableViewCellStyleSubtitle" id="3eb-mJ-xFj">
|
||||
<rect key="frame" x="0.0" y="22" width="320" height="48"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="47"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="UGt-vQ-i5K">
|
||||
<rect key="frame" x="10" y="4" width="38" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Subtitle" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Xv8-kG-Tap">
|
||||
<rect key="frame" x="10" y="26" width="47" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<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>
|
||||
</navigationItem>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="abw-PC-pyQ" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1537" y="1425"/>
|
||||
</scene>
|
||||
<!--Apps View Controller-->
|
||||
<scene sceneID="3cC-Qq-rgU">
|
||||
<objects>
|
||||
@ -2077,13 +2149,13 @@ You could use the word wall for inspiration in finding a memorable master passw
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="LHv-Mk-8Kp" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1004" y="-381"/>
|
||||
<point key="canvasLocation" x="996" y="-381"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="Square-bottom.png" width="551" height="58"/>
|
||||
<image name="avatar-0.png" width="110" height="110"/>
|
||||
<image name="background.png" width="480" height="480"/>
|
||||
<image name="background.png" width="568" height="568"/>
|
||||
<image name="book.png" width="320" height="480"/>
|
||||
<image name="guide_page_0.png" width="320" height="480"/>
|
||||
<image name="guide_page_1.png" width="320" height="480"/>
|
||||
@ -2121,6 +2193,146 @@ You could use the word wall for inspiration in finding a memorable master passw
|
||||
<image name="ui_spinner.png" width="75" height="75"/>
|
||||
<image name="ui_textfield.png" width="158" height="34"/>
|
||||
</resources>
|
||||
<classes>
|
||||
<class className="MPAllSitesViewController" superclassName="UITableViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPAllSitesViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="close:"/>
|
||||
<relationship kind="outlet" name="delegate"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPAppViewController" superclassName="UIViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPAppViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="deblock:" candidateClass="UIButton"/>
|
||||
<relationship kind="action" name="gorillas:" candidateClass="UIButton"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPAppsViewController" superclassName="UIViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPAppsViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="exit"/>
|
||||
<relationship kind="outlet" name="pagePositionView" candidateClass="UIImageView"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPGuideViewController" superclassName="UIViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPGuideViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="close"/>
|
||||
<relationship kind="outlet" name="pageControl" candidateClass="UIPageControl"/>
|
||||
<relationship kind="outlet" name="scrollView" candidateClass="UIScrollView"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPMainViewController" superclassName="UIViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPMainViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="action:" candidateClass="UIBarButtonItem"/>
|
||||
<relationship kind="action" name="closeAlert"/>
|
||||
<relationship kind="action" name="closeOutdatedAlert"/>
|
||||
<relationship kind="action" name="copyContent"/>
|
||||
<relationship kind="action" name="editLoginName:" candidateClass="UILongPressGestureRecognizer"/>
|
||||
<relationship kind="action" name="editPassword"/>
|
||||
<relationship kind="action" name="incrementPasswordCounter"/>
|
||||
<relationship kind="action" name="infoOutdatedAlert"/>
|
||||
<relationship kind="action" name="panHelpDown:" candidateClass="UIPanGestureRecognizer"/>
|
||||
<relationship kind="action" name="panHelpUp:" candidateClass="UIPanGestureRecognizer"/>
|
||||
<relationship kind="action" name="resetPasswordCounter:" candidateClass="UILongPressGestureRecognizer"/>
|
||||
<relationship kind="action" name="searchOutdatedElements"/>
|
||||
<relationship kind="action" name="toggleUser"/>
|
||||
<relationship kind="action" name="upgradePassword"/>
|
||||
<relationship kind="outlet" name="actionsTipContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="alertBody" candidateClass="UITextView"/>
|
||||
<relationship kind="outlet" name="alertContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="alertTitle" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="contentContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="contentField" candidateClass="UITextField"/>
|
||||
<relationship kind="outlet" name="contentTipBody" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="contentTipContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="displayContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="helpContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="helpView" candidateClass="UIWebView"/>
|
||||
<relationship kind="outlet" name="loginNameContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="loginNameField" candidateClass="UITextField"/>
|
||||
<relationship kind="outlet" name="loginNameTipBody" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="loginNameTipContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="outdatedAlertBack" candidateClass="UIImageView"/>
|
||||
<relationship kind="outlet" name="outdatedAlertCloseButton" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="outdatedAlertContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="passwordCounter" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="passwordEdit" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="passwordIncrementer" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="passwordUpgrade" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="passwordUser" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="pullDownGesture" candidateClass="UIPanGestureRecognizer"/>
|
||||
<relationship kind="outlet" name="pullDownView" candidateClass="UIImageView"/>
|
||||
<relationship kind="outlet" name="pullUpGesture" candidateClass="UIPanGestureRecognizer"/>
|
||||
<relationship kind="outlet" name="pullUpView" candidateClass="UIImageView"/>
|
||||
<relationship kind="outlet" name="searchDelegate" candidateClass="MPSearchDelegate"/>
|
||||
<relationship kind="outlet" name="searchTipContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="siteName" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="toolTipBody" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="toolTipContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="toolTipEditIcon" candidateClass="UIImageView"/>
|
||||
<relationship kind="outlet" name="typeButton" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="typeTipContainer" candidateClass="UIView"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPPreferencesViewController" superclassName="UITableViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPPreferencesViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="didToggleSwitch:" candidateClass="UISwitch"/>
|
||||
<relationship kind="action" name="settings:"/>
|
||||
<relationship kind="outlet" name="avatarTemplate" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="avatarsView" candidateClass="UIScrollView"/>
|
||||
<relationship kind="outlet" name="changeMPCell" candidateClass="UITableViewCell"/>
|
||||
<relationship kind="outlet" name="defaultTypeLabel" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="exportCell" candidateClass="UITableViewCell"/>
|
||||
<relationship kind="outlet" name="savePasswordSwitch" candidateClass="UISwitch"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPSearchDelegate" superclassName="NSObject">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPSearchDelegate.h"/>
|
||||
<relationships>
|
||||
<relationship kind="outlet" name="delegate"/>
|
||||
<relationship kind="outlet" name="searchDisplayController" candidateClass="UISearchDisplayController"/>
|
||||
<relationship kind="outlet" name="searchTipContainer" candidateClass="UIView"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPTypeViewController" superclassName="UITableViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPTypeViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="outlet" name="recommendedTipContainer" candidateClass="UIView"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPUnlockViewController" superclassName="UIViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPUnlockViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="add:" candidateClass="UIButton"/>
|
||||
<relationship kind="action" name="facebook:" candidateClass="UIButton"/>
|
||||
<relationship kind="action" name="google:" candidateClass="UIButton"/>
|
||||
<relationship kind="action" name="mail:" candidateClass="UIButton"/>
|
||||
<relationship kind="action" name="targetedUserAction:" candidateClass="UILongPressGestureRecognizer"/>
|
||||
<relationship kind="action" name="twitter:" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="avatarTemplate" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="avatarsView" candidateClass="UIScrollView"/>
|
||||
<relationship kind="outlet" name="createPasswordTipView" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="loadingUsersIndicator" candidateClass="UIActivityIndicatorView"/>
|
||||
<relationship kind="outlet" name="nameLabel" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="newsView" candidateClass="UIWebView"/>
|
||||
<relationship kind="outlet" name="oldNameLabel" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="passwordField" candidateClass="UITextField"/>
|
||||
<relationship kind="outlet" name="passwordFieldLabel" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="passwordTipLabel" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="passwordTipView" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="passwordView" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="spinner" candidateClass="UIImageView"/>
|
||||
<relationship kind="outlet" name="targetedUserActionGesture" candidateClass="UILongPressGestureRecognizer"/>
|
||||
<relationship kind="outlet" name="tip" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="uiContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="wordWall" candidateClass="UIView"/>
|
||||
</relationships>
|
||||
</class>
|
||||
</classes>
|
||||
<simulatedMetricsContainer key="defaultSimulatedMetrics">
|
||||
<nil key="statusBar"/>
|
||||
<simulatedOrientationMetrics key="orientation"/>
|
||||
|
6
Resources/MasterPassword.sketch/fonts
Normal file
6
Resources/MasterPassword.sketch/fonts
Normal file
@ -0,0 +1,6 @@
|
||||
SourceCodePro-Light
|
||||
LucidaGrande
|
||||
HelveticaNeue-Medium
|
||||
LucidaGrande
|
||||
HelveticaNeue-Light
|
||||
Inconsolata
|
@ -341,20 +341,7 @@
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
By default, Master Password uses the <em>Long Password</em> type for any new passwords. The user is able to choose a different password type, which is normally only done if the site's password policy is incompatible with the output password produced by this type.
|
||||
</p>
|
||||
<p>
|
||||
To create the create the output password, the bytes in the <code>seed</code> are encoded according to the template. The first <code>seed</code> byte is used to determine which of the type's templates to use for encoding an output password. We take the byte value of the first <code>seed</code> byte modulo the amount of templates set for the chosen password type and use the result as a zero-based index in the template list for the password type.
|
||||
</p>
|
||||
<code><pre>
|
||||
templates = [ "CvcvCvcvnoCvcv", "CvcvnoCvcvCvcv", "CvcvCvcvCvcvno", ... ]
|
||||
template = templates[ seed[0] % count( templates ) ]
|
||||
</pre></code>
|
||||
<p>
|
||||
Now that we know what template to use for building our output password, all that's left is to iterate the template, and produce a character of password output for each step. When we iterate the template (index <code>i</code>), we look in the character group identified by the character (string <code>passChars</code>) in the template at index <code>i</code>.
|
||||
</p>
|
||||
<p>
|
||||
The following character groups (<code>passChars</code>) are defined:
|
||||
Where each of the letters above expand any of the characters in their respective character group:
|
||||
<ul>
|
||||
<li><p>Template character: <code>V</code>
|
||||
<ul>
|
||||
@ -403,6 +390,19 @@
|
||||
</p></li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
By default, Master Password uses the <em>Long Password</em> type for any new passwords. The user is able to choose a different password type, which is normally only done if the site's password policy is incompatible with the output password produced by this type.
|
||||
</p>
|
||||
<p>
|
||||
To create the output password, the bytes in the <code>seed</code> are encoded according to the template. The first <code>seed</code> byte is used to determine which of the type's templates to use for encoding an output password. We take the byte value of the first <code>seed</code> byte modulo the amount of templates set for the chosen password type and use the result as a zero-based index in the template list for the password type.
|
||||
</p>
|
||||
<code><pre>
|
||||
templates = [ "CvcvCvcvnoCvcv", "CvcvnoCvcvCvcv", "CvcvCvcvCvcvno", ... ]
|
||||
template = templates[ seed[0] % count( templates ) ]
|
||||
</pre></code>
|
||||
<p>
|
||||
Now that we know what template to use for building our output password, all that's left is to iterate the template, and produce a character of password output for each step. When we iterate the template (index <code>i</code>), we look in the character group identified by the character (string <code>passChars</code>) in the template at index <code>i</code>.
|
||||
</p>
|
||||
<p>
|
||||
We use the <code>seed</code>'s byte value at index <code>i + 1</code> modulo the amount of characters in the character class to determine which character (<code>passChar</code>) in the class to use for the output password at index <code>i</code>.
|
||||
</p>
|
||||
|
Loading…
Reference in New Issue
Block a user