Permalink
Browse files

simplenote account preferences with dynamic login verificaiotn

  • Loading branch information...
1 parent 4c7947a commit 20067de0c3c7e7a4757dfcc22be7edbd50e40c54 Zachary Schneirov committed Feb 1, 2010
Showing with 161 additions and 29 deletions.
  1. +22 −5 NotationPrefsViewController.h
  2. +139 −24 NotationPrefsViewController.m
@@ -11,6 +11,7 @@
@class NotationPrefs;
@class PassphrasePicker;
@class PassphraseChanger;
+@class SyncResponseFetcher;
@interface FileKindListView : NSTableView {
IBOutlet NSPopUpButton *storageFormatPopupButton;
@@ -26,8 +27,8 @@
IBOutlet NSTextField *keyLengthField, *fileAttributesHelpText;
IBOutlet NSButton *newExtensionButton;
IBOutlet NSButton *newTypeButton;
- IBOutlet NSTextField *notationalAccountField;
- IBOutlet NSTextField *notationalPasswordField;
+ IBOutlet NSTextField *syncAccountField;
+ IBOutlet NSTextField *syncPasswordField;
IBOutlet NSButton *removeExtensionButton;
IBOutlet NSButton *removeTypeButton;
IBOutlet NSButton *confirmFileDeletionButton;
@@ -36,6 +37,12 @@
IBOutlet NSPopUpButton *storageFormatPopupButton;
IBOutlet NSMatrix *passwordSettingsMatrix;
IBOutlet NSWindow *webOptionsWindow;
+ IBOutlet NSButton *enabledSyncButton;
+ IBOutlet NSImageView *verifyStatusImageView;
+ IBOutlet NSTextField *verifyStatusField;
+ IBOutlet NSPopUpButton *syncingFrequency;
+ IBOutlet NSImageView *syncEncAlertView;
+ IBOutlet NSTextField *syncEncAlertField;
IBOutlet NSView *view;
@@ -47,11 +54,15 @@
PassphrasePicker *picker;
PassphraseChanger *changer;
+
+ BOOL verificationAttempted;
+ SyncResponseFetcher *loginVerifier;
NSString *disableEncryptionString, *enableEncryptionString;
}
- (NSView*)view;
+- (void)setSyncControlsState:(BOOL)syncState;
- (void)setEncryptionControlsState:(BOOL)encryptionState;
- (void)setSeparateFileControlsState:(BOOL)separateFileControlsState;
- (void)initializeControls;
@@ -69,12 +80,18 @@
- (void)notesStorageFormatDidChange;
- (int)notesStorageFormatInProgress;
- (void)runQueuedStorageFormatChangeInvocation;
-- (IBAction)verifySyncAccount:(id)sender;
-- (IBAction)showWebOptions:(id)sender;
-- (IBAction)createNotationalAccount:(id)sender;
+- (IBAction)visitSimplenoteSite:(id)sender;
- (IBAction)removedExtension:(id)sender;
- (IBAction)removedType:(id)sender;
+- (IBAction)toggledSyncing:(id)sender;
+- (IBAction)syncFrequencyChange:(id)sender;
+
+- (void)startVerifyingAfterDelay;
+- (void)startLoginVerifier;
+- (void)cancelLoginVerifier;
+- (void)setVerificationStatus:(int)status withString:(NSString*)aString;
+
- (void)encryptionFormatMismatchSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode
contextInfo:(void *)contextInfo;
- (IBAction)toggledEncryption:(id)sender;
@@ -8,8 +8,12 @@
#import "GlobalPrefs.h"
#import "NotationPrefsViewController.h"
+#import "InvocationRecorder.h"
#import "NotationPrefs.h"
#import "NSString_NV.h"
+#import "NSCollection_utils.h"
+#import "SyncResponseFetcher.h"
+#import "SimplenoteSession.h"
#import "PassphrasePicker.h"
#import "PassphraseChanger.h"
@@ -24,6 +28,8 @@ - (BOOL)acceptsFirstResponder {
}
@end
+enum {VERIFY_NOT_ATTEMPTED, VERIFY_FAILED, VERIFY_IN_PROGRESS, VERIFY_SUCCESS};
+
@implementation NotationPrefsViewController
- (NSView*)view {
@@ -55,6 +61,8 @@ - (void)dealloc {
[notationPrefs release];
[postStorageFormatInvocation release];
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
[super dealloc];
}
@@ -66,8 +74,16 @@ - (void)awakeFromNib {
[allowedExtensionsTable setDelegate:self];
[allowedTypesTable setDelegate:self];
+
+ //this additional management for sync prefs, plus the need for per-service settings and externally triggering updates really demands its own class
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ if (syncAccountField) [center addObserver:self selector:@selector(syncCredentialsDidChange:) name:NSControlTextDidChangeNotification object:syncAccountField];
+ if (syncPasswordField) {
+ [center addObserver:self selector:@selector(syncCredentialsDidChange:) name:NSControlTextDidChangeNotification object:syncPasswordField];
+ [center addObserver:self selector:@selector(syncEditingDidEnd:) name:NSControlTextDidEndEditingNotification object:syncPasswordField];
}
-
+ [center addObserver:self selector:@selector(initializeControls) name:SyncPrefsDidChangeNotification object:nil];
+
[self initializeControls];
}
@@ -106,9 +122,15 @@ - (void)initializeControls {
[self updateRemoveKeychainItemStatus];
[confirmFileDeletionButton setState:[notationPrefs confirmFileDeletion]];
- [notationalAccountField setStringValue:[notationPrefs serverUserName]];
- if ([notationPrefs serverUserName] && [[notationPrefs serverUserName] length] > 0)
- [notationalPasswordField setStringValue:@"xxxxxxxxxxxxxxxxxxxxxxxxx"];
+ [enabledSyncButton setState:[notationPrefs syncServiceIsEnabled:SimplenoteServiceName]];
+ NSString *username = [[notationPrefs syncAccountForServiceName:SimplenoteServiceName] objectForKey:@"username"];
+ NSString *password = [notationPrefs syncPasswordForServiceName:SimplenoteServiceName];
+ [syncAccountField setStringValue:username ? username : @""];
+ [syncPasswordField setStringValue:password ? password : @""];
+
+ [syncingFrequency selectItemWithTag:[notationPrefs syncFrequencyInMinutesForServiceName:SimplenoteServiceName]];
+
+ [self setSyncControlsState:[notationPrefs syncServiceIsEnabled:SimplenoteServiceName]];
[secureTextEntryButton setState:[notationPrefs secureTextEntry]];
@@ -117,6 +139,21 @@ - (void)initializeControls {
}
}
+- (void)setSyncControlsState:(BOOL)syncState {
+
+ if (syncState) {
+ [self startLoginVerifier];
+ } else {
+ [self cancelLoginVerifier];
+ }
+ [self setVerificationStatus:VERIFY_NOT_ATTEMPTED withString:@""];
+ [syncingFrequency setEnabled:syncState];
+ [syncAccountField setEnabled:syncState];
+ [syncPasswordField setEnabled:syncState];
+ [syncEncAlertView setHidden:!syncState || ![notationPrefs doesEncryption]];
+ [syncEncAlertField setHidden:!syncState || ![notationPrefs doesEncryption]];
+}
+
- (void)setEncryptionControlsState:(BOOL)encryptionState {
[enableEncryptionButton setTitle:(encryptionState ? disableEncryptionString : enableEncryptionString)];
[changePasswordButton setEnabled:encryptionState];
@@ -291,17 +328,101 @@ - (IBAction)changedFileStorageFormat:(id)sender {
[self runQueuedStorageFormatChangeInvocation];
}
}
-- (IBAction)verifySyncAccount:(id)sender {
- [[view window] endEditingFor:notationalAccountField];
- [[view window] endEditingFor:notationalPasswordField];
+
+- (IBAction)toggledSyncing:(id)sender {
+ [notationPrefs setSyncEnabled:[enabledSyncButton state] forService:SimplenoteServiceName];
+ [self setSyncControlsState:[enabledSyncButton state]];
+}
+
+- (IBAction)syncFrequencyChange:(id)sender {
+ if (sender) {
+ [self performSelector:_cmd withObject:nil afterDelay:0.0];
+ } else {
+ [notationPrefs setSyncFrequency:[syncingFrequency selectedTag] forService:SimplenoteServiceName];
+ }
+}
+
+- (void)syncEditingDidEnd:(NSNotification *)aNotification {
+ if (!verificationAttempted) {
+ [self cancelLoginVerifier];
+ [self startLoginVerifier];
+ }
+}
+
+- (void)syncCredentialsDidChange:(NSNotification *)aNotification {
-
+ if ([aNotification object] == syncAccountField) {
+ [notationPrefs removeSyncPasswordForService:SimplenoteServiceName];
+ [notationPrefs setSyncUsername:[syncAccountField stringValue] forService:SimplenoteServiceName];
+
+ [self startVerifyingAfterDelay];
+ } else if ([aNotification object] == syncPasswordField) {
+ [self startVerifyingAfterDelay];
+ }
}
-- (IBAction)showWebOptions:(id)sender {
- if (![webOptionsWindow isVisible])
- [webOptionsWindow center];
-
- [webOptionsWindow makeKeyAndOrderFront:sender];
+
+
+- (void)setVerificationStatus:(int)status withString:(NSString*)aString {
+
+ switch (status) {
+ case VERIFY_NOT_ATTEMPTED:
+ verificationAttempted = NO;
+ [verifyStatusImageView setImage:nil];
+ break;
+ case VERIFY_FAILED:
+ verificationAttempted = YES;
+ [verifyStatusImageView setImage:[NSImage imageNamed:@"statusError"]];
+ break;
+ case VERIFY_IN_PROGRESS:
+ [verifyStatusImageView setImage:[NSImage imageNamed:@"statusInProgress"]];
+ break;
+ case VERIFY_SUCCESS:
+ verificationAttempted = YES;
+ [verifyStatusImageView setImage:[NSImage imageNamed:@"statusValidated"]];
+ break;
+ }
+ [verifyStatusImageView setHidden: VERIFY_NOT_ATTEMPTED == status];
+ [verifyStatusField setStringValue: aString ? aString : @""];
+}
+
+- (void)startVerifyingAfterDelay {
+ [self cancelLoginVerifier];
+
+ [self performSelector:@selector(startLoginVerifier) withObject:nil afterDelay:1.5];
+}
+
+- (void)cancelLoginVerifier {
+ [loginVerifier cancel];
+ [loginVerifier autorelease];
+ loginVerifier = nil;
+ [self setVerificationStatus:VERIFY_NOT_ATTEMPTED withString:@""];
+
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(startLoginVerifier) object:nil];
+}
+
+- (void)startLoginVerifier {
+ if (!loginVerifier && [[syncAccountField stringValue] length] && [[syncPasswordField stringValue] length]) {
+
+ NSURL *loginURL = [SimplenoteSession servletURLWithPath:@"/api/login" parameters:nil];
+ loginVerifier = [[SyncResponseFetcher alloc] initWithURL:loginURL bodyStringAsUTF8B64:
+ [[NSDictionary dictionaryWithObjectsAndKeys: [syncAccountField stringValue], @"email", [syncPasswordField stringValue], @"password", nil]
+ URLEncodedString] delegate:self];
+ [loginVerifier start];
+ [self setVerificationStatus:VERIFY_IN_PROGRESS withString:@""];
+ }
+}
+
+- (void)syncResponseFetcher:(SyncResponseFetcher*)fetcher receivedData:(NSData*)data returningError:(NSString*)errString {
+ BOOL authFailed = errString && [fetcher statusCode] == 400;
+
+ [self setVerificationStatus:errString ? VERIFY_FAILED : VERIFY_SUCCESS withString:
+ authFailed ? NSLocalizedString(@"Incorrect login and password", @"sync status menu msg") : errString];
+
+ if (authFailed) {
+ [notationPrefs removeSyncPasswordForService:SimplenoteServiceName];
+ } else {
+ [notationPrefs setSyncPassword:[syncPasswordField stringValue] forService:SimplenoteServiceName];
+ }
}
- (IBAction)changedSecureTextEntry:(id)sender {
@@ -316,8 +437,8 @@ - (IBAction)changePassphrase:(id)sender {
[changer showAroundWindow:[view window]];
}
-- (IBAction)createNotationalAccount:(id)sender {
- //go to web page URL
+- (IBAction)visitSimplenoteSite:(id)sender {
+ [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://simplenoteapp.com/"]];
}
- (IBAction)removedExtension:(id)sender {
@@ -359,15 +480,9 @@ - (void)encryptionFormatMismatchSheetDidEnd:(NSWindow *)sheet returnCode:(int)re
[postStorageFormatInvocation release];
//so queue it up:
- NSWindow *myWindow = [view window];
- SEL selector = @selector(showAroundWindow:resultDelegate:);
- postStorageFormatInvocation = [NSInvocation invocationWithMethodSignature:[picker methodSignatureForSelector:selector]];
- [postStorageFormatInvocation setSelector:selector];
- [postStorageFormatInvocation setTarget:picker];
- [postStorageFormatInvocation setArgument:&myWindow atIndex:2];
- [postStorageFormatInvocation setArgument:&self atIndex:3];
- [postStorageFormatInvocation retainArguments];
- [postStorageFormatInvocation retain];
+ InvocationRecorder *invRecorder = [InvocationRecorder invocationRecorder];
+ [[invRecorder prepareWithInvocationTarget:picker] showAroundWindow:[view window] resultDelegate:self];
+ postStorageFormatInvocation = [[invRecorder invocation] retain];
}
}

0 comments on commit 20067de

Please sign in to comment.