diff --git a/app/assets/javascripts/app/controllers/footer.js b/app/assets/javascripts/app/controllers/footer.js index 450133349..fa6157410 100644 --- a/app/assets/javascripts/app/controllers/footer.js +++ b/app/assets/javascripts/app/controllers/footer.js @@ -23,7 +23,7 @@ angular.module('app') } }) .controller('FooterCtrl', function ($rootScope, authManager, modelManager, $timeout, dbManager, - syncManager, storageManager, passcodeManager, componentManager, singletonManager, packageManager) { + syncManager, storageManager, passcodeManager, componentManager, singletonManager) { this.getUser = function() { return authManager.user; @@ -47,12 +47,13 @@ angular.module('app') this.showAccountMenu = false; }.bind(this) - this.closeAccountMenu = () => { - this.showAccountMenu = false; - } - this.accountMenuPressed = function() { this.showAccountMenu = !this.showAccountMenu; + this.closeAllRooms(); + } + + this.closeAccountMenu = () => { + this.showAccountMenu = false; } this.hasPasscode = function() { @@ -112,7 +113,7 @@ angular.module('app') if(component.active) { // Show room, if it was not activated manually (in the event of event from componentManager) if(component.area == "rooms" && !component.showRoom) { - this.selectRoom(component); + component.showRoom = true; } $timeout(() => { var lastSize = component.getLastSize(); @@ -127,24 +128,17 @@ angular.module('app') } }}); - this.selectRoom = function(room) { + this.onRoomDismiss = function(room) { + room.showRoom = false; + } - // Allows us to send messages to component modal directive - if(!room.directiveController) { - room.directiveController = {onDismiss: () => { - room.showRoom = false; - }}; - } - - // Make sure to call dismiss() before setting new showRoom value - // This way the directive stays alive long enough to deactivate the associated component - // (The directive's life is at the mercy of "ng-if" => "room.showRoom") - if(room.showRoom) { - room.directiveController.dismiss(() => { - - }); - } else { - room.showRoom = true; + this.closeAllRooms = function() { + for(var room of this.rooms) { + room.showRoom = false; } } + + this.selectRoom = function(room) { + room.showRoom = !room.showRoom; + } }); diff --git a/app/assets/javascripts/app/directives/views/accountMenu.js b/app/assets/javascripts/app/directives/views/accountMenu.js index aa6a68476..7ec956322 100644 --- a/app/assets/javascripts/app/directives/views/accountMenu.js +++ b/app/assets/javascripts/app/directives/views/accountMenu.js @@ -17,7 +17,9 @@ class AccountMenu { $scope.server = syncManager.serverURL; $scope.close = function() { - $scope.closeFunction()(); + $timeout(() => { + $scope.closeFunction()(); + }) } $scope.encryptedBackupsAvailable = function() { diff --git a/app/assets/javascripts/app/directives/views/componentModal.js b/app/assets/javascripts/app/directives/views/componentModal.js index 0ee48c915..006b9b23e 100644 --- a/app/assets/javascripts/app/directives/views/componentModal.js +++ b/app/assets/javascripts/app/directives/views/componentModal.js @@ -18,20 +18,12 @@ class ComponentModal { controller($scope, $timeout, componentManager) { 'ngInject'; - if($scope.component.directiveController) { - $scope.component.directiveController.dismiss = function(callback) { - $scope.dismiss(callback); - } - } - $scope.dismiss = function(callback) { - var onDismiss = $scope.component.directiveController && $scope.component.directiveController.onDismiss(); $scope.el.remove(); $scope.$destroy(); - onDismiss && onDismiss(); + $scope.onDismiss && $scope.onDismiss()($scope.component); callback && callback(); } - } } diff --git a/app/assets/javascripts/app/models/app/extension.js b/app/assets/javascripts/app/models/app/extension.js index 4e80f484e..2a5111062 100644 --- a/app/assets/javascripts/app/models/app/extension.js +++ b/app/assets/javascripts/app/models/app/extension.js @@ -10,7 +10,7 @@ class Action { } } -class Extension extends Item { +class Extension extends Component { constructor(json) { super(json); @@ -33,9 +33,7 @@ class Extension extends Item { mapContentToLocalProperties(content) { super.mapContentToLocalProperties(content) - this.name = content.name; this.description = content.description; - this.url = content.url; this.supported_types = content.supported_types; if(content.actions) { @@ -45,18 +43,12 @@ class Extension extends Item { } } - referenceParams() { - return null; - } - get content_type() { return "Extension"; } structureParams() { var params = { - name: this.name, - url: this.url, description: this.description, actions: this.actions, supported_types: this.supported_types diff --git a/app/assets/javascripts/app/services/actionsManager.js b/app/assets/javascripts/app/services/actionsManager.js index bf2002df8..85a30253b 100644 --- a/app/assets/javascripts/app/services/actionsManager.js +++ b/app/assets/javascripts/app/services/actionsManager.js @@ -1,12 +1,10 @@ class ActionsManager { - constructor(httpManager, modelManager, authManager, syncManager, storageManager) { + constructor(httpManager, modelManager, authManager, syncManager) { this.httpManager = httpManager; this.modelManager = modelManager; this.authManager = authManager; - this.enabledRepeatActionUrls = JSON.parse(storageManager.getItem("enabledRepeatActionUrls")) || []; this.syncManager = syncManager; - this.storageManager = storageManager; } get extensions() { @@ -19,27 +17,11 @@ class ActionsManager { }) } - actionWithURL(url) { - for (var extension of this.extensions) { - return _.find(extension.actions, {url: url}) - } - } - - addExtension(url, callback) { - this.retrieveExtensionFromServer(url, callback); - } - - deleteExtension(extension) { - this.modelManager.setItemToBeDeleted(extension); - this.syncManager.sync(null); - } - /* Loads an extension in the context of a certain item. The server then has the chance to respond with actions that are relevant just to this item. The response extension is not saved, just displayed as a one-time thing. */ loadExtensionInContextOfItem(extension, item, callback) { - this.httpManager.getAbsolute(extension.url, {content_type: item.content_type, item_uuid: item.uuid}, function(response){ this.updateExtensionFromRemoteResponse(extension, response); callback && callback(extension); @@ -51,50 +33,9 @@ class ActionsManager { }.bind(this)) } - /* - Registers new extension and saves it to user's account - */ - retrieveExtensionFromServer(url, callback) { - this.httpManager.getAbsolute(url, {}, function(response){ - if(typeof response !== 'object') { - callback(null); - return; - } - var ext = this.handleExtensionLoadExternalResponseItem(url, response); - if(callback) { - callback(ext); - } - }.bind(this), function(response){ - console.error("Error registering extension", response); - callback(null); - }) - } - - handleExtensionLoadExternalResponseItem(url, externalResponseItem) { - // Don't allow remote response to set these flags - delete externalResponseItem.uuid; - - var extension = _.find(this.extensions, {url: url}); - if(extension) { - this.updateExtensionFromRemoteResponse(extension, externalResponseItem); - } else { - extension = new Extension(externalResponseItem); - extension.url = url; - extension.setDirty(true); - this.modelManager.addItem(extension); - this.syncManager.sync(null); - } - - return extension; - } - updateExtensionFromRemoteResponse(extension, response) { - if(response.description) { - extension.description = response.description; - } - if(response.supported_types) { - extension.supported_types = response.supported_types; - } + if(response.description) { extension.description = response.description; } + if(response.supported_types) { extension.supported_types = response.supported_types; } if(response.actions) { extension.actions = response.actions.map(function(action){ @@ -105,14 +46,6 @@ class ActionsManager { } } - refreshExtensionsFromServer() { - for(var ext of this.extensions) { - this.retrieveExtensionFromServer(ext.url, function(extension){ - extension.setDirty(true); - }); - } - } - executeAction(action, extension, item, callback) { var customCallback = function(response) { @@ -197,24 +130,6 @@ class ActionsManager { action.lastExecuted = new Date(); } - isRepeatActionEnabled(action) { - return _.includes(this.enabledRepeatActionUrls, action.url); - } - - queueAction(action, extension, delay, changedItems) { - this.actionQueue = this.actionQueue || []; - if(_.find(this.actionQueue, {url: action.url})) { - return; - } - - this.actionQueue.push(action); - - setTimeout(function () { - this.triggerWatchAction(action, extension, changedItems); - _.pull(this.actionQueue, action); - }.bind(this), delay * 1000); - } - outgoingParamsForItem(item, extension, decrypted = false) { var keys = this.authManager.keys(); if(decrypted) { diff --git a/app/assets/javascripts/app/services/componentManager.js b/app/assets/javascripts/app/services/componentManager.js index 42e42073c..5e1db02cf 100644 --- a/app/assets/javascripts/app/services/componentManager.js +++ b/app/assets/javascripts/app/services/componentManager.js @@ -9,6 +9,7 @@ class ComponentManager { this.modelManager = modelManager; this.syncManager = syncManager; this.desktopManager = desktopManager; + this.sysExtManager = sysExtManager; this.timeout = $timeout; this.streamObservers = []; this.contextStreamObservers = []; @@ -160,7 +161,7 @@ class ComponentManager { if(source && source == ModelManager.MappingSourceRemoteSaved) { params.isMetadataUpdate = true; } - this.removePrivatePropertiesFromResponseItems([params]); + this.removePrivatePropertiesFromResponseItems([params], component); return params; } @@ -289,11 +290,17 @@ class ComponentManager { } } - removePrivatePropertiesFromResponseItems(responseItems, includeUrls) { + removePrivatePropertiesFromResponseItems(responseItems, component, options = {}) { + if(component) { + // System extensions can bypass this step + if(this.sysExtManager.isSystemExtension(component)) { + return; + } + } // Don't allow component to overwrite these properties. - var privateProperties = ["appData", "autoupdateDisabled", "permissions", "active", "encrypted"]; - if(includeUrls) { - privateProperties = privateProperties.concat(["url", "hosted_url", "local_url"]); + var privateProperties = ["appData", "autoupdateDisabled", "permissions", "active"]; + if(options) { + if(options.includeUrls) { privateProperties = privateProperties.concat(["url", "hosted_url", "local_url"])} } for(var responseItem of responseItems) { @@ -302,7 +309,7 @@ class ComponentManager { console.assert(typeof responseItem.setDirty !== 'function'); for(var prop of privateProperties) { - delete responseItem[prop]; + delete responseItem.content[prop]; } } } @@ -402,7 +409,7 @@ class ComponentManager { this.runWithPermissions(component, requiredPermissions, () => { - this.removePrivatePropertiesFromResponseItems(responseItems, {includeUrls: true}); + this.removePrivatePropertiesFromResponseItems(responseItems, component, {includeUrls: true}); /* We map the items here because modelManager is what updates the UI. If you were to instead get the items directly, @@ -439,7 +446,7 @@ class ComponentManager { this.runWithPermissions(component, requiredPermissions, () => { var responseItem = message.data.item; - this.removePrivatePropertiesFromResponseItems([responseItem]); + this.removePrivatePropertiesFromResponseItems([responseItem], component); var item = this.modelManager.createItem(responseItem); if(responseItem.clientData) { item.setDomainDataItem(component.url || component.uuid, responseItem.clientData, ClientDataDomain); diff --git a/app/assets/javascripts/app/services/packageManager.js b/app/assets/javascripts/app/services/packageManager.js deleted file mode 100644 index fa0aa837e..000000000 --- a/app/assets/javascripts/app/services/packageManager.js +++ /dev/null @@ -1,41 +0,0 @@ -class PackageManager { - - constructor(httpManager, modelManager, syncManager, componentManager) { - this.httpManager = httpManager; - this.modelManager = modelManager; - this.syncManager = syncManager; - this.componentManager = componentManager; - } - - installPackage(url, callback) { - this.httpManager.getAbsolute(url, {}, function(aPackage){ - console.log("Got package data", aPackage); - if(typeof aPackage !== 'object') { - callback(null); - return; - } - - // Remove private properties - this.componentManager.removePrivatePropertiesFromResponseItems([aPackage]); - - aPackage.package_info = Object.assign({}, aPackage); - - var assembled = this.modelManager.createItem(aPackage);; - this.modelManager.addItem(assembled); - - assembled.setDirty(true); - this.syncManager.sync("installPackage"); - - console.log("Created assembled", assembled); - - callback && callback(assembled); - }.bind(this), function(response){ - console.error("Error retrieving package", response); - callback(null); - }) - } - - -} - -angular.module('app').service('packageManager', PackageManager); diff --git a/app/assets/javascripts/app/services/sysExtManager.js b/app/assets/javascripts/app/services/sysExtManager.js index 7ee8c76ea..c822a9f2d 100644 --- a/app/assets/javascripts/app/services/sysExtManager.js +++ b/app/assets/javascripts/app/services/sysExtManager.js @@ -7,14 +7,22 @@ class SysExtManager { this.syncManager = syncManager; this.singletonManager = singletonManager; + this.extensionsIdentifier = "org.standardnotes.extensions-manager"; + this.systemExtensions = []; + this.resolveExtensionsManager(); } - resolveExtensionsManager() { - let extensionsIdentifier = "org.standardnotes.extensions-manager"; + isSystemExtension(extension) { + return this.systemExtensions.includes(extension.uuid); + } - this.singletonManager.registerSingleton({content_type: "SN|Component", package_info: {identifier: extensionsIdentifier}}, (resolvedSingleton) => { + resolveExtensionsManager() { + + this.singletonManager.registerSingleton({content_type: "SN|Component", package_info: {identifier: this.extensionsIdentifier}}, (resolvedSingleton) => { // Resolved Singleton + this.systemExtensions.push(resolvedSingleton.uuid); + var needsSync = false; if(isDesktopApplication()) { if(!resolvedSingleton.local_url) { @@ -43,7 +51,7 @@ class SysExtManager { let packageInfo = { name: "Extensions", - identifier: extensionsIdentifier + identifier: this.extensionsIdentifier } var item = { @@ -73,6 +81,8 @@ class SysExtManager { component.setDirty(true); this.syncManager.sync("resolveExtensionsManager createNew"); + this.systemExtensions.push(component.uuid); + valueCallback(component); }); } diff --git a/app/assets/templates/footer.html.haml b/app/assets/templates/footer.html.haml index d719763bc..68510d557 100644 --- a/app/assets/templates/footer.html.haml +++ b/app/assets/templates/footer.html.haml @@ -17,7 +17,7 @@ .item{"ng-repeat" => "room in ctrl.rooms track by room.uuid"} .column{"ng-click" => "ctrl.selectRoom(room)"} .label {{room.name}} - %component-modal{"ng-if" => "room.showRoom", "component" => "room", "controller" => "room.directiveController"} + %component-modal{"ng-if" => "room.showRoom", "component" => "room", "on-dismiss" => "ctrl.onRoomDismiss"} .right