Permalink
Browse files

change Simplenote sync from using app engine api2 to Simperium api

Fixes an issue where NV would revert a note's contents after the first sync.
Provides proper support for merging of content if a note has been edited elsewhere inbetween syncs.
Much faster syncing.

Best effort has been made to preserve existing semantics and reuse existing data structures (syncMD)
wherever possible so users should not be affected when they upgrade.
Most changes are related to index fetching as Simperium only provides item id and version in its'
index whereas SN API2 provided metadata like a note's deleted status. Since deleted is no longer
available, we must fetch each note to determine it's deleted status, therefore additional code to
detect deleted status has been added to all the entry collectors.
This update also supports Simperium's changes api to fetch only the diffs for notes since a certain
mark (provided in changes and in index). A full sync (index pull) is performed when app is first
opened, subsequently only changes will be fetched.
  • Loading branch information...
1 parent 17d288e commit edd6a7fcc19f9c71418c303117605bdc31122745 @fredrocious fredrocious committed Feb 27, 2013
@@ -448,17 +448,18 @@ - (void)cancelLoginVerifier {
- (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];
+ NSURL *loginURL = [SimplenoteSession servletURLWithPath:@"/api2/login" parameters:nil];
+ loginVerifier = [[SyncResponseFetcher alloc] initWithURL:loginURL POSTData:
+ [[[NSDictionary dictionaryWithObjectsAndKeys:
+ [syncAccountField stringValue], @"email", [syncPasswordField stringValue], @"password", @"1", @"api", nil] URLEncodedString]
+ dataUsingEncoding:NSUTF8StringEncoding] 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;
+ 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];
@@ -483,7 +484,7 @@ - (IBAction)changePassphrase:(id)sender {
}
- (IBAction)visitSimplenoteSite:(id)sender {
- [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://simplenoteapp.com/"]];
+ [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://simplenote.com/"]];
}
- (IBAction)makeDefaultExtension:(id)sender {
@@ -28,6 +28,7 @@
- (NoteObject*)noteForKey:(NSString*)key ofServiceClass:(Class<SyncServiceSession>)serviceClass;
+- (void)processPartialNotesList:(NSArray*)entries withRemovedList:(NSArray*)removedEntries fromSyncSession:(id <SyncServiceSession>)syncSession;
- (void)makeNotesMatchList:(NSArray*)MDEntries fromSyncSession:(id <SyncServiceSession>)syncSession;
- (void)schedulePushToAllSyncServicesForNote:(id <SynchronizedNote>)aNote;
@@ -92,6 +92,12 @@ - (void)syncSession:(id <SyncServiceSession>)syncSession receivedFullNoteList:(N
[self makeNotesMatchList:allEntries fromSyncSession:syncSession];
}
+- (void)syncSession:(id <SyncServiceSession>)syncSession receivedPartialNoteList:(NSArray*)entries withRemovedList:(NSArray*)removed {
+
+ [self processPartialNotesList:entries withRemovedList:removed fromSyncSession:syncSession];
+}
+
+
- (void)syncSession:(id <SyncServiceSession>)syncSession receivedAddedNotes:(NSArray*)addedNotes {
//insert these notes into the list
//no need to "reveal" them to the user
@@ -120,6 +126,64 @@ - (void)syncSessionDidFinishRemoteModifications:(id <SyncServiceSession>)syncSes
[self _purgeAlreadyDistributedDeletedNotes];
}
+- (void)processPartialNotesList:(NSArray*)entries withRemovedList:(NSArray*)removedEntries fromSyncSession:(id <SyncServiceSession>)syncSession {
+ NSString *keyName = [[syncSession class] nameOfKeyElement];
+ NSString *serviceName = [[syncSession class] serviceName];
+ NSUInteger i = 0;
+ NSMutableArray *notesToDelete = [NSMutableArray array];
+ NSMutableArray *changedNotes = [NSMutableArray array];
+ NSMutableArray *checkEntries = [NSMutableArray array];
+ NSDictionary *localNotesDict = [self invertedDictionaryOfNotes:allNotes forSession:syncSession];
+
+ //we only have a partial remote list, plus possibly a list of permanently deleted notes.
+ //since we only have some remotes, we can't perform full sync operations like comparing
+ //full sets of notes to find out what local ones need to be removed. we rely on the partial
+ //list updates to keep us up to date.
+ for (i=0; i<[entries count]; i++) {
+ NSDictionary *remoteEntry = [entries objectAtIndex:i];
+ NSString *remoteKey = [remoteEntry objectForKey:keyName];
+ id <SynchronizedNote>note = [localNotesDict objectForKey:remoteKey];
+ NSDictionary *thisServiceInfo = [[note syncServicesMD] objectForKey:serviceName];
+ if ([localNotesDict objectForKey:remoteKey]) {
+ if ([syncSession remoteEntryWasMarkedDeleted:remoteEntry]) {
+ [notesToDelete addObject:note];
+ } else {
+ NSComparisonResult changeDiff = [syncSession localEntry:thisServiceInfo compareToRemoteEntry:remoteEntry];
+ if (changeDiff == NSOrderedAscending) {
+ [changedNotes addObject:note];
+ } else {
+ //NSLog(@"remote note not newer, not asking for changes");
+ }
+ }
+ } else {
+ // If we don't have the note and it is marked deleted, we don't care
+ // Otherwise, we should add it to checkEntries which will add it to our collection
+ if (![syncSession remoteEntryWasMarkedDeleted:remoteEntry]) {
+ [checkEntries addObject:remoteEntry];
+ }
+ }
+ }
+ for (i=0; i<[removedEntries count]; i++) {
+ NSDictionary *remoteEntry = [removedEntries objectAtIndex:i];
+ NSString *remoteKey = [remoteEntry objectForKey:keyName];
+ if ([localNotesDict objectForKey:remoteKey]) {
+ [notesToDelete addObject:[localNotesDict objectForKey:remoteKey]];
+ }
+ }
+ if ([checkEntries count]) {
+ [syncSession startCollectingAddedNotesWithEntries:checkEntries mergingWithNotes:nil];
+ }
+ if ([changedNotes count]) {
+ [syncSession startCollectingChangedNotesWithEntries:changedNotes];
+ }
+ if ([notesToDelete count]) {
+ NSLog(@"removing %u notes", [notesToDelete count]);
+ [syncSession suppressPushingForNotes:notesToDelete];
+ [self removeNotes:notesToDelete];
+ [syncSession stopSuppressingPushingForNotes:notesToDelete];
+ }
+}
+
- (void)makeNotesMatchList:(NSArray*)MDEntries fromSyncSession:(id <SyncServiceSession>)syncSession {
NSString *keyName = [[syncSession class] nameOfKeyElement];
NSString *serviceName = [[syncSession class] serviceName];
View
@@ -595,6 +595,10 @@ - (id)initWithCatalogEntry:(NoteCatalogEntry*)entry delegate:(id)aDelegate {
//assume any changes have been synchronized with undomanager
- (void)setContentString:(NSAttributedString*)attributedString {
+ [self setContentString:attributedString updateTime:YES];
+}
+
+- (void)setContentString:(NSAttributedString*)attributedString updateTime:(BOOL)updateTime {
if (attributedString) {
[contentString setAttributedString:attributedString];
@@ -604,7 +608,7 @@ - (void)setContentString:(NSAttributedString*)attributedString {
[delegate note:self attributeChanged:NotePreviewString];
- [self makeNoteDirtyUpdateTime:YES updateFile:YES];
+ [self makeNoteDirtyUpdateTime:updateTime updateFile:YES];
}
}
- (NSAttributedString*)contentString {
@@ -1538,7 +1542,7 @@ - (void)updateWithSyncBody:(NSString*)newBody andTitle:(NSString*)newTitle {
[attributedBodyString addStrikethroughNearDoneTagsForRange:NSMakeRange(0, [attributedBodyString length])];
//should eventually sync changes back to disk:
- [self setContentString:[attributedBodyString autorelease]];
+ [self setContentString:[attributedBodyString autorelease] updateTime:NO];
//actions that user-editing via AppDelegate would have handled for us:
[self updateContentCacheCStringIfNecessary];
@@ -28,7 +28,7 @@
NSArray *entriesToCollect;
NSMutableArray *entriesCollected, *entriesInError;
NSUInteger entryFinishedCount;
- NSString *authToken, *email;
+ NSString *simperiumToken;
SEL entriesFinishedCallback;
id collectionDelegate;
BOOL stopped;
@@ -37,7 +37,7 @@
id representedObject;
}
-- (id)initWithEntriesToCollect:(NSArray*)wantedEntries authToken:(NSString*)anAuthToken email:(NSString*)anEmail;
+- (id)initWithEntriesToCollect:(NSArray*)wantedEntries simperiumToken:(NSString*)aSimperiumToken;
- (NSArray*)entriesToCollect;
- (NSArray*)entriesCollected;
@@ -66,7 +66,7 @@
}
-- (id)initWithEntries:(NSArray*)wantedEntries operation:(SEL)opSEL authToken:(NSString*)anAuthToken email:(NSString*)anEmail;
+- (id)initWithEntries:(NSArray*)wantedEntries operation:(SEL)opSEL simperiumToken:(NSString*)aSimperiumToken;
- (SyncResponseFetcher*)_fetcherForNote:(NoteObject*)aNote creator:(BOOL)doesCreate;
- (SyncResponseFetcher*)fetcherForCreatingNote:(NoteObject*)aNote;
Oops, something went wrong.

0 comments on commit edd6a7f

Please sign in to comment.