Desktop manager to allow local backups
This commit is contained in:
@@ -55,6 +55,7 @@ angular.module('app.frontend')
|
||||
themeManager.activateInitialTheme();
|
||||
$scope.$apply();
|
||||
|
||||
$rootScope.$broadcast("initial-data-loaded");
|
||||
|
||||
syncManager.sync(null);
|
||||
// refresh every 30s
|
||||
|
||||
47
app/assets/javascripts/app/services/desktopManager.js
Normal file
47
app/assets/javascripts/app/services/desktopManager.js
Normal file
@@ -0,0 +1,47 @@
|
||||
// An interface used by the Desktop app to interact with SN
|
||||
|
||||
class DesktopManager {
|
||||
|
||||
constructor($rootScope, modelManager, authManager) {
|
||||
this.modelManager = modelManager;
|
||||
this.authManager = authManager;
|
||||
this.$rootScope = $rootScope;
|
||||
|
||||
$rootScope.$on("initial-data-loaded", () => {
|
||||
this.dataLoaded = true;
|
||||
if(this.dataLoadHandler) {
|
||||
this.dataLoadHandler();
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on("major-data-change", () => {
|
||||
if(this.majorDataChangeHandler) {
|
||||
this.majorDataChangeHandler();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
desktop_setInitialDataLoadHandler(handler) {
|
||||
this.dataLoadHandler = handler;
|
||||
if(this.dataLoaded) {
|
||||
this.dataLoadHandler();
|
||||
}
|
||||
}
|
||||
|
||||
desktop_requestBackupFile() {
|
||||
let data = this.modelManager.getAllItemsJSONData(
|
||||
this.authManager.keys(),
|
||||
this.authManager.getAuthParams(),
|
||||
this.authManager.protocolVersion(),
|
||||
true /* return null on empty */
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
desktop_setMajorDataChangeHandler(handler) {
|
||||
this.majorDataChangeHandler = handler;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app.frontend').service('desktopManager', DesktopManager);
|
||||
@@ -373,20 +373,9 @@ class AccountMenu {
|
||||
}
|
||||
|
||||
$scope.itemsData = function(keys) {
|
||||
var items = _.map(modelManager.allItems, function(item){
|
||||
var itemParams = new ItemParams(item, keys, authManager.protocolVersion());
|
||||
return itemParams.paramsForExportFile();
|
||||
}.bind(this));
|
||||
|
||||
var data = {items: items}
|
||||
|
||||
if(keys) {
|
||||
// auth params are only needed when encrypted with a standard file key
|
||||
data["auth_params"] = authManager.getAuthParams();
|
||||
}
|
||||
|
||||
var data = new Blob([JSON.stringify(data, null, 2 /* pretty print */)], {type: 'text/json'});
|
||||
return data;
|
||||
let data = modelManager.getAllItemsJSONData(keys, authManager.getAuthParams(), authManager.protocolVersion());
|
||||
let blobData = new Blob([data], {type: 'text/json'});
|
||||
return blobData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -122,6 +122,8 @@ class EncryptionHelper {
|
||||
var content = Neeto.crypto.decryptText(itemParams, true);
|
||||
if(!content) {
|
||||
item.errorDecrypting = true;
|
||||
} else {
|
||||
item.errorDecrypting = false;
|
||||
}
|
||||
item.content = content;
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ class ModelManager {
|
||||
|
||||
// first loop should add and process items
|
||||
for (var json_obj of items) {
|
||||
if((!json_obj.content_type || !json_obj.content) && !json_obj.deleted) {
|
||||
if((!json_obj.content_type || !json_obj.content) && !json_obj.deleted && !json_obj.errorDecrypting) {
|
||||
// An item that is not deleted should never have empty content
|
||||
console.error("Server response item is corrupt:", json_obj);
|
||||
continue;
|
||||
@@ -363,6 +363,31 @@ class ModelManager {
|
||||
itemOne.setDirty(true);
|
||||
itemTwo.setDirty(true);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Archives
|
||||
*/
|
||||
|
||||
getAllItemsJSONData(keys, authParams, protocolVersion, returnNullIfEmpty) {
|
||||
var items = _.map(this.allItems, (item) => {
|
||||
var itemParams = new ItemParams(item, keys, protocolVersion);
|
||||
return itemParams.paramsForExportFile();
|
||||
});
|
||||
|
||||
if(returnNullIfEmpty && items.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var data = {items: items}
|
||||
|
||||
if(keys) {
|
||||
// auth params are only needed when encrypted with a standard file key
|
||||
data["auth_params"] = authParams;
|
||||
}
|
||||
|
||||
return JSON.stringify(data, null, 2 /* pretty print */);
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app.frontend').service('modelManager', ModelManager);
|
||||
|
||||
@@ -282,7 +282,8 @@ class SyncManager {
|
||||
this.handleItemsResponse(response.saved_items, omitFields);
|
||||
|
||||
// Create copies of items or alternate their uuids if neccessary
|
||||
this.handleUnsavedItemsResponse(response.unsaved)
|
||||
var unsaved = response.unsaved;
|
||||
this.handleUnsavedItemsResponse(unsaved)
|
||||
|
||||
this.writeItemsToLocalStorage(saved, false, null);
|
||||
|
||||
@@ -306,6 +307,18 @@ class SyncManager {
|
||||
}.bind(this), 10); // wait 10ms to allow UI to update
|
||||
} else {
|
||||
this.writeItemsToLocalStorage(this.allRetreivedItems, false, null);
|
||||
|
||||
// The number of changed items that constitute a major change
|
||||
// This is used by the desktop app to create backups
|
||||
let majorDataChangeThreshold = 5;
|
||||
if(
|
||||
this.allRetreivedItems.length >= majorDataChangeThreshold ||
|
||||
saved.length >= majorDataChangeThreshold ||
|
||||
unsaved.length >= majorDataChangeThreshold
|
||||
) {
|
||||
this.$rootScope.$broadcast("major-data-change");
|
||||
}
|
||||
|
||||
this.allRetreivedItems = [];
|
||||
|
||||
this.callQueuedCallbacksAndCurrent(callback, response);
|
||||
|
||||
Reference in New Issue
Block a user