Merge branch 'master' of github.com:standardnotes/web into sfv3
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)}}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user