Wip
This commit is contained in:
@@ -66,21 +66,15 @@ angular.module('app.frontend')
|
||||
onReady();
|
||||
}
|
||||
|
||||
if(this.editorComponent && this.editorComponent != associatedEditor) {
|
||||
// Deactivate old editor
|
||||
componentManager.deactivateComponent(this.editorComponent);
|
||||
this.editorComponent = null;
|
||||
}
|
||||
|
||||
// Activate new editor if it's different from the one currently activated
|
||||
if(associatedEditor && associatedEditor != this.editorComponent) {
|
||||
if(associatedEditor) {
|
||||
// switch after timeout, so that note data isnt posted to current editor
|
||||
$timeout(() => {
|
||||
this.enableComponent(associatedEditor);
|
||||
this.editorComponent = associatedEditor;
|
||||
this.selectedEditor = associatedEditor;
|
||||
onReady();
|
||||
})
|
||||
} else {
|
||||
this.selectedEditor = null;
|
||||
onReady();
|
||||
}
|
||||
|
||||
@@ -111,23 +105,19 @@ angular.module('app.frontend')
|
||||
}
|
||||
}
|
||||
|
||||
this.selectedEditor = function(editorComponent) {
|
||||
this.selectEditor = function(editor) {
|
||||
console.log("selectEditor", editor);
|
||||
this.showEditorMenu = false;
|
||||
|
||||
if(this.editorComponent && this.editorComponent !== editorComponent) {
|
||||
// This disassociates the editor from the note, but the component itself still needs to be deactivated
|
||||
this.disableComponentForCurrentItem(this.editorComponent);
|
||||
// Now deactivate the component
|
||||
componentManager.deactivateComponent(this.editorComponent);
|
||||
}
|
||||
|
||||
if(editorComponent) {
|
||||
if(editor) {
|
||||
this.note.setAppDataItem("prefersPlainEditor", false);
|
||||
this.note.setDirty(true);
|
||||
this.enableComponent(editorComponent);
|
||||
this.associateComponentWithCurrentItem(editorComponent);
|
||||
componentManager.associateComponentWithItem(editor, this.note);
|
||||
} else {
|
||||
// Note prefers plain editor
|
||||
if(this.selectedEditor) {
|
||||
componentManager.disassociateComponentWithItem(this.selectedEditor, this.note);
|
||||
}
|
||||
this.note.setAppDataItem("prefersPlainEditor", true);
|
||||
this.note.setDirty(true);
|
||||
syncManager.sync();
|
||||
@@ -137,7 +127,7 @@ angular.module('app.frontend')
|
||||
})
|
||||
}
|
||||
|
||||
this.editorComponent = editorComponent;
|
||||
this.selectedEditor = editor;
|
||||
}.bind(this)
|
||||
|
||||
this.hasAvailableExtensions = function() {
|
||||
@@ -423,10 +413,10 @@ angular.module('app.frontend')
|
||||
}
|
||||
} else {
|
||||
// Editor
|
||||
if(component.active && this.note && component.isActiveForItem(this.note)) {
|
||||
this.editorComponent = component;
|
||||
if(component.active && this.note && (component.isActiveForItem(this.note) || component.isDefaultEditor())) {
|
||||
this.selectedEditor = component;
|
||||
} else {
|
||||
this.editorComponent = null;
|
||||
this.selectedEditor = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,15 +502,6 @@ angular.module('app.frontend')
|
||||
componentManager.contextItemDidChangeInArea("editor-editor");
|
||||
}
|
||||
|
||||
this.enableComponent = function(component) {
|
||||
componentManager.activateComponent(component);
|
||||
componentManager.setEventFlowForComponent(component, 1);
|
||||
}
|
||||
|
||||
this.associateComponentWithCurrentItem = function(component) {
|
||||
componentManager.associateComponentWithItem(component, this.note);
|
||||
}
|
||||
|
||||
let alertKey = "displayed-component-disable-alert";
|
||||
this.disableComponentForCurrentItem = function(component, showAlert) {
|
||||
componentManager.disassociateComponentWithItem(component, this.note);
|
||||
|
||||
@@ -23,7 +23,7 @@ angular.module('app.frontend')
|
||||
}
|
||||
})
|
||||
.controller('FooterCtrl', function ($rootScope, authManager, modelManager, $timeout, dbManager,
|
||||
syncManager, storageManager, passcodeManager, componentManager, singletonManager) {
|
||||
syncManager, storageManager, passcodeManager, componentManager, singletonManager, packageManager) {
|
||||
|
||||
this.user = authManager.user;
|
||||
|
||||
@@ -143,16 +143,17 @@ angular.module('app.frontend')
|
||||
|
||||
this.selectRoom = function(room) {
|
||||
room.show = !room.show;
|
||||
if(room.show) {
|
||||
this.componentManager.activateComponent(room);
|
||||
} else {
|
||||
this.hideRoom(room);
|
||||
}
|
||||
}
|
||||
|
||||
this.hideRoom = function(room) {
|
||||
room.show = false;
|
||||
this.componentManager.deactivateComponent(room);
|
||||
// Allows us to send messages to component modal directive
|
||||
if(!room.directiveController) {
|
||||
room.directiveController = {};
|
||||
}
|
||||
|
||||
if(!room.show) {
|
||||
room.directiveController.dismiss();
|
||||
}
|
||||
|
||||
console.log("Show", room.show);
|
||||
}
|
||||
|
||||
// Handle singleton ProLink instance
|
||||
|
||||
@@ -18,7 +18,12 @@ class Component extends Item {
|
||||
|
||||
mapContentToLocalProperties(content) {
|
||||
super.mapContentToLocalProperties(content)
|
||||
/* Legacy */
|
||||
this.url = content.url;
|
||||
/* New */
|
||||
this.local_url = content.local_url;
|
||||
this.hosted_url = content.hosted_url;
|
||||
|
||||
this.name = content.name;
|
||||
this.autoupdate = content.autoupdate;
|
||||
|
||||
@@ -29,7 +34,6 @@ class Component extends Item {
|
||||
|
||||
this.permissions = content.permissions;
|
||||
this.active = content.active;
|
||||
this.local = content.local;
|
||||
|
||||
// custom data that a component can store in itself
|
||||
this.componentData = content.componentData || {};
|
||||
@@ -44,12 +48,13 @@ class Component extends Item {
|
||||
structureParams() {
|
||||
var params = {
|
||||
url: this.url,
|
||||
hosted_url: this.hosted_url,
|
||||
local_url: this.local_url,
|
||||
name: this.name,
|
||||
area: this.area,
|
||||
package_info: this.package_info,
|
||||
permissions: this.permissions,
|
||||
active: this.active,
|
||||
local: this.local,
|
||||
autoupdate: this.autoupdate,
|
||||
componentData: this.componentData,
|
||||
disassociatedItemIds: this.disassociatedItemIds,
|
||||
|
||||
@@ -8,14 +8,12 @@ class Theme extends Item {
|
||||
super.mapContentToLocalProperties(content)
|
||||
this.url = content.url;
|
||||
this.name = content.name;
|
||||
this.local = content.local;
|
||||
}
|
||||
|
||||
structureParams() {
|
||||
var params = {
|
||||
url: this.url,
|
||||
name: this.name,
|
||||
local: this.local
|
||||
name: this.name
|
||||
};
|
||||
|
||||
_.merge(params, super.structureParams());
|
||||
|
||||
@@ -143,7 +143,8 @@ class ComponentManager {
|
||||
jsonForItem(item, component, source) {
|
||||
var params = {uuid: item.uuid, content_type: item.content_type, created_at: item.created_at, updated_at: item.updated_at, deleted: item.deleted};
|
||||
params.content = item.createContentJSONFromProperties();
|
||||
params.clientData = item.getDomainDataItem(component.url, ClientDataDomain) || {};
|
||||
/* Legacy is using component.url key, so if it's present, use it, otherwise use uuid */
|
||||
params.clientData = item.getDomainDataItem(component.url || component.uuid, ClientDataDomain) || {};
|
||||
|
||||
/* This means the this function is being triggered through a remote Saving response, which should not update
|
||||
actual local content values. The reason is, Save responses may be delayed, and a user may have changed some values
|
||||
@@ -186,7 +187,7 @@ class ComponentManager {
|
||||
|
||||
componentForUrl(url) {
|
||||
return this.components.filter(function(component){
|
||||
return component.url === url;
|
||||
return component.url === url || component.hosted_url === url;
|
||||
})[0];
|
||||
}
|
||||
|
||||
@@ -251,9 +252,12 @@ class ComponentManager {
|
||||
}
|
||||
}
|
||||
|
||||
removePrivatePropertiesFromResponseItems(responseItems) {
|
||||
removePrivatePropertiesFromResponseItems(responseItems, includeUrls) {
|
||||
// Don't allow component to overwrite these properties.
|
||||
let privateProperties = ["appData"];
|
||||
var privateProperties = ["appData", "autoupdate", "permissions", "active"];
|
||||
if(includeUrls) {
|
||||
privateProperties = privateProperties.concat(["url", "hosted_url", "local_url"]);
|
||||
}
|
||||
for(var responseItem of responseItems) {
|
||||
|
||||
// Do not pass in actual items here, otherwise that would be destructive.
|
||||
@@ -275,10 +279,10 @@ class ComponentManager {
|
||||
];
|
||||
|
||||
this.runWithPermissions(component, requiredPermissions, message.permissions, function(){
|
||||
if(!_.find(this.streamObservers, {identifier: component.url})) {
|
||||
if(!_.find(this.streamObservers, {identifier: component.uuid})) {
|
||||
// for pushing laster as changes come in
|
||||
this.streamObservers.push({
|
||||
identifier: component.url,
|
||||
identifier: component.uuid,
|
||||
component: component,
|
||||
originalMessage: message,
|
||||
contentTypes: message.data.content_types
|
||||
@@ -304,10 +308,10 @@ class ComponentManager {
|
||||
];
|
||||
|
||||
this.runWithPermissions(component, requiredPermissions, message.permissions, function(){
|
||||
if(!_.find(this.contextStreamObservers, {identifier: component.url})) {
|
||||
if(!_.find(this.contextStreamObservers, {identifier: component.uuid})) {
|
||||
// for pushing laster as changes come in
|
||||
this.contextStreamObservers.push({
|
||||
identifier: component.url,
|
||||
identifier: component.uuid,
|
||||
component: component,
|
||||
originalMessage: message
|
||||
})
|
||||
@@ -336,7 +340,7 @@ class ComponentManager {
|
||||
this.runWithPermissions(component, requiredPermissions, message.permissions, () => {
|
||||
var responseItems = message.data.items;
|
||||
|
||||
this.removePrivatePropertiesFromResponseItems(responseItems);
|
||||
this.removePrivatePropertiesFromResponseItems(responseItems, {includeUrls: true});
|
||||
|
||||
/*
|
||||
We map the items here because modelManager is what updates the UI. If you were to instead get the items directly,
|
||||
@@ -348,7 +352,7 @@ class ComponentManager {
|
||||
var responseItem = _.find(responseItems, {uuid: item.uuid});
|
||||
_.merge(item.content, responseItem.content);
|
||||
if(responseItem.clientData) {
|
||||
item.setDomainDataItem(component.url, responseItem.clientData, ClientDataDomain);
|
||||
item.setDomainDataItem(component.url || component.uuid, responseItem.clientData, ClientDataDomain);
|
||||
}
|
||||
item.setDirty(true);
|
||||
}
|
||||
@@ -374,7 +378,7 @@ class ComponentManager {
|
||||
this.removePrivatePropertiesFromResponseItems([responseItem]);
|
||||
var item = this.modelManager.createItem(responseItem);
|
||||
if(responseItem.clientData) {
|
||||
item.setDomainDataItem(component.url, responseItem.clientData, ClientDataDomain);
|
||||
item.setDomainDataItem(component.url || component.uuid, responseItem.clientData, ClientDataDomain);
|
||||
}
|
||||
this.modelManager.addItem(item);
|
||||
this.modelManager.resolveReferencesForItem(item);
|
||||
@@ -457,7 +461,7 @@ class ComponentManager {
|
||||
}
|
||||
return p.name == required.name && matchesContentTypes;
|
||||
})[0];
|
||||
console.log("required", required, "requested", requestedPermissions, "matching", matching);
|
||||
|
||||
if(!matching) {
|
||||
/* Required permissions can be 1 content type, and requestedPermisisons may send an array of content types.
|
||||
In the case of an array, we can just check to make sure that requiredPermissions content type is found in the array
|
||||
@@ -503,7 +507,6 @@ class ComponentManager {
|
||||
// since these calls are asyncronous, multiple dialogs may be requested at the same time. We only want to present one and trigger all callbacks based on one modal result
|
||||
var existingDialog = _.find(this.permissionDialogs, {component: component});
|
||||
|
||||
component.trusted = component.url.startsWith("https://standardnotes.org") || component.url.startsWith("https://extensions.standardnotes.org");
|
||||
var scope = this.$rootScope.$new(true);
|
||||
scope.component = component;
|
||||
scope.permissions = requestedPermissions;
|
||||
@@ -531,7 +534,7 @@ class ComponentManager {
|
||||
this.permissionDialogs.push(scope);
|
||||
|
||||
if(!existingDialog) {
|
||||
var el = this.$compile( "<permissions-modal component='component' permissions='permissions' callback='callback' class='permissions-modal'></permissions-modal>" )(scope);
|
||||
var el = this.$compile( "<permissions-modal component='component' permissions='permissions' callback='callback' class='modal'></permissions-modal>" )(scope);
|
||||
angular.element(document.body).append(el);
|
||||
} else {
|
||||
console.log("Existing dialog, not presenting.");
|
||||
@@ -541,7 +544,7 @@ class ComponentManager {
|
||||
openModalComponent(component) {
|
||||
var scope = this.$rootScope.$new(true);
|
||||
scope.component = component;
|
||||
var el = this.$compile( "<component-modal component='component' class='component-modal'></component-modal>" )(scope);
|
||||
var el = this.$compile( "<component-modal component='component' class='modal'></component-modal>" )(scope);
|
||||
angular.element(document.body).append(el);
|
||||
}
|
||||
|
||||
@@ -711,10 +714,10 @@ class ComponentManager {
|
||||
}
|
||||
|
||||
urlForComponent(component) {
|
||||
if(isDesktopApplication() && component.local && component.url.startsWith("sn://")) {
|
||||
return component.url.replace("sn://", this.desktopManager.getApplicationDataPath() + "/");
|
||||
if(isDesktopApplication() && component.local_url) {
|
||||
return component.local_url.replace("sn://", this.desktopManager.getApplicationDataPath() + "/");
|
||||
} else {
|
||||
return component.url;
|
||||
return component.url || component.hosted_url;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,7 @@ class DesktopManager {
|
||||
/* Can handle components and themes */
|
||||
installOfflineComponentFromData(componentData, callback) {
|
||||
this.componentInstallationHandler(componentData, (installedComponent) => {
|
||||
componentData.content.url = installedComponent.content.url;
|
||||
componentData.content.local = true;
|
||||
componentData.content.local_url = installedComponent.content.local_url;
|
||||
callback(componentData);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ class ComponentModal {
|
||||
this.scope = {
|
||||
show: "=",
|
||||
component: "=",
|
||||
controller: "=",
|
||||
callback: "="
|
||||
};
|
||||
}
|
||||
@@ -19,12 +20,17 @@ class ComponentModal {
|
||||
|
||||
let identifier = "modal-" + $scope.component.uuid;
|
||||
|
||||
$scope.dismiss = function() {
|
||||
componentManager.deregisterHandler(identifier);
|
||||
$scope.component.directiveController.dismiss = function() {
|
||||
$scope.component.show = false;
|
||||
componentManager.deactivateComponent($scope.component);
|
||||
componentManager.deregisterHandler(identifier);
|
||||
$scope.el.remove();
|
||||
}
|
||||
|
||||
$scope.dismiss = function() {
|
||||
$scope.component.directiveController.dismiss();
|
||||
}
|
||||
|
||||
$scope.url = function() {
|
||||
return componentManager.urlForComponent($scope.component);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
class ComponentView {
|
||||
|
||||
constructor() {
|
||||
this.restrict = "E";
|
||||
this.templateUrl = "frontend/directives/component-view.html";
|
||||
this.scope = {
|
||||
component: "="
|
||||
};
|
||||
}
|
||||
|
||||
link($scope, el, attrs, ctrl) {
|
||||
$scope.el = el;
|
||||
|
||||
$scope.$watch('component', function(component, prevComponent){
|
||||
console.log("Component View Setting Component", component);
|
||||
ctrl.componentValueChanging(component, prevComponent);
|
||||
});
|
||||
}
|
||||
|
||||
controller($scope, $timeout, componentManager, desktopManager) {
|
||||
'ngInject';
|
||||
|
||||
this.componentValueChanging = (component, prevComponent) => {
|
||||
if(prevComponent && component !== prevComponent) {
|
||||
// Deactive old component
|
||||
console.log("DEACTIVATING OLD COMPONENT", prevComponent);
|
||||
componentManager.deactivateComponent(prevComponent);
|
||||
}
|
||||
|
||||
if(component) {
|
||||
componentManager.activateComponent(component);
|
||||
componentManager.setEventFlowForComponent(component, 1);
|
||||
}
|
||||
}
|
||||
|
||||
let identifier = "component-view-" + Math.random();
|
||||
|
||||
$scope.url = function() {
|
||||
if($scope.component.offlineOnly) {
|
||||
return $scope.component.local_url;
|
||||
}
|
||||
|
||||
if(desktopManager.isDesktop && $scope.component.local_url) {
|
||||
return $scope.component.local_url;
|
||||
}
|
||||
|
||||
return $scope.component.hosted_url || $scope.component.url;
|
||||
}
|
||||
|
||||
componentManager.registerHandler({identifier: identifier, areas: ["*"], activationHandler: (component) => {
|
||||
if(component.active) {
|
||||
$timeout(function(){
|
||||
var iframe = componentManager.iframeForComponent(component);
|
||||
if(iframe) {
|
||||
iframe.onload = function() {
|
||||
componentManager.registerComponentWindow(component, iframe.contentWindow);
|
||||
}.bind(this);
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
actionHandler: function(component, action, data) {
|
||||
if(action == "set-size") {
|
||||
componentManager.handleSetSizeEvent(component, data);
|
||||
}
|
||||
}.bind(this)});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('app.frontend').directive('componentView', () => new ComponentView);
|
||||
@@ -9,7 +9,7 @@ class EditorMenu {
|
||||
};
|
||||
}
|
||||
|
||||
controller($scope, componentManager, syncManager) {
|
||||
controller($scope, componentManager, syncManager, $timeout) {
|
||||
'ngInject';
|
||||
|
||||
$scope.formData = {};
|
||||
@@ -23,12 +23,10 @@ class EditorMenu {
|
||||
$scope.selectEditor = function($event, editor) {
|
||||
if(editor) {
|
||||
editor.conflict_of = null; // clear conflict if applicable
|
||||
if(editor.local && !isDesktopApplication()) {
|
||||
alert("This editor is installed locally and is available only through Standard Notes for Desktop.")
|
||||
return;
|
||||
}
|
||||
}
|
||||
$scope.callback()(editor);
|
||||
$timeout(() => {
|
||||
$scope.callback()(editor);
|
||||
})
|
||||
}
|
||||
|
||||
$scope.toggleDefaultForEditor = function(editor) {
|
||||
|
||||
@@ -29,10 +29,11 @@ class PermissionsModal {
|
||||
}
|
||||
|
||||
controller($scope, modelManager) {
|
||||
|
||||
console.log("permissions", $scope.permissions);
|
||||
|
||||
$scope.formattedPermissions = $scope.permissions.map(function(permission){
|
||||
if(permission.name === "stream-items") {
|
||||
var title = "Access to ";
|
||||
var types = permission.content_types.map(function(type){
|
||||
var desc = modelManager.humanReadableDisplayForContentType(type);
|
||||
if(desc) {
|
||||
@@ -61,14 +62,14 @@ class PermissionsModal {
|
||||
}
|
||||
}
|
||||
|
||||
return title + typesString;
|
||||
return typesString;
|
||||
} else if(permission.name === "stream-context-item") {
|
||||
var mapping = {
|
||||
"editor-stack" : "working note",
|
||||
"note-tags" : "working note",
|
||||
"editor-editor": "working note"
|
||||
}
|
||||
return "Access to " + mapping[$scope.component.area];
|
||||
return mapping[$scope.component.area];
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
class PackageManager {
|
||||
|
||||
constructor(httpManager, modelManager, syncManager) {
|
||||
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);
|
||||
@@ -15,7 +15,11 @@ class PackageManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove private properties
|
||||
this.componentManager.removePrivatePropertiesFromResponseItems([aPackage]);
|
||||
|
||||
var assembled = this.modelManager.createItem(aPackage);
|
||||
|
||||
assembled.package_info = aPackage;
|
||||
this.modelManager.addItem(assembled);
|
||||
assembled.setDirty(true);
|
||||
|
||||
Reference in New Issue
Block a user