Fix issue with race condition when alternating UUIDs, removing locally, and mapping
This commit is contained in:
@@ -6,6 +6,7 @@ class ModelManager {
|
|||||||
this.tags = [];
|
this.tags = [];
|
||||||
this.itemSyncObservers = [];
|
this.itemSyncObservers = [];
|
||||||
this.itemChangeObservers = [];
|
this.itemChangeObservers = [];
|
||||||
|
this.itemsPendingRemoval = [];
|
||||||
this.items = [];
|
this.items = [];
|
||||||
this._extensions = [];
|
this._extensions = [];
|
||||||
this.acceptableContentTypes = ["Note", "Tag", "Extension", "SN|Editor", "SN|Theme", "SN|Component", "SF|Extension"];
|
this.acceptableContentTypes = ["Note", "Tag", "Extension", "SN|Editor", "SN|Theme", "SN|Component", "SF|Extension"];
|
||||||
@@ -30,7 +31,7 @@ class ModelManager {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
alternateUUIDForItem(item, callback) {
|
alternateUUIDForItem(item, callback, removeOriginal) {
|
||||||
// we need to clone this item and give it a new uuid, then delete item with old uuid from db (you can't mofidy uuid's in our indexeddb setup)
|
// we need to clone this item and give it a new uuid, then delete item with old uuid from db (you can't mofidy uuid's in our indexeddb setup)
|
||||||
var newItem = this.createItem(item);
|
var newItem = this.createItem(item);
|
||||||
|
|
||||||
@@ -41,12 +42,20 @@ class ModelManager {
|
|||||||
|
|
||||||
this.informModelsOfUUIDChangeForItem(newItem, item.uuid, newItem.uuid);
|
this.informModelsOfUUIDChangeForItem(newItem, item.uuid, newItem.uuid);
|
||||||
|
|
||||||
this.removeItemLocally(item, function(){
|
var block = () => {
|
||||||
this.addItem(newItem);
|
this.addItem(newItem);
|
||||||
newItem.setDirty(true);
|
newItem.setDirty(true);
|
||||||
newItem.markAllReferencesDirty();
|
newItem.markAllReferencesDirty();
|
||||||
callback();
|
callback();
|
||||||
}.bind(this));
|
}
|
||||||
|
|
||||||
|
if(removeOriginal) {
|
||||||
|
this.removeItemLocally(item, function(){
|
||||||
|
block();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
block();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
informModelsOfUUIDChangeForItem(newItem, oldUUID, newUUID) {
|
informModelsOfUUIDChangeForItem(newItem, oldUUID, newUUID) {
|
||||||
@@ -94,15 +103,23 @@ class ModelManager {
|
|||||||
// first loop should add and process items
|
// first loop should add and process items
|
||||||
for (var json_obj of items) {
|
for (var json_obj of items) {
|
||||||
json_obj = _.omit(json_obj, omitFields || [])
|
json_obj = _.omit(json_obj, omitFields || [])
|
||||||
var item = this.findItem(json_obj["uuid"]);
|
var item = this.findItem(json_obj.uuid);
|
||||||
|
|
||||||
_.omit(json_obj, omitFields);
|
|
||||||
|
|
||||||
if(item) {
|
if(item) {
|
||||||
item.updateFromJSON(json_obj);
|
item.updateFromJSON(json_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(json_obj["deleted"] == true || !_.includes(this.acceptableContentTypes, json_obj["content_type"])) {
|
if(!json_obj.content && !item) {
|
||||||
|
// A new incoming item must have a content field. If not, something has set an invalid state.
|
||||||
|
console.error("Content is missing for new item.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.itemsPendingRemoval.includes(json_obj.uuid)) {
|
||||||
|
_.pull(this.itemsPendingRemoval, json_obj.uuid);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(json_obj.deleted == true || !_.includes(this.acceptableContentTypes, json_obj["content_type"])) {
|
||||||
if(item) {
|
if(item) {
|
||||||
allModels.push(item);
|
allModels.push(item);
|
||||||
this.removeItemLocally(item)
|
this.removeItemLocally(item)
|
||||||
@@ -302,6 +319,8 @@ class ModelManager {
|
|||||||
|
|
||||||
item.isBeingRemovedLocally();
|
item.isBeingRemovedLocally();
|
||||||
|
|
||||||
|
this.itemsPendingRemoval.push(item.uuid);
|
||||||
|
|
||||||
if(item.content_type == "Tag") {
|
if(item.content_type == "Tag") {
|
||||||
_.pull(this.tags, item);
|
_.pull(this.tags, item);
|
||||||
} else if(item.content_type == "Note") {
|
} else if(item.content_type == "Note") {
|
||||||
|
|||||||
@@ -75,13 +75,16 @@ class SyncManager {
|
|||||||
Alternating here forces us to to create duplicates of the items instead.
|
Alternating here forces us to to create duplicates of the items instead.
|
||||||
*/
|
*/
|
||||||
markAllItemsDirtyAndSaveOffline(callback, alternateUUIDs) {
|
markAllItemsDirtyAndSaveOffline(callback, alternateUUIDs) {
|
||||||
var originalItems = this.modelManager.allItems;
|
|
||||||
|
|
||||||
var block = (items) => {
|
// use a copy, as alternating uuid will affect array
|
||||||
for(var item of items) {
|
var originalItems = this.modelManager.allItems.slice();
|
||||||
|
|
||||||
|
var block = () => {
|
||||||
|
var allItems = this.modelManager.allItems;
|
||||||
|
for(var item of allItems) {
|
||||||
item.setDirty(true);
|
item.setDirty(true);
|
||||||
}
|
}
|
||||||
this.writeItemsToLocalStorage(items, false, callback);
|
this.writeItemsToLocalStorage(allItems, false, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(alternateUUIDs) {
|
if(alternateUUIDs) {
|
||||||
@@ -90,18 +93,19 @@ class SyncManager {
|
|||||||
let alternateNextItem = () => {
|
let alternateNextItem = () => {
|
||||||
if(index >= originalItems.length) {
|
if(index >= originalItems.length) {
|
||||||
// We don't use originalItems as altnerating UUID will have deleted them.
|
// We don't use originalItems as altnerating UUID will have deleted them.
|
||||||
block(this.modelManager.allItems);
|
block();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var item = originalItems[index];
|
var item = originalItems[index];
|
||||||
this.modelManager.alternateUUIDForItem(item, alternateNextItem);
|
index++;
|
||||||
++index;
|
// false => dont remove original. We want to keep both copies
|
||||||
|
this.modelManager.alternateUUIDForItem(item, alternateNextItem, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
alternateNextItem();
|
alternateNextItem();
|
||||||
} else {
|
} else {
|
||||||
block(originalItems);
|
block();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,7 +365,7 @@ class SyncManager {
|
|||||||
// UUID conflicts can occur if a user attempts to
|
// UUID conflicts can occur if a user attempts to
|
||||||
// import an old data archive with uuids from the old account into a new account
|
// import an old data archive with uuids from the old account into a new account
|
||||||
handled = true;
|
handled = true;
|
||||||
this.modelManager.alternateUUIDForItem(item, handleNext);
|
this.modelManager.alternateUUIDForItem(item, handleNext, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(error.tag === "sync_conflict") {
|
else if(error.tag === "sync_conflict") {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
|
|
||||||
.scrollable
|
.scrollable
|
||||||
.infinite-scroll{"infinite-scroll" => "ctrl.paginate()", "can-load" => "true", "threshold" => "200"}
|
.infinite-scroll{"infinite-scroll" => "ctrl.paginate()", "can-load" => "true", "threshold" => "200"}
|
||||||
.note{"ng-repeat" => "note in (ctrl.sortedNotes = (ctrl.tag.notes | filter: ctrl.filterNotes | sortBy: ctrl.sortBy| limitTo:ctrl.notesToDisplay))",
|
.note{"ng-repeat" => "note in (ctrl.sortedNotes = (ctrl.tag.notes | filter: ctrl.filterNotes | sortBy: ctrl.sortBy| limitTo:ctrl.notesToDisplay)) track by note.uuid",
|
||||||
"ng-click" => "ctrl.selectNote(note)", "ng-class" => "{'selected' : ctrl.selectedNote == note}"}
|
"ng-click" => "ctrl.selectNote(note)", "ng-class" => "{'selected' : ctrl.selectedNote == note}"}
|
||||||
%strong.red.medium{"ng-if" => "note.conflict_of"} Conflicted copy
|
%strong.red.medium{"ng-if" => "note.conflict_of"} Conflicted copy
|
||||||
%strong.red.medium{"ng-if" => "note.errorDecrypting"} Error decrypting
|
%strong.red.medium{"ng-if" => "note.errorDecrypting"} Error decrypting
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
.info
|
.info
|
||||||
%input.title{"ng-disabled" => "true", "ng-model" => "ctrl.allTag.title"}
|
%input.title{"ng-disabled" => "true", "ng-model" => "ctrl.allTag.title"}
|
||||||
.count {{ctrl.noteCount(ctrl.allTag)}}
|
.count {{ctrl.noteCount(ctrl.allTag)}}
|
||||||
.tag{"ng-repeat" => "tag in ctrl.tags", "ng-click" => "ctrl.selectTag(tag)", "ng-class" => "{'selected' : ctrl.selectedTag == tag}"}
|
.tag{"ng-repeat" => "tag in ctrl.tags track by tag.uuid", "ng-click" => "ctrl.selectTag(tag)", "ng-class" => "{'selected' : ctrl.selectedTag == tag}"}
|
||||||
.info
|
.info
|
||||||
%input.title{"ng-attr-id" => "tag-{{tag.uuid}}", "ng-click" => "ctrl.selectTag(tag)", "ng-model" => "tag.title",
|
%input.title{"ng-attr-id" => "tag-{{tag.uuid}}", "ng-click" => "ctrl.selectTag(tag)", "ng-model" => "tag.title",
|
||||||
"ng-keyup" => "$event.keyCode == 13 && ctrl.saveTag($event, tag)", "mb-autofocus" => "true", "should-focus" => "ctrl.newTag || ctrl.editingTag == tag",
|
"ng-keyup" => "$event.keyCode == 13 && ctrl.saveTag($event, tag)", "mb-autofocus" => "true", "should-focus" => "ctrl.newTag || ctrl.editingTag == tag",
|
||||||
|
|||||||
Reference in New Issue
Block a user