2
0

iCloud sync of store.

[FIXED]     Guide toggle not working well.
[IMPROVED]  Core Data on a separate thread.
[IMPROVED]  Guide.
This commit is contained in:
Maarten Billemont 2012-02-05 00:36:19 +01:00
parent 960432577e
commit 7c36ff6acd
33 changed files with 169 additions and 92 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

View File

@ -203,6 +203,7 @@
DA84819414CB521E00A2FA22 /* tip_location_mercury.png in Resources */ = {isa = PBXBuildFile; fileRef = DA84817614CB521E00A2FA22 /* tip_location_mercury.png */; }; DA84819414CB521E00A2FA22 /* tip_location_mercury.png in Resources */ = {isa = PBXBuildFile; fileRef = DA84817614CB521E00A2FA22 /* tip_location_mercury.png */; };
DA84819514CB521E00A2FA22 /* tip_location_teal.png in Resources */ = {isa = PBXBuildFile; fileRef = DA84817714CB521E00A2FA22 /* tip_location_teal.png */; }; DA84819514CB521E00A2FA22 /* tip_location_teal.png in Resources */ = {isa = PBXBuildFile; fileRef = DA84817714CB521E00A2FA22 /* tip_location_teal.png */; };
DA84819614CB521E00A2FA22 /* tip_location_wood.png in Resources */ = {isa = PBXBuildFile; fileRef = DA84817814CB521E00A2FA22 /* tip_location_wood.png */; }; DA84819614CB521E00A2FA22 /* tip_location_wood.png in Resources */ = {isa = PBXBuildFile; fileRef = DA84817814CB521E00A2FA22 /* tip_location_wood.png */; };
DA8E8E4614DD7C1D0044257E /* logo-bare.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8E8E4514DD7C1D0044257E /* logo-bare.png */; };
DAA3B68E14CCCEE700F35AF6 /* icon_addressbook-person@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA3B53814CCCEE700F35AF6 /* icon_addressbook-person@2x.png */; }; DAA3B68E14CCCEE700F35AF6 /* icon_addressbook-person@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA3B53814CCCEE700F35AF6 /* icon_addressbook-person@2x.png */; };
DAA3B68F14CCCEE700F35AF6 /* icon_addressbook.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA3B53914CCCEE700F35AF6 /* icon_addressbook.png */; }; DAA3B68F14CCCEE700F35AF6 /* icon_addressbook.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA3B53914CCCEE700F35AF6 /* icon_addressbook.png */; };
DAA3B69014CCCEE700F35AF6 /* icon_addressbook@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA3B53A14CCCEE700F35AF6 /* icon_addressbook@2x.png */; }; DAA3B69014CCCEE700F35AF6 /* icon_addressbook@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA3B53A14CCCEE700F35AF6 /* icon_addressbook@2x.png */; };
@ -904,6 +905,8 @@
DA84817614CB521E00A2FA22 /* tip_location_mercury.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = tip_location_mercury.png; sourceTree = "<group>"; }; DA84817614CB521E00A2FA22 /* tip_location_mercury.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = tip_location_mercury.png; sourceTree = "<group>"; };
DA84817714CB521E00A2FA22 /* tip_location_teal.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = tip_location_teal.png; sourceTree = "<group>"; }; DA84817714CB521E00A2FA22 /* tip_location_teal.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = tip_location_teal.png; sourceTree = "<group>"; };
DA84817814CB521E00A2FA22 /* tip_location_wood.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = tip_location_wood.png; sourceTree = "<group>"; }; DA84817814CB521E00A2FA22 /* tip_location_wood.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = tip_location_wood.png; sourceTree = "<group>"; };
DA8E8E4514DD7C1D0044257E /* logo-bare.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "logo-bare.png"; path = "Resources/logo-bare.png"; sourceTree = "<group>"; };
DA8E8E4714DDA62D0044257E /* MasterPassword.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = MasterPassword.entitlements; sourceTree = "<group>"; };
DAA3B53814CCCEE700F35AF6 /* icon_addressbook-person@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_addressbook-person@2x.png"; sourceTree = "<group>"; }; DAA3B53814CCCEE700F35AF6 /* icon_addressbook-person@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_addressbook-person@2x.png"; sourceTree = "<group>"; };
DAA3B53914CCCEE700F35AF6 /* icon_addressbook.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_addressbook.png; sourceTree = "<group>"; }; DAA3B53914CCCEE700F35AF6 /* icon_addressbook.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_addressbook.png; sourceTree = "<group>"; };
DAA3B53A14CCCEE700F35AF6 /* icon_addressbook@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_addressbook@2x.png"; sourceTree = "<group>"; }; DAA3B53A14CCCEE700F35AF6 /* icon_addressbook@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_addressbook@2x.png"; sourceTree = "<group>"; };
@ -1805,6 +1808,7 @@
DA5BFA50147E415C00F98B1E /* MasterPassword */ = { DA5BFA50147E415C00F98B1E /* MasterPassword */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA8E8E4714DDA62D0044257E /* MasterPassword.entitlements */,
DA7C28A214AF02A000491972 /* Models */, DA7C28A214AF02A000491972 /* Models */,
DA7C28A314AF02B100491972 /* Data */, DA7C28A314AF02B100491972 /* Data */,
DA5BFA59147E415C00F98B1E /* OPAppDelegate.h */, DA5BFA59147E415C00F98B1E /* OPAppDelegate.h */,
@ -1834,6 +1838,7 @@
DA5BFA51147E415C00F98B1E /* Supporting Files */ = { DA5BFA51147E415C00F98B1E /* Supporting Files */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA8E8E4514DD7C1D0044257E /* logo-bare.png */,
DA6556F714D730B700841C99 /* Guide */, DA6556F714D730B700841C99 /* Guide */,
DAA3B80414CDBBC600F35AF6 /* jquery-1.6.1.min.js */, DAA3B80414CDBBC600F35AF6 /* jquery-1.6.1.min.js */,
DA84811E14CB50C100A2FA22 /* Tooltips */, DA84811E14CB50C100A2FA22 /* Tooltips */,
@ -2992,6 +2997,7 @@
DA65571214D760BD00841C99 /* guide_page_6@2x.png in Resources */, DA65571214D760BD00841C99 /* guide_page_6@2x.png in Resources */,
DA41A40B14DB3BF100638533 /* guide_page_0.png in Resources */, DA41A40B14DB3BF100638533 /* guide_page_0.png in Resources */,
DA41A40C14DB3BF100638533 /* guide_page_0@2x.png in Resources */, DA41A40C14DB3BF100638533 /* guide_page_0@2x.png in Resources */,
DA8E8E4614DD7C1D0044257E /* logo-bare.png in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -3182,6 +3188,7 @@
DA5BFA6E147E415C00F98B1E /* Debug */ = { DA5BFA6E147E415C00F98B1E /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = MasterPassword/MasterPassword.entitlements;
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "MasterPassword/MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "MasterPassword/MasterPassword-Prefix.pch";
INFOPLIST_FILE = "MasterPassword/MasterPassword-Info.plist"; INFOPLIST_FILE = "MasterPassword/MasterPassword-Info.plist";
@ -3194,6 +3201,7 @@
DA5BFA6F147E415C00F98B1E /* Release */ = { DA5BFA6F147E415C00F98B1E /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = MasterPassword/MasterPassword.entitlements;
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "MasterPassword/MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "MasterPassword/MasterPassword-Prefix.pch";
INFOPLIST_FILE = "MasterPassword/MasterPassword-Info.plist"; INFOPLIST_FILE = "MasterPassword/MasterPassword-Info.plist";

View File

@ -349,7 +349,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<rect key="contentStretch" x="0.050000000000000003" y="0.10000000000000001" width="0.89999999999999991" height="0.79999999999999982"/> <rect key="contentStretch" x="0.050000000000000003" y="0.10000000000000001" width="0.89999999999999991" height="0.79999999999999982"/>
</imageView> </imageView>
<imageView userInteractionEnabled="NO" alpha="0.80000001192092896" contentMode="scaleToFill" image="ui_panel_display.png" id="cw7-HD-Wht"> <imageView userInteractionEnabled="NO" alpha="0.80000000000000004" contentMode="scaleToFill" image="ui_panel_display.png" id="cw7-HD-Wht">
<rect key="frame" x="11" y="20" width="298" height="86"/> <rect key="frame" x="11" y="20" width="298" height="86"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<rect key="contentStretch" x="0.050000000000000003" y="0.10000000000000001" width="0.89999999999999991" height="0.79999999999999982"/> <rect key="contentStretch" x="0.050000000000000003" y="0.10000000000000001" width="0.89999999999999991" height="0.79999999999999982"/>

View File

@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string> <string>Passwords</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string> <string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFiles</key> <key>CFBundleIconFiles</key>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword</string>
</array>
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword</string>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)com.lyndir.lhunath.MasterPassword</string>
</array>
</dict>
</plist>

View File

@ -121,7 +121,7 @@
} }
- (void)applicationDidBecomeActive:(UIApplication *)application { - (void)applicationDidBecomeActive:(UIApplication *)application {
if ([[OPConfig get].showQuickstart boolValue]) if ([[OPConfig get].showQuickStart boolValue])
[self showGuide]; [self showGuide];
else else
[self loadKeyPhrase]; [self loadKeyPhrase];
@ -244,13 +244,14 @@
- (void)applicationWillResignActive:(UIApplication *)application { - (void)applicationWillResignActive:(UIApplication *)application {
[self saveContext];
if (![[OPConfig get].rememberKeyPhrase boolValue]) if (![[OPConfig get].rememberKeyPhrase boolValue])
self.keyPhrase = nil; self.keyPhrase = nil;
} }
- (void)applicationWillTerminate:(UIApplication *)application - (void)applicationWillTerminate:(UIApplication *)application {
{
// Saves changes in the application's managed object context before the application terminates.
[self saveContext]; [self saveContext];
} }
@ -269,18 +270,13 @@
return [(OPAppDelegate *)[UIApplication sharedApplication].delegate managedObjectModel]; return [(OPAppDelegate *)[UIApplication sharedApplication].delegate managedObjectModel];
} }
- (void)saveContext - (void)saveContext {
{
NSError *error = nil;
if ([self.managedObjectContext hasChanges] && ![self.managedObjectContext save:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. [self.managedObjectContext performBlock:^{
*/ NSError *error = nil;
err(@"Unresolved error %@, %@", error, [error userInfo]); if ([self.managedObjectContext hasChanges] && ![self.managedObjectContext save:&error])
abort(); err(@"Unresolved error %@", error);
} }];
} }
- (void)setKeyPhrase:(NSString *)keyPhrase { - (void)setKeyPhrase:(NSString *)keyPhrase {
@ -312,17 +308,30 @@
*/ */
- (NSManagedObjectContext *)managedObjectContext - (NSManagedObjectContext *)managedObjectContext
{ {
if (__managedObjectContext != nil) if (__managedObjectContext)
{
return __managedObjectContext; return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) if (coordinator) {
{ __managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
__managedObjectContext = [[NSManagedObjectContext alloc] init]; __managedObjectContext.persistentStoreCoordinator = coordinator;
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
[[NSNotificationCenter defaultCenter] addObserverForName:NSPersistentStoreDidImportUbiquitousContentChangesNotification
object:coordinator
queue:nil
usingBlock:^(NSNotification *note) {
dbg(@"Ubiquitous content change: %@", note);
[__managedObjectContext performBlock:^{
[__managedObjectContext mergeChangesFromContextDidSaveNotification:note];
[[NSNotificationCenter defaultCenter] postNotification:
[NSNotification notificationWithName:OPPersistentStoreDidChangeNotification
object:self userInfo:[note userInfo]]];
}];
}];
} }
return __managedObjectContext; return __managedObjectContext;
} }
@ -332,14 +341,11 @@
*/ */
- (NSManagedObjectModel *)managedObjectModel - (NSManagedObjectModel *)managedObjectModel
{ {
if (__managedObjectModel != nil) if (__managedObjectModel)
{
return __managedObjectModel; return __managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MasterPassword" withExtension:@"momd"];
__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return __managedObjectModel; NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MasterPassword" withExtension:@"momd"];
return __managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
} }
/** /**
@ -348,19 +354,23 @@
*/ */
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator - (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{ {
if (__persistentStoreCoordinator != nil) if (__persistentStoreCoordinator)
{
return __persistentStoreCoordinator; return __persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"MasterPassword.sqlite"]; NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"MasterPassword.sqlite"];
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
[__persistentStoreCoordinator lock];
NSError *error = nil;
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL
options:[NSDictionary dictionaryWithObjectsAndKeys: options:[NSDictionary dictionaryWithObjectsAndKeys:
(id)kCFBooleanTrue, NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
(id)kCFBooleanTrue, NSInferMappingModelAutomaticallyOption, [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
@"MasterPassword.store", NSPersistentStoreUbiquitousContentNameKey,
[[[NSFileManager defaultManager]
URLForUbiquityContainerIdentifier:nil]
URLByAppendingPathComponent:@"store"
isDirectory:YES], NSPersistentStoreUbiquitousContentURLKey,
nil] nil]
error:&error]) error:&error])
{ {
@ -395,6 +405,7 @@
@throw [NSException exceptionWithName:error.domain reason:error.localizedDescription @throw [NSException exceptionWithName:error.domain reason:error.localizedDescription
userInfo:[NSDictionary dictionaryWithObject:error forKey:@"cause"]]; userInfo:[NSDictionary dictionaryWithObject:error forKey:@"cause"]];
} }
[__persistentStoreCoordinator unlock];
return __persistentStoreCoordinator; return __persistentStoreCoordinator;
} }

View File

@ -13,7 +13,7 @@
@property (nonatomic, retain) NSNumber *rememberKeyPhrase; @property (nonatomic, retain) NSNumber *rememberKeyPhrase;
@property (nonatomic, retain) NSNumber *forgetKeyPhrase; @property (nonatomic, retain) NSNumber *forgetKeyPhrase;
@property (nonatomic, retain) NSNumber *helpHidden; @property (nonatomic, retain) NSNumber *helpHidden;
@property (nonatomic, retain) NSNumber *showQuickstart; @property (nonatomic, retain) NSNumber *showQuickStart;
+ (OPConfig *)get; + (OPConfig *)get;

View File

@ -10,7 +10,7 @@
@implementation OPConfig @implementation OPConfig
@dynamic dataStoreError, storeKeyPhrase, rememberKeyPhrase, forgetKeyPhrase, helpHidden, showQuickstart; @dynamic dataStoreError, storeKeyPhrase, rememberKeyPhrase, forgetKeyPhrase, helpHidden, showQuickStart;
- (id)init { - (id)init {
@ -24,7 +24,7 @@
[NSNumber numberWithBool:YES], NSStringFromSelector(@selector(rememberKeyPhrase)), [NSNumber numberWithBool:YES], NSStringFromSelector(@selector(rememberKeyPhrase)),
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(forgetKeyPhrase)), [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(forgetKeyPhrase)),
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(helpHidden)), [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(helpHidden)),
[NSNumber numberWithBool:YES], NSStringFromSelector(@selector(showQuickstart)), [NSNumber numberWithBool:YES], NSStringFromSelector(@selector(showQuickStart)),
nil]]; nil]];
return self; return self;

View File

@ -17,8 +17,8 @@
@property (nonatomic) int16_t type; @property (nonatomic) int16_t type;
@property (nonatomic) int16_t uses; @property (nonatomic) int16_t uses;
@property (nonatomic) NSTimeInterval lastUsed; @property (nonatomic) NSTimeInterval lastUsed;
@property (nonatomic, readonly) id content;
- (void)use; - (void)use;
- (id)content;
@end @end

View File

@ -28,7 +28,7 @@
[super viewWillDisappear:animated]; [super viewWillDisappear:animated];
[OPConfig get].showQuickstart = [NSNumber numberWithBool:NO]; [OPConfig get].showQuickStart = [NSNumber numberWithBool:NO];
[[OPAppDelegate get] loadKeyPhrase]; [[OPAppDelegate get] loadKeyPhrase];
} }

View File

@ -75,25 +75,29 @@
[self updateAnimated:NO]; [self updateAnimated:NO];
} }
- (void)viewWillDisappear:(BOOL)animated { - (void)viewDidAppear:(BOOL)animated {
[super viewWillDisappear:animated]; [super viewDidAppear:animated];
self.searchTipContainer.hidden = YES; // Put the search tip on the window so it's above the nav bar.
if (![self.searchTipContainer.superview isEqual:self.navigationController.navigationBar.superview]) {
CGRect frameInWindow = [self.searchTipContainer.window convertRect:self.searchTipContainer.frame
fromView:self.searchTipContainer.superview];
[self.searchTipContainer removeFromSuperview];
[self.navigationController.navigationBar.superview addSubview:self.searchTipContainer];
self.searchTipContainer.frame = [self.searchTipContainer.window convertRect:frameInWindow
toView:self.searchTipContainer.superview];
}
} }
- (void)viewDidLoad { - (void)viewDidLoad {
// Put the search tip on the window so it's above the nav bar.
CGRect newFrame = [self.navigationController.navigationBar convertRect:self.searchTipContainer.frame
fromView:self.searchTipContainer.superview];
[self.searchTipContainer removeFromSuperview];
[self.navigationController.navigationBar addSubview:self.searchTipContainer];
self.searchTipContainer.frame = newFrame;
self.searchTipContainer.hidden = YES;
// Because IB's edit button doesn't auto-toggle self.editable like editButtonItem does.
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ui_background"]]; self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ui_background"]];
//self.navigationItem.titleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"logo-bare.png"]];
self.navigationItem.titleView.frame = CGRectMake(0, 0, 50, 50);
self.navigationItem.titleView.center = self.navigationController.navigationBar.center;
self.navigationItem.titleView.contentMode = UIViewContentModeScaleAspectFit;
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:[NSOperationQueue mainQueue] [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) { usingBlock:^(NSNotification *note) {
if (![OPAppDelegate get].keyPhrase) { if (![OPAppDelegate get].keyPhrase) {
@ -216,10 +220,12 @@
- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message { - (void)showAlertWithTitle:(NSString *)title message:(NSString *)message {
self.alertTitle.text = title; self.alertTitle.text = title;
NSRange scrollRange = NSMakeRange(self.alertBody.text.length, message.length);
if ([self.alertBody.text length]) if ([self.alertBody.text length])
self.alertBody.text = [NSString stringWithFormat:@"%@\n\n---\n\n%@", self.alertBody.text, message]; self.alertBody.text = [NSString stringWithFormat:@"%@\n\n---\n\n%@", self.alertBody.text, message];
else else
self.alertBody.text = message; self.alertBody.text = message;
[self.alertBody scrollRangeToVisible:scrollRange];
[UIView animateWithDuration:0.2f animations:^{ [UIView animateWithDuration:0.2f animations:^{
self.alertContainer.alpha = 1; self.alertContainer.alpha = 1;
@ -310,8 +316,9 @@
[self updateElement:^{ [self updateElement:^{
// Update password type. // Update password type.
if (ClassFromOPElementType(type) != ClassFromOPElementType(self.activeElement.type)) { if (ClassFromOPElementType(type) != ClassFromOPElementType(self.activeElement.type))
// Type requires a different class of element. Recreate the element. // Type requires a different class of element. Recreate the element.
[[OPAppDelegate managedObjectContext] performBlockAndWait:^{
OPElementEntity *newElement = [NSEntityDescription insertNewObjectForEntityForName:ClassNameFromOPElementType(type) OPElementEntity *newElement = [NSEntityDescription insertNewObjectForEntityForName:ClassNameFromOPElementType(type)
inManagedObjectContext:[OPAppDelegate managedObjectContext]]; inManagedObjectContext:[OPAppDelegate managedObjectContext]];
newElement.name = self.activeElement.name; newElement.name = self.activeElement.name;
@ -321,7 +328,7 @@
[[OPAppDelegate managedObjectContext] deleteObject:self.activeElement]; [[OPAppDelegate managedObjectContext] deleteObject:self.activeElement];
self.activeElement = newElement; self.activeElement = newElement;
} }];
self.activeElement.type = type; self.activeElement.type = type;

View File

@ -42,16 +42,18 @@
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView { - (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView {
[[NSNotificationCenter defaultCenter] addObserverForName:OPPersistentStoreDidChangeNotification
object:nil queue:nil usingBlock:^(NSNotification *note) {
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
err(@"Couldn't fetch elements: %@", error);
}];
tableView.backgroundColor = [UIColor blackColor]; tableView.backgroundColor = [UIColor blackColor];
tableView.rowHeight = 34.0f; tableView.rowHeight = 34.0f;
tableView.separatorStyle = UITableViewCellSeparatorStyleNone; tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
} }
- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)tableView {
[tableView setEditing:self.searchDisplayController.searchContentsController.editing animated:NO];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString { - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self update]; [self update];
@ -89,7 +91,8 @@
[self.searchDisplayController.searchResultsTableView beginUpdates]; [self.searchDisplayController.searchResultsTableView beginUpdates];
} }
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.searchDisplayController.searchResultsTableView; UITableView *tableView = self.searchDisplayController.searchResultsTableView;
switch(type) { switch(type) {
@ -118,7 +121,8 @@
} }
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
UITableView *tableView = self.searchDisplayController.searchResultsTableView; UITableView *tableView = self.searchDisplayController.searchResultsTableView;
switch(type) { switch(type) {
@ -191,21 +195,33 @@
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
OPElementEntity *element;
if (indexPath.section < [[self.fetchedResultsController sections] count]) if (indexPath.section < [[self.fetchedResultsController sections] count])
element = [self.fetchedResultsController objectAtIndexPath:indexPath]; [self.delegate didSelectElement:[self.fetchedResultsController objectAtIndexPath:indexPath]];
else { else {
// "New" section. // "New" section.
element = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([OPElementGeneratedEntity class]) NSString *siteName = self.searchDisplayController.searchBar.text;
inManagedObjectContext:[OPAppDelegate managedObjectContext]]; [AlertViewController showAlertWithTitle:@"New Site"
message:l(@"Do you want to create a new site named:\n%@", siteName)
viewStyle:UIAlertViewStyleDefault
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
[self.fetchedResultsController.managedObjectContext performBlock:^{
OPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([OPElementGeneratedEntity class])
inManagedObjectContext:self.fetchedResultsController.managedObjectContext];
assert([element isKindOfClass:ClassFromOPElementType(element.type)]); assert([element isKindOfClass:ClassFromOPElementType(element.type)]);
element.name = self.searchDisplayController.searchBar.text; element.name = siteName;
element.mpHashHex = [OPAppDelegate get].keyPhraseHashHex; element.mpHashHex = [OPAppDelegate get].keyPhraseHashHex;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate didSelectElement:element]; [self.delegate didSelectElement:element];
});
}];
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil];
}
} }
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
@ -232,11 +248,11 @@
// "New" section. // "New" section.
return; return;
if (editingStyle == UITableViewCellEditingStyleDelete) { if (editingStyle == UITableViewCellEditingStyleDelete)
[self.fetchedResultsController.managedObjectContext performBlock:^{
OPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath]; OPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self.fetchedResultsController.managedObjectContext deleteObject:element];
[[OPAppDelegate managedObjectContext] deleteObject:element]; }];
}
} }

View File

@ -8,6 +8,8 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#define OPPersistentStoreDidChangeNotification @"OPPersistentStoreDidChange"
typedef enum { typedef enum {
OPElementContentTypePassword, OPElementContentTypePassword,
OPElementContentTypeNote, OPElementContentTypeNote,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 KiB

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 KiB

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 412 KiB

After

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 KiB

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 369 KiB

After

Width:  |  Height:  |  Size: 240 KiB

View File

@ -17,6 +17,9 @@
h2 { h2 {
font-size: inherit; font-size: inherit;
} }
h3 {
font-size: 12px;
}
i { i {
font-weight: bold; font-weight: bold;
} }
@ -33,6 +36,17 @@
color: inherit; color: inherit;
font-weight: bold; font-weight: bold;
} }
header {
height: 8em;
padding: 3em 0 0;
}
header h1, header h2 {
margin: 0;
padding: 0.5ex;
}
header h3 {
padding-top: 2em;
}
</style> </style>
<script src="jquery-1.6.1.min.js" type="text/javascript"></script> <script src="jquery-1.6.1.min.js" type="text/javascript"></script>
<script type="text/javascript"> <script type="text/javascript">
@ -43,8 +57,11 @@
</script> </script>
</head> </head>
<body> <body>
<h1 onclick="setClass('OPElementStoredEntity')">Master Password</h1> <header>
<h2 onclick="setClass('OPElementGeneratedEntity')">by Lyndir</h2> <h1>Master Password</h1>
<h2>by <a href="http://www.lyndir.com">Lyndir</a></h2>
<h3>&copy; 2011</h3>
</header>
<h2 id="1">&mdash; 1 &mdash;</h2> <h2 id="1">&mdash; 1 &mdash;</h2>
<p> <p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -106,9 +106,9 @@
<key>Type</key> <key>Type</key>
<string>PSToggleSwitchSpecifier</string> <string>PSToggleSwitchSpecifier</string>
<key>Title</key> <key>Title</key>
<string>Show Quickstart</string> <string>Show Quick Start</string>
<key>Key</key> <key>Key</key>
<string>showQuickstart</string> <string>showQuickStart</string>
<key>DefaultValue</key> <key>DefaultValue</key>
<true/> <true/>
</dict> </dict>