diff --git a/External/iCloudStoreManager b/External/iCloudStoreManager index 39ac68e0..261344e1 160000 --- a/External/iCloudStoreManager +++ b/External/iCloudStoreManager @@ -1 +1 @@ -Subproject commit 39ac68e0fb34f0c6e2f455f132b56fd158630ee7 +Subproject commit 261344e1faffa59b570343ca11be5dda412242e2 diff --git a/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword-iOS.xcodeproj/project.pbxproj index 90cd7d54..4a84d5e7 100644 --- a/MasterPassword-iOS.xcodeproj/project.pbxproj +++ b/MasterPassword-iOS.xcodeproj/project.pbxproj @@ -12,12 +12,14 @@ 93D392B30CE6C58A9A905E0A /* MPAlgorithmV0.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3938863322199C3E7E2E3 /* MPAlgorithmV0.m */; }; 93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; }; 93D3932889B6B4206E66A6D6 /* PearlEMail.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */; }; + 93D3944DE5E21C69AA8CC6D4 /* MPElementListController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39C56514D6DE72F5FB83E /* MPElementListController.m */; }; 93D394744B5485303B326ECB /* MPAlgorithm.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39B0DF5E3C56355186738 /* MPAlgorithm.m */; }; 93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; }; 93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; }; + 93D3995C0452184780AECD0C /* MPElementListSearchController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D394FF2CF2935332260C50 /* MPElementListSearchController.m */; }; 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 */; }; + 93D39BD0F5C7C76678712500 /* MPElementListAllViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39F16ADA770D8C13B4555 /* MPElementListAllViewController.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 */; }; @@ -184,7 +186,6 @@ DAB8D46415036BCF00CED3BC /* MPiOSConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D44915036BCF00CED3BC /* MPiOSConfig.m */; }; DAB8D46515036BCF00CED3BC /* MPGuideViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D44B15036BCF00CED3BC /* MPGuideViewController.m */; }; DAB8D46615036BCF00CED3BC /* MPMainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D44D15036BCF00CED3BC /* MPMainViewController.m */; }; - DAB8D46715036BCF00CED3BC /* MPSearchDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D44F15036BCF00CED3BC /* MPSearchDelegate.m */; }; DAB8D46815036BCF00CED3BC /* MPTypeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D45115036BCF00CED3BC /* MPTypeViewController.m */; }; DAB8D46915036BCF00CED3BC /* MPUnlockViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D45315036BCF00CED3BC /* MPUnlockViewController.m */; }; DAB8D46A15036BCF00CED3BC /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DAB8D45415036BCF00CED3BC /* Settings.bundle */; }; @@ -821,6 +822,7 @@ DAE4C98D157E63BE00EFE047 /* avatar-18.png in Resources */ = {isa = PBXBuildFile; fileRef = DAE4C967157E63BE00EFE047 /* avatar-18.png */; }; DAE4C98E157E63BE00EFE047 /* avatar-18@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAE4C968157E63BE00EFE047 /* avatar-18@2x.png */; }; DAEBC45314F6364500987BF6 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; }; + DAF02C5716C695F100489F65 /* MPElementListCellView.xib in Resources */ = {isa = PBXBuildFile; fileRef = DAF02C5616C695F100489F65 /* MPElementListCellView.xib */; }; DAFE4A1315039824003ABA7C /* NSObject+PearlExport.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45D815039823003ABA7C /* NSObject+PearlExport.h */; }; DAFE4A1415039824003ABA7C /* NSObject+PearlExport.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE45D915039823003ABA7C /* NSObject+PearlExport.m */; }; DAFE4A1515039824003ABA7C /* NSString+PearlNSArrayFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45DA15039823003ABA7C /* NSString+PearlNSArrayFormat.h */; }; @@ -969,26 +971,30 @@ /* Begin PBXFileReference section */ 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = ""; }; - 93D390B3209212B3049BEC2D /* MPAllSitesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAllSitesViewController.h; sourceTree = ""; }; + 93D390B3209212B3049BEC2D /* MPElementListAllViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementListAllViewController.h; sourceTree = ""; }; 93D390DABAE4368E90E37340 /* MPAppsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppsViewController.m; sourceTree = ""; }; 93D3938863322199C3E7E2E3 /* MPAlgorithmV0.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV0.m; sourceTree = ""; }; 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = ""; }; + 93D393BB5C57D7FCC57A1999 /* MPElementListSearchController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementListSearchController.h; sourceTree = ""; }; 93D393BB973253D4BAAC84AA /* PearlEMail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlEMail.m; sourceTree = ""; }; 93D394077F8FAB8167647187 /* Twitter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Twitter.framework; path = System/Library/Frameworks/Twitter.framework; sourceTree = SDKROOT; }; + 93D394FF2CF2935332260C50 /* MPElementListSearchController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementListSearchController.m; sourceTree = ""; }; 93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = ""; }; 93D396E57F8AB8BCF00ADFF6 /* MPAppViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppViewController.h; sourceTree = ""; }; 93D397CC23446E7E66640D82 /* MPAppsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppsViewController.h; sourceTree = ""; }; + 93D398BB1AD9781521B5AB56 /* MPElementListController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementListController.h; sourceTree = ""; }; 93D398E394E311C545E0A057 /* MPAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAlgorithm.h; sourceTree = ""; }; 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = ""; }; 93D39AAB616A652A4847E4CF /* MPAlgorithmV0.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAlgorithmV0.h; sourceTree = ""; }; 93D39B0DF5E3C56355186738 /* MPAlgorithm.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithm.m; sourceTree = ""; }; + 93D39C56514D6DE72F5FB83E /* MPElementListController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementListController.m; sourceTree = ""; }; 93D39C68AFA48A13015E4FAC /* MPKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPKey.h; sourceTree = ""; }; 93D39D0EF77FEC36EA0FB334 /* MPAlgorithmV1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAlgorithmV1.h; sourceTree = ""; }; 93D39E070BD3F45B3045A1DA /* MPAppViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppViewController.m; sourceTree = ""; }; 93D39E81EFABC6085AC8AE69 /* MPKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPKey.m; sourceTree = ""; }; 93D39E9D7B9005211E7D5262 /* MPAlgorithmV1.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV1.m; sourceTree = ""; }; - 93D39F16ADA770D8C13B4555 /* MPAllSitesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAllSitesViewController.m; sourceTree = ""; }; - 93D39F37240730C6311B8FBD /* MPElementPickerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementPickerDelegate.h; sourceTree = ""; }; + 93D39F16ADA770D8C13B4555 /* MPElementListAllViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementListAllViewController.m; sourceTree = ""; }; + 93D39F37240730C6311B8FBD /* MPElementListDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementListDelegate.h; sourceTree = ""; }; 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlEMail.h; sourceTree = ""; }; 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; }; @@ -1218,8 +1224,6 @@ DAB8D44B15036BCF00CED3BC /* MPGuideViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPGuideViewController.m; sourceTree = ""; }; DAB8D44C15036BCF00CED3BC /* MPMainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMainViewController.h; sourceTree = ""; }; DAB8D44D15036BCF00CED3BC /* MPMainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMainViewController.m; sourceTree = ""; }; - DAB8D44E15036BCF00CED3BC /* MPSearchDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSearchDelegate.h; sourceTree = ""; }; - DAB8D44F15036BCF00CED3BC /* MPSearchDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSearchDelegate.m; sourceTree = ""; }; DAB8D45015036BCF00CED3BC /* MPTypeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTypeViewController.h; sourceTree = ""; }; DAB8D45115036BCF00CED3BC /* MPTypeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPTypeViewController.m; sourceTree = ""; }; DAB8D45215036BCF00CED3BC /* MPUnlockViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUnlockViewController.h; sourceTree = ""; }; @@ -1925,6 +1929,7 @@ DAE4C967157E63BE00EFE047 /* avatar-18.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-18.png"; sourceTree = ""; }; DAE4C968157E63BE00EFE047 /* avatar-18@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-18@2x.png"; sourceTree = ""; }; DAEBC45214F6364500987BF6 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + DAF02C5616C695F100489F65 /* MPElementListCellView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPElementListCellView.xib; sourceTree = ""; }; DAF83D6B15E02E04009C8D49 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = ""; }; DAFE45D815039823003ABA7C /* NSObject+PearlExport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+PearlExport.h"; sourceTree = ""; }; DAFE45D915039823003ABA7C /* NSObject+PearlExport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+PearlExport.m"; sourceTree = ""; }; @@ -2517,6 +2522,7 @@ children = ( DAB8D9B11503757D00CED3BC /* Supporting Files */, DAB8D44215036BCF00CED3BC /* MainStoryboard_iPhone.storyboard */, + DAF02C5616C695F100489F65 /* MPElementListCellView.xib */, 93D397CC23446E7E66640D82 /* MPAppsViewController.h */, 93D390DABAE4368E90E37340 /* MPAppsViewController.m */, 93D396E57F8AB8BCF00ADFF6 /* MPAppViewController.h */, @@ -2531,16 +2537,18 @@ DAB8D44D15036BCF00CED3BC /* MPMainViewController.m */, DAC728C8157C247B00889EF2 /* MPPreferencesViewController.h */, DAC728C9157C247B00889EF2 /* MPPreferencesViewController.m */, - DAB8D44E15036BCF00CED3BC /* MPSearchDelegate.h */, - DAB8D44F15036BCF00CED3BC /* MPSearchDelegate.m */, DAB8D45015036BCF00CED3BC /* MPTypeViewController.h */, DAB8D45115036BCF00CED3BC /* MPTypeViewController.m */, DAB8D45215036BCF00CED3BC /* MPUnlockViewController.h */, DAB8D45315036BCF00CED3BC /* MPUnlockViewController.m */, DAB8D45415036BCF00CED3BC /* Settings.bundle */, - 93D39F16ADA770D8C13B4555 /* MPAllSitesViewController.m */, - 93D390B3209212B3049BEC2D /* MPAllSitesViewController.h */, - 93D39F37240730C6311B8FBD /* MPElementPickerDelegate.h */, + 93D39F16ADA770D8C13B4555 /* MPElementListAllViewController.m */, + 93D390B3209212B3049BEC2D /* MPElementListAllViewController.h */, + 93D39F37240730C6311B8FBD /* MPElementListDelegate.h */, + 93D39C56514D6DE72F5FB83E /* MPElementListController.m */, + 93D398BB1AD9781521B5AB56 /* MPElementListController.h */, + 93D393BB5C57D7FCC57A1999 /* MPElementListSearchController.h */, + 93D394FF2CF2935332260C50 /* MPElementListSearchController.m */, ); path = iOS; sourceTree = ""; @@ -4574,6 +4582,7 @@ DAB90E9916BD951200D06F4A /* SourceCodePro-Black.otf in Resources */, DAB90E9B16BD951200D06F4A /* SourceCodePro-ExtraLight.otf in Resources */, DAB90EA016BE1B4200D06F4A /* Exo-Bold.otf in Resources */, + DAF02C5716C695F100489F65 /* MPElementListCellView.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4689,7 +4698,6 @@ DAB8D46415036BCF00CED3BC /* MPiOSConfig.m in Sources */, DAB8D46515036BCF00CED3BC /* MPGuideViewController.m in Sources */, DAB8D46615036BCF00CED3BC /* MPMainViewController.m in Sources */, - DAB8D46715036BCF00CED3BC /* MPSearchDelegate.m in Sources */, DAB8D46815036BCF00CED3BC /* MPTypeViewController.m in Sources */, DAB8D46915036BCF00CED3BC /* MPUnlockViewController.m in Sources */, DA600C2515054F3A008E9AB6 /* MPAppDelegate_Key.m in Sources */, @@ -4708,7 +4716,9 @@ DA81253816B8546B00F4732F /* MPElementStoredEntity.m in Sources */, DA81253B16B8546B00F4732F /* MPUserEntity.m in Sources */, DA81253E16B8546C00F4732F /* MPElementGeneratedEntity.m in Sources */, - 93D39BD0F5C7C76678712500 /* MPAllSitesViewController.m in Sources */, + 93D39BD0F5C7C76678712500 /* MPElementListAllViewController.m in Sources */, + 93D3944DE5E21C69AA8CC6D4 /* MPElementListController.m in Sources */, + 93D3995C0452184780AECD0C /* MPElementListSearchController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme b/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme index 9ac0e8fb..e179af1f 100644 --- a/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme +++ b/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme @@ -50,8 +50,8 @@ - * @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() - -@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]; -} - -- (IBAction)add:(id)sender { - - [PearlAlert showAlertWithTitle:@"Add Site" message:nil viewStyle:UIAlertViewStylePlainTextInput initAlert:nil - tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { - if (alert.cancelButtonIndex == buttonIndex) - return; - - NSString *siteName = [alert textFieldAtIndex:0].text; - if (![siteName length]) - return; - - [MPAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { - MPUserEntity *activeUser = [[MPAppDelegate get] activeUserInContext:moc]; - assert(activeUser); - - MPElementType type = activeUser.defaultType; - if (!type) - type = activeUser.defaultType = MPElementTypeGeneratedLong; - NSString *typeEntityClassName = [MPAlgorithmDefault classNameOfType:type]; - - MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityClassName - inManagedObjectContext:moc]; - - element.name = siteName; - element.user = activeUser; - element.type = type; - element.lastUsed = [NSDate date]; - element.version = MPAlgorithmDefaultVersion; - [element saveContext]; - - NSManagedObjectID *elementOID = [element objectID]; - dispatch_async(dispatch_get_main_queue(), ^{ - MPElementEntity *element_ = (MPElementEntity *)[[MPAppDelegate managedObjectContextForThreadIfReady] - objectRegisteredForID:elementOID]; - [self.delegate didSelectElement:element_]; - [self close:nil]; - }); - }]; - - } - cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonOkay, 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)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(@"%d views, last on %@: %@", - element.uses, [self.dateFormatter stringFromDate:element.lastUsed], [element.algorithm shortNameOfType:element.type]); -} - -- (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 diff --git a/MasterPassword/iOS/MPAllSitesViewController.h b/MasterPassword/iOS/MPElementListAllViewController.h similarity index 74% rename from MasterPassword/iOS/MPAllSitesViewController.h rename to MasterPassword/iOS/MPElementListAllViewController.h index 5fc5163f..984cf97b 100644 --- a/MasterPassword/iOS/MPAllSitesViewController.h +++ b/MasterPassword/iOS/MPElementListAllViewController.h @@ -9,7 +9,7 @@ */ // -// MPAllSitesViewController +// MPElementListAllViewController // // Created by Maarten Billemont on 2013-01-31. // Copyright 2013 lhunath (Maarten Billemont). All rights reserved. @@ -17,11 +17,9 @@ #import -#import "MPElementPickerDelegate.h" +#import "MPElementListController.h" -@interface MPAllSitesViewController : UITableViewController - -@property (weak, nonatomic) IBOutlet id delegate; +@interface MPElementListAllViewController : MPElementListController - (IBAction)close:(id)sender; - (IBAction)add:(id)sender; diff --git a/MasterPassword/iOS/MPElementListAllViewController.m b/MasterPassword/iOS/MPElementListAllViewController.m new file mode 100644 index 00000000..2a06b214 --- /dev/null +++ b/MasterPassword/iOS/MPElementListAllViewController.m @@ -0,0 +1,56 @@ +/** + * 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 + * @license http://www.gnu.org/licenses/lgpl-3.0.txt + */ + +// +// MPElementListAllViewController +// +// Created by Maarten Billemont on 2013-01-31. +// Copyright 2013 lhunath (Maarten Billemont). All rights reserved. +// + +#import "MPElementListAllViewController.h" + +@implementation MPElementListAllViewController + +- (IBAction)close:(id)sender { + + [self dismissViewControllerAnimated:YES completion:nil]; +} + +- (IBAction)add:(id)sender { + + [PearlAlert showAlertWithTitle:@"Add Site" message:nil viewStyle:UIAlertViewStylePlainTextInput initAlert:nil + tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { + if (alert.cancelButtonIndex == buttonIndex) + return; + + __weak MPElementListAllViewController *wSelf = self; + [self addElementNamed:[alert textFieldAtIndex:0].text completion:^(BOOL success) { + if (success) + [wSelf close:nil]; + }]; + } + cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonOkay, nil]; +} + +- (void)viewWillAppear:(BOOL)animated { + + [super viewWillAppear:animated]; + + [self updateData]; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + + [super tableView:tableView didSelectRowAtIndexPath:indexPath]; + [self close:nil]; +} + +@end diff --git a/MasterPassword/iOS/MPElementListCellView.xib b/MasterPassword/iOS/MPElementListCellView.xib new file mode 100644 index 00000000..4fcf648c --- /dev/null +++ b/MasterPassword/iOS/MPElementListCellView.xib @@ -0,0 +1,259 @@ + + + + 1552 + 12C60 + 3084 + 1187.34 + 625.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 2083 + + + IBProxyObject + IBUIImageView + IBUILabel + IBUITableViewCell + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 1280 + + + + 1280 + + + + 1280 + + {{10, 4}, {38, 22}} + + + + + 3 + MCAwAA + + NO + YES + 7 + YES + IBCocoaTouchFramework + Title + + 3 + MQA + + + 1 + MSAxIDEAA + + 0 + + 2 + 18 + + + Helvetica-Bold + 18 + 16 + + NO + + + + 1280 + + {{10, 26}, {47, 18}} + + + + NO + YES + 7 + YES + IBCocoaTouchFramework + Subtitle + + 3 + MC42NjY2NjY2NjY3AA + + + 0 + + 1 + 14 + + + Helvetica + 14 + 16 + + NO + + + + {320, 47} + + + + + NO + YES + 4 + YES + IBCocoaTouchFramework + + + + {320, 48} + + + + + 3 + MAA + + IBCocoaTouchFramework + NO + 1 + 0.0 + + MPElementListCell + + + + + + + 274 + {320, 48} + + + _NS:9 + + NO + {{0.10000000000000001, 0.10000000000000001}, {0.79999999999999982, 0.79999999999999982}} + IBCocoaTouchFramework + + NSImage + ui_list_middle.png + + + + + + + + view + + + + 11 + + + + backgroundView + + + + 10 + + + + + + 0 + + + + + + -1 + + + File's Owner + + + -2 + + + + + 4 + + + + + + + + + 5 + + + + + 6 + + + + + 8 + + + + + + + UIViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 11 + + + 0 + IBCocoaTouchFramework + YES + 3 + + ui_list_middle.png + {300, 34} + + 2083 + + diff --git a/MasterPassword/iOS/MPElementListController.h b/MasterPassword/iOS/MPElementListController.h new file mode 100644 index 00000000..53c1a0be --- /dev/null +++ b/MasterPassword/iOS/MPElementListController.h @@ -0,0 +1,28 @@ +// +// Created by lhunath on 2013-02-09. +// +// To change the template use AppCode | Preferences | File Templates. +// + + +#import + +#import "MPElementListDelegate.h" + +typedef enum { + MPSearchScopeAll, + MPSearchScopeOutdated, +} MPSearchScope; + +@interface MPElementListController : UITableViewController + +@property (weak, nonatomic) IBOutlet id delegate; +@property (readonly) NSFetchedResultsController *fetchedResultsController; +@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)customTableViewUpdates; + +@end diff --git a/MasterPassword/iOS/MPElementListController.m b/MasterPassword/iOS/MPElementListController.m new file mode 100644 index 00000000..187deb76 --- /dev/null +++ b/MasterPassword/iOS/MPElementListController.m @@ -0,0 +1,269 @@ +// +// Created by lhunath on 2013-02-09. +// +// To change the template use AppCode | Preferences | File Templates. +// + + +#import "MPElementListController.h" + +#import "MPAppDelegate_Store.h" +#import "MPAppDelegate.h" + +@interface MPElementListController () +@end + +@implementation MPElementListController { + + NSFetchedResultsController *_fetchedResultsController; + NSDateFormatter *_dateFormatter; +} + +- (void)addElementNamed:(NSString *)siteName completion:(void(^)(BOOL success))completion { + + if (![siteName length]) { + if (completion) + completion(false); + return; + } + + [MPAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { + MPUserEntity *activeUser = [[MPAppDelegate get] activeUserInContext:moc]; + assert(activeUser); + + MPElementType type = activeUser.defaultType; + if (!type) + type = activeUser.defaultType = MPElementTypeGeneratedLong; + NSString *typeEntityClassName = [MPAlgorithmDefault classNameOfType:type]; + + MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityClassName + inManagedObjectContext:moc]; + + element.name = siteName; + element.user = activeUser; + element.type = type; + element.lastUsed = [NSDate date]; + element.version = MPAlgorithmDefaultVersion; + [element saveContext]; + + NSManagedObjectID *elementOID = [element objectID]; + dispatch_async(dispatch_get_main_queue(), ^{ + MPElementEntity *element_ = (MPElementEntity *) [[MPAppDelegate managedObjectContextForThreadIfReady] + objectRegisteredForID:elementOID]; + [self.delegate didSelectElement:element_]; + if (completion) + completion(true); + }); + }]; +} + +- (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; +} + +- (NSDateFormatter *)dateFormatter { + + if (!_dateFormatter) + (_dateFormatter = [NSDateFormatter new]).dateStyle = NSDateFormatterShortStyle; + + return _dateFormatter; +} + +- (void)updateData { + + MPUserEntity *activeUser = [MPAppDelegate get].activeUser; + if (!activeUser) + return; + + // Build predicate. + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"user == %@", activeUser]; + 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; + + NSError *error; + if (![self.fetchedResultsController performFetch:&error]) + err(@"Couldn't fetch elements: %@", error); + else + [self.tableView reloadData]; +} + +- (void)customTableViewUpdates { + +} + +// See MP-14, also crashes easily on internal assertions etc.. +- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { + + dbg(@"%@", NSStringFromSelector(_cmd)); + [self.tableView beginUpdates]; +} + +- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject + atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { + + switch (type) { + + case NSFetchedResultsChangeInsert: + dbg(@"%@ -- NSFetchedResultsChangeInsert:%@", NSStringFromSelector(_cmd), anObject); + [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; + break; + + case NSFetchedResultsChangeDelete: + dbg(@"%@ -- NSFetchedResultsChangeDelete:%@", NSStringFromSelector(_cmd), anObject); + [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; + break; + + case NSFetchedResultsChangeUpdate: + dbg(@"%@ -- NSFetchedResultsChangeUpdate:%@", NSStringFromSelector(_cmd), anObject); + [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; + break; + + case NSFetchedResultsChangeMove: + dbg(@"%@ -- NSFetchedResultsChangeMove:%@", NSStringFromSelector(_cmd), anObject); + [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] + withRowAnimation:UITableViewRowAnimationAutomatic]; + [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] + withRowAnimation:UITableViewRowAnimationAutomatic]; + break; + } +} + +- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )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); + [self customTableViewUpdates]; + [self.tableView endUpdates]; +} + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + + NSInteger integer = (NSInteger)[[self.fetchedResultsController sections] count]; + dbg(@"%@ = %d", NSStringFromSelector(_cmd), integer); + return integer; +} + +- (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; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MPElementListCell"]; + if (!cell) + cell = (UITableViewCell *) [[UIViewController alloc] initWithNibName:@"MPElementListCellView" bundle:nil].view; + + [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(@"%d views, last on %@: %@", + element.uses, [self.dateFormatter stringFromDate:element.lastUsed], [element.algorithm shortNameOfType:element.type]); +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + + [self.delegate didSelectElement:[self.fetchedResultsController objectAtIndexPath:indexPath]]; +} + +- (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 diff --git a/MasterPassword/iOS/MPElementPickerDelegate.h b/MasterPassword/iOS/MPElementListDelegate.h similarity index 88% rename from MasterPassword/iOS/MPElementPickerDelegate.h rename to MasterPassword/iOS/MPElementListDelegate.h index 2c62a42e..cae2b378 100644 --- a/MasterPassword/iOS/MPElementPickerDelegate.h +++ b/MasterPassword/iOS/MPElementListDelegate.h @@ -9,7 +9,7 @@ */ // -// MPElementPickerDelegate +// MPElementListDelegate // // Created by Maarten Billemont on 2013-01-31. // Copyright 2013 lhunath (Maarten Billemont). All rights reserved. @@ -17,7 +17,7 @@ #import "MPElementEntity.h" -@protocol MPElementPickerDelegate +@protocol MPElementListDelegate - (void)didSelectElement:(MPElementEntity *)element; diff --git a/MasterPassword/iOS/MPElementListSearchController.h b/MasterPassword/iOS/MPElementListSearchController.h new file mode 100644 index 00000000..b5062b28 --- /dev/null +++ b/MasterPassword/iOS/MPElementListSearchController.h @@ -0,0 +1,19 @@ +// +// MPSearchDelegate.h +// MasterPassword +// +// Created by Maarten Billemont on 04/01/12. +// Copyright (c) 2012 Lyndir. All rights reserved. +// + +#import +#import "MPElementListController.h" + +@interface MPElementListSearchController : MPElementListController + +@property (strong, nonatomic) UILabel *tipView; + +@property (strong, nonatomic) IBOutlet UISearchDisplayController *searchDisplayController; +@property (weak, nonatomic) IBOutlet UIView *searchTipContainer; + +@end diff --git a/MasterPassword/iOS/MPElementListSearchController.m b/MasterPassword/iOS/MPElementListSearchController.m new file mode 100644 index 00000000..f90ae2c2 --- /dev/null +++ b/MasterPassword/iOS/MPElementListSearchController.m @@ -0,0 +1,259 @@ +// +// MPSearchDelegate.m +// MasterPassword +// +// Created by Maarten Billemont on 04/01/12. +// Copyright (c) 2012 Lyndir. All rights reserved. +// + +#import "MPElementListSearchController.h" +#import "MPMainViewController.h" +#import "MPAppDelegate.h" + + +@interface MPElementListSearchController () + +@property (nonatomic) BOOL newSiteSectionWasNeeded; + +@end + +@implementation MPElementListSearchController +@synthesize searchDisplayController; + +- (id)init { + + if (!(self = [super init])) + return nil; + + self.tipView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 170)]; + self.tipView.textAlignment = NSTextAlignmentCenter; + self.tipView.backgroundColor = [UIColor clearColor]; + self.tipView.textColor = [UIColor lightTextColor]; + self.tipView.shadowColor = [UIColor blackColor]; + self.tipView.shadowOffset = CGSizeMake(0, -1); + self.tipView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight + | UIViewAutoresizingFlexibleBottomMargin; + self.tipView.numberOfLines = 0; + self.tipView.font = [UIFont systemFontOfSize:14]; + self.tipView.text = + @"Tip:\n" + @"Name your sites by their domain name:\n" + @"apple.com, twitter.com\n\n" + @"For email accounts, use the address:\n" + @"john@apple.com, john@gmail.com"; + + return self; +} + +- (void)searchBarBookmarkButtonClicked:(UISearchBar *)searchBar { + + [((MPMainViewController *)self.delegate) performSegueWithIdentifier:@"MP_AllSites" sender:self]; +} + +- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { + + // Simulate a tap on the first visible row. + UITableView *tableView = self.searchDisplayController.searchResultsTableView; + for (NSInteger section = 0; section < [self numberOfSectionsInTableView:tableView]; ++section) { + + if (![self tableView:tableView numberOfRowsInSection:section]) + continue; + + [self tableView:tableView didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]]; + } +} + +- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { + + [self.delegate didSelectElement:nil]; +} + +- (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; + }]; +} + +- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller { + + [self updateData]; +} + +- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller { + + controller.searchBar.prompt = nil; + controller.searchBar.searchResultsButtonSelected = NO; + controller.searchBar.selectedScopeButtonIndex = MPSearchScopeAll; + controller.searchBar.showsScopeBar = NO; +} + +- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView { + + tableView.backgroundColor = [UIColor blackColor]; + tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + tableView.rowHeight = 48.0f; + + self.tableView = tableView; +} + +- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString { + + [self updateData]; + return NO; +} + +- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption { + + [self updateData]; + return NO; +} + + +- (void)updateData { + + [super updateData]; + + UISearchBar *searchBar = self.searchDisplayController.searchBar; + CGRect searchBarFrame = searchBar.frame; + [searchBar.superview enumerateSubviews:^(UIView *subview, BOOL *stop, BOOL *recurse) { + + if ([subview isKindOfClass:[UIControl class]] && + CGPointEqualToPoint( + CGPointDistanceBetweenCGPoints(searchBarFrame.origin, subview.frame.origin), + CGPointMake(0, searchBarFrame.size.height))) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.tipView removeFromSuperview]; + [subview addSubview:self.tipView]; + }); + + *stop = YES; + } + } recurse:NO]; +} + +- (BOOL)newSiteSectionNeeded { + + NSString *query = [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + if (![query length]) + return NO; + + __block BOOL hasExactQueryMatch = NO; + [[self.fetchedResultsController sections] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + id sectionInfo = obj; + [[sectionInfo objects] enumerateObjectsUsingBlock:^(id obj_, NSUInteger idx_, BOOL *stop_) { + if ([[obj_ name] isEqualToString:query]) { + hasExactQueryMatch = YES; + *stop_ = YES; + } + }]; + if (hasExactQueryMatch) + *stop = YES; + }]; + + return !hasExactQueryMatch; +} + +- (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]] + 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]] + withRowAnimation:UITableViewRowAnimationAutomatic]; + } + self.newSiteSectionWasNeeded = newSiteSectionIsNeeded; + dbg(@"wasNeeded->%d", self.newSiteSectionWasNeeded); +} + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + + NSInteger sectionCount = [super numberOfSectionsInTableView:tableView]; + 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) + 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 { + + 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]]); + } +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + + NSUInteger fetchSections = [[self.fetchedResultsController sections] count]; + if (indexPath.section < (NSInteger)fetchSections) { + [super tableView:tableView didSelectRowAtIndexPath:indexPath]; + return; + } + + // "New" section. + 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 + initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { + [tableView deselectRowAtIndexPath:indexPath animated:YES]; + + if (buttonIndex == [alert cancelButtonIndex]) + return; + + [self addElementNamed:siteName completion:nil]; + } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil]; +} + +- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { + + NSUInteger fetchSections = [[self.fetchedResultsController sections] count]; + if (section < (NSInteger)fetchSections) + return [super tableView:tableView titleForHeaderInSection:section]; + + return @""; +} + +- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle +forRowAtIndexPath:(NSIndexPath *)indexPath { + + NSUInteger fetchSections = [[self.fetchedResultsController sections] count]; + if (indexPath.section < (NSInteger)fetchSections) + [super tableView:tableView commitEditingStyle:editingStyle forRowAtIndexPath:indexPath]; +} + + +@end diff --git a/MasterPassword/iOS/MPMainViewController.h b/MasterPassword/iOS/MPMainViewController.h index 2e3bb851..e9d9affb 100644 --- a/MasterPassword/iOS/MPMainViewController.h +++ b/MasterPassword/iOS/MPMainViewController.h @@ -8,12 +8,12 @@ #import #import "MPTypeViewController.h" -#import "MPSearchDelegate.h" +#import "MPElementListSearchController.h" -@interface MPMainViewController : UIViewController +@interface MPMainViewController : UIViewController @property (assign, nonatomic) BOOL siteInfoHidden; -@property (strong, nonatomic) IBOutlet MPSearchDelegate *searchDelegate; +@property (strong, nonatomic) IBOutlet MPElementListSearchController *searchDelegate; @property (strong, nonatomic) IBOutlet UIPanGestureRecognizer *pullDownGesture; @property (strong, nonatomic) IBOutlet UIPanGestureRecognizer *pullUpGesture; @property (weak, nonatomic) IBOutlet UITextField *contentField; diff --git a/MasterPassword/iOS/MPMainViewController.m b/MasterPassword/iOS/MPMainViewController.m index 6a2b20ae..114a58ca 100644 --- a/MasterPassword/iOS/MPMainViewController.m +++ b/MasterPassword/iOS/MPMainViewController.m @@ -10,7 +10,8 @@ #import "MPAppDelegate.h" #import "MPAppDelegate_Key.h" #import "MPAppDelegate_Store.h" -#import "MPAllSitesViewController.h" +#import "MPElementListAllViewController.h" +#import "MPElementListSearchController.h" @interface MPMainViewController() @@ -50,12 +51,12 @@ if ([[segue identifier] isEqualToString:@"MP_ChooseType"]) ((MPTypeViewController *)[segue destinationViewController]).delegate = self; if ([[segue identifier] isEqualToString:@"MP_AllSites"]) - ((MPAllSitesViewController *)[((UINavigationController *)[segue destinationViewController]) topViewController]).delegate = self; + ((MPElementListAllViewController *)[((UINavigationController *)[segue destinationViewController]) topViewController]).delegate = self; } - (void)viewDidLoad { - self.searchDelegate = [MPSearchDelegate new]; + self.searchDelegate = [MPElementListSearchController new]; self.searchDelegate.delegate = self; self.searchDelegate.searchDisplayController = self.searchDisplayController; self.searchDelegate.searchTipContainer = self.searchTipContainer; diff --git a/MasterPassword/iOS/MPSearchDelegate.h b/MasterPassword/iOS/MPSearchDelegate.h deleted file mode 100644 index a290caa9..00000000 --- a/MasterPassword/iOS/MPSearchDelegate.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// MPSearchDelegate.h -// MasterPassword -// -// Created by Maarten Billemont on 04/01/12. -// Copyright (c) 2012 Lyndir. All rights reserved. -// - -#import - -#import "MPElementEntity.h" -#import "MPElementPickerDelegate.h" - -typedef enum { - MPSearchScopeAll, - MPSearchScopeOutdated, -} MPSearchScope; - -@interface MPSearchDelegate : NSObject - -@property (strong, nonatomic) NSDateFormatter *dateFormatter; -@property (strong, readonly) NSFetchedResultsController *fetchedResultsController; -@property (strong, nonatomic) UILabel *tipView; - -@property (weak, nonatomic) IBOutlet id delegate; -@property (strong, nonatomic) IBOutlet UISearchDisplayController *searchDisplayController; -@property (weak, nonatomic) IBOutlet UIView *searchTipContainer; - -@end diff --git a/MasterPassword/iOS/MPSearchDelegate.m b/MasterPassword/iOS/MPSearchDelegate.m deleted file mode 100644 index e377b702..00000000 --- a/MasterPassword/iOS/MPSearchDelegate.m +++ /dev/null @@ -1,416 +0,0 @@ -// -// MPSearchDelegate.m -// MasterPassword -// -// Created by Maarten Billemont on 04/01/12. -// Copyright (c) 2012 Lyndir. All rights reserved. -// - -#import "MPSearchDelegate.h" -#import "MPAppDelegate.h" -#import "MPAppDelegate_Store.h" -#import "MPMainViewController.h" - -@implementation MPSearchDelegate { - - NSFetchedResultsController *_fetchedResultsController; -} - -- (id)init { - - if (!(self = [super init])) - return nil; - - self.dateFormatter = [NSDateFormatter new]; - self.dateFormatter.dateStyle = NSDateFormatterShortStyle; - - self.tipView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 170)]; - self.tipView.textAlignment = NSTextAlignmentCenter; - self.tipView.backgroundColor = [UIColor clearColor]; - self.tipView.textColor = [UIColor lightTextColor]; - self.tipView.shadowColor = [UIColor blackColor]; - self.tipView.shadowOffset = CGSizeMake(0, -1); - self.tipView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight - | UIViewAutoresizingFlexibleBottomMargin; - self.tipView.numberOfLines = 0; - self.tipView.font = [UIFont systemFontOfSize:14]; - self.tipView.text = - @"Tip:\n" - @"Name your sites by their domain name:\n" - @"apple.com, twitter.com\n\n" - @"For email accounts, use the address:\n" - @"john@apple.com, john@gmail.com"; - - return self; -} - -- (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)searchBarBookmarkButtonClicked:(UISearchBar *)searchBar { - - [((MPMainViewController *)self.delegate) performSegueWithIdentifier:@"MP_AllSites" sender:self]; -} - -- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { - - UITableView *tableView = self.searchDisplayController.searchResultsTableView; - for (NSInteger section = 0; section < [self numberOfSectionsInTableView:tableView]; ++section) { - NSInteger rowCount = [self tableView:tableView numberOfRowsInSection:section]; - if (!rowCount) - continue; - - if (rowCount == 1) - [self tableView:tableView didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]]; - break; - } -} - -- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { - - [self.delegate didSelectElement:nil]; -} - -- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { - -} - -- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller { - - controller.searchBar.prompt = @"Enter the site's name:"; - 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; - }]; -} - -- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller { - -} - -- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller { - - controller.searchBar.prompt = nil; - controller.searchBar.searchResultsButtonSelected = NO; - controller.searchBar.selectedScopeButtonIndex = MPSearchScopeAll; - controller.searchBar.showsScopeBar = NO; -} - -- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView { - - tableView.backgroundColor = [UIColor blackColor]; - tableView.separatorStyle = UITableViewCellSeparatorStyleNone; - tableView.rowHeight = 48.0f; -} - -- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString { - - if (!controller.active) - return NO; - - [self fetchData]; - - return YES; -} - -- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption { - - if (!controller.active) - return NO; - - [self fetchData]; - - return YES; -} - -- (void)fetchData { - - - NSString *query = [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - if (!query) - return; - - MPUserEntity *activeUser = [MPAppDelegate get].activeUser; - 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]]; - break; - } - self.fetchedResultsController.fetchRequest.predicate = predicate; - - NSError *error; - if (![self.fetchedResultsController performFetch:&error]) - err(@"Couldn't fetch elements: %@", error); - - [self.searchDisplayController.searchBar.superview enumerateSubviews:^(UIView *subview, BOOL *stop, BOOL *recurse) { - CGRect searchBarFrame = self.searchDisplayController.searchBar.frame; - if ([subview isKindOfClass:[UIControl class]] && - CGPointEqualToPoint( - CGPointDistanceBetweenCGPoints(searchBarFrame.origin, subview.frame.origin), - CGPointMake(0, searchBarFrame.size.height))) { - [self.tipView removeFromSuperview]; - [subview addSubview:self.tipView]; - *stop = YES; - } - } recurse:NO]; -} - -// 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)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.searchDisplayController.searchResultsTableView reloadData]; - // [self.searchDisplayController.searchResultsTableView endUpdates]; -} - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - - NSArray *sections = [self.fetchedResultsController sections]; - NSUInteger sectionCount = [sections count]; - - 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 sectionInfo = obj; - [[sectionInfo objects] enumerateObjectsUsingBlock:^(id obj_, NSUInteger idx_, BOOL *stop_) { - if ([[obj_ name] isEqualToString:query]) { - hasExactQueryMatch = YES; - *stop_ = YES; - } - }]; - if (hasExactQueryMatch) - *stop = YES; - }]; - if (!hasExactQueryMatch) - // Add a section for "new site". - ++sectionCount; - } - - return (NSInteger)sectionCount; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - - NSArray *sections = [self.fetchedResultsController sections]; - if (section < (NSInteger)[sections count]) - return (NSInteger)[[sections objectAtIndex:(unsigned)section] numberOfObjects]; - - return 1; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MPElementSearch"]; - if (!cell) { - 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 { - - if (indexPath.section < (NSInteger)[[self.fetchedResultsController sections] count]) { - MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath: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]); - } 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]]); - } -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - - if (indexPath.section < (NSInteger)[[self.fetchedResultsController sections] count]) - [self.delegate didSelectElement:[self.fetchedResultsController objectAtIndexPath:indexPath]]; - - else { - // "New" section. - 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 - initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { - [tableView deselectRowAtIndexPath:indexPath animated:YES]; - - if (buttonIndex == [alert cancelButtonIndex]) - return; - - [MPAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { - MPUserEntity *activeUser = [[MPAppDelegate get] activeUserInContext:moc]; - assert(activeUser); - - MPElementType type = activeUser.defaultType; - if (!type) - type = activeUser.defaultType = MPElementTypeGeneratedLong; - NSString *typeEntityClassName = [MPAlgorithmDefault classNameOfType:type]; - - MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityClassName - inManagedObjectContext:moc]; - - element.name = siteName; - element.user = activeUser; - element.type = type; - element.lastUsed = [NSDate date]; - element.version = MPAlgorithmDefaultVersion; - [element saveContext]; - - NSManagedObjectID *elementOID = [element objectID]; - dispatch_async(dispatch_get_main_queue(), ^{ - MPElementEntity *element_ = (MPElementEntity *)[[MPAppDelegate managedObjectContextForThreadIfReady] - objectRegisteredForID:elementOID]; - [self.delegate didSelectElement:element_]; - }); - }]; - } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil]; - } -} - -- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { - - if (section < (NSInteger)[[self.fetchedResultsController sections] count]) - return [[[self.fetchedResultsController sections] objectAtIndex:(unsigned)section] name]; - - return @""; -} - -- (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 (indexPath.section < (NSInteger)[[self.fetchedResultsController sections] count]) { - 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 diff --git a/MasterPassword/iOS/MPTypeViewController.m b/MasterPassword/iOS/MPTypeViewController.m index 12d3e87e..216673b1 100644 --- a/MasterPassword/iOS/MPTypeViewController.m +++ b/MasterPassword/iOS/MPTypeViewController.m @@ -87,8 +87,14 @@ return; } - selectedElement_.type = cellType; - NSString *typeContent = [selectedElement.algorithm generateContentForElement:selectedElement_ usingKey:[MPAppDelegate get].key]; + MPElementGeneratedEntity *cellElement = [NSEntityDescription insertNewObjectForEntityForName:[MPAlgorithmDefault classNameOfType:cellType] + inManagedObjectContext:moc]; + cellElement.type = cellType; + cellElement.name = selectedElement_.name; + cellElement.user = selectedElement_.user; + cellElement.loginName = selectedElement_.loginName; + cellElement.version = MPAlgorithmDefaultVersion; + NSString *typeContent = [cellElement.algorithm generateContentForElement:cellElement usingKey:[MPAppDelegate get].key]; dispatch_async(dispatch_get_main_queue(), ^{ [(UITextField *) [[tableView cellForRowAtIndexPath:indexPath] viewWithTag:2] setText:typeContent]; diff --git a/MasterPassword/iOS/MPUnlockViewController.m b/MasterPassword/iOS/MPUnlockViewController.m index 4feb757a..1e98bb47 100644 --- a/MasterPassword/iOS/MPUnlockViewController.m +++ b/MasterPassword/iOS/MPUnlockViewController.m @@ -319,37 +319,39 @@ - (void)showNewUserNameAlertFor:(MPUserEntity *)newUser completion:(void (^)(BOOL finished))completion { - [PearlAlert showAlertWithTitle:@"Enter Your Name" - message:nil viewStyle:UIAlertViewStylePlainTextInput - initAlert:^(UIAlertView *alert, UITextField *firstField) { - firstField.autocapitalizationType = UITextAutocapitalizationTypeWords; - firstField.keyboardType = UIKeyboardTypeAlphabet; - firstField.text = newUser.name; - firstField.placeholder = @"eg. Robert Lee Mitchell"; - firstField.enablesReturnKeyAutomatically = YES; + dispatch_async(dispatch_get_main_queue(), ^{ + [PearlAlert showAlertWithTitle:@"Enter Your Name" + message:nil viewStyle:UIAlertViewStylePlainTextInput + initAlert:^(UIAlertView *alert, UITextField *firstField) { + firstField.autocapitalizationType = UITextAutocapitalizationTypeWords; + firstField.keyboardType = UIKeyboardTypeAlphabet; + firstField.text = newUser.name; + firstField.placeholder = @"eg. Robert Lee Mitchell"; + firstField.enablesReturnKeyAutomatically = YES; + } + tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { + if (buttonIndex == [alert cancelButtonIndex]) { + completion(NO); + return; } - tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { - if (buttonIndex == [alert cancelButtonIndex]) { - completion(NO); - return; + if (![alert textFieldAtIndex:0].text.length) { + [PearlAlert showAlertWithTitle:@"Name Is Required" message:nil viewStyle:UIAlertViewStyleDefault initAlert:nil + tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) { + [newUser.managedObjectContext performBlock:^{ + [self showNewUserNameAlertFor:newUser completion:completion]; + }]; + } cancelTitle:@"Try Again" otherTitles:nil]; + return; + } + + // Save + [newUser.managedObjectContext performBlock:^{ + newUser.name = [alert textFieldAtIndex:0].text; + [self showNewUserAvatarAlertFor:newUser completion:completion]; + }]; } - if (![alert textFieldAtIndex:0].text.length) { - [PearlAlert showAlertWithTitle:@"Name Is Required" message:nil viewStyle:UIAlertViewStyleDefault initAlert:nil - tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) { - [newUser.managedObjectContext performBlock:^{ - [self showNewUserNameAlertFor:newUser completion:completion]; - }]; - } cancelTitle:@"Try Again" otherTitles:nil]; - return; - } - - // Save - [newUser.managedObjectContext performBlock:^{ - newUser.name = [alert textFieldAtIndex:0].text; - [self showNewUserAvatarAlertFor:newUser completion:completion]; - }]; - } - cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonSave, nil]; + cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonSave, nil]; + }); } - (void)showNewUserAvatarAlertFor:(MPUserEntity *)newUser completion:(void (^)(BOOL finished))completion { diff --git a/MasterPassword/iOS/MainStoryboard_iPhone.storyboard b/MasterPassword/iOS/MainStoryboard_iPhone.storyboard index 5200f7a5..04d44524 100644 --- a/MasterPassword/iOS/MainStoryboard_iPhone.storyboard +++ b/MasterPassword/iOS/MainStoryboard_iPhone.storyboard @@ -1713,43 +1713,13 @@ You could use the word wall for inspiration in finding a memorable master passw - + - + - - - - - - - - - - - - - - - - @@ -2265,147 +2235,6 @@ You could use the word wall for inspiration in finding a memorable master passw - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -