From d8fd2184090f91e4f100c746698e5b5e0ae5a2b6 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 21 Sep 2017 11:19:24 +0100 Subject: [PATCH] Allow project dependencies to be edited in dialog --- editor/js/ui/editor.js | 22 ++++ editor/js/ui/projectSettings.js | 197 ++++++++++++++++++++++++++++++- editor/sass/projects.scss | 7 +- red/api/editor/projects/index.js | 4 +- 4 files changed, 217 insertions(+), 13 deletions(-) diff --git a/editor/js/ui/editor.js b/editor/js/ui/editor.js index bb58caba9..27d02e8ae 100644 --- a/editor/js/ui/editor.js +++ b/editor/js/ui/editor.js @@ -1981,7 +1981,19 @@ RED.editor = (function() { editStack.push({type:type}); RED.view.state(RED.state.EDITING); var expressionEditor; + var changeTimer; + var checkValid = function() { + var v = expressionEditor.getValue(); + try { + JSON.parse(v); + $("#node-dialog-ok").removeClass('disabled'); + return true; + } catch(err) { + $("#node-dialog-ok").addClass('disabled'); + return false; + } + } var trayOptions = { title: options.title || getEditStackTitle(), buttons: [ @@ -1997,6 +2009,9 @@ RED.editor = (function() { text: RED._("common.label.done"), class: "primary", click: function() { + if (options.requireValid && !checkValid()) { + return; + } onComplete(expressionEditor.getValue()); RED.tray.close(); } @@ -2024,6 +2039,13 @@ RED.editor = (function() { mode:"ace/mode/json" }); expressionEditor.getSession().setValue(value||"",-1); + if (options.requireValid) { + expressionEditor.getSession().on('change', function() { + clearTimeout(changeTimer); + changeTimer = setTimeout(checkValid,200); + }); + checkValid(); + } $("#node-input-json-reformat").click(function(evt) { evt.preventDefault(); var v = expressionEditor.getValue()||""; diff --git a/editor/js/ui/projectSettings.js b/editor/js/ui/projectSettings.js index ba2ff6fa2..02daca871 100644 --- a/editor/js/ui/projectSettings.js +++ b/editor/js/ui/projectSettings.js @@ -44,7 +44,7 @@ RED.projects.settings = (function() { var tabContainer; var trayOptions = { - title: "NLS: Project Information",// RED._("menu.label.userSettings"), + title: "Project Information",// RED._("menu.label.userSettings"),, // TODO: nls buttons: [ { id: "node-dialog-ok", @@ -211,11 +211,11 @@ RED.projects.settings = (function() { if (summary) { container.text(summary).removeClass('node-info-node'); } else { - container.text("NLS: No summary available").addClass('node-info-none'); + container.text("No summary available").addClass('node-info-none');// TODO: nls } } - function createViewPane(activeProject) { + function createMainPane(activeProject) { var pane = $('
'); $('

').text(activeProject.name).appendTo(pane); @@ -244,16 +244,203 @@ RED.projects.settings = (function() { return pane; } + function updateProjectDependencies(activeProject,depsList) { + depsList.editableList('empty'); + depsList.editableList('addItem',{index:1, label:"Unknown Dependencies"}); // TODO: nls + depsList.editableList('addItem',{index:3, label:"Unused dependencies"}); // TODO: nls + + for (var m in modulesInUse) { + if (modulesInUse.hasOwnProperty(m)) { + depsList.editableList('addItem',{ + module: modulesInUse[m].module, + version: modulesInUse[m].version, + count: modulesInUse[m].count, + known: activeProject.dependencies.hasOwnProperty(m) + }); + } + } + + if (activeProject.dependencies) { + for (var m in activeProject.dependencies) { + if (activeProject.dependencies.hasOwnProperty(m) && !modulesInUse.hasOwnProperty(m)) { + depsList.editableList('addItem',{ + module: m, + version: activeProject.dependencies[m], //RED.nodes.registry.getModule(module).version, + count: 0, + known: true + }); + } + } + } + } + + function editDependencies(activeProject,depsJSON,container,depsList) { + RED.editor.editJSON({ + title: RED._('sidebar.project.editDependencies'), + value: depsJSON||JSON.stringify(activeProject.dependencies||{},"",4), + requireValid: true, + complete: function(v) { + try { + var parsed = JSON.parse(v); + var spinner = addSpinnerOverlay(container); + + var done = function(err,res) { + if (err) { + editDependencies(activeProject,v,container,depsList); + } + activeProject.dependencies = parsed; + updateProjectDependencies(activeProject,depsList); + } + utils.sendRequest({ + url: "projects/"+activeProject.name, + type: "PUT", + responses: { + 0: function(error) { + done(error,null); + }, + 200: function(data) { + done(null,data); + }, + 400: { + 'unexpected_error': function(error) { + done(error,null); + } + }, + } + },{dependencies:parsed}).always(function() { + spinner.remove(); + }); + } catch(err) { + editDependencies(activeProject,v,container,depsList); + } + } + }); + } + + function createDependenciesPane(activeProject) { + var pane = $('
'); + $('') + .appendTo(pane) + .click(function(evt) { + evt.preventDefault(); + editDependencies(activeProject,null,pane,depsList) + }); + var depsList = $("
    ",{style:"position: absolute;top: 60px;bottom: 20px;left: 20px;right: 20px;"}).appendTo(pane); + depsList.editableList({ + addButton: false, + addItem: function(row,index,entry) { + // console.log(entry); + var headerRow = $('
    ',{class:"palette-module-header"}).appendTo(row); + if (entry.label) { + row.parent().addClass("palette-module-section"); + headerRow.text(entry.label); + if (entry.index === 1) { + var addButton = $('').appendTo(headerRow).click(function(evt) { + evt.preventDefault(); + var deps = $.extend(true, {}, activeProject.dependencies); + for (var m in modulesInUse) { + if (modulesInUse.hasOwnProperty(m) && !modulesInUse[m].known) { + deps[m] = modulesInUse[m].version; + } + } + editDependencies(activeProject,JSON.stringify(deps,"",4),pane,depsList); + }); + } else if (entry.index === 3) { + var removeButton = $('').appendTo(headerRow).click(function(evt) { + evt.preventDefault(); + var deps = $.extend(true, {}, activeProject.dependencies); + for (var m in activeProject.dependencies) { + if (activeProject.dependencies.hasOwnProperty(m) && !modulesInUse.hasOwnProperty(m)) { + delete deps[m]; + } + } + editDependencies(activeProject,JSON.stringify(deps,"",4),pane,depsList); + }); + } + } else { + headerRow.toggleClass("palette-module-unused",entry.count === 0); + entry.element = headerRow; + var titleRow = $('
    ').appendTo(headerRow); + var icon = $('').appendTo(titleRow); + entry.icon = icon; + $('').html(entry.module).appendTo(titleRow); + var metaRow = $('
    ').appendTo(headerRow); + var versionSpan = $('').html(entry.version).appendTo(metaRow); + if (!entry.known) { + headerRow.addClass("palette-module-unknown"); + } else if (entry.known && entry.count === 0) { + + } + } + }, + sort: function(A,B) { + if (A.index && B.index) { + return A.index - B.index; + } + var Acategory = A.index?A.index:(A.known?(A.count>0?0:4):2); + var Bcategory = B.index?B.index:(B.known?(B.count>0?0:4):2); + if (Acategory === Bcategory) { + return A.module.localeCompare(B.module); + } else { + return Acategory - Bcategory; + } + } + }); + + updateProjectDependencies(activeProject,depsList); + return pane; + + } var utils; + var modulesInUse = {}; function init(_utils) { utils = _utils; addPane({ id:'main', - title: "NLS: Project", - get: createViewPane, + title: "Project", // TODO: nls + get: createMainPane, close: function() { } + }); + addPane({ + id:'deps', + title: "Dependencies", // TODO: nls + get: createDependenciesPane, + close: function() { } + }); + + RED.events.on('nodes:add', function(n) { + if (!/^subflow:/.test(n.type)) { + var module = RED.nodes.registry.getNodeSetForType(n.type).module; + if (module !== 'node-red') { + if (!modulesInUse.hasOwnProperty(module)) { + modulesInUse[module] = { + module: module, + version: RED.nodes.registry.getModule(module).version, + count: 0, + known: false + } + } + modulesInUse[module].count++; + } + } }) + RED.events.on('nodes:remove', function(n) { + if (!/^subflow:/.test(n.type)) { + var module = RED.nodes.registry.getNodeSetForType(n.type).module; + if (module !== 'node-red' && modulesInUse.hasOwnProperty(module)) { + modulesInUse[module].count--; + if (modulesInUse[module].count === 0) { + if (!modulesInUse[module].known) { + delete modulesInUse[module]; + } + } + } + } + }) + + + } return { init: init, diff --git a/editor/sass/projects.scss b/editor/sass/projects.scss index 31cccf4d2..1e38fc596 100644 --- a/editor/sass/projects.scss +++ b/editor/sass/projects.scss @@ -200,14 +200,11 @@ } } -.sidebar-projects-dependencies { - position: relative; - height: 100%; +#project-settings-tab-deps { .red-ui-editableList-container { padding: 0; } .red-ui-editableList-border { - border: none; border-radius: 0; } .red-ui-editableList-item-content { @@ -240,6 +237,6 @@ left:0; right:0; bottom:0; - overflow-y: scroll; + overflow-y: auto; padding: 8px 20px 20px; } diff --git a/red/api/editor/projects/index.js b/red/api/editor/projects/index.js index 207c61dda..98f3b2814 100644 --- a/red/api/editor/projects/index.js +++ b/red/api/editor/projects/index.js @@ -85,9 +85,7 @@ module.exports = { req.body.hasOwnProperty('dependencies')|| req.body.hasOwnProperty('summary')) { runtime.storage.projects.updateProject(req.params.id, req.body).then(function() { - setTimeout(function() { - res.redirect(303,req.baseUrl + '/'); - },5000); + res.redirect(303,req.baseUrl + '/'); }).otherwise(function(err) { if (err.code) { res.status(400).json({error:err.code, message: err.message});