From 51d3326a72a75c262d2365548c1fc7407e2a2e26 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Thu, 9 Feb 2017 12:54:37 -0600 Subject: [PATCH 01/40] fix decryption extension --- app/assets/javascripts/app/services/extensionManager.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/app/services/extensionManager.js b/app/assets/javascripts/app/services/extensionManager.js index d18cf868c..d9a0260f6 100644 --- a/app/assets/javascripts/app/services/extensionManager.js +++ b/app/assets/javascripts/app/services/extensionManager.js @@ -281,7 +281,11 @@ class ExtensionManager { } outgoingParamsForItem(item, extension) { - var itemParams = new ItemParams(item, this.syncManager.masterKey); + var ek = this.syncManager.masterKey; + if(!this.extensionUsesEncryptedData(extension)) { + ek = null; + } + var itemParams = new ItemParams(item, ek); return itemParams.paramsForExtension(); } From ae9899dee14933c7fcc3d20b2fc362937c95f03b Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Thu, 9 Feb 2017 13:02:08 -0600 Subject: [PATCH 02/40] comment on sync function --- app/assets/javascripts/app/services/syncManager.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/assets/javascripts/app/services/syncManager.js b/app/assets/javascripts/app/services/syncManager.js index 66344b15a..d2d4b2fd3 100644 --- a/app/assets/javascripts/app/services/syncManager.js +++ b/app/assets/javascripts/app/services/syncManager.js @@ -154,6 +154,9 @@ class SyncManager { var retrieved = this.handleItemsResponse(response.retrieved_items, null); // merge only metadata for saved items + // Update 2/9/17: I just realized we may not need to handle saved_items anymore. We used to do this because we wanted to merge presentation-related metadata, + // but that has since been removed. Since this function is an important part of the functioning of the app, I'm not going to remove it just yet without careful + // testing. var omitFields = ["content", "auth_hash"]; var saved = this.handleItemsResponse(response.saved_items, omitFields); From 577f286352ddffadb7b693668880aa6c50070cec Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Fri, 10 Feb 2017 11:49:50 -0600 Subject: [PATCH 03/40] change pw wip --- .../javascripts/app/services/authManager.js | 77 ++++++------------- .../services/directives/views/accountMenu.js | 53 +++++++++---- .../app/services/helpers/encryptionHelper.js | 10 +-- .../javascripts/app/services/modelManager.js | 7 ++ .../javascripts/app/services/syncManager.js | 44 +++++++++-- app/assets/stylesheets/app/_header.scss | 4 + .../directives/account-menu.html.haml | 18 +++++ 7 files changed, 134 insertions(+), 79 deletions(-) diff --git a/app/assets/javascripts/app/services/authManager.js b/app/assets/javascripts/app/services/authManager.js index fcc3b4d92..5a9d1bde2 100644 --- a/app/assets/javascripts/app/services/authManager.js +++ b/app/assets/javascripts/app/services/authManager.js @@ -73,13 +73,12 @@ angular.module('app.frontend') } Neeto.crypto.computeEncryptionKeysForUser(_.merge({password: password}, authParams), function(keys){ - var mk = keys.mk; var requestUrl = url + "/auth/sign_in"; var request = Restangular.oneUrl(requestUrl, requestUrl); var params = {password: keys.pw, email: email}; _.merge(request, params); request.post().then(function(response){ - this.handleAuthResponse(response, email, url, authParams, mk, keys.pw); + this.handleAuthResponse(response, email, url, authParams, keys.mk, keys.pw); callback(response); }.bind(this)) .catch(function(response){ @@ -91,7 +90,9 @@ angular.module('app.frontend') } this.handleAuthResponse = function(response, email, url, authParams, mk, pw) { - localStorage.setItem("server", url); + if(url) { + localStorage.setItem("server", url); + } localStorage.setItem("user", JSON.stringify(response.plain().user)); localStorage.setItem("auth_params", JSON.stringify(_.omit(authParams, ["pw_nonce"]))); localStorage.setItem("mk", mk); @@ -101,13 +102,12 @@ angular.module('app.frontend') this.register = function(url, email, password, callback) { Neeto.crypto.generateInitialEncryptionKeysForUser({password: password, email: email}, function(keys, authParams){ - var mk = keys.mk; var requestUrl = url + "/auth"; var request = Restangular.oneUrl(requestUrl, requestUrl); var params = _.merge({password: keys.pw, email: email}, authParams); _.merge(request, params); request.post().then(function(response){ - this.handleAuthResponse(response, email, url, authParams, mk, keys.pw); + this.handleAuthResponse(response, email, url, authParams, keys.mk, keys.pw); callback(response); }.bind(this)) .catch(function(response){ @@ -117,55 +117,26 @@ angular.module('app.frontend') }.bind(this)); } - // this.changePassword = function(current_password, new_password) { - // this.getAuthParamsForEmail(email, function(authParams){ - // if(!authParams) { - // callback(null); - // return; - // } - // Neeto.crypto.computeEncryptionKeysForUser(_.merge({password: current_password, email: user.email}, authParams), function(currentKeys) { - // Neeto.crypto.computeEncryptionKeysForUser(_.merge({password: new_password, email: user.email}, authParams), function(newKeys){ - // var data = {}; - // data.current_password = currentKeys.pw; - // data.password = newKeys.pw; - // data.password_confirmation = newKeys.pw; - // - // var user = this.user; - // - // this._performPasswordChange(currentKeys, newKeys, function(response){ - // if(response && !response.error) { - // // this.showNewPasswordForm = false; - // // reencrypt data with new mk - // this.reencryptAllItemsAndSave(user, newKeys.mk, currentKeys.mk, function(success){ - // if(success) { - // this.setMk(newKeys.mk); - // alert("Your password has been changed and your data re-encrypted."); - // } else { - // // rollback password - // this._performPasswordChange(newKeys, currentKeys, function(response){ - // alert("There was an error changing your password. Your password has been rolled back."); - // window.location.reload(); - // }) - // } - // }.bind(this)); - // } else { - // // this.showNewPasswordForm = false; - // alert("There was an error changing your password. Please try again."); - // } - // }.bind(this)) - // }.bind(this)); - // }.bind(this)); - // }.bind(this)); - // } + this.changePassword = function(email, new_password, callback) { + Neeto.crypto.generateInitialEncryptionKeysForUser({password: new_password, email: email}, function(keys, authParams){ + var requestUrl = localStorage.getItem("server") + "/auth/change_pw"; + var request = Restangular.oneUrl(requestUrl, requestUrl); + var params = _.merge({new_password: keys.pw}, authParams); + _.merge(request, params); - this._performPasswordChange = function(url, email, current_keys, new_keys, callback) { - var requestUrl = url + "/auth"; - var request = Restangular.oneUrl(requestUrl, requestUrl); - var params = {password: new_keys.pw, password_confirmation: new_keys.pw, current_password: current_keys.pw, email: email}; - _.merge(request, params); - request.patch().then(function(response){ - callback(response); - }) + request.post().then(function(response){ + this.handleAuthResponse(response, email, null, authParams, keys.mk, keys.pw); + callback(response.plain()); + }.bind(this)) + .catch(function(response){ + var error = response.data; + if(!error) { + error = {message: "Something went wrong while changing your password. Your password was not changed. Please try again."} + } + console.log("Change pw error", response); + callback({error: error}); + }) + }.bind(this)); } this.staticifyObject = function(object) { diff --git a/app/assets/javascripts/app/services/directives/views/accountMenu.js b/app/assets/javascripts/app/services/directives/views/accountMenu.js index 27c491280..7364b51c4 100644 --- a/app/assets/javascripts/app/services/directives/views/accountMenu.js +++ b/app/assets/javascripts/app/services/directives/views/accountMenu.js @@ -15,10 +15,6 @@ class AccountMenu { $scope.syncStatus = syncManager.syncStatus; - $scope.changePasswordPressed = function() { - $scope.showNewPasswordForm = !$scope.showNewPasswordForm; - } - $scope.encryptionKey = function() { return syncManager.masterKey; } @@ -31,19 +27,50 @@ class AccountMenu { return `${$scope.server}/dashboard/?server=${$scope.server}&id=${$scope.user.email}&pw=${$scope.serverPassword()}`; } - $scope.submitPasswordChange = function() { - $scope.passwordChangeData.status = "Generating New Keys..."; + $scope.newPasswordData = {}; - $timeout(function(){ - if(data.password != data.password_confirmation) { - alert("Your new password does not match its confirmation."); + $scope.showPasswordChangeForm = function() { + $scope.newPasswordData.showNewPasswordForm = true; + } + + $scope.submitPasswordChange = function() { + + if($scope.newPasswordData.newPassword != $scope.newPasswordData.newPasswordConfirmation) { + alert("Your new password does not match its confirmation."); + $scope.newPasswordData.status = null; + return; + } + + var email = $scope.user.email; + if(!email) { + alert("We don't have your email stored. Please log out then log back in to fix this issue."); + $scope.newPasswordData.status = null; + return; + } + + $scope.newPasswordData.status = "Generating New Keys..."; + + authManager.changePassword(email, $scope.newPasswordData.newPassword, function(response){ + if(response.error) { + alert("There was an error changing your password. Please try again."); + $scope.newPasswordData.status = null; return; } - authManager.changePassword($scope.passwordChangeData.current_password, $scope.passwordChangeData.new_password, function(response){ - - }) - + // re-encrypt all items + $scope.newPasswordData.status = "Re-encrypting all items with your new key..."; + modelManager.setAllItemsDirty(); + syncManager.sync(function(response){ + if(response.error) { + alert("There was an error re-encrypting your items. Your password was changed, but not all your items were properly re-encrypted and synced. You should try syncing again. If all else fails, you should restore your notes from backup.") + return; + } + $scope.newPasswordData.status = "Successfully changed password and re-encrypted all items."; + alert("Your password has been changed, and your items successfully re-encrypted and synced. Be sure to log out on all other signed in applications.") + $timeout(function(){ + $scope.newPasswordData = {}; + }, 1000) + }); }) } diff --git a/app/assets/javascripts/app/services/helpers/encryptionHelper.js b/app/assets/javascripts/app/services/helpers/encryptionHelper.js index 7cf011ac8..08095d4f0 100644 --- a/app/assets/javascripts/app/services/helpers/encryptionHelper.js +++ b/app/assets/javascripts/app/services/helpers/encryptionHelper.js @@ -2,13 +2,13 @@ class EncryptionHelper { static encryptItem(item, key) { var item_key = null; - if(item.enc_item_key) { - // we reuse the key, but this is optional - item_key = Neeto.crypto.decryptText(item.enc_item_key, key); - } else { + // if(item.enc_item_key) { + // // we reuse the key, but this is optional + // item_key = Neeto.crypto.decryptText(item.enc_item_key, key); + // } else { item_key = Neeto.crypto.generateRandomEncryptionKey(); item.enc_item_key = Neeto.crypto.encryptText(item_key, key); - } + // } var ek = Neeto.crypto.firstHalfOfKey(item_key); var ak = Neeto.crypto.secondHalfOfKey(item_key); diff --git a/app/assets/javascripts/app/services/modelManager.js b/app/assets/javascripts/app/services/modelManager.js index aa436b6e2..1d920c018 100644 --- a/app/assets/javascripts/app/services/modelManager.js +++ b/app/assets/javascripts/app/services/modelManager.js @@ -225,6 +225,13 @@ class ModelManager { item.removeAllRelationships(); } + /* Used when changing encryption key */ + setAllItemsDirty() { + for(var item of this.allItems) { + item.setDirty(true); + } + } + removeItemLocally(item, callback) { _.pull(this.items, item); diff --git a/app/assets/javascripts/app/services/syncManager.js b/app/assets/javascripts/app/services/syncManager.js index d2d4b2fd3..851d1c0a5 100644 --- a/app/assets/javascripts/app/services/syncManager.js +++ b/app/assets/javascripts/app/services/syncManager.js @@ -98,10 +98,38 @@ class SyncManager { return this._cursorToken; } + get queuedCallbacks() { + if(!this._queuedCallbacks) { + this._queuedCallbacks = []; + } + return this._queuedCallbacks; + } + + clearQueuedCallbacks() { + this._queuedCallbacks = []; + } + + callQueuedCallbacksAndCurrent(currentCallback, response) { + var allCallbacks = this.queuedCallbacks; + if(currentCallback) { + allCallbacks.push(currentCallback); + } + if(allCallbacks.length) { + console.log(allCallbacks.length, "queued callbacks"); + for(var eachCallback of allCallbacks) { + eachCallback(response); + } + this.clearQueuedCallbacks(); + } + } + sync(callback, options = {}) { if(this.syncStatus.syncOpInProgress) { this.repeatOnCompletion = true; + if(callback) { + this.queuedCallbacks.push(callback); + } console.log("Sync op in progress; returning."); return; } @@ -118,7 +146,6 @@ class SyncManager { var isContinuationSync = this.needsMoreSync; - this.repeatOnCompletion = false; this.syncStatus.syncOpInProgress = true; let submitLimit = 100; @@ -172,14 +199,17 @@ class SyncManager { this.syncToken = response.sync_token; this.cursorToken = response.cursor_token; - if(this.cursorToken || this.repeatOnCompletion || this.needsMoreSync) { + if(this.cursorToken || this.needsMoreSync) { setTimeout(function () { this.sync(callback, options); }.bind(this), 10); // wait 10ms to allow UI to update + } else if(this.repeatOnCompletion) { + this.repeatOnCompletion = false; + setTimeout(function () { + this.sync(null, options); + }.bind(this), 10); // wait 10ms to allow UI to update } else { - if(callback) { - callback(response); - } + this.callQueuedCallbacksAndCurrent(callback, response); } }.bind(this)) @@ -193,9 +223,7 @@ class SyncManager { this.$rootScope.$broadcast("sync:error", error); - if(callback) { - callback({error: "Sync error"}); - } + this.callQueuedCallbacksAndCurrent(callback, {error: "Sync error"}); }.bind(this)) } diff --git a/app/assets/stylesheets/app/_header.scss b/app/assets/stylesheets/app/_header.scss index 74efb241a..5ba6cab79 100644 --- a/app/assets/stylesheets/app/_header.scss +++ b/app/assets/stylesheets/app/_header.scss @@ -124,6 +124,10 @@ font-weight: bold !important; } +.italic { + font-style: italic !important; +} + .normal { font-weight: normal !important; } diff --git a/app/assets/templates/frontend/directives/account-menu.html.haml b/app/assets/templates/frontend/directives/account-menu.html.haml index 62ffe0db9..1db3d3669 100644 --- a/app/assets/templates/frontend/directives/account-menu.html.haml +++ b/app/assets/templates/frontend/directives/account-menu.html.haml @@ -36,6 +36,24 @@ %label.block.mt-5.mb-0 Server password: .wrap.normal.mt-1 {{serverPassword() ? serverPassword() : 'Not available. Sign out then sign back in to compute.'}} + + %a.block.mt-5{"ng-click" => "newPasswordData.changePassword = !newPasswordData.changePassword"} Change Password + %section.gray-bg.mt-10.medium-padding{"ng-if" => "newPasswordData.changePassword"} + %p.bold Change Password (Beta) + %p.mt-10 Since your encrpytion key is based on your password, changing your password requires all your notes and tags to be re-encrypted using your new key. + %p.mt-5 If you have thousands of items, this can take several minutes — you must keep the application window open during this process. + %p.mt-5 After changing your password, you must log out of all other applications currently signed in to your account. + %p.bold.mt-5 It is highly recommended you download a backup of your data before proceeding. + %div.mt-10 + %a.red.mr-5{"ng-if" => "!newPasswordData.showNewPasswordForm", "ng-click" => "showPasswordChangeForm()"} Continue + %a{"ng-click" => "newPasswordData.changePassword = false; newPasswordData.showNewPasswordForm = false"} Cancel + %div.mt-10{"ng-if" => "newPasswordData.showNewPasswordForm"} + %form + %input.form-control{"type" => "text", "ng-model" => "newPasswordData.newPassword", "placeholder" => "Enter new password"} + %input.form-control{"type" => "text", "ng-model" => "newPasswordData.newPasswordConfirmation", "placeholder" => "Confirm new password"} + %button.btn.dark-button.btn-block{"ng-click" => "submitPasswordChange()"} Submit + %p.italic.mt-10{"ng-if" => "newPasswordData.status"} {{newPasswordData.status}} + %a.block.mt-5{"href" => "{{dashboardURL()}}", "target" => "_blank"} Standard File Dashboard %div.bold.mt-10.blue{"delay-hide" => "true", "show" => "syncStatus.syncOpInProgress", "delay" => "1000"} From 5d6d594b6c4433d71a7a6e25aa0bab8a9e27244e Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Fri, 10 Feb 2017 13:10:19 -0600 Subject: [PATCH 04/40] solid password change --- .../directives/functional/delay-hide.js | 6 ++- .../services/directives/views/accountMenu.js | 44 +++++++++++-------- .../javascripts/app/services/syncManager.js | 13 +++--- .../directives/account-menu.html.haml | 19 ++++---- 4 files changed, 45 insertions(+), 37 deletions(-) diff --git a/app/assets/javascripts/app/services/directives/functional/delay-hide.js b/app/assets/javascripts/app/services/directives/functional/delay-hide.js index 7467208f4..6e381ed3f 100644 --- a/app/assets/javascripts/app/services/directives/functional/delay-hide.js +++ b/app/assets/javascripts/app/services/directives/functional/delay-hide.js @@ -20,11 +20,15 @@ angular }); function showSpinner() { + if(scope.hidePromise) { + $timeout.cancel(scope.hidePromise); + scope.hidePromise = null; + } showElement(true); } function hideSpinner() { - $timeout(showElement.bind(this, false), getDelay()); + scope.hidePromise = $timeout(showElement.bind(this, false), getDelay()); } function showElement(show) { diff --git a/app/assets/javascripts/app/services/directives/views/accountMenu.js b/app/assets/javascripts/app/services/directives/views/accountMenu.js index 7364b51c4..aa171bfab 100644 --- a/app/assets/javascripts/app/services/directives/views/accountMenu.js +++ b/app/assets/javascripts/app/services/directives/views/accountMenu.js @@ -30,7 +30,7 @@ class AccountMenu { $scope.newPasswordData = {}; $scope.showPasswordChangeForm = function() { - $scope.newPasswordData.showNewPasswordForm = true; + $scope.newPasswordData.showForm = true; } $scope.submitPasswordChange = function() { @@ -49,29 +49,35 @@ class AccountMenu { } $scope.newPasswordData.status = "Generating New Keys..."; + $scope.newPasswordData.showForm = false; - authManager.changePassword(email, $scope.newPasswordData.newPassword, function(response){ - if(response.error) { - alert("There was an error changing your password. Please try again."); - $scope.newPasswordData.status = null; - return; - } - - // re-encrypt all items - $scope.newPasswordData.status = "Re-encrypting all items with your new key..."; - modelManager.setAllItemsDirty(); - syncManager.sync(function(response){ + // perform a sync beforehand to pull in any last minutes changes before we change the encryption key (and thus cant decrypt new changes) + syncManager.sync(function(response){ + authManager.changePassword(email, $scope.newPasswordData.newPassword, function(response){ if(response.error) { - alert("There was an error re-encrypting your items. Your password was changed, but not all your items were properly re-encrypted and synced. You should try syncing again. If all else fails, you should restore your notes from backup.") + alert("There was an error changing your password. Please try again."); + $scope.newPasswordData.status = null; return; } - $scope.newPasswordData.status = "Successfully changed password and re-encrypted all items."; - alert("Your password has been changed, and your items successfully re-encrypted and synced. Be sure to log out on all other signed in applications.") - $timeout(function(){ - $scope.newPasswordData = {}; - }, 1000) - }); + + // re-encrypt all items + $scope.newPasswordData.status = "Re-encrypting all items with your new key..."; + + modelManager.setAllItemsDirty(); + syncManager.sync(function(response){ + if(response.error) { + alert("There was an error re-encrypting your items. Your password was changed, but not all your items were properly re-encrypted and synced. You should try syncing again. If all else fails, you should restore your notes from backup.") + return; + } + $scope.newPasswordData.status = "Successfully changed password and re-encrypted all items."; + $timeout(function(){ + alert("Your password has been changed, and your items successfully re-encrypted and synced. You must sign out on all other signed in applications and sign in again, or else you may corrupt your data.") + $scope.newPasswordData = {}; + }, 1000) + }); + }) }) + } $scope.loginSubmitPressed = function() { diff --git a/app/assets/javascripts/app/services/syncManager.js b/app/assets/javascripts/app/services/syncManager.js index 851d1c0a5..618d77ef0 100644 --- a/app/assets/javascripts/app/services/syncManager.js +++ b/app/assets/javascripts/app/services/syncManager.js @@ -115,7 +115,6 @@ class SyncManager { allCallbacks.push(currentCallback); } if(allCallbacks.length) { - console.log(allCallbacks.length, "queued callbacks"); for(var eachCallback of allCallbacks) { eachCallback(response); } @@ -144,7 +143,7 @@ class SyncManager { return; } - var isContinuationSync = this.needsMoreSync; + var isContinuationSync = this.syncStatus.needsMoreSync; this.syncStatus.syncOpInProgress = true; @@ -152,9 +151,9 @@ class SyncManager { var subItems = allDirtyItems.slice(0, submitLimit); if(subItems.length < allDirtyItems.length) { // more items left to be synced, repeat - this.needsMoreSync = true; + this.syncStatus.needsMoreSync = true; } else { - this.needsMoreSync = false; + this.syncStatus.needsMoreSync = false; } if(!isContinuationSync) { @@ -199,14 +198,14 @@ class SyncManager { this.syncToken = response.sync_token; this.cursorToken = response.cursor_token; - if(this.cursorToken || this.needsMoreSync) { + if(this.cursorToken || this.syncStatus.needsMoreSync) { setTimeout(function () { this.sync(callback, options); }.bind(this), 10); // wait 10ms to allow UI to update } else if(this.repeatOnCompletion) { this.repeatOnCompletion = false; setTimeout(function () { - this.sync(null, options); + this.sync(callback, options); }.bind(this), 10); // wait 10ms to allow UI to update } else { this.callQueuedCallbacksAndCurrent(callback, response); @@ -242,7 +241,7 @@ class SyncManager { var item = this.modelManager.findItem(itemResponse.uuid); var error = mapping.error; if(error.tag == "uuid_conflict") { - // uuid conflicts can occur if a user attempts to import an old data archive with uuids form the old account into a new account + // uuid conflicts can occur if a user attempts to import an old data archive with uuids from the old account into a new account this.modelManager.alternateUUIDForItem(item, handleNext); } ++i; diff --git a/app/assets/templates/frontend/directives/account-menu.html.haml b/app/assets/templates/frontend/directives/account-menu.html.haml index 1db3d3669..628cd68f8 100644 --- a/app/assets/templates/frontend/directives/account-menu.html.haml +++ b/app/assets/templates/frontend/directives/account-menu.html.haml @@ -28,6 +28,7 @@ %div{"ng-if" => "user"} %h2 {{user.email}} %p {{server}} + %a.block.mt-10{"href" => "{{dashboardURL()}}", "target" => "_blank"} → Standard File Dashboard %a.block.mt-5{"ng-click" => "showCredentials = !showCredentials"} Show Credentials %section.gray-bg.mt-10.medium-padding{"ng-if" => "showCredentials"} %label.block @@ -44,22 +45,20 @@ %p.mt-5 If you have thousands of items, this can take several minutes — you must keep the application window open during this process. %p.mt-5 After changing your password, you must log out of all other applications currently signed in to your account. %p.bold.mt-5 It is highly recommended you download a backup of your data before proceeding. - %div.mt-10 - %a.red.mr-5{"ng-if" => "!newPasswordData.showNewPasswordForm", "ng-click" => "showPasswordChangeForm()"} Continue - %a{"ng-click" => "newPasswordData.changePassword = false; newPasswordData.showNewPasswordForm = false"} Cancel - %div.mt-10{"ng-if" => "newPasswordData.showNewPasswordForm"} + %div.mt-10{"ng-if" => "!newPasswordData.status"} + %a.red.mr-5{"ng-if" => "!newPasswordData.showForm", "ng-click" => "showPasswordChangeForm()"} Continue + %a{"ng-click" => "newPasswordData.changePassword = false; newPasswordData.showForm = false"} Cancel + %div.mt-10{"ng-if" => "newPasswordData.showForm"} %form %input.form-control{"type" => "text", "ng-model" => "newPasswordData.newPassword", "placeholder" => "Enter new password"} %input.form-control{"type" => "text", "ng-model" => "newPasswordData.newPasswordConfirmation", "placeholder" => "Confirm new password"} %button.btn.dark-button.btn-block{"ng-click" => "submitPasswordChange()"} Submit - %p.italic.mt-10{"ng-if" => "newPasswordData.status"} {{newPasswordData.status}} + %p.italic.mt-10{"ng-if" => "newPasswordData.status"} {{newPasswordData.status}} - %a.block.mt-5{"href" => "{{dashboardURL()}}", "target" => "_blank"} Standard File Dashboard - - %div.bold.mt-10.blue{"delay-hide" => "true", "show" => "syncStatus.syncOpInProgress", "delay" => "1000"} + %div.bold.mt-10.blue{"delay-hide" => "true", "show" => "syncStatus.syncOpInProgress || syncStatus.needsMoreSync", "delay" => "1000"} .spinner.inline.mr-5.blue - Syncing - %span{"ng-if" => "syncStatus.total > 0"}: {{syncStatus.current}}/{{syncStatus.total}} + {{"Syncing" + (syncStatus.total > 0 ? ":" : "")}} + %span{"ng-if" => "syncStatus.total > 0"} {{syncStatus.current}}/{{syncStatus.total}} %p.bold.mt-10.red.block{"ng-if" => "syncStatus.error"} Error syncing: {{syncStatus.error.message}} .medium-v-space From 88372e3f02e199d0ea76ac54df965bd8168d21a5 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Fri, 10 Feb 2017 13:18:21 -0600 Subject: [PATCH 05/40] comment on writing saved items --- app/assets/javascripts/app/services/syncManager.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/app/services/syncManager.js b/app/assets/javascripts/app/services/syncManager.js index 618d77ef0..ddabf436d 100644 --- a/app/assets/javascripts/app/services/syncManager.js +++ b/app/assets/javascripts/app/services/syncManager.js @@ -179,10 +179,10 @@ class SyncManager { this.$rootScope.$broadcast("sync:updated_token", this.syncToken); var retrieved = this.handleItemsResponse(response.retrieved_items, null); + // merge only metadata for saved items - // Update 2/9/17: I just realized we may not need to handle saved_items anymore. We used to do this because we wanted to merge presentation-related metadata, - // but that has since been removed. Since this function is an important part of the functioning of the app, I'm not going to remove it just yet without careful - // testing. + // we write saved items to disk now because it clears their dirty status then saves + // if we saved items before completion, we had have to save them as dirty and save them again on success as clean var omitFields = ["content", "auth_hash"]; var saved = this.handleItemsResponse(response.saved_items, omitFields); From 26808cc57d162aa6d2b1d93194ea84f8c8d2adc4 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Fri, 10 Feb 2017 13:22:39 -0600 Subject: [PATCH 06/40] don't reuse encryption keys, change order of sync status --- .../app/services/helpers/encryptionHelper.js | 10 ++-------- .../frontend/directives/account-menu.html.haml | 14 +++++++------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/app/services/helpers/encryptionHelper.js b/app/assets/javascripts/app/services/helpers/encryptionHelper.js index 08095d4f0..e0e171af5 100644 --- a/app/assets/javascripts/app/services/helpers/encryptionHelper.js +++ b/app/assets/javascripts/app/services/helpers/encryptionHelper.js @@ -1,14 +1,8 @@ class EncryptionHelper { static encryptItem(item, key) { - var item_key = null; - // if(item.enc_item_key) { - // // we reuse the key, but this is optional - // item_key = Neeto.crypto.decryptText(item.enc_item_key, key); - // } else { - item_key = Neeto.crypto.generateRandomEncryptionKey(); - item.enc_item_key = Neeto.crypto.encryptText(item_key, key); - // } + var item_key = Neeto.crypto.generateRandomEncryptionKey(); + item.enc_item_key = Neeto.crypto.encryptText(item_key, key); var ek = Neeto.crypto.firstHalfOfKey(item_key); var ak = Neeto.crypto.secondHalfOfKey(item_key); diff --git a/app/assets/templates/frontend/directives/account-menu.html.haml b/app/assets/templates/frontend/directives/account-menu.html.haml index 628cd68f8..a1e03cd1e 100644 --- a/app/assets/templates/frontend/directives/account-menu.html.haml +++ b/app/assets/templates/frontend/directives/account-menu.html.haml @@ -28,7 +28,13 @@ %div{"ng-if" => "user"} %h2 {{user.email}} %p {{server}} - %a.block.mt-10{"href" => "{{dashboardURL()}}", "target" => "_blank"} → Standard File Dashboard + %div.bold.mt-10.blue{"delay-hide" => "true", "show" => "syncStatus.syncOpInProgress || syncStatus.needsMoreSync", "delay" => "1000"} + .spinner.inline.mr-5.blue + {{"Syncing" + (syncStatus.total > 0 ? ":" : "")}} + %span{"ng-if" => "syncStatus.total > 0"} {{syncStatus.current}}/{{syncStatus.total}} + %p.bold.mt-10.red.block{"ng-if" => "syncStatus.error"} Error syncing: {{syncStatus.error.message}} + + %a.block.mt-15{"href" => "{{dashboardURL()}}", "target" => "_blank"} → Standard File Dashboard %a.block.mt-5{"ng-click" => "showCredentials = !showCredentials"} Show Credentials %section.gray-bg.mt-10.medium-padding{"ng-if" => "showCredentials"} %label.block @@ -55,12 +61,6 @@ %button.btn.dark-button.btn-block{"ng-click" => "submitPasswordChange()"} Submit %p.italic.mt-10{"ng-if" => "newPasswordData.status"} {{newPasswordData.status}} - %div.bold.mt-10.blue{"delay-hide" => "true", "show" => "syncStatus.syncOpInProgress || syncStatus.needsMoreSync", "delay" => "1000"} - .spinner.inline.mr-5.blue - {{"Syncing" + (syncStatus.total > 0 ? ":" : "")}} - %span{"ng-if" => "syncStatus.total > 0"} {{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 From fe89fa8c3bc4b7b9b985d7c171832d12157096d9 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Fri, 10 Feb 2017 13:25:21 -0600 Subject: [PATCH 07/40] alert on import success --- .../javascripts/app/services/directives/views/accountMenu.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/app/services/directives/views/accountMenu.js b/app/assets/javascripts/app/services/directives/views/accountMenu.js index aa171bfab..bf54654f8 100644 --- a/app/assets/javascripts/app/services/directives/views/accountMenu.js +++ b/app/assets/javascripts/app/services/directives/views/accountMenu.js @@ -149,6 +149,8 @@ class AccountMenu { $scope.importData = null; if(!response) { alert("There was an error importing your data. Please try again."); + } else { + alert("Your data was successfully imported.") } }) }) @@ -182,8 +184,6 @@ class AccountMenu { } $scope.importJSONData = function(data, password, callback) { - console.log("Importing data", data); - var onDataReady = function() { var items = modelManager.mapResponseItemsToLocalModels(data.items); items.forEach(function(item){ From 296f77df6fe48d30ce7a62985be4ab3f4984c215 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Fri, 10 Feb 2017 14:13:45 -0600 Subject: [PATCH 08/40] only accept relevant items --- .../app/services/directives/views/accountMenu.js | 2 +- app/assets/javascripts/app/services/modelManager.js | 9 +++++++-- .../templates/frontend/directives/account-menu.html.haml | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/app/services/directives/views/accountMenu.js b/app/assets/javascripts/app/services/directives/views/accountMenu.js index bf54654f8..088b98969 100644 --- a/app/assets/javascripts/app/services/directives/views/accountMenu.js +++ b/app/assets/javascripts/app/services/directives/views/accountMenu.js @@ -71,7 +71,7 @@ class AccountMenu { } $scope.newPasswordData.status = "Successfully changed password and re-encrypted all items."; $timeout(function(){ - alert("Your password has been changed, and your items successfully re-encrypted and synced. You must sign out on all other signed in applications and sign in again, or else you may corrupt your data.") + alert("Your password has been changed, and your items successfully re-encrypted and synced. You must sign out of all other signed in applications and sign in again, or else you may corrupt your data.") $scope.newPasswordData = {}; }, 1000) }); diff --git a/app/assets/javascripts/app/services/modelManager.js b/app/assets/javascripts/app/services/modelManager.js index 1d920c018..8b23060dc 100644 --- a/app/assets/javascripts/app/services/modelManager.js +++ b/app/assets/javascripts/app/services/modelManager.js @@ -8,6 +8,7 @@ class ModelManager { this.itemChangeObservers = []; this.items = []; this._extensions = []; + this.acceptableContentTypes = ["Note", "Tag", "Extension"]; } get allItems() { @@ -62,7 +63,7 @@ class ModelManager { for (var json_obj of items) { json_obj = _.omit(json_obj, omitFields || []) var item = this.findItem(json_obj["uuid"]); - if(json_obj["deleted"] == true) { + if(json_obj["deleted"] == true || !_.includes(this.acceptableContentTypes, json_obj["content_type"])) { if(item) { this.removeItemLocally(item) } @@ -227,7 +228,11 @@ class ModelManager { /* Used when changing encryption key */ setAllItemsDirty() { - for(var item of this.allItems) { + var relevantItems = this.allItems.filter(function(item){ + return _.includes(this.acceptableContentTypes, item.content_type); + }.bind(this)); + + for(var item of relevantItems) { item.setDirty(true); } } diff --git a/app/assets/templates/frontend/directives/account-menu.html.haml b/app/assets/templates/frontend/directives/account-menu.html.haml index a1e03cd1e..6927bff0f 100644 --- a/app/assets/templates/frontend/directives/account-menu.html.haml +++ b/app/assets/templates/frontend/directives/account-menu.html.haml @@ -47,7 +47,7 @@ %a.block.mt-5{"ng-click" => "newPasswordData.changePassword = !newPasswordData.changePassword"} Change Password %section.gray-bg.mt-10.medium-padding{"ng-if" => "newPasswordData.changePassword"} %p.bold Change Password (Beta) - %p.mt-10 Since your encrpytion key is based on your password, changing your password requires all your notes and tags to be re-encrypted using your new key. + %p.mt-10 Since your encryption key is based on your password, changing your password requires all your notes and tags to be re-encrypted using your new key. %p.mt-5 If you have thousands of items, this can take several minutes — you must keep the application window open during this process. %p.mt-5 After changing your password, you must log out of all other applications currently signed in to your account. %p.bold.mt-5 It is highly recommended you download a backup of your data before proceeding. From 0fa048e23ca2b28c0bbd47df9ba68606b6410d69 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Mon, 13 Feb 2017 11:42:27 -0600 Subject: [PATCH 09/40] preview action, click outside panel handle --- .../app/frontend/controllers/editor.js | 4 --- .../app/frontend/models/api/item.js | 1 + .../app/frontend/models/app/extension.js | 4 ++- .../javascripts/app/services/dbManager.js | 1 - .../directives/functional/click-outside.js | 27 +++++++++++++++++ .../views/contextualExtensionsMenu.js | 20 +++++++++++++ .../app/services/extensionManager.js | 28 +++++++++++++++--- .../javascripts/app/services/modelManager.js | 8 ++--- app/assets/stylesheets/app/_directives.scss | 29 +++++++++++++++++++ app/assets/stylesheets/app/_editor.scss | 11 ------- app/assets/stylesheets/app/_header.scss | 9 +++--- .../directives/contextual-menu.html.haml | 14 +++++++++ .../templates/frontend/editor.html.haml | 8 ++--- .../templates/frontend/header.html.haml | 4 +-- 14 files changed, 133 insertions(+), 35 deletions(-) create mode 100644 app/assets/javascripts/app/services/directives/functional/click-outside.js diff --git a/app/assets/javascripts/app/frontend/controllers/editor.js b/app/assets/javascripts/app/frontend/controllers/editor.js index 51ceee65f..8aee7ed7f 100644 --- a/app/assets/javascripts/app/frontend/controllers/editor.js +++ b/app/assets/javascripts/app/frontend/controllers/editor.js @@ -231,10 +231,6 @@ angular.module('app.frontend') } } - this.clickedMenu = function() { - this.showMenu = !this.showMenu; - } - this.deleteNote = function() { if(confirm("Are you sure you want to delete this note?")) { this.remove()(this.note); diff --git a/app/assets/javascripts/app/frontend/models/api/item.js b/app/assets/javascripts/app/frontend/models/api/item.js index f6129f5a0..d486f9739 100644 --- a/app/assets/javascripts/app/frontend/models/api/item.js +++ b/app/assets/javascripts/app/frontend/models/api/item.js @@ -37,6 +37,7 @@ class Item { updateFromJSON(json) { _.merge(this, json); + if(this.created_at) { this.created_at = new Date(this.created_at); this.updated_at = new Date(this.updated_at); diff --git a/app/assets/javascripts/app/frontend/models/app/extension.js b/app/assets/javascripts/app/frontend/models/app/extension.js index 97a2576f0..fd45b3444 100644 --- a/app/assets/javascripts/app/frontend/models/app/extension.js +++ b/app/assets/javascripts/app/frontend/models/app/extension.js @@ -79,6 +79,7 @@ class Extension extends Item { super.mapContentToLocalProperties(contentObject) this.name = contentObject.name; this.url = contentObject.url; + this.supported_types = contentObject.supported_types; this.actions = contentObject.actions.map(function(action){ return new Action(action); }) @@ -99,7 +100,8 @@ class Extension extends Item { var params = { name: this.name, url: this.url, - actions: this.actions + actions: this.actions, + supported_types: this.supported_types }; _.merge(params, super.structureParams()); diff --git a/app/assets/javascripts/app/services/dbManager.js b/app/assets/javascripts/app/services/dbManager.js index d7e3edabb..90d529ec3 100644 --- a/app/assets/javascripts/app/services/dbManager.js +++ b/app/assets/javascripts/app/services/dbManager.js @@ -102,7 +102,6 @@ class DBManager { this.openDatabase((db) => { var request = db.transaction("items", "readwrite").objectStore("items").delete(item.uuid); request.onsuccess = function(event) { - console.log("Successfully deleted item", item.uuid); if(callback) { callback(true); } diff --git a/app/assets/javascripts/app/services/directives/functional/click-outside.js b/app/assets/javascripts/app/services/directives/functional/click-outside.js new file mode 100644 index 000000000..10e40e1da --- /dev/null +++ b/app/assets/javascripts/app/services/directives/functional/click-outside.js @@ -0,0 +1,27 @@ +angular + .module('app.frontend') + .directive('clickOutside', ['$document', function($document) { + return { + restrict: 'A', + replace: false, + link : function($scope, $element, attrs) { + + var didApplyClickOutside = false; + + $element.bind('click', function(e) { + didApplyClickOutside = false; + if (attrs.isOpen) { + e.stopPropagation(); + } + }); + + $document.bind('click', function() { + if(!didApplyClickOutside) { + $scope.$apply(attrs.clickOutside); + didApplyClickOutside = true; + } + }) + + } + } + }]); diff --git a/app/assets/javascripts/app/services/directives/views/contextualExtensionsMenu.js b/app/assets/javascripts/app/services/directives/views/contextualExtensionsMenu.js index a0ed0e97d..f9a57b1b6 100644 --- a/app/assets/javascripts/app/services/directives/views/contextualExtensionsMenu.js +++ b/app/assets/javascripts/app/services/directives/views/contextualExtensionsMenu.js @@ -11,6 +11,8 @@ class ContextualExtensionsMenu { controller($scope, modelManager, extensionManager) { 'ngInject'; + $scope.renderData = {}; + $scope.extensions = _.map(extensionManager.extensionsInContextOfItem($scope.item), function(ext){ return _.cloneDeep(ext); }); @@ -27,12 +29,30 @@ class ContextualExtensionsMenu { } $scope.executeAction = function(action, extension) { + if(action.verb == "nested") { + action.showNestedActions = !action.showNestedActions; + return; + } action.running = true; extensionManager.executeAction(action, extension, $scope.item, function(response){ action.running = false; + $scope.handleActionResponse(action, response); }) } + $scope.handleActionResponse = function(action, response) { + switch (action.verb) { + case "render": { + var item = response.item; + if(item.content_type == "Note") { + $scope.renderData.title = item.title; + $scope.renderData.text = item.text; + $scope.renderData.showRenderModal = true; + } + } + } + } + $scope.accessTypeForExtension = function(extension) { return extensionManager.extensionUsesEncryptedData(extension) ? "encrypted" : "decrypted"; } diff --git a/app/assets/javascripts/app/services/extensionManager.js b/app/assets/javascripts/app/services/extensionManager.js index d9a0260f6..20b21de82 100644 --- a/app/assets/javascripts/app/services/extensionManager.js +++ b/app/assets/javascripts/app/services/extensionManager.js @@ -28,7 +28,7 @@ class ExtensionManager { extensionsInContextOfItem(item) { return this.extensions.filter(function(ext){ - return ext.actionsWithContextForItem(item).length > 0; + return _.includes(ext.supported_types, item.content_type) || ext.actionsWithContextForItem(item).length > 0; }) } @@ -152,9 +152,29 @@ class ExtensionManager { case "get": { this.Restangular.oneUrl(action.url, action.url).get().then(function(response){ action.error = false; - var items = response.items; - this.modelManager.mapResponseItemsToLocalModels(items); - customCallback(items); + var items = response.items || [response.item]; + EncryptionHelper.decryptMultipleItems(items, localStorage.getItem("mk")); + items = this.modelManager.mapResponseItemsToLocalModels(items); + for(var item of items) { + item.setDirty(true); + } + this.syncManager.sync(null); + customCallback({items: items}); + }.bind(this)) + .catch(function(response){ + action.error = true; + customCallback(null); + }) + + break; + } + + case "render": { + this.Restangular.oneUrl(action.url, action.url).get().then(function(response){ + action.error = false; + EncryptionHelper.decryptItem(response.item, localStorage.getItem("mk")); + var item = this.modelManager.createItem(response.item); + customCallback({item: item}); }.bind(this)) .catch(function(response){ action.error = true; diff --git a/app/assets/javascripts/app/services/modelManager.js b/app/assets/javascripts/app/services/modelManager.js index 8b23060dc..ab8ca7f44 100644 --- a/app/assets/javascripts/app/services/modelManager.js +++ b/app/assets/javascripts/app/services/modelManager.js @@ -64,10 +64,10 @@ class ModelManager { json_obj = _.omit(json_obj, omitFields || []) var item = this.findItem(json_obj["uuid"]); if(json_obj["deleted"] == true || !_.includes(this.acceptableContentTypes, json_obj["content_type"])) { - if(item) { - this.removeItemLocally(item) - } - continue; + if(item) { + this.removeItemLocally(item) + } + continue; } _.omit(json_obj, omitFields); diff --git a/app/assets/stylesheets/app/_directives.scss b/app/assets/stylesheets/app/_directives.scss index 39eb623dc..8d5f28cc5 100644 --- a/app/assets/stylesheets/app/_directives.scss +++ b/app/assets/stylesheets/app/_directives.scss @@ -1,4 +1,33 @@ +.extension-render-modal { + position: fixed; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + top: 0; + bottom: 0; + z-index: 10000; + width: 100vw; + height: 100vh; + background-color: rgba(gray, 0.3); + + .content { + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + background-color: white; + width: 700px; + height: 500px; + margin: auto; + padding: 25px; + position: absolute; + top: 0; left: 0; bottom: 0; right: 0; + overflow-y: scroll; + } +} + .dropdown-menu.contextual-menu { + overflow-y: scroll; + max-height: 85vh; + .extension { &:not(:first-child) { diff --git a/app/assets/stylesheets/app/_editor.scss b/app/assets/stylesheets/app/_editor.scss index 6404713d5..3815f46a0 100644 --- a/app/assets/stylesheets/app/_editor.scss +++ b/app/assets/stylesheets/app/_editor.scss @@ -177,15 +177,6 @@ right: 0; } -.full-screen-button { - cursor: pointer; - position: absolute; - top: 25px; - right: 20px; - z-index: 100; -} - - ol { list-style-type: decimal; list-style-position: inside; @@ -196,8 +187,6 @@ ol { -webkit-padding-start: 0px; } - - .nav-tabs { a { color: black; diff --git a/app/assets/stylesheets/app/_header.scss b/app/assets/stylesheets/app/_header.scss index 5ba6cab79..1df8ef09a 100644 --- a/app/assets/stylesheets/app/_header.scss +++ b/app/assets/stylesheets/app/_header.scss @@ -150,6 +150,11 @@ } } +h2 { + margin-bottom: 0px; + margin-top: 0px; +} + .footer-bar { position: relative; width: 100%; @@ -194,10 +199,6 @@ display: block; } - h2 { - margin-bottom: 0px; - margin-top: 0px; - } h3 { font-size: 14px !important; diff --git a/app/assets/templates/frontend/directives/contextual-menu.html.haml b/app/assets/templates/frontend/directives/contextual-menu.html.haml index c30f5da5e..725b56474 100644 --- a/app/assets/templates/frontend/directives/contextual-menu.html.haml +++ b/app/assets/templates/frontend/directives/contextual-menu.html.haml @@ -10,5 +10,19 @@ %li.action{"ng-repeat" => "action in extension.actionsWithContextForItem(item)", "ng-click" => "executeAction(action, extension)"} .name {{action.label}} .desc {{action.desc}} + + %div{"ng-if" => "action.showNestedActions"} + %ul.mt-10 + %li.action.white-bg{"ng-repeat" => "subaction in action.subactions", "ng-click" => "executeAction(subaction, extension); $event.stopPropagation()", "style" => "margin-top: -1px;"} + .name {{subaction.label}} + .desc {{subaction.desc}} + %span{"ng-if" => "subaction.running"} + .spinner{"style" => "margin-top: 3px;"} + %span{"ng-if" => "action.running"} .spinner{"style" => "margin-top: 3px;"} + +.extension-render-modal{"ng-if" => "renderData.showRenderModal", "ng-click" => "renderData.showRenderModal = false"} + .content + %h2 {{renderData.title}} + %p.normal{"style" => "white-space: pre-wrap; font-family: monospace; font-size: 16px;"} {{renderData.text}} diff --git a/app/assets/templates/frontend/editor.html.haml b/app/assets/templates/frontend/editor.html.haml index af7635754..7b5242531 100644 --- a/app/assets/templates/frontend/editor.html.haml +++ b/app/assets/templates/frontend/editor.html.haml @@ -11,8 +11,8 @@ "ng-model" => "ctrl.tagsString", "placeholder" => "#tags", "ng-blur" => "ctrl.updateTagsFromTagsString($event, ctrl.tagsString)"} .section-menu %ul.nav.nav-pills - %li.dropdown - %a.dropdown-toggle{"ng-click" => "ctrl.clickedMenu(); ctrl.showExtensions = false"} + %li.dropdown{"click-outside" => "ctrl.showMenu = false;", "is-open" => "ctrl.showMenu"} + %a.dropdown-toggle{"ng-click" => "ctrl.showMenu = !ctrl.showMenu; ctrl.showExtensions = false;"} File %span.caret %span.sr-only @@ -27,8 +27,8 @@ %li{"ng-click" => "ctrl.deleteNote()"} .text Delete %li.sep - %li.dropdown{"ng-if" => "ctrl.hasAvailableExtensions()"} - %a.dropdown-toggle{"ng-click" => "ctrl.showExtensions = !ctrl.showExtensions; ctrl.showMenu = false"} + %li.dropdown{"ng-if" => "ctrl.hasAvailableExtensions()", "click-outside" => "ctrl.showExtensions = false;", "is-open" => "ctrl.showExtensions"} + %a.dropdown-toggle{"ng-click" => "ctrl.showExtensions = !ctrl.showExtensions; ctrl.showMenu = false;"} Extensions %span.caret %span.sr-only diff --git a/app/assets/templates/frontend/header.html.haml b/app/assets/templates/frontend/header.html.haml index f385f293c..972825eaf 100644 --- a/app/assets/templates/frontend/header.html.haml +++ b/app/assets/templates/frontend/header.html.haml @@ -1,10 +1,10 @@ .footer-bar .pull-left - .footer-bar-link + .footer-bar-link{"click-outside" => "ctrl.showAccountMenu = false;", "is-open" => "ctrl.showAccountMenu"} %a{"ng-click" => "ctrl.accountMenuPressed()", "ng-class" => "{red: ctrl.error}"} Account %account-menu{"ng-if" => "ctrl.showAccountMenu"} - .footer-bar-link + .footer-bar-link{"click-outside" => "ctrl.showExtensionsMenu = false;", "is-open" => "ctrl.showExtensionsMenu"} %a{"ng-click" => "ctrl.toggleExtensions()"} Extensions %global-extensions-menu{"ng-if" => "ctrl.showExtensionsMenu"} From 979b5eb2a80acfbc049c335647a1c8b3c4dd2122 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Wed, 15 Feb 2017 13:01:19 -0600 Subject: [PATCH 10/40] external editor support --- .../app/frontend/controllers/editor.js | 94 +++--- .../app/frontend/models/app/editor.js | 30 ++ .../app/frontend/models/local/itemParams.js | 2 +- .../services/directives/views/editorMenu.js | 72 +++++ .../app/services/filters/startFrom.js | 1 - .../app/services/filters/trusted.js | 5 + .../javascripts/app/services/modelManager.js | 4 +- app/assets/stylesheets/app/_directives.scss | 99 ------- app/assets/stylesheets/app/_extensions.scss | 111 ++++++++ app/assets/stylesheets/app/_header.scss | 142 --------- app/assets/stylesheets/app/_standard.scss | 269 ++++++++++++++++++ app/assets/stylesheets/frontend.css.scss | 3 +- .../directives/contextual-menu.html.haml | 20 +- .../frontend/directives/editor-menu.html.haml | 23 ++ .../templates/frontend/editor.html.haml | 18 +- 15 files changed, 581 insertions(+), 312 deletions(-) create mode 100644 app/assets/javascripts/app/frontend/models/app/editor.js create mode 100644 app/assets/javascripts/app/services/directives/views/editorMenu.js create mode 100644 app/assets/javascripts/app/services/filters/trusted.js delete mode 100644 app/assets/stylesheets/app/_directives.scss create mode 100644 app/assets/stylesheets/app/_extensions.scss create mode 100644 app/assets/stylesheets/app/_standard.scss create mode 100644 app/assets/templates/frontend/directives/editor-menu.html.haml diff --git a/app/assets/javascripts/app/frontend/controllers/editor.js b/app/assets/javascripts/app/frontend/controllers/editor.js index 8aee7ed7f..dae2f43b2 100644 --- a/app/assets/javascripts/app/frontend/controllers/editor.js +++ b/app/assets/javascripts/app/frontend/controllers/editor.js @@ -16,50 +16,9 @@ angular.module('app.frontend') link:function(scope, elem, attrs, ctrl) { - /** - * 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. - */ - var handleTab = function (event) { - if (!event.shiftKey && event.which == 9) { - event.preventDefault(); - var start = this.selectionStart; - var end = this.selectionEnd; - var spaces = " "; - - // Insert 4 spaces - this.value = this.value.substring(0, start) - + spaces + this.value.substring(end); - - // Place cursor 4 spaces away from where - // the tab key was pressed - this.selectionStart = this.selectionEnd = start + 4; - } - } - var handler = function(event) { if (event.ctrlKey || event.metaKey) { switch (String.fromCharCode(event.which).toLowerCase()) { - case 's': - event.preventDefault(); - $timeout(function(){ - ctrl.saveNote(event); - }); - break; - case 'e': - event.preventDefault(); - $timeout(function(){ - ctrl.clickedEditNote(); - }) - break; - case 'm': - event.preventDefault(); - $timeout(function(){ - ctrl.toggleMarkdown(); - }) - break; case 'o': event.preventDefault(); $timeout(function(){ @@ -70,14 +29,6 @@ angular.module('app.frontend') } }; - window.addEventListener('keydown', handler); - var element = document.getElementById("note-text-editor"); - element.addEventListener('keydown', handleTab); - - scope.$on('$destroy', function(){ - window.removeEventListener('keydown', handler); - }) - scope.$watch('ctrl.note', function(note, oldNote){ if(note) { ctrl.setNote(note, oldNote); @@ -88,7 +39,21 @@ angular.module('app.frontend') } } }) - .controller('EditorCtrl', function ($sce, $timeout, authManager, markdownRenderer, $rootScope, extensionManager, syncManager) { + .controller('EditorCtrl', function ($sce, $timeout, authManager, markdownRenderer, $rootScope, extensionManager, syncManager, modelManager) { + + window.addEventListener("message", function(){ + console.log("App received message:", event); + if(event.data.status) { + this.postNoteToExternalEditor(); + } else { + var id = event.data.id; + var text = event.data.text; + if(this.note.uuid == id) { + this.note.text = text; + this.changesMade(); + } + } + }.bind(this), false); this.setNote = function(note, oldNote) { this.editorMode = 'edit'; @@ -96,6 +61,13 @@ angular.module('app.frontend') this.showMenu = false; this.loadTagsString(); + if(note.editorUrl) { + this.customEditor = this.editorForUrl(note.editorUrl); + this.postNoteToExternalEditor(); + } else { + this.customEditor = null; + } + if(note.safeText().length == 0 && note.dummy) { this.focusTitle(100); } @@ -109,6 +81,28 @@ angular.module('app.frontend') } } + this.selectedEditor = function(editor) { + this.showEditorMenu = false; + if(editor.default) { + this.customEditor = null; + } else { + this.customEditor = editor; + } + this.note.editorUrl = editor.url; + }.bind(this) + + this.editorForUrl = function(url) { + var editors = modelManager.itemsForContentType("SN|Editor"); + return editors.filter(function(editor){return editor.url == url})[0]; + } + + this.postNoteToExternalEditor = function() { + var externalEditorElement = document.getElementById("editor-iframe"); + if(externalEditorElement) { + externalEditorElement.contentWindow.postMessage({text: this.note.text, id: this.note.uuid}, '*'); + } + } + this.hasAvailableExtensions = function() { return extensionManager.extensionsInContextOfItem(this.note).length > 0; } diff --git a/app/assets/javascripts/app/frontend/models/app/editor.js b/app/assets/javascripts/app/frontend/models/app/editor.js new file mode 100644 index 000000000..2b823d3a5 --- /dev/null +++ b/app/assets/javascripts/app/frontend/models/app/editor.js @@ -0,0 +1,30 @@ +class Editor extends Item { + + constructor(json_obj) { + super(json_obj); + } + + mapContentToLocalProperties(contentObject) { + super.mapContentToLocalProperties(contentObject) + this.url = contentObject.url; + this.name = contentObject.name; + } + + structureParams() { + var params = { + url: this.url, + name: this.name + }; + + _.merge(params, super.structureParams()); + return params; + } + + toJSON() { + return {uuid: this.uuid} + } + + get content_type() { + return "SN|Editor"; + } +} diff --git a/app/assets/javascripts/app/frontend/models/local/itemParams.js b/app/assets/javascripts/app/frontend/models/local/itemParams.js index 396a51bd1..22d7405ce 100644 --- a/app/assets/javascripts/app/frontend/models/local/itemParams.js +++ b/app/assets/javascripts/app/frontend/models/local/itemParams.js @@ -16,7 +16,7 @@ class ItemParams { } paramsForLocalStorage() { - this.additionalFields = ["updated_at", "dirty"]; + this.additionalFields = ["updated_at", "dirty", "editorUrl"]; this.forExportFile = true; return this.__params(); } diff --git a/app/assets/javascripts/app/services/directives/views/editorMenu.js b/app/assets/javascripts/app/services/directives/views/editorMenu.js new file mode 100644 index 000000000..82d260cb0 --- /dev/null +++ b/app/assets/javascripts/app/services/directives/views/editorMenu.js @@ -0,0 +1,72 @@ +class EditorMenu { + + constructor() { + this.restrict = "E"; + this.templateUrl = "frontend/directives/editor-menu.html"; + this.scope = { + callback: "&", + selectedEditor: "=" + }; + } + + controller($scope, modelManager, extensionManager, syncManager) { + 'ngInject'; + + $scope.formData = {}; + + let editorContentType = "SN|Editor"; + + let defaultEditor = { + default: true, + name: "Plain" + } + + $scope.sysEditors = [defaultEditor]; + $scope.editors = modelManager.itemsForContentType(editorContentType); + + $scope.editorForUrl = function(url) { + return $scope.editors.filter(function(editor){return editor.url == url})[0]; + } + + $scope.selectEditor = function(editor) { + $scope.callback()(editor); + } + + $scope.deleteEditor = function(editor) { + if(confirm("Are you sure you want to delete this editor?")) { + modelManager.setItemToBeDeleted(editor); + syncManager.sync(); + } + } + + $scope.submitNewEditorRequest = function() { + var editor = createEditor($scope.formData.url); + modelManager.addItem(editor); + editor.setDirty(true); + syncManager.sync(); + $scope.editors.push(editor); + $scope.formData = {}; + } + + function createEditor(url) { + var name = getParameterByName("name", url); + return modelManager.createItem({ + content_type: editorContentType, + url: url, + name: name + }) + } + + function getParameterByName(name, url) { + name = name.replace(/[\[\]]/g, "\\$&"); + var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), + results = regex.exec(url); + if (!results) return null; + if (!results[2]) return ''; + return decodeURIComponent(results[2].replace(/\+/g, " ")); + } + } + +} + +angular.module('app.frontend').directive('editorMenu', () => new EditorMenu); diff --git a/app/assets/javascripts/app/services/filters/startFrom.js b/app/assets/javascripts/app/services/filters/startFrom.js index 2ebd72e42..3cf824c4c 100644 --- a/app/assets/javascripts/app/services/filters/startFrom.js +++ b/app/assets/javascripts/app/services/filters/startFrom.js @@ -1,4 +1,3 @@ -// Start from filter angular.module('app.frontend').filter('startFrom', function() { return function(input, start) { return input.slice(start); diff --git a/app/assets/javascripts/app/services/filters/trusted.js b/app/assets/javascripts/app/services/filters/trusted.js new file mode 100644 index 000000000..7bb57b301 --- /dev/null +++ b/app/assets/javascripts/app/services/filters/trusted.js @@ -0,0 +1,5 @@ +angular.module('app.frontend').filter('trusted', ['$sce', function ($sce) { + return function(url) { + return $sce.trustAsResourceUrl(url); + }; +}]); diff --git a/app/assets/javascripts/app/services/modelManager.js b/app/assets/javascripts/app/services/modelManager.js index ab8ca7f44..9a26cfa14 100644 --- a/app/assets/javascripts/app/services/modelManager.js +++ b/app/assets/javascripts/app/services/modelManager.js @@ -8,7 +8,7 @@ class ModelManager { this.itemChangeObservers = []; this.items = []; this._extensions = []; - this.acceptableContentTypes = ["Note", "Tag", "Extension"]; + this.acceptableContentTypes = ["Note", "Tag", "Extension", "SN|Editor"]; } get allItems() { @@ -121,6 +121,8 @@ class ModelManager { item = new Tag(json_obj); } else if(json_obj.content_type == "Extension") { item = new Extension(json_obj); + } else if(json_obj.content_type == "SN|Editor") { + item = new Editor(json_obj); } else { item = new Item(json_obj); } diff --git a/app/assets/stylesheets/app/_directives.scss b/app/assets/stylesheets/app/_directives.scss deleted file mode 100644 index 8d5f28cc5..000000000 --- a/app/assets/stylesheets/app/_directives.scss +++ /dev/null @@ -1,99 +0,0 @@ -.extension-render-modal { - position: fixed; - margin-left: auto; - margin-right: auto; - left: 0; - right: 0; - top: 0; - bottom: 0; - z-index: 10000; - width: 100vw; - height: 100vh; - background-color: rgba(gray, 0.3); - - .content { - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); - background-color: white; - width: 700px; - height: 500px; - margin: auto; - padding: 25px; - position: absolute; - top: 0; left: 0; bottom: 0; right: 0; - overflow-y: scroll; - } -} - -.dropdown-menu.contextual-menu { - overflow-y: scroll; - max-height: 85vh; - - .extension { - - &:not(:first-child) { - margin-top: 18px; - } - - .ext-header { - background-color: #ededed; - border-bottom: 1px solid #d3d3d3; - padding-top: 12px; - padding-left: 10px; - padding-bottom: 10px; - position: relative; - - > .name { - font-size: 14px; - } - - > .access { - font-size: 12px; - opacity: 0.5; - font-weight: normal; - margin-top: 2px; - } - - > .loading { - position: absolute; - height: 15px; - width: 15px; - right: 10px; - top: 20px; - } - } - - ul { - margin-top: 0px; - margin-bottom: 0px; - list-style:none; - padding-left:0; - - li { - cursor: pointer; - height: auto; - - &.action { - padding: 10px; - border-bottom: 1px solid rgba(black, 0.1); - background-color: rgba(white, 0.9); - - &:hover { - background-color: rgba(gray, 0.05); - } - - > .name { - font-weight: bold; - font-size: 14px; - } - - > .desc { - font-weight: normal; - opacity: 0.5; - margin-top: 1px; - font-size: 12px; - } - } - } - } - } -} diff --git a/app/assets/stylesheets/app/_extensions.scss b/app/assets/stylesheets/app/_extensions.scss new file mode 100644 index 000000000..1958abff2 --- /dev/null +++ b/app/assets/stylesheets/app/_extensions.scss @@ -0,0 +1,111 @@ +.extension-render-modal { + position: fixed; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + top: 0; + bottom: 0; + z-index: 10000; + width: 100vw; + height: 100vh; + background-color: rgba(gray, 0.3); + + .content { + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + background-color: white; + width: 700px; + height: 500px; + margin: auto; + padding: 25px; + position: absolute; + top: 0; left: 0; bottom: 0; right: 0; + overflow-y: scroll; + } +} + +.menu-section-footer { + background-color: #ededed; + border-top: 1px solid #d3d3d3; + position: relative; + padding: 10px; +} + +.menu-section-header { + background-color: #ededed; + border-bottom: 1px solid #d3d3d3; + position: relative; + padding-top: 12px; + padding-left: 10px; + padding-bottom: 10px; + + + > .title { + font-size: 14px; + } + + > .subtitle { + font-size: 12px; + opacity: 0.5; + font-weight: normal; + margin-top: 2px; + } + + > .loading { + position: absolute; + height: 15px; + width: 15px; + right: 10px; + top: 20px; + } +} + +.dropdown-menu.editor-menu { + overflow-y: scroll; + max-height: 85vh; + + &:not(:first-child) { + margin-top: 18px; + } + + ul { + margin-top: 0px; + margin-bottom: 0px; + list-style:none; + padding-left:0; + + li { + cursor: pointer; + height: auto; + + &.menu-item { + padding: 10px; + border-bottom: 1px solid rgba(black, 0.1); + background-color: rgba(white, 0.9); + + &:hover { + background-color: rgba(gray, 0.05); + } + + &.nonactive { + cursor: default; + &:hover { + background-color: rgba(white, 0.9) !important; + } + } + + .menu-item-title { + font-weight: bold; + font-size: 14px; + } + + .menu-item-subtitle { + font-weight: normal; + opacity: 0.5; + margin-top: 1px; + font-size: 12px; + } + } + } + } +} diff --git a/app/assets/stylesheets/app/_header.scss b/app/assets/stylesheets/app/_header.scss index 1df8ef09a..fdeb83a11 100644 --- a/app/assets/stylesheets/app/_header.scss +++ b/app/assets/stylesheets/app/_header.scss @@ -1,145 +1,3 @@ -.pull-left { - float: left !important; -} - -.pull-right { - float: right !important; -} - -.mt-1 { - margin-top: 1px !important; -} - -.mt-2 { - margin-top: 2px !important; -} - -.mt-5 { - margin-top: 5px !important; -} - -.mt-10 { - margin-top: 10px !important; -} - -.mt-15 { - margin-top: 15px !important; -} - -.mt-25 { - margin-top: 25px !important; -} - -.mb-0 { - margin-bottom: 0px !important; -} - -.mb-5 { - margin-bottom: 5px !important; -} - -.mb-10 { - margin-bottom: 10px !important; -} - -.mr-5 { - margin-right: 5px; -} - -.faded { - opacity: 0.5; -} - -.center-align { - text-align: center !important; -} - -.center { - margin-left: auto !important; - margin-right: auto !important; -} - -.block { - display: block !important; -} - -.wrap { - word-wrap: break-word; -} - -.one-line-overflow { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; -} - -.small-v-space { - height: 6px; - display: block; -} - -.medium-v-space { - height: 12px; - display: block; -} - -.large-v-space { - height: 24px; - display: block; -} - -.small-padding { - padding: 5px !important; -} - -.medium-padding { - padding: 10px !important; -} - -.pb-4 { - padding-bottom: 4px !important; -} - -.pb-6 { - padding-bottom: 6px !important; -} - -.pb-10 { - padding-bottom: 10px !important; -} - -.large-padding { - padding: 22px !important; -} - -.red { - color: red !important; -} - -.blue { - color: $blue-color; -} - -.bold { - font-weight: bold !important; -} - -.italic { - font-style: italic !important; -} - -.normal { - font-weight: normal !important; -} - -.small { - font-size: 10px !important; -} - -.inline { - display: inline-block; -} - .fake-link { font-weight: bold; cursor: pointer; diff --git a/app/assets/stylesheets/app/_standard.scss b/app/assets/stylesheets/app/_standard.scss new file mode 100644 index 000000000..bf5e098b1 --- /dev/null +++ b/app/assets/stylesheets/app/_standard.scss @@ -0,0 +1,269 @@ +.pull-left { + float: left !important; +} + +.pull-right { + float: right !important; +} + +.mt-1 { + margin-top: 1px !important; +} + +.mt-2 { + margin-top: 2px !important; +} + +.mt-5 { + margin-top: 5px !important; +} + +.mt-10 { + margin-top: 10px !important; +} + +.mt-15 { + margin-top: 15px !important; +} + +.mt-20 { + margin-top: 20px !important; +} + +.mt-25 { + margin-top: 25px !important; +} + +.mt-50 { + margin-top: 50px !important; +} + +.mt-100 { + margin-top: 100px !important; +} + +.mb-0 { + margin-bottom: 0px !important; +} + +.mb-5 { + margin-bottom: 5px !important; +} + +.mb-10 { + margin-bottom: 10px !important; +} + +.mr-5 { + margin-right: 5px; +} + +.mr-10 { + margin-right: 10px; +} + +.mr-20 { + margin-right: 20px; +} + +.pb-0 { + padding-bottom: 0px !important; +} + +.faded { + opacity: 0.5; +} + +.center-align { + text-align: center !important; +} + +.center { + margin-left: auto !important; + margin-right: auto !important; +} + +.block { + display: block !important; +} + +.wrap { + word-wrap: break-word; +} + +.one-line-overflow { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +.small-v-space { + height: 6px; + display: block; +} + +.medium-v-space { + height: 12px; + display: block; +} + +.large-v-space { + height: 24px; + display: block; +} + +.small-padding { + padding: 5px !important; +} + +.medium-padding { + padding: 10px !important; +} + +.pb-4 { + padding-bottom: 4px !important; +} + +.pb-6 { + padding-bottom: 6px !important; +} + +.pb-10 { + padding-bottom: 10px !important; +} + +.large-padding { + padding: 22px !important; +} + +.red { + color: red !important; +} + +.bold { + font-weight: bold !important; +} + +.normal { + font-weight: normal !important; +} + +.small { + font-size: 10px !important; +} + +.medium { + font-size: 14px !important; +} + +.inline { + display: inline-block; + + &.top { + vertical-align: top; + } + + &.middle { + vertical-align: middle; + } +} + +button { + cursor: pointer; +} + +input.form-control { + margin-bottom: 10px; + border-radius: 0px; + min-height: 39px; + font-size: 14px; + padding-left: 6px; +} + +button { + border: none; + + @mixin wide-button() { + font-weight: bold; + text-align: center; + padding: 10px; + font-size: 16px; + // min-width: 200px; + + &:hover { + text-decoration: underline; + } + } + + &.black { + @include wide-button(); + background-color: black; + color: white; + } + + &.white { + @include wide-button(); + background-color: white; + color: black; + border: 1px solid rgba(gray, 0.2); + } +} + + +.gray-bg { + background-color: #f6f6f6; + border: 1px solid #f2f2f2; +} + +.white-bg { + background-color: white; + border: 1px solid rgba(gray, 0.2); +} + +.col-container { + // white-space: nowrap; +} + +@mixin col() { + display: inline-block; + vertical-align: top; + white-space: normal; +} + +.col-10 { + width: 10%; + @include col(); +} + +.col-15 { + width: 15%; + @include col(); +} + +.col-20 { + width: 20%; + @include col(); +} + +.col-45 { + width: 45%; + @include col(); +} + +.col-50 { + width: 50%; + @include col(); +} + +.col-80 { + width: 80%; + @include col(); +} + +.relative { + position: relative !important; +} + +.absolute { + position: absolute !important; +} diff --git a/app/assets/stylesheets/frontend.css.scss b/app/assets/stylesheets/frontend.css.scss index bfe290297..026702b13 100644 --- a/app/assets/stylesheets/frontend.css.scss +++ b/app/assets/stylesheets/frontend.css.scss @@ -1,5 +1,6 @@ $dark-gray: #2e2e2e; +@import "app/standard"; @import "app/mostrap"; @import "app/main"; @import "app/common"; @@ -7,7 +8,7 @@ $dark-gray: #2e2e2e; @import "app/tags"; @import "app/notes"; @import "app/editor"; -@import "app/directives"; +@import "app/extensions"; @font-face { font-family: 'icomoon'; diff --git a/app/assets/templates/frontend/directives/contextual-menu.html.haml b/app/assets/templates/frontend/directives/contextual-menu.html.haml index 725b56474..6fbfaa9aa 100644 --- a/app/assets/templates/frontend/directives/contextual-menu.html.haml +++ b/app/assets/templates/frontend/directives/contextual-menu.html.haml @@ -1,21 +1,21 @@ -%ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark.contextual-menu +%ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark.editor-menu .extension{"ng-repeat" => "extension in extensions"} - .ext-header - .name {{extension.name}} - .access + .menu-section-header + .title {{extension.name}} + .subtitle Can access your data %strong {{accessTypeForExtension(extension)}} .spinner.loading{"ng-if" => "extension.loading"} %ul - %li.action{"ng-repeat" => "action in extension.actionsWithContextForItem(item)", "ng-click" => "executeAction(action, extension)"} - .name {{action.label}} - .desc {{action.desc}} + %li.menu-item{"ng-repeat" => "action in extension.actionsWithContextForItem(item)", "ng-click" => "executeAction(action, extension)"} + .menu-item-title {{action.label}} + .menu-item-subtitle {{action.desc}} %div{"ng-if" => "action.showNestedActions"} %ul.mt-10 - %li.action.white-bg{"ng-repeat" => "subaction in action.subactions", "ng-click" => "executeAction(subaction, extension); $event.stopPropagation()", "style" => "margin-top: -1px;"} - .name {{subaction.label}} - .desc {{subaction.desc}} + %li.menu-item.white-bg{"ng-repeat" => "subaction in action.subactions", "ng-click" => "executeAction(subaction, extension); $event.stopPropagation()", "style" => "margin-top: -1px;"} + .menu-item-title {{subaction.label}} + .menu-item-subtitle {{subaction.desc}} %span{"ng-if" => "subaction.running"} .spinner{"style" => "margin-top: 3px;"} diff --git a/app/assets/templates/frontend/directives/editor-menu.html.haml b/app/assets/templates/frontend/directives/editor-menu.html.haml new file mode 100644 index 000000000..f7509a45c --- /dev/null +++ b/app/assets/templates/frontend/directives/editor-menu.html.haml @@ -0,0 +1,23 @@ +%ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark.editor-menu + .menu-section-header + .title System Editors + %ul + %li.menu-item{"ng-repeat" => "editor in sysEditors", "ng-click" => "selectEditor(editor)"} + %span.pull-left.mr-10{"ng-if" => "!selectedEditor"} ✓ + .menu-item-title.pull-left {{editor.name}} + + %div{"ng-if" => "editors.length > 0"} + .menu-section-header + .title External Editors + .subtitle Can access your current note decrypted. + %ul + %li.menu-item{"ng-repeat" => "editor in editors", "ng-click" => "selectEditor(editor)"} + %span.pull-left.mr-10{"ng-if" => "selectedEditor == editor"} ✓ + .pull-left{"style" => "width: 60%"} + .menu-item-title {{editor.name}} + .menu-item-subtitle.wrap {{editor.url}} + .pull-right + %button.white.medium.inline.top{"style" => "width: 50px; height: 40px;", "ng-click" => "deleteEditor(editor); $event.stopPropagation();"} ☓ + .menu-section-footer.mt-10 + %input.form-control{"ng-model" => "formData.url", "placeholder" => "Add new editor via URL", "ng-keyup" => "$event.keyCode == 13 && submitNewEditorRequest()"} + %a.block{"href" => ""} Available Editors diff --git a/app/assets/templates/frontend/editor.html.haml b/app/assets/templates/frontend/editor.html.haml index 7b5242531..24d719660 100644 --- a/app/assets/templates/frontend/editor.html.haml +++ b/app/assets/templates/frontend/editor.html.haml @@ -26,6 +26,15 @@ .shortcut Cmd + M %li{"ng-click" => "ctrl.deleteNote()"} .text Delete + + %li.sep + %li.dropdown{"click-outside" => "ctrl.showEditorMenu = false;", "is-open" => "ctrl.showEditorMenu"} + %a.dropdown-toggle{"ng-click" => "ctrl.showEditorMenu = !ctrl.showEditorMenu; ctrl.showMenu = false;"} + Editor + %span.caret + %span.sr-only + %editor-menu{"ng-if" => "ctrl.showEditorMenu", "callback" => "ctrl.selectedEditor", "selected-editor" => "ctrl.customEditor"} + %li.sep %li.dropdown{"ng-if" => "ctrl.hasAvailableExtensions()", "click-outside" => "ctrl.showExtensions = false;", "is-open" => "ctrl.showExtensions"} %a.dropdown-toggle{"ng-click" => "ctrl.showExtensions = !ctrl.showExtensions; ctrl.showMenu = false;"} @@ -34,13 +43,8 @@ %span.sr-only %contextual-extensions-menu{"ng-if" => "ctrl.showExtensions", "item" => "ctrl.note"} - .markdown.icon{"ng-if" => "ctrl.editorMode == 'preview'", "ng-click" => "ctrl.showMarkdown = !ctrl.showMarkdown"} - .icon-markdown - .panel.panel-default.info-panel{"ng-if" => "ctrl.showMarkdown"} - .panel-body{"style" => "text-align: center; color: black;"} - This editor is Markdown enabled. - .editor-content{"ng-class" => "{'fullscreen' : ctrl.fullscreen }"} - %textarea.editable#note-text-editor{"ng-class" => "{'fullscreen' : ctrl.fullscreen }", "ng-show" => "ctrl.editorMode == 'edit'", "ng-model" => "ctrl.note.text", + %iframe#editor-iframe{"ng-if" => "ctrl.customEditor", "ng-src" => "{{ctrl.customEditor.url | trusted}}", "frameBorder" => "0", "style" => "width: 100%; height: 100%; z-index: 1000; float: left;"} + %textarea.editable#note-text-editor{"ng-if" => "!ctrl.customEditor", "ng-class" => "{'fullscreen' : ctrl.fullscreen }", "ng-show" => "ctrl.editorMode == 'edit'", "ng-model" => "ctrl.note.text", "ng-change" => "ctrl.contentChanged()", "ng-click" => "ctrl.clickedTextArea()", "ng-focus" => "ctrl.onContentFocus()"} .preview{"ng-class" => "{'fullscreen' : ctrl.fullscreen }", "ng-if" => "ctrl.editorMode == 'preview'", "ng-bind-html" => "ctrl.renderedContent()", "ng-dblclick" => "ctrl.onPreviewDoubleClick()"} From 8f077245fa68db499d1426abf81af9b71213f81a Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Wed, 15 Feb 2017 13:20:17 -0600 Subject: [PATCH 11/40] remove markdown refs, cleanup --- Gruntfile.js | 1 - .../app/frontend/controllers/editor.js | 43 +++------ .../app/services/markdownRenderer.js | 21 ----- app/assets/stylesheets/app/_editor.scss | 87 ++++--------------- app/assets/stylesheets/frontend.css.scss | 4 - .../templates/frontend/editor.html.haml | 6 +- bower.json | 3 +- 7 files changed, 28 insertions(+), 137 deletions(-) delete mode 100644 app/assets/javascripts/app/services/markdownRenderer.js diff --git a/Gruntfile.js b/Gruntfile.js index 01020118a..197c493ce 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -86,7 +86,6 @@ module.exports = function(grunt) { 'vendor/assets/bower_components/angular-ui-router/release/angular-ui-router.js', 'vendor/assets/bower_components/lodash/dist/lodash.min.js', 'vendor/assets/bower_components/restangular/dist/restangular.js', - 'vendor/assets/bower_components/marked/lib/marked.js', 'vendor/assets/javascripts/crypto/*.js' ], dest: 'vendor/assets/javascripts/lib.js', diff --git a/app/assets/javascripts/app/frontend/controllers/editor.js b/app/assets/javascripts/app/frontend/controllers/editor.js index dae2f43b2..20e1ab420 100644 --- a/app/assets/javascripts/app/frontend/controllers/editor.js +++ b/app/assets/javascripts/app/frontend/controllers/editor.js @@ -29,6 +29,11 @@ angular.module('app.frontend') } }; + window.addEventListener('keydown', handler); + scope.$on('$destroy', function(){ + window.removeEventListener('keydown', handler); + }) + scope.$watch('ctrl.note', function(note, oldNote){ if(note) { ctrl.setNote(note, oldNote); @@ -39,7 +44,7 @@ angular.module('app.frontend') } } }) - .controller('EditorCtrl', function ($sce, $timeout, authManager, markdownRenderer, $rootScope, extensionManager, syncManager, modelManager) { + .controller('EditorCtrl', function ($sce, $timeout, authManager, $rootScope, extensionManager, syncManager, modelManager) { window.addEventListener("message", function(){ console.log("App received message:", event); @@ -56,7 +61,6 @@ angular.module('app.frontend') }.bind(this), false); this.setNote = function(note, oldNote) { - this.editorMode = 'edit'; this.showExtensions = false; this.showMenu = false; this.loadTagsString(); @@ -107,15 +111,12 @@ angular.module('app.frontend') return extensionManager.extensionsInContextOfItem(this.note).length > 0; } - this.onPreviewDoubleClick = function() { - this.editorMode = 'edit'; - this.focusEditor(100); - } - this.focusEditor = function(delay) { setTimeout(function(){ var element = document.getElementById("note-text-editor"); - element.focus(); + if(element) { + element.focus(); + } }, delay) } @@ -129,10 +130,6 @@ angular.module('app.frontend') this.showMenu = false; } - this.renderedContent = function() { - return markdownRenderer.renderHtml(markdownRenderer.renderedContentForText(this.note.safeText())); - } - var statusTimeout; this.saveNote = function($event) { @@ -192,7 +189,6 @@ angular.module('app.frontend') } this.onContentFocus = function() { - this.showSampler = false; $rootScope.$broadcast("editorFocused"); } @@ -203,12 +199,7 @@ angular.module('app.frontend') this.toggleFullScreen = function() { this.fullscreen = !this.fullscreen; if(this.fullscreen) { - if(this.editorMode == 'edit') { - // refocus - this.focusEditor(0); - } - } else { - + this.focusEditor(0); } } @@ -216,15 +207,6 @@ angular.module('app.frontend') this.showMenu = false; } - this.toggleMarkdown = function() { - if(this.editorMode == 'preview') { - this.editorMode = 'edit'; - this.focusEditor(0); - } else { - this.editorMode = 'preview'; - } - } - this.deleteNote = function() { if(confirm("Are you sure you want to delete this note?")) { this.remove()(this.note); @@ -232,11 +214,6 @@ angular.module('app.frontend') } } - this.clickedEditNote = function() { - this.editorMode = 'edit'; - this.focusEditor(100); - } - /* Tags */ this.loadTagsString = function() { diff --git a/app/assets/javascripts/app/services/markdownRenderer.js b/app/assets/javascripts/app/services/markdownRenderer.js deleted file mode 100644 index b6fd7a749..000000000 --- a/app/assets/javascripts/app/services/markdownRenderer.js +++ /dev/null @@ -1,21 +0,0 @@ -angular.module('app.frontend') - .service('markdownRenderer', function ($sce) { - - marked.setOptions({ - breaks: true, - sanitize: true - }); - - this.renderedContentForText = function(text) { - if(!text || text.length == 0) { - return ""; - } - return marked(text); - } - - this.renderHtml = function(html_code) { - return $sce.trustAsHtml(html_code); - }; - - - }); diff --git a/app/assets/stylesheets/app/_editor.scss b/app/assets/stylesheets/app/_editor.scss index 3815f46a0..c122eac3a 100644 --- a/app/assets/stylesheets/app/_editor.scss +++ b/app/assets/stylesheets/app/_editor.scss @@ -13,19 +13,17 @@ .section-title-bar { border-bottom: none !important; - &.fullscreen { - opacity: 0.0; - -webkit-transition: all 300ms ease-in-out; - -moz-transition: all 300ms ease-in-out; - -ms-transition: all 300ms ease-in-out; - -o-transition: all 300ms ease-in-out; - transition: all 300ms ease-in-out; - - &:hover { - opacity: 1.0; - } - - // z-index: -1; + // &.fullscreen { + // opacity: 0.0; + // -webkit-transition: all 300ms ease-in-out; + // -moz-transition: all 300ms ease-in-out; + // -ms-transition: all 300ms ease-in-out; + // -o-transition: all 300ms ease-in-out; + // transition: all 300ms ease-in-out; + // + // &:hover { + // opacity: 1.0; + // } } } @@ -44,6 +42,10 @@ width: 100%; padding-right: 10px; + &.fullscreen { + position: relative; + } + > .title { font-size: 18px; font-weight: bold; @@ -93,15 +95,6 @@ } } - .fullscreen-ghost-bar { - - position: absolute; - width: 20%; - height: 200px; - z-index: 100; - } - - .editor-content { max-height: 100%; @@ -119,19 +112,6 @@ padding-top: 0px; } - .sampler-container { - margin-top: 10px; - padding: 15px; - padding-top: 17px; - font-size: 17px; - // opacity: 0.5; - } - - .sampler { - // opacity: 0.5; - color: rgba(black, 0.3); - } - .editable { font-family: monospace; max-height: 100%; @@ -147,44 +127,9 @@ &.fullscreen { padding: 85px 10%; max-width: 1200px; - display: inline-block;; + display: inline-block; } } - - .preview { - // font-family: monospace; - max-height: 100%; - height: 100%; - line-height: 23px; - overflow-y: scroll; - padding: 0px 15px; - text-align: left; - - &.fullscreen { - padding: 85px 10%; - max-width: 1200px; - display: inline-block;; - } - } - } -} - - -.markdown { - margin-left: 15px; - float: right; - text-align: right; - right: 0; -} - -ol { - list-style-type: decimal; - list-style-position: inside; - -webkit-margin-before: 1em; - -webkit-margin-after: 1em; - -webkit-margin-start: 0px; - -webkit-margin-end: 0px; - -webkit-padding-start: 0px; } .nav-tabs { diff --git a/app/assets/stylesheets/frontend.css.scss b/app/assets/stylesheets/frontend.css.scss index 026702b13..e096e7f32 100644 --- a/app/assets/stylesheets/frontend.css.scss +++ b/app/assets/stylesheets/frontend.css.scss @@ -42,7 +42,3 @@ $dark-gray: #2e2e2e; display: inline-block; margin-left: 2px; } - -.icon-markdown:before { - content: "\e901"; -} diff --git a/app/assets/templates/frontend/editor.html.haml b/app/assets/templates/frontend/editor.html.haml index 24d719660..2556b403c 100644 --- a/app/assets/templates/frontend/editor.html.haml +++ b/app/assets/templates/frontend/editor.html.haml @@ -21,9 +21,6 @@ %li{"ng-click" => "ctrl.selectedMenuItem(); ctrl.toggleFullScreen()"} .text Toggle Fullscreen .shortcut Cmd + O - %li{"ng-click" => "ctrl.selectedMenuItem(); ctrl.toggleMarkdown()"} - .text Toggle Markdown Preview - .shortcut Cmd + M %li{"ng-click" => "ctrl.deleteNote()"} .text Delete @@ -45,6 +42,5 @@ .editor-content{"ng-class" => "{'fullscreen' : ctrl.fullscreen }"} %iframe#editor-iframe{"ng-if" => "ctrl.customEditor", "ng-src" => "{{ctrl.customEditor.url | trusted}}", "frameBorder" => "0", "style" => "width: 100%; height: 100%; z-index: 1000; float: left;"} - %textarea.editable#note-text-editor{"ng-if" => "!ctrl.customEditor", "ng-class" => "{'fullscreen' : ctrl.fullscreen }", "ng-show" => "ctrl.editorMode == 'edit'", "ng-model" => "ctrl.note.text", + %textarea.editable#note-text-editor{"ng-if" => "!ctrl.customEditor", "ng-class" => "{'fullscreen' : ctrl.fullscreen }", "ng-model" => "ctrl.note.text", "ng-change" => "ctrl.contentChanged()", "ng-click" => "ctrl.clickedTextArea()", "ng-focus" => "ctrl.onContentFocus()"} - .preview{"ng-class" => "{'fullscreen' : ctrl.fullscreen }", "ng-if" => "ctrl.editorMode == 'preview'", "ng-bind-html" => "ctrl.renderedContent()", "ng-dblclick" => "ctrl.onPreviewDoubleClick()"} diff --git a/bower.json b/bower.json index dd0074806..fa8d65b8f 100644 --- a/bower.json +++ b/bower.json @@ -9,8 +9,7 @@ "dependencies": { "angular": "1.6.1", "angular-ui-router": "^0.3.2", - "restangular": "^1.6.1", - "marked": "0.3.6" + "restangular": "^1.6.1" }, "resolutions": { "angular": "1.6.1" From f29d0bd6188a4661afe1a23a741ece53197da6ed Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Wed, 15 Feb 2017 13:23:45 -0600 Subject: [PATCH 12/40] remove icomoon --- app/assets/stylesheets/frontend.css.scss | 33 ----------------------- vendor/assets/fonts/icomoon/icomoon.eot | Bin 2264 -> 0 bytes vendor/assets/fonts/icomoon/icomoon.svg | 15 ----------- vendor/assets/fonts/icomoon/icomoon.ttf | Bin 2100 -> 0 bytes vendor/assets/fonts/icomoon/icomoon.woff | Bin 2176 -> 0 bytes 5 files changed, 48 deletions(-) delete mode 100755 vendor/assets/fonts/icomoon/icomoon.eot delete mode 100755 vendor/assets/fonts/icomoon/icomoon.svg delete mode 100755 vendor/assets/fonts/icomoon/icomoon.ttf delete mode 100755 vendor/assets/fonts/icomoon/icomoon.woff diff --git a/app/assets/stylesheets/frontend.css.scss b/app/assets/stylesheets/frontend.css.scss index e096e7f32..904cb614a 100644 --- a/app/assets/stylesheets/frontend.css.scss +++ b/app/assets/stylesheets/frontend.css.scss @@ -9,36 +9,3 @@ $dark-gray: #2e2e2e; @import "app/notes"; @import "app/editor"; @import "app/extensions"; - -@font-face { - font-family: 'icomoon'; - src: url('icomoon/icomoon.eot'); - src: url('icomoon/icomoon.eot') format('embedded-opentype'), - url('icomoon/icomoon.ttf') format('truetype'), - url('icomoon/icomoon.woff') format('woff'), - url('icomoon/icomoon.svg') format('svg'); - font-weight: normal; - font-style: normal; -} - -[class^="icon-"], [class*=" icon-"] { - /* use !important to prevent issues with browser extensions that change fonts */ - font-family: 'icomoon' !important; - speak: none; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; - // line-height: 1; - - /* Better Font Rendering =========== */ - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - - line-height: 10px; -} - -.inline-icon { - display: inline-block; - margin-left: 2px; -} diff --git a/vendor/assets/fonts/icomoon/icomoon.eot b/vendor/assets/fonts/icomoon/icomoon.eot deleted file mode 100755 index b972d618a5874c864eb258903ac24d321fd62a6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2264 zcmaJ@O=ufO6n?X-AO9f#R4WOqN-SHl#X=`L`2n__Ximl2f`m-&8 zjq9?frhgD0dNCxe59z5;Fom32y6vr@J=K)lYAA&`ZFB3X`sp{TmA!JDjF>my``&x= zW_F)u_E85>9G#Is?Tyh61D>bQR1Rt;3i5_~{}=X?V~&8c!{Z#d=?|uY&m&U0KYhO9zEt6*&2z z=d$Vb62?C0A7bpt<~HBm7XF-pzC&brV>y#v>i+G1g~<8{_Q_=s_8-NEL^i}wa5-Pz z@B%=Ob3IpFOp8la8}xUeOZoIhiM;eP^c-}5A)U|czS;R7^iPrFgHmz5jCG=QNZd}M z_!Tl+iG*DNQFNy{vcE`Xo!rmV7+*qLOA;mRSxoJn^E!w}bNvY==Z zuj-B`-Y@3(7LyhdlX#;Z3eXr`qStAGZqO~-qEBd-zNCHnj();|W0Mo{P+-{RlH3Us z4x6xS!d4R=F=6|2@Q6Nha|Y)Slzz!A55c)V9ug&6U}!i5I0?$_61m+l$3x5%=`msR z%B&`y?KzxI2YTKZ7mV>2W32yU0M-I6kJiW>tcgFVb&m+sxJuqv*7ubXMMGnGoz5VrY}l>tSLN?ps%RzKlVws(D6ja@yDQ9 z(JE?$P-`#KFe>5H+hKtTd)O7|mpuvD8I~txmn=T0el{4142n6(`YlyaRNlY;LQxfw zB7=2_TiM@(kveHxTN+bUwXPg$8tTzP)X}NjwxmJ)L8`&`lLM z5}&-pV(xQnNSNzeO{uBXKFQ~k1V3c`ddQ21+`w#FYIQZG`J_7MC3YzJG+rKjTc&;S z=Xil8_`ce+Fw{6o92G9vJn>LCiUq~a3$B11j|xpF?p3wXsi{#h30X@8WjTocAR393 zrmx+r%)I~m!j6`|{d7MoYEz>pzSSRM+%Xu8vFTFemFV_N<=(ZtwzIIE-G6$Uug^F$ z9%p+qH+o!Suk=!~$^Ez#{Opfi1xA8Q6-;)Nf!L_;~|&QJn6tEEe;{Vxj#; zzM5HEUnv&+7o+26U&Cicok`ATvXxwVt^IyK)AMq%Q1)jth0I#IoLTbU-1J{rEY4zW rQM#;u0+g|fEItYf`iDW9${3fx-K0&(EBYtJtno<^#Z%`u+6&tMJOX-P diff --git a/vendor/assets/fonts/icomoon/icomoon.svg b/vendor/assets/fonts/icomoon/icomoon.svg deleted file mode 100755 index 1d0728296..000000000 --- a/vendor/assets/fonts/icomoon/icomoon.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - -Generated by IcoMoon - - - - - - - - - - - \ No newline at end of file diff --git a/vendor/assets/fonts/icomoon/icomoon.ttf b/vendor/assets/fonts/icomoon/icomoon.ttf deleted file mode 100755 index 1f84022e65f483c33c2a53af4f089b6b101d1e80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2100 zcmaJ?O-vg{6n?Ygp8#@zdTk5W2?iUR$dw^pgQ-#^A5hw~LPSA|8YxoN0oS1T53nT= zrP@=|KPen~K}uQ<>8VmBR4J!c-S*a~Jrz~G6{)IJ(l)oA>YskIyM|3dI%wv7?|bjf zd$aQx6Cv` z?=#T1i5#yj<#UTYKi{ttIe&qDatVa{JN}T!g&2x1m1^rj0O)C<7b^=nzUXv8e-pY~ z%B@!^NIyU?LJya7rTot8UH?M=2sz%ZR@Q2$6XTRD?8N!!$m}GNb|gg69aFMDNnu^W z&o-DS;fawYdUTiTZJn|l6E^WJIwNcVkxk4HY^LOfSBM;R8GIl91kWgZ$PV8>yz}U8 zyV-7oAuwcPSo^nt!@uQU_Z1Og&ty1*VTkLsStO#&%ZB5z_y2R^h*<~mEN(PF5gMZl z^eWBM4Z1~}^dargXS7FO)AuMiH93)vMTT8I*`KlDxD6{d?6lz#8+Jbjj~F|fJ9vkn z^vix_2+sZK7?)juKnM=UI>UpoPWjVS;FWX?ZJ_>_khm7Qr;5An?zE=Ublj z)jQ6NV!@_Sl!UdZiae50Q7e4plkK3RjgI1vfZ5F&dW6txFVZkN;i=nUkqLL)7wK05 z8O0k{CKR8-A2dE5Oe6+*5o_y~rmC8l-+ZcSDyPI?OBPo4*I=SW`sSw2G)-%%2fB`a zbP%<5CNRLR)^cYJ_l0-1QmK`nJxSM^9g8PX zQ0lDYizw-&WJ12%&_}1HMtK%%b1teVQOpO)M4~!Hg_Vzy8=RS20#+@|fhR5O0Iplui8s$@VHfx}EZmJ>ln=y<>*xY=8Ws~vzy%9) z;AsmxfZw*Tll&C6unYVZ3wKkR?k_J?N|j2PmT7@1RKl!K`D%W3ZMjkopHGg{Rm#&U zt-%2X>~o%yG=5^#iR6`hp7%Q6n?YopC*x;1pg_n+c=41r!sO<+lhlpBp;$cL6NIQs#Gab*G-HQ)j#>M zMWPV2Co1639JoYkA%}26LW+f@zCc>|o@ z;(P%4(gKg$2Xt{!7p=BJW&?R`U&0Rv@zIlFrjP~q6LP=DW%rGae^yHCWt640<5VD3 z{fzC9l}OoDp1{A~wCoRxvkv&?!FtRd$0?$x4^*RJP)db>5ziEskATo33y>dk9Q&bp zl%Fhg9kvHQ&NB)ev*Y)V?>~Les5Kg3u){H#7}ogJYw>J*Hr#o+hX`sbMhxq^#qLc~ zAxgd?N;;|Q|2cldl!eq3y249CG)h43hX?@(}TVmuKV8nn4x zo}>wfO*n4CRudjJVfzd4u-Lh|gL42*ugeo3KyYs&q`GWF1A`&J2~Zxl%ID>DBE(E9 zy(Vm~%xdD|Xht?Ir|$fpYJbW{z_t0{-`s1D_H zFizCIkkN5zTdS2>Niba$1payO;+f}TjaD$Dkgq0+QlM)Z&!dQn>VbAls)gDcwb!=+ zz8Un0jvgWO+65X!C!BgaY-rpbb`SN&y~(&U93PLn94!^Ec>%RQYT|;%V4^$*R>K_JpeKu_h3nYMenTnp}Wf_&OSfqN_Ufq5BCZNrWQkr;Cg? zx~T()6BAci%yW?qDA#*d(|UTf$L06ClmK*nKIFqgZeTYfy}Ft<{H{9ZUF^u^H+XyS zW0}sypW|g3=jZxw7KR>2i=)F`Hg6&nj-sI0CB;1yPehd_RQIaJ$mHaRnu4ySgYkF} z@+caKtW4c{SebtJm$@CIaPQecPBkV+PGYOy#CTvZ7-Lf_k(Z;})0Ky}3dYXdcJAQW zJzk%@Gk(u?XK#1E`ZsmqMPMAxk5*5td^7yM$Fmay>%^VN4~PMMa{CB|I+`<0hcDy) zX%VxXc;A=U7+Yc=vqL2)cIu$VxHI`^WfYE7=jn5qkC7c*Gq(b)Nvr}-NNfSFNNmNO z=a$$8{!NKH$wwdX8`shS<~(MaSOLyStO8F-Yyo~pVk>zlAh8Yn4T(D`LHo-Kr9!Dx zq-9#55)~jND&EYltuL2~fh*B5x=C4Dqjdye0{dK{D2<)D>P+%RHdo1K){r!dLM!;I z&d}PKku#-YIgra1vul}hb}?{gGjMI8G>h7zG$V4Ckv)fPilU+nm0=gb-JwnBYvKU2 P%>$su<{e>vE2r?k;`eUi From 149da72e920a0ed6483403cee52a79d1ce94a983 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Wed, 15 Feb 2017 13:31:20 -0600 Subject: [PATCH 13/40] css cleanup --- app/assets/stylesheets/app/_fonts.scss | 92 --------- app/assets/stylesheets/app/_main.scss | 17 -- app/assets/stylesheets/app/_mostrap.scss | 248 ++--------------------- 3 files changed, 17 insertions(+), 340 deletions(-) delete mode 100644 app/assets/stylesheets/app/_fonts.scss diff --git a/app/assets/stylesheets/app/_fonts.scss b/app/assets/stylesheets/app/_fonts.scss deleted file mode 100644 index 2d5708715..000000000 --- a/app/assets/stylesheets/app/_fonts.scss +++ /dev/null @@ -1,92 +0,0 @@ -@font-face { - font-family: ProximaNova; - src: font-url('ProximaNova/ProximaNova-Regular.eot'); - src: local('☺'), - font-url('ProximaNova/ProximaNova-Regular.woff') format('woff'), - font-url('ProximaNova/ProximaNova-Regular.ttf') format('truetype'), - font-url('ProximaNova/ProximaNova-Regular.svg') format('svg'); -} - -@font-face { - font-family: ProximaNova; - font-style: italic; - src: font-url('ProximaNova/ProximaNova-Regular-Italic.eot'); - src: local('☺'), - font-url('ProximaNova/ProximaNova-Regular-Italic.woff') format('woff'), - font-url('ProximaNova/ProximaNova-Regular-Italic.ttf') format('truetype'), - font-url('ProximaNova/ProximaNova-Regular-Italic.svg') format('svg'); -} - -@font-face { - font-family: ProximaNova; - font-weight: 600; - src: font-url('ProximaNova/ProximaNova-Semibold.eot'); - src: local('☺'), - font-url('ProximaNova/ProximaNova-Semibold.woff') format('woff'), - font-url('ProximaNova/ProximaNova-Semibold.ttf') format('truetype'), - font-url('ProximaNova/ProximaNova-Semibold.svg') format('svg'); -} - -@font-face { - font-family: ProximaNova; - font-weight: 600; - font-style: italic; - src: font-url('ProximaNova/ProximaNova-Semibold-Italic.eot'); - src: local('☺'), - font-url('ProximaNova/ProximaNova-Semibold-Italic.woff') format('woff'), - font-url('ProximaNova/ProximaNova-Semibold-Italic.ttf') format('truetype'), - font-url('ProximaNova/ProximaNova-Semibold-Italic.svg') format('svg'); -} - -@font-face { - font-family: ProximaNova; - font-weight: bold; - src: font-url('ProximaNova/ProximaNova-Bold.eot'); - src: local('☺'), - font-url('ProximaNova/ProximaNova-Bold.woff') format('woff'), - font-url('ProximaNova/ProximaNova-Bold.ttf') format('truetype'), - font-url('ProximaNova/ProximaNova-Bold.svg') format('svg'); -} - -@font-face { - font-family: ProximaNova; - font-weight: bold; - font-style: italic; - src: font-url('ProximaNova/ProximaNova-Bold-Italic.eot'); - src: local('☺'), - font-url('ProximaNova/ProximaNova-Bold-Italic.woff') format('woff'), - font-url('ProximaNova/ProximaNova-Bold-Italic.ttf') format('truetype'), - font-url('ProximaNova/ProximaNova-Bold-Italic.svg') format('svg'); -} - - -@font-face { - font-family: ProximaNova; - font-weight: 100; - src: font-url('fonts/ProximaNova-Thin.eot'); - src: local('☺'), - font-url('ProximaNova/ProximaNova-Thin.woff') format('woff'), - font-url('ProximaNova/ProximaNova-Thin.ttf') format('truetype'), - font-url('ProximaNova/ProximaNova-Thin.svg') format('svg'); -} - -@font-face { - font-family: ProximaNova; - font-weight: 100; - font-style: italic; - src: font-url('fonts/ProximaNova-Thin-Italic.eot'); - src: local('☺'), - font-url('ProximaNova/ProximaNova-Thin-Italic.woff') format('woff'), - font-url('ProximaNova/ProximaNova-Thin-Italic.ttf') format('truetype'), - font-url('ProximaNova/ProximaNova-Thin-Italic.svg') format('svg'); -} - -@font-face { - font-family: ProximaNova; - font-weight: 900; - src: font-url('ProximaNova/ProximaNova-Extrabold.eot'); - src: local('☺'), - font-url('ProximaNova/ProximaNova-Extrabold.woff') format('woff'), - font-url('ProximaNova/ProximaNova-Extrabold.ttf') format('truetype'), - font-url('ProximaNova/ProximaNova-Extrabold.svg') format('svg'); -} diff --git a/app/assets/stylesheets/app/_main.scss b/app/assets/stylesheets/app/_main.scss index b71e01bac..65b5a2a9c 100644 --- a/app/assets/stylesheets/app/_main.scss +++ b/app/assets/stylesheets/app/_main.scss @@ -29,10 +29,6 @@ $blue-color: #086dd6; } } -[ng\:cloak], [ng-cloak], .ng-cloak { - display: none !important; -} - html, body { font-family: -apple-system, BlinkMacSystemFont, @@ -72,19 +68,6 @@ a { } } -pre { - padding: 16px; - overflow: auto; - line-height: 1.45; - background-color: #f7f7f7; - border-radius: 3px; -} - -code { - word-wrap: break-word; - line-height: 1.45; -} - p { overflow: auto; } diff --git a/app/assets/stylesheets/app/_mostrap.scss b/app/assets/stylesheets/app/_mostrap.scss index 7b540f5b0..2776256a1 100644 --- a/app/assets/stylesheets/app/_mostrap.scss +++ b/app/assets/stylesheets/app/_mostrap.scss @@ -7,15 +7,11 @@ $screen-xs: 480px !default; //** Deprecated `$screen-xs-min` as of v3.2.0 $screen-xs-min: $screen-xs !default; -//** Deprecated `$screen-phone` as of v3.0.1 -$screen-phone: $screen-xs-min !default; // Small screen / tablet //** Deprecated `$screen-sm` as of v3.0.1 $screen-sm: 768px !default; $screen-sm-min: $screen-sm !default; -//** Deprecated `$screen-tablet` as of v3.0.1 -$screen-tablet: $screen-sm-min !default; // Medium screen / desktop //** Deprecated `$screen-md` as of v3.0.1 @@ -67,111 +63,6 @@ $screen-md-max: ($screen-lg-min - 1) !default; *:focus {outline:0;} -.navbar { - min-height: 0px !important; - background-color: white; - height: 80px; - margin-bottom: 0px; - padding-top: 10px; - border-radius: 0px; -} -@media (min-width: 768px) { - .navbar { - border-radius: 4px; - } -} - -.navbar { - position: relative; - min-height: 50px; - margin-bottom: 20px; - border: 1px solid transparent; -} - -@media (min-width: 768px) { - .navbar-header { - float: left; - } -} - -.container > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-header, .container-fluid > .navbar-collapse { - margin-right: 0; - margin-left: 0; -} - -@media (min-width: 768px) { - .navbar > .container .navbar-brand, .navbar > .container-fluid .navbar-brand { - // margin-left: -15px; - } -} - -.navbar-brand { - float: left; - padding: 15px 15px; - font-size: 18px; - line-height: 20px; - height: 50px; -} - -@media (min-width: 768px) { - .container > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-header, .container-fluid > .navbar-collapse { - // margin-right: 0; - // margin-left: 0; - } -} - -.container > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-header, .container-fluid > .navbar-collapse { - // margin-right: -15px; - // margin-left: -15px; -} - -@media (min-width: 768px) { - .navbar-collapse.collapse { - display: block !important; - height: auto !important; - padding-bottom: 0; - overflow: visible !important; - } -} -@media (min-width: 768px) { - .navbar-collapse { - width: auto; - border-top: 0; - box-shadow: none; - } -} - -.navbar-collapse { - overflow-x: visible; - padding-right: 15px; - padding-left: 15px; - border-top: 1px solid transparent; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); - -webkit-overflow-scrolling: touch; -} - -.collapse { - display: none; -} - -@media (min-width: 768px) { - .navbar-right { - float: right !important; - // margin-right: -15px; - } -} -@media (min-width: 768px) { - .navbar-text { - float: left; - margin-left: 15px; - margin-right: 15px; - } -} -.navbar-text { - margin-top: 15px; - margin-bottom: 15px; -} - .dropup, .dropdown { position: relative; } @@ -232,10 +123,6 @@ button:focus {outline:0;} right: 0; } -.open > .dropdown-menu { - display: block; -} - .btn { display: inline-block; margin-bottom: 0; @@ -262,23 +149,6 @@ button:focus {outline:0;} width: 100%; } -// ul, menu, dir { -// display: block; -// list-style-type: disc; -// -webkit-margin-before: 1em; -// -webkit-margin-after: 1em; -// -webkit-margin-start: 0px; -// -webkit-margin-end: 0px; -// -webkit-padding-start: 40px; -// } - -.dropdown-menu .divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} - .panel { position: absolute; right: 0px; @@ -290,59 +160,35 @@ button:focus {outline:0;} background-color: white; } -.panel-top { - bottom: 0px; -} - -.panel-left { - left: -50px; -} - .panel-right { left: 0px; } -.panel-centered { - position: relative; - width: 400px; - margin: 0 auto; - padding: 10px; - text-align: center; -} - .panel-body { padding: 15px; } .form-control { - display: block; - width: 100%; - height: 34px; - padding: 6px 12px; - font-size: 14px; - line-height: 1.42857; - color: #555555; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - box-shadow: inset 0 1px 1px rgba(0,0,0,0.075); - -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; - transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857; + color: #555555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + box-shadow: inset 0 1px 1px rgba(0,0,0,0.075); + -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; + transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; } input, button, select, textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; -} - -.has-feedback { - position: relative; -} - -.form-tag { - margin-bottom: 15px; + font-family: inherit; + font-size: inherit; + line-height: inherit; } .checkbox { @@ -352,63 +198,3 @@ input, button, select, textarea { margin-right: auto; margin-bottom: 10px; } - -.btn-link { - background-color: transparent; - -webkit-box-shadow: none; - box-shadow: none; - text-decoration: none; -} - -.btn-link:hover, .btn-link:focus { - color: #23527c; - text-decoration: underline; - background-color: transparent; -} - -.animated { - -webkit-animation-duration: 1s; - animation-duration: 1s; - -webkit-animation-fill-mode: both; - animation-fill-mode: both; -} - -.animated-fast { - -webkit-animation-duration: 0.5s; - animation-duration: 0.5s; - -webkit-animation-fill-mode: both; - animation-fill-mode: both; -} - -.fadeInDown { - -webkit-animation-name: fadeInDown; - animation-name: fadeInDown; -} - -.fadeIn { - -webkit-animation-name: fadeIn; - animation-name: fadeIn; -} - -@keyframes fadeIn { - 0% { - opacity: 0; - } - - 100% { - opacity: 1; - } -} - -@keyframes fadeInDown { - 0% { - opacity: 0; - -webkit-transform: translate3d(0,-100%,0); - transform: translate3d(0,-100%,0); - } - 100% { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} From e7b3b613167c3003f3ecaf9faa76c53c1cd75561 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Wed, 15 Feb 2017 17:34:30 -0600 Subject: [PATCH 14/40] updates --- .../javascripts/app/services/directives/views/editorMenu.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/javascripts/app/services/directives/views/editorMenu.js b/app/assets/javascripts/app/services/directives/views/editorMenu.js index 82d260cb0..63aab1e70 100644 --- a/app/assets/javascripts/app/services/directives/views/editorMenu.js +++ b/app/assets/javascripts/app/services/directives/views/editorMenu.js @@ -36,6 +36,7 @@ class EditorMenu { if(confirm("Are you sure you want to delete this editor?")) { modelManager.setItemToBeDeleted(editor); syncManager.sync(); + _.pull($scope.editors, editor); } } From 48dab1d05553f48d94482adea1207e13f2f7dafa Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Wed, 15 Feb 2017 21:57:13 -0600 Subject: [PATCH 15/40] link fix --- app/assets/templates/frontend/directives/editor-menu.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/templates/frontend/directives/editor-menu.html.haml b/app/assets/templates/frontend/directives/editor-menu.html.haml index f7509a45c..d4dfe9c7d 100644 --- a/app/assets/templates/frontend/directives/editor-menu.html.haml +++ b/app/assets/templates/frontend/directives/editor-menu.html.haml @@ -20,4 +20,4 @@ %button.white.medium.inline.top{"style" => "width: 50px; height: 40px;", "ng-click" => "deleteEditor(editor); $event.stopPropagation();"} ☓ .menu-section-footer.mt-10 %input.form-control{"ng-model" => "formData.url", "placeholder" => "Add new editor via URL", "ng-keyup" => "$event.keyCode == 13 && submitNewEditorRequest()"} - %a.block{"href" => ""} Available Editors + %a.block{"href" => "https://standardnotes.org/extensions", "target" => "_blank"} Available Editors From 25cb99cf8413a4be839bab061027a8ae51fa2f9a Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Wed, 15 Feb 2017 22:01:14 -0600 Subject: [PATCH 16/40] link color fix --- app/assets/stylesheets/app/_main.scss | 4 ++++ .../templates/frontend/directives/editor-menu.html.haml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/app/_main.scss b/app/assets/stylesheets/app/_main.scss index 65b5a2a9c..092bfb6d5 100644 --- a/app/assets/stylesheets/app/_main.scss +++ b/app/assets/stylesheets/app/_main.scss @@ -29,6 +29,10 @@ $blue-color: #086dd6; } } +.blue { + color: $blue-color; +} + html, body { font-family: -apple-system, BlinkMacSystemFont, diff --git a/app/assets/templates/frontend/directives/editor-menu.html.haml b/app/assets/templates/frontend/directives/editor-menu.html.haml index d4dfe9c7d..374e58918 100644 --- a/app/assets/templates/frontend/directives/editor-menu.html.haml +++ b/app/assets/templates/frontend/directives/editor-menu.html.haml @@ -20,4 +20,4 @@ %button.white.medium.inline.top{"style" => "width: 50px; height: 40px;", "ng-click" => "deleteEditor(editor); $event.stopPropagation();"} ☓ .menu-section-footer.mt-10 %input.form-control{"ng-model" => "formData.url", "placeholder" => "Add new editor via URL", "ng-keyup" => "$event.keyCode == 13 && submitNewEditorRequest()"} - %a.block{"href" => "https://standardnotes.org/extensions", "target" => "_blank"} Available Editors + %a.block.blue{"href" => "https://standardnotes.org/extensions", "target" => "_blank"} Available Editors From ba1d077d63f3722832bd99829fe156d2b3978c36 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Thu, 16 Feb 2017 15:58:32 -0600 Subject: [PATCH 17/40] converted grid to flexbox --- .../app/frontend/controllers/home.js | 2 - app/assets/stylesheets/app/_common.scss | 42 --- app/assets/stylesheets/app/_editor.scss | 256 +++++++----------- app/assets/stylesheets/app/_main.scss | 69 +++-- app/assets/stylesheets/app/_notes.scss | 5 +- app/assets/stylesheets/app/_tags.scss | 5 +- .../templates/frontend/editor.html.haml | 81 +++--- app/assets/templates/frontend/home.html.haml | 13 +- .../frontend/layouts/about.html.haml | 6 - .../frontend/layouts/footer.html.haml | 4 - app/views/application/frontend.html.erb | 2 +- 11 files changed, 197 insertions(+), 288 deletions(-) delete mode 100644 app/assets/templates/frontend/layouts/about.html.haml delete mode 100644 app/assets/templates/frontend/layouts/footer.html.haml diff --git a/app/assets/javascripts/app/frontend/controllers/home.js b/app/assets/javascripts/app/frontend/controllers/home.js index 507f3c931..79880660d 100644 --- a/app/assets/javascripts/app/frontend/controllers/home.js +++ b/app/assets/javascripts/app/frontend/controllers/home.js @@ -1,7 +1,5 @@ angular.module('app.frontend') .controller('HomeCtrl', function ($scope, $rootScope, $timeout, modelManager, syncManager, authManager) { - $rootScope.bodyClass = "app-body-class"; - syncManager.loadLocalItems(function(items) { $scope.$apply(); diff --git a/app/assets/stylesheets/app/_common.scss b/app/assets/stylesheets/app/_common.scss index ea9cb0895..87f8bbdb0 100644 --- a/app/assets/stylesheets/app/_common.scss +++ b/app/assets/stylesheets/app/_common.scss @@ -72,45 +72,3 @@ } } } - - -.section-menu { - padding-top: 0px; - width: 100%; - position: absolute; - padding-left: inherit; - padding-right: inherit; - left: 0; - right: 0; - bottom: 0px; - background-color: #f1f1f1; - color: $selected-text-color; - height: 28px; - cursor: default; - - ol, ul { - margin-top: 5px; - margin-bottom: 10px; - - &.dropdown-menu { - margin-top: 10px; - } - } - - ul { - li { - text-align: left; - - &.sep { - margin: 6px; - display: block; - } - - a { - font-size: 13px; - font-weight: bold; - padding: 0 0; - } - } - } -} diff --git a/app/assets/stylesheets/app/_editor.scss b/app/assets/stylesheets/app/_editor.scss index c122eac3a..95707f564 100644 --- a/app/assets/stylesheets/app/_editor.scss +++ b/app/assets/stylesheets/app/_editor.scss @@ -1,5 +1,10 @@ +$heading-height: 75px; .editor { - width: 60%; + flex: 1 50%; + min-width: 300px; + display: flex; + flex-direction: column; + overflow-y: hidden; &.fullscreen { width: 100%; @@ -12,187 +17,124 @@ .section-title-bar { border-bottom: none !important; - - // &.fullscreen { - // opacity: 0.0; - // -webkit-transition: all 300ms ease-in-out; - // -moz-transition: all 300ms ease-in-out; - // -ms-transition: all 300ms ease-in-out; - // -o-transition: all 300ms ease-in-out; - // transition: all 300ms ease-in-out; - // - // &:hover { - // opacity: 1.0; - // } - } + height: $heading-height !important; } - $heading-height: 100px; + .section-menu { + flex: 1 0 28px; + max-height: 28px; + } +} - .editor-heading { +.editor-heading { - position: absolute; - width: 100%; - padding: 15px; + width: 100%; + padding: 15px; + padding-top: 0px; + background-color: white; + + min-height: $heading-height; + + padding-right: 10px; + + &.fullscreen { + position: relative; + } + + > .title { + font-size: 18px; + font-weight: bold; padding-top: 0px; - background-color: white; - - min-height: $heading-height; - width: 100%; - padding-right: 10px; - &.fullscreen { - position: relative; - } - - > .title { - font-size: 18px; + > .input { + float: left; + text-overflow:ellipsis; + width: 90%; font-weight: bold; - padding-top: 0px; - width: 100%; - - > .input { - float: left; - text-overflow:ellipsis; - width: 90%; - font-weight: bold; - border: none; - outline: none; - background-color: transparent; - - &:disabled { - color: black; - } - } - - } - .save-status { - width: 20% !important; - float: right; - position: absolute; - - right: 20px; - font-size: 12px; - text-transform: none; - font-weight: normal; - margin-top: 4px; - width: 120px; - text-align: right; - color: rgba(black, 0.23); - } - - .tags { - clear: left; - width: 100%; - height: 25px; - - .tags-input { - background-color: transparent; - width: 100%; - border: none; - } - } - } - - .editor-content { - max-height: 100%; - - height: 100%; - clear: both; - min-width: 0; - padding-left: 10px; - padding-right: 10px; - overflow: auto; - text-align: center; // centers children div horizontally - z-index: 10; - padding-top: $heading-height; - - &.fullscreen { - padding-top: 0px; - } - - .editable { - font-family: monospace; - max-height: 100%; - height: 100%; - width: 100%; border: none; outline: none; - padding: 15px; - padding-top: 17px; - font-size: 17px; - resize: none; + background-color: transparent; - &.fullscreen { - padding: 85px 10%; - max-width: 1200px; - display: inline-block; + &:disabled { + color: black; } } + + } + + .save-status { + width: 20% !important; + float: right; + position: absolute; + + right: 20px; + font-size: 12px; + text-transform: none; + font-weight: normal; + margin-top: 4px; + width: 120px; + text-align: right; + color: rgba(black, 0.23); + } + + .tags { + clear: left; + width: 100%; + height: 25px; + + .tags-input { + background-color: transparent; + width: 100%; + border: none; + } + } } -.nav-tabs { - a { - color: black; - text-decoration: none; +.editor-content { + flex: 1; + z-index: 10; + background-color: white; + overflow-y: hidden;; + + &.fullscreen { + padding-top: 0px; } - a { - background-color: transparent; + + #editor-iframe { + max-height: 100%; + height: 100%; + width: 100%; + } + + .editable { + font-family: monospace; + max-height: 100%; + overflow-y: scroll; + height: 100%; + width: 100%; + border: none; + outline: none; + padding: 15px; + padding-top: 11px; + font-size: 17px; + resize: none; + + &.fullscreen { + padding: 85px 10%; + max-width: 1200px; + display: inline-block; + } } } + .nav { padding-left: 0; margin-bottom: 0; list-style: none; } -.nav-pills>li { - float: left; -} -.nav>li { - position: relative; - display: block; -} - -.nav-pills>li.active>a, .nav-pills>li.active>a:focus, .nav-pills>li.active>a:hover { - color: #fff; - background-color: #337ab7; -} -.nav-pills>li>a { - border-radius: 4px; -} .nav>li>a { position: relative; display: block; - padding: 10px 15px; -} - -.nav-tabs>li { - float: left; - margin-bottom: -1px; -} -.nav>li { - position: relative; - display: block; -} - -.nav-tabs>li.active>a, .nav-tabs>li.active>a:focus, .nav-tabs>li.active>a:hover { - color: black; - cursor: pointer; - background-color: #fff; - border: 1px solid #ddd; - border-bottom-color: transparent; -} -.nav-tabs>li>a { - margin-right: 2px; - line-height: 1.42857143; - border: 1px solid transparent; - // border-radius: 4px 4px 0 0; - cursor: pointer; -} -.nav>li>a { - position: relative; - display: block; - padding: 10px 15px; } diff --git a/app/assets/stylesheets/app/_main.scss b/app/assets/stylesheets/app/_main.scss index 092bfb6d5..621d97678 100644 --- a/app/assets/stylesheets/app/_main.scss +++ b/app/assets/stylesheets/app/_main.scss @@ -45,6 +45,7 @@ body { height: 100%; font-size: 20px; margin: 0; + background-color: $bg-color; } .dark-button { @@ -80,35 +81,21 @@ p { min-height: 100vh; height: 100vh; overflow: auto; -} - -.app-body-class { - height: 100%; background-color: $bg-color; - min-width: 100px; - min-width: 900px; } $header-height: 25px; -.app-container { - display: table; - background-color: $bg-color; - width: 100%; - height: calc(100% - #{$header-height}); - padding-top: 0px; - font-size: 0; - margin-top: 0; -} - $section-header-height: 70px; .app { - height: 100%; + // height: 100%; + height: calc(100% - #{$header-height}); width: 100%; - display: table-row; + display: flex; vertical-align: top; overflow: hidden; + position: relative; .light-button { background-color: $bg-color; @@ -127,15 +114,8 @@ $section-header-height: 70px; .section { padding-bottom: 0px; - - display: block; height: 100%; max-height: calc(100vh - #{$header-height}); - float: left; - overflow-y: auto; - overflow-x: hidden; - min-width: 0; - font-size: 17px; .scrollable { @@ -148,7 +128,6 @@ $section-header-height: 70px; max-height: 100%; background-color: white; position: relative; - box-shadow: 0px 0px 2px rgba(gray, 0.3); } .section-title-bar { @@ -185,3 +164,41 @@ $section-header-height: 70px; } } } + +.section-menu { + width: 100%; + padding-top: 0px; + padding-left: 21px; + padding-right: 21px; + + background-color: #f1f1f1; + color: $selected-text-color; + height: 28px; + cursor: default; + + ol, ul { + margin-top: 5px; + margin-bottom: 10px; + + &.dropdown-menu { + margin-top: 10px; + } + } + + ul { + li { + text-align: left; + + &.sep { + margin: 6px; + display: block; + } + + a { + font-size: 13px; + font-weight: bold; + padding: 0 0; + } + } + } +} diff --git a/app/assets/stylesheets/app/_notes.scss b/app/assets/stylesheets/app/_notes.scss index 923e7cf57..4294ff6de 100644 --- a/app/assets/stylesheets/app/_notes.scss +++ b/app/assets/stylesheets/app/_notes.scss @@ -1,8 +1,11 @@ .notes { - width: 25%; border-left: 1px solid #dddddd; border-right: 1px solid #dddddd; + flex: 1 20%; + max-width: 350px; + min-width: 170px; + $notes-title-bar-height: 130px; .notes-title-bar { diff --git a/app/assets/stylesheets/app/_tags.scss b/app/assets/stylesheets/app/_tags.scss index 7d3ce9e34..ba45efd89 100644 --- a/app/assets/stylesheets/app/_tags.scss +++ b/app/assets/stylesheets/app/_tags.scss @@ -1,5 +1,8 @@ .tags { - width: 15%; + // width: 15%; + flex: 1 10%; + max-width: 180px; + min-width: 100px; $tags-title-bar-height: 55px; diff --git a/app/assets/templates/frontend/editor.html.haml b/app/assets/templates/frontend/editor.html.haml index 2556b403c..41dedafed 100644 --- a/app/assets/templates/frontend/editor.html.haml +++ b/app/assets/templates/frontend/editor.html.haml @@ -1,46 +1,45 @@ .section.editor{"ng-class" => "{'fullscreen' : ctrl.fullscreen}"} - .content - .section-title-bar.editor-heading{"ng-class" => "{'fullscreen' : ctrl.fullscreen }"} - .title - %input.input#note-title-editor{"ng-model" => "ctrl.note.title", "ng-keyup" => "$event.keyCode == 13 && ctrl.saveTitle($event)", - "ng-change" => "ctrl.nameChanged()", "ng-focus" => "ctrl.onNameFocus()", - "select-on-click" => "true"} - .save-status{"ng-class" => "{'red bold': ctrl.saveError}", "ng-bind-html" => "ctrl.noteStatus"} - .tags - %input.tags-input{"type" => "text", "ng-keyup" => "$event.keyCode == 13 && ctrl.updateTagsFromTagsString($event, ctrl.tagsString)", - "ng-model" => "ctrl.tagsString", "placeholder" => "#tags", "ng-blur" => "ctrl.updateTagsFromTagsString($event, ctrl.tagsString)"} - .section-menu - %ul.nav.nav-pills - %li.dropdown{"click-outside" => "ctrl.showMenu = false;", "is-open" => "ctrl.showMenu"} - %a.dropdown-toggle{"ng-click" => "ctrl.showMenu = !ctrl.showMenu; ctrl.showExtensions = false;"} - File - %span.caret - %span.sr-only + .section-title-bar.editor-heading{"ng-class" => "{'fullscreen' : ctrl.fullscreen }"} + .title + %input.input#note-title-editor{"ng-model" => "ctrl.note.title", "ng-keyup" => "$event.keyCode == 13 && ctrl.saveTitle($event)", + "ng-change" => "ctrl.nameChanged()", "ng-focus" => "ctrl.onNameFocus()", + "select-on-click" => "true"} + .save-status{"ng-class" => "{'red bold': ctrl.saveError}", "ng-bind-html" => "ctrl.noteStatus"} + .tags + %input.tags-input{"type" => "text", "ng-keyup" => "$event.keyCode == 13 && ctrl.updateTagsFromTagsString($event, ctrl.tagsString)", + "ng-model" => "ctrl.tagsString", "placeholder" => "#tags", "ng-blur" => "ctrl.updateTagsFromTagsString($event, ctrl.tagsString)"} + .section-menu + %ul.nav + %li.dropdown.pull-left.mr-10{"click-outside" => "ctrl.showMenu = false;", "is-open" => "ctrl.showMenu"} + %a.dropdown-toggle{"ng-click" => "ctrl.showMenu = !ctrl.showMenu; ctrl.showExtensions = false;"} + File + %span.caret + %span.sr-only - %ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark{"ng-if" => "ctrl.showMenu"} - %li{"ng-click" => "ctrl.selectedMenuItem(); ctrl.toggleFullScreen()"} - .text Toggle Fullscreen - .shortcut Cmd + O - %li{"ng-click" => "ctrl.deleteNote()"} - .text Delete + %ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark{"ng-if" => "ctrl.showMenu"} + %li{"ng-click" => "ctrl.selectedMenuItem(); ctrl.toggleFullScreen()"} + .text Toggle Fullscreen + .shortcut Cmd + O + %li{"ng-click" => "ctrl.deleteNote()"} + .text Delete - %li.sep - %li.dropdown{"click-outside" => "ctrl.showEditorMenu = false;", "is-open" => "ctrl.showEditorMenu"} - %a.dropdown-toggle{"ng-click" => "ctrl.showEditorMenu = !ctrl.showEditorMenu; ctrl.showMenu = false;"} - Editor - %span.caret - %span.sr-only - %editor-menu{"ng-if" => "ctrl.showEditorMenu", "callback" => "ctrl.selectedEditor", "selected-editor" => "ctrl.customEditor"} + %li.sep + %li.dropdown.pull-left.mr-10{"click-outside" => "ctrl.showEditorMenu = false;", "is-open" => "ctrl.showEditorMenu"} + %a.dropdown-toggle{"ng-click" => "ctrl.showEditorMenu = !ctrl.showEditorMenu; ctrl.showMenu = false;"} + Editor + %span.caret + %span.sr-only + %editor-menu{"ng-if" => "ctrl.showEditorMenu", "callback" => "ctrl.selectedEditor", "selected-editor" => "ctrl.customEditor"} - %li.sep - %li.dropdown{"ng-if" => "ctrl.hasAvailableExtensions()", "click-outside" => "ctrl.showExtensions = false;", "is-open" => "ctrl.showExtensions"} - %a.dropdown-toggle{"ng-click" => "ctrl.showExtensions = !ctrl.showExtensions; ctrl.showMenu = false;"} - Extensions - %span.caret - %span.sr-only - %contextual-extensions-menu{"ng-if" => "ctrl.showExtensions", "item" => "ctrl.note"} + %li.sep + %li.dropdown.pull-left{"ng-if" => "ctrl.hasAvailableExtensions()", "click-outside" => "ctrl.showExtensions = false;", "is-open" => "ctrl.showExtensions"} + %a.dropdown-toggle{"ng-click" => "ctrl.showExtensions = !ctrl.showExtensions; ctrl.showMenu = false;"} + Extensions + %span.caret + %span.sr-only + %contextual-extensions-menu{"ng-if" => "ctrl.showExtensions", "item" => "ctrl.note"} - .editor-content{"ng-class" => "{'fullscreen' : ctrl.fullscreen }"} - %iframe#editor-iframe{"ng-if" => "ctrl.customEditor", "ng-src" => "{{ctrl.customEditor.url | trusted}}", "frameBorder" => "0", "style" => "width: 100%; height: 100%; z-index: 1000; float: left;"} - %textarea.editable#note-text-editor{"ng-if" => "!ctrl.customEditor", "ng-class" => "{'fullscreen' : ctrl.fullscreen }", "ng-model" => "ctrl.note.text", - "ng-change" => "ctrl.contentChanged()", "ng-click" => "ctrl.clickedTextArea()", "ng-focus" => "ctrl.onContentFocus()"} + .editor-content{"ng-class" => "{'fullscreen' : ctrl.fullscreen }"} + %iframe#editor-iframe{"ng-if" => "ctrl.customEditor", "ng-src" => "{{ctrl.customEditor.url | trusted}}", "frameBorder" => "0", "style" => "width: 100%;"} + %textarea.editable#note-text-editor{"ng-if" => "!ctrl.customEditor", "ng-class" => "{'fullscreen' : ctrl.fullscreen }", "ng-model" => "ctrl.note.text", + "ng-change" => "ctrl.contentChanged()", "ng-click" => "ctrl.clickedTextArea()", "ng-focus" => "ctrl.onContentFocus()"} diff --git a/app/assets/templates/frontend/home.html.haml b/app/assets/templates/frontend/home.html.haml index f7057c955..82642f6f9 100644 --- a/app/assets/templates/frontend/home.html.haml +++ b/app/assets/templates/frontend/home.html.haml @@ -1,12 +1,11 @@ .main-ui-view - .app-container - .app - %tags-section{"save" => "tagsSave", "add-new" => "tagsAddNew", "will-select" => "tagsWillMakeSelection", "selection-made" => "tagsSelectionMade", "all-tag" => "allTag", - "tags" => "tags"} + .app + %tags-section{"save" => "tagsSave", "add-new" => "tagsAddNew", "will-select" => "tagsWillMakeSelection", "selection-made" => "tagsSelectionMade", "all-tag" => "allTag", + "tags" => "tags"} - %notes-section{"remove-tag" => "notesRemoveTag", "add-new" => "notesAddNew", "selection-made" => "notesSelectionMade", - "tag" => "selectedTag", "remove" => "deleteNote"} + %notes-section{"remove-tag" => "notesRemoveTag", "add-new" => "notesAddNew", "selection-made" => "notesSelectionMade", + "tag" => "selectedTag", "remove" => "deleteNote"} - %editor-section{"ng-if" => "selectedNote", "note" => "selectedNote", "remove" => "deleteNote", "save" => "saveNote", "update-tags" => "updateTagsForNote"} + %editor-section{"ng-if" => "selectedNote", "note" => "selectedNote", "remove" => "deleteNote", "save" => "saveNote", "update-tags" => "updateTagsForNote"} %header diff --git a/app/assets/templates/frontend/layouts/about.html.haml b/app/assets/templates/frontend/layouts/about.html.haml deleted file mode 100644 index c274dd210..000000000 --- a/app/assets/templates/frontend/layouts/about.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -.about.animated.fadeIn - .title About - .summary Namewhale helps you find a unique name for your startup. Using an intelligent, seed-based algorithm, names are generated based on the sound, style, and feel of the seed words you chose. - .links - %a{"href" => "https://itunes.apple.com/us/app/namewhale/id1028881375?ls=1&mt=8", "target" => "_blank"} Namewhale on the AppStore - %a{"href" => "https://twitter.com/namewhale", "target" => "_blank"} @namewhale diff --git a/app/assets/templates/frontend/layouts/footer.html.haml b/app/assets/templates/frontend/layouts/footer.html.haml deleted file mode 100644 index f378522db..000000000 --- a/app/assets/templates/frontend/layouts/footer.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -%footer.footer{"ng-class" => "footerClass"} - .container - .row - .footer-about-section diff --git a/app/views/application/frontend.html.erb b/app/views/application/frontend.html.erb index 5e965dc17..e60481af3 100644 --- a/app/views/application/frontend.html.erb +++ b/app/views/application/frontend.html.erb @@ -37,7 +37,7 @@ - +
From b961a0936f9255d597922cfc1da3ba12cd70923b Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Thu, 16 Feb 2017 16:04:55 -0600 Subject: [PATCH 18/40] flex fix --- app/assets/stylesheets/app/_editor.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/app/_editor.scss b/app/assets/stylesheets/app/_editor.scss index 95707f564..2e8aab006 100644 --- a/app/assets/stylesheets/app/_editor.scss +++ b/app/assets/stylesheets/app/_editor.scss @@ -94,8 +94,9 @@ $heading-height: 75px; .editor-content { flex: 1; z-index: 10; + overflow-y: hidden; + height: 100%; background-color: white; - overflow-y: hidden;; &.fullscreen { padding-top: 0px; @@ -113,6 +114,7 @@ $heading-height: 75px; overflow-y: scroll; height: 100%; width: 100%; + border: none; outline: none; padding: 15px; From 253710acb95cdd363f36e87005e8e0c3e99724c3 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Thu, 16 Feb 2017 17:16:15 -0600 Subject: [PATCH 19/40] editor fixes --- .../javascripts/app/frontend/controllers/editor.js | 4 ++-- app/assets/stylesheets/app/_editor.scss | 12 ++++++++---- app/assets/stylesheets/app/_extensions.scss | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/app/frontend/controllers/editor.js b/app/assets/javascripts/app/frontend/controllers/editor.js index 20e1ab420..3254d78f8 100644 --- a/app/assets/javascripts/app/frontend/controllers/editor.js +++ b/app/assets/javascripts/app/frontend/controllers/editor.js @@ -46,8 +46,8 @@ angular.module('app.frontend') }) .controller('EditorCtrl', function ($sce, $timeout, authManager, $rootScope, extensionManager, syncManager, modelManager) { - window.addEventListener("message", function(){ - console.log("App received message:", event); + window.addEventListener("message", function(event){ + // console.log("App received message:", event); if(event.data.status) { this.postNoteToExternalEditor(); } else { diff --git a/app/assets/stylesheets/app/_editor.scss b/app/assets/stylesheets/app/_editor.scss index 2e8aab006..cd2c35a9a 100644 --- a/app/assets/stylesheets/app/_editor.scss +++ b/app/assets/stylesheets/app/_editor.scss @@ -96,6 +96,7 @@ $heading-height: 75px; z-index: 10; overflow-y: hidden; height: 100%; + display: flex; background-color: white; &.fullscreen { @@ -103,16 +104,14 @@ $heading-height: 75px; } #editor-iframe { - max-height: 100%; - height: 100%; + flex: 1; width: 100%; } .editable { font-family: monospace; - max-height: 100%; + flex: 1; overflow-y: scroll; - height: 100%; width: 100%; border: none; @@ -134,9 +133,14 @@ $heading-height: 75px; padding-left: 0; margin-bottom: 0; list-style: none; + ul { + font-weight: bold; + } } .nav>li>a { position: relative; display: block; + font-weight: bold; + user-select: none; } diff --git a/app/assets/stylesheets/app/_extensions.scss b/app/assets/stylesheets/app/_extensions.scss index 1958abff2..c5cf82fd2 100644 --- a/app/assets/stylesheets/app/_extensions.scss +++ b/app/assets/stylesheets/app/_extensions.scss @@ -42,6 +42,7 @@ > .title { font-size: 14px; + font-weight: bold; } > .subtitle { From 9c2b121f32459b35f3378fff9035a2c13d09a5d8 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Thu, 16 Feb 2017 17:19:17 -0600 Subject: [PATCH 20/40] rm body tag test --- test/javascripts/controllers/HomeCtrl_spec.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/test/javascripts/controllers/HomeCtrl_spec.js b/test/javascripts/controllers/HomeCtrl_spec.js index a44ae7e93..0ae1e1650 100644 --- a/test/javascripts/controllers/HomeCtrl_spec.js +++ b/test/javascripts/controllers/HomeCtrl_spec.js @@ -3,7 +3,7 @@ describe("app.frontend", function() { beforeEach(module('app.frontend')); describe('Home Controller', function() { - + var scope; beforeEach(inject(function($rootScope, $controller, modelManager) { scope = $rootScope.$new(); @@ -13,10 +13,6 @@ describe("app.frontend", function() { }); })); - it('should have a body class', function() { - expect(scope.bodyClass).toEqual('app-body-class'); - }); - it('should have an All tag', function() { expect(scope.allTag).toBeDefined(); expect(scope.allTag.title).toEqual("All"); @@ -31,7 +27,7 @@ describe("app.frontend", function() { scope.tagsAddNew("testTag"); expect($modelManager.items).toContain("testTag"); }); - + }); }); From e98d4f47984cc75773e5399391b3c65b671df9e7 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Thu, 16 Feb 2017 23:58:02 -0600 Subject: [PATCH 21/40] extension register error message --- .../app/services/directives/views/globalExtensionsMenu.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/app/services/directives/views/globalExtensionsMenu.js b/app/assets/javascripts/app/services/directives/views/globalExtensionsMenu.js index 9945ac81c..921dcfecc 100644 --- a/app/assets/javascripts/app/services/directives/views/globalExtensionsMenu.js +++ b/app/assets/javascripts/app/services/directives/views/globalExtensionsMenu.js @@ -21,7 +21,12 @@ class GlobalExtensionsMenu { if($scope.newExtensionData.url) { extensionManager.addExtension($scope.newExtensionData.url, function(response){ if(!response) { - alert("Unable to register this extension. Make sure the link is valid and try again."); + if($scope.newExtensionData.url.indexOf("name=") != -1) { + // user is mistakenly trying to register editor extension, most likely + alert("Unable to register this extension. It looks like you may be trying to install an editor extension. To do that, click 'Editor' under the current note's title."); + } else { + alert("Unable to register this extension. Make sure the link is valid and try again."); + } } else { $scope.newExtensionData.url = ""; $scope.showNewExtensionForm = false; From 418320067469e24b34f68aac67c6c1b0db5bac9f Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Fri, 17 Feb 2017 17:54:41 -0600 Subject: [PATCH 22/40] sort by --- .../app/frontend/controllers/notes.js | 20 +++++++++++++++++++ .../javascripts/app/services/modelManager.js | 4 +--- app/assets/stylesheets/app/_standard.scss | 2 +- app/assets/templates/frontend/notes.html.haml | 10 +++++++++- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/app/frontend/controllers/notes.js b/app/assets/javascripts/app/frontend/controllers/notes.js index 5bb7fba5a..76ee180f0 100644 --- a/app/assets/javascripts/app/frontend/controllers/notes.js +++ b/app/assets/javascripts/app/frontend/controllers/notes.js @@ -26,6 +26,8 @@ angular.module('app.frontend') }) .controller('NotesCtrl', function (authManager, $timeout, $rootScope, modelManager) { + this.sortBy = localStorage.getItem("sortBy") || "created_at"; + $rootScope.$on("editorFocused", function(){ this.showMenu = false; }.bind(this)) @@ -110,4 +112,22 @@ angular.module('app.frontend') } }.bind(this), 100) } + + this.selectedMenuItem = function() { + this.showMenu = false; + } + + this.selectedSortByCreated = function() { + this.setSortBy("created_at"); + } + + this.selectedSortByUpdated = function() { + this.setSortBy("updated_at"); + } + + this.setSortBy = function(type) { + this.sortBy = type; + localStorage.setItem("sortBy", type); + } + }); diff --git a/app/assets/javascripts/app/services/modelManager.js b/app/assets/javascripts/app/services/modelManager.js index 9a26cfa14..37b24a832 100644 --- a/app/assets/javascripts/app/services/modelManager.js +++ b/app/assets/javascripts/app/services/modelManager.js @@ -147,9 +147,7 @@ class ModelManager { } } else if(item.content_type == "Note") { if(!_.find(this.notes, {uuid: item.uuid})) { - this.notes.splice(_.sortedLastIndexBy(this.notes, item, function(item){ - return -item.created_at; - }), 0, item); + this.notes.unshift(item); } } else if(item.content_type == "Extension") { if(!_.find(this._extensions, {uuid: item.uuid})) { diff --git a/app/assets/stylesheets/app/_standard.scss b/app/assets/stylesheets/app/_standard.scss index bf5e098b1..e9f7e8b04 100644 --- a/app/assets/stylesheets/app/_standard.scss +++ b/app/assets/stylesheets/app/_standard.scss @@ -157,7 +157,7 @@ } .inline { - display: inline-block; + display: inline-block !important; &.top { vertical-align: top; diff --git a/app/assets/templates/frontend/notes.html.haml b/app/assets/templates/frontend/notes.html.haml index 94b9e9dec..f259f62c8 100644 --- a/app/assets/templates/frontend/notes.html.haml +++ b/app/assets/templates/frontend/notes.html.haml @@ -17,10 +17,18 @@ %ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark{"ng-if" => "ctrl.showMenu"} %li %a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedTagDelete()"} Delete Tag + %li + %a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedSortByCreated()"} + %span.top.mt-5.mr-5{"ng-if" => "ctrl.sortBy == 'created_at'"} ✓ + Sort by date created + %li + %a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedSortByUpdated()"} + %span.top.mt-5.mr-5{"ng-if" => "ctrl.sortBy == 'updated_at'"} ✓ + Sort by date updated .scrollable .infinite-scroll{"infinite-scroll" => "ctrl.paginate()", "can-load" => "true", "threshold" => "200"} - .note{"ng-repeat" => "note in ctrl.tag.notes | filter: ctrl.filterNotes | limitTo:ctrl.notesToDisplay", + .note{"ng-repeat" => "note in ctrl.tag.notes | filter: ctrl.filterNotes | orderBy: ctrl.sortBy:true | limitTo:ctrl.notesToDisplay", "ng-click" => "ctrl.selectNote(note)", "ng-class" => "{'selected' : ctrl.selectedNote == note}"} .name{"ng-if" => "note.title"} {{note.title}} From a0d582db52fa3c8e9876f859ecbdd67e2a1501f1 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Sun, 19 Feb 2017 22:57:36 -0600 Subject: [PATCH 23/40] locally clear refs before processing --- app/assets/javascripts/app/frontend/controllers/home.js | 1 + app/assets/javascripts/app/frontend/models/api/item.js | 6 +++++- app/assets/javascripts/app/frontend/models/app/note.js | 8 ++++++++ app/assets/javascripts/app/frontend/models/app/tag.js | 9 +++++++++ app/assets/javascripts/app/services/helpers/crypto.js | 6 ++++-- app/assets/javascripts/app/services/modelManager.js | 1 + app/assets/stylesheets/app/_standard.scss | 1 + .../frontend/directives/global-extensions-menu.html.haml | 4 ++-- app/assets/templates/frontend/editor.html.haml | 2 +- 9 files changed, 32 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/app/frontend/controllers/home.js b/app/assets/javascripts/app/frontend/controllers/home.js index 79880660d..ea70d15e6 100644 --- a/app/assets/javascripts/app/frontend/controllers/home.js +++ b/app/assets/javascripts/app/frontend/controllers/home.js @@ -29,6 +29,7 @@ angular.module('app.frontend') modelManager.createRelationshipBetweenItems(note, tag); } + note.setDirty(true); syncManager.sync(); } diff --git a/app/assets/javascripts/app/frontend/models/api/item.js b/app/assets/javascripts/app/frontend/models/api/item.js index d486f9739..4cc38b8fc 100644 --- a/app/assets/javascripts/app/frontend/models/api/item.js +++ b/app/assets/javascripts/app/frontend/models/api/item.js @@ -37,7 +37,7 @@ class Item { updateFromJSON(json) { _.merge(this, json); - + if(this.created_at) { this.created_at = new Date(this.created_at); this.updated_at = new Date(this.updated_at); @@ -113,6 +113,10 @@ class Item { this.setDirty(true); } + locallyClearAllReferences() { + + } + mergeMetadataFromItem(item) { _.merge(this, _.omit(item, ["content"])); } diff --git a/app/assets/javascripts/app/frontend/models/app/note.js b/app/assets/javascripts/app/frontend/models/app/note.js index b7b4a6c06..e79a64682 100644 --- a/app/assets/javascripts/app/frontend/models/app/note.js +++ b/app/assets/javascripts/app/frontend/models/app/note.js @@ -56,6 +56,14 @@ class Note extends Item { this.tags = []; } + locallyClearAllReferences() { + super.locallyClearAllReferences(); + this.tags.forEach(function(tag){ + _.pull(tag.notes, this); + }.bind(this)) + this.tags = []; + } + isBeingRemovedLocally() { this.tags.forEach(function(tag){ _.pull(tag.notes, this); diff --git a/app/assets/javascripts/app/frontend/models/app/tag.js b/app/assets/javascripts/app/frontend/models/app/tag.js index f8361ca5a..be36360c9 100644 --- a/app/assets/javascripts/app/frontend/models/app/tag.js +++ b/app/assets/javascripts/app/frontend/models/app/tag.js @@ -55,6 +55,15 @@ class Tag extends Item { this.notes = []; } + locallyClearAllReferences() { + super.locallyClearAllReferences(); + this.notes.forEach(function(note){ + _.pull(note.tags, this); + }.bind(this)) + + this.notes = []; + } + isBeingRemovedLocally() { this.notes.forEach(function(note){ _.pull(note.tags, this); diff --git a/app/assets/javascripts/app/services/helpers/crypto.js b/app/assets/javascripts/app/services/helpers/crypto.js index e93b9017c..ab09b41e8 100644 --- a/app/assets/javascripts/app/services/helpers/crypto.js +++ b/app/assets/javascripts/app/services/helpers/crypto.js @@ -60,11 +60,13 @@ class SNCrypto { } base64(text) { - return CryptoJS.enc.Utf8.parse(text).toString(CryptoJS.enc.Base64) + // return CryptoJS.enc.Utf8.parse(text).toString(CryptoJS.enc.Base64) + return window.btoa(text); } base64Decode(base64String) { - return CryptoJS.enc.Base64.parse(base64String).toString(CryptoJS.enc.Utf8) + // return CryptoJS.enc.Base64.parse(base64String).toString(CryptoJS.enc.Utf8) + return window.atob(base64String); } sha256(text) { diff --git a/app/assets/javascripts/app/services/modelManager.js b/app/assets/javascripts/app/services/modelManager.js index 37b24a832..ef67bc86a 100644 --- a/app/assets/javascripts/app/services/modelManager.js +++ b/app/assets/javascripts/app/services/modelManager.js @@ -168,6 +168,7 @@ class ModelManager { } resolveReferencesForItem(item) { + item.locallyClearAllReferences(); var contentObject = item.contentObject; if(!contentObject.references) { return; diff --git a/app/assets/stylesheets/app/_standard.scss b/app/assets/stylesheets/app/_standard.scss index e9f7e8b04..60cc3601a 100644 --- a/app/assets/stylesheets/app/_standard.scss +++ b/app/assets/stylesheets/app/_standard.scss @@ -89,6 +89,7 @@ .wrap { word-wrap: break-word; + word-break: break-all; } .one-line-overflow { diff --git a/app/assets/templates/frontend/directives/global-extensions-menu.html.haml b/app/assets/templates/frontend/directives/global-extensions-menu.html.haml index 2dd5df9d1..a28067719 100644 --- a/app/assets/templates/frontend/directives/global-extensions-menu.html.haml +++ b/app/assets/templates/frontend/directives/global-extensions-menu.html.haml @@ -1,8 +1,8 @@ .panel.panel-default.account-panel.panel-right .panel-body - %div{"style" => "font-size: 18px;", "ng-if" => "!extensionManager.extensions.length"} No extensions installed + %div{"style" => "font-size: 15px;", "ng-if" => "!extensionManager.extensions.length"} No extensions installed %div{"ng-if" => "extensionManager.extensions.length"} - %section.gray-bg.inline-h.mb-10.medium-padding{"ng-repeat" => "extension in extensionManager.extensions", "ng-init" => "extension.formData = {}"} + %section.gray-bg.inline-h.mb-10.medium-padding{"ng-repeat" => "extension in extensionManager.extensions | orderBy: 'name'", "ng-init" => "extension.formData = {}"} %h3.center-align {{extension.name}} .center-align.centered.mt-10 %label.block.normal Send data: diff --git a/app/assets/templates/frontend/editor.html.haml b/app/assets/templates/frontend/editor.html.haml index 41dedafed..e8956bb3b 100644 --- a/app/assets/templates/frontend/editor.html.haml +++ b/app/assets/templates/frontend/editor.html.haml @@ -6,7 +6,7 @@ "select-on-click" => "true"} .save-status{"ng-class" => "{'red bold': ctrl.saveError}", "ng-bind-html" => "ctrl.noteStatus"} .tags - %input.tags-input{"type" => "text", "ng-keyup" => "$event.keyCode == 13 && ctrl.updateTagsFromTagsString($event, ctrl.tagsString)", + %input.tags-input{"type" => "text", "ng-keyup" => "$event.keyCode == 13 && $event.target.blur();", "ng-model" => "ctrl.tagsString", "placeholder" => "#tags", "ng-blur" => "ctrl.updateTagsFromTagsString($event, ctrl.tagsString)"} .section-menu %ul.nav From 511fb55b91d9d20d050442efae5288b9f808b44e Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Mon, 20 Feb 2017 20:23:16 -0600 Subject: [PATCH 24/40] set deletede to false on import items --- .../javascripts/app/services/directives/views/accountMenu.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/javascripts/app/services/directives/views/accountMenu.js b/app/assets/javascripts/app/services/directives/views/accountMenu.js index 088b98969..e243a35cb 100644 --- a/app/assets/javascripts/app/services/directives/views/accountMenu.js +++ b/app/assets/javascripts/app/services/directives/views/accountMenu.js @@ -188,6 +188,7 @@ class AccountMenu { var items = modelManager.mapResponseItemsToLocalModels(data.items); items.forEach(function(item){ item.setDirty(true); + item.deleted = false; item.markAllReferencesDirty(); }) From 99d68e4180d15653515e16268f5c5f2b47b1c9d0 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Tue, 21 Feb 2017 10:53:52 -0600 Subject: [PATCH 25/40] catch sf exts --- .../app/services/directives/views/globalExtensionsMenu.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/app/services/directives/views/globalExtensionsMenu.js b/app/assets/javascripts/app/services/directives/views/globalExtensionsMenu.js index 921dcfecc..8365a67ab 100644 --- a/app/assets/javascripts/app/services/directives/views/globalExtensionsMenu.js +++ b/app/assets/javascripts/app/services/directives/views/globalExtensionsMenu.js @@ -21,7 +21,9 @@ class GlobalExtensionsMenu { if($scope.newExtensionData.url) { extensionManager.addExtension($scope.newExtensionData.url, function(response){ if(!response) { - if($scope.newExtensionData.url.indexOf("name=") != -1) { + if($scope.newExtensionData.url.indexOf("type=sf") != -1) { + alert("Unable to register this extension. You are attempting to register a Standard File extension in Standard Notes. You should instead open your Standard File Dashboard and register this extension there."); + } else if($scope.newExtensionData.url.indexOf("name=") != -1) { // user is mistakenly trying to register editor extension, most likely alert("Unable to register this extension. It looks like you may be trying to install an editor extension. To do that, click 'Editor' under the current note's title."); } else { From 2eefef7e607051bd58bdb7df964147b44f5e91b7 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Tue, 21 Feb 2017 11:22:09 -0600 Subject: [PATCH 26/40] update favicon --- public/favicon/android-chrome-192x192.png | Bin 1112 -> 1161 bytes public/favicon/android-chrome-384x384.png | Bin 2310 -> 2454 bytes public/favicon/apple-touch-icon.png | Bin 1000 -> 1046 bytes public/favicon/browserconfig.xml | 18 ++++++------ public/favicon/favicon-16x16.png | Bin 552 -> 556 bytes public/favicon/favicon-32x32.png | Bin 670 -> 677 bytes public/favicon/favicon.ico | Bin 15086 -> 15086 bytes public/favicon/manifest.json | 33 +++++++++++----------- public/favicon/mstile-150x150.png | Bin 1551 -> 1606 bytes public/favicon/safari-pinned-tab.svg | 14 ++++----- 10 files changed, 33 insertions(+), 32 deletions(-) diff --git a/public/favicon/android-chrome-192x192.png b/public/favicon/android-chrome-192x192.png index 419b3202aa3ec5db2b8a82856f80e0f77eaa77d7..2f4f3ba3c8b679302e6d69efbf2ab88a99382242 100644 GIT binary patch delta 911 zcmcb?(aAZXr2Zk3s1TpPgxEX#7#NuKJY5_^DsH{K9qSQXC~^Gb=5LQ;q>lP{w#;Pi z?Rv3k+f~<=$k@`0r(@j&L(C0b-z?j$vP2`m;FPq!S=IcgFG{ZBu4h&pHJC2q+L5Qi z9PaXLPQ(MN|BH*$t$DuL9KQcONdC$9g~@VUOLsE)FOp+m_{tE(u!5bz!IpvH6(d6c zKSM(m1H%<&h6P|bkko`{kDKq!Uh|1Bi^astD$KKY7ULPFhFzrQi9GM!~YsoO@ z;01%ig$s&9vUM z_EuC-iiwo(w5zYTy~+N@^IuQIVaxX)ZAqDhZKw9i&KG87nDL@$$-4Re@sH(ZrSi@A zwv%U)*S2k(*BVaT44qW<>u%k#hJa?jQxRulbNd7380BSVKL4Mc+pp&Mk0HJ+ZEO4_ zt*ZJ+rlj`i6_0#X)0|%=hl)So*pU*mqp0`sm8sVL@pkX`-`lm{YyXF0_M|@y?{iPJ zT2;S(J>$;`|JpYtGpEcqi%n+lm>h!w)AE&q2oZ$ShHA*h*Pr%c>oW(WiJC1XH zSQR3-Ei>k6-nSV)Cht1ISrENPOJ-J}^&6pv%X1j!CS9+*dN^I|=Z6PAW%1Vf4ctr@ z)>c`BD9XP(`6*uWlOmIa`abj8Rqcx(^`7`SS@3}Fc_aHU+nyUo`J3cz7+~RsBf}6w3oAntD?>AF14AnV e1J3uA9w-`e^HVa@DsgM5*mUpFp5`D0BSdYm+ofVMD2J6E*~` z2z6WfQtLL$yRM9fE?aXSD!oW_T_SDn8KNy&D9GVzs(7<|!K9v9Jsa9hx13Z8);c{U zDXZqp6Q|k5b9eSWc=12x*S~Gg_y4HfF}?cSQ;ERY$0z79IP7AGWO&KMu!Nfd!o$lk z=6O-CI`3a5}C)fU5Vy;l5cmGGaOe*7CM*HkL zZkyKZTBWZLVrriGf8+HPk>LkC?(Wh2V%{VA-IVoCeT2!?`Mvi1JnnoWxiWX7GUT%h>iZC0@R_j9xFD?Ro;{QkAM zmCx$x_AktH%xoK)K0jORf8Oyjdzoi@S;N7zSqHcFtF3SMpUJZI79-OuMvd=re-2zI z{HlBFle75&$)$Je=j?J3u1!64;?eY3jBejG-kN<;o3nGto8oiavl!XDC+~kyd-T*9 z&a({f!;bB$T*Egflq;e9lYMg}Ye~xSU29)&miVOo`{uR#RgAZclJ9P7)oq)n6~Af{ z<6egCMoD+SMe>&F_VbF?UF5#7=ntpNCaG6?0o!(+|HZ_2TQ=dhIs?OGW@a(19Ht%f zUi!%U-x6HCus>Ty*tzfPtk{AtX7MvS_FvA@D7pQ={6$|{d5W8d94j!iC)yd)h=7?ymV&m6V)QRZARhyM%{_!wT|CvM1nU|%+Ic}vQEtNjcN45}rr z5hW>!C8<`)MX5lF!N|bSOxMs**T^`;$k@uj%*xbK+rZGuz@WH2&u9{gj@LfHgaB0(iFHY5-rtl_c+@&&X&S$nBa z7J*t6T0lkFWRn&|BwX|g2Ai;og0h802z#3Q^Ukz0cjitzbAOyUbDsA+bI$wxIOn9f zyE&*R9Z&)Qs5m*=c>(}~{%wj<0C1~ks8T2Ef5!C;0JYi5+gIhJF^cNw=?XxcJ^;`) z05+u%^d5jHEC4eU0Px=dpcz)&>~Tu^AoHb*gB_5hWcNLmMhYw^S9^sC8TC&PsyU?L zb^sJDo$SsKVg&OejMNLXgHM;@9sGxhmFF_6jRy`YCm%Xz`}2by$9NB}2|Yd4P9x)y zNg2)glI?e|TQQf*Nj3UsZn3?-I%?2FC=AMbRDRSPe{$$kuMEt#P2}@Pk%gG}oc;J- z8)w&AcRFNx@16dC)O{ProzeksfT{vy4!U3{1PBMI@Y4tXB&Z)r@v@*Xyl#P4M|(X@ zfZ%rv6Hh}LHO}vSYc8dNZlMie5E*3F zKV6g5(d}RT!pJRYeo;P6CM|Dtk0@jfIcWsCflRa+dwuy@%VsolsJV%@DYF0iYZpPC z$TZ+%u5wl5keLR%iZ1gahAn)Vb~dj5e8sSO)CbGfHv^;!rDmTMVBQwqU%7DHbH8)r z9I-vd{|?l0ecy|wt(y3_hKz_!&Z}neq+zyNV9PknNJt+g%!O}i&R}oNoc^ASNRkiO zPV6@MrX=pfE)Ekv?|#U1uh#ft(#yKBN0J}BJM-1wuA2Kon>RWyzhiGt&z?$*gq5Tu z$*tEfFSlk%d_QRA@jq*$-v_gzzWHa8v8ZbEoW&BZj9;FZxAUu{R6Tpw`d{{Nxyq^; z9o|x@DDPhGu<8ccoAu>y1#R2ru*xXf+)ZWD*?=EG_-dIlc`^%K`Kj@)rZ0hdBlATg@v8 zgEz*G+>dkw@jTyYn=c(c9`zq(DG~IS7{^dhG%1uTFBw0n|FCQIm@zhuN1Xu0?a#wQ z68P&XKXKYKttv;hYCqThq@%%bo;q9CB~I$q-G|5UW~pO%6l9+join*33VuB}q;p}D zI(In*9u}C95U|tm78~pl6zTnJxv3GMNNGvXE?d9t`;~U{2){a2lHiU7Rdd2|`FS2g z5WhCNIF7V%4f=@!=tbzfl6+xZa%16STu5g8JOvnu3(;=G&j93op+X# z9xI{%GVXa0VL{V_|Mv&-WX&yyT6ZuPgo})LT(dG=RKvZ=f6^L_0Tnv6Syk%~htd4Z zdi{p=!=r7bw+`r8e-qAiyIlT9cV2I-C7T}xCc^%QRE;)z?MPT0w3785-0_! zs=ymG$#kH>VOdBW+y(#tX~EEzR3w|-iSz@O3%OEJ3Oy5-`j($;Y)i_#xuBSNzgNVu zcBPbiV1PvUyR-kV*+c*JcsGEaMVX*K__>bw?e92p8~)z+c77`8QTl^5>7qf2((QdY zV+S)rV_XXCV3>u!>X!@WS^E<_Pcj0^q?g4Jmc0+l?<$Lir$*4E4j5r^X2-Ec$Bl3V zoH-t6jK`T8VsUsZc8@65@jnR6P(KDe_KyiM4}ShAB@p8XJ}l3x(WtP9P&y-+hGNBr z(NK&qmh?o5DV|*3s~GN$!StTz^*O!s1aLj&6I5lS9?~UO4UR0?cUcXD!>B+>n+4PWEngcWo(2zX3)8yEgy; literal 2310 zcmd6pc~sL^7RO&UqDD{~Bv`cxhea%lED|w-Y++)gB$R|5kwu^>vIi4L6Mk`lVg=fI zpvVD|jAO-xtx#ZCOo~Enh=5QQ0SS}?1ZFbqAe))^XFHvL$NA%(d++Dn@44@Yz?5BC57bgAdaz5u|WcW0Lt0O8}gC~ZK-x_P?+P?L|@jzDQ^Lex25ZvZmP0D!Io zu&G6m3V<{l0OrC0AQb_CODbvl)Jb~*kMyFDfhId&A=OrBHr3k`J&wWQbk3C3>$_{o zBP!XA$sJa|W_{@weFR#xziZE8MY9fDT?#5Z@Ql1B=|4VFe(N(M`(PJlJ?>xx>Kl_F z33m5G-8)mTn4n6&Tgz{t%0<8gy@ z(4i1W$S*$Xn+L^bc&WXbimqt^+NLrTOd?&efCe04<4EYeC5=BI*a@#z+R+vBb&5O9 zts9ttFPf55DeIZ1T+?AqgH>Gh;x99J-jEwlZOiQ$SF4GU8jpOEdN?~QxUPQ3<<4LK z21@_&U00Fg8q{IQ+$p1oILd80aec?c6sg*tUJaoc`iuG>CiIk3=~c5)*Op*)C-~!y zk~D_~O+c=3RYsb}#XJTP!YhmFF6K8#TP4SwH$0WPOlzoqV@5A$B@A|lZ-H? z8I(5_u(z}3t;^?zTW#HZdu9$GescIw`b=$x+2ehK(Ro{)(20S zywcRuKH2dJ)q%V@W+Rz}rC$e%kwDGh&>A$BXHqZp&YlMgzkPIkUDSVZiy@!Axv!ed z?A?idMeAee1#+R)8MCp>!lMfbZ5ucBdQLk`AQqIRPtHz2C^UCoyt37;bG9%+b`^2C zsb!6X#t*Sy#^-mHEDv|mos;Jaj^z)xu7zC5zRz>^Pl5(Hk80#b-kcv>G<}Jgn(k(j zVB?>;*+1{AN^4!p%VS~<4vW=obG##hjg~6SPE}fbXi9fPf1=LT2``7p8rWE61}tWs zS|K@AbS8j)D9CTTjOk(Up?IhGV~fWwZwo%hb5+4tUef#o87n{M#8{q`$q`QvUv7Ln zuqTsSF85Li=_bWBJs|%&-iz~Os=3%@?Cr`zwJa!S>}zv7Sr+4|RKuxX9f$H_|AM3s z#j%QwzS>)D!o~S=R@a?Jx^6ApD4AttS=!0!V3MiQmilZ;-31)d(fCEI0cLM?wEJfq z~GDr*YYwUf988dDis)Nd)z5 zMPN8|NAjY*^_%5wvCI~mu|$3t(~eR#8C5s7;wYudQ?|m?yf?)5qIT4fopp6%D$%pF zkIx$~;roAYSlb9L-YAnQzI0NlWpTm_Gxy%1NASV6wJ`nko*ChmIP1Ret)Nf{C8S>? z_6x8oQCjE+jmMpfA{D>E6&NR>^yKE7Ed(w$fo2<%3@gKIkB#B^y(Kd2OKQjm@076? zi4_U{E7S_1)S~&#cgQFMTzJM*MZfeAzn3E9_|d%<*B{+LsmgrQ<2)io$oSVy#D$3t zMijow!jA;;3n};?|5Yxhm`mO62ESfTY*2>PFX=hKe zA(9+UTiDo=Y;01q6TbbOAU^RDD<=KF3*f^8)>=V81~ZWD8^IwYr6k6%E=LpC=}FN9 zRuWtLCE=D#F74fw>~C%^r*|r-Q@#LUjIoZwV2!a}xAhTN0vS5_^b*3jmPzOw`t|uU j#j_*N(4EjZ@p)@7hdaPta(5`ujsR3o8oAOv{OZ2}UGja` diff --git a/public/favicon/apple-touch-icon.png b/public/favicon/apple-touch-icon.png index edd53c010c88933ec9c524ef718db117258b4d35..62049710351948e16c5b94eafcbee571dcd21128 100644 GIT binary patch delta 836 zcmaFCK8<5SN&Q16Q6WCQte}T?85o#$d%8G=RNQ)d%QHhbQ0DkY&*1If7Kz>N_#LFN zgN4O5LLe|&(Nbelvsjc3hhB@q4w)+oS0cWo6a}!@ep$k@^_xlu$E9u&F58X{u~iv8 zD@9#gqi&zkII=JEijR`w%%9V1>W^32xAC7jGy6>S#kz9`GW_b99lZH6E-YYWG0C{V z#3W^s!N|xfwWXnfE!H!`R_5?QDpi-3TZ-LR$++;;>H4~DmRnUFeha_P^*t2N zRHAVINpRZ>#tRD^o7s4Q+L0Nz8_o9Zsi^q;=wyO{1lRF*U)0r)EbdM5&v^Z)Qg7>- z7~B1s_3>^7ngWj;nO-?Nw{GD@*Cs zigsLkeEM_VqBB+|O8m9E<4((m-&W6j;qJY~X{D8&8u#C{Vzd0b#qT426g|1psLLH2 z*`B@2b>U_8O*y5qd-IR?o_Tow?_BB4^UjqzPrBeQ?K7jk`uk-2dyVhUUG-gkp2O+2 z`*QWp2gmoXKXfXP}w`*~lk-xCS5U3aqfeBCWl zvg_;|`FBrsdQ4U~J)bF3lC{@pYs8+_>sNa}+^5QFyTM^$vRLoJ&{c(DT)*nJ&RWQ> zBW3?h>&w3_0$)N^KlMe`hn`z~>RXflBHtH&s-L3k7L}}?T@`#V-q&oQzjjG`>eI$w z3o^a99NR^Hb)-K1IA^6-@}dhj9WcWhD6(tPh3usZ*1yd-o05KJlhKR(lObiYGP)&Z zvo8oI2XFk>KXskfe)F@e3%sS_u~ZLd0U6-}x1@V-&RVgZB_o|lswDEl+j&d*j$V$_ zx#hvTzf9w>=3F+VQP~yNx-&kjNkBeqYJ_xOJ z72?}1a_OM6ESHtWw75MICEQIdN1PkWIuw?iNL!##ap~b&KEopMw->T9RbM#%Dn1c7 zai#J9HB3ME=Kb0hxwn4a(eKrFHXmgFw$J{aZSkKI2W85b9lZJKO)@SpG4V=mX>e#} z(^a^zz>%8+N$6jC$r9@f-@Ag3FX-~E&SIRq_=U}8W5(D!qEbei1Hw0c%((w#+r3}k zeSZP9^GX4AE^ve~mM#{4Ki^iS;=<+2(o&V5GfmkqocytP+hq2#ouBJ#k3>GuZAlO?|obYHstlB6I)kdYIpIF+Oj)<=3)>uIWnIK^NDb7yFaf`g;FX1z}sM6B{bZ z;?I?J-e{_;RC&DX&ed5@>$Wi4R%@M}wBJC3Z|!qa?tOH`T1XWvbH_nsq)2XZJEsXe95|X7x%R?dcNRK zJr(*f@Aa2s+w`(dUwmwLH0y%pwL@~Idlfisw*_y!Rk|$r_mwSnhy9sOUrcS zm&cB#wtr&F8yH0jzxH<>m#*FURgU|>)!ag8WR zNi0dVN-jzTQVd20hGx2khPp<^Ax6el24+^Kmf8k}Rt5&e?Rk?>H00)|WTsVO(ZCJW N!F21I-sI)X`2ZB|Xodg) diff --git a/public/favicon/browserconfig.xml b/public/favicon/browserconfig.xml index 03c04ac40..b3930d0f0 100644 --- a/public/favicon/browserconfig.xml +++ b/public/favicon/browserconfig.xml @@ -1,9 +1,9 @@ - - - - - - #da532c - - - + + + + + + #da532c + + + diff --git a/public/favicon/favicon-16x16.png b/public/favicon/favicon-16x16.png index 86d316815527f95400145d4ce5cbd2e839113e40..adb406548c03c4433503096777bc49c4d6a0ad39 100644 GIT binary patch delta 399 zcmZ3%vW8`XO}!ceLqiJ#!!HIP{epp^)PRBERRRNp)eHs(@q#(K0&Rd2OiAAEE)4(M z`_JqL^4Lo}eO=ifGKmWD@ymu@^acv8@pN$v(Kz2b(U$9wgFx&4zXyZf>V|w^V^iEZ zJs@XKbNGhOJZ5o64=Ft1G1Hvn=IG+_Fl+NBZ65>U`lANM{okLt8zyu)ZWG_BntD!B zcZ;mCmDu4*m${s;;@oP_cO_TY^6Gf=?f%ytm9aI5EK7M*Cq##iETF2YFd)^mS!_z%4G$W<774h!ap~ho_5Uh{pNe zi?)7FfdZ@#-rxKE$x6$co7>=GRFczKs~jJV+>H9W%u}@!w2vg1gsp36ImFaqqW15@ zJYKbq=?7*e8QEK=`>9!!DV|Jzy7R)tuNrB`rh2C;N=m;=@wg}_QopKA_~H?x{RLSM zd~z4Pf4Y6A=Ech;Z%<8}E#rLUQ%XzqbFcZgc^o$|-EW#O`Nrc#+di;Od-m!-_ta?7 z9l^g&-K>dyezShr5k~WnTXtnOt8=I9>E<#NK6&LZ|It)+*-1}3vw^-)Epd$~Nl7e8 zwMs5Z1yT$~28L$3hK9OE#vw+=Rt9EPrk2_UhE@g!#qD{MP&DM`r(~v8;?}@)>zdx= Hy^Q$)VCcOQHn(hx_mRx^7qif#u55 zi14GG7S=b4K5!mkf2cD5z(Gel2ECvl3s1cr0*aIBeK|HiHrN{2Ejhc}X8ZD&e@#EX zuYA7n+^Gfe^Uv^~b2H0jkub}QR}~dHsWqd!aZc{ydBIG))1GoTyf|2VioZhCY1=)4 z{|o{(AJ>FVFPq4H{M}`y#=yx{CXdgpi(I|P#Np%~G4^jPRlHg=HcYc=F1!3mj7Kuu zD9N>5v-f0U-rah=vlikjElgequ0Lmzf8oiQ%&qoC%IkG4Cmi;+S*0Ly>VztL&=va+ zH&yI*7X8jEalXFj*8VxS4(wv|ms|CBs$t{gCk~N6o&P`jaoy+#vx7{Mo0;jFWv*!vn7imD~95hW>!ldm)KN}1~#80i`r zh8S8{8Jbubnra&uS{WEvuqR)g{Et!G9=o(>)a^{5Ig%iAg7ec#$`gxH85~pclTsBt Wa}(23gHjVyDhp4h+Dz7G$_D^huERb6 delta 477 zcmZ3=I*)aNO}#1uLqjtI!_WUf`XvKHsR0ASs{{rHs~HRo;`x)}kGe51Ffb)~ySp&_ zZ|^^|AIRe@@Q5sCU=Rc96jNpN3{v?36l5>)^mS!_z%4G$W<774h!Xu zEN4AovH69mqQdKdHinj0al&>FVmV&MIsajAc+kFgPM~wps=ScNOibKiB3~A-OS(R< zO6b5OUCV{Dmen%~cd_MW96PI!@9TGUSETW@yQiYUdfq48ktoaUDp-1cTjFHBM^+LO zzdm#MveMsH>Hf=%bO$zH{oK0(b`0FG#@6Ug9pV{jBgPVKBd$-jU_b>m^pq#E$ zo?Dw6_dwBZ!Lo0z<{@>u&Gif!turS1^>}SpQ*l_4vcG%%+9!KNST&?yoch~Xw4U(+ zvzNJtnrzEX{mJVY#ih)24GndTj6;l!tqjbpOf9ty46O_dire!hO@4q=n(5Xxy~$=w F`2gvYxYhsw diff --git a/public/favicon/favicon.ico b/public/favicon/favicon.ico index 9ed62469316653e8289ba579cc6ae4c962b41bb3..4f73435c7c48dd51fc75ad6281e6c51b7144203d 100644 GIT binary patch literal 15086 zcmeI3S%@4(7{@!VSJY(JfJPEcMocsh9zn%`f{G~S!2|IKo+v>)o`m3o0ng})k185L zVemi`MMV%5qs|H@QB+hEd@z`mfEz{P2Cob;8GpZ-scgGWPw&i5@61Yn@Jm&99pAsd zt*NQ1olR1R8Br7!3ia!ODEgjz1EiMUx_=Z6Qeg>oXcFBB-mluC zTJk^qcLvWi3l5(qs8!9u4kGh3d<|dqIBHa@nu8ro<^!m}FE9ezdki(HO^s?*b1?HI z_yaD50vy+4s7Y;VRBI#szj6Om=v7mj8gu>K`3vRo?@ru<*F63q65UDj_;)An!E2}E ze*hhy04KwVFdNeBNGe!g3J_n1aoN;5t^X*zeF{6k_0n=kvsG016-Hql7=wvgr}fXn z@fL7RwHq#hG`pJ$)|a>7K$xiI`u`oNJpTFkaYVQma82Fn`ad5(`S{7l&-DHNn#&*W zTkyMd53GPR8>B+`{(XadH`foIgKdoId4PN~bFJ_?1pS^vwPKxKKj`}@2ztIGzrdRb zb{6-4hEV_ZgnCK0v(SGL&OB2Jn%0x|q&U7V?&EqHY=Tg4nEVoG#j?Hr^kTfT3tb7Qu-Hl9);P^s%52L9dqCg_ypXW zI3I;)aBtw*>w9nw%!GC&auwVRE8#LY1rCGB*i`7BjJ~wCx&E{WWm}%d zKil}G^`6H+EkfCr=kd=rzG=Ou#ox8bZEzl(-(#prZE7rmXC}eS`%c*Pit85FVZBIL zQ>its-+j@&@G890Dw+?(}Wr4N1SGnn~zVf@?~$~&3r zLtnZ7v8d+pPal7tOND98&&YW5yxHH@`QCo!_`=wAyW`Jy&_BW3(CEBoKKDO^t#CRd zS-bu{H)4j5K89HhI$yxOVeriLOh~fae|z&!9{=u+zsniWj{nc0(Msl6?r($NVHqS@ zyW{_CI`p%(9ZqV{`32nj6Mlr_87vz5jJIn zed|MC`V8jWeiy8sBJ`!tuAAotL!8Xq$GlmrRLY5rjFgLMY};Hej`o*}yA~$*gAaY- zwrVcu9S{ZBcTV9f|6eO`>sq+JMQl@mE8#I%0as5D>)OV)=5K&j%{I2pqWxU@HGIM5 zka3+5w(Su{sFGp BK=J?p literal 15086 zcmeI2YiJ!s7=|~d8d1_jycL7wfJ$3wV-QpnY(+#1VnnQH5yhfni~bfA#Xu!iK?DU0 zibnnA1ux(qwQ3@0t0)yOc&Q-O{D`+|RMZ}Ajn8vVCt26*$)3}*$%*U-PiA&@X1;g7 z%$c2?-6(2{+N1vdh_n|HjYbtpg7DJk;{|N~7_mW?mR(Xc^jSCxlGZ`NUGO)A`tGG23t6q%`t`dG$`CZUCVL4k1J}o{ zuh+n1@H>QhrkkN2%R|5G<)f;1Cr-=Q05+AJ(h=l{rAH2VC<7RXF7h5 zdi9KZs#h#8{dVr&`;G7>{0Q-D*~hVKlMi7Nl%NsIQ-9Fo^Pqk%h3nvYxB||CW58#a zMr|(iH>xd5yF!1KDCJkDh<|>u&C+ub|143;uTByF{9>D>=h@=#I%^}G3+J^MYEqjT zN5DOkVCKX7Yu75SU0M;fxgJcuzdnSrJHTfTpGR65HL6w3adY2(AraTUVDpm|V^Hl+ zN4=8vC<^KOma}qie%dur?KkhVV^`JBrqOCT*j7zP+pFnBM>WmoIzt?H5mvvLsf3wd za7dR?T!M38>N_pIqB4q40bn}t>FD4@3~4J z`qF1G^RGa1?hNOfnd(Ddh5zZO7V)2b{5g9$tu2Mar$aZDb03s1R-@%mYp=S|@pm=- zy65*btcIkypZOW=hTD=SW5*RjzO_YUqFdJk3lG#T6 z_t25=;k-WA4>!X~aK3Xd`77|;IP^oBHR|8~)`RcT^y71^I-C=I-)EWexgA`?q}hS^ zUqq+AKY9ZkYmNo+|Nbs)fWx4c9f<$X?_x5>_ySl1j=duxi#5Z47C&iq3jdAG|7r28 zx4c#3{~8z44gP*yU*&;)>qB48p}~g8_#SUNyan5G!oKyPFMS4EMdlW`b&k-NKIh%s zFBoNKW{~^Y@Z@AUmGSZN@c39dW1Jt`ULKz4Di81JP45RE`oe9+T+llpN-(ug;Vl1O zD^YYMTvaEwDS`Km`{8m}ml5mQ#|H&5KHlaZU!eWKDtjn1qkH{(x;~KivDv{rqt654Xy>*SVSr zPH%GCzGZ85>UcK{<}e$3jB=(_Ynq_lPBmcqrY*o8Rfxy8dp^mTIQ0iI`)L-O>$_L8 z<76)O^*}NzhN(`@odR}*C2kvUy>_JLO>g3(6LCG_6eDKc`JQn!FFqz3c$aq$oo&SLNC4{_?>+d*wk)> zY8*l#p#uU8!B|RD!Wh(NL-b{R#oR#UZe>BGQO?5W>xX4(f|Yzh=;%pJ>5sQ1EE}|Q zs__a;dDI}4g%4~)Ef~=z)iRlJ*wKTk;|phuwhP6zNWRinx?62fp_L+&Ql@1zme^== ziTpv%hBYyz?$_i_Nz?i~7hh#Ov6|5dUcQD;$qM9@8Il+vQm7@m%-s|Z8z$GH+M0lf z$1fu5{IsncntasJjaom#VeOszYu68|ndpX`DGM9!nHvp=HETX{=~WkPS#_WGAg76r zb9r}jDY`86N(8Cil@%68;xjZg6B?d0*}EjpD+|%t*o|sQaCD{WprcZd$wSJ7a=lF^ zXe;!|EJ&CU;nf$xGC>#r)A`@*S;VU4uAO^UE;kmLdXgJ%2=Ye6DZ23|Rq~egCzw=& zK&?$i$y)ZYBrSL1Ln^yW{S_NCuBOvERzqROhn?xXNVEJkFFqE1REq5)b8*zDVSAg& zX7=tFTR(_cw195j?cZ@k7Q^Mc6vgPy1;`DtQBCb$3G>f~YT9V8`!TdM ze9Dtlupe3C*B5T3vA)#zU&)(gQ#{k%fpfwM9JOI^dj4aRO8*$`O)5>&DRdf|D(KC7 zbALF^C^d_kN1_DaS7?pMM?%Pk0b>4~2Z^~qeFMF*mbVY|B#H6Y+kG+7X#Su9N1Yss z$nAKJ$LSWppU+l4rKiMzCl{7?S-lR|{$gBX7rzp==8=xxPA`5DsEa!HHn+33zzj|D z2^*;I?>XZt^LQw`XT#B-!$Zptz5fPkw%Y;yWC#B*feK1?9l)%Ay*cT;(`t2q@s&XN zb^e0}<>5E&0iBGl~TVSyBo7a)O{-uL1qmp4L3Xa+pMhH4U mI6yo@JQPk02`7f)gNWflK_6LIC!qX)jZ0g4mY|35{J#NFZ%P0F delta 1191 zcmX@c)6X-Zr2YZ7xHy~jylo;*3=Aw=JY5_^DsH{K>)$b@ROa}{^S{#nZeGSYGf;^8 zW_;(OCaaK$D~gAruPbq_%w}bkbXl(F7^L3$O36`HtBKE5)@q7i2Zw8chv{_uD6-)U*{8fW@4K)_@VR?+$fMv2VohG2=ary_0R z?S0bg`?lJ;q&nVao^xnxrDeDA-ER`iCXJGc8X`S2LU$kDI?8u@s*?~o<{@F40du-_@tBVVdZB~1p>iSIGvo9t7TblpA z^Zu3-_TQ4*XnVIeIdI*n+?}ScH(4d~_Ai)hIYEB2t^Bjce{4eJAHRzQbX6B{`=D`KQ+6o^rvCUx5)XOKIvAMZ%#|CwSMi9cjC2W$)7WO|Nfc! z{!>bQ*w@^))SG30&Im`|KV)mo!X|ipQO+6ubdh8B_Q93**S}|WSqiytUl(@lQ;6B~ zNB>LT{JAr8?asY#bHz!5kzIW`*%OIKWPcQb&dun;@+0U3; z-`vu?${+2pn{9Nh{d3H8-^fef($d_*^-{&3Pwbh#UG7xP_TIHOl8+wb4sWpXlA?ec|m+{r72`EVA->M}-R9_%-r2ZkMz~b|PRr-p_pUtn+D*pX?A_<5H6^!{ z`_qc|WktxJPtDEG`mjsjc*Hf+HSbPl+sf{>&No>zZ?g97d)FS6D)vp?xgzoFdx_^p3f877s^|q_3hySxBi9gD?_4tY8-`jha-Ix8i#b{02KmObN zvAxZ59nF(0G&;#77K_XNt+{i-yzCI2j%jrUB@RbT7-G~>3k^cKA%hBN=BSGFDQy{~EUWzVz88yV#%uVqxU zD1Q8S!sAN4$)NPlZG7(=$KDyzg)tM_Z^#I9NxFzf7k%d+%fv6NkJ4cRhO zf^XjaCNSBNRbz4hD|h_?nJ?x}&F`2+wH~bk=2z7c*NBpo#FA92au_SA|5B1SOU$6cpvB KWlny<8V3L-Bsq=% diff --git a/public/favicon/safari-pinned-tab.svg b/public/favicon/safari-pinned-tab.svg index 64aa526bd..60a2b3ee2 100644 --- a/public/favicon/safari-pinned-tab.svg +++ b/public/favicon/safari-pinned-tab.svg @@ -2,19 +2,19 @@ Created by potrace 1.11, written by Peter Selinger 2001-2013 - - + From ac2661bc6e7bc0aad83c2a4bb3db82a05383b6e3 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Sun, 26 Feb 2017 21:10:49 -0600 Subject: [PATCH 27/40] editor data --- .../app/frontend/controllers/editor.js | 10 ++++++++-- .../app/frontend/models/app/editor.js | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/app/frontend/controllers/editor.js b/app/assets/javascripts/app/frontend/controllers/editor.js index 3254d78f8..d12c50b13 100644 --- a/app/assets/javascripts/app/frontend/controllers/editor.js +++ b/app/assets/javascripts/app/frontend/controllers/editor.js @@ -53,8 +53,14 @@ angular.module('app.frontend') } else { var id = event.data.id; var text = event.data.text; - if(this.note.uuid == id) { + var data = event.data.data; + + if(this.note.uuid === id) { this.note.text = text; + var changesMade = this.customEditor.setData(id, data); + if(changesMade) { + this.customEditor.setDirty(true); + } this.changesMade(); } } @@ -103,7 +109,7 @@ angular.module('app.frontend') this.postNoteToExternalEditor = function() { var externalEditorElement = document.getElementById("editor-iframe"); if(externalEditorElement) { - externalEditorElement.contentWindow.postMessage({text: this.note.text, id: this.note.uuid}, '*'); + externalEditorElement.contentWindow.postMessage({text: this.note.text, data: this.customEditor.dataForKey(this.note.uuid), id: this.note.uuid}, '*'); } } diff --git a/app/assets/javascripts/app/frontend/models/app/editor.js b/app/assets/javascripts/app/frontend/models/app/editor.js index 2b823d3a5..9cff50789 100644 --- a/app/assets/javascripts/app/frontend/models/app/editor.js +++ b/app/assets/javascripts/app/frontend/models/app/editor.js @@ -8,12 +8,14 @@ class Editor extends Item { super.mapContentToLocalProperties(contentObject) this.url = contentObject.url; this.name = contentObject.name; + this.data = contentObject.data || {}; } structureParams() { var params = { url: this.url, - name: this.name + name: this.name, + data: this.data }; _.merge(params, super.structureParams()); @@ -27,4 +29,17 @@ class Editor extends Item { get content_type() { return "SN|Editor"; } + + setData(key, value) { + var dataHasChanged = JSON.stringify(this.data[key]) !== JSON.stringify(value); + if(dataHasChanged) { + this.data[key] = value; + return true; + } + return false; + } + + dataForKey(key) { + return this.data[key] || {}; + } } From fc6fc10ceb24de10a97840bbb7a79b7161551d3b Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Mon, 27 Feb 2017 08:02:10 -0600 Subject: [PATCH 28/40] server,email,pw as url params --- app/assets/javascripts/app/frontend/controllers/home.js | 9 ++++++++- app/assets/javascripts/app/frontend/routes.js | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/app/frontend/controllers/home.js b/app/assets/javascripts/app/frontend/controllers/home.js index ea70d15e6..6522171f1 100644 --- a/app/assets/javascripts/app/frontend/controllers/home.js +++ b/app/assets/javascripts/app/frontend/controllers/home.js @@ -1,5 +1,12 @@ angular.module('app.frontend') -.controller('HomeCtrl', function ($scope, $rootScope, $timeout, modelManager, syncManager, authManager) { +.controller('HomeCtrl', function ($scope, $stateParams, $rootScope, $timeout, modelManager, syncManager, authManager) { + + if($stateParams.server && $stateParams.email && authManager.offline()) { + authManager.login($stateParams.server, $stateParams.email, $stateParams.pw, function(response){ + window.location.reload(); + }) + } + syncManager.loadLocalItems(function(items) { $scope.$apply(); diff --git a/app/assets/javascripts/app/frontend/routes.js b/app/assets/javascripts/app/frontend/routes.js index f1f9322fa..687d4cab9 100644 --- a/app/assets/javascripts/app/frontend/routes.js +++ b/app/assets/javascripts/app/frontend/routes.js @@ -7,7 +7,7 @@ angular.module('app.frontend') }) .state('home', { - url: '/', + url: '/?server&email&pw', parent: 'base', views: { 'content@' : { From b4821073cc5b5e6bdcd87dba5ee637376e584859 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Mon, 27 Feb 2017 08:03:14 -0600 Subject: [PATCH 29/40] rm log --- .../javascripts/app/services/directives/views/accountMenu.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/app/services/directives/views/accountMenu.js b/app/assets/javascripts/app/services/directives/views/accountMenu.js index e243a35cb..4b2046284 100644 --- a/app/assets/javascripts/app/services/directives/views/accountMenu.js +++ b/app/assets/javascripts/app/services/directives/views/accountMenu.js @@ -82,7 +82,6 @@ class AccountMenu { $scope.loginSubmitPressed = function() { $scope.formData.status = "Generating Login Keys..."; - 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){ if(!response || response.error) { From 7c01b22159836243c4dd5f00faf0d0d0ce693dd1 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Mon, 27 Feb 2017 09:12:30 -0600 Subject: [PATCH 30/40] allow iframe --- app/controllers/application_controller.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2d6b04dea..43b6f0688 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,10 +1,10 @@ class ApplicationController < ActionController::Base - # Prevent CSRF attacks by raising an exception. - # For APIs, you may want to use :null_session instead. protect_from_forgery with: :null_session after_action :set_csrf_cookie + after_action :allow_iframe + layout :false def frontend @@ -13,8 +13,13 @@ class ApplicationController < ActionController::Base rescue_from ActionView::MissingTemplate do |exception| end + protected + def allow_iframe + response.headers.except! 'X-Frame-Options' + end + def set_app_domain @appDomain = request.domain @appDomain << ':' + request.port.to_s unless request.port.blank? From 646819ebdc9283f36e892cde8cba5b582dcc14ae Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Mon, 27 Feb 2017 11:43:19 -0600 Subject: [PATCH 31/40] auto sign in function --- .../app/frontend/controllers/home.js | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/app/frontend/controllers/home.js b/app/assets/javascripts/app/frontend/controllers/home.js index 6522171f1..e59ece731 100644 --- a/app/assets/javascripts/app/frontend/controllers/home.js +++ b/app/assets/javascripts/app/frontend/controllers/home.js @@ -1,10 +1,27 @@ angular.module('app.frontend') .controller('HomeCtrl', function ($scope, $stateParams, $rootScope, $timeout, modelManager, syncManager, authManager) { - if($stateParams.server && $stateParams.email && authManager.offline()) { - authManager.login($stateParams.server, $stateParams.email, $stateParams.pw, function(response){ - window.location.reload(); - }) + function autoSignInFromParams() { + if(!authManager.offline()) { + // check if current account + if(syncManager.serverURL == $stateParams.server && authManager.user.email == $stateParams.email) { + // already signed in, return + return; + } else { + // sign out + syncManager.destroyLocalData(function(){ + window.location.reload(); + }) + } + } else { + authManager.login($stateParams.server, $stateParams.email, $stateParams.pw, function(response){ + window.location.reload(); + }) + } + } + + if($stateParams.server && $stateParams.email) { + autoSignInFromParams(); } syncManager.loadLocalItems(function(items) { From 34bb12748900d2724e93355c2e885be87ad114aa Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Mon, 27 Feb 2017 11:44:05 -0600 Subject: [PATCH 32/40] remove fullscreen shortcut --- .../app/frontend/controllers/editor.js | 19 ------------------- .../templates/frontend/editor.html.haml | 1 - 2 files changed, 20 deletions(-) diff --git a/app/assets/javascripts/app/frontend/controllers/editor.js b/app/assets/javascripts/app/frontend/controllers/editor.js index d12c50b13..cc3e8bf4b 100644 --- a/app/assets/javascripts/app/frontend/controllers/editor.js +++ b/app/assets/javascripts/app/frontend/controllers/editor.js @@ -15,25 +15,6 @@ angular.module('app.frontend') bindToController: true, link:function(scope, elem, attrs, ctrl) { - - var handler = function(event) { - if (event.ctrlKey || event.metaKey) { - switch (String.fromCharCode(event.which).toLowerCase()) { - case 'o': - event.preventDefault(); - $timeout(function(){ - ctrl.toggleFullScreen(); - }) - break; - } - } - }; - - window.addEventListener('keydown', handler); - scope.$on('$destroy', function(){ - window.removeEventListener('keydown', handler); - }) - scope.$watch('ctrl.note', function(note, oldNote){ if(note) { ctrl.setNote(note, oldNote); diff --git a/app/assets/templates/frontend/editor.html.haml b/app/assets/templates/frontend/editor.html.haml index e8956bb3b..f16bfb3ed 100644 --- a/app/assets/templates/frontend/editor.html.haml +++ b/app/assets/templates/frontend/editor.html.haml @@ -19,7 +19,6 @@ %ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark{"ng-if" => "ctrl.showMenu"} %li{"ng-click" => "ctrl.selectedMenuItem(); ctrl.toggleFullScreen()"} .text Toggle Fullscreen - .shortcut Cmd + O %li{"ng-click" => "ctrl.deleteNote()"} .text Delete From 8462482f461b1a3c810fe8ef26c9802042767dc9 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Mon, 27 Feb 2017 12:31:32 -0600 Subject: [PATCH 33/40] note selection adjustment --- .../javascripts/app/frontend/controllers/home.js | 7 ++++--- .../javascripts/app/frontend/controllers/notes.js | 15 +++------------ app/assets/templates/frontend/notes.html.haml | 2 +- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/app/frontend/controllers/home.js b/app/assets/javascripts/app/frontend/controllers/home.js index e59ece731..9ee571cb2 100644 --- a/app/assets/javascripts/app/frontend/controllers/home.js +++ b/app/assets/javascripts/app/frontend/controllers/home.js @@ -34,10 +34,11 @@ angular.module('app.frontend') }, 30000); }); - $scope.allTag = new Tag({all: true}); - $scope.allTag.title = "All"; + var allTag = new Tag({all: true}); + allTag.title = "All"; $scope.tags = modelManager.tags; - $scope.allTag.notes = modelManager.notes; + allTag.notes = modelManager.notes; + $scope.allTag = allTag; /* Editor Callbacks diff --git a/app/assets/javascripts/app/frontend/controllers/notes.js b/app/assets/javascripts/app/frontend/controllers/notes.js index 76ee180f0..9f7b13fa4 100644 --- a/app/assets/javascripts/app/frontend/controllers/notes.js +++ b/app/assets/javascripts/app/frontend/controllers/notes.js @@ -32,8 +32,6 @@ angular.module('app.frontend') this.showMenu = false; }.bind(this)) - var isFirstLoad = true; - this.notesToDisplay = 20; this.paginate = function() { this.notesToDisplay += 20 @@ -51,16 +49,9 @@ angular.module('app.frontend') tag.notes.forEach(function(note){ note.visible = true; }) - this.selectFirstNote(false); - if(isFirstLoad) { - $timeout(function(){ - this.createNewNote(); - isFirstLoad = false; - }.bind(this)) - } else if(tag.notes.length == 0) { - this.createNewNote(); - } + var createNew = tag.notes.length == 0; + this.selectFirstNote(createNew); } this.selectedTagDelete = function() { @@ -69,7 +60,7 @@ angular.module('app.frontend') } this.selectFirstNote = function(createNew) { - var visibleNotes = this.tag.notes.filter(function(note){ + var visibleNotes = this.sortedNotes.filter(function(note){ return note.visible; }); diff --git a/app/assets/templates/frontend/notes.html.haml b/app/assets/templates/frontend/notes.html.haml index f259f62c8..e7b1aa5c2 100644 --- a/app/assets/templates/frontend/notes.html.haml +++ b/app/assets/templates/frontend/notes.html.haml @@ -28,7 +28,7 @@ .scrollable .infinite-scroll{"infinite-scroll" => "ctrl.paginate()", "can-load" => "true", "threshold" => "200"} - .note{"ng-repeat" => "note in ctrl.tag.notes | filter: ctrl.filterNotes | orderBy: ctrl.sortBy:true | limitTo:ctrl.notesToDisplay", + .note{"ng-repeat" => "note in (ctrl.sortedNotes = (ctrl.tag.notes | filter: ctrl.filterNotes | orderBy: ctrl.sortBy:true | limitTo:ctrl.notesToDisplay))", "ng-click" => "ctrl.selectNote(note)", "ng-class" => "{'selected' : ctrl.selectedNote == note}"} .name{"ng-if" => "note.title"} {{note.title}} From e08c284a30e15e9b121078c17ce6563cbfbe6086 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Mon, 27 Feb 2017 12:40:42 -0600 Subject: [PATCH 34/40] rename file --- app/assets/templates/frontend/editor.html.haml | 2 +- app/assets/templates/frontend/notes.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/templates/frontend/editor.html.haml b/app/assets/templates/frontend/editor.html.haml index f16bfb3ed..ee505a48d 100644 --- a/app/assets/templates/frontend/editor.html.haml +++ b/app/assets/templates/frontend/editor.html.haml @@ -12,7 +12,7 @@ %ul.nav %li.dropdown.pull-left.mr-10{"click-outside" => "ctrl.showMenu = false;", "is-open" => "ctrl.showMenu"} %a.dropdown-toggle{"ng-click" => "ctrl.showMenu = !ctrl.showMenu; ctrl.showExtensions = false;"} - File + Menu %span.caret %span.sr-only diff --git a/app/assets/templates/frontend/notes.html.haml b/app/assets/templates/frontend/notes.html.haml index e7b1aa5c2..e6869f2d6 100644 --- a/app/assets/templates/frontend/notes.html.haml +++ b/app/assets/templates/frontend/notes.html.haml @@ -10,7 +10,7 @@ %ul.nav.nav-pills %li.dropdown %a.dropdown-toggle{"ng-click" => "ctrl.showMenu = !ctrl.showMenu"} - Tag options + Menu %span.caret %span.sr-only From 1f90bf028690b3576d9c298feb0f875341d27f65 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Mon, 27 Feb 2017 13:59:11 -0600 Subject: [PATCH 35/40] editor note references --- .../app/frontend/controllers/editor.js | 23 ++++++++--- .../app/frontend/models/app/editor.js | 41 +++++++++++++++++++ .../app/frontend/models/app/note.js | 2 +- .../app/frontend/models/local/itemParams.js | 2 +- .../javascripts/app/services/modelManager.js | 18 +++++--- .../javascripts/app/services/syncManager.js | 1 - 6 files changed, 74 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/app/frontend/controllers/editor.js b/app/assets/javascripts/app/frontend/controllers/editor.js index cc3e8bf4b..04252b04f 100644 --- a/app/assets/javascripts/app/frontend/controllers/editor.js +++ b/app/assets/javascripts/app/frontend/controllers/editor.js @@ -52,8 +52,10 @@ angular.module('app.frontend') this.showMenu = false; this.loadTagsString(); - if(note.editorUrl) { - this.customEditor = this.editorForUrl(note.editorUrl); + var editor = this.editorForNote(note); + + if(editor) { + this.customEditor = editor; this.postNoteToExternalEditor(); } else { this.customEditor = null; @@ -75,16 +77,27 @@ angular.module('app.frontend') this.selectedEditor = function(editor) { this.showEditorMenu = false; if(editor.default) { + if(this.customEditor) { + this.customEditor.removeItemAsRelationship(this.note); + this.customEditor.setDirty(true); + } this.customEditor = null; } else { this.customEditor = editor; + this.customEditor.addItemAsRelationship(this.note); + this.customEditor.setDirty(true); } - this.note.editorUrl = editor.url; }.bind(this) - this.editorForUrl = function(url) { + this.editorForNote = function(note) { var editors = modelManager.itemsForContentType("SN|Editor"); - return editors.filter(function(editor){return editor.url == url})[0]; + for(var editor of editors) { + // console.log(editor.notes, editor.references); + if(_.includes(editor.notes, note)) { + return editor; + } + } + return null; } this.postNoteToExternalEditor = function() { diff --git a/app/assets/javascripts/app/frontend/models/app/editor.js b/app/assets/javascripts/app/frontend/models/app/editor.js index 9cff50789..49e84666d 100644 --- a/app/assets/javascripts/app/frontend/models/app/editor.js +++ b/app/assets/javascripts/app/frontend/models/app/editor.js @@ -2,6 +2,9 @@ class Editor extends Item { constructor(json_obj) { super(json_obj); + if(!this.notes) { + this.notes = []; + } } mapContentToLocalProperties(contentObject) { @@ -22,6 +25,44 @@ class Editor extends Item { return params; } + referenceParams() { + var references = _.map(this.notes, function(note){ + return {uuid: note.uuid, content_type: note.content_type}; + }) + + return references; + } + + addItemAsRelationship(item) { + if(item.content_type == "Note") { + if(!_.find(this.notes, item)) { + this.notes.push(item); + } + } + super.addItemAsRelationship(item); + } + + removeItemAsRelationship(item) { + if(item.content_type == "Note") { + _.pull(this.notes, item); + } + super.removeItemAsRelationship(item); + } + + removeAllRelationships() { + super.removeAllRelationships(); + this.notes = []; + } + + locallyClearAllReferences() { + super.locallyClearAllReferences(); + this.notes = []; + } + + allReferencedObjects() { + return this.notes; + } + toJSON() { return {uuid: this.uuid} } diff --git a/app/assets/javascripts/app/frontend/models/app/note.js b/app/assets/javascripts/app/frontend/models/app/note.js index e79a64682..58cd84292 100644 --- a/app/assets/javascripts/app/frontend/models/app/note.js +++ b/app/assets/javascripts/app/frontend/models/app/note.js @@ -62,7 +62,7 @@ class Note extends Item { _.pull(tag.notes, this); }.bind(this)) this.tags = []; - } + } isBeingRemovedLocally() { this.tags.forEach(function(tag){ diff --git a/app/assets/javascripts/app/frontend/models/local/itemParams.js b/app/assets/javascripts/app/frontend/models/local/itemParams.js index 22d7405ce..396a51bd1 100644 --- a/app/assets/javascripts/app/frontend/models/local/itemParams.js +++ b/app/assets/javascripts/app/frontend/models/local/itemParams.js @@ -16,7 +16,7 @@ class ItemParams { } paramsForLocalStorage() { - this.additionalFields = ["updated_at", "dirty", "editorUrl"]; + this.additionalFields = ["updated_at", "dirty"]; this.forExportFile = true; return this.__params(); } diff --git a/app/assets/javascripts/app/services/modelManager.js b/app/assets/javascripts/app/services/modelManager.js index ef67bc86a..f5b5c04ac 100644 --- a/app/assets/javascripts/app/services/modelManager.js +++ b/app/assets/javascripts/app/services/modelManager.js @@ -59,7 +59,9 @@ class ModelManager { } mapResponseItemsToLocalModelsOmittingFields(items, omitFields) { - var models = []; + var models = [], processedObjects = []; + + // first loop should add and process items for (var json_obj of items) { json_obj = _.omit(json_obj, omitFields || []) var item = this.findItem(json_obj["uuid"]); @@ -80,11 +82,16 @@ class ModelManager { this.addItem(item); - if(json_obj.content) { - this.resolveReferencesForItem(item); - } - models.push(item); + processedObjects.push(json_obj); + } + + // second loop should process references + for (var index in processedObjects) { + var json_obj = processedObjects[index]; + if(json_obj.content) { + this.resolveReferencesForItem(models[index]); + } } this.notifySyncObserversOfModels(models); @@ -174,6 +181,7 @@ class ModelManager { return; } + for(var reference of contentObject.references) { var referencedItem = this.findItem(reference.uuid); if(referencedItem) { diff --git a/app/assets/javascripts/app/services/syncManager.js b/app/assets/javascripts/app/services/syncManager.js index ddabf436d..e8ebb82df 100644 --- a/app/assets/javascripts/app/services/syncManager.js +++ b/app/assets/javascripts/app/services/syncManager.js @@ -187,7 +187,6 @@ class SyncManager { var saved = this.handleItemsResponse(response.saved_items, omitFields); this.handleUnsavedItemsResponse(response.unsaved) - this.writeItemsToLocalStorage(saved, false, null); this.writeItemsToLocalStorage(retrieved, false, null); From ba68e287ced983dc8ac414eec318af803e350736 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Mon, 27 Feb 2017 20:19:57 -0600 Subject: [PATCH 36/40] no new note on launch --- .../app/frontend/controllers/editor.js | 4 ---- .../app/frontend/controllers/home.js | 8 +++++--- .../app/frontend/controllers/notes.js | 18 +++++++++++++++--- app/assets/templates/frontend/editor.html.haml | 6 +++--- app/assets/templates/frontend/home.html.haml | 2 +- app/assets/templates/frontend/notes.html.haml | 4 ++-- 6 files changed, 26 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/app/frontend/controllers/editor.js b/app/assets/javascripts/app/frontend/controllers/editor.js index 04252b04f..6a49ab1a1 100644 --- a/app/assets/javascripts/app/frontend/controllers/editor.js +++ b/app/assets/javascripts/app/frontend/controllers/editor.js @@ -18,8 +18,6 @@ angular.module('app.frontend') scope.$watch('ctrl.note', function(note, oldNote){ if(note) { ctrl.setNote(note, oldNote); - } else { - ctrl.note = {}; } }); } @@ -28,7 +26,6 @@ angular.module('app.frontend') .controller('EditorCtrl', function ($sce, $timeout, authManager, $rootScope, extensionManager, syncManager, modelManager) { window.addEventListener("message", function(event){ - // console.log("App received message:", event); if(event.data.status) { this.postNoteToExternalEditor(); } else { @@ -92,7 +89,6 @@ angular.module('app.frontend') this.editorForNote = function(note) { var editors = modelManager.itemsForContentType("SN|Editor"); for(var editor of editors) { - // console.log(editor.notes, editor.references); if(_.includes(editor.notes, note)) { return editor; } diff --git a/app/assets/javascripts/app/frontend/controllers/home.js b/app/assets/javascripts/app/frontend/controllers/home.js index 9ee571cb2..550827f6b 100644 --- a/app/assets/javascripts/app/frontend/controllers/home.js +++ b/app/assets/javascripts/app/frontend/controllers/home.js @@ -25,6 +25,7 @@ angular.module('app.frontend') } syncManager.loadLocalItems(function(items) { + $scope.allTag.didLoad = true; $scope.$apply(); syncManager.sync(null); @@ -35,10 +36,11 @@ angular.module('app.frontend') }); var allTag = new Tag({all: true}); - allTag.title = "All"; - $scope.tags = modelManager.tags; - allTag.notes = modelManager.notes; + allTag.needsLoad = true; $scope.allTag = allTag; + $scope.allTag.title = "All"; + $scope.tags = modelManager.tags; + $scope.allTag.notes = modelManager.notes; /* Editor Callbacks diff --git a/app/assets/javascripts/app/frontend/controllers/notes.js b/app/assets/javascripts/app/frontend/controllers/notes.js index 9f7b13fa4..0ddf86061 100644 --- a/app/assets/javascripts/app/frontend/controllers/notes.js +++ b/app/assets/javascripts/app/frontend/controllers/notes.js @@ -18,7 +18,16 @@ angular.module('app.frontend') link:function(scope, elem, attrs, ctrl) { scope.$watch('ctrl.tag', function(tag, oldTag){ if(tag) { - ctrl.tagDidChange(tag, oldTag); + if(tag.needsLoad) { + scope.$watch('ctrl.tag.didLoad', function(didLoad){ + if(didLoad) { + tag.needsLoad = false; + ctrl.tagDidChange(tag, oldTag); + } + }); + } else { + ctrl.tagDidChange(tag, oldTag); + } } }); } @@ -45,12 +54,15 @@ angular.module('app.frontend') } this.noteFilter.text = ""; + this.setNotes(tag.notes); + } - tag.notes.forEach(function(note){ + this.setNotes = function(notes) { + notes.forEach(function(note){ note.visible = true; }) - var createNew = tag.notes.length == 0; + var createNew = notes.length == 0; this.selectFirstNote(createNew); } diff --git a/app/assets/templates/frontend/editor.html.haml b/app/assets/templates/frontend/editor.html.haml index ee505a48d..933e00c99 100644 --- a/app/assets/templates/frontend/editor.html.haml +++ b/app/assets/templates/frontend/editor.html.haml @@ -1,5 +1,5 @@ .section.editor{"ng-class" => "{'fullscreen' : ctrl.fullscreen}"} - .section-title-bar.editor-heading{"ng-class" => "{'fullscreen' : ctrl.fullscreen }"} + .section-title-bar.editor-heading{"ng-if" => "ctrl.note", "ng-class" => "{'fullscreen' : ctrl.fullscreen }"} .title %input.input#note-title-editor{"ng-model" => "ctrl.note.title", "ng-keyup" => "$event.keyCode == 13 && ctrl.saveTitle($event)", "ng-change" => "ctrl.nameChanged()", "ng-focus" => "ctrl.onNameFocus()", @@ -8,7 +8,7 @@ .tags %input.tags-input{"type" => "text", "ng-keyup" => "$event.keyCode == 13 && $event.target.blur();", "ng-model" => "ctrl.tagsString", "placeholder" => "#tags", "ng-blur" => "ctrl.updateTagsFromTagsString($event, ctrl.tagsString)"} - .section-menu + .section-menu{"ng-if" => "ctrl.note"} %ul.nav %li.dropdown.pull-left.mr-10{"click-outside" => "ctrl.showMenu = false;", "is-open" => "ctrl.showMenu"} %a.dropdown-toggle{"ng-click" => "ctrl.showMenu = !ctrl.showMenu; ctrl.showExtensions = false;"} @@ -20,7 +20,7 @@ %li{"ng-click" => "ctrl.selectedMenuItem(); ctrl.toggleFullScreen()"} .text Toggle Fullscreen %li{"ng-click" => "ctrl.deleteNote()"} - .text Delete + .text Delete Note %li.sep %li.dropdown.pull-left.mr-10{"click-outside" => "ctrl.showEditorMenu = false;", "is-open" => "ctrl.showEditorMenu"} diff --git a/app/assets/templates/frontend/home.html.haml b/app/assets/templates/frontend/home.html.haml index 82642f6f9..61203afcd 100644 --- a/app/assets/templates/frontend/home.html.haml +++ b/app/assets/templates/frontend/home.html.haml @@ -6,6 +6,6 @@ %notes-section{"remove-tag" => "notesRemoveTag", "add-new" => "notesAddNew", "selection-made" => "notesSelectionMade", "tag" => "selectedTag", "remove" => "deleteNote"} - %editor-section{"ng-if" => "selectedNote", "note" => "selectedNote", "remove" => "deleteNote", "save" => "saveNote", "update-tags" => "updateTagsForNote"} + %editor-section{"note" => "selectedNote", "remove" => "deleteNote", "save" => "saveNote", "update-tags" => "updateTagsForNote"} %header diff --git a/app/assets/templates/frontend/notes.html.haml b/app/assets/templates/frontend/notes.html.haml index e6869f2d6..63a42b7f0 100644 --- a/app/assets/templates/frontend/notes.html.haml +++ b/app/assets/templates/frontend/notes.html.haml @@ -15,8 +15,6 @@ %span.sr-only %ul.dropdown-menu.dropdown-menu-left.nt-dropdown-menu.dark{"ng-if" => "ctrl.showMenu"} - %li - %a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedTagDelete()"} Delete Tag %li %a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedSortByCreated()"} %span.top.mt-5.mr-5{"ng-if" => "ctrl.sortBy == 'created_at'"} ✓ @@ -25,6 +23,8 @@ %a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedSortByUpdated()"} %span.top.mt-5.mr-5{"ng-if" => "ctrl.sortBy == 'updated_at'"} ✓ Sort by date updated + %li + %a.text{"ng-click" => "ctrl.selectedMenuItem(); ctrl.selectedTagDelete()"} Delete Tag .scrollable .infinite-scroll{"infinite-scroll" => "ctrl.paginate()", "can-load" => "true", "threshold" => "200"} From 4dc2cfffb7b84ff0cd39680c38eb05aaa224aefc Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Tue, 28 Feb 2017 09:24:59 -0600 Subject: [PATCH 37/40] add data check for editor --- app/assets/javascripts/app/frontend/models/app/editor.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/assets/javascripts/app/frontend/models/app/editor.js b/app/assets/javascripts/app/frontend/models/app/editor.js index 49e84666d..00a09cc0f 100644 --- a/app/assets/javascripts/app/frontend/models/app/editor.js +++ b/app/assets/javascripts/app/frontend/models/app/editor.js @@ -5,6 +5,9 @@ class Editor extends Item { if(!this.notes) { this.notes = []; } + if(!this.data) { + this.data = {}; + } } mapContentToLocalProperties(contentObject) { From 19967577080088e2341635f8bacf92a7513b17ee Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Tue, 28 Feb 2017 11:09:47 -0600 Subject: [PATCH 38/40] editor change fixes --- .../app/frontend/controllers/editor.js | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/app/frontend/controllers/editor.js b/app/assets/javascripts/app/frontend/controllers/editor.js index 6a49ab1a1..b57f2e1ea 100644 --- a/app/assets/javascripts/app/frontend/controllers/editor.js +++ b/app/assets/javascripts/app/frontend/controllers/editor.js @@ -35,9 +35,11 @@ angular.module('app.frontend') if(this.note.uuid === id) { this.note.text = text; - var changesMade = this.customEditor.setData(id, data); - if(changesMade) { - this.customEditor.setDirty(true); + if(data) { + var changesMade = this.customEditor.setData(id, data); + if(changesMade) { + this.customEditor.setDirty(true); + } } this.changesMade(); } @@ -45,15 +47,28 @@ angular.module('app.frontend') }.bind(this), false); this.setNote = function(note, oldNote) { + var currentEditor = this.customEditor; + this.customEditor = null; this.showExtensions = false; this.showMenu = false; this.loadTagsString(); - var editor = this.editorForNote(note); - - if(editor) { + var setEditor = function(editor) { this.customEditor = editor; this.postNoteToExternalEditor(); + }.bind(this) + + var editor = this.editorForNote(note); + if(editor) { + if(currentEditor !== editor) { + // switch after timeout, so that note data isnt posted to current editor + $timeout(function(){ + setEditor(editor); + }.bind(this)); + } else { + // switch immediately + setEditor(editor); + } } else { this.customEditor = null; } @@ -73,11 +88,13 @@ angular.module('app.frontend') this.selectedEditor = function(editor) { this.showEditorMenu = false; + + if(this.customEditor && editor !== this.customEditor) { + this.customEditor.removeItemAsRelationship(this.note); + this.customEditor.setDirty(true); + } + if(editor.default) { - if(this.customEditor) { - this.customEditor.removeItemAsRelationship(this.note); - this.customEditor.setDirty(true); - } this.customEditor = null; } else { this.customEditor = editor; From 3b69a7d211c2ee7b9c6266ee21bd38d10a05f664 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Wed, 1 Mar 2017 16:58:46 -0600 Subject: [PATCH 39/40] adds merge local, closes #71 --- .../services/directives/views/accountMenu.js | 30 ++++++++++++++++--- .../directives/account-menu.html.haml | 6 +++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/app/services/directives/views/accountMenu.js b/app/assets/javascripts/app/services/directives/views/accountMenu.js index 4b2046284..3d52a9af8 100644 --- a/app/assets/javascripts/app/services/directives/views/accountMenu.js +++ b/app/assets/javascripts/app/services/directives/views/accountMenu.js @@ -6,10 +6,10 @@ class AccountMenu { this.scope = {}; } - controller($scope, authManager, modelManager, syncManager, $timeout) { + controller($scope, authManager, modelManager, syncManager, dbManager, $timeout) { 'ngInject'; - $scope.formData = {url: syncManager.serverURL}; + $scope.formData = {mergeLocal: true, url: syncManager.serverURL}; $scope.user = authManager.user; $scope.server = syncManager.serverURL; @@ -113,10 +113,32 @@ class AccountMenu { }) } + $scope.localNotesCount = function() { + return modelManager.filteredNotes.length; + } + + $scope.mergeLocalChanged = function() { + if(!$scope.formData.mergeLocal) { + if(!confirm("Unchecking this option means any of the notes you have written while you were signed out will be deleted. Are you sure you want to discard these notes?")) { + $scope.formData.mergeLocal = true; + } + } + } + $scope.onAuthSuccess = function() { - syncManager.markAllItemsDirtyAndSaveOffline(function(){ + var block = function() { window.location.reload(); - }) + } + + if($scope.formData.mergeLocal) { + syncManager.markAllItemsDirtyAndSaveOffline(function(){ + block(); + }) + } else { + dbManager.clearAllItems(function(){ + block(); + }) + } } $scope.destroyLocalData = function() { diff --git a/app/assets/templates/frontend/directives/account-menu.html.haml b/app/assets/templates/frontend/directives/account-menu.html.haml index 6927bff0f..3c93889a5 100644 --- a/app/assets/templates/frontend/directives/account-menu.html.haml +++ b/app/assets/templates/frontend/directives/account-menu.html.haml @@ -8,6 +8,10 @@ %input.form-control{:name => 'server', :placeholder => 'Server URL', :required => true, :type => 'text', 'ng-model' => 'formData.url'} %input.form-control{:autofocus => 'autofocus', :name => 'email', :placeholder => 'Email', :required => true, :type => 'email', 'ng-model' => 'formData.email'} %input.form-control{:placeholder => 'Password', :name => 'password', :required => true, :type => 'password', 'ng-model' => 'formData.user_password'} + .checkbox{"ng-if" => "localNotesCount() > 0"} + %label + %input{"type" => "checkbox", "ng-model" => "formData.mergeLocal", "ng-bind" => "true", "ng-change" => "mergeLocalChanged()"} + Merge local notes ({{localNotesCount()}} notes) %div{"ng-if" => "!formData.status"} %button.btn.dark-button.half-button{"ng-click" => "loginSubmitPressed()", "data-style" => "expand-right", "data-size" => "s", "state" => "buttonState"} @@ -94,4 +98,4 @@ .spinner.mt-10{"ng-if" => "importData.loading"} - %a.block.mt-25.red{"ng-click" => "destroyLocalData()"} Destroy all local data + %a.block.mt-25.red{"ng-click" => "destroyLocalData()"} {{ user ? "Sign out and clear local data" : "Clear all local data" }} From 052c6a9cde4c3fd826edc002145166ee0f0d7c76 Mon Sep 17 00:00:00 2001 From: Mo Bitar Date: Wed, 1 Mar 2017 17:07:18 -0600 Subject: [PATCH 40/40] password confirmation on registration, closes #68 --- .../javascripts/app/services/directives/views/accountMenu.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/assets/javascripts/app/services/directives/views/accountMenu.js b/app/assets/javascripts/app/services/directives/views/accountMenu.js index 3d52a9af8..7fd9ca4c1 100644 --- a/app/assets/javascripts/app/services/directives/views/accountMenu.js +++ b/app/assets/javascripts/app/services/directives/views/accountMenu.js @@ -98,6 +98,11 @@ class AccountMenu { } $scope.submitRegistrationForm = function() { + var confirmation = prompt("Please confirm your password. Note that because your notes are encrypted using your password, Standard Notes does not have a password reset option. You cannot forget your password.") + if(confirmation !== $scope.formData.user_password) { + alert("The two passwords you entered do not match. Please try again."); + return; + } $scope.formData.status = "Generating Account Keys..."; $timeout(function(){