Merge branch 'master' of github.com:standardnotes/web into sfv3

This commit is contained in:
Mo Bitar
2018-05-17 10:08:50 -05:00
9 changed files with 106 additions and 22 deletions

View File

@@ -40,6 +40,33 @@ angular.module('app')
this.loadTagsString();
}.bind(this));
modelManager.addItemSyncObserver("component-manager", "Note", (allItems, validItems, deletedItems, source) => {
if(!this.note) { return; }
// Before checking if isMappingSourceRetrieved, we check if this item was deleted via a local source,
// such as alternating uuids during sign in. Otherwise, we only want to make interface updates if it's a
// remote retrieved source.
if(this.note.deleted) {
$rootScope.notifyDelete();
return;
}
if(!ModelManager.isMappingSourceRetrieved(source)) {
return;
}
var matchingNote = allItems.find((item) => {
return item.uuid == this.note.uuid;
});
if(!matchingNote) {
return;
}
// Update tags
this.loadTagsString();
});
this.noteDidChange = function(note, oldNote) {
this.setNote(note, oldNote);
this.reloadComponentContext();

View File

@@ -145,11 +145,11 @@ angular.module('app')
}
$scope.tagsSelectionMade = function(tag) {
$scope.selectedTag = tag;
if($scope.selectedNote && $scope.selectedNote.dummy) {
modelManager.removeItemLocally($scope.selectedNote);
}
$scope.selectedTag = tag;
}
$scope.tagsAddNew = function(tag) {
@@ -227,7 +227,7 @@ angular.module('app')
this.$apply(fn);
};
$scope.notifyDelete = function() {
$rootScope.notifyDelete = function() {
$timeout(function() {
$rootScope.$broadcast("noteDeleted");
}.bind(this), 0);
@@ -243,7 +243,7 @@ angular.module('app')
if(note.dummy) {
modelManager.removeItemLocally(note);
$scope.notifyDelete();
$rootScope.notifyDelete();
return;
}
@@ -251,11 +251,11 @@ angular.module('app')
if(authManager.offline()) {
// when deleting items while ofline, we need to explictly tell angular to refresh UI
setTimeout(function () {
$scope.notifyDelete();
$rootScope.notifyDelete();
$scope.safeApply();
}, 50);
} else {
$scope.notifyDelete();
$rootScope.notifyDelete();
}
}, null, "deleteNote");
}

View File

@@ -89,7 +89,7 @@ angular.module('app')
this.onNoteRemoval = function() {
let visibleNotes = this.visibleNotes();
if(this.selectedIndex < visibleNotes.length) {
this.selectNote(visibleNotes[this.selectedIndex]);
this.selectNote(visibleNotes[Math.max(this.selectedIndex, 0)]);
} else {
this.selectNote(visibleNotes[visibleNotes.length - 1]);
}
@@ -190,11 +190,14 @@ angular.module('app')
}
this.selectNote = function(note, viaClick = false) {
if(!note) { return; }
if(!note) {
this.createNewNote();
return;
}
this.selectedNote = note;
note.conflict_of = null; // clear conflict
this.selectionMade()(note);
this.selectedIndex = this.visibleNotes().indexOf(note);
this.selectedIndex = Math.max(this.visibleNotes().indexOf(note), 0);
if(viaClick && this.isFiltering()) {
desktopManager.searchText(this.noteFilter.text);

View File

@@ -129,10 +129,12 @@ angular.module('app')
return;
}
this.save()(tag, function(savedTag){
this.selectTag(tag);
this.newTag = null;
}.bind(this));
this.save()(tag, (savedTag) => {
$timeout(() => {
this.selectTag(tag);
this.newTag = null;
})
});
}
function inputElementForTag(tag) {

View File

@@ -105,11 +105,18 @@ class AccountMenu {
}
$scope.login = function(extraParams) {
// Prevent a timed sync from occuring while signing in. There may be a race condition where when
// calling `markAllItemsDirtyAndSaveOffline` during sign in, if an authenticated sync happens to occur
// right before that's called, items retreived from that sync will be marked as dirty, then resynced, causing mass duplication.
// Unlock sync after all sign in processes are complete.
syncManager.lockSyncing();
$scope.formData.status = "Generating Login Keys...";
$timeout(function(){
authManager.login($scope.formData.url, $scope.formData.email, $scope.formData.user_password, $scope.formData.ephemeral, extraParams,
(response) => {
if(!response || response.error) {
syncManager.unlockSyncing();
$scope.formData.status = null;
var error = response ? response.error : {message: "An unknown error occured."}
@@ -133,7 +140,10 @@ class AccountMenu {
// Success
else {
$scope.onAuthSuccess();
$scope.onAuthSuccess(() => {
syncManager.unlockSyncing();
syncManager.sync("onLogin");
});
}
});
})
@@ -156,7 +166,9 @@ class AccountMenu {
var error = response ? response.error : {message: "An unknown error occured."}
alert(error.message);
} else {
$scope.onAuthSuccess();
$scope.onAuthSuccess(() => {
syncManager.sync("onRegister");
});
}
});
})
@@ -170,12 +182,12 @@ class AccountMenu {
}
}
$scope.onAuthSuccess = function() {
$scope.onAuthSuccess = function(callback) {
var block = function() {
$timeout(function(){
$scope.onSuccessfulAuth()();
syncManager.refreshErroredItems();
syncManager.sync("onAuthSuccess");
callback && callback();
})
}
@@ -196,7 +208,7 @@ class AccountMenu {
// clearAllModels will remove data from backing store, but not from working memory
// See: https://github.com/standardnotes/desktop/issues/131
$scope.clearDatabaseAndRewriteAllItems = function(alternateUuids, callback) {
storageManager.clearAllModels(function(){
storageManager.clearAllModels(() => {
syncManager.markAllItemsDirtyAndSaveOffline(function(){
callback && callback();
}, alternateUuids)

View File

@@ -55,6 +55,13 @@ class Item {
}
}
refreshContentObject() {
// Before an item can be duplicated or cloned, we must update this.content (if it is an object) with the object's
// current physical properties, because updateFromJSON, which is what all new items must go through,
// will call this.mapContentToLocalProperties(this.contentObject), which may have stale values if not explicitly updated.
this.content = this.structureParams();
}
/* Allows the item to handle the case where the item is deleted and the content is null */
handleDeletedContent() {

View File

@@ -10,6 +10,14 @@ class ModelManager {
ModelManager.MappingSourceRemoteActionRetrieved = "MappingSourceRemoteActionRetrieved"; /* aciton-based Extensions like note history */
ModelManager.MappingSourceFileImport = "MappingSourceFileImport";
ModelManager.isMappingSourceRetrieved = (source) => {
return [
ModelManager.MappingSourceRemoteRetrieved,
ModelManager.MappingSourceComponentRetrieved,
ModelManager.MappingSourceRemoteActionRetrieved
].includes(source);
}
this.storageManager = storageManager;
this.notes = [];
this.tags = [];
@@ -44,7 +52,11 @@ class ModelManager {
}
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 modify uuid's in our indexeddb setup)
// Collapse in memory properties to item's content object, as the new item will be created based on the content object, and not the physical properties. (like note.text or note.title)
item.refreshContentObject();
var newItem = this.createItem(item);
newItem.uuid = SFJS.crypto.generateUUID();
@@ -54,6 +66,7 @@ class ModelManager {
this.informModelsOfUUIDChangeForItem(newItem, item.uuid, newItem.uuid);
console.log(item.uuid, "-->", newItem.uuid);
var block = () => {
@@ -261,7 +274,14 @@ class ModelManager {
return item;
}
createDuplicateItem(itemResponse, sourceItem) {
/*
Be sure itemResponse is a generic Javascript object, and not an Item.
An Item needs to collapse its properties into its content object before it can be duplicated.
Note: the reason we need this function is specificallty for the call to resolveReferencesForItem.
This method creates but does not add the item to the global inventory. It's used by syncManager
to check if this prospective duplicate item is identical to another item, including the references.
*/
createDuplicateItem(itemResponse) {
var dup = this.createItem(itemResponse, true);
this.resolveReferencesForItem(dup);
return dup;

View File

@@ -193,8 +193,21 @@ class SyncManager {
this.$interval.cancel(this.syncStatus.checker);
}
lockSyncing() {
this.syncLocked = true;
}
unlockSyncing() {
this.syncLocked = false;
}
sync(callback, options = {}, source) {
if(this.syncLocked) {
console.log("Sync Locked, Returning;");
return;
}
if(!options) options = {};
if(typeof callback == 'string') {
@@ -475,7 +488,7 @@ class SyncManager {
// We want a new uuid for the new item. Note that this won't neccessarily adjust references.
itemResponse.uuid = null;
var dup = this.modelManager.createDuplicateItem(itemResponse, item);
var dup = this.modelManager.createDuplicateItem(itemResponse);
if(!itemResponse.deleted && !item.isItemContentEqualWith(dup)) {
this.modelManager.addItem(dup);
dup.conflict_of = item.uuid;

View File

@@ -18,7 +18,7 @@
.tag{"ng-repeat" => "tag in ctrl.tags track by tag.uuid", "ng-click" => "ctrl.selectTag(tag)", "ng-class" => "{'selected' : ctrl.selectedTag == tag}"}
.info
%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)", "sn-autofocus" => "true", "should-focus" => "ctrl.newTag || ctrl.editingTag == tag",
"ng-keyup" => "$event.keyCode == 13 && $event.target.blur()", "sn-autofocus" => "true", "should-focus" => "ctrl.newTag || ctrl.editingTag == tag",
"ng-change" => "ctrl.tagTitleDidChange(tag)", "ng-blur" => "ctrl.saveTag($event, tag)", "spellcheck" => "false"}
.count {{ctrl.noteCount(tag)}}