Mac fixes and improvements to usability.
[FIXED] Mac: Locking shouldn't unset the active user. [IMPROVED] Mac: Behavior of auto-completing site-name field improved. [ADDED] Mac: Alert when MP is incorrect when unlocking.
This commit is contained in:
parent
b07298e203
commit
d5bffd86d6
@ -40,6 +40,8 @@ typedef enum {
|
||||
MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate,
|
||||
} MPElementType;
|
||||
|
||||
#define MPErrorDomain @"MPErrorDomain"
|
||||
|
||||
#define MPCheckpointHelpChapter @"MPCheckpointHelpChapter"
|
||||
#define MPCheckpointCopyToPasteboard @"MPCheckpointCopyToPasteboard"
|
||||
#define MPCheckpointCopyLoginNameToPasteboard @"MPCheckpointCopyLoginNameToPasteboard"
|
||||
|
@ -29,5 +29,6 @@
|
||||
- (IBAction)togglePreference:(NSMenuItem *)sender;
|
||||
- (IBAction)newUser:(NSMenuItem *)sender;
|
||||
- (IBAction)signOut:(id)sender;
|
||||
- (IBAction)lock:(id)sender;
|
||||
|
||||
@end
|
||||
|
@ -37,7 +37,7 @@ static EventHotKeyID MPLockHotKey = {.signature = 'lock', .id = 1};
|
||||
[MPMacConfig get];
|
||||
|
||||
#ifdef DEBUG
|
||||
[PearlLogger get].printLevel = PearlLogLevelTrace;
|
||||
[PearlLogger get].printLevel = PearlLogLevelDebug;//Trace;
|
||||
#endif
|
||||
});
|
||||
}
|
||||
@ -60,7 +60,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
return noErr;
|
||||
}
|
||||
if (hotKeyID.signature == MPLockHotKey.signature && hotKeyID.id == MPLockHotKey.id) {
|
||||
[((__bridge MPAppDelegate *)userData) signOut:nil];
|
||||
[((__bridge MPAppDelegate *)userData) lock:nil];
|
||||
return noErr;
|
||||
}
|
||||
|
||||
@ -163,6 +163,11 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
[self signOutAnimated:YES];
|
||||
}
|
||||
|
||||
- (IBAction)lock:(id)sender {
|
||||
|
||||
self.key = nil;
|
||||
}
|
||||
|
||||
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue {
|
||||
|
||||
if (configKey == @selector(rememberLogin))
|
||||
|
@ -11,22 +11,19 @@
|
||||
#import "MPAppDelegate_Key.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
|
||||
#define MPAlertUnlockMP @"MPAlertUnlockMP"
|
||||
#define MPAlertIncorrectMP @"MPAlertIncorrectMP"
|
||||
|
||||
|
||||
@interface MPPasswordWindowController ()
|
||||
|
||||
@property (nonatomic, strong) NSString *oldSiteName;
|
||||
@property (nonatomic, strong) NSArray /* MPElementEntity */ *siteResults;
|
||||
@property (nonatomic) BOOL inProgress;
|
||||
|
||||
@property (nonatomic) BOOL siteFieldPreventCompletion;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPPasswordWindowController
|
||||
@synthesize oldSiteName, siteResults;
|
||||
@synthesize siteField;
|
||||
@synthesize contentField;
|
||||
@synthesize tipField;
|
||||
@synthesize inProgress = _inProgress;
|
||||
|
||||
|
||||
- (void)windowDidLoad {
|
||||
|
||||
@ -37,6 +34,8 @@
|
||||
[self.userLabel setStringValue:PearlString(@"%@'s password for:", [MPAppDelegate get].activeUser.name)];
|
||||
} forKeyPath:@"activeUser" options:NSKeyValueObservingOptionInitial context:nil];
|
||||
[[MPAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
|
||||
if (![MPAppDelegate get].key)
|
||||
[self unlock];
|
||||
if ([MPAppDelegate get].activeUser && [MPAppDelegate get].key)
|
||||
[MPAlgorithmDefault migrateUser:[MPAppDelegate get].activeUser completion:^(BOOL userRequiresNewMigration) {
|
||||
if (userRequiresNewMigration)
|
||||
@ -56,18 +55,6 @@
|
||||
usingBlock:^(NSNotification *note) {
|
||||
[[NSApplication sharedApplication] hide:self];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSControlTextDidChangeNotification object:self.siteField queue:nil
|
||||
usingBlock:^(NSNotification *note) {
|
||||
NSString *newSiteName = [self.siteField stringValue];
|
||||
BOOL shouldComplete = [self.oldSiteName length] < [newSiteName length];
|
||||
self.oldSiteName = newSiteName;
|
||||
|
||||
if ([self trySite])
|
||||
shouldComplete = NO;
|
||||
|
||||
if (shouldComplete)
|
||||
[[[note userInfo] objectForKey:@"NSFieldEditor"] complete:nil];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:MPNotificationSignedOut object:nil queue:nil
|
||||
usingBlock:^(NSNotification *note) {
|
||||
[self.window close];
|
||||
@ -78,6 +65,12 @@
|
||||
|
||||
- (void)unlock {
|
||||
|
||||
if (![MPAppDelegate get].activeUser)
|
||||
// No user to sign in with.
|
||||
return;
|
||||
if ([MPAppDelegate get].key)
|
||||
// Already logged in.
|
||||
return;
|
||||
if ([[MPAppDelegate get] signInAsUser:[MPAppDelegate get].activeUser usingMasterPassword:nil])
|
||||
// Load the key from the keychain.
|
||||
return;
|
||||
@ -88,6 +81,10 @@
|
||||
if ([MPAppDelegate get].key)
|
||||
return;
|
||||
|
||||
self.content = @"";
|
||||
[self.siteField setStringValue:@""];
|
||||
[self.tipField setStringValue:@""];
|
||||
|
||||
NSAlert *alert = [NSAlert alertWithMessageText:@"Master Password is locked."
|
||||
defaultButton:@"Unlock" alternateButton:@"Change" otherButton:@"Cancel"
|
||||
informativeTextWithFormat:@"The master password is required to unlock the application for:\n\n%@", [MPAppDelegate get].activeUser.name];
|
||||
@ -96,12 +93,17 @@
|
||||
[alert layout];
|
||||
[passwordField becomeFirstResponder];
|
||||
[alert beginSheetModalForWindow:self.window modalDelegate:self
|
||||
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:NULL];
|
||||
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertUnlockMP];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
|
||||
|
||||
if (contextInfo == MPAlertIncorrectMP) {
|
||||
[self.window close];
|
||||
return;
|
||||
}
|
||||
if (contextInfo == MPAlertUnlockMP) {
|
||||
switch (returnCode) {
|
||||
case NSAlertAlternateReturn:
|
||||
// "Change" button.
|
||||
@ -131,7 +133,8 @@
|
||||
[self.progressView startAnimation:nil];
|
||||
self.inProgress = YES;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
|
||||
BOOL success = [[MPAppDelegate get] signInAsUser:[MPAppDelegate get].activeUser usingMasterPassword:[(NSSecureTextField *)alert.accessoryView stringValue]];
|
||||
BOOL success = [[MPAppDelegate get] signInAsUser:[MPAppDelegate get].activeUser
|
||||
usingMasterPassword:[(NSSecureTextField *)alert.accessoryView stringValue]];
|
||||
self.inProgress = NO;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@ -139,8 +142,13 @@
|
||||
|
||||
if (success)
|
||||
self.contentContainer.alphaValue = 1;
|
||||
else
|
||||
[self.window close];
|
||||
else {
|
||||
[[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
|
||||
NSLocalizedDescriptionKey : PearlString(@"Incorrect master password for user %@",
|
||||
[MPAppDelegate get].activeUser.name)
|
||||
}]] beginSheetModalForWindow:self.window modalDelegate:self
|
||||
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertIncorrectMP];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -148,6 +156,9 @@
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words
|
||||
@ -159,19 +170,27 @@
|
||||
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])];
|
||||
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses_" ascending:NO]];
|
||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name BEGINSWITH[cd] %@) AND user == %@",
|
||||
query, query, [MPAppDelegate get].activeUser];
|
||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(name BEGINSWITH[cd] %@) AND user == %@",
|
||||
query, [MPAppDelegate get].activeUser];
|
||||
|
||||
NSError *error = nil;
|
||||
self.siteResults = [[MPAppDelegate managedObjectContextIfReady] executeFetchRequest:fetchRequest error:&error];
|
||||
if (error)
|
||||
err(@"Couldn't fetch elements: %@", error);
|
||||
err(@"While fetching elements for completion: %@", error);
|
||||
|
||||
if ([self.siteResults count] == 1) {
|
||||
[textView setString:[(MPElementEntity *)[self.siteResults objectAtIndex:0] name]];
|
||||
[textView setSelectedRange:NSMakeRange([query length], [[textView string] length] - [query length])];
|
||||
if ([self trySite])
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableArray *mutableResults = [NSMutableArray arrayWithCapacity:[self.siteResults count] + 1];
|
||||
if (self.siteResults)
|
||||
for (MPElementEntity *element in self.siteResults)
|
||||
[mutableResults addObject:element.name];
|
||||
// [mutableResults addObject:query]; // For when the app should be able to create new sites.
|
||||
|
||||
return mutableResults;
|
||||
}
|
||||
|
||||
@ -181,9 +200,26 @@
|
||||
[self.window close];
|
||||
return YES;
|
||||
}
|
||||
if ((self.siteFieldPreventCompletion = [NSStringFromSelector(commandSelector) hasPrefix:@"delete"]))
|
||||
return NO;
|
||||
if (commandSelector == @selector(insertNewline:) && [self.content length]) {
|
||||
if ([self trySite])
|
||||
[self copyContents];
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)copyContents {
|
||||
|
||||
[[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
|
||||
if ([[NSPasteboard generalPasteboard] setString:self.content forType:NSPasteboardTypeString]) {
|
||||
if (![[NSPasteboard generalPasteboard] setString:self.content forType:NSPasteboardTypeString]) {
|
||||
wrn(@"Couldn't copy password to pasteboard.");
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.tipField.alphaValue = 1;
|
||||
[self.tipField setStringValue:@"Copied! Hit ⎋ (ESC) to close window."];
|
||||
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC));
|
||||
@ -193,22 +229,43 @@
|
||||
[self.tipField.animator setAlphaValue:0];
|
||||
[NSAnimationContext endGrouping];
|
||||
});
|
||||
});
|
||||
|
||||
[[self findElement] use];
|
||||
return YES;
|
||||
} else
|
||||
wrn(@"Couldn't copy password to pasteboard.");
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)controlTextDidEndEditing:(NSNotification *)obj {
|
||||
- (void)controlTextDidEndEditing:(NSNotification *)note {
|
||||
|
||||
if (note.object != self.siteField)
|
||||
return;
|
||||
|
||||
if (obj.object == self.siteField)
|
||||
[self trySite];
|
||||
}
|
||||
|
||||
- (void)controlTextDidChange:(NSNotification *)note {
|
||||
|
||||
if (note.object != self.siteField)
|
||||
return;
|
||||
|
||||
// Update the site content as the site name changes.
|
||||
BOOL hasValidSite = [self trySite];
|
||||
|
||||
if ([[NSApp currentEvent] type] == NSKeyDown && [[[NSApp currentEvent] charactersIgnoringModifiers] isEqualToString:@"\r"]) {
|
||||
if (hasValidSite)
|
||||
[self copyContents];
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.siteFieldPreventCompletion) {
|
||||
self.siteFieldPreventCompletion = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
self.siteFieldPreventCompletion = YES;
|
||||
[(NSText *)[note.userInfo objectForKey:@"NSFieldEditor"] complete:self];
|
||||
self.siteFieldPreventCompletion = NO;
|
||||
}
|
||||
|
||||
- (NSString *)content {
|
||||
|
||||
return _content;
|
||||
@ -237,8 +294,6 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
dbg(@"element:\n%@", [result debugDescription]);
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
||||
NSString *description = [result.content description];
|
||||
if (!description)
|
||||
|
@ -3,12 +3,12 @@
|
||||
<data>
|
||||
<int key="IBDocument.SystemTarget">1070</int>
|
||||
<string key="IBDocument.SystemVersion">12C60</string>
|
||||
<string key="IBDocument.InterfaceBuilderVersion">2840</string>
|
||||
<string key="IBDocument.InterfaceBuilderVersion">2844</string>
|
||||
<string key="IBDocument.AppKitVersion">1187.34</string>
|
||||
<string key="IBDocument.HIToolboxVersion">625.00</string>
|
||||
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string key="NS.object.0">2840</string>
|
||||
<string key="NS.object.0">2844</string>
|
||||
</object>
|
||||
<array key="IBDocument.IntegratedClassDependencies">
|
||||
<string>NSCustomObject</string>
|
||||
@ -241,14 +241,6 @@
|
||||
</object>
|
||||
<int key="connectionID">726</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBActionConnection" key="connection">
|
||||
<string key="label">signOut:</string>
|
||||
<reference key="source" ref="976324537"/>
|
||||
<reference key="destination" ref="229948989"/>
|
||||
</object>
|
||||
<int key="connectionID">727</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBOutletConnection" key="connection">
|
||||
<string key="label">showItem</string>
|
||||
@ -345,6 +337,14 @@
|
||||
</object>
|
||||
<int key="connectionID">763</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBActionConnection" key="connection">
|
||||
<string key="label">lock:</string>
|
||||
<reference key="source" ref="976324537"/>
|
||||
<reference key="destination" ref="229948989"/>
|
||||
</object>
|
||||
<int key="connectionID">764</int>
|
||||
</object>
|
||||
</array>
|
||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||
<array key="orderedObjects">
|
||||
@ -538,96 +538,9 @@
|
||||
<nil key="activeLocalization"/>
|
||||
<dictionary class="NSMutableDictionary" key="localizations"/>
|
||||
<nil key="sourceID"/>
|
||||
<int key="maxID">763</int>
|
||||
</object>
|
||||
<object class="IBClassDescriber" key="IBDocument.Classes">
|
||||
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">MPAppDelegate</string>
|
||||
<string key="superclassName">MPAppDelegate_Shared</string>
|
||||
<dictionary class="NSMutableDictionary" key="actions">
|
||||
<string key="activate:">id</string>
|
||||
<string key="newUser:">NSMenuItem</string>
|
||||
<string key="signOut:">id</string>
|
||||
<string key="togglePreference:">NSMenuItem</string>
|
||||
</dictionary>
|
||||
<dictionary class="NSMutableDictionary" key="actionInfosByName">
|
||||
<object class="IBActionInfo" key="activate:">
|
||||
<string key="name">activate:</string>
|
||||
<string key="candidateClassName">id</string>
|
||||
</object>
|
||||
<object class="IBActionInfo" key="newUser:">
|
||||
<string key="name">newUser:</string>
|
||||
<string key="candidateClassName">NSMenuItem</string>
|
||||
</object>
|
||||
<object class="IBActionInfo" key="signOut:">
|
||||
<string key="name">signOut:</string>
|
||||
<string key="candidateClassName">id</string>
|
||||
</object>
|
||||
<object class="IBActionInfo" key="togglePreference:">
|
||||
<string key="name">togglePreference:</string>
|
||||
<string key="candidateClassName">NSMenuItem</string>
|
||||
</object>
|
||||
</dictionary>
|
||||
<dictionary class="NSMutableDictionary" key="outlets">
|
||||
<string key="createUserItem">NSMenuItem</string>
|
||||
<string key="lockItem">NSMenuItem</string>
|
||||
<string key="rememberPasswordItem">NSMenuItem</string>
|
||||
<string key="savePasswordItem">NSMenuItem</string>
|
||||
<string key="showItem">NSMenuItem</string>
|
||||
<string key="statusMenu">NSMenu</string>
|
||||
<string key="useICloudItem">NSMenuItem</string>
|
||||
<string key="usersItem">NSMenuItem</string>
|
||||
</dictionary>
|
||||
<dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
|
||||
<object class="IBToOneOutletInfo" key="createUserItem">
|
||||
<string key="name">createUserItem</string>
|
||||
<string key="candidateClassName">NSMenuItem</string>
|
||||
</object>
|
||||
<object class="IBToOneOutletInfo" key="lockItem">
|
||||
<string key="name">lockItem</string>
|
||||
<string key="candidateClassName">NSMenuItem</string>
|
||||
</object>
|
||||
<object class="IBToOneOutletInfo" key="rememberPasswordItem">
|
||||
<string key="name">rememberPasswordItem</string>
|
||||
<string key="candidateClassName">NSMenuItem</string>
|
||||
</object>
|
||||
<object class="IBToOneOutletInfo" key="savePasswordItem">
|
||||
<string key="name">savePasswordItem</string>
|
||||
<string key="candidateClassName">NSMenuItem</string>
|
||||
</object>
|
||||
<object class="IBToOneOutletInfo" key="showItem">
|
||||
<string key="name">showItem</string>
|
||||
<string key="candidateClassName">NSMenuItem</string>
|
||||
</object>
|
||||
<object class="IBToOneOutletInfo" key="statusMenu">
|
||||
<string key="name">statusMenu</string>
|
||||
<string key="candidateClassName">NSMenu</string>
|
||||
</object>
|
||||
<object class="IBToOneOutletInfo" key="useICloudItem">
|
||||
<string key="name">useICloudItem</string>
|
||||
<string key="candidateClassName">NSMenuItem</string>
|
||||
</object>
|
||||
<object class="IBToOneOutletInfo" key="usersItem">
|
||||
<string key="name">usersItem</string>
|
||||
<string key="candidateClassName">NSMenuItem</string>
|
||||
</object>
|
||||
</dictionary>
|
||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||
<string key="majorKey">IBProjectSource</string>
|
||||
<string key="minorKey">./Classes/MPAppDelegate.h</string>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">MPAppDelegate_Shared</string>
|
||||
<string key="superclassName">PearlAppDelegate</string>
|
||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||
<string key="majorKey">IBProjectSource</string>
|
||||
<string key="minorKey">./Classes/MPAppDelegate_Shared.h</string>
|
||||
</object>
|
||||
</object>
|
||||
</array>
|
||||
<int key="maxID">764</int>
|
||||
</object>
|
||||
<object class="IBClassDescriber" key="IBDocument.Classes"/>
|
||||
<int key="IBDocument.localizationMode">0</int>
|
||||
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
|
||||
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
|
||||
|
Loading…
Reference in New Issue
Block a user