diff --git a/app/assets/javascripts/app/frontend/controllers/header.js b/app/assets/javascripts/app/frontend/controllers/header.js
index c0e18fd99..624d3b391 100644
--- a/app/assets/javascripts/app/frontend/controllers/header.js
+++ b/app/assets/javascripts/app/frontend/controllers/header.js
@@ -32,11 +32,10 @@ angular.module('app.frontend')
this.updateOfflineStatus();
this.findErrors = function() {
- this.error = syncManager.error;
+ this.error = syncManager.syncStatus.error;
}
this.findErrors();
-
this.accountMenuPressed = function() {
this.serverData = {};
this.showAccountMenu = !this.showAccountMenu;
diff --git a/app/assets/javascripts/app/services/authManager.js b/app/assets/javascripts/app/services/authManager.js
index 8ccdbca6c..d2b0ea24c 100644
--- a/app/assets/javascripts/app/services/authManager.js
+++ b/app/assets/javascripts/app/services/authManager.js
@@ -92,7 +92,7 @@ angular.module('app.frontend')
this.handleAuthResponse = function(response, email, url, authParams, mk) {
localStorage.setItem("server", url);
- localStorage.setItem("user", JSON.stringify(response.plain()));
+ localStorage.setItem("user", JSON.stringify(response.plain().user));
localStorage.setItem("auth_params", JSON.stringify(_.omit(authParams, ["pw_nonce"])));
localStorage.setItem("mk", mk);
localStorage.setItem("jwt", response.token);
@@ -243,10 +243,8 @@ angular.module('app.frontend')
items: items
}
- if(ek.name == SNKeyName) {
- // auth params are only needed when encrypted with a standard file key
- data["auth_params"] = this.getAuthParams();
- }
+ // auth params are only needed when encrypted with a standard file key
+ data["auth_params"] = this.getAuthParams();
return makeTextFile(JSON.stringify(data, null, 2 /* pretty print */));
}
diff --git a/app/assets/javascripts/app/services/directives/views/accountMenu.js b/app/assets/javascripts/app/services/directives/views/accountMenu.js
index c04148bfd..a29a90db8 100644
--- a/app/assets/javascripts/app/services/directives/views/accountMenu.js
+++ b/app/assets/javascripts/app/services/directives/views/accountMenu.js
@@ -11,6 +11,9 @@ class AccountMenu {
$scope.formData = {url: syncManager.serverURL};
$scope.user = authManager.user;
+ $scope.server = syncManager.serverURL;
+
+ $scope.syncStatus = syncManager.syncStatus;
$scope.changePasswordPressed = function() {
$scope.showNewPasswordForm = !$scope.showNewPasswordForm;
@@ -37,14 +40,14 @@ class AccountMenu {
console.log("logging in with url", $scope.formData.url);
$timeout(function(){
authManager.login($scope.formData.url, $scope.formData.email, $scope.formData.user_password, function(response){
- $scope.formData.status = null;
if(!response || response.error) {
+ $scope.formData.status = null;
var error = response ? response.error : {message: "An unknown error occured."}
if(!response || (response && !response.didDisplayAlert)) {
alert(error.message);
}
} else {
- window.location.reload();
+ $scope.onAuthSuccess();
}
});
})
@@ -55,17 +58,23 @@ class AccountMenu {
$timeout(function(){
authManager.register($scope.formData.url, $scope.formData.email, $scope.formData.user_password, function(response){
- $scope.formData.status = null;
if(!response || response.error) {
+ $scope.formData.status = null;
var error = response ? response.error : {message: "An unknown error occured."}
alert(error.message);
} else {
- window.location.reload();
+ $scope.onAuthSuccess();
}
});
})
}
+ $scope.onAuthSuccess = function() {
+ syncManager.markAllItemsDirtyAndSaveOffline(function(){
+ window.location.reload();
+ })
+ }
+
$scope.destroyLocalData = function() {
if(!confirm("Are you sure you want to end your session? This will delete all local items and extensions.")) {
return;
@@ -79,24 +88,16 @@ class AccountMenu {
/* Import/Export */
- $scope.archiveFormData = {encryption_type: $scope.user ? 'mk' : 'ek'};
+ $scope.archiveFormData = {encrypted: $scope.user ? true : false};
$scope.user = authManager.user;
$scope.downloadDataArchive = function() {
- if($scope.archiveFormData.encryption_type == 'ek') {
- if(!$scope.archiveFormData.ek) {
- alert("You must set an encryption key to export the data encrypted.")
- return;
- }
- }
-
var link = document.createElement('a');
link.setAttribute('download', 'notes.json');
- var ek = $scope.archiveFormData.encryption_type == 'ek' ? $scope.archiveFormData.ek : null;
- var encrypted = $scope.archiveFormData.encryption_type != 'none';
+ var ek = $scope.archiveFormData.encrypted ? syncManager.masterKey : null;
- link.href = authManager.itemsDataFile(encrypted, ek);
+ link.href = authManager.itemsDataFile(ek);
link.click();
}
diff --git a/app/assets/javascripts/app/services/syncManager.js b/app/assets/javascripts/app/services/syncManager.js
index 90239e622..e2c8aa1ef 100644
--- a/app/assets/javascripts/app/services/syncManager.js
+++ b/app/assets/javascripts/app/services/syncManager.js
@@ -13,6 +13,10 @@ class SyncManager {
return localStorage.getItem("server") || "http://localhost:3000";
}
+ get masterKey() {
+ return localStorage.getItem("mk");
+ }
+
writeItemsToLocalStorage(items, offlineOnly, callback) {
var params = items.map(function(item) {
var itemParams = new ItemParams(item, null);
@@ -49,13 +53,21 @@ class SyncManager {
}
}
+ markAllItemsDirtyAndSaveOffline(callback) {
+ var items = this.modelManager.allItems;
+ for(var item of items) {
+ item.setDirty(true);
+ }
+ this.writeItemsToLocalStorage(items, false, callback);
+ }
+
get syncURL() {
return this.serverURL + "/items/sync";
}
sync(callback, options = {}) {
- if(this.syncOpInProgress) {
+ if(this.syncStatus.syncOpInProgress) {
this.repeatOnCompletion = true;
console.log("Sync op in progress; returning.");
return;
@@ -74,7 +86,7 @@ class SyncManager {
var isContinuationSync = this.needsMoreSync;
this.repeatOnCompletion = false;
- this.syncOpInProgress = true;
+ this.syncStatus.syncOpInProgress = true;
let submitLimit = 100;
var subItems = allDirtyItems.slice(0, submitLimit);
@@ -103,7 +115,7 @@ class SyncManager {
request.post().then(function(response) {
this.modelManager.clearDirtyItems(subItems);
- this.error = null;
+ this.syncStatus.error = null;
this.syncToken = response.sync_token;
this.cursorToken = response.cursor_token;
@@ -119,7 +131,7 @@ class SyncManager {
this.writeItemsToLocalStorage(saved, false, null);
this.writeItemsToLocalStorage(retrieved, false, null);
- this.syncOpInProgress = false;
+ this.syncStatus.syncOpInProgress = false;
this.syncStatus.current += subItems.length;
if(this.cursorToken || this.repeatOnCompletion || this.needsMoreSync) {
@@ -135,8 +147,8 @@ class SyncManager {
console.log("Sync error: ", response);
var error = response.data ? response.data.error : {message: "Could not connect to server."};
- this.syncOpInProgress = false;
- this.error = error;
+ this.syncStatus.syncOpInProgress = false;
+ this.syncStatus.error = error;
this.writeItemsToLocalStorage(allDirtyItems, false, null);
this.$rootScope.$broadcast("sync:error", error);
diff --git a/app/assets/stylesheets/app/_header.scss b/app/assets/stylesheets/app/_header.scss
index c0c912a7c..9f4bb069a 100644
--- a/app/assets/stylesheets/app/_header.scss
+++ b/app/assets/stylesheets/app/_header.scss
@@ -18,6 +18,10 @@
margin-top: 15px !important;
}
+.mt-25 {
+ margin-top: 25px !important;
+}
+
.mb-10 {
margin-bottom: 10px !important;
}
@@ -54,22 +58,54 @@
display: block;
}
+.medium-v-space {
+ height: 12px;
+ display: block;
+}
+
+.large-v-space {
+ height: 24px;
+ display: block;
+}
+
.medium-padding {
padding: 10px !important;
}
.large-padding {
- padding: 15px !important;
+ padding: 22px !important;
}
.red {
color: red !important;
}
+.blue {
+ color: $blue-color;
+}
+
.bold {
font-weight: bold !important;
}
+.normal {
+ font-weight: normal !important;
+}
+
+.inline {
+ display: inline-block;
+}
+
+.fake-link {
+ font-weight: bold;
+ cursor: pointer;
+ color: $blue-color;
+
+ &:hover {
+ text-decoration: underline;
+ }
+}
+
.footer-bar {
position: relative;
width: 100%;
@@ -114,6 +150,11 @@
display: block;
}
+ h2 {
+ margin-bottom: 0px;
+ margin-top: 0px;
+ }
+
h3 {
font-size: 14px !important;
margin-top: 4px !important;
@@ -122,6 +163,7 @@
h4 {
margin-bottom: 0px !important;
+ font-size: 13px !important;
}
section {
diff --git a/app/assets/templates/frontend/directives/account-menu.html.haml b/app/assets/templates/frontend/directives/account-menu.html.haml
index 167b4117e..18df4f476 100644
--- a/app/assets/templates/frontend/directives/account-menu.html.haml
+++ b/app/assets/templates/frontend/directives/account-menu.html.haml
@@ -1,7 +1,5 @@
.panel.panel-default.panel-right.account-data-menu
- .panel-body
-
- -# If not user
+ .panel-body.large-padding
%div{"ng-if" => "!user"}
%p Enter your Standard File account information. You can also register for free using the default server address.
.small-v-space
@@ -26,31 +24,38 @@
%p{"style" => "font-size: 13px; text-align: center;"}
Because notes are locally encrypted using a secret key derived from your password, there's no way to decrypt these notes if you forget your password.
For this reason, Standard Notes cannot offer a password reset option. You must make sure to store or remember your password.
- -# End if not user
- -# If user
%div{"ng-if" => "user"}
- %label Local Encryption
+ %h2 {{user.email}}
+ %p {{server}}
+
+ %p.bold.mt-10.blue{"delay-hide" => "true", "show" => "syncStatus.syncOpInProgress", "delay" => "1000"} Syncing: {{syncStatus.current}}/{{syncStatus.total}}
+ %p.bold.mt-10.red.block{"ng-if" => "syncStatus.error"} Error syncing: {{syncStatus.error.message}}
+
+ .medium-v-space
+
+ %h4 Local Encryption
%p Notes are encrypted locally before being sent to the server. Neither the server owner nor an intrusive entity can decrypt your locally encrypted notes.
- %label Status:
- {{encryptionStatusForNotes()}}
- -# End if user
+ %div.mt-5
+ %label Status:
+ {{encryptionStatusForNotes()}}
- .mt-5{"ng-if" => "user"}
- %label{"ng-if" => "user"}
- %input{"type" => "radio", "ng-model" => "archiveFormData.encrypted", "ng-value" => "true", "ng-change" => "archiveFormData.encrypted = true"}
- Encrypted
- %label
- %input{"type" => "radio", "ng-model" => "archiveFormData.encrypted", "ng-value" => "false", "ng-change" => "archiveFormData.encrypted = false"}
- Decrypted
- %a{"ng-click" => "downloadDataArchive()"} Download Data Archive
+ .mt-25{"ng-if" => "!importData.loading"}
+ %h4 Data Archives
+ .mt-5{"ng-if" => "user"}
+ %label.normal.inline{"ng-if" => "user"}
+ %input{"type" => "radio", "ng-model" => "archiveFormData.encrypted", "ng-value" => "true", "ng-change" => "archiveFormData.encrypted = true"}
+ Encrypted
+ %label.normal.inline
+ %input{"type" => "radio", "ng-model" => "archiveFormData.encrypted", "ng-value" => "false", "ng-change" => "archiveFormData.encrypted = false"}
+ Decrypted
+
+ %a.block{"ng-click" => "downloadDataArchive()"} Download Data Archive
+
+ %label.block.mt-5
+ %input{"type" => "file", "style" => "display: none;", "file-change" => "->", "handler" => "ctrl.importFileSelected(files)"}
+ .fake-link Import Data from Archive
- %div{"ng-if" => "!importData.loading"}
- %label#import-archive
- %input{"type" => "file", "style" => "display: none;", "file-change" => "->", "handler" => "importFileSelected(files)"}
- %a.disabled
- %span
- Import Data from Archive
%div{"ng-if" => "importData.requestPassword"}
Enter the account password associated with the import file.
%input{"type" => "text", "ng-model" => "importData.password"}
@@ -58,4 +63,4 @@
.spinner{"ng-if" => "importData.loading"}
- %a{"ng-click" => "destroyLocalData()"} Destroy all local data
+ %a.block.mt-25.red{"ng-click" => "destroyLocalData()"} Destroy all local data