From 8eaa22eae39a256b25719c071a77e1639bb232a1 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Fri, 20 Jul 2018 09:30:57 -0500 Subject: [PATCH] Auto cleanup session history --- .../directives/views/sessionHistoryMenu.js | 9 ++ .../app/services/sessionHistory.js | 86 +++++++++++++++++-- .../directives/session-history-menu.html.haml | 5 +- 3 files changed, 90 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/app/directives/views/sessionHistoryMenu.js b/app/assets/javascripts/app/directives/views/sessionHistoryMenu.js index 859c32680..64e94bc5a 100644 --- a/app/assets/javascripts/app/directives/views/sessionHistoryMenu.js +++ b/app/assets/javascripts/app/directives/views/sessionHistoryMenu.js @@ -12,6 +12,7 @@ class SessionHistoryMenu { 'ngInject'; $scope.diskEnabled = sessionHistory.diskEnabled; + $scope.autoOptimize = sessionHistory.autoOptimize; $scope.reloadHistory = function() { $scope.history = sessionHistory.historyForItem($scope.item); @@ -65,6 +66,14 @@ class SessionHistoryMenu { }); } + $scope.toggleAutoOptimize = function() { + sessionHistory.toggleAutoOptimize().then(() => { + $timeout(() => { + $scope.autoOptimize = sessionHistory.autoOptimize; + }) + }); + } + } } diff --git a/app/assets/javascripts/app/services/sessionHistory.js b/app/assets/javascripts/app/services/sessionHistory.js index dd45f0c04..867c1ebb7 100644 --- a/app/assets/javascripts/app/services/sessionHistory.js +++ b/app/assets/javascripts/app/services/sessionHistory.js @@ -1,3 +1,7 @@ +const SessionHistoryPersistKey = "sessionHistory_persist"; +const SessionHistoryRevisionsKey = "sessionHistory_revisions"; +const SessionHistoryAutoOptimizeKey = "sessionHistory_autoOptimize"; + class SessionHistory { constructor(modelManager, storageManager, authManager, passcodeManager, $timeout) { @@ -45,17 +49,32 @@ class SessionHistory { async clearAllHistory() { this.historyContainer.clearAllHistory(); - return this.storageManager.removeItem("sessionHistory"); + return this.storageManager.removeItem(SessionHistoryRevisionsKey); } async toggleDiskSaving() { this.diskEnabled = !this.diskEnabled; if(this.diskEnabled) { - this.storageManager.setItem("persistSessionHistory", JSON.stringify(true)); + this.storageManager.setItem(SessionHistoryPersistKey, JSON.stringify(true)); this.saveToDisk(); } else { - this.storageManager.removeItem("persistSessionHistory"); + 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)); } } @@ -66,18 +85,18 @@ class SessionHistory { 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("sessionHistory", JSON.stringify(syncParams)); + // console.log("Saving to disk", syncParams); + this.storageManager.setItem(SessionHistoryRevisionsKey, JSON.stringify(syncParams)); }) } async loadFromDisk() { - var diskValue = await this.storageManager.getItem("persistSessionHistory"); + var diskValue = await this.storageManager.getItem(SessionHistoryPersistKey); if(diskValue) { this.diskEnabled = JSON.parse(diskValue); } - var historyValue = await this.storageManager.getItem("sessionHistory"); + var historyValue = await this.storageManager.getItem(SessionHistoryRevisionsKey); if(historyValue) { historyValue = JSON.parse(historyValue); let encryptionParams = await this.encryptionParams(); @@ -87,6 +106,18 @@ class SessionHistory { } 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(); } } @@ -110,7 +141,7 @@ class HistoryContainer extends SFItem { this.content.itemsDictionary[item.uuid] = new ItemHistory(); } var itemHistory = this.content.itemsDictionary[item.uuid]; - return itemHistory.addRevision(item); + return itemHistory.addRevision(item, this.autoOptimize); } historyForItem(item) { @@ -124,6 +155,14 @@ class HistoryContainer extends SFItem { 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 { @@ -132,6 +171,7 @@ class ItemHistory { 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)); @@ -139,16 +179,44 @@ class ItemHistory { } } - addRevision(item) { + 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 { diff --git a/app/assets/templates/directives/session-history-menu.html.haml b/app/assets/templates/directives/session-history-menu.html.haml index 626a5cc0a..09cfa4e90 100644 --- a/app/assets/templates/directives/session-history-menu.html.haml +++ b/app/assets/templates/directives/session-history-menu.html.haml @@ -9,9 +9,12 @@ %div{"ng-if" => "showOptions"} %menu-row{"label" => "'Clear note local history'", "ng-click" => "clearItemHistory(); $event.stopPropagation();"} %menu-row{"label" => "'Clear all local history'", "ng-click" => "clearAllHistory(); $event.stopPropagation();"} + %menu-row{"label" => "(autoOptimize ? 'Disable' : 'Enable') + ' auto cleanup'", "ng-click" => "toggleAutoOptimize(); $event.stopPropagation();"} + .sublabel + Automatically cleans up small revisions to conserve space. %menu-row{"label" => "(diskEnabled ? 'Disable' : 'Enable') + ' saving history to disk'", "ng-click" => "toggleDiskSaving(); $event.stopPropagation();"} .sublabel - Saving to disk may decrease app loading speed and increase memory footprint. + Saving to disk may increase app loading time and memory footprint. %menu-row{"ng-repeat" => "revision in history.revisions", "ng-click" => "openRevision(revision); $event.stopPropagation();",