sync provider wip
This commit is contained in:
@@ -20,7 +20,7 @@ angular.module('app.frontend')
|
||||
* Insert 4 spaces when a tab key is pressed,
|
||||
* only used when inside of the text editor.
|
||||
* If the shift key is pressed first, this event is
|
||||
* not fired.
|
||||
* not fired.
|
||||
*/
|
||||
var handleTab = function (event) {
|
||||
if (!event.shiftKey && event.which == 9) {
|
||||
@@ -146,8 +146,6 @@ angular.module('app.frontend')
|
||||
note.dummy = false;
|
||||
this.save()(note, function(success){
|
||||
if(success) {
|
||||
apiController.clearDraft();
|
||||
|
||||
if(statusTimeout) $timeout.cancel(statusTimeout);
|
||||
statusTimeout = $timeout(function(){
|
||||
this.noteStatus = "All changes saved"
|
||||
@@ -171,10 +169,6 @@ angular.module('app.frontend')
|
||||
this.changesMade = function() {
|
||||
this.note.hasChanges = true;
|
||||
this.note.dummy = false;
|
||||
if(apiController.isUserSignedIn()) {
|
||||
// signed out users have local autosave, dont need draft saving
|
||||
apiController.saveDraftToDisk(this.note);
|
||||
}
|
||||
|
||||
if(saveTimeout) $timeout.cancel(saveTimeout);
|
||||
if(statusTimeout) $timeout.cancel(statusTimeout);
|
||||
@@ -236,7 +230,6 @@ angular.module('app.frontend')
|
||||
}
|
||||
|
||||
this.deleteNote = function() {
|
||||
apiController.clearDraft();
|
||||
this.remove()(this.note);
|
||||
this.showMenu = false;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ angular.module('app.frontend')
|
||||
this.user = apiController.user;
|
||||
|
||||
this.accountMenuPressed = function() {
|
||||
this.serverData = {url: apiController.getServer()};
|
||||
this.serverData = {url: syncManager.serverURL()};
|
||||
this.showAccountMenu = !this.showAccountMenu;
|
||||
this.showFaq = false;
|
||||
this.showNewPasswordForm = false;
|
||||
|
||||
@@ -7,9 +7,9 @@ angular.module('app.frontend')
|
||||
|
||||
syncManager.sync(null);
|
||||
// refresh every 30s
|
||||
setInterval(function () {
|
||||
syncManager.sync(null);
|
||||
}, 30000);
|
||||
// setInterval(function () {
|
||||
// syncManager.sync(null);
|
||||
// }, 30000);
|
||||
});
|
||||
|
||||
$scope.allTag = new Tag({all: true});
|
||||
|
||||
@@ -48,14 +48,8 @@ angular.module('app.frontend')
|
||||
|
||||
if(isFirstLoad) {
|
||||
$timeout(function(){
|
||||
var draft = apiController.getDraft();
|
||||
if(draft) {
|
||||
var note = draft;
|
||||
this.selectNote(note);
|
||||
} else {
|
||||
this.createNewNote();
|
||||
isFirstLoad = false;
|
||||
}
|
||||
this.createNewNote();
|
||||
isFirstLoad = false;
|
||||
}.bind(this))
|
||||
} else if(tag.notes.length == 0) {
|
||||
this.createNewNote();
|
||||
|
||||
@@ -7,7 +7,7 @@ class ItemParams {
|
||||
}
|
||||
|
||||
paramsForExportFile() {
|
||||
this.additionalFields = ["created_at", "updated_at"];
|
||||
this.additionalFields = ["updated_at"];
|
||||
this.forExportFile = true;
|
||||
return _.omit(this.__params(), ["deleted"]);
|
||||
}
|
||||
@@ -16,16 +16,22 @@ class ItemParams {
|
||||
return this.paramsForExportFile();
|
||||
}
|
||||
|
||||
paramsForLocalStorage() {
|
||||
this.additionalFields = ["updated_at", "dirty"];
|
||||
this.forExportFile = true;
|
||||
return this.__params();
|
||||
}
|
||||
|
||||
paramsForSync() {
|
||||
return __params(null, false);
|
||||
return this.__params(null, false);
|
||||
}
|
||||
|
||||
__params() {
|
||||
var itemCopy = _.cloneDeep(this.item);
|
||||
|
||||
console.assert(!item.dummy, "Item is dummy, should not have gotten here.", item.dummy)
|
||||
console.assert(!this.item.dummy, "Item is dummy, should not have gotten here.", this.item.dummy)
|
||||
|
||||
var params = {uuid: item.uuid, content_type: item.content_type, deleted: item.deleted};
|
||||
var params = {uuid: this.item.uuid, content_type: this.item.content_type, deleted: this.item.deleted, created_at: this.item.created_at};
|
||||
|
||||
if(this.ek) {
|
||||
this.encryptionHelper.encryptItem(itemCopy, this.ek);
|
||||
@@ -42,7 +48,7 @@ class ItemParams {
|
||||
}
|
||||
|
||||
if(this.additionalFields) {
|
||||
_.merge(params, _.pick(item, this.additionalFields));
|
||||
_.merge(params, _.pick(this.item, this.additionalFields));
|
||||
}
|
||||
|
||||
return params;
|
||||
|
||||
@@ -28,18 +28,10 @@ angular.module('app.frontend')
|
||||
Auth
|
||||
*/
|
||||
|
||||
this.defaultServerURL = function() {
|
||||
return localStorage.getItem("server") || "https://n3.standardnotes.org";
|
||||
}
|
||||
|
||||
this.getAuthParams = function() {
|
||||
return JSON.parse(localStorage.getItem("auth_params"));
|
||||
}
|
||||
|
||||
this.isUserSignedIn = function() {
|
||||
return localStorage.getItem("jwt");
|
||||
}
|
||||
|
||||
this.getAuthParamsForEmail = function(url, email, callback) {
|
||||
var requestUrl = url + "/auth/params";
|
||||
var request = Restangular.oneUrl(requestUrl, requestUrl);
|
||||
@@ -103,7 +95,7 @@ angular.module('app.frontend')
|
||||
localStorage.setItem("jwt", response.token);
|
||||
localStorage.setItem("user", JSON.stringify(response.user));
|
||||
localStorage.setItem("auth_params", JSON.stringify(_.omit(authParams, ["pw_nonce"])));
|
||||
syncManager.addKey(syncManager.SNKeyName, mk);
|
||||
keyManager.addKey(SNKeyName, mk);
|
||||
syncManager.addStandardFileSyncProvider(url);
|
||||
}
|
||||
|
||||
@@ -119,6 +111,7 @@ angular.module('app.frontend')
|
||||
callback(response);
|
||||
}.bind(this))
|
||||
.catch(function(response){
|
||||
console.log("Registration error", response);
|
||||
callback(response.data);
|
||||
})
|
||||
}.bind(this));
|
||||
@@ -251,7 +244,7 @@ angular.module('app.frontend')
|
||||
items: items
|
||||
}
|
||||
|
||||
if(ek.name == syncManager.SNKeyName) {
|
||||
if(ek.name == SNKeyName) {
|
||||
// auth params are only needed when encrypted with a standard file key
|
||||
data["auth_params"] = this.getAuthParams();
|
||||
}
|
||||
@@ -263,34 +256,27 @@ angular.module('app.frontend')
|
||||
return JSON.parse(JSON.stringify(object));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Drafts
|
||||
*/
|
||||
|
||||
this.saveDraftToDisk = function(draft) {
|
||||
localStorage.setItem("draft", JSON.stringify(draft));
|
||||
}
|
||||
|
||||
this.clearDraft = function() {
|
||||
localStorage.removeItem("draft");
|
||||
}
|
||||
|
||||
this.getDraft = function() {
|
||||
var draftString = localStorage.getItem("draft");
|
||||
if(!draftString || draftString == 'undefined') {
|
||||
return null;
|
||||
}
|
||||
var jsonObj = _.merge({content_type: "Note"}, JSON.parse(draftString));
|
||||
return modelManager.createItem(jsonObj);
|
||||
}
|
||||
|
||||
this.signoutOfStandardFile = function(callback) {
|
||||
this.signoutOfStandardFile = function(destroyAll, callback) {
|
||||
syncManager.removeStandardFileSyncProvider();
|
||||
if(destroyAll) {
|
||||
this.destroyLocalData(callback);
|
||||
} else {
|
||||
localStorage.removeItem("user");
|
||||
localStorage.removeItem("jwt");
|
||||
localStorage.removeItem("server");
|
||||
localStorage.removeItem("auth_params");
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
this.destroyLocalData = function(callback) {
|
||||
dbManager.clearAllItems(function(){
|
||||
localStorage.clear();
|
||||
callback();
|
||||
if(callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -10,6 +10,15 @@ class AccountDataMenu {
|
||||
controller($scope, apiController, modelManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.destroyLocalData = function() {
|
||||
if(!confirm("Are you sure you want to end your session? This will delete all local items, sync providers, keys, and extensions.")) {
|
||||
return;
|
||||
}
|
||||
|
||||
apiController.destroyLocalData(function(){
|
||||
window.location.reload();
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +31,22 @@ class AccountSyncSection {
|
||||
}
|
||||
|
||||
$scope.removeSyncProvider = function(provider) {
|
||||
syncManager.removeSyncProvider(provider);
|
||||
if(provider.isStandardNotesAccount) {
|
||||
alert("To remove your Standard Notes sync, sign out of your Standard Notes account.")
|
||||
return;
|
||||
}
|
||||
|
||||
if(confirm("Are you sure you want to remove this sync provider?")) {
|
||||
syncManager.removeSyncProvider(provider);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.changeEncryptionKey = function(provider) {
|
||||
if(provider.isStandardNotesAccount) {
|
||||
alert("To change your encryption key for your Standard Notes account, you need to change your password. However, this functionality is not currently supported.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!confirm("Changing your encryption key will re-encrypt all your notes with the new key and sync them back to the server. This can take several minutes. We strongly recommend downloading a backup of your notes before continuing.")) {
|
||||
return;
|
||||
}
|
||||
@@ -46,6 +58,7 @@ class AccountSyncSection {
|
||||
$scope.saveKey = function(provider) {
|
||||
provider.showKeyForm = false;
|
||||
provider.keyName = provider.formData.keyName;
|
||||
syncManager.didMakeChangesToSyncProviders();
|
||||
|
||||
if(provider.enabled) {
|
||||
syncManager.addAllDataAsNeedingSyncForProvider(provider);
|
||||
|
||||
@@ -7,10 +7,10 @@ class AccountVendorAccountSection {
|
||||
};
|
||||
}
|
||||
|
||||
controller($scope, apiController, modelManager, $timeout, dbManager) {
|
||||
controller($scope, apiController, modelManager, $timeout, dbManager, syncManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.loginData = {mergeLocal: true, url: apiController.defaultServerURL()};
|
||||
$scope.loginData = {mergeLocal: true, url: syncManager.serverURL()};
|
||||
$scope.user = apiController.user;
|
||||
|
||||
$scope.changePasswordPressed = function() {
|
||||
@@ -19,7 +19,7 @@ class AccountVendorAccountSection {
|
||||
|
||||
$scope.signOutPressed = function() {
|
||||
$scope.showAccountMenu = false;
|
||||
apiController.signoutOfStandardFile(function(){
|
||||
apiController.signoutOfStandardFile(false, function(){
|
||||
window.location.reload();
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class ImportExportMenu {
|
||||
$scope.user = apiController.user;
|
||||
|
||||
$scope.downloadDataArchive = function() {
|
||||
if(!apiController.isUserSignedIn() && $scope.archiveFormData.encryption_type == 'ek') {
|
||||
if($scope.archiveFormData.encryption_type == 'ek') {
|
||||
if(!$scope.archiveFormData.ek) {
|
||||
alert("You must set an encryption key to export the data encrypted.")
|
||||
return;
|
||||
|
||||
@@ -1,26 +1,38 @@
|
||||
export const SNKeyName = "Standard Notes Key";
|
||||
|
||||
class SyncManager {
|
||||
|
||||
constructor(modelManager) {
|
||||
constructor(modelManager, syncRunner) {
|
||||
this.modelManager = modelManager;
|
||||
this.syncPerformer = new SyncPerformer()
|
||||
this.SNKeyName = "Standard Notes Key";
|
||||
this.syncRunner = syncRunner;
|
||||
this.syncRunner.setOnChangeProviderCallback(function(){
|
||||
this.didMakeChangesToSyncProviders();
|
||||
}.bind(this))
|
||||
this.loadSyncProviders();
|
||||
}
|
||||
|
||||
get offline() {
|
||||
return this.syncProviders.length == 0;
|
||||
return this.enabledProviders.length == 0;
|
||||
}
|
||||
|
||||
serverURL() {
|
||||
return localStorage.getItem("server") || "https://n3.standardnotes.org";
|
||||
}
|
||||
|
||||
get enabledProviders() {
|
||||
return this.syncProviders.filter(function(provider){return provider.enabled == true});
|
||||
}
|
||||
|
||||
sync(callback) {
|
||||
this.syncPerformer.sync(this.syncProviders, callback);
|
||||
this.syncRunner.sync(this.enabledProviders, callback);
|
||||
}
|
||||
|
||||
syncWithProvider(provider, callback) {
|
||||
this.syncPerformer.performSyncWithProvider(provider, callback);
|
||||
this.syncRunner.performSyncWithProvider(provider, callback);
|
||||
}
|
||||
|
||||
loadLocalItems(callback) {
|
||||
this.syncPerformer.loadLocalItems(callback);
|
||||
this.syncRunner.loadLocalItems(callback);
|
||||
}
|
||||
|
||||
syncProviderForURL(url) {
|
||||
@@ -46,16 +58,17 @@ class SyncManager {
|
||||
}
|
||||
|
||||
removeStandardFileSyncProvider() {
|
||||
var sfProvider = _.find(this.syncProviders, {url: this.defaultServerURL() + "/items/sync"})
|
||||
var sfProvider = _.find(this.syncProviders, {url: this.serverURL() + "/items/sync"})
|
||||
_.pull(this.syncProviders, sfProvider);
|
||||
this.didMakeChangesToSyncProviders();
|
||||
}
|
||||
|
||||
addStandardFileSyncProvider(url) {
|
||||
var defaultProvider = new SyncProvider({url: url + "/items/sync", primary: this.syncProviders.length == 0});
|
||||
defaultProvider.keyName = this.SNKeyName;
|
||||
var defaultProvider = new SyncProvider({url: url + "/items/sync", primary: this.enabledProviders.length == 0});
|
||||
defaultProvider.keyName = SNKeyName;
|
||||
defaultProvider.enabled = this.syncProviders.length == 0;
|
||||
this.syncProviders.push(defaultProvider);
|
||||
this.didMakeChangesToSyncProviders();
|
||||
return defaultProvider;
|
||||
}
|
||||
|
||||
@@ -74,14 +87,15 @@ class SyncManager {
|
||||
this.syncProviders.push(new SyncProvider(p));
|
||||
}
|
||||
} else {
|
||||
// no providers saved, use default
|
||||
if(this.isUserSignedIn()) {
|
||||
var defaultProvider = this.addStandardFileSyncProvider(this.defaultServerURL());
|
||||
// no providers saved, this means migrating from old system to new
|
||||
// check if user is signed in
|
||||
if(this.offline && localStorage.getItem("user")) {
|
||||
var defaultProvider = this.addStandardFileSyncProvider(this.serverURL());
|
||||
defaultProvider.syncToken = localStorage.getItem("syncToken");
|
||||
// migrate old key structure to new
|
||||
var mk = localStorage.getItem("mk");
|
||||
if(mk) {
|
||||
keyManager.addKey(this.SNKeyName, mk);
|
||||
keyManager.addKey(SNKeyName, mk);
|
||||
localStorage.removeItem("mk");
|
||||
}
|
||||
this.didMakeChangesToSyncProviders();
|
||||
@@ -129,43 +143,3 @@ class SyncManager {
|
||||
}
|
||||
|
||||
angular.module('app.frontend').service('syncManager', SyncManager);
|
||||
|
||||
class SyncProvider {
|
||||
|
||||
constructor(obj) {
|
||||
this.encrypted = true;
|
||||
_.merge(this, obj);
|
||||
}
|
||||
|
||||
addPendingItems(items) {
|
||||
if(!this.pendingItems) {
|
||||
this.pendingItems = [];
|
||||
}
|
||||
|
||||
this.pendingItems = this.pendingItems.concat(items);
|
||||
}
|
||||
|
||||
removePendingItems(items) {
|
||||
this.pendingItems = _.difference(this.pendingItems, items);
|
||||
}
|
||||
|
||||
get status() {
|
||||
if(!this.enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(this.primary) return "primary";
|
||||
else return "secondary";
|
||||
}
|
||||
|
||||
asJSON() {
|
||||
return {
|
||||
enabled: this.enabled,
|
||||
url: this.url,
|
||||
primary: this.primary,
|
||||
keyName: this.keyName,
|
||||
syncToken: this.syncToken
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
43
app/assets/javascripts/app/services/sync/syncProvider.js
Normal file
43
app/assets/javascripts/app/services/sync/syncProvider.js
Normal file
@@ -0,0 +1,43 @@
|
||||
class SyncProvider {
|
||||
|
||||
constructor(obj) {
|
||||
this.encrypted = true;
|
||||
_.merge(this, obj);
|
||||
}
|
||||
|
||||
addPendingItems(items) {
|
||||
if(!this.pendingItems) {
|
||||
this.pendingItems = [];
|
||||
}
|
||||
|
||||
this.pendingItems = this.pendingItems.concat(items);
|
||||
}
|
||||
|
||||
removePendingItems(items) {
|
||||
this.pendingItems = _.difference(this.pendingItems, items);
|
||||
}
|
||||
|
||||
get isStandardNotesAccount() {
|
||||
return this.keyName == SNKeyName;
|
||||
}
|
||||
|
||||
get status() {
|
||||
if(!this.enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(this.primary) return "primary";
|
||||
else return "secondary";
|
||||
}
|
||||
|
||||
asJSON() {
|
||||
return {
|
||||
enabled: this.enabled,
|
||||
url: this.url,
|
||||
primary: this.primary,
|
||||
keyName: this.keyName,
|
||||
syncToken: this.syncToken
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
class SyncPerformer {
|
||||
class SyncRunner {
|
||||
|
||||
constructor(modelManager, dbManager, encryptionHelper, keyManager) {
|
||||
constructor($rootScope, modelManager, dbManager, encryptionHelper, keyManager, Restangular) {
|
||||
this.rootScope = $rootScope;
|
||||
this.modelManager = modelManager;
|
||||
this.dbManager = dbManager;
|
||||
this.encryptionHelper = encryptionHelper;
|
||||
this.keyManager = keyManager;
|
||||
this.Restangular = Restangular;
|
||||
}
|
||||
|
||||
setOnChangeProviderCallback(callback) {
|
||||
@@ -15,9 +17,14 @@ class SyncPerformer {
|
||||
this.onChangeProviderCallback(provider);
|
||||
}
|
||||
|
||||
writeItemsToLocalStorage(items, callback) {
|
||||
writeItemsToLocalStorage(items, offlineOnly, callback) {
|
||||
var params = items.map(function(item) {
|
||||
return this.paramsForItem(item, false, ["created_at", "updated_at", "dirty"], true)
|
||||
var itemParams = new ItemParams(item, null);
|
||||
itemParams = itemParams.paramsForLocalStorage();
|
||||
if(offlineOnly) {
|
||||
delete itemParams.dirty;
|
||||
}
|
||||
return itemParams;
|
||||
}.bind(this));
|
||||
|
||||
this.dbManager.saveItems(params, callback);
|
||||
@@ -32,7 +39,8 @@ class SyncPerformer {
|
||||
}
|
||||
|
||||
syncOffline(items, callback) {
|
||||
this.writeItemsToLocalStorage(items, function(responseItems){
|
||||
console.log("Writing items offline", items);
|
||||
this.writeItemsToLocalStorage(items, true, function(responseItems){
|
||||
// delete anything needing to be deleted
|
||||
for(var item of items) {
|
||||
if(item.deleted) {
|
||||
@@ -57,10 +65,6 @@ class SyncPerformer {
|
||||
}
|
||||
|
||||
for(let provider of providers) {
|
||||
if(!provider.enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
provider.addPendingItems(allDirtyItems);
|
||||
this.didMakeChangesToSyncProvider(provider);
|
||||
|
||||
@@ -100,13 +104,13 @@ class SyncPerformer {
|
||||
provider.repeatOnCompletion = false;
|
||||
}
|
||||
|
||||
console.log("Syncing with provider", provider, subItems);
|
||||
console.log("Syncing with provider:", provider.url, "items:", subItems.length);
|
||||
|
||||
// Remove dirty items now. If this operation fails, we'll re-add them.
|
||||
// This allows us to queue changes on the same item
|
||||
provider.removePendingItems(subItems);
|
||||
|
||||
var request = Restangular.oneUrl(provider.url, provider.url);
|
||||
var request = this.Restangular.oneUrl(provider.url, provider.url);
|
||||
request.limit = 150;
|
||||
request.items = _.map(subItems, function(item){
|
||||
var itemParams = new ItemParams(item, provider.ek);
|
||||
@@ -119,12 +123,12 @@ class SyncPerformer {
|
||||
|
||||
request.post().then(function(response) {
|
||||
|
||||
console.log("Sync completion", response);
|
||||
console.log("Completed sync for provider:", provider.url, "Response:", response);
|
||||
|
||||
provider.syncToken = response.sync_token;
|
||||
|
||||
if(provider.primary) {
|
||||
$rootScope.$broadcast("sync:updated_token", provider.syncToken);
|
||||
this.rootScope.$broadcast("sync:updated_token", provider.syncToken);
|
||||
|
||||
// handle cursor token (more results waiting, perform another sync)
|
||||
provider.cursorToken = response.cursor_token;
|
||||
@@ -136,8 +140,8 @@ class SyncPerformer {
|
||||
|
||||
this.handleUnsavedItemsResponse(response.unsaved, provider)
|
||||
|
||||
this.writeItemsToLocalStorage(saved, null);
|
||||
this.writeItemsToLocalStorage(retrieved, null);
|
||||
this.writeItemsToLocalStorage(saved, false, null);
|
||||
this.writeItemsToLocalStorage(retrieved, false, null);
|
||||
}
|
||||
|
||||
provider.syncOpInProgress = false;
|
||||
@@ -159,7 +163,7 @@ class SyncPerformer {
|
||||
provider.syncOpInProgress = false;
|
||||
|
||||
if(provider.primary) {
|
||||
this.writeItemsToLocalStorage(allItems, null);
|
||||
this.writeItemsToLocalStorage(allItems, false, null);
|
||||
}
|
||||
|
||||
if(callback) {
|
||||
@@ -194,3 +198,5 @@ class SyncPerformer {
|
||||
return this.modelManager.mapResponseItemsToLocalModelsOmittingFields(responseItems, omitFields);
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('app.frontend').service('syncRunner', SyncRunner);
|
||||
@@ -16,3 +16,5 @@
|
||||
%section.account-item
|
||||
%h3{"ng-click" => "showIO = !showIO"} Import/Export
|
||||
%import-export-menu{"ng-if" => "showIO"}
|
||||
|
||||
%a{"ng-click" => "destroyLocalData()"} Destroy all local data
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
.providers
|
||||
.provider{"ng-repeat" => "provider in syncProviders"}
|
||||
.type {{provider.primary == null ? 'Not enabled' : (provider.primary ? 'Primary' : 'Secondary')}}
|
||||
.type {{!provider.enabled ? 'Not enabled' : (provider.primary ? 'Primary' : 'Secondary')}}
|
||||
.key{"ng-if" => "provider.keyName"} Using key: {{provider.keyName}}
|
||||
.url {{provider.url}}
|
||||
.options
|
||||
%div{"ng-if" => "!provider.enabled"}
|
||||
%div{"ng-if" => "!provider.keyName || provider.showKeyForm"}
|
||||
%div{"ng-if" => "!provider.keyName || provider.showKeyForm"}
|
||||
%p
|
||||
%strong Choose encryption key:
|
||||
%select{"ng-model" => "provider.formData.keyName"}
|
||||
%option{"ng-repeat" => "key in keys", "ng-selected" => "{{key.name == provider.formData.keyName}}", "value" => "{{key.name}}"}
|
||||
{{key.name}}
|
||||
%button{"ng-click" => "saveKey(provider)"} Set
|
||||
%select{"ng-model" => "provider.formData.keyName"}
|
||||
%option{"ng-repeat" => "key in keys", "ng-selected" => "{{key.name == provider.formData.keyName}}", "value" => "{{key.name}}"}
|
||||
{{key.name}}
|
||||
%button{"ng-click" => "saveKey(provider)"} Set
|
||||
|
||||
%div{"ng-if" => "!provider.enabled"}
|
||||
%button.light{"ng-click" => "enableSyncProvider(provider, true)"} Enable as Primary sync provider
|
||||
%button.light{"ng-click" => "enableSyncProvider(provider, false)"} Enable as Secondary sync provider
|
||||
%button.light{"ng-if" => "syncProviders.length > 1", "ng-click" => "enableSyncProvider(provider, false)"} Enable as Secondary sync provider
|
||||
%button.light{"ng-if" => "provider.keyName", "ng-click" => "changeEncryptionKey(provider)"} Change Encryption Key
|
||||
%button.light{"ng-click" => "removeSyncProvider(provider)"} Remove Provider
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
.server {{serverURL}}
|
||||
.links{"ng-if" => "user"}
|
||||
.link-item
|
||||
%a{"ng-click" => "signOutPressed()"} Sign Out
|
||||
%a{"ng-click" => "signOutPressed()"} Sign out
|
||||
%p Note: Signing out does not delete your local items, extensions, and keys.
|
||||
.meta-container
|
||||
.title Local Encryption
|
||||
.desc Notes are encrypted locally before being sent to the server. Neither the server owner nor an intrusive entity can decrypt your locally encrypted notes.
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
.items
|
||||
.item.account
|
||||
%a{"ng-click" => "ctrl.accountMenuPressed()"} Data
|
||||
%account-data-menu
|
||||
-# {"ng-if" => "ctrl.showAccountMenu"}
|
||||
%account-data-menu{"ng-if" => "ctrl.showAccountMenu"}
|
||||
|
||||
.item
|
||||
%a{"ng-click" => "ctrl.toggleExtensions()"} Extensions
|
||||
|
||||
Reference in New Issue
Block a user