Optimize digest cycle by reloading state only upon explicit data change

This commit is contained in:
Mo Bitar
2019-01-30 17:00:16 -06:00
parent 4f42330167
commit 595ce94295
7 changed files with 103 additions and 78 deletions

View File

@@ -35,15 +35,19 @@ angular.module('app')
syncManager.addEventHandler((syncEvent, data) => {
if(syncEvent == "local-data-loaded") {
this.localDataLoaded = true;
if(this.tag && this.tag.notes.length == 0) {
if(this.tag && this.notes.length == 0) {
this.createNewNote();
}
}
});
modelManager.addItemSyncObserver("note-list", "Note", (allItems, validItems, deletedItems, source, sourceKey) => {
modelManager.addItemSyncObserver("note-list", "*", (allItems, validItems, deletedItems, source, sourceKey) => {
// reload our notes
this.setNotes(this.tag.notes);
// Note has changed values, reset its flags
for(var note of allItems) {
let notes = allItems.filter((item) => item.content_type == "Note");
for(let note of notes) {
note.flags = null;
}
@@ -53,6 +57,14 @@ angular.module('app')
}
});
this.setNotes = function(notes) {
this.notes = this.sortNotes(notes, this.sortBy, this.sortReverse);
}
this.reorderNotes = function() {
this.setNotes(this.notes);
}
this.loadPreferences = function() {
let prevSortValue = this.sortBy;
@@ -147,7 +159,7 @@ angular.module('app')
this.panelTitle = function() {
if(this.isFiltering()) {
return `${this.tag.notes.filter((i) => {return i.visible;}).length} search results`;
return `${this.notes.filter((i) => {return i.visible;}).length} search results`;
} else if(this.tag) {
return `${this.tag.title}`;
}
@@ -245,16 +257,21 @@ angular.module('app')
this.noteFilter.text = "";
if(tag.notes.length > 0) {
tag.notes.forEach((note) => { note.visible = true; })
this.selectFirstNote();
} else if(this.localDataLoaded) {
this.createNewNote();
}
this.setNotes(tag.notes);
// perform in timeout since visibleNotes relies on renderedNotes which relies on render to complete
$timeout(() => {
if(this.notes.length > 0) {
this.notes.forEach((note) => { note.visible = true; })
this.selectFirstNote();
} else if(this.localDataLoaded) {
this.createNewNote();
}
})
}
this.visibleNotes = function() {
return this.sortedNotes.filter(function(note){
return this.renderedNotes.filter(function(note){
return note.visible;
});
}
@@ -298,9 +315,9 @@ angular.module('app')
}
this.createNewNote = function() {
// The "Note X" counter is based off this.tag.notes.length, but sometimes, what you see in the list is only a subset.
// The "Note X" counter is based off this.notes.length, but sometimes, what you see in the list is only a subset.
// We can use this.visibleNotes().length, but that only accounts for non-paginated results, so first 15 or so.
var title = "Note" + (this.tag.notes ? (" " + (this.tag.notes.length + 1)) : "");
var title = "Note" + (this.notes ? (" " + (this.notes.length + 1)) : "");
let newNote = modelManager.createItem({content_type: "Note", content: {text: "", title: title}});
newNote.dummy = true;
this.newNote = newNote;
@@ -389,12 +406,14 @@ angular.module('app')
this.toggleReverseSort = function() {
this.selectedMenuItem();
this.sortReverse = !this.sortReverse;
this.reorderNotes();
authManager.setUserPrefValue("sortReverse", this.sortReverse);
authManager.syncUserPreferences();
}
this.setSortBy = function(type) {
this.sortBy = type;
this.reorderNotes();
authManager.setUserPrefValue("sortBy", this.sortBy);
authManager.syncUserPreferences();
}
@@ -416,4 +435,50 @@ angular.module('app')
return note.tags && note.tags.length > 1;
}
this.sortNotes = function(items, sortBy, reverse) {
let sortValueFn = (a, b, pinCheck = false) => {
if(!pinCheck) {
if(a.pinned && b.pinned) {
return sortValueFn(a, b, true);
}
if(a.pinned) { return -1; }
if(b.pinned) { return 1; }
}
var aValue = a[sortBy] || "";
var bValue = b[sortBy] || "";
let vector = 1;
if(reverse) {
vector *= -1;
}
if(sortBy == "title") {
aValue = aValue.toLowerCase();
bValue = bValue.toLowerCase();
if(aValue.length == 0 && bValue.length == 0) {
return 0;
} else if(aValue.length == 0 && bValue.length != 0) {
return 1 * vector;
} else if(aValue.length != 0 && bValue.length == 0) {
return -1 * vector;
} else {
vector *= -1;
}
}
if(aValue > bValue) { return -1 * vector;}
else if(aValue < bValue) { return 1 * vector;}
return 0;
}
items = items || [];
var result = items.sort(function(a, b){
return sortValueFn(a, b);
})
return result;
};
});

View File

@@ -33,6 +33,26 @@ angular.module('app')
}
});
modelManager.addItemSyncObserver("note-list", "*", (allItems, validItems, deletedItems, source, sourceKey) => {
// recompute note counts
let tags = [];
if(this.tags) {
tags = tags.concat(this.tags);
}
if(this.smartTags) {
tags = tags.concat(this.smartTags);
}
for(let tag of tags) {
var validNotes = SNNote.filterDummyNotes(tag.notes).filter(function(note){
return !note.archived && !note.content.trashed;
});
tag.cachedNoteCount = validNotes.length;
}
});
this.panelController = {};
$rootScope.$on("user-preferences-changed", () => {
@@ -146,12 +166,4 @@ angular.module('app')
this.removeTag()(tag);
this.selectTag(this.smartTags[0]);
}
this.noteCount = function(tag) {
var validNotes = SNNote.filterDummyNotes(tag.notes).filter(function(note){
return !note.archived && !note.content.trashed;
});
return validNotes.length;
}
});

View File

@@ -1,48 +0,0 @@
angular.module('app')
.filter('sortBy', function ($filter) {
return function(items, sortBy, reverse) {
let sortValueFn = (a, b, pinCheck = false) => {
if(!pinCheck) {
if(a.pinned && b.pinned) {
return sortValueFn(a, b, true);
}
if(a.pinned) { return -1; }
if(b.pinned) { return 1; }
}
var aValue = a[sortBy] || "";
var bValue = b[sortBy] || "";
let vector = 1;
if(reverse) {
vector *= -1;
}
if(sortBy == "title") {
aValue = aValue.toLowerCase();
bValue = bValue.toLowerCase();
if(aValue.length == 0 && bValue.length == 0) {
return 0;
} else if(aValue.length == 0 && bValue.length != 0) {
return 1 * vector;
} else if(aValue.length != 0 && bValue.length == 0) {
return -1 * vector;
} else {
vector *= -1;
}
}
if(aValue > bValue) { return -1 * vector;}
else if(aValue < bValue) { return 1 * vector;}
return 0;
}
items = items || [];
var result = items.sort(function(a, b){
return sortValueFn(a, b);
})
return result;
};
});

View File

@@ -1,5 +0,0 @@
angular.module('app').filter('startFrom', function() {
return function(input, start) {
return input.slice(start);
};
});

View File

@@ -123,7 +123,8 @@ class ModelManager extends SFModelManager {
let notTrashedPredicate = new SFPredicate("content.trashed", "=", false);
predicates.push(notTrashedPredicate);
}
return this.itemsMatchingPredicates(predicates);
let results = this.itemsMatchingPredicates(predicates);
return results;
}
trashSmartTag() {

View File

@@ -45,7 +45,7 @@
.scrollable
.infinite-scroll#notes-scrollable{"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:ctrl.sortReverse | limitTo:ctrl.notesToDisplay)) track by note.uuid",
.note{"ng-repeat" => "note in (ctrl.renderedNotes = (ctrl.notes | filter: ctrl.filterNotes | limitTo:ctrl.notesToDisplay)) track by note.uuid",
"ng-click" => "ctrl.selectNote(note, true)", "ng-class" => "{'selected' : ctrl.selectedNote == note}"}
%strong.red.medium-text{"ng-if" => "note.conflict_of"} Conflicted copy
%strong.red.medium-text{"ng-if" => "note.errorDecrypting"} Unable to Decrypt

View File

@@ -17,7 +17,7 @@
"ng-class" => "{'selected' : ctrl.selectedTag == tag, 'faded' : !tag.content.isAllTag}"}
.info
%input.title{"ng-disabled" => "true", "ng-model" => "tag.title"}
.count{"ng-show" => "tag.content.isAllTag"} {{ctrl.noteCount(tag)}}
.count{"ng-show" => "tag.content.isAllTag"} {{tag.cachedNoteCount}}
.tags-title-section.section-title-bar
.section-title-bar-header
@@ -28,7 +28,7 @@
%input.title{"ng-attr-id" => "tag-{{tag.uuid}}", "ng-click" => "ctrl.selectTag(tag)", "ng-model" => "tag.title",
"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)}}
.count {{tag.cachedNoteCount}}
.red.small-text.bold{"ng-if" => "tag.conflict_of"} Conflicted copy
.red.small-text.bold{"ng-if" => "tag.errorDecrypting"} Unable to Decrypt