diff --git a/Gruntfile.js b/Gruntfile.js
index 39a79854f..f3afa8c4a 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -72,6 +72,7 @@ module.exports = function(grunt) {
src: [
'node_modules/sn-models/dist/sn-models.js',
'app/assets/javascripts/app/*.js',
+ 'app/assets/javascripts/app/models/**/*.js',
'app/assets/javascripts/app/controllers/**/*.js',
'app/assets/javascripts/app/services/**/*.js',
'app/assets/javascripts/app/filters/**/*.js',
diff --git a/app/assets/javascripts/app/directives/views/actionsMenu.js b/app/assets/javascripts/app/directives/views/actionsMenu.js
index 7e97d868f..62cfece78 100644
--- a/app/assets/javascripts/app/directives/views/actionsMenu.js
+++ b/app/assets/javascripts/app/directives/views/actionsMenu.js
@@ -49,7 +49,7 @@ class ActionsMenu {
switch (action.verb) {
case "render": {
var item = response.item;
- actionsManager.presentRevisionPreviewModal(item);
+ actionsManager.presentRevisionPreviewModal(item.uuid, item.content);
}
}
}
diff --git a/app/assets/javascripts/app/directives/views/revisionPreviewModal.js b/app/assets/javascripts/app/directives/views/revisionPreviewModal.js
index 40cf3034b..d5d6313d3 100644
--- a/app/assets/javascripts/app/directives/views/revisionPreviewModal.js
+++ b/app/assets/javascripts/app/directives/views/revisionPreviewModal.js
@@ -4,9 +4,8 @@ class RevisionPreviewModal {
this.restrict = "E";
this.templateUrl = "directives/revision-preview-modal.html";
this.scope = {
- revision: "=",
- show: "=",
- callback: "="
+ uuid: "=",
+ content: "="
};
}
@@ -27,25 +26,23 @@ class RevisionPreviewModal {
var item;
if(asCopy) {
- var contentCopy = Object.assign({}, $scope.revision.content);
+ var contentCopy = Object.assign({}, $scope.content);
if(contentCopy.title) { contentCopy.title += " (copy)"; }
item = modelManager.createItem({content_type: "Note", content: contentCopy});
modelManager.addItem(item);
} else {
- // revision can be an ItemRevision revision object or just a plain SFItem
- var uuid = $scope.revision.uuid;
+ var uuid = $scope.uuid;
item = modelManager.findItem(uuid);
- item.content = Object.assign({}, $scope.revision.content);
+ item.content = Object.assign({}, $scope.content);
modelManager.mapResponseItemsToLocalModels([item], SFModelManager.MappingSourceRemoteActionRetrieved);
}
+
item.setDirty(true);
syncManager.sync();
$scope.dismiss();
}
-
}
-
}
angular.module('app').directive('revisionPreviewModal', () => new RevisionPreviewModal);
diff --git a/app/assets/javascripts/app/directives/views/sessionHistoryMenu.js b/app/assets/javascripts/app/directives/views/sessionHistoryMenu.js
index 64e94bc5a..4522b9abb 100644
--- a/app/assets/javascripts/app/directives/views/sessionHistoryMenu.js
+++ b/app/assets/javascripts/app/directives/views/sessionHistoryMenu.js
@@ -20,7 +20,7 @@ class SessionHistoryMenu {
$scope.reloadHistory();
$scope.openRevision = function(revision) {
- actionsManager.presentRevisionPreviewModal(revision);
+ actionsManager.presentRevisionPreviewModal(revision.item.uuid, revision.item.content);
}
$scope.classForRevision = function(revision) {
diff --git a/app/assets/javascripts/app/models/noteHistoryEntry.js b/app/assets/javascripts/app/models/noteHistoryEntry.js
new file mode 100644
index 000000000..922388b86
--- /dev/null
+++ b/app/assets/javascripts/app/models/noteHistoryEntry.js
@@ -0,0 +1,37 @@
+class NoteHistoryEntry extends SFItemHistoryEntry {
+
+ setPreviousEntry(previousEntry) {
+ super.setPreviousEntry(previousEntry);
+ if(previousEntry) {
+ this.textCharDiffLength = this.item.content.text.length - previousEntry.item.content.text.length;
+ } else {
+ this.textCharDiffLength = this.item.content.text.length;
+ }
+ }
+
+ previewTitle() {
+ return this.item.updated_at.toLocaleString();
+ }
+
+ operationVector() {
+ if(!this.hasPreviousEntry || this.textCharDiffLength == 0) {
+ return 0;
+ } else if(this.textCharDiffLength < 0) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+
+ previewSubTitle() {
+ if(!this.hasPreviousEntry) {
+ return `${this.textCharDiffLength} characters loaded`
+ } else if(this.textCharDiffLength < 0) {
+ return `${this.textCharDiffLength * -1} characters removed`
+ } else if(this.textCharDiffLength > 0) {
+ return `${this.textCharDiffLength} characters added`
+ } else {
+ return "Title changed"
+ }
+ }
+}
diff --git a/app/assets/javascripts/app/services/actionsManager.js b/app/assets/javascripts/app/services/actionsManager.js
index 7790262cf..1953907c3 100644
--- a/app/assets/javascripts/app/services/actionsManager.js
+++ b/app/assets/javascripts/app/services/actionsManager.js
@@ -198,10 +198,11 @@ class ActionsManager {
})
}
- presentRevisionPreviewModal(revision) {
+ presentRevisionPreviewModal(uuid, content) {
var scope = this.$rootScope.$new(true);
- scope.revision = revision;
- var el = this.$compile( "" )(scope);
+ scope.uuid = uuid;
+ scope.content = content;
+ var el = this.$compile( "" )(scope);
angular.element(document.body).append(el);
}
diff --git a/app/assets/javascripts/app/services/sessionHistory.js b/app/assets/javascripts/app/services/sessionHistory.js
index e20114fe9..d64dec37f 100644
--- a/app/assets/javascripts/app/services/sessionHistory.js
+++ b/app/assets/javascripts/app/services/sessionHistory.js
@@ -1,280 +1,24 @@
-const SessionHistoryPersistKey = "sessionHistory_persist";
-const SessionHistoryRevisionsKey = "sessionHistory_revisions";
-const SessionHistoryAutoOptimizeKey = "sessionHistory_autoOptimize";
-
-class SessionHistory {
+class SessionHistory extends SFSessionHistoryManager {
constructor(modelManager, storageManager, authManager, passcodeManager, $timeout) {
- this.modelManager = modelManager;
- this.storageManager = storageManager;
- this.authManager = authManager;
- this.passcodeManager = passcodeManager;
- this.$timeout = $timeout;
- this.loadFromDisk().then(() => {
- this.modelManager.addItemSyncObserver("session-history", "Note", (allItems, validItems, deletedItems, source, sourceKey) => {
- for(let item of allItems) {
- this.addRevision(item);
- }
- });
- })
- }
-
- async encryptionParams() {
- let offline = this.authManager.offline();
- let auth_params = offline ? this.passcodeManager.passcodeAuthParams() : await this.authManager.getAuthParams();
- let keys = offline ? this.passcodeManager.keys() : await this.authManager.keys();
- return {keys, auth_params};
- }
-
- addRevision(item) {
- var added = this.historyContainer.addRevision(item);
-
- if(added) {
- if(this.diskTimeout) {this.$timeout.cancel(this.diskTimeout)};
- this.diskTimeout = this.$timeout(() => {
- this.saveToDisk();
- }, 1000)
- }
- }
-
- historyForItem(item) {
- return this.historyContainer.historyForItem(item);
- }
-
- async clearItemHistory(item) {
- delete this.historyContainer.clearItemHistory(item);
- return this.saveToDisk();
- }
-
- async clearAllHistory() {
- this.historyContainer.clearAllHistory();
- return this.storageManager.removeItem(SessionHistoryRevisionsKey);
- }
-
- async toggleDiskSaving() {
- this.diskEnabled = !this.diskEnabled;
-
- if(this.diskEnabled) {
- this.storageManager.setItem(SessionHistoryPersistKey, JSON.stringify(true));
- this.saveToDisk();
- } else {
- this.storageManager.setItem(SessionHistoryPersistKey, JSON.stringify(false));
- return this.storageManager.removeItem(SessionHistoryRevisionsKey);
- }
- }
-
- get autoOptimize() {
- return this.historyContainer.autoOptimize;
- }
-
- async toggleAutoOptimize() {
- this.historyContainer.autoOptimize = !this.historyContainer.autoOptimize;
-
- if(this.historyContainer.autoOptimize) {
- this.storageManager.setItem(SessionHistoryAutoOptimizeKey, JSON.stringify(true));
- } else {
- this.storageManager.setItem(SessionHistoryAutoOptimizeKey, JSON.stringify(false));
- }
- }
-
- async saveToDisk() {
- if(!this.diskEnabled) {
- return;
- }
- let encryptionParams = await this.encryptionParams();
- var itemParams = new SFItemParams(this.historyContainer, encryptionParams.keys, encryptionParams.auth_params);
- itemParams.paramsForSync().then((syncParams) => {
- // console.log("Saving to disk", syncParams);
- this.storageManager.setItem(SessionHistoryRevisionsKey, JSON.stringify(syncParams));
- })
- }
-
- async loadFromDisk() {
- var diskValue = await this.storageManager.getItem(SessionHistoryPersistKey);
- if(diskValue) {
- this.diskEnabled = JSON.parse(diskValue);
+ SFItemHistory.HistoryEntryClassMapping = {
+ "Note" : NoteHistoryEntry
}
- var historyValue = await this.storageManager.getItem(SessionHistoryRevisionsKey);
- if(historyValue) {
- historyValue = JSON.parse(historyValue);
- let encryptionParams = await this.encryptionParams();
- await SFJS.itemTransformer.decryptItem(historyValue, encryptionParams.keys);
- var historyContainer = new HistoryContainer(historyValue);
- this.historyContainer = historyContainer;
- } else {
- this.historyContainer = new HistoryContainer();
- }
-
- var autoOptimizeValue = await this.storageManager.getItem(SessionHistoryAutoOptimizeKey);
- if(autoOptimizeValue) {
- this.historyContainer.autoOptimize = JSON.parse(autoOptimizeValue);
- } else {
- // default value is true
- this.historyContainer.autoOptimize = true;
- }
- }
-
- async optimize() {
- return this.historyContainer.optimize();
- }
-}
-
-class HistoryContainer extends SFItem {
- constructor(json_obj) {
- super(json_obj);
-
- if(!this.content.itemsDictionary) {
- this.content.itemsDictionary = {};
- }
-
- var objectKeys = Object.keys(this.content.itemsDictionary);
- objectKeys.forEach((key) => {
- var value = this.content.itemsDictionary[key];
- this.content.itemsDictionary[key] = new ItemHistory(value);
- });
- }
-
- addRevision(item) {
- if(!this.content.itemsDictionary[item.uuid]) {
- this.content.itemsDictionary[item.uuid] = new ItemHistory();
- }
- var itemHistory = this.content.itemsDictionary[item.uuid];
- return itemHistory.addRevision(item, this.autoOptimize);
- }
-
- historyForItem(item) {
- return this.content.itemsDictionary[item.uuid];
- }
-
- clearItemHistory(item) {
- delete this.content.itemsDictionary[item.uuid];
- }
-
- clearAllHistory() {
- this.content.itemsDictionary = {};
- }
-
- optimize() {
- var objectKeys = Object.keys(this.content.itemsDictionary);
- objectKeys.forEach((key) => {
- var itemHistory = this.content.itemsDictionary[key];
- itemHistory.optimize();
- });
- }
-}
-
-class ItemHistory {
-
- constructor(json_obj = {}) {
- if(!this.revisions) {
- this.revisions = [];
- }
-
- if(json_obj.revisions) {
- for(var revision of json_obj.revisions) {
- this.revisions.push(new NoteRevision(revision, this.revisions[this.revisions.length - 1], revision.date));
+ var keyRequestHandler = async () => {
+ let offline = authManager.offline();
+ let auth_params = offline ? passcodeManager.passcodeAuthParams() : await authManager.getAuthParams();
+ let keys = offline ? passcodeManager.keys() : await authManager.keys();
+ return {
+ keys: keys,
+ offline: offline,
+ auth_params: auth_params
}
}
- }
- addRevision(item, autoOptimize) {
- var previousRevision = this.revisions[this.revisions.length - 1];
- var prospectiveRevision = new NoteRevision(item, previousRevision, item.updated_at);
-
- // Don't add first revision if text length is 0, as this means it's a new note.
- // Actually, we'll skip this. If we do this, the first character added to a new note
- // will be displayed as "1 characters loaded"
- // if(!previousRevision && prospectiveRevision.textCharDiffLength == 0) {
- // return;
- // }
-
- // Don't add if text is the same
- if(prospectiveRevision.isSameAsRevision(previousRevision)) {
- return;
- }
-
- this.revisions.push(prospectiveRevision);
-
- // Clean up if there are too many revisions
- const LargeRevisionAmount = 100;
- if(autoOptimize && this.revisions.length > LargeRevisionAmount) {
- this.optimize();
- }
-
- return prospectiveRevision;
- }
-
- optimize() {
- const SmallRevisionLength = 15;
- this.revisions = this.revisions.filter((revision, index) => {
- // Keep only first and last item and items whos diff length is greater than the small revision length.
- var isFirst = index == 0;
- var isLast = index == this.revisions.length - 1;
- var isSmallRevision = Math.abs(revision.textCharDiffLength) < SmallRevisionLength;
- return isFirst || isLast || !isSmallRevision;
- })
- }
-
-}
-
-class ItemRevision {
-
- constructor(item, previousRevision, date) {
- if(typeof(date) == "string") {
- this.date = new Date(date);
- } else {
- this.date = date;
- }
- this.uuid = item.uuid;
- this.hasPreviousRevision = previousRevision != null;
- this.content = Object.assign({}, item.content);
- }
-
- isSameAsRevision(revision) {
- if(!revision) {
- return false;
- }
- return JSON.stringify(this.content) === JSON.stringify(revision.content);
- }
-
-}
-
-class NoteRevision extends ItemRevision {
- constructor(item, previousRevision, date) {
- super(item, previousRevision, date);
- if(previousRevision) {
- this.textCharDiffLength = this.content.text.length - previousRevision.content.text.length;
- } else {
- this.textCharDiffLength = this.content.text.length;
- }
- }
-
- previewTitle() {
- return this.date.toLocaleString();
- }
-
- operationVector() {
- if(!this.hasPreviousRevision || this.textCharDiffLength == 0) {
- return 0;
- } else if(this.textCharDiffLength < 0) {
- return -1;
- } else {
- return 1;
- }
- }
-
- previewSubTitle() {
- if(!this.hasPreviousRevision) {
- return `${this.textCharDiffLength} characters loaded`
- } else if(this.textCharDiffLength < 0) {
- return `${this.textCharDiffLength * -1} characters removed`
- } else if(this.textCharDiffLength > 0) {
- return `${this.textCharDiffLength} characters added`
- } else {
- return "Title changed"
- }
+ var contentTypes = ["Note"];
+ super(modelManager, storageManager, keyRequestHandler, contentTypes, $timeout);
}
}
diff --git a/app/assets/templates/directives/revision-preview-modal.html.haml b/app/assets/templates/directives/revision-preview-modal.html.haml
index 1e9b5f107..df37fb182 100644
--- a/app/assets/templates/directives/revision-preview-modal.html.haml
+++ b/app/assets/templates/directives/revision-preview-modal.html.haml
@@ -9,5 +9,5 @@
%a.close-button.info{"ng-click" => "restore(true)"} Restore as copy
%a.close-button.info{"ng-click" => "dismiss(); $event.stopPropagation()"} Close
.content.selectable
- %h2 {{revision.content.title}}
- %p.normal{"style" => "white-space: pre-wrap; font-size: 16px;"} {{revision.content.text}}
+ %h2 {{content.title}}
+ %p.normal{"style" => "white-space: pre-wrap; font-size: 16px;"} {{content.text}}
diff --git a/app/assets/templates/directives/session-history-menu.html.haml b/app/assets/templates/directives/session-history-menu.html.haml
index d4556896c..244ee9ba6 100644
--- a/app/assets/templates/directives/session-history-menu.html.haml
+++ b/app/assets/templates/directives/session-history-menu.html.haml
@@ -2,7 +2,7 @@
.menu-panel.dropdown-menu
.header
.column
- %h4.title {{history.revisions.length || 'No'}} revisions
+ %h4.title {{history.entries.length || 'No'}} revisions
%h4{"ng-click" => "showOptions = !showOptions; $event.stopPropagation();"}
%a Options
@@ -16,7 +16,7 @@
.sublabel
Saving to disk may increase app loading time and memory footprint.
- %menu-row{"ng-repeat" => "revision in history.revisions",
+ %menu-row{"ng-repeat" => "revision in history.entries",
"ng-click" => "openRevision(revision); $event.stopPropagation();",
"label" => "revision.previewTitle()"}
.sublabel.opaque{"ng-class" => "classForRevision(revision)"}
diff --git a/package.json b/package.json
index 0b66c8a4b..e0081141c 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,7 @@
"karma-jasmine": "^1.1.0",
"karma-phantomjs-launcher": "^1.0.2",
"sn-stylekit": "1.0.15",
- "standard-file-js": "0.3.2",
+ "standard-file-js": "file:~/Desktop/sf/sfjs",
"sn-models": "0.1.0",
"connect": "^3.6.6",
"mocha": "^5.2.0",